Python est un langage de pro­gram­ma­tion orienté objet qui est très utilisé. Beaucoup d’uti­li­sa­teurs ap­pré­cient Python parce qu’il permet, lorsqu’il est utilisé comme langage de script, de créer des pro­grammes plus vite qu’avec des langages compilés comme Java. Par rapport aux langages de pro­gram­ma­tion pro­cé­du­raux clas­siques comme Perl, Python présente l'avan­tage d’être bien lisible et facile à modifier. Que ce soit pour de l’in­tel­li­gence ar­ti­fi­cielle, une interface graphique d’uti­li­sa­teur ou de l’ad­mi­nis­tra­tion de système, Python peut être utilisé dans le cadre d’un grand nombre de tâches. Mais plus un langage de pro­gram­ma­tion est fré­quem­ment utilisé, plus il est important qu’il lui soit associée une bonne culture de l'erreur. La jour­na­li­sa­tion des erreurs devrait avoir lieu depuis la première phase de dé­ve­lop­pe­ment jusqu'à la mise à dis­po­si­tion de l’uti­li­sa­teur final.

Dans la bi­blio­thèque Python, il existe un module pratique de jour­na­li­sa­tion. Appliqué à un simple débogage ou à une jour­na­li­sa­tion cen­tra­li­sée à partir de dif­fé­rents serveurs, ce module logging peut con­si­dé­ra­ble­ment faciliter le travail des dé­ve­lop­peurs et opé­ra­teurs.

Qu’est-ce que la jour­na­li­sa­tion ?

La jour­na­li­sa­tion est un protocole destiné à re­cueil­lir dans un fichier toutes les in­for­ma­tions im­por­tantes sur les incidents de fonc­tion­ne­ment. En fonction de ce qui doit être examiné, la jour­na­li­sa­tion peut concerner uni­que­ment certains types d’actions ou d’incidents ou bien concerner toutes les actions.

Quand on apprend un nouveau langage de pro­gram­ma­tion, on est iné­vi­ta­ble­ment amené à faire des erreurs. Bien que Python présente des struc­tures fa­mi­lières pour un pro­gram­ma­teur con­nais­sant déjà le C++ ou Java (comme la forme des loops), chaque langage possède, malgré tout, ses par­ti­cu­la­ri­tés. Par exemple, Python délimite le code par un système d’in­den­ta­tion. Si on oublie une espace en cours de pro­gram­ma­tion, l'ap­pli­ca­tion la plus simple ne pourra pas fonc­tion­ner. Un protocole d’erreur invite les dé­ve­lop­peurs inex­pé­ri­men­tés à déboguer la ligne cor­res­pon­dante en affichant le message d’erreur « unex­pec­ted In­den­ta­tion ». Dans une telle situation, la jour­na­li­sa­tion Python en­re­gistre les erreurs de script simples et génère un message signalant l’erreur. Mais ce n’est pas tout. Les dé­ve­lop­peurs utilisent la jour­na­li­sa­tion en pro­gram­ma­tion pour des ap­pli­ca­tions com­plè­te­ment dif­fé­rentes :

  • Débogage : tout le code source est examiné à la recherche d’erreurs de façon à garantir que le programme fini fonc­tion­nera sans problème.
  • Recherche et cor­rec­tion des failles de sécurité : les risques de sécurité sont iden­ti­fiés de manière anticipée et traités.
  • In­for­ma­tique légale : l’in­for­ma­tique légale permet de dé­ter­mi­ner la cause d’incidents critiques, comme des attaques de pirates in­for­ma­tiques, en utilisant les fichiers journaux.
  • Audit in­for­ma­tique : un audit in­for­ma­tique établit si la sécurité et l’intégrité des données est garantie ; il compare les objectifs de l'en­tre­prise concernée à ses struc­tures in­for­ma­tiques exis­tantes pour évaluer leur com­pa­ti­bi­lité et analyse l'ef­fi­ca­cité des pro­grammes et des systèmes d'ex­ploi­ta­tion en place.
  • Com­pa­rai­son des dif­fé­rentes versions des journaux : dans la mesure où chaque itération génère son propre fichier journal, il est possible de procéder à des com­pa­rai­sons.

Beaucoup de données peuvent être déduites de la jour­na­li­sa­tion, surtout si on écrit une ap­pli­ca­tion complexe en Python. Les dé­ve­lop­peurs re­cueil­lent ces données grâce à la jour­na­li­sa­tion Python sur fichier (c’est-à-dire à un fichier journal fourni par le module logging Python et enrichi d’in­for­ma­tions de jour­na­li­sa­tion via un pointeur). À cet effet, il est important que le fichier journal fonc­tionne en asyn­chro­ni­sa­tion. Autrement, la jour­na­li­sa­tion en Python risque de bloquer l’exécution du script.

