1use std::net::SocketAddr;
2
3use axum::{
4 extract::State,
5 http::{Response, StatusCode},
6 routing::get,
7 serve::WithGracefulShutdown,
8 Router,
9};
10use lazy_static::lazy_static;
11use opentelemetry::global;
12use opentelemetry_sdk::metrics::SdkMeterProvider;
13use prometheus::{Encoder as _, Registry, TextEncoder};
14use tokio_util::sync::CancellationToken;
15use tracing::{debug, info};
16
17use crate::{
18 constant::{AGGLAYER_KERNEL_OTEL_SCOPE_NAME, AGGLAYER_RPC_OTEL_SCOPE_NAME},
19 error::MetricsError,
20};
21
22mod constant;
23mod error;
24
25pub use error::Error;
26pub use opentelemetry::KeyValue;
27
28lazy_static! {
29 pub static ref SEND_TX: opentelemetry::metrics::Counter<u64> = global::meter(AGGLAYER_RPC_OTEL_SCOPE_NAME)
32 .u64_counter("send_tx")
33 .with_description("Number of transactions received on the RPC")
34 .build();
35
36 pub static ref VERIFY_ZKP: opentelemetry::metrics::Counter<u64> = global::meter(AGGLAYER_KERNEL_OTEL_SCOPE_NAME)
37 .u64_counter("verify_zkp")
38 .with_description("Number of ZKP verifications")
39 .build();
40
41 pub static ref VERIFY_SIGNATURE: opentelemetry::metrics::Counter<u64> = global::meter(AGGLAYER_KERNEL_OTEL_SCOPE_NAME)
42 .u64_counter("verify_signature")
43 .with_description("Number of signature verifications")
44 .build();
45
46 pub static ref CHECK_TX: opentelemetry::metrics::Counter<u64> = global::meter(AGGLAYER_KERNEL_OTEL_SCOPE_NAME)
47 .u64_counter("check_tx")
48 .with_description("Number of transactions checked")
49 .build();
50
51 pub static ref EXECUTE: opentelemetry::metrics::Counter<u64> = global::meter(AGGLAYER_KERNEL_OTEL_SCOPE_NAME)
52 .u64_counter("execute")
53 .with_description("Number of transactions executed")
54 .build();
55
56 pub static ref SETTLE: opentelemetry::metrics::Counter<u64> = global::meter(AGGLAYER_KERNEL_OTEL_SCOPE_NAME)
57 .u64_counter("settle")
58 .with_description("Number of transactions settled")
59 .build();
60}
61
62pub mod prover {
63 use lazy_static::lazy_static;
64 use opentelemetry::global;
65
66 use crate::constant::AGGLAYER_PROVER_RPC_OTEL_SCOPE_NAME;
67
68 lazy_static! {
69 pub static ref PROVING_REQUEST_RECV: opentelemetry::metrics::Counter<u64> =
70 global::meter(AGGLAYER_PROVER_RPC_OTEL_SCOPE_NAME)
71 .u64_counter("proving_request_recv")
72 .with_description("Number of proving request received")
73 .build();
74 pub static ref PROVING_REQUEST_SUCCEEDED: opentelemetry::metrics::Counter<u64> =
75 global::meter(AGGLAYER_PROVER_RPC_OTEL_SCOPE_NAME)
76 .u64_counter("proving_request_succeeded")
77 .with_description("Number of proving request that succeeded")
78 .build();
79 pub static ref PROVING_REQUEST_FAILED: opentelemetry::metrics::Counter<u64> =
80 global::meter(AGGLAYER_PROVER_RPC_OTEL_SCOPE_NAME)
81 .u64_counter("proving_request_failed")
82 .with_description("Number of proving request that failed")
83 .build();
84 pub static ref PROVING_FALLBACK_TRIGGERED: opentelemetry::metrics::Counter<u64> =
85 global::meter(AGGLAYER_PROVER_RPC_OTEL_SCOPE_NAME)
86 .u64_counter("proving_fallback_triggered")
87 .with_description("Number of proving fallback triggered")
88 .build();
89 }
90}
91
92pub struct ServerBuilder {}
93
94#[buildstructor::buildstructor]
95impl ServerBuilder {
96 #[builder(entry = "builder", exit = "build", visibility = "pub")]
136 pub async fn serve(
137 addr: SocketAddr,
138 registry: Option<Registry>,
139 cancellation_token: CancellationToken,
140 ) -> Result<
141 WithGracefulShutdown<
142 axum::routing::IntoMakeService<Router>,
143 axum::Router,
144 impl futures::Future<Output = ()>,
145 >,
146 Error,
147 > {
148 let registry = registry.unwrap_or_default();
149 let _ = Self::init_meter_provider(®istry);
150
151 let app = Router::new()
152 .route(
153 "/metrics",
154 get(|State(registry): State<prometheus::Registry>| async move {
155 match Self::gather_metrics(®istry) {
156 Ok(metrics) => Response::new(metrics),
157 Err(error) => Response::builder()
158 .status(StatusCode::INTERNAL_SERVER_ERROR)
159 .body(error.to_string())
160 .unwrap(),
161 }
162 }),
163 )
164 .with_state(registry);
165
166 info!("Starting metrics server on {}", addr);
167
168 let listener = tokio::net::TcpListener::bind(addr).await?;
169
170 Ok(axum::serve(listener, app.into_make_service())
171 .with_graceful_shutdown(shutdown_signal(cancellation_token)))
172 }
173
174 fn init_meter_provider(registry: &Registry) -> Result<(), MetricsError> {
175 let exporter = opentelemetry_prometheus::exporter()
177 .with_registry(registry.clone())
178 .build()
179 .unwrap();
180
181 let provider = SdkMeterProvider::builder().with_reader(exporter).build();
183
184 global::set_meter_provider(provider);
185 Ok(())
186 }
187
188 fn gather_metrics(registry: &prometheus::Registry) -> Result<String, MetricsError> {
189 let encoder = TextEncoder::new();
191 let metric_families = registry.gather();
192 let mut result = Vec::new();
193 encoder.encode(&metric_families, &mut result)?;
194
195 Ok(String::from_utf8(result)?)
196 }
197}
198
199async fn shutdown_signal(cancellation: CancellationToken) {
200 tokio::select! {
201 _ = cancellation.cancelled() => {
202 debug!("Shutting down metrics server...");
203 },
204 }
205}