Custom exception reporters in Django

If your Django project runs into exceptions in production, Django can email you a complete stacktrace, and all the context information you'd usually see in development mode. As of Django 3.0, you can tweak the content of these emails (and Django 3.1 made it a bit easier for us, so this blog post assumes Django 3.1 or later releases).

Use case and alternatives

First off: Why would you want to do this? Certainly, receiving stacktraces per email seems like a clumsy and outdated method of tracking errors in production. Sentry, for example, provides a sophisticated UI and offers Django integration, allowing you to see similar errors together and notice when you encounter regressions. For most professional projects, the free or paid (or self-hosted) Sentry tiers would be a better choice than what I'm describing below.

There are two use cases I encounter where external tools aren't a good fit. For small hobby projects, using external tools like that feels like massive overkill. Getting pinged via email if something ever goes wrong is good enough for those projects.

The other case is a bit trickier: Projects that aren't actually under your control. I encounter this with pretalx, which is a Django project I run in production – but others run it, too. I'd like to give them the opportunity to debug their setup when errors occur without forcing them to use any particular tool.

Setting up email reporting

Setting up email reporting in the first place requires two things, both of which have to be configured in your project's settings.

Your email address – or rather, the email address you want the reports to go to! maybe don't use your personal account. Email reporting is not designed to be clever, so if somebody finds an exception, you may find your inbox flooded with reports. The email address goes into the ADMINS variable, like this:

ADMINS = [("Tobias", "r@rixx.de")]

You also need your Django project configured to send out emails. Your production settings should look roughly like this:

EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "mail.example.org"
EMAIL_PORT = 25
EMAIL_HOST_USER = "rixx@example.org"
EMAIL_HOST_PASSWORD = "PLZCHANGEME"
EMAIL_USE_TLS = True  # or EMAIL_USE_SSL
SERVER_EMAIL = DEFAULT_FROM_EMAIL = "project@example.org"

Writing your exception reporter

Note that SERVER_EMAIL is the sender address used for exception emails and DEFAULT_FROM_EMAIL for all other emails.

The best way to use your custom reporter, unless you want to remove all Django-provided information, is to add information to the top or to the bottom – I do both, to make the important information easier to find. For example, nearly all requests in pretalx happen in the context of an event, so my exception emails contain the name and context information for the event involved in the error.

Another good use of these emails is to mention your project's issue tracker or documentation, to make sure the poor administrator receiving the email will know what to do with it.

To this end, you will want to override the inherited get_traceback_text method, like this:

from django.views.debug import ExceptionReporter

class CustomExceptionReporter(ExceptionReporter):
    def get_traceback_text(self):
        traceback_text = super().get_traceback_text()

        if settings.DEBUG or not self.is_email:
            return traceback_text

        tldr = get_tldr_from_request(self.request)

        intro = f"""
You are receiving this email because ...
If you find that the problem was not due to a configuration error,
please report this issue at

    https://github.com/pretalx/pretalx/issues/new/choose

"""
        traceback_text = f"{tldr}\n{intro}\n\n{traceback_text}\n{tldr}\n"
        return traceback_text

You can do whatever you like to your traceback texts, of course – but take care to make your code as safe as possible! Exceptions in your exception reporter mean you will never hear about them, so it's wise to be extremely cautious and use more try/except and getattr than you usually would.

Finally, you have to include your exception reporter in your settings, like this:

DEFAULT_EXCEPTION_REPORTER = "path.to.CustomExceptionReporter"

Notes

That's it! Congratulations, you now have working custom email reporting. In pretalx, the simple version looks like this:

If you looked at the documentation, you will notice that we ignored the get_traceback_html method. It provides the content for HTML emails – a feature that is turned off by default.

We ignored it for a good reason: While HTML emails may be prettier to look at, the documentation warns us of including the full DEBUG=True-style HTML part of error emails due to the sensitive data that will not get filtered out. If you don't want to include stacktraces at all in your exception reporting, you could consider overriding get_traceback_html and turning on the include_html flag in your settings, to make sure your administrators can be dazzled with all the very limited power of HTML emails.

There are a couple of other things you might want to do: I'll probably write a short post about exempting certain paths from these emails, for one. If you come up with other ideas – for example providing links to likely help in the documentation – tell me about it!