Code source de logger
"""
Module de configuration du système de logging pour l'application d'analyse culinaire.
Ce module configure un système de logging avec rotation des fichiers pour
séparer les logs de debug et d'erreur, avec affichage optionnel en console.
Example:
Utilisation basique du logger::
from src.logger import logger
logger.info("Application démarrée")
logger.error("Erreur lors du traitement")
logger.debug("Information de débogage")
Attributes:
LOG_DIR (str): Répertoire de stockage des fichiers de log
DEBUG_LOG_FILE (str): Chemin vers le fichier de log de debug
ERROR_LOG_FILE (str): Chemin vers le fichier de log d'erreur
logger (logging.Logger): Logger principal configuré pour l'application
Note:
Les fichiers de log utilisent une rotation par taille (500 bytes) avec
conservation de 2 fichiers de sauvegarde.
Ils sont stockés dans le répertoire /logs, créé pour cette utilisation.
"""
import logging
import os
from logging.handlers import RotatingFileHandler
from typing import Final
# Configuration des constantes
LOG_DIR: Final[str] = "logs"
"""str: Répertoire de stockage des fichiers de log."""
DEBUG_LOG_FILE: Final[str] = os.path.join(LOG_DIR, "debug.log")
"""str: Chemin complet vers le fichier de log de debug."""
ERROR_LOG_FILE: Final[str] = os.path.join(LOG_DIR, "errors.log")
"""str: Chemin complet vers le fichier de log d'erreur."""
# Paramètres de rotation des logs
MAX_LOG_SIZE: Final[int] = 5 * 1024 * 1024 # 5 MB
"""int: Taille maximale des fichiers de log avant rotation (5 MB)."""
BACKUP_COUNT: Final[int] = 3
"""int: Nombre de fichiers de sauvegarde à conserver lors de la rotation."""
def _create_log_directory() -> None:
"""
Crée le répertoire de logs s'il n'existe pas.
:returns: ``None``.
:rtype: None
:raises OSError: Si la création du répertoire échoue.
.. note::
Utilise ``exist_ok=True`` pour éviter les erreurs si le répertoire existe déjà.
"""
try:
os.makedirs(LOG_DIR, exist_ok=True)
except OSError as e:
print(f"Erreur lors de la création du répertoire de logs: {e}")
raise
def _create_formatter() -> logging.Formatter:
"""
Crée le formateur standard pour tous les handlers de log.
:returns: Formateur configuré avec timestamp, nom, niveau et message.
:rtype: logging.Formatter
.. code-block:: text
2024-01-15 14:30:25 - webapp - INFO - Application démarrée
"""
return logging.Formatter(
fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
def _create_debug_handler(formatter: logging.Formatter) -> RotatingFileHandler:
"""
Crée le handler pour les logs de debug avec rotation.
:param formatter: Formateur à appliquer au handler.
:type formatter: logging.Formatter
:returns: Handler configuré pour les logs de debug.
:rtype: RotatingFileHandler
.. note::
Niveau ``DEBUG`` et plus, rotation 5 MB, 3 fichiers de sauvegarde, encodage UTF-8.
"""
handler = RotatingFileHandler(
filename=DEBUG_LOG_FILE,
maxBytes=MAX_LOG_SIZE,
backupCount=BACKUP_COUNT,
encoding="utf-8"
)
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
return handler
def _create_error_handler(formatter: logging.Formatter) -> RotatingFileHandler:
"""
Crée le handler pour les logs d'erreur avec rotation.
:param formatter: Formateur à appliquer au handler.
:type formatter: logging.Formatter
:returns: Handler dédié aux messages d'erreur.
:rtype: RotatingFileHandler
.. note::
Niveau ``ERROR`` et plus, rotation 5 MB, 3 fichiers de sauvegarde, encodage UTF-8.
"""
handler = RotatingFileHandler(
filename=ERROR_LOG_FILE,
maxBytes=MAX_LOG_SIZE,
backupCount=BACKUP_COUNT,
encoding="utf-8"
)
handler.setLevel(logging.ERROR)
handler.setFormatter(formatter)
return handler
def _create_console_handler(formatter: logging.Formatter) -> logging.StreamHandler:
"""
Crée le handler pour l'affichage console.
:param formatter: Formateur à appliquer au handler.
:type formatter: logging.Formatter
:returns: Handler configuré pour l'affichage console.
:rtype: logging.StreamHandler
.. note::
Niveau ``INFO`` et plus, sortie ``sys.stdout``.
"""
handler = logging.StreamHandler()
handler.setLevel(logging.INFO)
handler.setFormatter(formatter)
return handler
def _configure_logger() -> logging.Logger:
"""
Configure et initialise le logger principal de l'application.
:returns: Logger configuré avec tous les handlers.
:rtype: logging.Logger
:raises OSError: Si la création des fichiers de log échoue.
.. note::
Niveau global ``DEBUG`` ; handlers dédiés pour debug.log, errors.log et la console.
"""
# Créer le logger principal
app_logger = logging.getLogger("webapp")
app_logger.setLevel(logging.DEBUG)
# Éviter la duplication des logs si le logger est reconfiguré
if app_logger.handlers:
app_logger.handlers.clear()
# Créer le répertoire de logs
_create_log_directory()
# Créer le formateur commun
formatter = _create_formatter()
# Créer et ajouter les handlers
debug_handler = _create_debug_handler(formatter)
error_handler = _create_error_handler(formatter)
console_handler = _create_console_handler(formatter)
app_logger.addHandler(debug_handler)
app_logger.addHandler(error_handler)
app_logger.addHandler(console_handler)
# Log initial de confirmation
app_logger.debug("Logger configuré avec succès")
return app_logger
[docs]
def get_logger(name: str = "webapp") -> logging.Logger:
"""
Récupère une instance du logger configuré.
:param name: Nom du logger souhaité.
:type name: str
:returns: Logger configuré correspondant au nom demandé.
:rtype: logging.Logger
.. code-block:: python
from src.logger import get_logger
logger = get_logger(__name__)
logger.info("Module initialisé")
"""
return logging.getLogger(name)
# --- Initialisation du logger principal ---
logger: logging.Logger = _configure_logger()
"""
logging.Logger: Logger principal de l'application, configuré avec rotation des fichiers.
Ce logger est directement utilisable après import:
Example:
Import et utilisation directe::
from src.logger import logger
logger.info("Message d'information")
logger.error("Message d'erreur")
logger.debug("Message de debug")
Note:
- Les logs DEBUG vont dans logs/debug.log
- Les logs ERROR+ vont dans logs/errors.log
- Les logs INFO+ s'affichent en console
"""
# --- Configuration pour les tests ---
[docs]
def disable_logging() -> None:
"""
Désactive temporairement le logging (utile pour les tests).
:returns: ``None``.
:rtype: None
.. note::
Le niveau est fixé à ``CRITICAL`` ; utilisez ``enable_logging()`` pour rétablir la configuration.
"""
logging.getLogger("webapp").setLevel(logging.CRITICAL)
[docs]
def enable_logging() -> None:
"""
Réactive le logging après désactivation.
:returns: ``None``.
:rtype: None
.. note::
Le niveau est remis à ``DEBUG`` pour retrouver le comportement normal.
"""
logging.getLogger("webapp").setLevel(logging.DEBUG)
if __name__ == "__main__":
"""
Test du module de logging.
Génère des logs de test pour vérifier le bon fonctionnement
de tous les handlers configurés.
"""
# Tests des différents niveaux de log
logger.debug("Test du niveau DEBUG")
logger.info("Test du niveau INFO")
logger.warning("Test du niveau WARNING")
logger.error("Test du niveau ERROR")
logger.critical("Test du niveau CRITICAL")
print(f"Logs générés dans le répertoire: {LOG_DIR}")
print(f"Debug log: {DEBUG_LOG_FILE}")
print(f"Error log: {ERROR_LOG_FILE}")