Analyse des erreurs avec le logging Python : les 5 niveaux de jour­na­li­sa­tion Python des fichiers

Certains dé­ve­lop­peurs utilisent la fonction print pour vérifier si leur script contient des erreurs. Ils se con­centrent pour cela sur les passages où ils craignent la présence d’une erreur. D'autres utilisent print dans leur script à titre préventif. Cette méthode présente un problème : cela les oblige à vérifier a pos­te­riori l’ensemble du code pour placer des com­men­taires sur les commandes con­cer­nées ou pour résoudre les problèmes. Dans le cas contraire, le fichier texte de sortie risque d'ap­pa­raître lorsque l’uti­li­sa­teur lance le programme. En outre, cela rend le code source assez peu lisible.

À l'aide d’une simple jour­na­li­sa­tion, on peut à la fois s’épargner ce travail sup­plé­men­taire et mettre en œuvre une solution d'analyse des erreurs plus élégante. La jour­na­li­sa­tion Python distingue cinq dif­fé­rents degrés de gravité pour les erreurs de code : level of Severity (« degré de sévérité ») Si on souhaite définir son propre filtrage, on peut le faire très sim­ple­ment. Le module de jour­na­li­sa­tion Python de Vinay Sajip intégré propose une clas­si­fi­ca­tion des degrés de gravité qui nous semble ju­di­cieuse :

Nom du niveau de jour­na­li­sa­tion Python Uti­li­sa­tion Pos­si­bi­lité d’édition de message
Debug Diag­nos­tic des problèmes, très détaillé In­den­ta­tion inat­ten­due à la ligne XY
Info Confirme que le système fonc­tionne cor­rec­te­ment La fonction 1*1 est exécutée
Warning L'ap­pli­ca­tion travaille cor­rec­te­ment d’une manière générale, mais une situation inat­ten­due s’est produite, ou un problème à venir est signalé Espace disque faible
Error Une fonction n'a pas pu être effectuée car un problème s’est produit Une erreur s’est produite et l'action a été annulée
Critical Un problème grave s’est produit. Il faudra peut-être arrêter l'ap­pli­ca­tion. Erreur fatale : le programme ne peut plus se connecter à ce service et doit être arrêté

Les dif­fé­rents niveaux pré­sen­tent les in­for­ma­tions sur les incidents par ordre croissant de gravité. Les niveaux de jour­na­li­sa­tion Python sont des fonctions statiques. En pro­gram­ma­tion orientée objet, il s'agit des contenus d’une classe. Pour chaque instance de la classe à l’intérieur d’un objet, les fonctions statiques sont toujours iden­tiques. Elles ne changent pas et sont également dis­po­nibles lorsqu’aucune instance n’est appelée. Par exemple, Error com­mu­nique dans chaque instance un message d’erreur. Si cette instance est appelée dans le même objet d’exécution, le message d'erreur associé reste le même. Pour les autres actions, un autre message d’erreur peut être déterminé.

Debug est le niveau le plus faible ; c’est pourquoi des in­for­ma­tions de moindre priorité y sont également émises. Cela ne signifie cependant pas que le degré de gravité d’une erreur est plus élevé qu’au niveau Critical. Debug comprend tous les autres niveaux et émet ainsi toutes les com­mu­ni­ca­tions jusqu'au niveau Critical Error.

Le module de jour­na­li­sa­tion logging de Python

Le module de jour­na­li­sa­tion Python fait partie de la bi­blio­thèque Python. L'in­ter­face de jour­na­li­sa­tion assure que les in­te­rac­tions avec le reste du code source sont fluides ; en outre, elle reste en per­ma­nence opé­ra­tion­nelle. La jour­na­li­sa­tion simple et l’envoi des in­for­ma­tions sur un fichier sont ra­pi­de­ment inscrits dans le code existant à l'aide du pointeur. Le module logging dispose d'autres fonctions grâce aux­quelles vous pouvez adapter cet outil. Voici les prin­ci­paux éléments du module de jour­na­li­sa­tion :

  • Logger
  • Handler
  • Filter
  • Formatter

Les instances sont ré­ca­pi­tu­lées dans l'ins­tance LogRecord et s'échan­gent à l’intérieur de l’instance.

Logger

Logger en­re­gistre les actions pendant que le programme fonc­tionne. Il n'ap­pa­raît pas di­rec­te­ment en tant qu'ins­tance : il faut l'appeler avec la fonction logging.getLogger(Log­ger­name). Assignez un nom au Logger, par exemple pour figurer les hié­rar­chies de façon struc­tu­rée. En Python, on distingue sous-paquets et paquets en les séparant par des points (.). Le paquet log peut posséder des sous-paquets log.bam ou log.bar.loco. De la même manière, Logger fonc­tionne de telle sorte que l’objet « log » contient les in­for­ma­tions con­cer­nant ses sous-paquets « log.bam » et « log.bar.loco ».

