使用SMTPHandler在Python中记录MemoryHandler中的输出

时间:2009-10-23 00:40:58

标签: python logging

我将日志记录模块MemoryHandler设置为为SMTPHandler目标排队调试和错误消息。我想要的是当包含所有调试语句的进程错误(每行一个)时发送一封电子邮件。我得到的是每个调试消息的单独电子邮件。

这看起来应该是微不足道的,也是日志记录包的一部分,但我找不到任何关于它的内容,没有例子,谷歌上没有任何内容。

log = logging.getLogger()
log.setLevel(logging.DEBUG)
debug_format = logging.Formatter("%(levelname)s at %(asctime)s in %(filename)s (line %(lineno)d):: %(message)s")

# write errors to email
error_mail_subject = "ERROR: Script error in %s on %s" % (sys.argv[0], os.uname()[1])
error_mail_handler = logging.handlers.SMTPHandler(SMTP_HOST, 'errors@'+os.uname()[1], [LOG_EMAIL], error_mail_subject)
error_mail_handler.setLevel(logging.ERROR)
#error_mail_handler.setLevel(logging.DEBUG)
error_mail_handler.setFormatter(debug_format)

# buffer debug messages so they can be sent with error emails
memory_handler = logging.handlers.MemoryHandler(1024*10, logging.ERROR, error_mail_handler)
memory_handler.setLevel(logging.DEBUG)

# attach handlers
log.addHandler(memory_handler)
log.addHandler(error_mail_handler)

与此相关:

如果error_mail_handler目标是memory_handler,是否需要将error_mail_handler明确添加到记录器中? memory_handler应该设置为DEBUG还是ERROR目标?当它从{{1}}喂养时是否还需要一个目标?

很想看到解决此问题的任何人的一些工作代码。

6 个答案:

答案 0 :(得分:25)

您可能希望使用或修改this test script中的BufferingSMTPHandler

通常,如果记录器的目标是已添加到记录器的MemoryHandler处理程序,则无需向记录器添加处理程序。如果设置处理程序的级别,这将影响处理程序实际处理的内容 - 它将不处理任何比其级别设置更不严重的程序。

答案 1 :(得分:5)

我编写了自己的BufferingSMTPHandler的beastly线程安全实现,它从一个单独的线程发送电子邮件。主要目标是不阻止主线程。

如上所述,它使用两个队列 - 这似乎是必要的,以便实现一些在代码的“可配置参数”部分中定义的有用的类级参数。虽然您可以按原样使用代码,但如果您学习并使用它来编写自己的类,可能会更好。

的问题:

  • 某些类级参数可能是实例级的。
  • 可以使用threading.Timersignal模块来避免永远运行的循环。

答案 2 :(得分:2)

如果您使用的是django - 这里是简单的缓冲处理程序,它将使用标准的django电子邮件方法:

import logging

from django.conf import settings
from django.core.mail import EmailMessage


class DjangoBufferingSMTPHandler(logging.handlers.BufferingHandler):
    def __init__(self, capacity, toaddrs=None, subject=None):
        logging.handlers.BufferingHandler.__init__(self, capacity)

        if toaddrs:
            self.toaddrs = toaddrs
        else:
            # Send messages to site administrators by default
            self.toaddrs = zip(*settings.ADMINS)[-1]

        if subject:
            self.subject = subject
        else:
            self.subject = 'logging'

    def flush(self):
        if len(self.buffer) == 0:
            return

        try:
            msg = "\r\n".join(map(self.format, self.buffer))
            emsg = EmailMessage(self.subject, msg, to=self.toaddrs)
            emsg.send()
        except Exception:
            # handleError() will print exception info to stderr if logging.raiseExceptions is True
            self.handleError(record=None)
        self.buffer = []

在django settings.py中,您需要配置电子邮件和日志记录,如下所示:

EMAIL_USE_TLS = True
EMAIL_PORT = 25  
EMAIL_HOST = ''  # example: 'smtp.yandex.ru'
EMAIL_HOST_USER = ''  # example: 'user@yandex.ru'
EMAIL_HOST_PASSWORD = ''
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
SERVER_EMAIL = EMAIL_HOST_USER

LOGGING = {
    'handlers': {
        ...
        'mail_buffer': {
            'level': 'WARN',
            'capacity': 9999,
            'class': 'utils.logging.DjangoBufferingSMTPHandler',
            # optional: 
            # 'toaddrs': 'admin@host.com'
            # 'subject': 'log messages'
        }
    },
    ...
}

答案 3 :(得分:2)

更新了 Vinay Sajip 对 python3 的回答。

import logging
from logging.handlers import BufferingHandler

