use std::sync::OnceLock;
use metrics_exporter_prometheus::{PrometheusBuilder, PrometheusHandle};
use crate::infrastructure::http::axum_adapter::PjsError;
static RECORDER: OnceLock<PrometheusHandle> = OnceLock::new();
pub fn install_global_recorder() -> Result<PrometheusHandle, PjsError> {
RECORDER
.get_or_try_init(|| {
PrometheusBuilder::new().install_recorder().map_err(|e| {
log_recorder_build_failure_once(&e);
PjsError::HttpError("failed to install metrics recorder".to_string())
})
})
.cloned()
}
fn log_recorder_build_failure_once(e: &impl std::fmt::Display) {
static WARNED: std::sync::Once = std::sync::Once::new();
WARNED.call_once(|| {
tracing::error!(error = %e, "failed to install Prometheus recorder (logged once)");
});
}
pub async fn metrics_handler() -> impl axum::response::IntoResponse {
match install_global_recorder() {
Ok(handle) => {
let body = handle.render();
axum::response::Response::builder()
.status(axum::http::StatusCode::OK)
.header(
axum::http::header::CONTENT_TYPE,
"text/plain; version=0.0.4; charset=utf-8",
)
.body(axum::body::Body::from(body))
.unwrap_or_else(|_| {
axum::response::Response::builder()
.status(axum::http::StatusCode::INTERNAL_SERVER_ERROR)
.body(axum::body::Body::empty())
.expect("infallible empty response")
})
}
Err(_) => axum::response::Response::builder()
.status(axum::http::StatusCode::INTERNAL_SERVER_ERROR)
.body(axum::body::Body::from("metrics recorder unavailable"))
.expect("infallible error response"),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn install_is_idempotent() {
let h1 = install_global_recorder().expect("first install");
let h2 = install_global_recorder().expect("second install");
assert_eq!(h1.render(), h2.render());
}
}