MailingLogger

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

MailingLogger is a handler for the python logging framework that sends log entries as email messages using an SMTP server. It is configured as any other logging handler would be, full details of which can be found in the Python core documentation. For the examples below, we’ll stick to manually configuring the logging elements.

A MailingLogger is instantiated as follows:

>>> from mailinglogger import MailingLogger
>>> handler = MailingLogger('from@example.com',('to@example.com',))

It can then be added as a handler for any logger as follows:

>>> import logging
>>> logger = logging.getLogger()
>>> logger.addHandler(handler)

Now, when that logger receives a message, an email containing the message will be sent:

>>> logging.error('my message')
sending to ('to@example.com',) from 'from@example.com' using ('localhost', 25)
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset="us-ascii"
Date: ...
From: from@example.com
MIME-Version: 1.0
Message-ID: <...MailingLogger@...>
Subject: my message
To: to@example.com
X-Log-Level: ERROR
X-Mailer: MailingLogger...

my message

From the above example, you can see that MailingLogger sends mail messages that are correctly formatted, including Date and Message-ID headers.

You will also notice that an X-Mailer header has been added specifying that mailinglogger is the sender of the mail. An X-Log-Level header has also been added indicating the level of the message that was logged. These can be useful for filtering mail sent by MailingLogger. If you wish to filter mail by environment or other configuration data, the support for adding extra headers may be useful.

Now, to continue with the examples, just like any other handler, we can also set the logging level, and messages logged below this level will not result in emails being sent:

>>> handler.setLevel(logging.CRITICAL)
>>> logging.error('my message')
>>> handler.setLevel(logging.WARNING)
>>> logging.warning('my message')
sending to ('to@example.com',) from 'from@example.com' using ('localhost', 25)
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset="us-ascii"
Date: ...
From: from@example.com
MIME-Version: 1.0
Message-ID: <...MailingLogger@...>
Subject: my message
To: to@example.com
X-Log-Level: WARNING
X-Mailer: MailingLogger...

my message

Controlling the subject line

As you can see from the above examples, the subject line of the email sent is, by default, the first line of the message logged. This can be changed by supplying the subject parameter when instantiating the handler object:

>>> handler = MailingLogger('from@example.com',('to@example.com',),
...                         subject='[MyLogger] %(line)s')
>>> logger.addHandler(handler)
>>> logging.error('my %i message',13)
sending to ('to@example.com',) from 'from@example.com' using ('localhost', 25)
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset="us-ascii"
Date: ...
From: from@example.com
MIME-Version: 1.0
Message-ID: <...MailingLogger@...>
Subject: [MyLogger] my 13 message
To: to@example.com
X-Log-Level: ERROR
X-Mailer: MailingLogger...

my 13 message

Full details of how the subject line can be formatted can be found in the SubjectFormatter documentation.

Sending empty emails

By default, the MailingLogger handler will not send emails if they would have been empty:

>>> logging.error(' ')

However, if you want empty entries to be mailed anyway, all you need to do is supply the send_empty_entries parameter:

>>> handler = MailingLogger('from@example.com',('to@example.com',),
...                         send_empty_entries=True)
>>> logger.addHandler(handler)
>>> logging.error(' ')
sending to ('to@example.com',) from 'from@example.com' using ('localhost', 25)
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset="us-ascii"
Date: ...
From: from@example.com
MIME-Version: 1.0
Message-ID: <...MailingLogger@...>
Subject: ...
To: to@example.com
X-Log-Level: ERROR
X-Mailer: MailingLogger...

Limiting the number of emails sent

Now, one problem that may be encountered with a logger that sends emails is that if you inadvertantly log a large number of entries that would result in mail being sent, you may cause problems with MTAs, mailbox quotas and the like.

To prevent this, MailingLogger allows a limit on the number of entries sent per hour to be specified. By default, this is set to 10 entries per hour. This can be overridden by passing the flood_level option to the MailingLogger constructor:

>>> handler = MailingLogger('from@example.com',('to@example.com',),
...                         flood_level=1)
>>> logger.addHandler(handler)

With this setup, we can log at most one message:

