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>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d722a81a87
commit
d0fa1e109c
@@ -1,4 +1,5 @@
|
||||
import logging
|
||||
from contextvars import ContextVar
|
||||
|
||||
from app.config import get_settings
|
||||
|
||||
@@ -7,6 +8,19 @@ from app.config import get_settings
|
||||
# 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
|
||||
@@ -14,6 +28,17 @@ def configure_logging() -> None:
|
||||
# sinon les logs `app.*` n'apparaissent pas dans la sortie du conteneur.
|
||||
logging.basicConfig(
|
||||
level=get_settings().log_level,
|
||||
format="%(asctime)s %(levelname)s %(name)s %(message)s",
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user