Fixed #29714 -- Allowed using ExceptionReporter subclass with AdminEmailHandler.

This commit is contained in:
Nasir Hussain 2019-02-24 20:10:14 +05:00 committed by Mariusz Felisiak
parent 3c6a4fdb6d
commit 25706d7285
5 changed files with 53 additions and 4 deletions

View File

@ -7,7 +7,6 @@ from django.core import mail
from django.core.mail import get_connection from django.core.mail import get_connection
from django.core.management.color import color_style from django.core.management.color import color_style
from django.utils.module_loading import import_string from django.utils.module_loading import import_string
from django.views.debug import ExceptionReporter
request_logger = logging.getLogger('django.request') request_logger = logging.getLogger('django.request')
@ -83,10 +82,11 @@ class AdminEmailHandler(logging.Handler):
request data will be provided in the email report. request data will be provided in the email report.
""" """
def __init__(self, include_html=False, email_backend=None): def __init__(self, include_html=False, email_backend=None, reporter_class=None):
super().__init__() super().__init__()
self.include_html = include_html self.include_html = include_html
self.email_backend = email_backend self.email_backend = email_backend
self.reporter_class = import_string(reporter_class or 'django.views.debug.ExceptionReporter')
def emit(self, record): def emit(self, record):
try: try:
@ -116,7 +116,7 @@ class AdminEmailHandler(logging.Handler):
else: else:
exc_info = (None, record.getMessage(), None) exc_info = (None, record.getMessage(), None)
reporter = ExceptionReporter(request, is_email=True, *exc_info) reporter = self.reporter_class(request, is_email=True, *exc_info)
message = "%s\n\n%s" % (self.format(no_exc_record), reporter.get_traceback_text()) message = "%s\n\n%s" % (self.format(no_exc_record), reporter.get_traceback_text())
html_message = reporter.get_traceback_html() if self.include_html else None html_message = reporter.get_traceback_html() if self.include_html else None
self.send_mail(subject, message, fail_silently=True, html_message=html_message) self.send_mail(subject, message, fail_silently=True, html_message=html_message)

View File

@ -259,6 +259,14 @@ Internationalization
language cookies. The default values of these settings preserve the previous language cookies. The default values of these settings preserve the previous
behavior. behavior.
Logging
~~~~~~~
* The new ``reporter_class`` parameter of
:class:`~django.utils.log.AdminEmailHandler` allows providing an
``django.views.debug.ExceptionReporter`` subclass to customize the traceback
text sent to site :setting:`ADMINS` when :setting:`DEBUG` is ``False``.
Management Commands Management Commands
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~

View File

@ -595,7 +595,7 @@ Handlers
Django provides one log handler in addition to those provided by the Django provides one log handler in addition to those provided by the
Python logging module. Python logging module.
.. class:: AdminEmailHandler(include_html=False, email_backend=None) .. class:: AdminEmailHandler(include_html=False, email_backend=None, reporter_class=None)
This handler sends an email to the site :setting:`ADMINS` for each log This handler sends an email to the site :setting:`ADMINS` for each log
message it receives. message it receives.
@ -652,6 +652,26 @@ Python logging module.
By default, an instance of the email backend specified in By default, an instance of the email backend specified in
:setting:`EMAIL_BACKEND` will be used. :setting:`EMAIL_BACKEND` will be used.
The ``reporter_class`` argument of ``AdminEmailHandler`` allows providing
an ``django.views.debug.ExceptionReporter`` subclass to customize the
traceback text sent in the email body. You provide a string import path to
the class you wish to use, like this:
.. code-block:: python
'handlers': {
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
'include_html': True,
'reporter_class': 'somepackage.error_reporter.CustomErrorReporter'
}
},
.. versionadded:: 3.0
The ``reporter_class`` argument was added.
.. method:: send_mail(subject, message, *args, **kwargs) .. method:: send_mail(subject, message, *args, **kwargs)
Sends emails to admin users. To customize this behavior, you can Sends emails to admin users. To customize this behavior, you can

View File

@ -2,6 +2,7 @@ import logging
from django.conf import settings from django.conf import settings
from django.core.mail.backends.base import BaseEmailBackend from django.core.mail.backends.base import BaseEmailBackend
from django.views.debug import ExceptionReporter
class MyHandler(logging.Handler): class MyHandler(logging.Handler):
@ -13,3 +14,8 @@ class MyHandler(logging.Handler):
class MyEmailBackend(BaseEmailBackend): class MyEmailBackend(BaseEmailBackend):
def send_messages(self, email_messages): def send_messages(self, email_messages):
pass pass
class CustomExceptionReporter(ExceptionReporter):
def get_traceback_text(self):
return 'custom traceback text'

View File

@ -16,6 +16,7 @@ from django.utils.log import (
DEFAULT_LOGGING, AdminEmailHandler, CallbackFilter, RequireDebugFalse, DEFAULT_LOGGING, AdminEmailHandler, CallbackFilter, RequireDebugFalse,
RequireDebugTrue, ServerFormatter, RequireDebugTrue, ServerFormatter,
) )
from django.views.debug import ExceptionReporter
from . import views from . import views
from .logconfig import MyEmailBackend from .logconfig import MyEmailBackend
@ -431,6 +432,20 @@ class AdminEmailHandlerTest(SimpleTestCase):
finally: finally:
admin_email_handler.include_html = old_include_html admin_email_handler.include_html = old_include_html
def test_default_exception_reporter_class(self):
admin_email_handler = self.get_admin_email_handler(self.logger)
self.assertEqual(admin_email_handler.reporter_class, ExceptionReporter)
@override_settings(ADMINS=[('A.N.Admin', 'admin@example.com')])
def test_custom_exception_reporter_is_used(self):
record = self.logger.makeRecord('name', logging.ERROR, 'function', 'lno', 'message', None, None)
record.request = self.request_factory.get('/')
handler = AdminEmailHandler(reporter_class='logging_tests.logconfig.CustomExceptionReporter')
handler.emit(record)
self.assertEqual(len(mail.outbox), 1)
msg = mail.outbox[0]
self.assertEqual(msg.body, 'message\n\ncustom traceback text')
class SettingsConfigTest(AdminScriptTestCase): class SettingsConfigTest(AdminScriptTestCase):
""" """