Cours
Si vous débutez en Python et souhaitez aller plus loin, suivez le cours Introduction to Data Science in Python de DataCamp.
De manière générale, le logging consiste à conserver un journal généré par un programme informatique. Un fichier journal (log) contient des enregistrements d'événements survenus dans votre code (par exemple, un code Python) ou votre système d'exploitation. Ces événements ou messages sont écrits dans un fichier de log ; un message ou un événement peut aussi représenter une interaction entre différents utilisateurs.
La façon de générer les logs dépend souvent des développeurs. Ils peuvent vouloir journaliser chaque opération d'une application, n'écrire que lorsqu'une erreur survient, ou encore suivre des opérations essentielles réalisées par un utilisateur. Une opération essentielle pourrait être d'identifier quel utilisateur final ou quelle adresse IP a accédé à l'application ou au serveur ; en cas de tentative infructueuse, le développeur peut alors s'appuyer sur les logs pour obtenir des informations plus précises qu'une simple pile d'appels.
Un bon exemple est lorsque vous essayez d'installer un paquet sur Linux avec sudo sans avoir les droits sudo. Vous voyez le message "Xyz is not in the sudoers file. This incident will be reported", qui est écrit dans un fichier de log.
La bonne nouvelle, c'est que Python inclut un module de logging prêt à l'emploi, sans installation supplémentaire.
En quoi le logging diffère-t-il d'une fonction print ?
Le logging est proche, dans l'esprit, d'une fonction print. Cependant, print ne fournit pas de nombreuses informations utiles au développeur. Le logging, lui, peut enregistrer l'horodatage et le numéro de ligne où l'erreur s'est produite. Il peut écrire des erreurs ou toute information dans des fichiers, des sockets, etc., et propose cinq niveaux de gravité pour différencier vos journaux.
Les cinq niveaux de logging présentés ici sont :
-
Debug (10) : utile pour diagnostiquer les problèmes dans le code.
-
Info (20) : peut servir d'accusé de réception qu'aucun bug n'est présent dans le code. Un bon cas d'usage du niveau Info est le suivi de l'entraînement d'un modèle de machine learning.
-
Warning (30) : indique un problème potentiel à venir. Par exemple, l'avertissement d'un module qui pourrait être déprécié ou un avertissement de mémoire vive insuffisante.
-
Error (40) : un bug sérieux dans le code, comme une erreur de syntaxe, un dépassement mémoire, des exceptions.
-
Critical (50) : une erreur pouvant entraîner l'arrêt du programme ou une fin d'exécution brutale.
Assez de théorie, n'est-ce pas ?
Voyons maintenant comment implémenter le logging dans un code Python.
Mais avant cela, parcourons la table des attributs de logging, car vous utiliserez certains des attributs ci-dessous dans ce tutoriel.
import logging
Vous allez maintenant appeler la méthode basicConfig(), qui crée une configuration de base pour que le système de logging fonctionne correctement.
Elle permet de définir les paramètres suivants :
- la gravité du logging : les cinq niveaux de logging.
- l'enregistrement des événements dans un fichier.
- l'écriture uniquement des journaux actuels en écrasant les anciens via le paramètre filemode.
- le format de fichier dans lequel les logs sont stockés.
N'oubliez pas que la méthode basicConfig() est facultative et que vous pouvez choisir de ne pas l'utiliser.
logging.basicConfig()
Construisons maintenant les cinq niveaux de gravité.
logging.debug("A Debug Logging Message")
logging.info("A Info Logging Message")
logging.warning("A Warning Logging Message")
WARNING:root:A Warning Logging Message
logging.error("An Error Logging Message")
ERROR:root:An Error Logging Message
logging.critical("A Critical Logging Message")
CRITICAL:root:A Critical Logging Message
Comme vous pouvez le constater ci-dessus, seuls les messages dont la gravité est supérieure à 20 ont été effectivement journalisés, car c'est la configuration par défaut du logging en Python.
Cependant, comme indiqué plus haut, vous pouvez modifier les niveaux de gravité avec la méthode basicConfig(). Ici, vous passez le niveau de gravité à 20, ce qui signifie que les niveaux ayant une gravité supérieure à 10 seront logués.
Remarque : merci de redémarrer le noyau avant d'exécuter les cellules ci-dessous afin d'obtenir le bon résultat.
import logging
logging.basicConfig(level=logging.INFO)
logging.debug("A Debug Logging Message")
logging.info("A Info Logging Message")
INFO:root:A Info Logging Message
logging.warning("A Warning Logging Message")
WARNING:root:A Warning Logging Message
logging.error("An Error Logging Message")
ERROR:root:An Error Logging Message
logging.critical("A Critical Logging Message")
CRITICAL:root:A Critical Logging Message
Journalisons maintenant les sorties ci-dessus dans un fichier log. Pour cela, utilisez la méthode basicConfig().
import logging
logging.basicConfig(level = logging.INFO, filename = 'datacamp.log')
logging.debug("A Debug Logging Message")
logging.info("A Info Logging Message")
logging.warning("A Warning Logging Message")
logging.error("An Error Logging Message")
logging.critical("A Critical Logging Message")

Comme vous le voyez, les logs sont désormais générés dans le fichier datacamp.log. En exécutant les cellules ci-dessus plusieurs fois, vous constaterez que les entrées sont ajoutées au fichier à chaque exécution. Par défaut, le fichier est ouvert en mode append.

