Search code examples
pythonloggingsmtphandlerpython-logging

How to make SMTPHandler not block


I installed a local SMTP server and used logging.handlers.SMTPHandler to log an exception using this code:

import logging
import logging.handlers
import time
gm = logging.handlers.SMTPHandler(("localhost", 25), '[email protected]', ['[email protected]'], 'Hello Exception!',)
gm.setLevel(logging.ERROR)
logger.addHandler(gm)
t0 = time.clock()
try:
    1/0
except:
    logger.exception('testest')
print time.clock()-t0

It took more than 1sec to complete, blocking the python script for this whole time. How come? How can I make it not block the script?


Solution

  • Here's the implementation I'm using, which I based on this Gmail adapted SMTPHandler.
    I took the part that sends to SMTP and placed it in a different thread.

    import logging.handlers
    import smtplib
    from threading import Thread
    
    def smtp_at_your_own_leasure(mailhost, port, username, password, fromaddr, toaddrs, msg):
        smtp = smtplib.SMTP(mailhost, port)
        if username:
            smtp.ehlo() # for tls add this line
            smtp.starttls() # for tls add this line
            smtp.ehlo() # for tls add this line
            smtp.login(username, password)
        smtp.sendmail(fromaddr, toaddrs, msg)
        smtp.quit()
    
    class ThreadedTlsSMTPHandler(logging.handlers.SMTPHandler):
        def emit(self, record):
            try:
                import string # for tls add this line
                try:
                    from email.utils import formatdate
                except ImportError:
                    formatdate = self.date_time
                port = self.mailport
                if not port:
                    port = smtplib.SMTP_PORT
                msg = self.format(record)
                msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\nDate: %s\r\n\r\n%s" % (
                                self.fromaddr,
                                string.join(self.toaddrs, ","),
                                self.getSubject(record),
                                formatdate(), msg)
                thread = Thread(target=smtp_at_your_own_leasure, args=(self.mailhost, port, self.username, self.password, self.fromaddr, self.toaddrs, msg))
                thread.start()
            except (KeyboardInterrupt, SystemExit):
                raise
            except:
                self.handleError(record)
    

    Usage example:

    logger = logging.getLogger()
    
    gm = ThreadedTlsSMTPHandler(("smtp.gmail.com", 587), 'bugs@my_company.com', ['admin@my_company.com'], 'Error found!', ('[email protected]', 'top_secret_gmail_password'))
    gm.setLevel(logging.ERROR)
    
    logger.addHandler(gm)
    
    try:
        1/0
    except:
        logger.exception('FFFFFFFFFFFFFFFFFFFFFFFUUUUUUUUUUUUUUUUUUUUUU-')