3

In order to log "some dependency, somewhere deep" errors that trigger a server error 500, without stack traces in the console log on production instances due to DEBUG=False, I implemented the standard custom 500 handler that a fair number of Stackoverflow questions about printing a stack trace for a 500 error recommend:

import sys
import traceback

def server_error_500_handler(request):
    type, value, tb = sys.exc_info()
    print('\n----intercepted 500 error stack trace----')
    print(value)
    print(type)
    print(traceback.format_exception(type, value, tb))
    print('----\n')

However, these then also all say to end with render(request, '500.html'), whereas I don't want to serve a custom 500 page, but want the code to go "back" (if there is such a thing) to just serving whatever Django itself does already. Is there some way to make it do that? Or, is there some way to listen for the 500 event without hijacking the 500 error return codepath?

Mike 'Pomax' Kamermans
  • 49,297
  • 16
  • 112
  • 153

1 Answers1

4

Instead of making a custom 500 handler, make a custom middleware of your own and implement a process_exception method in it:

import traceback


class Log500ErrorsMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        return response
    
    def process_exception(self, request, exception):
        print('\n----intercepted 500 error stack trace----')
        print(exception)
        print(type(exception))
        tb = exception.__traceback__
        print(traceback.format_exception(type(exception), exception, tb))
        print('----\n')
        return None # Let other middlewares do further processing

Then add it to the MIDDLEWARE setting, all the way at the end, since the middlewares are run in bottom-up order during the response/exception phase, so if you put it last it will always get run (some middleware can decide to short circuit and return a response otherwise, so having anything after it might prevent it from running).

MIDDLEWARE = [
    ...
    'path.to.Log500ErrorsMiddleware',
]
Mike 'Pomax' Kamermans
  • 49,297
  • 16
  • 112
  • 153
Abdul Aziz Barkat
  • 19,475
  • 3
  • 20
  • 33
  • 1
    Does it matter where in the middleware list that gets put? (e.g. is there any specific Django middleware that it _has_ to go after?) – Mike 'Pomax' Kamermans Jun 09 '21 at 16:47
  • 1
    @Mike'Pomax'Kamermans put it _last_ like I show in the example (Notice the lack of trailing `...`) since the middlewares are run in bottom up manner during the response / exception phase so if you put it last it will always get run (Some middleware can decide to short circuit and return a response otherwise) – Abdul Aziz Barkat Jun 09 '21 at 16:49