How to log uncaught exceptions in Flask routes with logging?
Flask is a popular web framework for Python that allows you to create web applications easily and quickly. However, sometimes your Flask routes may encounter uncaught exceptions that cause your application to crash or return an error response. To debug and fix these errors, you need to log them using the logging module.
Logging uncaught exceptions in Flask routes
The logging module is a standard library module that provides a flexible and powerful way to handle and record different levels of events, errors, and messages in your Python programs. You can use logging to configure different handlers, formatters, and levels for your log messages, and send them to different destinations, such as files, consoles, emails, or web services.
To log uncaught exceptions in Flask routes, you need to do two things:
- Configure a logger object with a handler and a formatter that suit your needs
- Register an error handler function that logs the exception information using the logger object
Configuring a logger object
To configure a logger object, you can use the logging.basicConfig() function, which sets up a default handler and formatter for the root logger. The root logger is the parent of all other loggers and it handles all the log messages that are not handled by any other logger. You can pass different parameters to the basicConfig() function, such as:
- level: the minimum level of severity that the logger will handle, such as logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, or logging.CRITICAL
- filename: the name of the file where the log messages will be written
- filemode: the mode of opening the file, such as 'a' for append or 'w' for write
- format: the format string that specifies how the log messages will be displayed, such as '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
- datefmt: the format string that specifies how the date and time will be displayed, such as '%Y-%m-%d %H:%M:%S'
For example, you can configure a logger object with a file handler and a simple formatter like this:
import logging
logging.basicConfig(level=logging.ERROR,
filename='app.log',
filemode='a',
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S')
This will create a file named app.log in the same directory as your Flask app, and write all the log messages with level ERROR or higher to it, using the specified format and date format.
Registering an error handler function
To register an error handler function, you can use the app.errorhandler() decorator, which takes an HTTP status code or an exception class as an argument, and wraps a function that handles the error. The function should take an exception object as a parameter, and return a response object or a tuple of (response, status code).
Inside the error handler function, you can use the logging.exception() method, which logs a message with level ERROR and adds the exception information to the log message. You can pass a custom message as an argument, or use the default message 'Exception occurred'.
For example, you can register an error handler function for the generic Exception class like this:
from flask import Flask, render_template
app = Flask(__name__)
@app.errorhandler(Exception)
def handle_exception(e):
# log the exception
logging.exception('Exception occurred')
# return a custom error page or message
return render_template('error.html'), 500
This will log any uncaught exception that occurs in your Flask routes, and return a custom error page with status code 500.
Example of logging uncaught exceptions in Flask routes
To demonstrate how logging uncaught exceptions in Flask routes works, let's create a simple Flask app that has two routes: one that returns a normal response, and one that raises a ZeroDivisionError. We will use the same logger configuration and error handler function as above.
from flask import Flask, render_template
app = Flask(__name__)
# configure the logger
logging.basicConfig(level=logging.ERROR,
filename='app.log',
filemode='a',
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S')
# register the error handler
@app.errorhandler(Exception)
def handle_exception(e):
# log the exception
logging.exception('Exception occurred')
# return a custom error page or message
return render_template('error.html'), 500
# define the normal route
@app.route('/')
def index():
return 'Hello, world!'
# define the route that raises an exception
@app.route('/error')
def error():
# this will cause a ZeroDivisionError
x = 1 / 0
return 'This will never be returned'
# run the app
if __name__ == '__main__':
app.run(debug=True)
If we run this app and visit the / route, we will see the normal response 'Hello, world!'. However, if we visit the /error route, we will see the custom error page that says 'Something went wrong'. We can also check the app.log file and see the log message that contains the exception information, such as:
2021-07-07 12:34:56 - werkzeug - ERROR - Exception occurred
Traceback (most recent call last):
File "/home/user/.local/lib/python3.8/site-packages/flask/app.py", line 2464, in __call__
return self.wsgi_app(environ, start_response)
File "/home/user/.local/lib/python3.8/site-packages/flask/app.py", line 2450, in wsgi_app
response = self.handle_exception(e)
File "/home/user/.local/lib/python3.8/site-packages/flask/app.py", line 1867, in handle_exception
reraise(exc_type, exc_value, tb)
File "/home/user/.local/lib/python3.8/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/home/user/.local/lib/python3.8/site-packages/flask/app.py", line 2447, in wsgi_app
response = self.full_dispatch_request()
File "/home/user/.local/lib/python3.8/site-packages/flask/app.py", line 1952, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/home/user/.local/lib/python3.8/site-packages/flask/app.py", line 1821, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/home/user/.local/lib/python3.8/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/home/user/.local/lib/python3.8/site-packages/flask/app.py", line 1950, in full_dispatch_request
rv = self.dispatch_request()
File "/home/user/.local/lib/python3.8/site-packages/flask/app.py", line 1936, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/home/user/flask_app.py", line 31, in error
x = 1 / 0
ZeroDivisionError: division by zero
This way, we can easily identify and debug the source of the error, and improve our Flask app accordingly. Logging uncaught exceptions in Flask routes is a good practice that can help you maintain and troubleshoot your web applications.
See also:
Flask - Handling Application Errors
and Unhandled exceptions.
test.log
, and it's totally possible to see that it is not logged as expected (there is surely a better way with Flask). – Seilerlogging
module? – Seiler