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}