>>> logging.error('An Error')
sending to ('to@example.com',) from 'from@example.com' using ('localhost', 25)
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset="us-ascii"
Date: Mon, 01 Jan 2007 10:00:00 -0000
From: from@example.com
MIME-Version: 1.0
Message-ID: <...MailingLogger@...>
Subject: An Error
To: to@example.com
X-Log-Level: ERROR
X-Mailer: MailingLogger...

An Error

Now that the flood level has been reached, a final warning message is sent if any more messages are logged:

>>> logging.error('Another Error')
sending to ('to@example.com',) from 'from@example.com' using ('localhost', 25)
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset="us-ascii"
Date: Mon, 01 Jan 2007 10:00:00 -0000
From: from@example.com
MIME-Version: 1.0
Message-ID: <...MailingLogger@...>
Subject: Too Many Log Entries
To: to@example.com
X-Log-Level: CRITICAL
X-Mailer: MailingLogger...

Too Many Log Entries

More than 1 entries have been logged that would have resulted in
emails being sent.

No further emails will be sent for log entries generated between
10:00:00 and 11:00:00

Please consult any other configured logs, such as a File Logger,
that may contain important entries that have not been emailed.

Any further messages logged will not result in an email being sent:

>>> logging.error('Yet Another Error')

Specifying the host to send email through

By default, as we’ve seen above, MailingLogger uses the local host to send mails. If you wish to use a specific smtp server to send mail, this can be done by specifying the mailhost parameter to the MailingLogger constructor:

>>> handler = MailingLogger('from@example.com',('to@example.com',),
...                         mailhost='smtp.example.com')
>>> logger.addHandler(handler)
>>> logging.error('An Error')
sending to ('to@example.com',) from 'from@example.com' using ('smtp.example.com', 25)
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset="us-ascii"
Date: ...
From: from@example.com
MIME-Version: 1.0
Message-ID: <...MailingLogger@...>
Subject: An Error
To: to@example.com
X-Log-Level: ERROR
X-Mailer: MailingLogger...

An Error

If the smtp server you wish to use is running on non-standard port, you can configure MailingLogger to use this port by specifying mailhost as a tuple containing the smtp server’s hostname and the port on which it is listening:

>>> handler = MailingLogger('from@example.com',('to@example.com',),
...                         mailhost=('smtp.example.com',2500))
>>> logger.addHandler(handler)
>>> logging.error('An Error')
sending to ('to@example.com',) from 'from@example.com' using ('smtp.example.com', 2500)
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset="us-ascii"
Date: ...
From: from@example.com
MIME-Version: 1.0
Message-ID: <...MailingLogger@...>
Subject: An Error
To: to@example.com
X-Log-Level: ERROR
X-Mailer: MailingLogger...

An Error

If the smtp server you wish to use requires authentication, pass the required username and password to the MailingLogger constructor:

>>> handler = MailingLogger('from@example.com',('to@example.com',),
...                         username='auser',password='theirpassword')
>>> logger.addHandler(handler)
>>> logging.error('An Error')
sending to ('to@example.com',) from 'from@example.com' using ('localhost', 25)
(authenticated using username:'auser' and password:'theirpassword')
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset="us-ascii"
Date: ...
From: from@example.com
MIME-Version: 1.0
Message-ID: <...MailingLogger@...>
Subject: An Error
To: to@example.com
X-Log-Level: ERROR
X-Mailer: MailingLogger...

An Error

Warning

For performance reasons, it’s recommended that you don’t use SMTP authentication unless you absolutely need to.

Adding extra headers

If you wish to add headers for filtering purposes, you can use the headers parameter:

>>> logging.getLogger('').removeHandler(handler)
>>> handler = MailingLogger('from@example.com',('to@example.com',),
...                             headers={'foo':'bar','Baz':'bob'})
>>> logger.addHandler(handler)

Now, when a log message results in an email being send, the email will be sent with the configured headers:

>>> logging.error('The Error!')
sending to ('to@example.com',) from 'from@example.com' using ('localhost', 25)
Baz: bob
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset="us-ascii"
Date: ...
From: from@example.com
MIME-Version: 1.0
Message-ID: <...MailingLogger@...>
Subject: The Error!
To: to@example.com
X-Log-Level: ERROR
X-Mailer: MailingLogger ...
foo: bar

The Error!