Articles

La journalisation Python – Guide le plus simple avec code complet et exemples

Le module de journalisation vous permet de surveiller l’exécution de votre code, de sorte que lorsque le code plante, vous pouvez vérifier les journaux et identifier ce qui l’a causé. Les messages de journalisation ont une hiérarchie intégrée – en commençant par les messages de débogage, d’information, d’avertissement, d’erreur et critiques. Vous pouvez également inclure des informations de traçabilité. Il est conçu pour les petits et grands projets python avec de multiples modules et est fortement recommandé pour toute programmation python modulaire. Ce post est une explication simple et claire de la façon d’utiliser le module de journalisation.

Logging in Python – Simplified Guide with Full Code and Examples. Photo par Andrea Reiman.

Contenu

    1. Pourquoi la journalisation ?
    2. Un exemple de base de journalisation
    3. Les 5 niveaux de journalisation
    4. Comment journaliser vers un fichier au lieu de la console
    5. Comment changer le format de journalisation
    6. Pourquoi travailler avec le logger racine pour tous les modules n’est pas la meilleure idée
    7. Comment créer un nouveau logger ?
    8. Qu’est-ce que et comment configurer un gestionnaire de fichiers et un formateur ?
    9. Comment inclure des informations de traçage dans les messages journalisés
    10. Exercices
    11. Conclusion

Pourquoi journaliser ?

Imaginez que vous exécutez un script python, et que vous voulez savoir quelle partie du script a été exécutée, à quel moment et, dans certains cas, inspecter les valeurs des variables.

Usuellement, vous pouvez juste ‘print()‘ sortir des messages significatifs afin de les voir dans la console. Et c’est probablement tout ce dont vous avez besoin lorsque vous développez de petits programmes.

Le problème est le suivant : lorsque vous utilisez cette approche sur des projets plus importants contenant de nombreux modules, vous voulez une approche plus structurée et plus robuste.

Pourquoi ?

Parce que, le code pourrait passer par différentes étapes comme dans le développement, le débogage, la revue, les tests ou va en production vous voulez voir différents niveaux de détails dans les messages imprimés.

Le type de messages que vous voulez imprimer pendant le développement ou le débogage peut être très différent de ce que vous voulez voir une fois qu’il va en production. Selon l’objectif, vous voulez que le code imprime différents types de messages.

Imaginez faire tout cela avec seulement if else et print déclarations.

En outre, un certain niveau de hiérarchie est nécessaire dans les messages imprimés.

C’est-à-dire que pendant une certaine exécution de ‘test’, vous voulez voir uniquement les avertissements et les messages d’erreur. Alors que pendant le ‘débogage’, des messages plus détaillés qui aident au débogage sont nécessaires. En outre, si vous voulez imprimer sur quel module et à quelle heure les codes ont été exécutés, le script python devient désordonné.

Toutes ces questions sont joliment traitées par le module logging.

En utilisant la journalisation, vous pouvez :

  1. Contrôler le niveau des messages pour n’enregistrer que ceux qui sont nécessaires
  2. Contrôler où afficher ou enregistrer les journaux
  3. Contrôler comment formater les journaux avec des modèles de messages intégrés-.dans les modèles de messages
  4. Savoir de quel module proviennent les messages

Vous pourriez dire ‘Je vois que logging peut être utile mais cela semble trop technique et semble un peu difficile à appréhender’. Eh bien, oui, logging nécessite un peu de courbe d’apprentissage mais c’est le but de ce post : rendre la journalisation facile à apprendre.

Sans plus attendre, entrons dans le vif du sujet.

Un exemple basique de journalisation

Python fournit un module intégré logging qui fait partie de la bibliothèque standard de python. Vous n’avez donc pas besoin d’installer quoi que ce soit.

Pour utiliser la journalisation, il suffit de mettre en place la configuration de base en utilisant logging.basicConfig(). En fait, cette configuration est également facultative. Nous verrons cela bientôt.

Puis, au lieu de print(), vous appelez logging.{level}(message) pour afficher le message dans la console.

import logginglogging.basicConfig(level=logging.INFO)def hypotenuse(a, b): """Compute the hypotenuse""" return (a**2 + b**2)**0.5logging.info("Hypotenuse of {a}, {b} is {c}".format(a=3, b=4, c=hypotenuse(a,b)))#> INFO:root:Hypotenuse of 3, 4 is 5.0

