Sending HTML Emails

Note

Throughout the examples below, mail messages sent using smtplib are printed to the screen so we can see what’s going on:

>>> import smtplib
>>> server = smtplib.SMTP('localhost')
>>> server.sendmail('from@example.com', ['to@example.com'], 'The message')
sending to ['to@example.com'] from 'from@example.com' using ('localhost', 25)
The message

Love it or loath it, HTML email is now common and the formatting opportunities it provides can be used to highlight important parts of messages. mailinglogger provides no pre-canned configuration of this but below is an example of how to configure a SummarisingLogger such that important log rows stand out from the summary.

To start with, we’ll need a template for the summary email that includes any headers or footers requires, along with setting up any CSS to be used in the mail message:

template = """<html><head><style>
    tt {
      padding: 0;
      margin: 0;
      color: #444444;
    }
    tt.WARNING {
      color: #CC6600
    }
    tt.ERROR {
      color: #990000
    }
    tt.CRITICAL {
      color: #990000
    }
    </style></head>
    <body><pre>%s</pre></body></html>
"""

To format each message logged as HTML, we need an appropriate formatter:

import logging

formatter = logging.Formatter(
    '<tt class="%(levelname)s">%(asctime)s %(levelname)-8s %(message)s</tt>',
    '%Y-%m-%d %H:%M:%S'
    )

Be careful with the HTML and CSS used in both the template and the formatter. In particular, keep the CSS as simple as possible as CSS support varies across email clients. The above template and formatter are known to generate emails that work with both Thunderbird and Outlook and allow plain-text replies to those emails to be made without lots of annoying whitespace being inserted.

The SummarisingLogger can now be configured as normal, but making sure we use the template and formatter specified above, and setting the content_type of the email sent to text/html:

from mailinglogger import SummarisingLogger

handler = SummarisingLogger('from@example.com',['to@example.com'],
                            template=template,
                            content_type='text/html')
handler.setFormatter(formatter)

One other thing that needs doing is the addition of a filter to the handler to make sure that messages containing things that might be interpretted as HTML are correctly quoted. HTMLFilter is just such a filter:

from mailinglogger.common import HTMLFilter

handler.addFilter(HTMLFilter())

Now the handler can be added to a logger and used as normal:

>>> logger = logging.getLogger()
>>> logger.addHandler(handler)
>>> logger.setLevel(logging.INFO)
>>> logger.info('An info message')
>>> logger.warning('Malformed html: <a href=">foo &&')
>>> logger.error('An error')
>>> logging.critical('Something critical')

Now when the summary is sent, you can see that it is a correctly formatted HTML message:

>>> logging.shutdown()
sending to ['to@example.com'] from 'from@example.com' using ('localhost', 25)
Content-Transfer-Encoding: 7bit
Content-Type: text/html; charset="us-ascii"
Date: Mon, 01 Jan 2007 10:00:00 -0000
From: from@example.com
MIME-Version: 1.0
Message-ID: ...
Subject: Summary of Log Messages (CRITICAL)
To: to@example.com
X-Log-Level: CRITICAL
X-Mailer: MailingLogger ...

<html><head><style>
    tt {
      padding: 0;
      margin: 0;
      color: #444444;
    }
    tt.WARNING {
      color: #CC6600
    }
    tt.ERROR {
      color: #990000
    }
    tt.CRITICAL {
      color: #990000
    }
    </style></head>
    <body><pre><tt class="INFO">2007-01-01 10:00:00 INFO     An info message</tt>
<tt class="WARNING">2007-01-01 10:00:00 WARNING  Malformed html: &lt;a href=&quot;&gt;foo &amp;&amp;</tt>
<tt class="ERROR">2007-01-01 10:00:00 ERROR    An error</tt>
<tt class="CRITICAL">2007-01-01 10:00:00 CRITICAL Something critical</tt>
</pre></body></html>