Handler

Handler récupère les in­for­ma­tions de Logger et les transfère ailleurs. Handler est une classe de base qui détermine la façon dont l’interface de l’instance Handler agit. La classe Handler vous permet de dé­ter­mi­ner la cible. Stream­Hand­ler envoie les in­for­ma­tions dans un flux de sortie ; Fi­le­Hand­ler les envoie sur un fichier. Pour un programme donné, il est possible d’utiliser plusieurs Handler qui envoient les in­for­ma­tions du même Logger. C’est utile si on souhaite par exemple lire des in­for­ma­tions de débogage dans la console ou des messages d'erreur dans un fichier séparé.

À l'aide de la méthode setLevel (), vous pouvez définir le degré de gravité le plus faible qu’une in­for­ma­tion de journal pourra noter et renvoyer à Handler. Plutôt que logger.setLevel (qui détermine le niveau de jour­na­li­sa­tion Python), la méthode utilisée s'appelle alors [hand­ler­name].setLevel (cf. pré­sen­ta­tion de code ligne 5 : fh.setLevel).

Formatter

Con­trai­re­ment à Handler, l’objet de formatage Formatter s’utilise di­rec­te­ment en tant qu’instance dans le code d'ap­pli­ca­tion. Ces instances per­met­tent de dé­ter­mi­ner le format de jour­na­li­sa­tion python où votre no­ti­fi­ca­tion sera émise dans le fichier journal. Si vous n’effectuez pas de formatage, seule l'in­for­ma­tion spé­ci­fique à Logger apparaît. La fonction suivante vous permet d'appeler Formatter et de dé­ter­mi­ner le format des in­for­ma­tions et de la date :

logging.Formatter.__init__(fmt=[formatmessage], datefmt=[formatdate])
#ou bien :
logging.Formatter.__init__(fmt=None, datefmt=None)

Si vous ne précisez pas de format pour la date dans l'at­tri­but, Formatter fournit le format et la date à l’amé­ri­caine : « année-mois-jour heures:minutes:secondes ».

Filter

Filter permet de dé­ter­mi­ner de façon encore plus précise les in­for­ma­tions livrées. Dé­fi­nis­sez d'abord Filter, puis ajoutez-le dans le Handler cor­res­pon­dant ou dans Logger par la méthode addFilter(). Si la valeur de Filter, dé­ter­mi­née sur la base des pro­prié­tés de l’in­for­ma­tion, est False, l’in­for­ma­tion n’est pas transmise. Utilisez la fonction logging.Filter(name=fh), où l'at­tri­but fh fait référence à un nom de Logger, pour pouvoir autoriser les fichiers journaux d’un Logger donné et bloquer tous les autres Loggers.

Le module de jour­na­li­sa­tion Python avec un exemple

Python met à la dis­po­si­tion des dé­ve­lop­peurs l'outil graphique Turtle, pour tester des commandes de base. Dans l’exemple suivant, le testeur utilise Turtle. L'outil graphique doit courir tout droit sur un arrière-plan vert, tourner vers la gauche, continuer tout droit puis décrire un cercle. Dans cet exemple, nous intégrons les commandes de jour­na­li­sa­tion Python Info et Error :

# -*- coding: UTF-8 -*-
import turtle
import logging
turtle.bgcolor("green")
turtle.fd(30)
turtle.lt(90)
turtle.fd(50)
logging.info('Ça marche, ou plutôt ça court.')
turtle.circle(50)
logging.error("Oups, ça n’a pas marché comme prévu.")

La capture d'écran ci-dessus montre à quoi ressemble le résultat. Le module Turtle (fenêtre de gauche) a accepté les commandes et fonc­tionne comme prévu. Dans la fenêtre de droite, à côté de la commande Turtle, le code comprend aussi les commandes de jour­na­li­sa­tion de niveau INFO et ERROR. Le format de sortie classique d’un message journal est le suivant : [Niveau de gravité]: [Origine du message]:[In­for­ma­tion du message]

Toutefois, dans l’exemple, la console (Console 1/A) n'indique que le message de jour­na­li­sa­tion Python ERROR : Error:root:Oups, ça n’a pas marché comme prévu.

C’est dû au fait que la con­fi­gu­ra­tion de base du module logging de Python est réglée sur WARNING. Le module ignore toute in­di­ca­tion plus détaillée tant que ces réglages ne sont pas modifiés.

Modifier le niveau de jour­na­li­sa­tion Python

Les commandes suivantes vous per­met­tent de régler la con­fi­gu­ra­tion sur le niveau DEBUG :

