== Logging

La structure des niveaux de journaux est essentielle.

[quote,Dan North, former ToughtWorks consultant]
When deciding whether a message should be ERROR or WARN, imagine being woken up at 4 a.m. Low printer toner is not an ERROR.

* *DEBUG*: Il s'agit des informations qui concernent tout ce qui peut se passer durant l'exécution de l'application. Généralement, ce niveau est désactivé pour une application qui passe en production, sauf s'il est nécessaire d'isoler un comportement en particulier, auquel cas il suffit de le réactiver temporairement.
* *INFO*: Enregistre les actions pilotées par un utilisateur - Démarrage de la transaction de paiement, ...
* *WARN*: Regroupe les informations qui pourraient potentiellement devenir des erreurs.
* *ERROR*: Indique les informations internes - Erreur lors de l'appel d'une API, erreur interne, ...
* *FATAL* (ou *EXCEPTION*): ... généralement suivie d'une terminaison du programme ;-) - Bind raté d'un socket, etc.

La configuration des _loggers_ est relativement simple, un peu plus complexe si nous nous penchons dessus, et franchement complète si nous creusons encore.
Il est ainsi possible de définir des formattages, gestionnaires (_handlers_) et loggers distincts, en fonction de nos applications.

Sauf que comme nous l'avons vu avec les 12 facteurs, nous devons traiter les informations de notre application comme un flux d'évènements.
Il n'est donc pas réellement nécessaire de chipoter la configuration, puisque la seule classe qui va réellement nous intéresser concerne les `StreamHandler`.
La configuration que nous allons utiliser est celle-ci:

. Formattage: à définir - mais la variante suivante est complète, lisible et pratique: `{levelname} {asctime} {module} {process:d} {thread:d} {message}`
. Handler: juste un, qui définit un `StreamHandler`
. Logger: pour celui-ci, nous avons besoin d'un niveau (`level`) et de savoir s'il faut propager les informations vers les sous-paquets, auquel cas il nous suffira de fixer la valeur de `propagate` à `True`.


[source,python]
----
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
        },
        'simple': {
            'format': '{levelname} {asctime} {module} {message}',
        },
    },
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': "verbose"
        }
    },
    'loggers': {
        'khana': {
            'handlers': ['console'],
            'level': env("LOG_LEVEL", default="DEBUG"),
            'propagate': True,
        },
    }
}
----

Pour utiliser nos loggers, il suffit de copier le petit bout de code suivant:

[source,python]
----
import logging

logger = logging.getLogger(__name__)

logger.debug('helloworld')
----

https://docs.djangoproject.com/en/stable/topics/logging/#examples[Par exemples].

Nous verrons plus loin (cf. ) comment configurer gunicorn pour intercepter correctement toutes ces informations.
