feat(telemetry): add axum_middleware

This commit is contained in:
2026-01-30 16:28:29 +08:00
parent 4db955113c
commit 9465892cc6
5 changed files with 139 additions and 1 deletions

72
src/axum_middleware.rs Normal file
View File

@@ -0,0 +1,72 @@
#[cfg(all(feature = "response", feature = "telemetry"))]
use axum::{
extract::{ConnectInfo, MatchedPath, Request},
middleware::Next,
response::Response,
};
#[cfg(all(feature = "response", feature = "telemetry"))]
use std::net::SocketAddr;
#[cfg(all(feature = "response", feature = "telemetry"))]
use tracing::field;
#[cfg(all(feature = "response", feature = "telemetry"))]
fn first_forwarded_for(req: &Request) -> Option<String> {
let raw = req.headers().get("x-forwarded-for")?.to_str().ok()?;
raw.split(',')
.next()
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.map(|s| s.to_string())
}
#[cfg(all(feature = "response", feature = "telemetry"))]
fn connect_info_ip(req: &Request) -> Option<String> {
req.extensions()
.get::<ConnectInfo<SocketAddr>>()
.map(|ci| ci.0.ip().to_string())
}
#[cfg(all(feature = "response", feature = "telemetry"))]
fn matched_route(req: &Request) -> Option<String> {
req.extensions()
.get::<MatchedPath>()
.map(|m| m.as_str().to_string())
}
#[cfg(all(feature = "response", feature = "telemetry"))]
fn request_id(req: &Request) -> Option<String> {
req.headers()
.get("x-request-id")
.and_then(|v| v.to_str().ok())
.map(|s| s.to_string())
}
#[cfg(all(feature = "response", feature = "telemetry"))]
pub async fn trace_http_request(req: Request, next: Next) -> Response {
let method = req.method().to_string();
let path = req.uri().path().to_string();
let route = matched_route(&req).unwrap_or_else(|| path.clone());
let client_ip = first_forwarded_for(&req).or_else(|| connect_info_ip(&req));
let req_id = request_id(&req);
let span = tracing::info_span!(
"http.request",
http.method = %method,
http.path = %path,
http.route = %route,
client.ip = %client_ip.clone().unwrap_or_else(|| "unknown".into()),
request_id = %req_id.clone().unwrap_or_else(|| "unknown".into()),
tenant_id = field::Empty,
user_id = field::Empty,
status = field::Empty,
);
let _guard = span.enter();
let resp = next.run(req).await;
let status = resp.status().as_u16();
tracing::Span::current().record("status", status);
resp
}