fedimint_metrics/
lib.rs

1#![deny(clippy::pedantic)]
2#![allow(clippy::missing_errors_doc)]
3
4use std::net::SocketAddr;
5use std::sync::LazyLock;
6
7use axum::Router;
8use axum::http::StatusCode;
9use axum::routing::get;
10use fedimint_core::task::{TaskGroup, TaskShutdownToken};
11use prometheus::Registry;
12pub use prometheus::{
13    self, Encoder, Gauge, GaugeVec, Histogram, HistogramVec, IntCounter, IntCounterVec,
14    TextEncoder, histogram_opts, opts, register_histogram_with_registry,
15    register_int_counter_vec_with_registry,
16};
17use tokio::net::TcpListener;
18use tracing::error;
19
20pub static REGISTRY: LazyLock<Registry> =
21    LazyLock::new(|| Registry::new_custom(Some("fm".into()), None).unwrap());
22
23pub static AMOUNTS_BUCKETS_SATS: LazyLock<Vec<f64>> = LazyLock::new(|| {
24    vec![
25        0.0,
26        0.1,
27        1.0,
28        10.0,
29        100.0,
30        1000.0,
31        10000.0,
32        100_000.0,
33        1_000_000.0,
34        10_000_000.0,
35        100_000_000.0,
36    ]
37});
38
39async fn get_metrics() -> (StatusCode, String) {
40    let metric_families = REGISTRY.gather();
41    let result = || -> anyhow::Result<String> {
42        let mut buffer = Vec::new();
43        let encoder = TextEncoder::new();
44        encoder.encode(&metric_families, &mut buffer)?;
45        Ok(String::from_utf8(buffer)?)
46    };
47    match result() {
48        Ok(result) => (StatusCode::OK, result),
49        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, format!("{e:?}")),
50    }
51}
52
53pub async fn run_api_server(
54    bind_address: SocketAddr,
55    task_group: TaskGroup,
56) -> anyhow::Result<TaskShutdownToken> {
57    let app = Router::new().route("/metrics", get(get_metrics));
58    let listener = TcpListener::bind(bind_address).await?;
59    let serve = axum::serve(listener, app.into_make_service());
60
61    let handle = task_group.make_handle();
62    let shutdown_rx = handle.make_shutdown_rx();
63    task_group.spawn("Metrics Api", |_| async {
64        let graceful = serve.with_graceful_shutdown(async {
65            shutdown_rx.await;
66        });
67
68        if let Err(e) = graceful.await {
69            error!("Error shutting down metrics api: {e:?}");
70        }
71    });
72    let shutdown_receiver = handle.make_shutdown_rx();
73
74    Ok(shutdown_receiver)
75}