簡體   English   中英

使用 SMTPHandler 在 Python 日志記錄 MemoryHandler 中整理輸出

[英]Collate output in Python logging MemoryHandler with SMTPHandler

我已將日志記錄模塊 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)

與此相關:

如果它是memory_handler的目標,我是否需要明確地將error_mail_handler添加到記錄器中? error_mail_handler應該設置為 DEBUG 還是 ERROR 目標? 當它從memory_handler被饋送時,它甚至需要一個目標嗎?

希望看到解決此問題的任何人的一些工作代碼。

您可能希望使用或調整此測試腳本中BufferingSMTPHandler

通常,如果它是已添加到記錄器的 MemoryHandler 處理程序的目標,則不需要向記錄器添加處理程序。 如果您設置處理程序的級別,這將影響處理程序實際處理的內容 - 它不會處理比其級別設置更輕的任何內容。

與其緩沖電子郵件,不如考慮將無緩沖發布到消息應用程序上的消息流中,例如在 Matrix、Discord、Slack 等上。話雖如此,我編寫了自己的BufferingSMTPHandler備份鏈接)的線程安全實現,它發送電子郵件從一個單獨的線程。 主要目標是不阻塞主線程。

正如所寫,它使用兩個隊列——這似乎是實現代碼“可配置參數”部分中定義的一些有用的類級參數所必需的。 盡管您可以按原樣使用代碼,但如果您學習並使用它來編寫自己的類可能會更好。

問題:

  • 一些類級參數也許可以是實例級的。
  • threading.Timersignal模塊都可以用來避免永遠運行的循環。

如果您使用的是 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'
        }
    },
    ...
}

更新了 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)

為此,我使用了 Vinay Sajip 建議的BufferingSMTPHandler並做了一個小調整:我將緩沖區長度設置為非常大的值(比如 5000 條日志記錄),並每隔幾秒手動調用一次處理程序的刷新方法,並在檢查互聯網連接后。

# 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)

我認為關於 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")

希望這可以幫助某人。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM