Manually trigger Django email error report

Yes you can manually send email error report even if you catch the exception.

There are several ways you can go about this.

  1. You can use the existing default logger configuration (and its associated handler configuration, documented here) for django.request which sends all error messages to the mail_admins handler, which sends the anything logged with log.error from django.request when debug is false as email using AdminEmailHandler, whose existing call point is in handle_uncaught_exception.
  2. You can add additional logger configuration which uses the same handler, to catch your exception earlier than django.request and call log.error earlier.
  3. You can subclass django.request, specifically handle_uncaught_exception.
  4. You can use a custom middleware ( for example StandardExceptionMiddleware) or ExceptionMiddleware
  5. You can manually call the contents of emit in AdminEmailHandler or mail.mail_admins directly.

Of these options, Option 4 seems to be the most commonly done.

Based on the additional information in your comment a code example of 2 is below.

First the code that would be added to view

from django.http import HttpResponse
import logging
logger = logging.getLogger(__name__)

def my_view(request):

    try:
        result = do_something()
        return HttpResponse('<h1>Page was found' + result + '</h1>')
    except Exception: 
         # Can have whatever status_code and title you like, but I was just matching the existing call.
         logger.error('Internal Server Error: %s', request.path,
            exc_info=sys.exc_info(),
            extra={
            'status_code': 500,
            'request': request
            }
         )
         return HttpResponse('<h1>Page was found, and exception was mailed to admins.</h1>')

This is based of Django documentation for view writing and and introduction to Django logging, but hasn't been tested.

Then the additional logger configuration is add to the to the loggers entry (as per here)

'nameofdjangoapplicationgoeshere': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': False,
        },

Just setup a simple log handler in your settings.

LOGGING = {
    'version': 1, 
    'disable_existing_loggers': False,
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        }
    },
    'handlers': {
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler'
        },
        'app': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler'
        },
    },
    'loggers': {
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        },
    }
}

and then in your view, you can do anything

 import logging
 logger = logging.getLogger('app')

 def some_view(request):
     try:
         # something
         if something_wnet_wrong:
             logger.error('Something went wrong!')
         return some_http_response
     except:
         #something else
         logger.error(sys.exc_info(), request)        
         return some_other_response

If you want detailed error report, you can try something like this.

You also need to take care of sensitive information.


You can use the following code to send manually an email about a request and an exception e:

import sys
import traceback
from django.core import mail
from django.views.debug import ExceptionReporter

def send_manually_exception_email(request, e):
    exc_info = sys.exc_info()
    reporter = ExceptionReporter(request, is_email=True, *exc_info)
    subject = e.message.replace('\n', '\\n').replace('\r', '\\r')[:989]
    message = "%s\n\n%s" % (
        '\n'.join(traceback.format_exception(*exc_info)),
        reporter.filter.get_request_repr(request)
    )
    mail.mail_admins(
        subject, message, fail_silently=True,
        html_message=reporter.get_traceback_html()
    )

You can test it in a view like this:

def test_view(request):
    try:
        raise Exception
    except Exception as e:
        send_manually_exception_email(request, e)