mirror of
https://github.com/tokio-rs/axum.git
synced 2025-01-19 23:33:46 +01:00
Add metrics example (#671)
This commit is contained in:
parent
031e0fd472
commit
5698fb8be9
3 changed files with 113 additions and 1 deletions
9
.github/workflows/CI.yml
vendored
9
.github/workflows/CI.yml
vendored
|
@ -166,4 +166,11 @@ jobs:
|
||||||
CC: clang
|
CC: clang
|
||||||
with:
|
with:
|
||||||
command: check
|
command: check
|
||||||
args: --all --all-targets --all-features --target armv5te-unknown-linux-musleabi
|
args: >
|
||||||
|
--all-targets
|
||||||
|
--all-features
|
||||||
|
-p axum
|
||||||
|
-p axum-core
|
||||||
|
-p axum-extra
|
||||||
|
-p axum-debug
|
||||||
|
--target armv5te-unknown-linux-musleabi
|
||||||
|
|
14
examples/prometheus-metrics/Cargo.toml
Normal file
14
examples/prometheus-metrics/Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
[package]
|
||||||
|
name = "example-prometheus-metrics"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
axum = { path = "../../axum" }
|
||||||
|
axum-extra = { path = "../../axum-extra" }
|
||||||
|
metrics = "0.17"
|
||||||
|
metrics-exporter-prometheus = "0.7"
|
||||||
|
tokio = { version = "1.0", features = ["full"] }
|
||||||
|
tracing = "0.1"
|
||||||
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
91
examples/prometheus-metrics/src/main.rs
Normal file
91
examples/prometheus-metrics/src/main.rs
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
//! 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, response::IntoResponse, routing::get, Router};
|
||||||
|
use axum_extra::middleware::{self, Next};
|
||||||
|
use metrics_exporter_prometheus::{Matcher, PrometheusBuilder, PrometheusHandle};
|
||||||
|
use std::{
|
||||||
|
future::ready,
|
||||||
|
net::SocketAddr,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
// Set the RUST_LOG, if it hasn't been explicitly defined
|
||||||
|
if std::env::var_os("RUST_LOG").is_none() {
|
||||||
|
std::env::set_var("RUST_LOG", "example_todos=debug,tower_http=debug")
|
||||||
|
}
|
||||||
|
tracing_subscriber::fmt::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,
|
||||||
|
];
|
||||||
|
|
||||||
|
let recorder = PrometheusBuilder::new()
|
||||||
|
.set_buckets_for_metric(
|
||||||
|
Matcher::Full("http_requests_duration_seconds".to_string()),
|
||||||
|
EXPONENTIAL_SECONDS,
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let recorder_handle = recorder.handle();
|
||||||
|
|
||||||
|
metrics::set_boxed_recorder(Box::new(recorder)).unwrap();
|
||||||
|
|
||||||
|
recorder_handle
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
Loading…
Reference in a new issue