Is there a way how to configure SMTPHandler in Python to do more advanced stuff?
Asked Answered
B

4

7

I am using standard SMTPHandler logger to catch my Python exceptions. Is there a way how to put exception name into the subject of the mail? It would be much better than with static subject, because Gmail (and not only Gmail) can group conversations according to subject and so it could group it according to the type of error.

E.g., if 50 completely the same errors occure + 1 different, I'd see two conversations in my inbox instead of 1 group consisting 51 emails, where I can very easily overlook the single different one.

Also, is there a way how to prevent sending still the same errors? E. g. somehow define my own function deciding if email is to be sent or not. The function would take some basic info in params so it could decide (e.g. cache & see if such an issue was already sent).

I browsed the docs, but I can't find anything like that. It seems to be very simple. If SMTPHandler can't do this, what would be the best and still simple alternative? Any neat libraries?

Thank you!

Barrio answered 10/2, 2012 at 22:57 Comment(0)
D
9

You simply need to create your own subclass of SMTPHandler.

For your first request: You just need to override the getSubject(record) method. As to what you can put in the subject, look at help(Formatter) after importing Formatter from logging.

For your second request: You must override the emit(record) method. Inspect the record and make your own decision about whether it should be send. If so then just call the super(SMTPHandler,self).emit(record) method.

class MySMTPHandler(SMTPHandler):

    def getSubject(self, record):
        return "My Error Format from the record dict"

    def emit(self, record):
        #check for record in self.already_send or something
        if sendit:
           super(MySMTPHandler,self).emit(record)
Dessertspoon answered 11/2, 2012 at 0:36 Comment(0)
R
2

The easy and flexible way is to format not only email content, but also email subject. This require subclassing logging.handlers.SMTPHandler.

This way if there are any variable references in the subject you configured, it would be expanded on demand. Here is the implementation including test code:

    import logging, logging.handlers
    class TitledSMTPHandler(logging.handlers.SMTPHandler):
        def getSubject(self, record):
            formatter = logging.Formatter(fmt=self.subject)
            return formatter.format(record)

    # below is to test
    logging.getLogger().addHandler(TitledSMTPHandler(
        ('your.smtp.server',587), 
        '[email protected]', '[email protected]', 
        '%(asctime)s Error: %(message)s', 
        ('SMTP login', 'SMTP password'), ()
    ))

    logging.error("TestError")

Replace the SMTP configuration with yours, run the code and you should receive an email where the error message is in subject (also in email body).

If you define the handler in a logging configration file, remember to escape the parameter references. e.g. %(asctime)s should become %%(asctime)s, to prevent configparser's premature interpolation -- however the %% escape is not hornored in and before python 3.1.


Please, if you ask one question at a time, you will help other Googlers a lot. You should start two topics, one is called "Is there a way how to put exception name into the subject of the mail?" and the other "is there a way how to prevent sending still the same errors?"

I will only answer your first question in order to focus on one topic. Perhaps you thought the two questions may be depending on each other hence must be asked together, while they merely came to your mind together. I think it is still feasiable to suggest you to change the title of your question to emphasize one issue, instead of saying "advanced stuff".

I also like to suggest visiting comp.lang.python if the question is an open topic and/or cannot be well defined (e.g. "I want advanced stuff, what everybody else use?")

Radbun answered 27/12, 2013 at 13:10 Comment(1)
return formatter.format(record) should be -> return formatter.formatMessage(record), otherwise you'll get: ValueError: Header values may not contain linefeed or carriage return charactersBlister
B
0

You could inherit the SMTPHandler class and enhance or replace its functionality for example

class A(object):
    def __init__(self):
        self.email = "[email protected]"

    def send_error(self, error):
        send_mail(self.email, error)

class B(A):
    def __init__(self):
        A.__init__(self)

    def send_error(self, error):
        if not error.sent:
            A.send_mail(self, error)
        else:
            #do nothing
            pass
Britt answered 10/2, 2012 at 23:7 Comment(0)
B
0

First of all, I would suggest creating a custom logger (it will pass record upstream), but adding a handler to the upstream root logger so that our record can be processed along with other library records (events). Also, I'd like to note that getSubject() returns a string object, so you may easily customise the e-mail subject to suit your needs:

import logging.handlers
import logging
import platform

class TitledSMTPHandler(logging.handlers.SMTPHandler):
    @override
    def getSubject(self, record):
        return f"{record.levelname} [{platform.node()}]: {record.filename}"

log = logging.getLogger(__name__)
root_logger.getLogger()
root_logger.addHandler(TitledSMTPHandler(
              ('your.smtp.server', 587),
              '[email protected]',
              '[email protected]',
              'subject',
              ('SMTP login', 'SMTP password'),
              ()
))

log.warning("message")

So, you may also use any other variable from the record object and include it in the e-mail subject.

Braille answered 12/2 at 12:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.