Как заставить SMTPHandler не блокировать - PullRequest
7 голосов
/ 23 декабря 2011

Я установил локальный SMTP-сервер и использовал logging.handlers.SMTPHandler для регистрации исключения с помощью этого кода:

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

Для завершения потребовалось более 1 секунды, блокируя скрипт Python на все это время.Как так?Как сделать так, чтобы скрипт не блокировал?

Ответы [ 5 ]

13 голосов
/ 27 декабря 2011

Вот реализация, которую я использую, на основе которой этот Gmail адаптировал SMTPHandler .
Я взял часть, которая отправляет SMTP, и поместил ее в другой поток.

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)

Пример использования:

logger = logging.getLogger()

gm = ThreadedTlsSMTPHandler(("smtp.gmail.com", 587), 'bugs@my_company.com', ['admin@my_company.com'], 'Error found!', ('my_company_account@gmail.com', 'top_secret_gmail_password'))
gm.setLevel(logging.ERROR)

logger.addHandler(gm)

try:
    1/0
except:
    logger.exception('FFFFFFFFFFFFFFFFFFFFFFFUUUUUUUUUUUUUUUUUUUUUU-')
5 голосов
/ 23 декабря 2011

Вы можете использовать QueueHandler и QueueListener .Взятые из документов:

Наряду с классом QueueListener, QueueHandler может использоваться, чтобы позволить обработчикам выполнять свою работу в отдельном потоке от того, который выполняет регистрацию.Это важно в веб-приложениях, а также в других сервисных приложениях, где потоки, обслуживающие клиентов, должны реагировать как можно быстрее, а любые потенциально медленные операции (такие как отправка электронной почты через SMTPHandler) выполняются в отдельном потоке.

Увы, они доступны только с Python 3.2 и выше.

4 голосов
/ 21 сентября 2014

Самая простая форма асинхронного обработчика smtp для меня - просто переопределить метод emit и использовать оригинальный метод в новом потоке.GIL не является проблемой в этом случае, потому что есть запрос ввода-вывода к SMTP-серверу, который выпускает GIL.Код выглядит следующим образом:

class ThreadedSMTPHandler(SMTPHandler):
    def emit(self, record):
        thread = Thread(target=SMTPHandler.emit, args=(self, record))
        thread.start()
0 голосов
/ 23 декабря 2011

При кодировании на Python следует помнить о GIL (Global Interpreter Lock). Эта блокировка предотвращает одновременное выполнение нескольких процессов. Есть много вещей, которые блокируют действия в Python. Они остановят все, пока не завершат.

В настоящее время единственный способ обойти GIL - это отменить действие, которое вы пытаетесь выполнить, для внешнего источника, такого как предлагают aix и MattH, или реализовать свой код, используя многопроцессорный модуль (http://docs.python.org/library/multiprocessing.html), чтобы один процесс обрабатывал отправка сообщений, а остальное обрабатывается другим процессом.

0 голосов
/ 23 декабря 2011

Скорее всего, вам нужно написать собственный обработчик журналирования, который будет отправлять электронную почту в фоновом режиме.

...