Sending application exception notifications via Mailgun.

Python logging handler for Mailgun

Sander Sink

Yet another reason to love python is the wonderful logging API provided by a standard library. Having a decent logger provided by the language saves developers time from integrating with some third-party library, but most importantly it contributes to a clean system where all modules use the same logger. Out of the box, there is a handler for almost any typical use-case, but the best feature is the option to create your own with relative ease. So-far we’ve been using SMTPHandler, but since moving to Mailgun, I decided to experiment with a custom handler to submit logs via their API instead. I’d like to mention that creating a custom logging handler is not a requirement, it is still possible to use their SMTP server with SMTP handler. To get started, we need to extend Handler baseclass and override emit method to make a POST request.

import logging
import requests

class MailgunHandler(logging.Handler):

    def __init__(self, api_url, api_key, sender, recipients, subject):
        # run the regular Handler __init__
        logging.Handler.__init__(self)
        self.api_url = api_url
        self.api_key = api_key
        self.sender = sender
        self.recipients = recipients
        self.subject = subject

    def emit(self, record):
        # record.message is the log message
        for recipient in self.recipients:
            data = {
                "from": self.sender,
                "to": recipient,
                "subject": self.subject,
                "text": self.format(record)
            }
            requests.post(self.api_url, auth=("api", self.api_key), data=data)

I have created a simple flask application that registers the handler and also implemented a route that triggers an error to test it out.

import logging
from flask import Flask
from mailgun_handler import MailgunHandler

app = Flask(__name__)
# Setup logging handler
mail_handler = MailgunHandler(
    api_url='https://api.mailgun.net/v3/fadeit.dk/messages',
    api_key='secret',
    sender=[email protected]',
    recipients=[[email protected]', [email protected]'],
    subject='Application error!'
)
mail_handler.setLevel(logging.ERROR)
mail_handler.setFormatter(logging.Formatter('''
    Message type:       %(levelname)s
    Location:           %(pathname)s:%(lineno)d
    Module:             %(module)s
    Function:           %(funcName)s
    Time:               %(asctime)s
    
    Message:
    
    %(message)s
'''))
app.logger.addHandler(mail_handler)

@app.route('/error')
def error():
    raise Exception("Beds are burning!")
        
if __name__ == '__main__':
    #app.run(debug=True) #Handler isn't executed if app is run in debug mode
    app.run()