Le message de journalisation imprimé a le format par défaut suivant : {LEVEL}:{LOGGER}:{MESSAGE}.

Dans le cas ci-dessus, le niveau est info, car, j’ai appelé logging.info().

Le logger est appelé root, car c’est le logger par défaut et je n’en ai pas encore créé un nouveau.

Mais qu’est-ce qu’un logger au fait ?

Un logger est comme une entité que vous pouvez créer et configurer pour enregistrer différents types et formats de messages.

Vous pouvez configurer un logger qui imprime sur la console et un autre logger qui envoie les logs dans un fichier, qui a un niveau de log différent et qui est spécifique à un module donné. Plus d’explications et d’exemples à venir à ce sujet.

Enfin, le message est la chaîne que j’ai passée à logging.info().

Maintenant, que se serait-il passé si vous n’aviez pas configuré logging.basicConfig(level=logging.INFO)?

Réponse : Le journal n’aurait pas été imprimé.

Pourquoi ?

Pour le savoir, comprenons les niveaux de journalisation.

Les 5 niveaux de journalisation

logging a 5 niveaux hiérarchiques différents de journalisation auxquels un logger donné peut être configuré.

Voyons ce que la doc python a à dire sur chaque niveau:

  1. DEBUG : Informations détaillées, pour le diagnostic des problèmes. Valeur=10.
  2. INFO : Confirmer que les choses fonctionnent comme prévu. Valeur=20.
  3. WARNING : Quelque chose d’inattendu s’est produit, ou indiquant un problème quelconque. Mais le logiciel fonctionne toujours comme prévu. Valeur=30.
  4. ERROR : Problème plus grave, le logiciel n’est pas en mesure d’exécuter une certaine fonction. Valeur=40
  5. CRITIQUE : Une erreur grave, le programme lui-même peut être incapable de continuer à fonctionner. Valeur=50

Maintenant, pour en revenir à la question précédente de savoir ce qui se serait passé si vous n’aviez pas configuré logging.basicConfig(level=logging.INFO) dans l’exemple précédent.

La réponse est : le journal n’aurait pas été imprimé car, le logger par défaut est le ‘root’ et son niveau basicConfig par défaut est ‘WARNING’. Cela signifie que seuls les messages de logging.warning() et les niveaux supérieurs seront journalisés.

Donc, le message de logging.info() n’aurait pas été imprimé. Et c’est pourquoi la configuration de base a été définie comme INFO initialement (dans logging.basicConfig(level=logging.INFO)).

Si j’avais défini le niveau comme logging.ERROR à la place, seul le message de logging.error et logging.critical sera enregistré. Clair ?

import logginglogging.basicConfig(level=logging.WARNING)def hypotenuse(a, b): """Compute the hypotenuse""" return (a**2 + b**2)**0.5kwargs = {'a':3, 'b':4, 'c':hypotenuse(3, 4)}logging.debug("a = {a}, b = {b}".format(**kwargs))logging.info("Hypotenuse of {a}, {b} is {c}".format(**kwargs))logging.warning("a={a} and b={b} are equal".format(**kwargs))logging.error("a={a} and b={b} cannot be negative".format(**kwargs))logging.critical("Hypotenuse of {a}, {b} is {c}".format(**kwargs))#> WARNING:root:a=3 and b=3 are equal#> ERROR:root:a=-1 and b=4 cannot be negative#> CRITICAL:root:Hypotenuse of a, b is 5.0

Comment journaliser vers un fichier au lieu de la console

Pour envoyer les messages de journalisation vers un fichier à partir du logger root, vous devez définir l’argument file dans logging.basicConfig()

import logginglogging.basicConfig(level=logging.INFO, file='sample.log')

Maintenant, tous les messages de log ultérieurs iront directement dans le fichier ‘sample.log’ dans votre répertoire de travail actuel. Si vous voulez l’envoyer vers un fichier dans un répertoire différent, donnez le chemin d’accès complet au fichier.

Comment changer le format de journalisation

Le module de journalisation fournit des raccourcis pour ajouter divers détails aux messages journalisés. L’image ci-dessous, tirée de Python docs, montre cette liste.

Formats de journalisation
Formats de journalisation

