Ajoute un fichier log texte rotatif (wgsecure.log) dans le dossier applicatif, en complément du journal JSON interne, pour diagnostiquer un échec (ex. split-DNS) sans dépendre de l'UI. Corrige le panneau Journal qui coupait les messages longs sans moyen de les lire en entier (défilement horizontal réactivé + infobulle). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
87 lines
2.3 KiB
Python
87 lines
2.3 KiB
Python
"""Journal des événements de connexion WGSecure."""
|
|
import os
|
|
import json
|
|
import logging
|
|
from datetime import datetime
|
|
from logging.handlers import RotatingFileHandler
|
|
|
|
from app.utils.platform_utils import get_config_dir
|
|
|
|
_FILE = "events.json"
|
|
_MAX_ENTRIES = 200
|
|
|
|
_LOG_FILE = "wgsecure.log"
|
|
_LOG_LEVELS = {
|
|
"error": logging.ERROR,
|
|
"warning": logging.WARNING,
|
|
}
|
|
_file_logger: logging.Logger | None = None
|
|
|
|
|
|
def _path() -> str:
|
|
return os.path.join(get_config_dir(), _FILE)
|
|
|
|
|
|
def _get_file_logger() -> logging.Logger:
|
|
"""Logger fichier rotatif : trace texte des opérations, lisible sans
|
|
passer par l'UI (diagnostic d'un échec quand le panneau Journal ne
|
|
suffit pas)."""
|
|
global _file_logger
|
|
if _file_logger is None:
|
|
logger = logging.getLogger("wgsecure")
|
|
logger.setLevel(logging.INFO)
|
|
handler = RotatingFileHandler(
|
|
os.path.join(get_config_dir(), _LOG_FILE),
|
|
maxBytes=1_000_000, backupCount=3, encoding="utf-8",
|
|
)
|
|
handler.setFormatter(logging.Formatter(
|
|
"%(asctime)s [%(levelname)s] %(message)s", "%Y-%m-%d %H:%M:%S",
|
|
))
|
|
logger.addHandler(handler)
|
|
logger.propagate = False
|
|
_file_logger = logger
|
|
return _file_logger
|
|
|
|
|
|
def log_event(msg: str, kind: str = "info") -> None:
|
|
"""Ajoute un événement horodaté au journal."""
|
|
events = _load()
|
|
events.append({
|
|
"ts": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
|
"kind": kind, # info | success | error | warning
|
|
"msg": msg,
|
|
})
|
|
_save(events[-_MAX_ENTRIES:])
|
|
# Une ligne = un événement, même si msg contient des retours à la ligne
|
|
# (ex. message de succès de connexion suivi d'un avertissement).
|
|
_get_file_logger().log(
|
|
_LOG_LEVELS.get(kind, logging.INFO), msg.replace("\n", " | ")
|
|
)
|
|
|
|
|
|
def get_events(n: int = 50) -> list[dict]:
|
|
return _load()[-n:]
|
|
|
|
|
|
def clear() -> None:
|
|
_save([])
|
|
|
|
|
|
def _load() -> list[dict]:
|
|
p = _path()
|
|
if not os.path.exists(p):
|
|
return []
|
|
try:
|
|
with open(p, "r", encoding="utf-8") as f:
|
|
return json.load(f)
|
|
except Exception:
|
|
return []
|
|
|
|
|
|
def _save(events: list[dict]) -> None:
|
|
try:
|
|
with open(_path(), "w", encoding="utf-8") as f:
|
|
json.dump(events, f, ensure_ascii=False)
|
|
except Exception:
|
|
pass
|