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