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.
If the smtp server you wish to use requires TLS (Transport Level Security),
pass the required username and password and the secure parameter to the
MailingLogger constructor. secure must be an empty tuple or
contain one or two members. See the smtplib documentation for details:
>>> handler = MailingLogger('from@example.com',('to@example.com',),
... username='auser',password='theirpassword',
... secure=())
>>> 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
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!