class BufferingSMTPHandler(BufferingHandler):
    def __init__(self, mailhost, fromaddr, toaddrs, subject, capacity):
        logging.handlers.BufferingHandler.__init__(self, capacity)
        self.mailhost = mailhost
        self.mailport = None
        self.fromaddr = fromaddr
        self.toaddrs = toaddrs
        self.subject = subject
        self.setFormatter(logging.Formatter("%(asctime)s %(levelname)-5s %(message)s"))

    def flush(self):
        if len(self.buffer) > 0:
            try:
                import smtplib
                port = self.mailport
                if not port:
                    port = smtplib.SMTP_PORT
                smtp = smtplib.SMTP(self.mailhost, port)
                msg = '''From: {}\r\nTo: {}\r\nSubject: {}\r\n\r\n'''.format(
                            self.fromaddr,
                            ",".join(self.toaddrs),
                            self.subject
                            )
                for record in self.buffer:
                    s = self.format(record)
                    print (s)
                    msg = msg + s + "\r\n"
                smtp.sendmail(self.fromaddr, self.toaddrs, msg)
                smtp.quit()
            except:
                self.handleError(None)  # no particular record
            self.buffer = []

#update for @Anant
if __name__ == '__main__'
    buff_smtp_handler=BufferingSMTPHandler(...your args)
    buff_smtp_handler.setLevel(logging.ERROR)
    handlers=[buff_smtp_handler]
    logging.basicConfig(handlers=handlers)

答案 4 :(得分:0)

为此,我使用Vinay Sajip建议的 BufferingSMTPHandler 进行一次小调整:我将缓冲区长度设置为非常大的(比如5000个日志记录)并且manualy调用处理程序的flush方法检查互联网连接后每隔几秒钟。

# init
log_handler1 = BufferingSMTPHandler(
    'smtp.host.lala', "from@test.com", ['to@test.com'], 'Log event(s)',5000)
...
logger.addHandler(log_handler1)
...

# main code
...
if internet_connection_ok and seconds_since_last_flush>60:
    log_handler1.flush() # send buffered log records (if any)

答案 5 :(得分:0)

我认为有关SMTP记录器的要点是,它旨在发送重要的日志消息,如果将其发送给人工收件人或由自动收件人进行进一步处理,则将起到某种警报的作用。

如果要通过电子邮件发送日志消息的集合,则构成在任务执行结束时发送的报告,并将该日志写入文件,然后通过电子邮件发送该文件似乎是一个合理的解决方案。

我研究了基本的FileHandler日志处理程序,以及如何建立一种机制来写入临时文件,然后在脚本退出时附加该临时文件。

我找到了“ atexit”模块,该模块允许注册一种方法,该方法将在脚本退出时针对对象执行。

import logging
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
import os
from email import encoders
import uuid
# atexit allows for a method to be set to handle an object when the script             exits
import atexit
filename = uuid.uuid4().hex
class MailLogger:

def __init__(self, filePath, smtpDict):
    self.filePath = filePath
    self.smtpDict = smtpDict
    # Generate random file name
    filename = '%s.txt' % ( uuid.uuid4().hex )
    # Create full filename
    filename = '%s/%s' % (filePath,filename)
    self.filename = filename
    self.fileLogger = logging.getLogger('mailedLog')
    self.fileLogger.setLevel(logging.INFO)
    self.fileHandler = logging.FileHandler(filename)
    self.fileHandler.setLevel(logging.INFO)
    formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
    self.fileHandler.setFormatter(formatter)
    self.fileLogger.addHandler(self.fileHandler)
    atexit.register(self.mailOut)

def mailOut(self):
    '''
    Script is exiting so time to mail out the log file

    "emailSettings":    {
                        "smtpServer"    :     "smtp.dom.com",
                        "smtpPort"        :    25,
                        "sender"        :    "sender@dom.com>",
                        "recipients"    :    [
                                                "recipient@dom.com"
                                            ],
                        "subject"        :    "Email Subject"
},
    '''
    # Close the file handler
    smtpDict = self.smtpDict
    self.fileHandler.close()
    msg = MIMEMultipart('alternative')
    s = smtplib.SMTP(smtpDict["smtpServer"], smtpDict["smtpPort"] )        
    msg['Subject'] = smtpDict["subject"]
    msg['From'] = smtpDict["sender"]
    msg['To'] = ','.join(smtpDict["recipients"])
    body = 'See attached report file'    
    content = MIMEText(body, 'plain')
    msg.attach(content)
    attachment = MIMEBase('application', 'octet-stream')
    attachment.set_payload(open(self.filename, 'rb').read())
    encoders.encode_base64(attachment)
    attachment.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(self.filename))
    msg.attach(attachment)
    s.send_message(msg)
    s.quit()

我的基本测试脚本是:

from EmailLogRpt import MailLogger
import time
smtpDict = {
                        "smtpServer"    :     "smtp.dom.com",
                        "smtpPort"        :    25,
                        "sender"        :    "sender@dom.com",
                        "recipients"    :    [
                                                "recpient@dom.com>"
                                            ],
                        "subject"        :    "Email Subject"
}
myMailLogger = MailLogger("/home/ed/tmp",smtpDict).fileLogger
myMailLogger.info("test msg 1")
time.sleep(5)
myMailLogger.info("test msg 2")

希望这对某人有帮助。