Changeons le format du message de journalisation pour afficher l’HEURE, le NIVEAU et le MESSAGE. Pour cela, il suffit d’ajouter le format à l’argument format de logging.basiconfig().

import logginglogging.basicConfig(level=logging.INFO, format='%(asctime)s :: %(levelname)s :: %(message)s')logging.info("Just like that!")#> 2019-02-17 11:40:38,254 :: INFO :: Just like that!

6. Pourquoi travailler avec le logger racine pour tous les modules n’est pas la meilleure idée

Parce qu’ils vont tous partager le même logger ‘racine’.

Mais pourquoi est-ce mauvais ?

Regardons le code ci-dessous :

# 1. code inside myprojectmodule.pyimport logginglogging.basicConfig(file='module.log')#-----------------------------# 2. code inside main.py (imports the code from myprojectmodule.py)import loggingimport myprojectmodule # This runs the code in myprojectmodule.pylogging.basicConfig(file='main.log') # No effect, because!

Imaginez que vous avez un ou plusieurs modules dans votre projet. Et ces modules utilisent le module racine de base. Alors, lors de l’importation du module (‘myprojectmodule.py‘), tout le code de ce module s’exécute et le logger se configure.

Une fois configuré, le logger racine du fichier principal (qui a importé le module ‘myprojectmodule‘) ne pourra plus modifier les paramètres du logger racine. Car, les logging.basicConfig() une fois définis ne peuvent pas être modifiés.

Cela signifie que si vous voulez enregistrer les messages de myprojectmodule dans un fichier et les journaux du module principal dans un autre fichier, le logger root ne peut pas cela.

Pour ce faire, vous devez créer un nouveau logger.

Comment créer un nouveau logger?

Vous pouvez créer un nouveau logger en utilisant la méthode ‘logger.getLogger(name)‘. Si un logger avec le même nom existe, alors ce logger sera utilisé.

Bien que vous puissiez donner à peu près n’importe quel nom au logger, la convention est d’utiliser la variable __name__ comme ceci :

logger = logging.getLogger(__name__)logger.info('my logging message')

Mais, pourquoi utiliser __name__ comme nom du logger, au lieu de coder en dur un nom ?

Parce que la variable __name__ va contenir le nom du module (fichier python) qui a appelé le code. Ainsi, lorsqu’il est utilisé à l’intérieur d’un module, il créera un logger portant la valeur fournie par l’attribut __name__ du module.

En faisant cela, si vous finissez par changer le nom du module (nom du fichier) à l’avenir, vous n’avez pas à modifier le code interne.

Maintenant, une fois que vous avez créé un nouveau logger, vous devez vous souvenir de consigner tous vos messages en utilisant la nouvelle méthode logger.info() au lieu de la méthode logging.info() de la racine.

Un autre aspect à noter est que tous les enregistreurs ont une hiérarchie intégrée.

Qu’est-ce que je veux dire par là ?

Par exemple, si vous avez configuré l’enregistreur racine pour enregistrer les messages dans un fichier particulier. Vous avez également un logger personnalisé pour lequel vous n’avez pas configuré le gestionnaire de fichier pour envoyer des messages à la console ou à un autre fichier journal.

Dans ce cas, le logger personnalisé se repliera et écrira dans le fichier défini par le root logger lui-même. Jusqu’à ce que et à moins que vous configuriez le fichier de log de votre logger personnalisé.

Alors, qu’est-ce qu’un gestionnaire de fichiers et comment en configurer un ?

Qu’est-ce que et comment configurer un gestionnaire et un formateur de fichiers ?

Les classes FileHandler() et Formatter() sont utilisées pour configurer le fichier de sortie et le format des messages pour les loggers autres que le root logger.

Vous vous souvenez comment nous avons configuré le nom de fichier et le format du message dans le logger racine (à l’intérieur de logging.basicConfig()) plus tôt ?

Nous avons simplement spécifié les paramètres filename et format dans logging.basicConfig() et tous les journaux ultérieurs sont allés dans ce fichier.

Cependant, lorsque vous créez un logger séparé, vous devez les configurer individuellement à l’aide des objets logging.FileHandler() et logging.Formatter().

Une FileHandler permet de faire en sorte que votre logger personnalisé se connecte à un fichier différent. De même, un Formatter est utilisé pour modifier le format de vos messages journalisés.

