use std::sync::OnceLock;
use metrics_exporter_prometheus::{PrometheusBuilder, PrometheusHandle};
static HANDLE: OnceLock<PrometheusHandle> = OnceLock::new();
pub fn init_metrics() -> PrometheusHandle {
HANDLE
.get_or_init(|| {
let handle = PrometheusBuilder::new()
.install_recorder()
.expect("Prometheus metrics recorder");
metrics::describe_counter!(
"http_requests_total",
"Total number of HTTP requests handled."
);
metrics::describe_histogram!(
"http_request_duration_seconds",
"HTTP request duration in seconds."
);
metrics::describe_gauge!(
"http_requests_in_flight",
"Number of HTTP requests currently being processed."
);
handle
})
.clone()
}
pub fn record_request(route: &str, method: &str, status: u16, duration_secs: f64) {
let route = if route.is_empty() { "plugin" } else { route };
let status = status.to_string();
metrics::counter!(
"http_requests_total",
"method" => method.to_owned(),
"route" => route.to_owned(),
"status" => status.clone(),
)
.increment(1);
metrics::histogram!(
"http_request_duration_seconds",
"method" => method.to_owned(),
"route" => route.to_owned(),
"status" => status,
)
.record(duration_secs);
}
pub fn metrics_route_handler(
handle: PrometheusHandle,
) -> impl Fn(
crate::web::context::RequestContext,
) -> futures::future::BoxFuture<'static, axum::response::Response>
+ Send
+ Sync
+ Clone
+ 'static {
move |_ctx| {
let body = handle.render();
Box::pin(async move {
axum::response::Response::builder()
.status(200)
.header("Content-Type", "text/plain; version=0.0.4; charset=utf-8")
.body(axum::body::Body::from(body))
.unwrap()
})
}
}