Spaces:
Sleeping
Sleeping
| """ | |
| middleware/logging_middleware.py — Structured request/response logging. | |
| Attaches a trace_id to every request, logs timing, method, path, status. | |
| """ | |
| from __future__ import annotations | |
| import time | |
| import uuid | |
| from typing import Callable | |
| from fastapi import Request, Response | |
| from starlette.middleware.base import BaseHTTPMiddleware | |
| from observability.logger import audit, get_logger, log_system_event | |
| log = get_logger("http") | |
| class RequestLoggingMiddleware(BaseHTTPMiddleware): | |
| async def dispatch(self, request: Request, call_next: Callable) -> Response: | |
| trace_id = str(uuid.uuid4())[:8] | |
| request.state.trace_id = trace_id | |
| start = time.perf_counter() | |
| log_system_event( | |
| level="info", | |
| message=f"API Request: {request.method} {request.url.path}", | |
| source="gateway", | |
| payload={"trace_id": trace_id, "query": str(request.url.query)} | |
| ) | |
| response = await call_next(request) | |
| duration_ms = (time.perf_counter() - start) * 1000 | |
| log_system_event( | |
| level="info" if response.status_code < 400 else "error", | |
| message=f"API Response: {response.status_code} ({duration_ms:.1f}ms)", | |
| source="gateway", | |
| payload={"trace_id": trace_id, "status": response.status_code, "latency_ms": round(duration_ms, 2)} | |
| ) | |
| response.headers["X-Trace-Id"] = trace_id | |
| response.headers["X-Response-Time"] = f"{duration_ms:.1f}ms" | |
| # Audit slow requests | |
| if duration_ms > 200: | |
| await audit( | |
| "slow_request", | |
| payload={ | |
| "path": request.url.path, | |
| "duration_ms": round(duration_ms, 2), | |
| "trace_id": trace_id, | |
| }, | |
| level="warning", | |
| ) | |
| return response | |