lmrc_http_common/middleware/
logging.rs

1//! Request logging middleware
2
3use axum::{
4    extract::Request,
5    middleware::Next,
6    response::Response,
7};
8use std::time::Instant;
9use tracing::{info, info_span, Instrument};
10
11/// Middleware layer for request logging
12pub struct LoggingLayer;
13
14impl LoggingLayer {
15    pub fn new() -> Self {
16        Self
17    }
18}
19
20impl Default for LoggingLayer {
21    fn default() -> Self {
22        Self::new()
23    }
24}
25
26/// Middleware function that logs request details
27pub async fn log_request(req: Request, next: Next) -> Response {
28    let method = req.method().clone();
29    let uri = req.uri().clone();
30    let version = req.version();
31
32    // Get request ID from extensions if available
33    let request_id = req
34        .extensions()
35        .get::<super::request_id::RequestId>()
36        .map(|id| id.as_str().to_string())
37        .unwrap_or_else(|| "unknown".to_string());
38
39    let span = info_span!(
40        "http_request",
41        method = %method,
42        uri = %uri,
43        version = ?version,
44        request_id = %request_id,
45    );
46
47    async move {
48        let start = Instant::now();
49
50        info!("request started");
51
52        let response = next.run(req).await;
53
54        let latency = start.elapsed();
55        let status = response.status();
56
57        info!(
58            status = %status.as_u16(),
59            latency_ms = %latency.as_millis(),
60            "request completed"
61        );
62
63        response
64    }
65    .instrument(span)
66    .await
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72    use axum::{
73        body::Body,
74        http::{Request, StatusCode},
75        middleware,
76        routing::get,
77        Router,
78    };
79    use tower::ServiceExt;
80
81    #[tokio::test]
82    async fn test_logging_middleware() {
83        async fn handler() -> &'static str {
84            "ok"
85        }
86
87        let app = Router::new()
88            .route("/", get(handler))
89            .layer(middleware::from_fn(log_request));
90
91        let request = Request::builder().uri("/").body(Body::empty()).unwrap();
92
93        let response = app.oneshot(request).await.unwrap();
94
95        assert_eq!(response.status(), StatusCode::OK);
96    }
97}