Configuration et utilisation de la journalisation

Django contient une configuration de journalisation par défaut fonctionnelle qui peut facilement être étendue.

Appel de journalisation basique

Pour envoyer un message de journalisation à partir de votre code, écrivez un appel de journalisation.

Ne soyez pas tenté-e-s d’utiliser des appels de journalisation dans settings.py.

La façon dont la journalisation de Django est configuré dans le cadre de la fonction setup() signifie que les appels de journalisation placés dans settings.py peuvent ne pas fonctionner comme prévu, car la journalisation n’est pas encore configurée à ce stade. Pour explorer la journalisation, utilisez une fonction de vue comme suggéré dans l’exemple ci-dessous.

Tout d’abord, importez la bibliothèque Python logging et obtenez une instance de journalisation avec logging.getLogger(). Passez à la méthode getLogger() un nom pour l’identifier et pour les lignes qu’il produira. Une bonne option est d’utiliser __name__ (voir Utilisation de l’espace de noms des journaliseurs ci-dessous pour en savoir plus), ce qui donnera le nom du module Python actuel sous forme de chemin pointé

import logging

logger = logging.getLogger(__name__)

Par convention, il est bon d’effectuer cette déclaration au niveau du module.

Puis dans une fonction, par exemple dans une vue, envoyez un enregistrement au journaliseur

def some_view(request):
    ...
    if some_risky_state:
        logger.warning('Platform is running at risk')

Lorsque ce code est exécuté, un objet LogRecord contenant ce message sera envoyé au journaliseur. SI vous utilisez la configuration de journalisation de Django par défaut, le message apparaîtra dans la console.

Le niveau WARNING utilisé dans l’exemple ci-dessus est l’un des quelques niveaux de sévérité de journalisation: DEBUG`, INFO, WARNING, ERROR, CRITICAL. Un autre exemple pourrait être

logger.critical('Payment system is not responding')

Important

Les enregistrements ayant un niveau plus faible que WARNING n’apparaîtront pas dans la console par défaut. La modification de ce comportement nécessite une configuration supplémentaire.

Personnalisation de la configuration de journalisation

Même si la configuration de journalisation de Django fonctionne telle quelle, vous pouvez contrôler exactement la manière dont vos journaux sont envoyés à diverses destinations, vers des fichiers journaux, des services externes, par courriel, etc. en ajoutant un peu de configuration supplémentaire.

Vous pouvez configurer :

  • les correspondances des journaliseurs (logger) pour déterminer quels enregistrements sont envoyés à quels gestionnaires
  • les gestionnaires (handler) pour déterminer ce qu’ils font des enregistrements qu’ils reçoivent
  • les filtres, pour ajouter du contrôle supplémentaire sur le transfert des enregistrements, et même les modifier sur place
  • les formateurs, pour convertir les objets LogRecord en une chaîne ou toute autre forme qui soit lisible par les humains ou un autre système

Il existe plusieurs manières de configurer la journalisation. Dans Django, le réglage LOGGING est le plus couramment utilisé. Ce réglage utilise le format dictConfig, et étend la configuration de journalisation par défaut.

Consultez Configuration de la journalisation pour une explication sur la manière dont vos réglages personnalisés sont fusionnés avec ceux de Django.

Consultez la documentation de journalisation de Python pour plus de détails sur les autres manières de configurer la journalisation. Par simplification, cette documentation ne va considérer que la configuration au travers du réglage LOGGING.

Configuration de journalisation basique

Lors de la configuration de la journalisation, il est raisonnable de

Créer un dictionnaire LOGGING

Dans votre fichier settings.py:

LOGGING = {
    'version': 1,                       # the dictConfig format version
    'disable_existing_loggers': False,  # retain the default loggers
}

Il est presque toujours recommandé de conserver et étendre la configuration de journalisation par défaut en définissant disable_existing_loggers à False.

Configurer un gestionnaire

Cet exemple configure un seul gestionnaire (handler) nommé file, qui utilise la classe FileHandler de Python pour enregistrer les journaux de niveau DEBUG et plus élevé dans le fichier general.log (à la racine du projet) :

LOGGING = {
    [...]
    'handlers': {
        'file': {
            'class': 'logging.FileHandler',
            'filename': 'general.log',
        },
    },
}

Différentes classes de gestionnaire acceptent différentes options de configuration. Pour plus d’informations sur les classes de gestionnaire disponibles, consultez AdminEmailHandler fournie par Django et les diverses classes de gestionnaire fournies par Python.

Les niveaux de journalisation peuvent aussi être définis sur les gestionnaires (par défaut, elles acceptent des messages de tous les niveaux). En utilisant l’exemple ci-dessus, l’ajout de :

{
    'class': 'logging.FileHandler',
    'filename': 'general.log',
    'level': 'DEBUG',
}

définirait une configuration de gestionnaire n’acceptant que des enregistrements de niveau DEBUG ou supérieur.

Configuration d’une correspondance de journaliseur

Pour envoyer des enregistrements à ce gestionnaire, configurez une correspondance pour l’utiliser, par exemple :

LOGGING = {
    [...]
    'loggers': {
        '': {
            'level': 'DEBUG',
            'handlers': ['file'],
        },
    },
}

Le nom de correspondance détermine quels sont les enregistrements de journaux qu’il va traiter. Cette configuration ('') is anonyme. Cela signifie qu’elle traitera les enregistrements de tous les journaliseurs (voir Utilisation de l’espace de noms des journaliseurs ci-dessous sur la manière d’utiliser les noms de correspondance pour déterminer les journaliseurs concernés par le traitement de ses enregistrements).

Ceci va transmettre les messages de niveau DEBUG et supérieur au gestionnaire nommé file.

Notez qu’un journaliseur peut transmettre des messages à plusieurs gestionnaires, il existe donc une relation de type plusieurs-à-plusieurs entre les journaliseurs et les gestionnaires.

Si vous exécutez

logger.debug('Attempting to connect to API')

dans vote code, vous trouverez ce message dans le fichier general.log` à la racine de votre projet.

