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 tokio::{net::TcpListener, task::JoinHandle};
11use tokio_util::sync::CancellationToken;
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 cancel: CancellationToken,
29}
30
31impl MetricsService {
32 pub fn new(args: MetricsArgs, registry: Registry, cancel: CancellationToken) -> Self {
38 Self {
39 addr: args.metrics_address,
40 registry,
41 cancel,
42 }
43 }
44
45 pub fn registry(&self) -> &Registry {
47 &self.registry
48 }
49
50 pub async fn run(self) -> anyhow::Result<JoinHandle<()>> {
52 let Self {
53 addr,
54 registry,
55 cancel,
56 } = self;
57
58 let listener = TcpListener::bind(&self.addr)
59 .await
60 .with_context(|| format!("Failed to bind metrics at {addr}"))?;
61
62 let app = Router::new()
63 .route("/metrics", get(metrics))
64 .layer(Extension(registry));
65
66 Ok(tokio::spawn(async move {
67 info!("Starting metrics service on {}", addr);
68 axum::serve(listener, app)
69 .with_graceful_shutdown(async move {
70 cancel.cancelled().await;
71 info!("Shutdown received, shutting down metrics service");
72 })
73 .await
74 .unwrap()
75 }))
76 }
77}
78
79impl Default for MetricsArgs {
80 fn default() -> Self {
81 Self {
82 metrics_address: "0.0.0.0:9184".parse().unwrap(),
83 }
84 }
85}
86
87pub fn uptime(version: &str) -> anyhow::Result<Box<dyn Collector>> {
89 let init = Instant::now();
90 let opts = prometheus::opts!("uptime", "how long the service has been running in seconds")
91 .variable_label("version");
92
93 let metric = move || init.elapsed().as_secs();
94 let uptime = ClosureMetric::new(opts, ValueType::Counter, metric, &[version])
95 .context("Failed to create uptime metric")?;
96
97 Ok(Box::new(uptime))
98}
99
100async fn metrics(Extension(registry): Extension<Registry>) -> (StatusCode, String) {
102 match TextEncoder.encode_to_string(®istry.gather()) {
103 Ok(s) => (StatusCode::OK, s),
104 Err(e) => (
105 StatusCode::INTERNAL_SERVER_ERROR,
106 format!("unable to encode metrics: {e}"),
107 ),
108 }
109}