2011-12-23 10 views
6

Ho installato un locale SMTP server e utilizzato per accedere logging.handlers.SMTPHandler un'eccezione utilizzando questo codice:Come fare SMTPHandler non blocca

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 

ci sono voluti più di 1 secondo per completare, bloccando lo script python per tutto questo tempo. Come mai? Come posso far sì che non blocchi la sceneggiatura?

risposta

13

Ecco l'implementazione che sto utilizzando, che ho basato su this Gmail adapted SMTPHandler.
Ho preso la parte che invia a SMTP e l'ho inserita in un thread diverso.

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) 

Esempio di utilizzo:

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

Molto probabilmente è necessario scrivere il proprio gestore di registrazione che farebbe l'invio della e-mail in background.

+0

ad es. Chiamando il programma locale 'sendmail' che probabilmente è arrivato con il tuo MTA e lo invia al tuo server SMTP locale senza usare SMTP. – MattH

5

È possibile utilizzare QueueHandler e QueueListener. Preso dalla documentazione:

Insieme alla classe QueueListener, QueueHandler può essere utilizzato per far gestori fanno il loro lavoro su un thread separato da quello che fa la registrazione. Questo è importante nelle applicazioni Web e in altre applicazioni di servizio in cui i client di riparazione dei thread devono rispondere il più rapidamente possibile a , mentre eventuali operazioni potenzialmente lente (come ad esempio l'invio di e-mail tramite SMTPHandler) vengono eseguite su un thread separato.

Purtroppo sono disponibili solo da Python 3.2 in poi.

+2

+1 e sono * disponibili * per le versioni precedenti di Python tramite il progetto logutils: http://plumberjack.blogspot.com/2010/10/logutils-using-recent-logging-features.html –

0

Una cosa da tenere a mente quando la codifica in Python è GIL (Global Interpreter Lock). Questo blocco impedisce più di un processo nello stesso momento. ci sono molte cose che sono attività 'bloccanti' in Python. Fermeranno tutto fino al completamento.

Attualmente l'unico modo per aggirare il GIL è quello di spingere l'azione che si sta tentando a una fonte esterna come suggerito da aix e MattH o di implementare il codice usando il modulo multiprocessing (http: //docs.python. org/library/multiprocessing.html) in modo che un processo gestisca l'invio di messaggi e il resto venga gestito dall'altro processo.

2

La forma più semplice di gestore smtp asincrono per me è solo per ignorare emit metodo e utilizzare il metodo originale in un nuovo thread. GIL non è un problema in questo caso perché esiste una chiamata I/O al server SMTP che rilascia GIL. Il codice è il seguente

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