import logging# Gets or creates a loggerlogger = logging.getLogger(__name__) # set log levellogger.setLevel(logging.WARNING)# define file handler and set formatterfile_handler = logging.FileHandler('logfile.log')formatter = logging.Formatter('%(asctime)s : %(levelname)s : %(name)s : %(message)s')file_handler.setFormatter(formatter)# add file handler to loggerlogger.addHandler(file_handler)# Logslogger.debug('A debug message')logger.info('An info message')logger.warning('Something is not right.')logger.error('A Major error has happened.')logger.critical('Fatal error. Cannot continue')

Veuillez remarquer comment nous définissons le formateur sur le ‘file_handler‘ et non le ‘logger‘ directement.

En supposant que le code ci-dessus soit exécuté depuis le programme principal, si vous regardez à l’intérieur du répertoire de travail, un fichier nommé logfile.log sera créé s’il n’existe pas et contiendra les messages ci-dessous.

#> 2019-02-17 12:40:14,797 : WARNING : __main__ : Something is not right.#> 2019-02-17 12:40:14,798 : ERROR : __main__ : A Major error has happened.#> 2019-02-17 12:40:14,798 : CRITICAL : __main__ : Fatal error. Cannot continue

Notez encore une fois que le Formatter est défini sur l’objet FileHandler et non directement sur le logger. Une chose à laquelle il faudra peut-être s’habituer.

Comment inclure des informations de traçage dans les messages journalisés

A part ‘debuginfowarningerror‘, et ‘critical‘, vous pouvez enregistrer des exceptions qui incluront toute information de traçage associée.

Avec logger.exception, vous pouvez consigner des informations de traçage si le code rencontre une erreur quelconque. logger.exception consignera le message fourni dans ses arguments ainsi que les informations de traceback du message d’erreur.

Vous trouverez ci-dessous un bel exemple.

import logging# Create or get the loggerlogger = logging.getLogger(__name__) # set log levellogger.setLevel(logging.INFO)def divide(x, y): try: out = x / y except ZeroDivisionError: logger.exception("Division by zero problem") else: return out# Logslogger.error("Divide {x} / {y} = {c}".format(x=10, y=0, c=divide(10,0)))#> ERROR:__main__:Division by zero problem#> Traceback (most recent call last):#> File "<ipython-input-16-a010a44fdc0a>", line 12, in divide#> out = x / y#> ZeroDivisionError: division by zero#> ERROR:__main__:None

Exercices

  1. Créer un nouveau répertoire de projet et un nouveau fichier python nommé ‘example.py‘. Importez le module de journalisation et configurez le logger racine au niveau des messages ‘debug’. Enregistrez un message ‘info’ avec le texte :  » Ceci est le message de journalisation du logueur racine ! « .
  2. Configurez le logueur racine pour formater le message  » Ceci est le message de journalisation du logueur racine ! » comme suit :
#> 2019-03-03 17:18:32,703 :: INFO :: Module <stdin> :: Line No 1 :: This is root logger's logging message!

Afficher la solution

import logginglogging.basicConfig(level=logging.INFO, format='%(asctime)s :: %(levelname)s :: Module %(module)s :: Line No %(lineno)s :: %(message)s')logging.info("This is root logger's logging mesage!")
  1. Créer un autre fichier python dans le même répertoire appelé ‘mymod.py‘ et créer un nouveau logger portant le nom du module. Configurez-le au niveau des messages d »erreur’ et faites en sorte qu’il envoie les sorties du journal dans un fichier appelé « mymod_{current_date}.log ».
  2. À partir du logger ‘mymod’ créé ci-dessus, enregistrez le message ‘critique’ suivant dans ledit fichier de journal : « Ceci est un message critique ! Ne l’ignorez pas ».

Conclusion

Mes félicitations si vous avez pu résoudre les exercices !

C’était assez utile et direct n’est-ce pas ?

La journalisation est un outil formidable mais n’est pas populaire est les flux de travail de la science de l’information comme elle devrait l’être. J’espère que les concepts de journalisation sont clairs et la prochaine fois que vous travaillez sur un projet basé sur python, ma demande aimable pour vous est de vous rappeler de donner au module logging un coup.

Happy logging!

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *