Просмотр исходного кода

Fixes #2266: Permit additional logging of exceptions beyond custom middleware

Jeremy Stretch 7 лет назад
Родитель
Сommit
c8a73b5b15
3 измененных файлов с 41 добавлено и 16 удалено
  1. 2 0
      netbox/netbox/urls.py
  2. 16 15
      netbox/utilities/middleware.py
  3. 23 1
      netbox/utilities/views.py

+ 2 - 0
netbox/netbox/urls.py

@@ -74,3 +74,5 @@ if settings.DEBUG:
 urlpatterns = [
 urlpatterns = [
     url(r'^{}'.format(settings.BASE_PATH), include(_patterns))
     url(r'^{}'.format(settings.BASE_PATH), include(_patterns))
 ]
 ]
+
+handler500 = 'utilities.views.server_error'

+ 16 - 15
netbox/utilities/middleware.py

@@ -4,8 +4,8 @@ import sys
 
 
 from django.conf import settings
 from django.conf import settings
 from django.db import ProgrammingError
 from django.db import ProgrammingError
-from django.http import Http404, HttpResponseRedirect
-from django.shortcuts import render
+from django.http import Http404, HttpResponseRedirect, HttpResponseServerError
+from django.template import loader
 from django.urls import reverse
 from django.urls import reverse
 
 
 BASE_PATH = getattr(settings, 'BASE_PATH', False)
 BASE_PATH = getattr(settings, 'BASE_PATH', False)
@@ -65,23 +65,24 @@ class ExceptionHandlingMiddleware(object):
         if isinstance(exception, Http404):
         if isinstance(exception, Http404):
             return
             return
 
 
-        # Determine the type of exception
+        # Determine the type of exception. If it's a common issue, return a custom error page with instructions.
+        custom_template = None
         if isinstance(exception, ProgrammingError):
         if isinstance(exception, ProgrammingError):
-            template_name = 'exceptions/programming_error.html'
+            custom_template = 'exceptions/programming_error.html'
         elif isinstance(exception, ImportError):
         elif isinstance(exception, ImportError):
-            template_name = 'exceptions/import_error.html'
+            custom_template = 'exceptions/import_error.html'
         elif (
         elif (
             sys.version_info[0] >= 3 and isinstance(exception, PermissionError)
             sys.version_info[0] >= 3 and isinstance(exception, PermissionError)
         ) or (
         ) or (
             isinstance(exception, OSError) and exception.errno == 13
             isinstance(exception, OSError) and exception.errno == 13
         ):
         ):
-            template_name = 'exceptions/permission_error.html'
-        else:
-            template_name = '500.html'
-
-        # Return an error message
-        type_, error, traceback = sys.exc_info()
-        return render(request, template_name, {
-            'exception': str(type_),
-            'error': error,
-        }, status=500)
+            custom_template = 'exceptions/permission_error.html'
+
+        # Return a custom error message, or fall back to Django's default 500 error handling (500.html)
+        if custom_template:
+            type_, error, traceback = sys.exc_info()
+            template = loader.get_template(custom_template)
+            return HttpResponseServerError(template.render({
+                'exception': str(type_),
+                'error': error,
+            }))

+ 23 - 1
netbox/utilities/views.py

@@ -2,6 +2,7 @@ from __future__ import unicode_literals
 
 
 from collections import OrderedDict
 from collections import OrderedDict
 from copy import deepcopy
 from copy import deepcopy
+import sys
 
 
 from django.conf import settings
 from django.conf import settings
 from django.contrib import messages
 from django.contrib import messages
@@ -10,12 +11,16 @@ from django.core.exceptions import ValidationError
 from django.db import transaction, IntegrityError
 from django.db import transaction, IntegrityError
 from django.db.models import ProtectedError
 from django.db.models import ProtectedError
 from django.forms import CharField, Form, ModelMultipleChoiceField, MultipleHiddenInput, Textarea
 from django.forms import CharField, Form, ModelMultipleChoiceField, MultipleHiddenInput, Textarea
+from django.http import HttpResponseServerError
 from django.shortcuts import get_object_or_404, redirect, render
 from django.shortcuts import get_object_or_404, redirect, render
-from django.template.exceptions import TemplateSyntaxError
+from django.template import loader
+from django.template.exceptions import TemplateDoesNotExist, TemplateSyntaxError
 from django.urls import reverse
 from django.urls import reverse
 from django.utils.html import escape
 from django.utils.html import escape
 from django.utils.http import is_safe_url
 from django.utils.http import is_safe_url
 from django.utils.safestring import mark_safe
 from django.utils.safestring import mark_safe
+from django.views.decorators.csrf import requires_csrf_token
+from django.views.defaults import ERROR_500_TEMPLATE_NAME
 from django.views.generic import View
 from django.views.generic import View
 from django_tables2 import RequestConfig
 from django_tables2 import RequestConfig
 
 
@@ -858,3 +863,20 @@ class BulkComponentCreateView(View):
             'table': table,
             'table': table,
             'return_url': reverse(self.default_return_url),
             'return_url': reverse(self.default_return_url),
         })
         })
+
+
+@requires_csrf_token
+def server_error(request, template_name=ERROR_500_TEMPLATE_NAME):
+    """
+    Custom 500 handler to provide additional context when rendering 500.html.
+    """
+    try:
+        template = loader.get_template(template_name)
+    except TemplateDoesNotExist:
+        return HttpResponseServerError('<h1>Server Error (500)</h1>', content_type='text/html')
+    type_, error, traceback = sys.exc_info()
+
+    return HttpResponseServerError(template.render({
+        'exception': str(type_),
+        'error': error,
+    }))