我安装了一个本地SMTP server并使用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脚本。怎么会?如何让它不阻止脚本?
答案 0 :(得分:13)
这是我正在使用的实现,我基于this Gmail adapted 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-')
答案 1 :(得分:5)
您可以使用QueueHandler和QueueListener。取自文档:
与QueueListener类一起,可以使用QueueHandler来释放 处理程序在一个单独的线程上完成它们的工作 伐木。这在Web应用程序和其他应用程序中很重要 服务应用程序,服务客户端的线程需要响应 尽可能快地进行任何潜在的慢速操作(例如 通过SMTPHandler发送电子邮件)是在一个单独的线程上完成的。
唉,他们只能从Python 3.2开始使用。
答案 2 :(得分:4)
对我来说,最简单的异步smtp处理程序形式只是覆盖emit
方法并在新线程中使用原始方法。在这种情况下,GIL不是问题,因为对SMTP服务器的I / O调用会释放GIL。代码如下
class ThreadedSMTPHandler(SMTPHandler):
def emit(self, record):
thread = Thread(target=SMTPHandler.emit, args=(self, record))
thread.start()
答案 3 :(得分:0)
很可能你需要编写自己的日志处理程序,以便在后台发送电子邮件。
答案 4 :(得分:0)
在Python中编码时要记住的是GIL(全局解释器锁)。此锁定可防止同时发生多个进程。在Python中有许多“阻塞”活动。他们将阻止一切,直到他们完成。
目前围绕GIL的唯一方法是推迟尝试外部源的操作,如aix和MattH建议,或使用多处理模块实现代码(http://docs.python.org /library/multiprocessing.html)以便一个进程处理消息的发送,其余进程由另一个进程处理。
答案 5 :(得分:0)
由于OP pointed out,QueueHandler和QueueListener可以解决问题!我进行了一些研究,并改编了this page上的代码,为您提供了一些示例代码:
# In your init part,
# assuming your logger is given by the "logger" variable
# and your config is storded in the "config" dictionary
logging_queue = Queue(-1)
queue_handler = QueueHandler(logging_queue)
queue_handler.setLevel(logging.ERROR)
queue_handler.setFormatter(logging_formatter)
logger.addHandler(queue_handler)
smtp_handler = SMTPHandler(mailhost=(config['MAIL_SERVER'], config['MAIL_PORT']),
fromaddr=config['MAIL_SENDER'],
toaddrs=[config['ERROR_MAIL']],
subject='Application error',
credentials=(config['MAIL_USERNAME'], config['MAIL_PASSWORD']),
secure=tuple())
smtp_handler.setLevel(logging.ERROR)
smtp_handler.setFormatter(logging_formatter)
queue_listener = QueueListener(logging_queue, smtp_handler)
queue_listener.start()
# Let's test it. The warning is not mailed, the error is.
logger.warning('Test warning')
logger.error('Test error')
我不确定的是是否有必要两次使用setLevel
和setFormatter
,也许没有必要。