From eeae01887aa7bf5ab4dc4d9c5dc90e06c465accd Mon Sep 17 00:00:00 2001 From: tv Date: Sat, 24 Oct 2026 06:48:14 +0200 Subject: expose stats for prometheus to scrape --- src/api/stats.rs | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) (limited to 'src/api/stats.rs') diff --git a/src/api/stats.rs b/src/api/stats.rs index 3822b5f..b39e054 100644 --- a/src/api/stats.rs +++ b/src/api/stats.rs @@ -1,6 +1,14 @@ -//! /admin/stats: how this node is doing, in numbers. +//! /admin/stats: how this node is doing, in numbers. JSON, or the Prometheus text +//! format when the client asks for it (`prometheus`). -use axum::{Json, extract::State}; +mod prometheus; + +use axum::{ + Json, + extract::State, + http::{HeaderMap, header}, + response::{IntoResponse, Response}, +}; use serde::Serialize; use super::peers::EpochInfo; @@ -107,7 +115,20 @@ struct StabilityStats { } /// `GET /admin/stats`. -pub(crate) async fn get_stats(State(state): State) -> Json { +pub(crate) async fn get_stats(State(state): State, headers: HeaderMap) -> Response { + let stats = collect(&state).await; + let accept = headers + .get(header::ACCEPT) + .and_then(|value| value.to_str().ok()); + if prometheus::wanted(accept) { + let body = prometheus::render(&state, &stats).await; + ([(header::CONTENT_TYPE, prometheus::CONTENT_TYPE)], body).into_response() + } else { + Json(stats).into_response() + } +} + +async fn collect(state: &AppState) -> StatsResponse { let node = state.stats.snapshot(); let (heads, tier) = { @@ -117,7 +138,7 @@ pub(crate) async fn get_stats(State(state): State) -> Json = state.peer_states.read().await.values().cloned().collect(); @@ -125,7 +146,7 @@ pub(crate) async fn get_stats(State(state): State) -> Json) -> Json) -> Json