File size: 3,133 Bytes
c33971d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// Comprehensive request/response logging middleware
use axum::{extract::Request, middleware::Next, response::Response};
use std::time::Instant;
use uuid::Uuid;

use crate::interfaces::http::api::AppState;

/// Enhanced request logging with performance metrics
pub async fn request_response_logging_middleware(
    axum::extract::State(_state): axum::extract::State<AppState>,
    mut req: Request,
    next: Next,
) -> Response {
    let request_id = Uuid::new_v4().to_string();
    let method = req.method().to_string();
    let path = req.uri().path().to_string();
    let query = req.uri().query().map(|q| q.to_string());

    // Extract client IP
    let client_ip = req
        .headers()
        .get("x-forwarded-for")
        .and_then(|h| h.to_str().ok())
        .unwrap_or("unknown")
        .to_string();

    // Log request start
    tracing::info!(
        request_id = %request_id,
        method = %method,
        path = %path,
        client_ip = %client_ip,
        query = ?query,
        "Request started"
    );

    // Track timing
    let start_time = Instant::now();

    // Add request ID to extensions
    req.extensions_mut().insert(request_id.clone());

    // Call next middleware
    let mut response = next.run(req).await;

    let duration = start_time.elapsed();
    let status = response.status();
    let duration_ms = duration.as_millis() as u64;

    // Log based on status and duration
    let log_level = match (
        status.is_client_error(),
        status.is_server_error(),
        duration_ms > 1000,
    ) {
        (true, _, _) => "warn",  // Client errors
        (_, true, _) => "error", // Server errors
        (_, _, true) => "warn",  // Slow requests
        _ => "info",             // Normal requests
    };

    match log_level {
        "error" => {
            tracing::error!(
                request_id = %request_id,
                method = %method,
                path = %path,
                status = %status.as_u16(),
                duration_ms = duration_ms,
                "Request failed"
            );
        }
        "warn" => {
            tracing::warn!(
                request_id = %request_id,
                method = %method,
                path = %path,
                status = %status.as_u16(),
                duration_ms = duration_ms,
                "Request slow or error"
            );
        }
        _ => {
            tracing::info!(
                request_id = %request_id,
                method = %method,
                path = %path,
                status = %status.as_u16(),
                duration_ms = duration_ms,
                "Request completed"
            );
        }
    }

    // Add timing header
    if let Ok(header_value) = axum::http::HeaderValue::from_str(&duration_ms.to_string()) {
        response
            .headers_mut()
            .insert("x-response-time-ms", header_value);
    }

    response
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_logging_levels() {
        // Integration tests would verify logging output
        // This is a placeholder for middleware verification
    }
}