feat(gui): log fichier rotatif V6 à chemin connu (E1)
This commit is contained in:
67
gui_v6/logging_setup.py
Normal file
67
gui_v6/logging_setup.py
Normal file
@@ -0,0 +1,67 @@
|
||||
"""Configuration du log fichier de la GUI V6 (E1).
|
||||
|
||||
Sans ceci, la GUI frozen fenêtrée (sans console) perd ses logs de diagnostic.
|
||||
Le log est posé dans le même répertoire applicatif que la licence
|
||||
(``%LOCALAPPDATA%/Aivanov/Anonymisation``) pour faciliter sa récupération (E2/E3).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from pathlib import Path
|
||||
|
||||
_CONFIGURED = False
|
||||
|
||||
|
||||
def _app_data_dir() -> Path:
|
||||
base = os.environ.get("LOCALAPPDATA")
|
||||
if base:
|
||||
root = Path(base)
|
||||
else: # Linux/dev
|
||||
root = Path.home() / ".local" / "share"
|
||||
return root / "Aivanov" / "Anonymisation"
|
||||
|
||||
|
||||
def log_file_path() -> Path:
|
||||
return _app_data_dir() / "logs" / "anonymisation.log"
|
||||
|
||||
|
||||
def setup_file_logging() -> Path:
|
||||
"""Configure un handler fichier rotatif sur le logger racine. Idempotent."""
|
||||
global _CONFIGURED
|
||||
path = log_file_path()
|
||||
if _CONFIGURED:
|
||||
return path
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
handler = RotatingFileHandler(
|
||||
str(path), maxBytes=2_000_000, backupCount=3, encoding="utf-8"
|
||||
)
|
||||
handler.setFormatter(
|
||||
logging.Formatter("%(asctime)s %(levelname)s %(name)s: %(message)s")
|
||||
)
|
||||
root = logging.getLogger()
|
||||
if root.level == logging.NOTSET:
|
||||
root.setLevel(logging.INFO)
|
||||
root.addHandler(handler)
|
||||
# Best-effort : si le cœur utilise loguru, on ajoute aussi un sink fichier.
|
||||
try:
|
||||
from loguru import logger as _loguru
|
||||
|
||||
_loguru.add(str(path), rotation="2 MB", retention=3, encoding="utf-8")
|
||||
except Exception:
|
||||
pass
|
||||
_CONFIGURED = True
|
||||
return path
|
||||
|
||||
|
||||
def _reset_for_tests() -> None:
|
||||
"""Réinitialise l'état pour l'isolation des tests (NE PAS appeler en prod)."""
|
||||
global _CONFIGURED
|
||||
root = logging.getLogger()
|
||||
for h in list(root.handlers):
|
||||
if isinstance(h, RotatingFileHandler):
|
||||
root.removeHandler(h)
|
||||
h.close()
|
||||
_CONFIGURED = False
|
||||
16
tests/unit/test_gui_v6_logging_setup.py
Normal file
16
tests/unit/test_gui_v6_logging_setup.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import logging
|
||||
|
||||
|
||||
def test_setup_file_logging_writes_to_known_path(tmp_path, monkeypatch):
|
||||
monkeypatch.setenv("LOCALAPPDATA", str(tmp_path))
|
||||
from gui_v6.logging_setup import setup_file_logging, _reset_for_tests
|
||||
|
||||
try:
|
||||
log_path = setup_file_logging()
|
||||
assert log_path.parent.exists()
|
||||
logging.getLogger("test.e1").warning("ligne-temoin-42")
|
||||
for h in logging.getLogger().handlers:
|
||||
h.flush()
|
||||
assert "ligne-temoin-42" in log_path.read_text(encoding="utf-8")
|
||||
finally:
|
||||
_reset_for_tests()
|
||||
Reference in New Issue
Block a user