From c8a73b5b15a796b2d5b30190877dd2f9e605299f Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 23 Jul 2018 23:00:09 -0400 Subject: [PATCH] Fixes #2266: Permit additional logging of exceptions beyond custom middleware --- netbox/netbox/urls.py | 2 ++ netbox/utilities/middleware.py | 29 +++++++++++++++-------------- netbox/utilities/views.py | 24 +++++++++++++++++++++++- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/netbox/netbox/urls.py b/netbox/netbox/urls.py index 4907555ed..3a2a7205f 100644 --- a/netbox/netbox/urls.py +++ b/netbox/netbox/urls.py @@ -74,3 +74,5 @@ if settings.DEBUG: urlpatterns = [ url(r'^{}'.format(settings.BASE_PATH), include(_patterns)) ] + +handler500 = 'utilities.views.server_error' diff --git a/netbox/utilities/middleware.py b/netbox/utilities/middleware.py index 64fb70a07..a613d091a 100644 --- a/netbox/utilities/middleware.py +++ b/netbox/utilities/middleware.py @@ -4,8 +4,8 @@ import sys from django.conf import settings 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 BASE_PATH = getattr(settings, 'BASE_PATH', False) @@ -65,23 +65,24 @@ class ExceptionHandlingMiddleware(object): if isinstance(exception, Http404): 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): - template_name = 'exceptions/programming_error.html' + custom_template = 'exceptions/programming_error.html' elif isinstance(exception, ImportError): - template_name = 'exceptions/import_error.html' + custom_template = 'exceptions/import_error.html' elif ( sys.version_info[0] >= 3 and isinstance(exception, PermissionError) ) or ( isinstance(exception, OSError) and exception.errno == 13 ): - template_name = 'exceptions/permission_error.html' - else: - template_name = '500.html' + custom_template = 'exceptions/permission_error.html' - # Return an error message - type_, error, traceback = sys.exc_info() - return render(request, template_name, { - 'exception': str(type_), - 'error': error, - }, status=500) + # 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, + })) diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index 94b44fc48..dcb4529b1 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals from collections import OrderedDict from copy import deepcopy +import sys from django.conf import settings from django.contrib import messages @@ -10,12 +11,16 @@ from django.core.exceptions import ValidationError from django.db import transaction, IntegrityError from django.db.models import ProtectedError 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.template.exceptions import TemplateSyntaxError +from django.template import loader +from django.template.exceptions import TemplateDoesNotExist, TemplateSyntaxError from django.urls import reverse from django.utils.html import escape from django.utils.http import is_safe_url 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_tables2 import RequestConfig @@ -858,3 +863,20 @@ class BulkComponentCreateView(View): 'table': table, '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('

Server Error (500)

', content_type='text/html') + type_, error, traceback = sys.exc_info() + + return HttpResponseServerError(template.render({ + 'exception': str(type_), + 'error': error, + }))