setup_logging() branche un TimedRotatingFileHandler vers LOG_FILE (rotation quotidienne + rétention 180j, Règlement IA Art.12) + console. Sous pythonw (sans console), basicConfig->stderr était perdu => diagnostic terrain aveugle. main.py appelle setup_logging au démarrage, avec fallback console si le fichier est indisponible (ne jamais empêcher Léa de démarrer). TDD: tests/unit/test_agent_v1_logging.py (3 tests RED->GREEN ; module chargé par chemin pour éviter les imports lourds DETTE-011/013). py_compile main.py OK. refs DETTE-021 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
57 lines
2.1 KiB
Python
57 lines
2.1 KiB
Python
"""Journalisation client Léa — DETTE-021.
|
|
|
|
Branche un handler **fichier** (`TimedRotatingFileHandler`) sur le logger racine,
|
|
en plus de la console. Sans cela, sous `pythonw.exe` (pas de console), les logs
|
|
partent sur stderr et sont **perdus** — diagnostic terrain impossible.
|
|
|
|
Rotation quotidienne + rétention `retention_days` (Règlement IA Art. 12 :
|
|
journalisation automatique + conservation minimum 180 j).
|
|
"""
|
|
import logging
|
|
from logging.handlers import TimedRotatingFileHandler
|
|
from pathlib import Path
|
|
|
|
_FMT = "%(asctime)s %(levelname)-7s %(name)-25s %(message)s"
|
|
|
|
|
|
def setup_logging(log_file, level=logging.INFO, retention_days=180):
|
|
"""Configure le logging racine : fichier (rotation quotidienne, `retention_days`
|
|
fichiers conservés) + console. **Idempotent** : ne réempile pas nos handlers.
|
|
|
|
Args:
|
|
log_file: chemin du fichier de log (`config.LOG_FILE` en prod).
|
|
level: niveau racine (INFO par défaut ; DEBUG géré par l'appelant).
|
|
retention_days: nb de fichiers quotidiens conservés (180 = Règlement IA Art. 12).
|
|
|
|
Returns:
|
|
Le `TimedRotatingFileHandler` créé.
|
|
"""
|
|
log_file = Path(log_file)
|
|
log_file.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
root = logging.getLogger()
|
|
root.setLevel(level)
|
|
|
|
# Idempotence : retirer nos propres handlers posés par un appel précédent.
|
|
for h in list(root.handlers):
|
|
if getattr(h, "_lea_managed", False):
|
|
h.close()
|
|
root.removeHandler(h)
|
|
|
|
file_handler = TimedRotatingFileHandler(
|
|
str(log_file), when="midnight", backupCount=retention_days, encoding="utf-8"
|
|
)
|
|
file_handler.setFormatter(logging.Formatter(_FMT, datefmt="%Y-%m-%d %H:%M:%S"))
|
|
file_handler.setLevel(level)
|
|
file_handler._lea_managed = True
|
|
root.addHandler(file_handler)
|
|
|
|
# Console conservée (utile en dev / si lancé avec une console).
|
|
console = logging.StreamHandler()
|
|
console.setFormatter(logging.Formatter(_FMT, datefmt="%H:%M:%S"))
|
|
console.setLevel(level)
|
|
console._lea_managed = True
|
|
root.addHandler(console)
|
|
|
|
return file_handler
|