mirror of
https://github.com/tokio-rs/axum.git
synced 2025-10-02 15:24:54 +00:00
97 lines
2.6 KiB
Rust
97 lines
2.6 KiB
Rust
//! Someday tower-http will hopefully have a metrics middleware, until then you can track
|
|
//! metrics like this.
|
|
//!
|
|
//! Run with
|
|
//!
|
|
//! ```not_rust
|
|
//! cargo run -p example-prometheus-metrics
|
|
//! ```
|
|
|
|
use axum::{
|
|
extract::MatchedPath,
|
|
http::Request,
|
|
middleware::{self, Next},
|
|
response::IntoResponse,
|
|
routing::get,
|
|
Router,
|
|
};
|
|
use metrics_exporter_prometheus::{Matcher, PrometheusBuilder, PrometheusHandle};
|
|
use std::{
|
|
future::ready,
|
|
net::SocketAddr,
|
|
time::{Duration, Instant},
|
|
};
|
|
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
tracing_subscriber::registry()
|
|
.with(tracing_subscriber::EnvFilter::new(
|
|
std::env::var("RUST_LOG")
|
|
.unwrap_or_else(|_| "example_todos=debug,tower_http=debug".into()),
|
|
))
|
|
.with(tracing_subscriber::fmt::layer())
|
|
.init();
|
|
|
|
let recorder_handle = setup_metrics_recorder();
|
|
|
|
let app = Router::new()
|
|
.route("/fast", get(|| async {}))
|
|
.route(
|
|
"/slow",
|
|
get(|| async {
|
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
}),
|
|
)
|
|
.route("/metrics", get(move || ready(recorder_handle.render())))
|
|
.route_layer(middleware::from_fn(track_metrics));
|
|
|
|
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
|
tracing::debug!("listening on {}", addr);
|
|
axum::Server::bind(&addr)
|
|
.serve(app.into_make_service())
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
fn setup_metrics_recorder() -> PrometheusHandle {
|
|
const EXPONENTIAL_SECONDS: &[f64] = &[
|
|
0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0,
|
|
];
|
|
|
|
PrometheusBuilder::new()
|
|
.set_buckets_for_metric(
|
|
Matcher::Full("http_requests_duration_seconds".to_string()),
|
|
EXPONENTIAL_SECONDS,
|
|
)
|
|
.unwrap()
|
|
.install_recorder()
|
|
.unwrap()
|
|
}
|
|
|
|
async fn track_metrics<B>(req: Request<B>, next: Next<B>) -> impl IntoResponse {
|
|
let start = Instant::now();
|
|
let path = if let Some(matched_path) = req.extensions().get::<MatchedPath>() {
|
|
matched_path.as_str().to_owned()
|
|
} else {
|
|
req.uri().path().to_owned()
|
|
};
|
|
let method = req.method().clone();
|
|
|
|
let response = next.run(req).await;
|
|
|
|
let latency = start.elapsed().as_secs_f64();
|
|
let status = response.status().as_u16().to_string();
|
|
|
|
let labels = [
|
|
("method", method.to_string()),
|
|
("path", path),
|
|
("status", status),
|
|
];
|
|
|
|
metrics::increment_counter!("http_requests_total", &labels);
|
|
metrics::histogram!("http_requests_duration_seconds", latency, &labels);
|
|
|
|
response
|
|
}
|