mirror of https://github.com/django/django.git
Fixed #30752 -- Allowed using ExceptionReporter subclasses in error reports.
This commit is contained in:
parent
a5a28de89d
commit
13e4abf83e
|
@ -567,6 +567,10 @@ LOGGING_CONFIG = 'logging.config.dictConfig'
|
||||||
# Custom logging configuration.
|
# Custom logging configuration.
|
||||||
LOGGING = {}
|
LOGGING = {}
|
||||||
|
|
||||||
|
# Default exception reporter class used in case none has been
|
||||||
|
# specifically assigned to the HttpRequest instance.
|
||||||
|
DEFAULT_EXCEPTION_REPORTER = 'django.views.debug.ExceptionReporter'
|
||||||
|
|
||||||
# Default exception reporter filter class used in case none has been
|
# Default exception reporter filter class used in case none has been
|
||||||
# specifically assigned to the HttpRequest instance.
|
# specifically assigned to the HttpRequest instance.
|
||||||
DEFAULT_EXCEPTION_REPORTER_FILTER = 'django.views.debug.SafeExceptionReporterFilter'
|
DEFAULT_EXCEPTION_REPORTER_FILTER = 'django.views.debug.SafeExceptionReporterFilter'
|
||||||
|
|
|
@ -86,7 +86,7 @@ class AdminEmailHandler(logging.Handler):
|
||||||
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')
|
self.reporter_class = import_string(reporter_class or settings.DEFAULT_EXCEPTION_REPORTER)
|
||||||
|
|
||||||
def emit(self, record):
|
def emit(self, record):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -47,7 +47,7 @@ def technical_500_response(request, exc_type, exc_value, tb, status_code=500):
|
||||||
Create a technical server error response. The last three arguments are
|
Create a technical server error response. The last three arguments are
|
||||||
the values returned from sys.exc_info() and friends.
|
the values returned from sys.exc_info() and friends.
|
||||||
"""
|
"""
|
||||||
reporter = ExceptionReporter(request, exc_type, exc_value, tb)
|
reporter = get_exception_reporter_class(request)(request, exc_type, exc_value, tb)
|
||||||
if request.is_ajax():
|
if request.is_ajax():
|
||||||
text = reporter.get_traceback_text()
|
text = reporter.get_traceback_text()
|
||||||
return HttpResponse(text, status=status_code, content_type='text/plain; charset=utf-8')
|
return HttpResponse(text, status=status_code, content_type='text/plain; charset=utf-8')
|
||||||
|
@ -67,6 +67,11 @@ def get_exception_reporter_filter(request):
|
||||||
return getattr(request, 'exception_reporter_filter', default_filter)
|
return getattr(request, 'exception_reporter_filter', default_filter)
|
||||||
|
|
||||||
|
|
||||||
|
def get_exception_reporter_class(request):
|
||||||
|
default_exception_reporter_class = import_string(settings.DEFAULT_EXCEPTION_REPORTER)
|
||||||
|
return getattr(request, 'exception_reporter_class', default_exception_reporter_class)
|
||||||
|
|
||||||
|
|
||||||
class SafeExceptionReporterFilter:
|
class SafeExceptionReporterFilter:
|
||||||
"""
|
"""
|
||||||
Use annotations made by the sensitive_post_parameters and
|
Use annotations made by the sensitive_post_parameters and
|
||||||
|
|
|
@ -305,6 +305,62 @@ following attributes and methods:
|
||||||
traceback frame. Sensitive values are replaced with
|
traceback frame. Sensitive values are replaced with
|
||||||
:attr:`cleansed_substitute`.
|
:attr:`cleansed_substitute`.
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
|
If you need to customize error reports beyond filtering you may specify a
|
||||||
|
custom error reporter class by defining the
|
||||||
|
:setting:`DEFAULT_EXCEPTION_REPORTER` setting::
|
||||||
|
|
||||||
|
DEFAULT_EXCEPTION_REPORTER = 'path.to.your.CustomExceptionReporter'
|
||||||
|
|
||||||
|
The exception reporter is responsible for compiling the exception report data,
|
||||||
|
and formatting it as text or HTML appropriately. (The exception reporter uses
|
||||||
|
:setting:`DEFAULT_EXCEPTION_REPORTER_FILTER` when preparing the exception
|
||||||
|
report data.)
|
||||||
|
|
||||||
|
Your custom reporter class needs to inherit from
|
||||||
|
:class:`django.views.debug.ExceptionReporter`.
|
||||||
|
|
||||||
|
.. class:: ExceptionReporter
|
||||||
|
|
||||||
|
.. method:: get_traceback_data()
|
||||||
|
|
||||||
|
Return a dictionary containing traceback information.
|
||||||
|
|
||||||
|
This is the main extension point for customizing exception reports, for
|
||||||
|
example::
|
||||||
|
|
||||||
|
from django.views.debug import ExceptionReporter
|
||||||
|
|
||||||
|
|
||||||
|
class CustomExceptionReporter(ExceptionReporter):
|
||||||
|
def get_traceback_data(self):
|
||||||
|
data = super().get_traceback_data()
|
||||||
|
# ... remove/add something here ...
|
||||||
|
return data
|
||||||
|
|
||||||
|
.. method:: get_traceback_html()
|
||||||
|
|
||||||
|
Return HTML version of exception report.
|
||||||
|
|
||||||
|
Used for HTML version of debug 500 HTTP error page.
|
||||||
|
|
||||||
|
.. method:: get_traceback_text()
|
||||||
|
|
||||||
|
Return plain text version of exception report.
|
||||||
|
|
||||||
|
Used for plain text version of debug 500 HTTP error page and email
|
||||||
|
reports.
|
||||||
|
|
||||||
|
As with the filter class, you may control which exception reporter class to use
|
||||||
|
within any given view by setting the ``HttpRequest``’s
|
||||||
|
``exception_reporter_class`` attribute::
|
||||||
|
|
||||||
|
def my_view(request):
|
||||||
|
if request.user.is_authenticated:
|
||||||
|
request.exception_reporter_class = CustomExceptionReporter()
|
||||||
|
...
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
You can also set up custom error reporting by writing a custom piece of
|
You can also set up custom error reporting by writing a custom piece of
|
||||||
|
|
|
@ -1250,6 +1250,19 @@ Default: ``'utf-8'``
|
||||||
Default charset to use for all ``HttpResponse`` objects, if a MIME type isn't
|
Default charset to use for all ``HttpResponse`` objects, if a MIME type isn't
|
||||||
manually specified. Used when constructing the ``Content-Type`` header.
|
manually specified. Used when constructing the ``Content-Type`` header.
|
||||||
|
|
||||||
|
.. setting:: DEFAULT_EXCEPTION_REPORTER
|
||||||
|
|
||||||
|
``DEFAULT_EXCEPTION_REPORTER``
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
|
Default: ``'``:class:`django.views.debug.ExceptionReporter`\ ``'``
|
||||||
|
|
||||||
|
Default exception reporter class to be used if none has been assigned to the
|
||||||
|
:class:`~django.http.HttpRequest` instance yet. See
|
||||||
|
:ref:`custom-error-reports`.
|
||||||
|
|
||||||
.. setting:: DEFAULT_EXCEPTION_REPORTER_FILTER
|
.. setting:: DEFAULT_EXCEPTION_REPORTER_FILTER
|
||||||
|
|
||||||
``DEFAULT_EXCEPTION_REPORTER_FILTER``
|
``DEFAULT_EXCEPTION_REPORTER_FILTER``
|
||||||
|
@ -3537,6 +3550,7 @@ Email
|
||||||
|
|
||||||
Error reporting
|
Error reporting
|
||||||
---------------
|
---------------
|
||||||
|
* :setting:`DEFAULT_EXCEPTION_REPORTER`
|
||||||
* :setting:`DEFAULT_EXCEPTION_REPORTER_FILTER`
|
* :setting:`DEFAULT_EXCEPTION_REPORTER_FILTER`
|
||||||
* :setting:`IGNORABLE_404_URLS`
|
* :setting:`IGNORABLE_404_URLS`
|
||||||
* :setting:`MANAGERS`
|
* :setting:`MANAGERS`
|
||||||
|
|
|
@ -173,6 +173,10 @@ Error Reporting
|
||||||
:setting:`DEFAULT_EXCEPTION_REPORTER_FILTER` when applying settings
|
:setting:`DEFAULT_EXCEPTION_REPORTER_FILTER` when applying settings
|
||||||
filtering.
|
filtering.
|
||||||
|
|
||||||
|
* The new :setting:`DEFAULT_EXCEPTION_REPORTER` allows providing a
|
||||||
|
:class:`django.views.debug.ExceptionReporter` subclass to customize exception
|
||||||
|
report generation. See :ref:`custom-error-reports` for details.
|
||||||
|
|
||||||
File Storage
|
File Storage
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -249,6 +249,15 @@ class DebugViewTests(SimpleTestCase):
|
||||||
response = self.client.get('/path-post/1/')
|
response = self.client.get('/path-post/1/')
|
||||||
self.assertContains(response, 'Page not found', status_code=404)
|
self.assertContains(response, 'Page not found', status_code=404)
|
||||||
|
|
||||||
|
def test_exception_reporter_from_request(self):
|
||||||
|
response = self.client.get('/custom_reporter_class_view/')
|
||||||
|
self.assertContains(response, 'custom traceback text', status_code=500)
|
||||||
|
|
||||||
|
@override_settings(DEFAULT_EXCEPTION_REPORTER='view_tests.views.CustomExceptionReporter')
|
||||||
|
def test_exception_reporter_from_settings(self):
|
||||||
|
response = self.client.get('/raises500/')
|
||||||
|
self.assertContains(response, 'custom traceback text', status_code=500)
|
||||||
|
|
||||||
|
|
||||||
class DebugViewQueriesAllowedTests(SimpleTestCase):
|
class DebugViewQueriesAllowedTests(SimpleTestCase):
|
||||||
# May need a query to initialize MySQL connection
|
# May need a query to initialize MySQL connection
|
||||||
|
|
|
@ -26,6 +26,7 @@ urlpatterns = [
|
||||||
path('raises403/', views.raises403),
|
path('raises403/', views.raises403),
|
||||||
path('raises404/', views.raises404),
|
path('raises404/', views.raises404),
|
||||||
path('raises500/', views.raises500),
|
path('raises500/', views.raises500),
|
||||||
|
path('custom_reporter_class_view/', views.custom_reporter_class_view),
|
||||||
|
|
||||||
path('technical404/', views.technical404, name='my404'),
|
path('technical404/', views.technical404, name='my404'),
|
||||||
path('classbased404/', views.Http404View.as_view()),
|
path('classbased404/', views.Http404View.as_view()),
|
||||||
|
|
|
@ -10,7 +10,7 @@ from django.template import TemplateDoesNotExist
|
||||||
from django.urls import get_resolver
|
from django.urls import get_resolver
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from django.views.debug import (
|
from django.views.debug import (
|
||||||
SafeExceptionReporterFilter, technical_500_response,
|
ExceptionReporter, SafeExceptionReporterFilter, technical_500_response,
|
||||||
)
|
)
|
||||||
from django.views.decorators.debug import (
|
from django.views.decorators.debug import (
|
||||||
sensitive_post_parameters, sensitive_variables,
|
sensitive_post_parameters, sensitive_variables,
|
||||||
|
@ -227,6 +227,22 @@ def custom_exception_reporter_filter_view(request):
|
||||||
return technical_500_response(request, *exc_info)
|
return technical_500_response(request, *exc_info)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomExceptionReporter(ExceptionReporter):
|
||||||
|
custom_traceback_text = 'custom traceback text'
|
||||||
|
|
||||||
|
def get_traceback_html(self):
|
||||||
|
return self.custom_traceback_text
|
||||||
|
|
||||||
|
|
||||||
|
def custom_reporter_class_view(request):
|
||||||
|
request.exception_reporter_class = CustomExceptionReporter
|
||||||
|
try:
|
||||||
|
raise Exception
|
||||||
|
except Exception:
|
||||||
|
exc_info = sys.exc_info()
|
||||||
|
return technical_500_response(request, *exc_info)
|
||||||
|
|
||||||
|
|
||||||
class Klass:
|
class Klass:
|
||||||
|
|
||||||
@sensitive_variables('sauce')
|
@sensitive_variables('sauce')
|
||||||
|
|
Loading…
Reference in New Issue