Spaces:
Running
Running
| // 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 | |
| } | |
| mod tests { | |
| fn test_logging_levels() { | |
| // Integration tests would verify logging output | |
| // This is a placeholder for middleware verification | |
| } | |
| } | |