import logging from contextvars import ContextVar from app.config import get_settings # RGPD — RÈGLE STRICTE # Ne jamais journaliser de données nominatives : pas de nom, prénom, email ni # téléphone dans les logs. On journalise des identifiants (UUID/entier) et des # codes. Toute nouvelle instruction de log doit respecter cette règle. # Identifiant de la requête HTTP en cours (voir `app/request_logging.py`), # repris sur chaque ligne de log émise pendant son traitement — y compris # dans ses tâches de fond (`BackgroundTasks`, même contexte) — pour retrouver # d'un `grep` tout ce qui s'est passé lors de l'erreur qu'un utilisateur # signale (la référence lui est affichée, voir `frontend/.../state.tsx`). request_id_var: ContextVar[str] = ContextVar("request_id", default="-") class RequestIdFilter(logging.Filter): def filter(self, record: logging.LogRecord) -> bool: record.request_id = request_id_var.get() return True def configure_logging() -> None: # `force=True` : on réinstalle le handler racine même si l'import de # `app.main` se fait après la configuration de logging d'uvicorn/gunicorn, # sinon les logs `app.*` n'apparaissent pas dans la sortie du conteneur. logging.basicConfig( level=get_settings().log_level, format="%(asctime)s %(levelname)s [%(request_id)s] %(name)s %(message)s", force=True, ) # Filtre posé sur le handler (pas sur un logger) : il voit aussi les # lignes des bibliothèques (uvicorn, sqlalchemy…) propagées à la racine. for handler in logging.getLogger().handlers: handler.addFilter(RequestIdFilter()) # Journal d'accès d'uvicorn remplacé par `app/request_logging.py` : le # sien écrit l'URL complète, chaîne de requête comprise — donc le jeton # `access_token` du formulaire public (CLAUDE.md § 2, `FormTemplate`). logging.getLogger("uvicorn.access").disabled = True # Bavard au niveau INFO (une ligne par appel SumUp/hCaptcha), sans # intérêt à côté des logs de l'appli. logging.getLogger("httpx").setLevel(logging.WARNING)