2011-12-23 60 views
6

我安裝了一個本地SMTP server和使用logging.handlers.SMTPHandler使用此代碼記錄異常:如何使SMTPHandler不會阻止

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 

花了1秒相比更長時間才能完成,阻斷python腳本該全時間。怎麼來的?我怎樣才能讓它不會阻止腳本?

回答

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), '[email protected]_company.com', ['[email protected]_company.com'], 'Error found!', ('[email protected]', 'top_secret_gmail_password')) 
gm.setLevel(logging.ERROR) 

logger.addHandler(gm) 

try: 
    1/0 
except: 
    logger.exception('FFFFFFFFFFFFFFFFFFFFFFFUUUUUUUUUUUUUUUUUUUUUU-') 
0

最可能的是,你需要編寫你自己的日誌處理程序,它會做的電子郵件的發送的背景。

+0

例如調用可能隨MTA附帶的本地'sendmail'程序,並提交給本地SMTP服務器而不使用SMTP。 – MattH

5

你可以使用QueueHandlerQueueListener。從文檔摘自:

隨着QueueListener類,QueueHandler可以用來讓 做處理從其中一個確實 記錄一個單獨的線程工作。其中螺紋的客戶提供服務需要(通過發送一個SMTPHandler電子郵件如 )迴應 儘快,而任何潛在的慢操作一個單獨的線程完成這是在Web應用程序以及其他 服務應用很重要。

唉,他們只能從Python的3.2起。

+2

+1,它們*通過logutils項目可用於較舊的Python版本:http://plumberjack.blogspot.com/2010/10/logutils-using-recent-logging-features.html –

0

在Python中編寫代碼時需要注意的一點是GIL(全局解釋器鎖)。此鎖可防止同時發生多個進程。在Python中有很多東西是「阻塞」活動。他們會阻止一切,直到他們完成。

目前圍繞GIL的唯一方法是推動您嘗試使用外部來源(如aix和MattH)所建議的操作,或者使用多處理模塊(http://docs.python)來實現您的代碼。 org/library/multiprocessing.html),以便一個進程處理消息的發送,其餘進程由另一個進程處理。

2

異步SMTP處理程序對我來說,最簡單的形式就是重寫emit方法和新的線程中使用的原始方法。 GIL在這種情況下不是問題,因爲有一個I/O調用發送GIL的SMTP服務器。的代碼如下

class ThreadedSMTPHandler(SMTPHandler): 
    def emit(self, record): 
     thread = Thread(target=SMTPHandler.emit, args=(self, record)) 
     thread.start()