solution_challenge_backend / backend /observability.py
github-actions
Deploy to Hugging Face
c794b6b
Raw
History Blame Contribute Delete
1.55 kB
"""Request ID + structured access logging for production operations."""
from __future__ import annotations
import json
import logging
import time
import uuid
from typing import Callable
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response
logger = logging.getLogger("cepheus.access")
class RequestContextMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next: Callable) -> Response:
request_id = request.headers.get("X-Request-ID") or str(uuid.uuid4())
request.state.request_id = request_id
start = time.perf_counter()
status_code = 500
try:
response = await call_next(request)
status_code = response.status_code
response.headers["X-Request-ID"] = request_id
return response
finally:
duration_ms = round((time.perf_counter() - start) * 1000, 2)
log_entry = {
"request_id": request_id,
"method": request.method,
"path": request.url.path,
"status": status_code,
"duration_ms": duration_ms,
"client": request.client.host if request.client else None,
}
if status_code >= 500:
logger.error(json.dumps(log_entry))
elif status_code >= 400:
logger.warning(json.dumps(log_entry))
else:
logger.info(json.dumps(log_entry))