Files
training.python.beginner/documentation/12-logging.md
2025-07-04 19:26:39 +02:00

8.8 KiB

title, author
title author
Journalisation d'événements Steve Kossouho

Journaliser des événements en Python


Journaliser ?

Lorsque l'on exécute un programme au long cours, souvent un service, un serveur ou même une application graphique, il est souvent important de pouvoir retracer ce qui s'est produit avant qu'une exception soit levée, ou qu'un comportement inattendu montre le bout de son nez.


La journalisation est un outil formidable à cet effet, puisqu'elle vous permet de consigner, dans la console ou tout autre support, une liste chronologique de messages et d'événements que vous pouvez retracer a posteriori. Pour peu que vous consigniez assez de messages, ils peuvent être capitaux, ne serait-ce que pour fournir du support à vos utilisateurs.


À quoi ça ressemble ?

La journalisation est sensiblement identique sur tous les systèmes. Vous consignez des messages avec des niveaux de sévérité divers.

En voici un exemple arbitraire :

2021-01-01 23:51:15 - DEBUG - Logging message 3
2021-01-01 23:51:10 - DEBUG - Logging message 2
2021-01-01 23:50:37 - WARNING - Logging message 1

Un administrateur ou un outil peuvent consulter ce genre de fichier pour en savoir plus sur leur système. En marquant les messages comme possédant un niveau de sévérité, il est aussi possible de voir rapidement quels messages doivent être traités en priorité.


Les niveaux de sévérité d'un système de journalisation sont les suivants :

  • DEBUG : Pour les développeurs, remplace le print.
  • INFO : Information standard (version, action effectuée etc.)
  • WARNING : Attention à porter à un problème potentiel.
  • ERROR : Une erreur s'est produite, gênante pour les utilisateurs.
  • CRITICAL : Une erreur irrécupérable à corriger immédiatement s'est produite.

Le module logging

La bibliothèque standard de Python propose un module, qui semble inspiré d'une API C++ ou Java (typographie) et qui offre les outils nécessaires à organiser une bonne journalisation.


import logging

logger = logging.getLogger(__name__)

Récupère le logger portant le nom du module en cours. Attention, si le module est celui que vous avez lancé spécifiquement, la variable __name__ (ou l'attribut __name__ du module) prendra la valeur "__main__".


import logging

logger = logging.getLogger(__name__)
logger.log(logging.WARNING, "Message.")
logger.warning("Same message as before.")

Consignation de nouveaux messages.


Système de loggers

Un logger est un objet que vous utilisez pour consigner de nouveaux messages dans votre système. Cet objet doit être associé à un ou plusieurs objets de type handler, qui sont des canaux de sortie pour le logger.


Hiérarchie des loggers

Les loggers portent tous un nom. Le système de journalisation de Python fonctionne avec un système de hiérarchie, où le nom d'un logger définit où il se trouve dans la hiérarchie. Un message consigné par un logger est transmis à son logger parent, jusqu'au logger racine.


Exemple de configuration


Dans l'exemple précédent,

  • Un message consigné avec le logger nommé data.excel remonte jusqu'au au logger racine.
  • Tous les handlers associés aux loggers rencontrés en remontant la hiérarchie sont utilisés pour traiter le message.
  • Si aucun handler n'a été rencontré après être arrivé au sommet, le handler de dernier recours (lastResort) est utilisé pour consigner l'événement.
  • Ce handler affiche les messages de sévérité >= logging.WARNING{.python} dans la console d'erreur (rouge).

Utiliser les handlers

Le clou du spectacle consiste à configurer des handlers pour les associer à des loggers. Pour configurer correctement un handler, il nous faut définir :

  • Son type (sortie fichier, console);
  • Le format textuel des événements consignés;
  • Son niveau de sévérité minimum pour accepter des messages.

Configuration d'un handler

import logging
handler = logging.FileHandler("filename.log", encoding="utf-8")
# Chaîne de formatage des événements
formatting = logging.Formatter("%(asctime)s - %(message)s")
handler.setFormatter(formatting)
# Le handler acceptera tous les messages >= DEBUG
handler.setLevel(logging.DEBUG)

Référence des spécifieurs pour le format de texte


Types de handlers

Les types de handlers disponibles sont disponibles ici :

  • logging.FileHandler : sérialise dans un fichier (documentation).
  • logging.StreamHandler : sérialise dans un flux, généralement console (voir slide suivant).
  • logging.handlers.* : handlers plus avancés, dont fichiers avec rotation.

Handlers supplémentaires


Types de flux console

Pour les handlers vers un flux, voici la liste des flux classiques disponibles :

  • sys.stdout : sortie console normale.
  • sys.stderr : sortie console d'erreur (affiché en rouge).

(voir le slide suivant pour l'utilisation de ces flux)


Ajouter un handler à un logger

Une fois un handler configuré, vous pouvez l'associer à un logger (puisque c'est sur le logger que vous allez utiliser des méthodes de journalisation).

import logging
import sys

logger = logging.getLogger("logger.name")
handler = logging.StreamHandler(sys.stdout)
# Imaginons que l'on configure notre handler correctement
# (ici, il manque un format à spécifier)
# On peut *ajouter* notre handler à notre logger
logger.addHandler(handler)

Un logger peut également définir son propre niveau minimal de sévérité, qui sera utilisé pour potentiellement limiter la portée de ses handlers.

import logging
import sys

logger = logging.getLogger("logger.name")
handler = logging.StreamHandler(sys.stdout) # il faudrait spécifier un format
logger.addHandler(handler)
# Limiter tous les handlers associés au niveau WARNING
logger.setLevel(logging.WARNING)

Le logger racine

Le logger racine est souvent utilisé pour configurer une journalisation globale pour toute votre application, puisque tout logger remonte toujours ses messages vers celui-ci.

Pour le configurer, utilisez la variable root du module logging :

import logging
# Exemple minimaliste
handler = logging.FileHandler("filename")
# Tous les loggers vont passer par ce handler
logging.root.addHandler(handler)

Récapitulatif minimal

import logging

# Les 3 types d'objets nécessaires à la configuration
logger = logging.getLogger("myname")  # nouveau logger nommé "myname"
handler = logging.FileHandler("myfile.log", encoding="utf-8")  # handler fichier
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")  # format de messages

# Configuration du handler
handler.setFormatter(formatter)  # configurer le handler
handler.setLevel(logging.DEBUG)  # niveau de sévérité minimum

# Configuration du logger
logger.addHandler(handler)  # ajouter le handler au logger
logger.setLevel(logging.INFO)  # niveau de sévérité minimum du logger

Exemple de logger à deux handlers

Exemple d'usage d'un logger


Plus : Fichier de configuration

On peut configurer sa journalisation et créer d'une traite un ensemble de loggers via un fichier de configuration (il est possible d'utiliser un dictionnaire), qui sera stocké sous la forme d'un fichier ini. Le contenu à insérer dans ce fichier est décrit dans la section Configuration file format de la documentation Python.

Les fichiers ini ont une structure qui est toujours de la forme :

[section1]
property1 = value
property2 = value

[section2]
property3 = value

Charger un fichier de configuration

Lorsque vous avez défini un fichier de configuration avec tous ses loggers, handlers et formatters, vous pouvez l'utiliser via la fonction logging.config.fileConfig() :

from logging.config import fileConfig

# Initialiser des loggers depuis une fonction
# Le fichier devrait être stocké dans un dossier à l'arborescence bien
# choisie.
fileConfig("logging.conf.ini", disable_existing_loggers=False, encoding="utf-8")

Bibliothèques simplifiées de journalisation