import logging
logging.basicConfig(level=logging.DEBUG)

Dans l’image ci-dessus, la console montre la jour­na­li­sa­tion pour chaque nouvel appel. Si le programme est arrêté, la console supprime tous les en­re­gis­tre­ments. Pour garder l'œil sur vos données d'ins­crip­tion, vous devriez utiliser un fichier journal. Cette pratique porte en anglais le nom de Logging to File (« jour­na­li­sa­tion sur fichier »).

Consigner la jour­na­li­sa­tion Python sur un fichier

La jour­na­li­sa­tion Python sur fichier fonc­tionne de deux manières. Soit vous four­nis­sez un fichier journal précisant la con­fi­gu­ra­tion de base, soit vous utilisez Handler. Si vous ne dé­ter­mi­nez pas de cible, la jour­na­li­sa­tion Python dépose tem­po­rai­re­ment les in­for­ma­tions dans la console.

Créez un fichier pour votre jour­na­li­sa­tion Python comme suit :

import logging
logging.basicConfig( level=logging.DEBUG, filename='example.log')

Fi­le­Hand­ler est une instance de la classe Logging. Elle agit en même temps que l’instance logging. Elle sert à dé­ter­mi­ner où chaque type de fichier de jour­na­li­sa­tion sera envoyé et sous quel format. En plus de Fi­le­Hand­ler, il existe dans le module de jour­na­li­sa­tion de la bi­blio­thèque Python d'autres Handlers de jour­na­li­sa­tion, comme Stream­Hand­ler et Null­Hand­ler. Toutefois, pour pouvoir pour­suivre l'analyse des fichiers de jour­na­li­sa­tion, il est re­com­mandé d’utiliser un fichier journal.

Créez donc un Fi­le­Hand­ler qui consigne les messages Debug dans un fichier :

Dans l’image ci-dessus, la commande logging.getLogger() appelle le module jour­na­li­sa­tion Python. « fh » est défini comme Fi­le­Hand­ler possédant l’attribut « debug.log ». « fh » crée ainsi le fichier journal « debug.log » et vous transmet les in­for­ma­tions de journal. La méthode ad­dHand­ler() assigne au Logger le Handler cor­res­pon­dant. Vous pouvez nommer le fichier journal comme vous voulez.

Vous pouvez utiliser les fonctions suivantes pour tester tout cela vous-même :

import logging
logger = logging.getLogger('Journal_exemple')
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler('debug.log')
fh.setLevel(logging.DEBUG)
logger.addHandler(fh)
logger.debug('Information-Debug')
logger.info('Message info')
logger.warning('avertissement')
logger.error(message-erreur)
logger.critical('erreur grave')

Si le fichier journal fourni avec la jour­na­li­sa­tion Python sur fichier livre des in­for­ma­tions utiles pour certaines tâches, il arrive que des com­mu­ni­ca­tions simples ne soient pas suf­fi­santes. Un fichier d’ho­ro­da­tage (« Timestamp ») et le nom du Logger aident à mieux classer les in­di­ca­tions. L’image suivante donne un exemple de la façon dont vous pouvez dé­ter­mi­ner le format grâce à des attributs Formatter. Dans la fenêtre de Notepad debug.log, le texte indique les in­for­ma­tions de journal en précisant la date, l’heure, le nom de Logger, le niveau de journal et l’in­for­ma­tion.

Voici à nouveau le code pour vous permettre de procéder à des tests :

import logging
logger = logging.getLogger('Journal_exemple')
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler('debug.log')
fh.setLevel(logging.DEBUG)
logger.addHandler(fh)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
logger.addHandler(fh)
logger.debug('Information Debug')
logger.info('Information Info')
logger.warning('Avertissement')
logger.error('Message d’erreur')
logger.critical('Erreur grave')

En résumé

La jour­na­li­sa­tion Python avec le module logging est un outil pratique de pré­ven­tion des erreurs, de contrôle après piratage ou tout sim­ple­ment d’analyse. Alors que d'autres langages de pro­gram­ma­tion ajoutent la jour­na­li­sa­tion a pos­te­riori, le module de jour­na­li­sa­tion de Python est dis­po­nible dans la bi­blio­thèque standard. Si vous insérez la méthode dans votre code, les in­for­ma­tions de journal seront créées à dif­fé­rents niveaux : dans des fichiers et sur la console. Le formatage et Filter, tout comme Handler, per­met­tent une con­fi­gu­ra­tion qui répond aux besoins de l’uti­li­sa­teur. Dans ce contexte, pour vous sim­pli­fier le travail sur les fichiers journaux sous Python, veillez ab­so­lu­ment à attribuer ra­pi­de­ment des noms à votre Logger et à ses enfants.

Aller au menu principal