import logging from enum import Enum from app.tasks.emails import sendEmailRaw def _has_newline(line): """Used by has_bad_header to check for \\r or \\n""" if line and ("\r" in line or "\n" in line): return True return False def _is_bad_subject(subject): """Copied from: flask_mail.py class Message def has_bad_headers""" if _has_newline(subject): for linenum, line in enumerate(subject.split("\r\n")): if not line: return True if linenum > 0 and line[0] not in "\t ": return True if _has_newline(line): return True if len(line.strip()) == 0: return True return False class FlaskMailSubjectFormatter(logging.Formatter): def format(self, record): record.message = record.getMessage() if self.usesTime(): record.asctime = self.formatTime(record, self.datefmt) s = self.formatMessage(record) return s class FlaskMailTextFormatter(logging.Formatter): pass # TODO: hier nog niet tevreden over (vooral logger.error(..., exc_info, stack_info)) class FlaskMailHTMLFormatter(logging.Formatter): pre_template = "
%s" def formatException(self, exc_info): formatted_exception = logging.Handler.formatException(self, exc_info) return FlaskMailHTMLFormatter.pre_template % ("Exception information", formatted_exception) def formatStack(self, stack_info): return FlaskMailHTMLFormatter.pre_template % ("
%s
", stack_info)
# see: https://github.com/python/cpython/blob/3.6/Lib/logging/__init__.py (class Handler)
class FlaskMailHandler(logging.Handler):
def __init__(self, mailer, subject_template, level=logging.NOTSET):
logging.Handler.__init__(self, level)
self.mailer = mailer
self.send_to = mailer.app.config["MAIL_UTILS_ERROR_SEND_TO"]
self.subject_template = subject_template
self.html_formatter = None
def setFormatter(self, text_fmt, html_fmt=None):
"""
Set the formatters for this handler. Provide at least one formatter.
When no text_fmt is provided, no text-part is created for the email body.
"""
assert (text_fmt, html_fmt) != (None, None), "At least one formatter should be provided"
if type(text_fmt)==str:
text_fmt = FlaskMailTextFormatter(text_fmt)
self.formatter = text_fmt
if type(html_fmt)==str:
html_fmt = FlaskMailHTMLFormatter(html_fmt)
self.html_formatter = html_fmt
def getSubject(self, record):
fmt = FlaskMailSubjectFormatter(self.subject_template)
subject = fmt.format(record)
#Since templates can cause header problems, and we rather have a incomplete email then an error, we fix this
if _is_bad_subject(subject):
subject="FlaskMailHandler log-entry from %s [original subject is replaced, because it would result in a bad header]" % self.mailer.app.name
return subject
def emit(self, record):
text = self.format(record) if self.formatter else None
html = self.html_formatter.format(record) if self.html_formatter else None
sendEmailRaw.delay(self.send_to, self.getSubject(record), text, html)
def register_mail_error_handler(app, mailer):
subject_template = "ContentDB crashed (%(module)s > %(funcName)s)"
text_template = """
Message type: %(levelname)s
Location: %(pathname)s:%(lineno)d
Module: %(module)s
Function: %(funcName)s
Time: %(asctime)s
Message:
%(message)s"""
html_template = """
Message type: | %(levelname)s |
---|---|
Location: | %(pathname)s:%(lineno)d |
Module: | %(module)s |
Function: | %(funcName)s |
Time: | %(asctime)s |
%(message)s
"""
import logging
mail_handler = FlaskMailHandler(mailer, subject_template)
mail_handler.setLevel(logging.ERROR)
mail_handler.setFormatter(text_template, html_template)
app.logger.addHandler(mail_handler)