//! Prometheus scrape endpoint. Counters are updated by the server crate as //! messages flow; table-size gauges are refreshed at scrape time. use axum::extract::State; use axum::http::{StatusCode, header}; use axum::response::IntoResponse; use bgp_server::metrics::{ LOCAL_ROUTES, RIB_ATTRIBUTE_SETS, RIB_JOURNAL_ENTRIES, RIB_PREFIXES, RIB_ROUTES, }; use prometheus::{Encoder, TextEncoder}; use crate::AppState; pub async fn metricz(State(state): State) -> impl IntoResponse { // A scrape that cannot read a table fails. Serving the last known // size would hide that the daemon is broken from the one system whose // job is to notice. let gauge = |count: usize| i64::try_from(count).unwrap_or(i64::MAX); match state.local_rib.len().await { Ok(local) => LOCAL_ROUTES.set(gauge(local)), Err(e) => return (StatusCode::SERVICE_UNAVAILABLE, e.to_string()).into_response(), } RIB_ROUTES.set(gauge(state.rib.len())); RIB_PREFIXES.set(gauge(state.rib.prefix_count())); RIB_ATTRIBUTE_SETS.set(gauge(state.rib.attrs().len())); RIB_JOURNAL_ENTRIES.set(gauge(state.rib.journal_len())); let mut buf = Vec::new(); match TextEncoder::new().encode(&prometheus::gather(), &mut buf) { Ok(()) => ( StatusCode::OK, [(header::CONTENT_TYPE, "text/plain; version=0.0.4")], buf, ) .into_response(), Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(), } }