[FIXED] Setting up a database handler for flask logger

Issue

I’m looking to set up a logging database handler that will save all the information that Flask currently outputs into the console (unhandled exceptions, werkzeug, sqlachemy), as well as my custom logging messages.

I’m having trouble setting this all up. Questions:

1) Why doesn’t the simulated Arithmetic Error in the about view doesn’t get picked up by any logger?

2) Why doesn’t the werkzeug logger correctly display the server loaded and only has output ‘error on request %s’ for the simulated Aritmetic error.

3)Is there any simpler way just to pipe everything that is written by the Flask server (and its components) to a DB?

Appreciate the responses and any other suggestion.

What I’ve done until now:

models.py

class Log(DB.Model):
    __tablename__ = 'logs'
    id = DB.Column(DB.Integer, primary_key=True) # auto incrementing
    logger = DB.Column(DB.String(100)) # the name of the logger. (e.g. myapp.views)
    level = DB.Column(DB.String(100)) # info, debug, or error?
    trace = DB.Column(DB.String(4096)) # the full traceback printout
    msg = DB.Column(DB.String(4096)) # any custom log you may have included
    created_at = DB.Column(DB.DateTime, default=DB.func.now()) # the current timestamp

    def __init__(self, logger=None, level=None, trace=None, msg=None):
        self.logger = logger
        self.level = level
        self.trace = trace
        self.msg = msg

    def __unicode__(self):
        return self.__repr__()

    def __repr__(self):
        return "<Log: %s - %s>" % (self.created_at.strftime('%m/%d/%Y-%H:%M:%S'), self.msg[:50])

core.py

"""
This module contains the core objects of the application:
the Flask (APP) object and the database object.
"""
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from samo.config import CONFIG_BY_NAME, ENVIRONMENT


APP = Flask(__name__)
APP.config.from_object('samo.config')
APP.config.from_object(CONFIG_BY_NAME[ENVIRONMENT])

DB = SQLAlchemy(APP)
DB.create_all()

LOGIN_MANAGER = LoginManager()
LOGIN_MANAGER.init_app(APP)
LOGIN_MANAGER.session_protection = "strong"
LOGIN_MANAGER.login_view = "auth.login"

run.py

from samo.core import APP as app, DB
from flask_wtf.csrf import CSRFProtect
from samo.config import config
from samo.models import Log
import traceback
import logging

class SQLAlchemyHandler(logging.Handler):

    def emit(self, record):
        trace = None
        exc = record.__dict__['exc_info']
        if exc:
            trace = traceback.format_exc(exc)
        log = Log(
            logger=record.__dict__['name'],
            level=record.__dict__['levelname'],
            trace=trace,
            msg=record.__dict__['msg'],)
        DB.session.add(log)
        DB.session.commit()

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

ch = SQLAlchemyHandler()
ch.setLevel(logging.INFO)

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)



loggers = [logger, logging.getLogger('werkzeug'),  logging.getLogger('sqlalchemy'), logging.getLogger('flask.app')]

for l in loggers:
    l.addHandler(ch)

csrf = CSRFProtect()

if __name__ == '__main__':

    csrf.init_app(app)
    logger.critical('TEST CRITICAL ERROR')
    app.run(host=config['ENV']['HOST'])

views.py

@APP.route('/about')
def about():
    """
    Renders the about page.

    :return: html template
    """

    search = SearchForm(request.form)

    raise ArithmeticError('A nasty error')

    return render_template('about.html', postsearchform=search)

what was inserted in the database

enter image description here

Solution

In case anyone stumbles upon this

in run.py, this is the only change I made to get to the desired result.

from samo.core import APP as app, DB
from flask_wtf.csrf import CSRFProtect
from samo.config import config
from samo.models import Log
import traceback
import logging

class SQLAlchemyHandler(logging.Handler):

    def emit(self, record):
        trace = None
        exc = record.__dict__['exc_info']
        if exc:
            trace = traceback.format_exc() ##CHANGE HERE, removed exc parameter
        log = Log(
            logger=record.__dict__['name'],
            level=record.__dict__['levelname'],
            trace=trace,
            msg=record.__dict__['msg'],)
        DB.session.add(log)
        DB.session.commit()

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

ch = SQLAlchemyHandler()
ch.setLevel(logging.INFO)

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)



loggers = [logger, logging.getLogger('werkzeug'),  logging.getLogger('sqlalchemy'), logging.getLogger('flask.app')]

for l in loggers:
    l.addHandler(ch)

csrf = CSRFProtect()

if __name__ == '__main__':

    csrf.init_app(app)
    logger.critical('TEST CRITICAL ERROR')
    app.run(host=config['ENV']['HOST'])

Problem solved.

Answered By – cnstlungu

Answer Checked By – Clifford M. (Easybugfix Volunteer)

Leave a Reply

(*) Required, Your email will not be published