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