""" Configuration du système de logging pour PriceWatch. Fournit un logger configuré avec formatage coloré et niveaux appropriés. Les logs incluent : timestamp, niveau, module, et message. """ import logging import sys from typing import Optional class ColoredFormatter(logging.Formatter): """Formatter avec couleurs pour améliorer la lisibilité en CLI.""" # Codes ANSI pour les couleurs COLORS = { "DEBUG": "\033[36m", # Cyan "INFO": "\033[32m", # Vert "WARNING": "\033[33m", # Jaune "ERROR": "\033[31m", # Rouge "CRITICAL": "\033[35m", # Magenta } RESET = "\033[0m" BOLD = "\033[1m" def format(self, record: logging.LogRecord) -> str: """Formate le log avec couleurs selon le niveau.""" # Copie pour éviter de modifier l'original log_color = self.COLORS.get(record.levelname, self.RESET) record.levelname = f"{log_color}{self.BOLD}{record.levelname}{self.RESET}" # Colorer le nom du module record.name = f"\033[90m{record.name}{self.RESET}" return super().format(record) def setup_logging(level: str = "INFO", enable_colors: bool = True) -> logging.Logger: """ Configure le logger racine de PriceWatch. Args: level: Niveau de log (DEBUG, INFO, WARNING, ERROR, CRITICAL) enable_colors: Activer la colorisation (désactiver pour les logs fichier) Returns: Logger configuré Justification technique: - Handler unique sur stdout pour éviter les duplications - Format détaillé avec timestamp ISO8601 pour faciliter le debug - Colorisation optionnelle pour améliorer l'UX en CLI """ logger = logging.getLogger("pricewatch") # Éviter d'ajouter plusieurs handlers si appelé plusieurs fois if logger.handlers: return logger logger.setLevel(getattr(logging, level.upper(), logging.INFO)) logger.propagate = False # Handler console console_handler = logging.StreamHandler(sys.stdout) console_handler.setLevel(logger.level) # Format avec timestamp ISO8601 log_format = "%(asctime)s | %(levelname)-8s | %(name)s | %(message)s" date_format = "%Y-%m-%d %H:%M:%S" if enable_colors and sys.stdout.isatty(): formatter = ColoredFormatter(log_format, datefmt=date_format) else: formatter = logging.Formatter(log_format, datefmt=date_format) console_handler.setFormatter(formatter) logger.addHandler(console_handler) return logger def get_logger(name: Optional[str] = None) -> logging.Logger: """ Retourne un logger enfant de 'pricewatch'. Args: name: Nom du sous-module (ex: 'scraping.http') Returns: Logger configuré """ if name: return logging.getLogger(f"pricewatch.{name}") return logging.getLogger("pricewatch") def set_level(level: str) -> None: """ Change dynamiquement le niveau de log. Args: level: Nouveau niveau (DEBUG, INFO, WARNING, ERROR, CRITICAL) """ logger = logging.getLogger("pricewatch") logger.setLevel(getattr(logging, level.upper(), logging.INFO)) for handler in logger.handlers: handler.setLevel(logger.level) # Initialisation par défaut au premier import _default_logger = setup_logging()