When I debug Open edX, with multiple concurrently running applications, I often find myself missing the Django default exception stacktrace reporter:
This stacktrace is extremely convenient: it displays the stacktrace in a very readable format, along with all local variable values and tons of additional information, such as settings. Also, it’s neatly packaged in a single static html page.
However, this page is only displayed when a synchronous http requests results in a 500 error. I don’t have access to all that information when I troubleshoot, for instance:
- Calls between separate applications
- Ajax requests
- Asynchronous Celery tasks
It occurred to me that I could create a single logging formatter that would generate that same html code. The implementation is really simple, and consists of a simple modification of the LOGGING
setting:
import logging
class StacktraceFormatter(logging.Formatter):
def format(self, record):
"""
Html-formatted stacktraces
(heavily inspired by django.utils.log.AdminEmailHandler)
"""
try:
request = record.request
except Exception:
request = None
if record.exc_info:
exc_info = record.exc_info
else:
exc_info = (None, record.getMessage(), None)
from django.views.debug import ExceptionReporter
reporter = ExceptionReporter(request, is_email=False, *exc_info)
return reporter.get_traceback_html()
LOGGING["formatters"]["stacktrace"] = {
"class": "lms.envs.devstack.StacktraceFormatter"
}
# Configure a handler to use this formatter
LOGGING["handlers"]["stacktrace"] = {
"level": "ERROR",
"class": "logging.handlers.RotatingFileHandler",
# Rotate very frequently
"maxBytes": 512,
# Keep just a few error stacktraces
"backupCount": 10,
# Files will be saved as stacktrace.html, stacktrace.html.1, stacktrace.html.2, ...
"filename": os.path.join(LOG_DIR, "stacktrace.html"),
"formatter": "stacktrace",
}
# Configure the root logger to use this handler
LOGGING["loggers"][""]["handlers"].append("stacktrace")
With this simple configuration change, I get all stacktraces stored as stacktrace.html.*
files in my LOG_DIR
and I can explore them with my web browser. This is basically the poor man’s Sentry. I find this extremely convenient, and I’m surprised that I did not find any other Python/Django project that implemented the same feature. So I wanted to ask you fellow Open edX developers if you have ever seen something similar? In particular I’m curious to hear about you @nedbat