libp2p_server/
http_service.rs

1// Copyright 2022 Protocol Labs.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the "Software"),
5// to deal in the Software without restriction, including without limitation
6// the rights to use, copy, modify, merge, publish, distribute, sublicense,
7// and/or sell copies of the Software, and to permit persons to whom the
8// Software is furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19// DEALINGS IN THE SOFTWARE.
20
21use 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";
31pub(crate) async fn metrics_server(
32    registry: Registry,
33    metrics_path: String,
34) -> Result<(), std::io::Error> {
35    // Serve on localhost.
36    let addr: SocketAddr = ([0, 0, 0, 0], 8888).into();
37    let service = MetricService::new(registry);
38    let server = Router::new()
39        .route(&metrics_path, get(respond_with_metrics))
40        .with_state(service);
41    let tcp_listener = TcpListener::bind(addr).await?;
42    let local_addr = tcp_listener.local_addr()?;
43    tracing::info!(metrics_server=%format!("http://{}{}", local_addr, metrics_path));
44    axum::serve(tcp_listener, server.into_make_service()).await?;
45    Ok(())
46}
47
48async fn respond_with_metrics(state: State<MetricService>) -> impl IntoResponse {
49    let mut sink = String::new();
50    let reg = state.get_reg();
51    encode(&mut sink, &reg.lock().unwrap()).unwrap();
52
53    (
54        StatusCode::OK,
55        [(axum::http::header::CONTENT_TYPE, METRICS_CONTENT_TYPE)],
56        sink,
57    )
58}
59
60#[derive(Clone)]
61pub(crate) struct MetricService {
62    reg: Arc<Mutex<Registry>>,
63}
64
65type SharedRegistry = Arc<Mutex<Registry>>;
66
67impl MetricService {
68    fn new(reg: Registry) -> Self {
69        Self {
70            reg: Arc::new(Mutex::new(reg)),
71        }
72    }
73
74    fn get_reg(&self) -> SharedRegistry {
75        Arc::clone(&self.reg)
76    }
77}