ibc_telemetry/
server.rs

1use std::error::Error;
2use std::net::SocketAddr;
3use std::sync::Arc;
4
5use axum::extract::Query;
6use axum::response::IntoResponse;
7use axum::routing::get;
8use axum::{Extension, Router};
9
10use prometheus::{Encoder, TextEncoder};
11
12use crate::encoder::JsonEncoder;
13use crate::state::TelemetryState;
14
15#[derive(Copy, Clone, Debug, Default, serde::Deserialize)]
16enum Format {
17    #[serde(rename = "text")]
18    #[default]
19    Text,
20
21    #[serde(rename = "json")]
22    Json,
23}
24
25#[derive(Copy, Clone, Debug, serde::Deserialize)]
26struct Metrics {
27    format: Option<Format>,
28}
29
30pub async fn listen(
31    addr: SocketAddr,
32    state: Arc<TelemetryState>,
33) -> Result<(), Box<dyn Error + Send + Sync>> {
34    let app = Router::new()
35        .route("/metrics", get(get_metrics))
36        .layer(Extension(state));
37
38    axum::Server::bind(&addr)
39        .serve(app.into_make_service())
40        .await?;
41
42    Ok(())
43}
44
45async fn get_metrics(
46    Extension(state): Extension<Arc<TelemetryState>>,
47    Query(query): Query<Metrics>,
48) -> impl IntoResponse {
49    match query.format.unwrap_or_default() {
50        Format::Text => {
51            let encoder = TextEncoder::new();
52            let mut buffer = Vec::new();
53            encoder.encode(&state.gather(), &mut buffer).unwrap();
54
55            ([("content-type", "text/plain; charset=utf-8")], buffer)
56        }
57        Format::Json => {
58            let encoder = JsonEncoder::new();
59            let mut buffer = Vec::new();
60            encoder.encode(&state.gather(), &mut buffer).unwrap();
61
62            (
63                [("content-type", "application/javascript; charset=utf-8")],
64                buffer,
65            )
66        }
67    }
68}