sui_indexer_alt_metrics/
lib.rs1use std::net::SocketAddr;
5use std::time::Instant;
6
7use anyhow::Context;
8use axum::Extension;
9use axum::Router;
10use axum::http::StatusCode;
11use axum::routing::get;
12use prometheus::Registry;
13use prometheus::TextEncoder;
14use prometheus::core::Collector;
15use prometheus_closure_metric::ClosureMetric;
16use prometheus_closure_metric::ValueType;
17use sui_futures::service::Service;
18use tokio::net::TcpListener;
19use tokio::sync::oneshot;
20use tracing::info;
21
22#[cfg(feature = "postgres")]
23pub mod db;
24
25#[derive(clap::Args, Debug, Clone)]
26pub struct MetricsArgs {
27 #[arg(long, default_value_t = Self::default().metrics_address)]
29 pub metrics_address: SocketAddr,
30}
31
32pub struct MetricsService {
35 addr: SocketAddr,
36 registry: Registry,
37}
38
39impl MetricsService {
40 pub fn new(args: MetricsArgs, registry: Registry) -> Self {
45 Self {
46 addr: args.metrics_address,
47 registry,
48 }
49 }
50
51 pub fn registry(&self) -> &Registry {
53 &self.registry
54 }
55
56 pub async fn run(self) -> anyhow::Result<Service> {
58 let Self { addr, registry } = self;
59
60 let listener = TcpListener::bind(&self.addr)
61 .await
62 .with_context(|| format!("Failed to bind metrics at {addr}"))?;
63
64 let app = Router::new()
65 .route("/metrics", get(metrics))
66 .layer(Extension(registry));
67
68 let (stx, srx) = oneshot::channel::<()>();
69 Ok(Service::new()
70 .with_shutdown_signal(async move {
71 let _ = stx.send(());
72 })
73 .spawn(async move {
74 info!("Starting metrics service on {addr}");
75 Ok(axum::serve(listener, app)
76 .with_graceful_shutdown(async move {
77 let _ = srx.await;
78 info!("Shutdown received, shutting down metrics service");
79 })
80 .await?)
81 }))
82 }
83}
84
85impl Default for MetricsArgs {
86 fn default() -> Self {
87 Self {
88 metrics_address: "0.0.0.0:9184".parse().unwrap(),
89 }
90 }
91}
92
93pub fn uptime(version: &str) -> anyhow::Result<Box<dyn Collector>> {
95 let init = Instant::now();
96 let opts = prometheus::opts!("uptime", "how long the service has been running in seconds")
97 .variable_label("version");
98
99 let metric = move || init.elapsed().as_secs();
100 let uptime = ClosureMetric::new(opts, ValueType::Counter, metric, &[version])
101 .context("Failed to create uptime metric")?;
102
103 Ok(Box::new(uptime))
104}
105
106async fn metrics(Extension(registry): Extension<Registry>) -> (StatusCode, String) {
108 match TextEncoder.encode_to_string(®istry.gather()) {
109 Ok(s) => (StatusCode::OK, s),
110 Err(e) => (
111 StatusCode::INTERNAL_SERVER_ERROR,
112 format!("unable to encode metrics: {e}"),
113 ),
114 }
115}