Journalisation

Flask utilise le module Python logging standard. Les messages de votre application Flask sont transmis via la méthode app.logger qui utilise le même nom que app.name. Ce logger peut également être utilisé pour enregistrer vos propres messages.

@app.route('/login', methods=['POST'])
def login():
    user = get_user(request.form['username'])

    if user.check_password(request.form['password']):
        login_user(user)
        app.logger.info('%s logged in successfully', user.username)
        return redirect(url_for('index'))
    else:
        app.logger.info('%s failed to log in', user.username)
        abort(401)

Si vous ne configurez pas la journalisation, le niveau de log par défaut de Python est généralement « warning ». Aucun message dont le niveau est situé sous celui configuré ne sera enregistré.

Configuration de base

Lorsque vous souhaitez configurer la journalisation de votre projet, vous devez le faire dès que possible lorsque l’application démarre. Si la méthode app.logger est appelée avant que la journalisation soit configurée, elle utilisera le gestionnaire par défaut. Il est donc recommandé de configurer la journalisation avant même la création de l’objet application.

Cet exemple utilise dictConfig() afin de créer une configuration similaire à celle par défaut de Flask pour l’ensemble de la journalisation:

from logging.config import dictConfig

dictConfig({
    'version': 1,
    'formatters': {'default': {
        'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s',
    }},
    'handlers': {'wsgi': {
        'class': 'logging.StreamHandler',
        'stream': 'ext://flask.logging.wsgi_errors_stream',
        'formatter': 'default'
    }},
    'root': {
        'level': 'INFO',
        'handlers': ['wsgi']
    }
})

app = Flask(__name__)

Configuration par défaut

Si vous ne configurez pas la journalisation, Flask ajoutera automatiquement un StreamHandler à la méthode app.logger. Lors de chaque appel à une requête, il écrira alors dans le flux spécifié par le serveur WSGI via environ['wsgi.errors'] (qui vaut en général sys.stderr). Hors des appels aux requêtes, il écrira sur sys.stderr.

Supprimer le gestionnaire par défaut

Si vous configurez la journalisation après avoir accédé à la méthode app.logger, et que vous souhaitez retirer le gestionnaire par défaut, vous pouvez l’importez pour le supprimer:

from flask.logging import default_handler

app.logger.removeHandler(default_handler)

Envoyer les erreurs par mail aux admins

Lorsque l’application est exécutée sur un serveur de production, il est sans doute peu vraisemblable que vous regarderez les messages de logs régulièrement. Le serveur WSGI enverra probablement les messages dans un fichier qui ne sera alors exploité que lorsqu’un utilisateur signalera qu’un problème est survenu.

Afin d’être proactif sur les problèmes, vous pouvez configurer un gestionnaire SMTP logging.handlers.SMTPHandler afin de transmettre les messages de niveau « erreur » et plus par mail.

import logging
from logging.handlers import SMTPHandler

mail_handler = SMTPHandler(
    mailhost='127.0.0.1',
    fromaddr='server-error@example.com',
    toaddrs=['admin@example.com'],
    subject='Application Error'
)
mail_handler.setLevel(logging.ERROR)
mail_handler.setFormatter(logging.Formatter(
    '[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
))

if not app.debug:
    app.logger.addHandler(mail_handler)

Cela nécessite un serveur SMTP configuré sur le même serveur. Voir la documentation Python pour plus d’informations sur la configuration de ce gestionnaire.

Ajouter des informations de la Requête

Avoir plus d’information à propos de la requête traitée, comme une adresse IP, peut vous aider à résoudre vos problèmes. Vous pouvez créer une sous-classe de logging.Formatter afin d’injecter vos propres données dans les messages. Vous pouvez adapter le formatage du gestionnaire par défaut de Flask, le gestionnaire par mail défini ci-dessus, ou n’importe quel autre.

from flask import has_request_context, request
from flask.logging import default_handler

class RequestFormatter(logging.Formatter):
    def format(self, record):
        if has_request_context():
            record.url = request.url
            record.remote_addr = request.remote_addr
        else:
            record.url = None
            record.remote_addr = None

        return super().format(record)

formatter = RequestFormatter(
    '[%(asctime)s] %(remote_addr)s requested %(url)s\n'
    '%(levelname)s in %(module)s: %(message)s'
)
default_handler.setFormatter(formatter)
mail_handler.setFormatter(formatter)

Autres librairies

D’autres librairies peuvent utiliser la journalisation et il est alors nécessaire de récupérer leurs messages. La manière la plus simple est d’ajouter des gestionnaires au logger racine en plus de celui de l’application.

from flask.logging import default_handler

root = logging.getLogger()
root.addHandler(default_handler)
root.addHandler(mail_handler)

Cependant, et suivant votre projet, il peut être plus intéressant de configurer chaque logger séparément au lieu d’utiliser seulement le logger racine.

for logger in (
    app.logger,
    logging.getLogger('sqlalchemy'),
    logging.getLogger('other_package'),
):
    logger.addHandler(default_handler)
    logger.addHandler(mail_handler)

Werkzeug

Werkzeug utilise son propre logger 'werkzeug' pour écrire les informations basiques des requêtes et des réponses. Si le logger racine n’a pas de gestionnaire configuré, Werkzeug ajoute une classe StreamHandler à son logger.

Les extensions Flask

Certaines extensions peuvent choisir d’utiliser la méthode app.logger ou bien leur propre logger pour leur journalisation. Il est nécessaire de vérifier la documentation de chaque extension pour les détails à ce sujet.