Dans un projet python, la gestion des logs est essentielle pour assurer le suivi, le débogage et la maintenabilité du code, surtout dans les projets data. Un bon logger doit être centralisé, réutilisable, structurant et capable d’écrire dans des fichiers. Dans cet article, nous vous montrons comment mettre en place un logger adapté à un projet Python organisé en plusieurs fichiers, avec des logs enrichis et adaptés aux usages data science. Python comporte un module interne nommé logging qui permet de gérer les logs python.
Objectifs
- Centraliser les logs de manière propre avec un logger python
- Écrire les logs dans des fichiers avec rotation
- Structurer les messages (texte enrichi ou JSON)
- Journaliser des objets complexes comme des DataFrame (extraits, dimensions)
- Fournir un outil de monitoring fiable pour les projets data et IA
Structure recommandée du projet
mon_projet/
├── main.py
├── core/
│ ├── traitement.py
│ └── utils.py
├── config/
│ └── logger.py
├── logs/
└── (fichiers de logs générés ici)
Configuration centrale dans logger.py
Le fichier logger.py
contient toute la logique de configuration du logger. Il est conçu pour être appelé depuis n’importe quel module de l’application sans multiplier les handlers (ce qui provoquerait des doublons de logs).
Voici les principaux éléments paramétrables :
- Nom du logger : souvent
__name__
, il permet d’identifier le module appelant. - Niveau de log :
DEBUG
,INFO
,WARNING
,ERROR
,CRITICAL
selon le degré de verbiage souhaité. On utilise souventDEBUG
en développement,INFO
en production. - Format du message : avec
logging.Formatter
, vous pouvez inclure l’heure, le niveau, le module, la ligne et le message. - Handler console : pour afficher les logs en temps réel dans le terminal.
- Handler fichier : ici on utilise un
RotatingFileHandler
pour limiter la taille des fichiers de logs et conserver un historique.
# config/logger.py
import logging
from logging.handlers import RotatingFileHandler
import os
LOG_DIR = os.path.join(os.path.dirname(__file__), '..', 'logs')
os.makedirs(LOG_DIR, exist_ok=True)
def get_logger(name: str) -> logging.Logger:
logger = logging.getLogger(name)
if not logger.hasHandlers():
logger.setLevel(logging.DEBUG) # Niveau par défaut : DEBUG
formatter = logging.Formatter(
'[%(asctime)s] %(levelname)s | %(name)s | %(filename)s:%(lineno)d | %(message)s'
)
# Handler pour la console
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
# Handler pour le fichier avec rotation
file_handler = RotatingFileHandler(
os.path.join(LOG_DIR, 'application.log'),
maxBytes=5 * 1024 * 1024, # 5 Mo
backupCount=3 # Jusqu'à 3 fichiers de sauvegarde
)
file_handler.setFormatter(formatter)
# Ajout des handlers
logger.addHandler(console_handler)
logger.addHandler(file_handler)
return logger
Vous pouvez adapter ces paramètres selon l’environnement (dev, test, prod) ou les charger depuis un fichier de configuration YAML ou JSON.
Exemple d’utilisation dans les fichiers Python
# core/utils.py
from config.logger import get_logger
logger = get_logger(__name__)
def addition(a, b):
logger.debug(f"Addition de {a} et {b}")
return a + b
# core/traitement.py
from config.logger import get_logger
logger = get_logger(__name__)
def process(data):
logger.info(f"Traitement des données : {data}")
Écriture dans un fichier avec rotation
Les logs sont écrits dans le fichier suivant :
logs/application.log
Les fichiers sont automatiquement archivés si leur taille dépasse 5 Mo, avec jusqu’à 3 backups.
Logs enrichis avec contexte métier
Dans de nombreux projets, notamment en data science ou en business intelligence, il est utile de logguer non seulement des messages techniques, mais aussi du contexte métier pour pouvoir tracer plus facilement l’origine des traitements ou comprendre un comportement sans avoir à lire le code.
Par exemple, on peut vouloir savoir :
- Quel utilisateur ou quel service a lancé le traitement
- Sur quelle table ou source de données il a été appliqué
- Dans quel environnement cela s’est produit (dev, staging, prod)
- Quelle est l’ID du batch ou du job concerné
Python permet d’ajouter des champs personnalisés via le paramètre extra
de la fonction logger.info()
(ou tout autre niveau). Voici un exemple :
logger.info("Import terminé avec succès", extra={
'user': 'analyste1',
'table': 'ventes',
'env': 'production',
'job_id': 'batch_20250310'
})
Pour que ces informations apparaissent dans les logs, il faut adapter le formatter à ces clés supplémentaires. Exemple :
formatter = logging.Formatter(
'[%(asctime)s] %(levelname)s | %(name)s | %(message)s | user=%(user)s table=%(table)s env=%(env)s job_id=%(job_id)s'
)
Cette approche est particulièrement utile pour les logs structurés en JSON, utilisables ensuite dans des outils comme ELK, Splunk ou Datadog.
Format JSON (optionnel)
Pour une intégration avec des outils comme ELK ou Datadog, vous pouvez formater vos logs en JSON. On devra utiliser un package supplémentaire, python-json-logger, pour l’installer, il vous suffit de lancer cette commande dans votre terminal :
pip install python-json-logger
Ensuite dans votre code python :
from pythonjsonlogger import jsonlogger
json_formatter = jsonlogger.JsonFormatter('%(asctime)s %(levelname)s %(name)s %(message)s %(user)s')
file_handler.setFormatter(json_formatter)
Journaliser un DataFrame Pandas
Il est inutile (et risqué) de logger un DataFrame complet. En revanche, vous pouvez logguer :
- Le nombre de lignes et de colonnes
- Les noms et types de colonnes
- Un petit aperçu du contenu
import pandas as pd
from config.logger import get_logger
logger = get_logger(__name__)
def analyser_dataframe(df: pd.DataFrame):
logger.info(f"DataFrame chargé : {df.shape[0]} lignes × {df.shape[1]} colonnes")
logger.debug(f"Colonnes : {list(df.columns)}")
logger.debug(f"Types : {df.dtypes.to_dict()}")
logger.debug(f"Aperçu :\n{df.head(3).to_string(index=False)}")
Bonnes pratiques
- Utiliser un fichier
logger.py
unique pour toute l’application - Éviter les doublons de handlers avec
if not logger.hasHandlers()
- Enrichir les logs avec du contexte utile (utilisateur, table, environnement)
- Ne pas loguer de données sensibles
- En production, adapter les niveaux et les formats (JSON + INFO/ERROR)
- Externaliser la configuration dans un fichier si nécessaire pour la rendre modulaire
Conclusion
Un bon logger vous évitera bien des heures de débogage et facilitera l’analyse de vos processus data. Centralisez-le, structurez-le, et adaptez-le à votre contexte.
Les fichiers liés à ce blog sont disponible dans ce répertoire GitHub.
Chez stat4decision, nous l’intégrons systématiquement dans nos projets et nos formations pour garantir transparence et traçabilité. Découvrez nos accompagnements python.
Partager cet article