tempo_commonware_node/metrics.rs
1use std::net::SocketAddr;
2
3use axum::{
4 Extension, Router,
5 body::Body,
6 http::{Response, StatusCode, header},
7 routing::get,
8};
9use commonware_runtime::{Handle, Metrics as _, Spawner as _, tokio::Context};
10use eyre::WrapErr as _;
11use tokio::net::TcpListener;
12
13/// Installs a metrics server so that commonware can publish its metrics.
14///
15/// This is lifted straight from [`commonware_runtime::tokio::telemetry::init`],
16/// because it also wants to install a tracing subscriber, which clashes with
17/// reth ethereum cli doing the same thing.
18pub fn install(context: Context, listen_addr: SocketAddr) -> Handle<eyre::Result<()>> {
19 context.spawn(move |context| async move {
20 // Create a tokio listener for the metrics server.
21 //
22 // We explicitly avoid using a runtime `Listener` because
23 // it will track bandwidth used for metrics and apply a policy
24 // for read/write timeouts fit for a p2p network.
25 let listener = TcpListener::bind(listen_addr)
26 .await
27 .wrap_err("failed to bind provided address")?;
28
29 // Create a router for the metrics server
30 let app = Router::new()
31 .route(
32 "/metrics",
33 get(|Extension(ctx): Extension<Context>| async move {
34 Response::builder()
35 .status(StatusCode::OK)
36 .header(header::CONTENT_TYPE, "text/plain; version=0.0.4")
37 .body(Body::from(ctx.encode()))
38 .expect("Failed to create response")
39 }),
40 )
41 .layer(Extension(context));
42
43 // Serve the metrics over HTTP.
44 //
45 // `serve` will spawn its own tasks using `tokio::spawn` (and there is no way to specify
46 // it to do otherwise). These tasks will not be tracked like metrics spawned using `Spawner`.
47 axum::serve(listener, app.into_make_service())
48 .await
49 .map_err(Into::into)
50 })
51}