Le module logging permet de faire des logs. Ces logs peuvent avoir différents niveaux de détail, et différentes destinations : fichiers textes, mails, etc.
Source : Le PyMOTW de Doug Hellman.
Dans cette section, on montre comment écrire des logs dans un fichier. Pour ce faire, il faut placer ce bout de code dans un fichier, puis l'exécuter avec python :
import logging FICHIER_LOG = '/tmp/log_exemple.log' logging.basicConfig(filename = FICHIER_LOG, level = logging.DEBUG) logging.debug('Un message dans le fichir log')
Un fichier log_exemple.log apparaît alors dans /tmp, qui contient :
$ more /tmp/log_exemple.log DEBUG:root:Un message dans le fichir log
Si on relance une seconde fois le script, on trouve
$ more /tmp/log_exemple.log DEBUG:root:Un message dans le fichir log DEBUG:root:Un message dans le fichir log
Notons dès à présent qu'en l'absence de l'argument filename, les messages seront envoyés dans le terminal.
Pour créer un nouveau fichier à chaque appel, un argument filemode = 'w' doit être passé au constructeur basicConfig(), comme suit :
import logging FICHIER_LOG = '/tmp/log_exemple.log' logging.basicConfig(filename = FICHIER_LOG, level = logging.DEBUG, filemode = 'w') logging.debug('Un message dans le fichir log')
Le contenu du fichier log_exemple.log de /tmp est alors remplacé par :
$ more /tmp/log_exemple.log DEBUG:root:Un message dans le fichir log
Plutôt que de gérer soi-même la taille des fichiers logs, on peut préciser la taille maximale d'un tel fichier, avant la création d'un nouveau log. Pour cela, on doit utiliser RotatingFileHandler.
#-*-coding:utf8-*-
import logging
import logging.handlers
FICHIER_LOG = '/tmp/log_exemple.log'
# On n'utilise plus une configuration de base, mais on crée à la main
# son propre objet Logger
mon_logger = logging.getLogger('MonLogger')
mon_logger.setLevel(logging.DEBUG)
# On précise alors combien d'octets chaque fichier peut contenir, et
# combien de fichiers logs différents on autorise :
handler = logging.handlers.RotatingFileHandler(FICHIER_LOG,
maxBytes = 20,
backupCount = 5)
mon_logger.addHandler(handler)
# Reste à remplir artificiellement les logs
for i in range(20):
mon_logger.debug('i = %d' % i
)
Si on liste le contenu de /tmp, on s'aperçoit que plusieurs fichiers logs ont bien été créés...
$ ls /tmp/log* /tmp/log_exemple.log /tmp/log_exemple.log.2 /tmp/log_exemple.log.4 /tmp/log_exemple.log.1 /tmp/log_exemple.log.3 /tmp/log_exemple.log.5
et qu'ils ne contiennent à chaque fois que le nombre spécifié d'octets...
$ more /tmp/log_exemple.log.1 k = 17 k = 18
Pour le remplissage, la règle est la suivante :
Il existe différents niveaux de logs, avec des valeurs numériques associées :
On peut produire différents types de messages, avec différents niveaux d'importance, et décider de n'afficher (ou de n'enregistrer) que les messages supérieurs à un niveau donné.
Par exemple, si un message $m$ est de type WARNING, et qu'on a demandé au logger de n'afficher que des erreurs (niveaux supérieurs à 40), alors $m$ ne sera pas émis.
Considérons le bout de code suivant...
#-*-coding:utf8-*- import logging logging.basicConfig(level = logging.WARNING) logging.debug("Ceci est un message de deboguage") logging.info("Ceci est un message d'information") logging.warning("Ceci est un message d'alerte") logging.error("Ceci est un message d'erreur") logging.critical("Ceci est un message critique")
Il est constitué de 5 messages de log, chacun ayant un niveau différent. Au constructeur basicConfig, on a précisé que les messages émis doivent au moins être des alertes (donc tout message d'un niveau d'importance supérieur ou égal à l'alerte, à savoir l'alerte, l'erreur, et le critique).
Puisqu'aucun fichier n'est précisé au niveau du constructeur, l'émission se fera dans le terminal.
Après exécution, on y trouvera les messages suivants...
WARNING:root:Ceci est un message d'alerte ERROR:root:Ceci est un message d'erreur CRITICAL:root:Ceci est un message critique
Jusqu'à présent, les logs émis contenaient tous root. Cela provient du fait que le module logging permet la création de différents objets logger, pouvant hériter les uns des autres, et possédant leurs noms propres.
Cela permet de préciser, dans le message, d'où provient le log (de quel package, par exemple) : il suffit de créer un nouvel objet à chaque fois qu'une origine est à distinguer.
Ainsi, le code suivant
#-*-coding:utf8-*- import logging logging.basicConfig(level = logging.WARNING) logger1 = logging.getLogger('package1.module1') logger2 = logging.getLogger('package2.module2') logger1.warning("Ce message provient d'un module") logger2.warning("ET ce message provient d'un autre module")
produit la sortie
WARNING:package1.module1:Ce message provient d'un module WARNING:package2.module2:ET ce message provient d'un autre module
Chaque nouvel objet logger hérite de la configuration de son parent, et peut éventuellement être reconfiguré différement (changer le niveau d'alertes, par exemple).