Stocker les logs de votre application Flask en base de données avec SQLAlchemy

Vous désirez stocker les logs de votre application dans votre base de données plutôt qu'afficher les erreurs dans la console ou dans un fichier ? Voici un petit récapitulatif des actions à effectuer pour arriver à votre but.

Nous utiliserons une application Flask avec l'extension Flask-SQLAlchemy dans cet exemple.

Création de notre application

Créons dans un premier temps notre application et initialisons notre extension afin de les utiliser par la suite.

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)  # notre application

# nous utilisons dans cet exemple une base de données sqlite
# vous devez adapter cette ligne selon votre base de données
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:////tmp/test.db"

db = SQLAlchemy(app=app)  # notre extension

with app.app_context():
    db.create_all()  # création des tables

Définition du modèle de base de données

from sqlalchemy.sql import func

from myapp.app import db

class LogModel(db.Model):
    """Notre modèle de base de données pour stocker les logs."""
    
    id = db.Column(db.Integer, primary_key=True)
    level = db.Column(db.Integer, nullable=False)
    msg = db.Column(db.String, nullable=False)
    created_at = db.Column(db.DateTime, default=func.now())

Rien de révolutionnaire ici, nous avons juste créé une nouvelle table grâce à SQLAlchemy pour y stocker nos logs. Vous pouvez y retrouver les champs classiques d'un log :

  • level → Le LogLevel de notre entrée
  • msg → Le message de notre entrée
  • created_at → La date de création de l'entrée

Ajouter un handler aux logs de notre application

Les applications Flask disposent d'un logger global lié à l'instance app que nous avons créée précédemment, il est accessible via l'attribut app.logger. Il s'agit d'un objet logging.Logger dont le comportement peut être modifié grâce à des Formatters ou des Handlers.

La configuration par défaut d'une application Flask lie notre objet app.logger à un StreamHandler qui écrira les logs de l'application généralement dans sys.stderr.

Implémentons un handler nommé SQLAlchemyHandler qui se chargera de stocker nos logs dans notre base de données.

import logging

from myapp.log import LogModel


class SQLAlchemyHandler(logging.Handler):
    """Ajouter les logs de l'application dans la table log."""

    def emit(self, record: logging.LogRecord):
        log = LogModel(
             level=record.levelno,
             msg=record.msg,
        )
        db.session.add(log)
        db.session.commit()
    

Plutôt facile non ? Nous récupérons l'objet LogRecord pour initialiser une instance du modèle que nous avons défini et l'ajouter à notre session.

Ajoutons maintenant notre handler à notre application en modifiant notre premier fichier Python.

import logging

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

from myapp.handler import SQLAlchemyHandler


app = Flask(__name__)  # notre application

# nous utilisons dans cet exemple une base de données sqlite
# vous devez adapter cette ligne selon votre base de données
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:////tmp/test.db"

db = SQLAlchemy(app=app)  # notre extension

with app.app_context():
    db.create_all()  # création des tables
    
handler = SQLAlchemyHandler()
handler.setLevel(logging.NOTSET)  # définissez votre niveau de log
app.logger.addHandler(handler)

Dorénavant chaque log de notre application sera également stocké en base de données dans notre table log.