Files
AdamThomasandClaude Opus 5.5 d0fa1e109c
ci-cd / build-backend (push) Successful in 38s
ci-cd / build-frontend (push) Successful in 20s
ci-cd / deploy (push) Successful in 13s
Logs de diagnostic et rapprochement SumUp ↔ bénévole
- Une ligne de log par requête (route modèle, statut, durée, code d'erreur,
  compte), identifiant X-Request-ID repris dans chaque log et affiché à
  l'utilisateur sous une erreur, exceptions non gérées tracées (500 JSON)
- Journal d'accès d'uvicorn coupé : il exposait le jeton du formulaire public
- Logs métier : paiement SumUp, connexion, import, plannings, solveur
- Nom du bénévole dans la description du checkout SumUp
- Code et identifiant de transaction SumUp enregistrés au paiement
  (migration 0023), affichés sur la fiche et dans l'export Excel

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:54:35 +02:00

45 lines
2.1 KiB
Python

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)