chore: sync project files
This commit is contained in:
112
pricewatch/app/core/logging.py
Executable file
112
pricewatch/app/core/logging.py
Executable file
@@ -0,0 +1,112 @@
|
||||
"""
|
||||
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()
|
||||
Reference in New Issue
Block a user