AlexH
AlexH

Reputation: 327

Django error email is too long. How do I truncate it?

It seems like the error emails in Django 1.9 are much longer than they were previously. There's a whole section for "settings" which I think is superfluous and potentially too revealing.

What is the best way to edit the error email that Django sends?

edit: I am not just trying to hide sensitive information. There is a lot more content in the email in Django 1.9 and I want to change the format of the email to be shorter. I liked it the old way.

Upvotes: 5

Views: 1786

Answers (2)

lmaayanl
lmaayanl

Reputation: 448

For people still looking for an answer now:

In django 3.0 they added the option to add reporter_class which customize just the email body, and the traceback text rendering.

So, If you're just looking to change the email template, there's no need to also override the AdminEmailHandler.

so based on @Airith answer, you need to have:

# custom_exception_reporter.py

from django.views import debug
from django import template

TECHNICAL_500_TEXT_TEMPLATE = """ 
# custom template here, copy the original and make adjustments 
"""

class CustomExceptionReporter(debug.ExceptionReporter):
    def get_traceback_text(self):
        t = debug.DEBUG_ENGINE.from_string(TECHNICAL_500_TEXT_TEMPLATE)
        c = template.Context(self.get_traceback_data(), autoescape=False, use_l10n=False)
        return t.render(c)

and then in your log config:

'handlers': {
        'mail_admins': {
             'level': 'ERROR',
             'class': 'django.utils.log.AdminEmailHandler',
             'include_html': False,
             'reporter_class': 'project.custom_exception_reporter.CustomExceptionReporter'
        },

Two notes:

  1. If you wish to also send the email body as HTML, you need to have another template - TECHNICAL_500_TEMPLATE, new function - get_traceback_html(), and set include_html = True in the log configuration. Also here you should copy django's default template and just change what you need.
  2. If you don't want to save the entire template in that file, but in a different .html file, notice that even if you will place it inside the template dir you defined, it will not find it. I solved it by keeping the template.html file in the same dir with the custom reporter .py file.

example of my custom_exception_report.py, where I keep the template in the same directory (as described in note #2):

import os
from django.views import debug
from django import template

TECHNICAL_500_TEXT_TEMPLATE = "technical_500.text"


class CustomExceptionReporter(debug.ExceptionReporter):
    def get_traceback_text(self):
        t = self._get_template(TECHNICAL_500_TEXT_TEMPLATE)
        c = template.Context(self.get_traceback_data(), autoescape=False, use_l10n=False)
        return t.render(c)

    @staticmethod
    def _get_template(template_name):
        dir_path = os.path.dirname(os.path.realpath(__file__))
        template_path = os.path.join(dir_path, template_name)
        with open(template_path, 'r') as fh:
            return debug.DEBUG_ENGINE.from_string(fh.read())

You can read more about the report class in django docs here

Upvotes: 4

Airith
Airith

Reputation: 2164

There's a django template variable TECHNICAL_500_TEMPLATE/TECHNICAL_500_TEXT_TEMPLATE in the django debug view that controls what's visible in the error reporting, and of course error emails. A comment explains that the template is in a python variable so that errors can be generated in case the template loader breaks. You could change this variable in your django package but I don't recommend that. TECHNICAL_500_TEMPLATE is referenced by the ExceptionReporter class in the same file.

The class AdminEmailHandler in django utils log then uses the ExceptionReporter to generate the html error report.

You can subclass AdminEmailHandler and override the emit function to include your subclassed version of ExceptionReporter that uses your own defined TECHNICAL_500_TEMPLATE.

Here's an example:

Create reporter.py with

from copy import copy

from django.views import debug
from django.utils import log
from django.conf import settings
from django import template

TECHNICAL_500_TEMPLATE = """
    # custom template here, copy the original and make adjustments
"""
TECHNICAL_500_TEXT_TEMPLATE = """
    # custom template here, copy the original and make adjustments
"""

class CustomExceptionReporter(debug.ExceptionReporter):
    def get_traceback_html(self):
        t = debug.DEBUG_ENGINE.from_string(TECHNICAL_500_TEMPLATE)
        c = template.Context(self.get_traceback_data(), use_l10n=False)
        return t.render(c)

    def get_traceback_text(self):
        t = debug.DEBUG_ENGINE.from_string(TECHNICAL_500_TEXT_TEMPLATE)
        c = template.Context(self.get_traceback_data(), autoescape=False, use_l10n=False)
        return t.render(c)

class CustomAdminEmailHandler(log.AdminEmailHandler):
    def emit(self, record):
        try:
            request = record.request
            subject = '%s (%s IP): %s' % (
                record.levelname,
                ('internal' if request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS
                 else 'EXTERNAL'),
                record.getMessage()
            )
        except Exception:
            subject = '%s: %s' % (
                record.levelname,
                record.getMessage()
            )
            request = None
        subject = self.format_subject(subject)

        no_exc_record = copy(record)
        no_exc_record.exc_info = None
        no_exc_record.exc_text = None

        if record.exc_info:
            exc_info = record.exc_info
        else:
            exc_info = (None, record.getMessage(), None)

        reporter = CustomExceptionReporter(request, is_email=True, *exc_info)
        message = "%s\n\n%s" % (self.format(no_exc_record), reporter.get_traceback_text())
        html_message = reporter.get_traceback_html() if self.include_html else None
        self.send_mail(subject, message, fail_silently=True, html_message=html_message)

And then just set your django settings to use your new handler in the logging section.

LOGGING = {
    # Your other logging settings
    # ...
    'handlers': {
        'mail_admins': {
            'level': 'ERROR',
            'class': 'project.reporter.CustomAdminEmailHandler',
            'filters': ['special']
        }
    },
}

If you just want to hide settings you can comment out the 'settings': get_safe_settings(), line 294, if you override and copy paste def get_traceback_data(self): in your CustomExceptionReporter

Upvotes: 9

Related Questions