Configuration d’un formateur

Par défaut, le résultat final de journalisation contient la partie message de chaque enregistrement de journal. Utilisez un formateur si vous souhaitez inclure des données supplémentaires. Commencez par nommer et définir vos formateurs. Cet exemple définit des formateurs nommés verbose et simple:

LOGGING = {
    [...]
    'formatters': {
        'verbose': {
            'format': '{name} {levelname} {asctime} {module} {process:d} {thread:d} {message}',
            'style': '{',
        },
        'simple': {
            'format': '{levelname} {message}',
            'style': '{',
        },
    },
}

Le mot-clé style permet d’indiquer { pour la mise en forme str.format() ou $ pour string.Template; la valeur par défaut est $.

Voir LogRecord attributes au sujet des attributs de LogRecord qu’il est possible d’inclure.

Pour appliquer un formateur à un gestionnaire, ajoutez une entrée formatter au dictionnaire du gestionnaire en vous référant au formateur par son nom, par exemple :

'handlers': {
    'file': {
        'class': 'logging.FileHandler',
        'filename': 'general.log',
        'formatter': 'verbose',
    },
},

Utilisation de l’espace de noms des journaliseurs

La configuration de journalisation anonyme '' capture les journaux de n’importe quelle application Python. Une configuration de journalisation nommée ne capturera que les journaux de journaliseurs dont le nom correspond.

L’espace de noms d’une instance de journaliseur est défini en utilisant getLogger(). Par exemple, dans views.py de my_app:

logger = logging.getLogger(__name__)

va créer un journaliseur dans l’espace de noms my_app.views. __name__ permet d’organiser automatiquement les messages de journalisation en fonction de leur provenance dans les applications de votre projet. Cela garantit par la même occasion que les collisions de noms seront évitées.

Une correspondance de journaliseur nommée my_app.views va capturer les enregistrements de ce journaliseur :

LOGGING = {
    [...]
    'loggers': {
        'my_app.views': {
            ...
        },
    },
}

Une correspondance de journaliseur nommée my_app sera plus permissive, capturant les enregistrements de journaliseurs partout dans l’espace de noms my_app (y compris my_app.views`, my_app.utils et ainsi de suite) :

LOGGING = {
    [...]
    'loggers': {
        'my_app': {
            ...
        },
    },
}

Il est aussi possible de définir les espaces de noms des journaliseurs de manière explicite

logger = logging.getLogger('project.payment')

et de définir ensuite en fonction les correspondances de journaliseurs.

Utilisation des hiérarchies et de la propagation des journaliseurs

La nomenclature des journaliseurs est hiérarchique. my_app est le parent de my_app.views, qui est le parent de my_app.views.private. Sans autre indication, les correspondances de journaliseurs vont propager les enregistrements qu’elles traitent à leurs parents ; un enregistrement d’un journaliseur dans l’espace de noms my_app.views.private sera traité à la fois par les correspondances de my_app et de my_app.views.

Pour contrôler ce comportement, donnez une valeur à la clé de propagation sur les correspondances que vous définissez

LOGGING = {
    [...]
    'loggers': {
        'my_app': {
            [...]
        },
        'my_app.views': {
            [...]
        },
        'my_app.views.private': {
            [...]
            'propagate': False,
        },
    },
}

propagate vaut True par défaut. Dans cet exemple, les journaux de my_app.views.private ne seront pas traités par les parents, mais les journaux de my_app.views le seront.

Configuration de journalisation réactive

La journalisation est d’autant plus utile qu’elle contient des informations riches, mais sans information inutile, et cette pesée d’intérêts dépend de ce que vous faites. Lorsque vous êtes en mode débogage, le niveau d’informations souhaité dépasse largement ce qui sera nécessaire lorsque vous passerez votre projet en production.

Vous pouvez configurer la journalisation pour qu’elle vous offre le niveau de détail nécessaire au moment où vous en avez besoin. Au lieu de modifier manuellement la configuration pour cet objectif, une meilleure façon est d’appliquer une configuration automatiquement en fonction de l’environnement.

Par exemple, vous pouvez définir une variable d’environnement DJANGO_LOG_LEVEL appropriée dans vos environnements de développement et de pré-production, et en faire usage dans une configuration de journaliseur de cette façon

'level': os.getenv('DJANGO_LOG_LEVEL', 'WARNING')

de cette manière, tant que l’environnement n’indique par un niveau de journalisation plus bas, cette configuration ne propagera que les enregistrements de sévérité WARNING et supérieure à son gestionnaire.

D’autres options de la configuration (comme les options level ou formatter des gestionnaires) peuvent également être ajustées de cette façon.

Back to Top