diff --git a/django/utils/log.py b/django/utils/log.py index 2de6dbbb598..e40d87159c6 100644 --- a/django/utils/log.py +++ b/django/utils/log.py @@ -7,7 +7,6 @@ from django.core import mail from django.core.mail import get_connection from django.core.management.color import color_style from django.utils.module_loading import import_string -from django.views.debug import ExceptionReporter request_logger = logging.getLogger('django.request') @@ -83,10 +82,11 @@ class AdminEmailHandler(logging.Handler): 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__() self.include_html = include_html self.email_backend = email_backend + self.reporter_class = import_string(reporter_class or 'django.views.debug.ExceptionReporter') def emit(self, record): try: @@ -116,7 +116,7 @@ class AdminEmailHandler(logging.Handler): else: 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()) html_message = reporter.get_traceback_html() if self.include_html else None self.send_mail(subject, message, fail_silently=True, html_message=html_message) diff --git a/docs/releases/3.0.txt b/docs/releases/3.0.txt index 52d435d5d54..9745a346600 100644 --- a/docs/releases/3.0.txt +++ b/docs/releases/3.0.txt @@ -259,6 +259,14 @@ Internationalization language cookies. The default values of these settings preserve the previous 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 ~~~~~~~~~~~~~~~~~~~ diff --git a/docs/topics/logging.txt b/docs/topics/logging.txt index 0820451709a..ba73234fc1a 100644 --- a/docs/topics/logging.txt +++ b/docs/topics/logging.txt @@ -595,7 +595,7 @@ Handlers Django provides one log handler in addition to those provided by the 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 message it receives. @@ -652,6 +652,26 @@ Python logging module. By default, an instance of the email backend specified in :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) Sends emails to admin users. To customize this behavior, you can diff --git a/tests/logging_tests/logconfig.py b/tests/logging_tests/logconfig.py index b068103eb9a..93b01865906 100644 --- a/tests/logging_tests/logconfig.py +++ b/tests/logging_tests/logconfig.py @@ -2,6 +2,7 @@ import logging from django.conf import settings from django.core.mail.backends.base import BaseEmailBackend +from django.views.debug import ExceptionReporter class MyHandler(logging.Handler): @@ -13,3 +14,8 @@ class MyHandler(logging.Handler): class MyEmailBackend(BaseEmailBackend): def send_messages(self, email_messages): pass + + +class CustomExceptionReporter(ExceptionReporter): + def get_traceback_text(self): + return 'custom traceback text' diff --git a/tests/logging_tests/tests.py b/tests/logging_tests/tests.py index a257cafa905..c341e899972 100644 --- a/tests/logging_tests/tests.py +++ b/tests/logging_tests/tests.py @@ -16,6 +16,7 @@ from django.utils.log import ( DEFAULT_LOGGING, AdminEmailHandler, CallbackFilter, RequireDebugFalse, RequireDebugTrue, ServerFormatter, ) +from django.views.debug import ExceptionReporter from . import views from .logconfig import MyEmailBackend @@ -431,6 +432,20 @@ class AdminEmailHandlerTest(SimpleTestCase): finally: 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): """