metrics_example/
http_service.rs1use std::{
22 net::SocketAddr,
23 sync::{Arc, Mutex},
24};
25
26use axum::{extract::State, http::StatusCode, response::IntoResponse, routing::get, Router};
27use prometheus_client::{encoding::text::encode, registry::Registry};
28use tokio::net::TcpListener;
29
30const METRICS_CONTENT_TYPE: &str = "application/openmetrics-text;charset=utf-8;version=1.0.0";
31
32pub(crate) async fn metrics_server(registry: Registry) -> Result<(), std::io::Error> {
33 let addr: SocketAddr = ([127, 0, 0, 1], 0).into();
35 let service = MetricService::new(registry);
36 let server = Router::new()
37 .route("/metrics", get(respond_with_metrics))
38 .with_state(service);
39 let tcp_listener = TcpListener::bind(addr).await?;
40 let local_addr = tcp_listener.local_addr()?;
41 tracing::info!(metrics_server=%format!("http://{}/metrics", local_addr));
42 axum::serve(tcp_listener, server.into_make_service()).await?;
43 Ok(())
44}
45
46#[derive(Clone)]
47pub(crate) struct MetricService {
48 reg: Arc<Mutex<Registry>>,
49}
50
51async fn respond_with_metrics(state: State<MetricService>) -> impl IntoResponse {
52 let mut sink = String::new();
53 let reg = state.get_reg();
54 encode(&mut sink, ®.lock().unwrap()).unwrap();
55
56 (
57 StatusCode::OK,
58 [(axum::http::header::CONTENT_TYPE, METRICS_CONTENT_TYPE)],
59 sink,
60 )
61}
62
63type SharedRegistry = Arc<Mutex<Registry>>;
64
65impl MetricService {
66 fn new(registry: Registry) -> Self {
67 Self {
68 reg: Arc::new(Mutex::new(registry)),
69 }
70 }
71
72 fn get_reg(&self) -> SharedRegistry {
73 Arc::clone(&self.reg)
74 }
75}