Vous pouvez utiliser le mot-clé filemode pour éviter que le fichier de log ne contienne des doublons (mode append). Passez le mode de fichier à write pour écraser les anciens logs et ne conserver que les plus récents. Comme filemode est défini sur w, le fichier de log sera ouvert en mode écriture à chaque appel à basicConfig(), ce qui finira par écraser le fichier.
Le fonctionnement du mode de fichier dans logging est similaire à la gestion standard des fichiers proposée par Python.
import logging
logging.basicConfig(level = logging.INFO, filename = 'datacamp.log', filemode = 'w')
logging.debug("A Debug Logging Message")
logging.info("A Info Logging Message")
logging.warning("A Warning Logging Message")
logging.error("An Error Logging Message")
logging.critical("A Critical Logging Message")
Exécutez plusieurs fois les lignes de code ci-dessus. Vous remarquerez que cette fois, seules les entrées courantes sont sauvegardées et les précédentes sont écrasées, comme ci-dessous.

Regardons d'autres attributs de logging comme la date, l'heure, et le numéro de ligne où l'avertissement ou l'erreur a été généré. Vous en conviendrez : ce sont autant d'atouts par rapport à un simple print, et le logging est bien plus puissant.
Pour ce faire, vous passerez les attributs asctime et lineno. Vous passerez aussi l'attribut message, qui sera l'emplacement réservé au texte que vous souhaitez afficher dans les logs. Tous ces attributs seront transmis au paramètre format.
import logging
logging.basicConfig(format='Date-Time : %(asctime)s : Line No. : %(lineno)d - %(message)s', \
level = logging.DEBUG)
logging.debug("A Debug Logging Message")
logging.info("A Info Logging Message")
logging.warning("A Warning Logging Message")
logging.error("An Error Logging Message")
logging.critical("A Critical Logging Message")
Date-Time : 2020-03-18 23:58:12,429 : Line No. : 1 - A Debug Logging Message
Date-Time : 2020-03-18 23:58:12,436 : Line No. : 2 - A Info Logging Message
Date-Time : 2020-03-18 23:58:12,437 : Line No. : 3 - A Warning Logging Message
Date-Time : 2020-03-18 23:58:12,440 : Line No. : 4 - An Error Logging Message
Date-Time : 2020-03-18 23:58:12,441 : Line No. : 5 - A Critical Logging Message
Voyons les définitions de chaque attribut directement depuis ce tableau.
-
asctime: heure lisible par l'humain à laquelle le LogRecord a été généré. Par défaut, au format2020-03-18 22:24:42,670(les chiffres après la virgule sont les millisecondes). Comme il s'agit d'une chaîne, vous utilisez le spécificateursaprès cet attribut. -
lineno: numéro de ligne source où l'appel au logging a été effectué. Comme c'est un entier, vous utilisez le spécificateurdaprès cet attribut. -
message: le message journalisé, calculé comme msg % args. Il est défini quandFormatter.format()est invoqué. Si vous ne précisez pas cet attribut via le paramètre format(), les messages que vous souhaitez consigner ne seront pas générés. De même, pour l'attribut message, vous utilisezspuisqu'il s'agit d'une chaîne. -
funcName: affiche le nom du module ou de la fonction dans lequel le log est produit. Utile lorsque votre code comporte de nombreuses fonctions et que vous souhaitez regrouper les logs par fonction ou méthode.
Voyons un exemple simple pour remplacer print par logging dans une fonction de debug.
import logging
logging.basicConfig(format='%(asctime)s :: %(levelname)s :: %(funcName)s :: %(lineno)d \
:: %(message)s', level = logging.INFO)
def addition(x, y):
add = x + y
return add
def subtract(x, y):
sub = x - y
return sub
def multiply(x, y):
mul = x * y
return mul
def divide(x, y):
div = x / y
return div
def exponent(x, y):
exp = x ** y
return exp
num1 = 20
num2 = 2
def main():
add_result = addition(num1, num2)
logging.info('Add: {} + {} = {}'.format(num1, num2, add_result))
sub_result = subtract(num1, num2)
logging.info('Sub: {} - {} = {}'.format(num1, num2, sub_result))
mul_result = multiply(num1, num2)
logging.info('Mul: {} * {} = {}'.format(num1, num2, mul_result))
div_result = divide(num1, num2)
logging.info('Div: {} / {} = {}'.format(num1, num2, div_result))
exp_result = exponent(num1, num2)
logging.info('Exp: {} ** {} = {}'.format(num1, num2, exp_result))
main()
2020-03-19 00:01:27,372 :: INFO :: main :: 35 :: Add: 20 + 2 = 22
2020-03-19 00:01:27,376 :: INFO :: main :: 38 :: Sub: 20 - 2 = 18
2020-03-19 00:01:27,377 :: INFO :: main :: 41 :: Mul: 20 * 2 = 40
2020-03-19 00:01:27,378 :: INFO :: main :: 44 :: Div: 20 / 2 = 10.0
2020-03-19 00:01:27,379 :: INFO :: main :: 47 :: Exp: 20 ** 2 = 400
Conclusion
Félicitations pour avoir terminé ce tutoriel.
N'hésitez pas à explorer d'autres attributs de logging et à voir comment les exploiter dans votre code.
Vous pouvez poser toutes vos questions liées à ce tutoriel dans la section des commentaires ci-dessous.
Si vous débutez en Python et souhaitez en apprendre davantage, suivez le cours Introduction to Data Science in Python de DataCamp.