"""Configuration de la journalisation avec masquage automatique des secrets. Ce module fournit un formateur de logs qui rédige les secrets (tokens, mots de passe, URLs sensibles) ainsi que l'initialisation du logging global du pipeline ``pronote-sync``. """ from __future__ import annotations import logging import sys from pronote_sync.utils.redaction import redact_secrets _LOG_FORMAT = "%(asctime)s | %(levelname)-8s | %(name)s | %(message)s" _DATE_FORMAT = "%Y-%m-%d %H:%M:%S" class RedactingFormatter(logging.Formatter): """Formateur de logs qui masque les secrets des messages et des arguments.""" def format(self, record: logging.LogRecord) -> str: """Formate un enregistrement de log en masquant les secrets. Le message et chaque argument textuel de l'enregistrement sont rédigés avant le formatage final effectué par :class:`logging.Formatter`. :param record: Enregistrement de log à formater. :return: Message formaté, avec les secrets remplacés par ``REDACTED``. :rtype: str """ record.msg = redact_secrets(str(record.msg)) args = record.args if args: if isinstance(args, tuple): record.args = tuple( redact_secrets(arg) if isinstance(arg, str) else arg for arg in args ) else: record.args = { key: redact_secrets(value) if isinstance(value, str) else value for key, value in args.items() } return super().format(record) def setup_logging(level: str = "INFO") -> None: """Configure la journalisation globale avec masquage des secrets. Les gestionnaires existants du logger racine sont supprimés, puis un gestionnaire unique écrivant sur ``sys.stdout`` est installé. Les loggers tiers ``urllib3`` et ``slixmpp`` sont ramenés au niveau ``WARNING``. :param level: Nom du niveau de log (ex: ``"DEBUG"``, ``"INFO"``) ; les noms inconnus sont ignorés au profit de ``INFO``. :rtype: None """ numeric_level: int = logging.getLevelNamesMapping().get(level.upper(), logging.INFO) handler = logging.StreamHandler(sys.stdout) handler.setFormatter(RedactingFormatter(fmt=_LOG_FORMAT, datefmt=_DATE_FORMAT)) root = logging.getLogger() for existing in list(root.handlers): root.removeHandler(existing) root.setLevel(numeric_level) root.addHandler(handler) for noisy_name in ("urllib3", "slixmpp"): logging.getLogger(noisy_name).setLevel(logging.WARNING)