Logging

Le module logging fait parti de la bibliothèque standard de Python depuis la version 2.3. Il est succinctement décrit dans la PEP 282. La documentation est notoirement difficile à lire, à l’exception du basic logging tutorial.

Le logging sert deux buts:

  • Le logging de diagnostic enregistre les événements liés au fonctionnement de l’application. Si un utilisateur appelle pour signaler une erreur, par exemple, les logs peuvent être recherchés pour avoir un contexte.

  • logging d’audit enregistre les événements pour l’analyse des affaires. Les transactions d’un utilisateur peuvent être extraites et combinées avec d’autres détails de l’utilisateur pour les rapports ou pour optimiser un objectif d’affaires.

... ou Print?

La seule fois que print est une meilleure option que le logging est lorsque l’objectif est d’afficher une déclaration d’aide pour une application en ligne de commande. D’autres raisons pour lesquelles le logging est mieux que print:

  • Le log record, qui a été créé avec chaque événement de logging, contient des informations de diagnostic facilement disponibles tels que le nom du fichier, le chemin complet, la fonction et le numéro de ligne de l’événement de logging.

  • Les événements loggués dans les modules inclus sont automatiquement accessibles via la racine du logger au flux de logs de votre application, à moins que vous ne les filtriez.

  • Le logging peut être réduite au silence de manière sélective en utilisant la méthode logging.Logger.setLevel() ou désactivée en définissant l’attribut logging.Logger.disabled à True.

Logging dans une bibliothèque

Les notes pour configuring logging for a library sont dans le logging tutorial. Parce que l’utilisateur, pas la bibliothèque, devrait dicter ce qui se passe quand un événement de logging a lieu, une admonition à accepter est de se répéter:

Note

Il est fortement recommandé de ne pas ajouter de handlers autres que NullHandler aux loggers de votre bibliothèque.

Les meilleures pratiques lors de l’instanciation des loggers dans une bibliothèque est de seulement les créer en utilisant la variable globale __name__: le module logging crée une hiérarchie de loggers en utilisant la notation point, donc utiliser __name__ assure l’absence de collisions de noms.

Voici un exemple de bonne pratique depuis la requests source – placez ceci dans votre __init__.py

# Set default logging handler to avoid "No handler found" warnings.
import logging
try:  # Python 2.7+
    from logging import NullHandler
except ImportError:
    class NullHandler(logging.Handler):
        def emit(self, record):
            pass

logging.getLogger(__name__).addHandler(NullHandler())

Logging dans une application

Le site twelve factor app, une référence faisant autorité pour les bonnes pratiques dans le développement d’application, contient une section sur les meilleures pratiques de logging. Elle prône avec insistance pour le traitement des événements de journal comme un flux d’événements, et pour envoyer ce flux d’événements à la sortie standard pour être traité par l’environnement d’application.

Il y a au moins 3 manières de configurer un logger:

  • En utilisant un fichier formaté sous forme INI
    • Avantages: possible de mettre à jour la configuration lors de l’exécution en utilisant la fonction logging.config.listen() pour écouter sur un socket.

    • Inconvénients: moins de contrôle (par exemple pour des loggers ou filtres comme sous-classes) que possible lors de la configuration d’un logger dans le code.

  • En utilisant un dictionnaire ou un fichier formaté sous forme JSON:
    • Avantages: en plus de la mise à jour tout en exécutant, il est possible de charger à partir d’un fichier en utilisant le module json, dans la bibliothèque standard depuis Python 2.6.

    • Inconvénients: moins de contrôle que quand configuration d’un logger dans le code.

  • En utilisant du code:
    • Avantages: contrôle complet sur la configuration.

    • Inconvénients: modifications nécessitent un changement du code source.

Exemple de configuration via un fichier INI

Disons que le fichier est nommé logging_config.ini. Plus de détails pour le format de fichier se trouvent dans la section logging configuration du logging tutorial.

[loggers]
keys=root

[handlers]
keys=stream_handler

[formatters]
keys=formatter

[logger_root]
level=DEBUG
handlers=stream_handler

[handler_stream_handler]
class=StreamHandler
level=DEBUG
formatter=formatter
args=(sys.stderr,)

[formatter_formatter]
format=%(asctime)s %(name)-12s %(levelname)-8s %(message)s

Ensuite, utilisez logging.config.fileConfig() dans le code:

import logging
from logging.config import fileConfig

fileConfig('logging_config.ini')
logger = logging.getLogger()
logger.debug('often makes a very good meal of %s', 'visiting tourists')

Exemple de configuration via un dictionnaire

A partir de Python 2.7, vous pouvez utiliser un dictionnaire avec les détails de configuration. La PEP 391 contient une liste des éléments obligatoires et optionnels dans le dictionnaire de configuration.

import logging
from logging.config import dictConfig

logging_config = dict(
    version = 1,
    formatters = {
        'f': {'format':
              '%(asctime)s %(name)-12s %(levelname)-8s %(message)s'}
        },
    handlers = {
        'h': {'class': 'logging.StreamHandler',
              'formatter': 'f',
              'level': logging.DEBUG}
        },
    root = {
        'handlers': ['h'],
        'level': logging.DEBUG,
        },
)

dictConfig(logging_config)

logger = logging.getLogger()
logger.debug('often makes a very good meal of %s', 'visiting tourists')

Exemple de configuration directement dans le code

import logging

logger = logging.getLogger()
handler = logging.StreamHandler()
formatter = logging.Formatter(
        '%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)

logger.debug('often makes a very good meal of %s', 'visiting tourists')