如何使用 python 日志记录的 SMTPHandler 和 SSL 发送电子邮件
How can I send an email using python logging's SMTPHandler and SSL
我正在开发一个烧瓶应用程序,我想在其中将错误级别日志记录发送到一个电子邮件地址。我尝试设置典型的错误处理程序:
mail_handler = SMTPHandler(mailhost=(app.config['MAIL_SERVER'], app.config['MAIL_PORT']),
fromaddr=app.config['MAIL_FROM_EMAIL'],
toaddrs=['me@my_address.com'],
subject='The server died. That sucks... :(',
credentials=(app.config['MAIL_USERNAME'], app.config['MAIL_PASSWORD']))
请注意,配置值是使用 flask-mail、MAIL_USE_SSL=True
和 MAIL_PORT=465
设置的。
但是,在调用错误时(在测试期间故意),我收到套接字超时错误 - 除了端口之外,我看不到如何告诉处理程序使用 SSL。有一个可以传递的 secure=()
参数(参见 the SMTPHandler docs),但指定我们使用 TLS,而不是 SSL。
任何线索如何做到这一点?谢谢!
编辑 - 请参阅 post 的底部了解更多最新信息,代码
感谢 Ned Deily pointing out that smtplib (which sits under SMTPHandler) requires special treatment. I also found this post 通过重载 SMTPHandler(在这种情况下修复 TLS 问题)演示了如何做到这一点。
使用 smtplib.SMTP_SSL
(see smtplib docs),而不是直接的 smtplib.SMTP
,我能够让整个系统正常工作。这是我用来设置处理程序的 utils/logs.py 文件(这应该是一个很好的文件示例,以及电子邮件、处理程序):
from your.application.file import app
import smtplib
import logging
from logging.handlers import RotatingFileHandler, SMTPHandler
# Provide a class to allow SSL (Not TLS) connection for mail handlers by overloading the emit() method
class SSLSMTPHandler(SMTPHandler):
def emit(self, record):
"""
Emit a record.
"""
try:
port = self.mailport
if not port:
port = smtplib.SMTP_PORT
smtp = smtplib.SMTP_SSL(self.mailhost, port)
msg = self.format(record)
if self.username:
smtp.login(self.username, self.password)
smtp.sendmail(self.fromaddr, self.toaddrs, msg)
smtp.quit()
except (KeyboardInterrupt, SystemExit):
raise
except:
self.handleError(record)
# Create file handler for error/warning/info/debug logs
file_handler = RotatingFileHandler('logs/app.log', maxBytes=1*1024*1024, backupCount=100)
# Apply format to the log messages
formatter = logging.Formatter("[%(asctime)s] | %(levelname)s | {%(pathname)s:%(lineno)d} | %(message)s")
file_handler.setFormatter(formatter)
# Set the level according to whether we're debugging or not
if app.debug:
file_handler.setLevel(logging.DEBUG)
else:
file_handler.setLevel(logging.WARN)
# Create equivalent mail handler
mail_handler = SSLSMTPHandler(mailhost=(app.config['MAIL_SERVER'], app.config['MAIL_PORT']),
fromaddr=app.config['MAIL_FROM_EMAIL'],
toaddrs='my@emailaddress.com',
subject='Your app died. Sad times...',
credentials=(app.config['MAIL_USERNAME'], app.config['MAIL_PASSWORD']))
# Set the email format
mail_handler.setFormatter(logging.Formatter('''
Message type: %(levelname)s
Location: %(pathname)s:%(lineno)d
Module: %(module)s
Function: %(funcName)s
Time: %(asctime)s
Message:
%(message)s
'''))
# Only email errors, not warnings
mail_handler.setLevel(logging.ERROR)
这是在我的申请文件中注册的:
# Register the handlers against all the loggers we have in play
# This is done after app configuration and SQLAlchemy initialisation,
# drop the sqlalchemy if not using - I thought a full example would be helpful.
import logging
from .utils.logs import mail_handler, file_handler
loggers = [app.logger, logging.getLogger('sqlalchemy'), logging.getLogger('werkzeug')]
for logger in loggers:
logger.addHandler(file_handler)
# Note - I added a boolean configuration parameter, MAIL_ON_ERROR,
# to allow direct control over whether to email on errors.
# You may wish to use 'if not app.debug' instead.
if app.config['MAIL_ON_ERROR']:
logger.addHandler(mail_handler)
编辑:
评论者@EduGord 无法正确发出记录。
深入挖掘,基础 SMTPHandler class 发送消息的方式与 3 多年前不同。
此更新的 emit()
方法应该使消息格式正确:
from email.message import EmailMessage
import email.utils
class SSLSMTPHandler(SMTPHandler):
def emit(self, record):
"""
Emit a record.
"""
try:
port = self.mailport
if not port:
port = smtplib.SMTP_PORT
smtp = smtplib.SMTP_SSL(self.mailhost, port)
msg = EmailMessage()
msg['From'] = self.fromaddr
msg['To'] = ','.join(self.toaddrs)
msg['Subject'] = self.getSubject(record)
msg['Date'] = email.utils.localtime()
msg.set_content(self.format(record))
if self.username:
smtp.login(self.username, self.password)
smtp.send_message(msg, self.fromaddr, self.toaddrs)
smtp.quit()
except (KeyboardInterrupt, SystemExit):
raise
except:
self.handleError(record)
希望这对某人有所帮助!
我正在开发一个烧瓶应用程序,我想在其中将错误级别日志记录发送到一个电子邮件地址。我尝试设置典型的错误处理程序:
mail_handler = SMTPHandler(mailhost=(app.config['MAIL_SERVER'], app.config['MAIL_PORT']),
fromaddr=app.config['MAIL_FROM_EMAIL'],
toaddrs=['me@my_address.com'],
subject='The server died. That sucks... :(',
credentials=(app.config['MAIL_USERNAME'], app.config['MAIL_PASSWORD']))
请注意,配置值是使用 flask-mail、MAIL_USE_SSL=True
和 MAIL_PORT=465
设置的。
但是,在调用错误时(在测试期间故意),我收到套接字超时错误 - 除了端口之外,我看不到如何告诉处理程序使用 SSL。有一个可以传递的 secure=()
参数(参见 the SMTPHandler docs),但指定我们使用 TLS,而不是 SSL。
任何线索如何做到这一点?谢谢!
编辑 - 请参阅 post 的底部了解更多最新信息,代码
感谢 Ned Deily pointing out that smtplib (which sits under SMTPHandler) requires special treatment. I also found this post 通过重载 SMTPHandler(在这种情况下修复 TLS 问题)演示了如何做到这一点。
使用 smtplib.SMTP_SSL
(see smtplib docs),而不是直接的 smtplib.SMTP
,我能够让整个系统正常工作。这是我用来设置处理程序的 utils/logs.py 文件(这应该是一个很好的文件示例,以及电子邮件、处理程序):
from your.application.file import app
import smtplib
import logging
from logging.handlers import RotatingFileHandler, SMTPHandler
# Provide a class to allow SSL (Not TLS) connection for mail handlers by overloading the emit() method
class SSLSMTPHandler(SMTPHandler):
def emit(self, record):
"""
Emit a record.
"""
try:
port = self.mailport
if not port:
port = smtplib.SMTP_PORT
smtp = smtplib.SMTP_SSL(self.mailhost, port)
msg = self.format(record)
if self.username:
smtp.login(self.username, self.password)
smtp.sendmail(self.fromaddr, self.toaddrs, msg)
smtp.quit()
except (KeyboardInterrupt, SystemExit):
raise
except:
self.handleError(record)
# Create file handler for error/warning/info/debug logs
file_handler = RotatingFileHandler('logs/app.log', maxBytes=1*1024*1024, backupCount=100)
# Apply format to the log messages
formatter = logging.Formatter("[%(asctime)s] | %(levelname)s | {%(pathname)s:%(lineno)d} | %(message)s")
file_handler.setFormatter(formatter)
# Set the level according to whether we're debugging or not
if app.debug:
file_handler.setLevel(logging.DEBUG)
else:
file_handler.setLevel(logging.WARN)
# Create equivalent mail handler
mail_handler = SSLSMTPHandler(mailhost=(app.config['MAIL_SERVER'], app.config['MAIL_PORT']),
fromaddr=app.config['MAIL_FROM_EMAIL'],
toaddrs='my@emailaddress.com',
subject='Your app died. Sad times...',
credentials=(app.config['MAIL_USERNAME'], app.config['MAIL_PASSWORD']))
# Set the email format
mail_handler.setFormatter(logging.Formatter('''
Message type: %(levelname)s
Location: %(pathname)s:%(lineno)d
Module: %(module)s
Function: %(funcName)s
Time: %(asctime)s
Message:
%(message)s
'''))
# Only email errors, not warnings
mail_handler.setLevel(logging.ERROR)
这是在我的申请文件中注册的:
# Register the handlers against all the loggers we have in play
# This is done after app configuration and SQLAlchemy initialisation,
# drop the sqlalchemy if not using - I thought a full example would be helpful.
import logging
from .utils.logs import mail_handler, file_handler
loggers = [app.logger, logging.getLogger('sqlalchemy'), logging.getLogger('werkzeug')]
for logger in loggers:
logger.addHandler(file_handler)
# Note - I added a boolean configuration parameter, MAIL_ON_ERROR,
# to allow direct control over whether to email on errors.
# You may wish to use 'if not app.debug' instead.
if app.config['MAIL_ON_ERROR']:
logger.addHandler(mail_handler)
编辑:
评论者@EduGord 无法正确发出记录。 深入挖掘,基础 SMTPHandler class 发送消息的方式与 3 多年前不同。
此更新的 emit()
方法应该使消息格式正确:
from email.message import EmailMessage
import email.utils
class SSLSMTPHandler(SMTPHandler):
def emit(self, record):
"""
Emit a record.
"""
try:
port = self.mailport
if not port:
port = smtplib.SMTP_PORT
smtp = smtplib.SMTP_SSL(self.mailhost, port)
msg = EmailMessage()
msg['From'] = self.fromaddr
msg['To'] = ','.join(self.toaddrs)
msg['Subject'] = self.getSubject(record)
msg['Date'] = email.utils.localtime()
msg.set_content(self.format(record))
if self.username:
smtp.login(self.username, self.password)
smtp.send_message(msg, self.fromaddr, self.toaddrs)
smtp.quit()
except (KeyboardInterrupt, SystemExit):
raise
except:
self.handleError(record)
希望这对某人有所帮助!