# ============================================ # Projet : API-KA # Fichier : src/api/middleware/logging.py # Node : m3u96b # Author : Simon-Pierre Boucher # Contact : contact@spboucher.ai # Date : 2026-08-16 # ============================================ """Middleware de journalisation : chaque requête HTTP est loggée en JSON.""" from __future__ import annotations import datetime import time from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint from starlette.requests import Request from starlette.responses import Response from src.api import reqstats from src.utils.logger import get_logger class RequestLoggingMiddleware(BaseHTTPMiddleware): """Logge méthode, chemin, statut, durée et IP client de chaque requête.""" async def dispatch( self, request: Request, call_next: RequestResponseEndpoint ) -> Response: start = time.perf_counter() response = await call_next(request) duration_ms = round((time.perf_counter() - start) * 1000, 2) # Journalisation légère pour /stats : simple append en mémoire (O(1), # aucune I/O ici) — la persistance se fait en lot par la tâche de fond. try: reqstats.record( ts=datetime.datetime.now(tz=datetime.UTC), method=request.method, path=request.url.path, status=response.status_code, duration_ms=duration_ms, ) except Exception: # pragma: no cover — jamais bloquant pour l'API pass get_logger("apika.api").info( "http_request", extra={ "method": request.method, "path": request.url.path, "status_code": response.status_code, "duration_ms": duration_ms, "client_ip": request.client.host if request.client else "unknown", }, ) return response