use std::time::{Duration, Instant};
use axum::Router;
use axum::extract::{MatchedPath, Request};
use axum::middleware::Next;
use axum::response::Response;
use axum::routing::get;
use ::metrics::{counter, gauge, histogram};
use ::metrics_exporter_prometheus::{Matcher, PrometheusBuilder, PrometheusHandle};
use sqlx::PgPool;
const LATENCY_BUCKETS: &[f64] = &[
0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0,
];
pub fn init_metrics() -> eyre::Result<PrometheusHandle> {
let handle = PrometheusBuilder::new()
.set_buckets_for_metric(
Matcher::Full("http_request_duration_seconds".to_owned()),
LATENCY_BUCKETS,
)?
.install_recorder()?;
Ok(handle)
}
pub fn metrics_router(handle: PrometheusHandle) -> Router {
Router::new().route(
"/metrics",
get(move || {
let handle = handle.clone();
async move { handle.render() }
}),
)
}
pub async fn track_metrics(req: Request, next: Next) -> Response {
let start = Instant::now();
let method = req.method().as_str().to_owned();
let path = req
.extensions()
.get::<MatchedPath>()
.map(|p| p.as_str().to_owned())
.unwrap_or_else(|| "unknown".to_owned());
gauge!("http_requests_in_flight").increment(1.0);
let response = next.run(req).await;
gauge!("http_requests_in_flight").decrement(1.0);
let status = response.status().as_u16().to_string();
let latency = start.elapsed().as_secs_f64();
counter!(
"http_requests_total",
"method" => method.clone(),
"path" => path.clone(),
"status" => status.clone(),
)
.increment(1);
histogram!(
"http_request_duration_seconds",
"method" => method,
"path" => path,
"status" => status,
)
.record(latency);
response
}
pub fn spawn_pool_metrics(pool: PgPool, interval: Duration) -> tokio::task::JoinHandle<()> {
tokio::spawn(async move {
let mut ticker = tokio::time::interval(interval);
loop {
ticker.tick().await;
let total = pool.size() as f64;
let idle = pool.num_idle() as f64;
gauge!("db_pool_connections", "state" => "total").set(total);
gauge!("db_pool_connections", "state" => "idle").set(idle);
gauge!("db_pool_connections", "state" => "active").set((total - idle).max(0.0));
}
})
}