Abdullah Diab’s Blog

How to make django management commands send errors email to admins

I usually use django’s custom management commands to do different tasks on the web application, most of the times with a cron job running those commands. The problem is that when you’re using a cron job, you won’t get the beautiful django error email when something goes wrong.

Django's error email sample

You can of course log all the commands you run and the cron jobs you have in files, but the problem is that sometimes you need to be notified instantly via your email, so here’s how to make django management commands send you the errors email.

Understanding how django’s error email works

If you check django’s logging documentation you will find that it has a built-in handler called AdminEmailHandler this handler sends an email to the site admins for each log message it receives.

So basically all you need to do is to tell your django commands to log the errors using a logger with that handler, and you’ll receive the emails.

To do so define a logger in your settings.py:

LOGGING = {
    #OTHER OPTIONS
    'filters': {
        #OTHER FILTERS
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        }
    },
    'handlers': {
        #OTHER HANDLERS
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler',
            'include_html': True
        }
    },
    'loggers': {
        #OTHER LOGGERS
        'management_commands': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        },
    }
}

And after that in your custom commands log the errors you face using the logger you defined:

from django.utils.log import getLogger
from django.core.management.base import BaseCommand
import sys

logger = getLogger('management_commands')

class Command(BaseCommand):
    def handle(self, *args, **options):
        try:
            #DO YOUR STUFF HERE
        except Exception as e:
            logger.error('Admin Command Error: %s', ' '.join(sys.argv), exc_info=sys.exc_info())
            #Raise the exception again so it gets logged in standard error also.
            raise e

Enabling the logger to all commands in one configuration

You can also make all the commands (custom or not) send the error by email by editing manage.py almost the same way as we edited the command file above:

from django.utils.log import getLogger
import sys

logger = getLogger('management_commands')

#Wrap this line with a try-catch execute_from_command_line(sys.argv)
    try:
        execute_from_command_line(sys.argv)
    except Exception as e:
        logger.error('Admin Command Error: %s', ' '.join(sys.argv), exc_info=sys.exc_info())
        raise e

This way all the commands will send the error by email also to django admins.

Enjoy it!