Django has an awesome debug page that shows up whenever there's an exception in the code. That page shows all levels in the stack compactly, and you can expand any level you're interested in. It shows only in debug mode.
Django also has an awesome feature for sending email reports when errors are encountered on the production server by actual users. These reports have a stacktrace with much less information than the styled debug page.
There's an awesome optional setting 'include_html': True
that makes the email include all the information of the debug page, which is really useful. The problem with this setting is that the HTML comes apparently unstyled, so all those levels of the stack are expanded to show all the data they contain.
This results in such a long email that GMail usually can't even display it without sending you to a dedicated view. But the real problem is that it's too big to navigate in and find the stack level you want.
What I want: I want Django to send that detailed stacktrace, but I want the levels of the stack to be collapsable just like in the debug page. How can I do that?
(And no, I don't want to use Sentry.)
We use a middleware to process any exceptions that happen in production. The basic idea is save the debug html to a location on the file system that can be served via a password protected view and then send a link to the generated view to whoever needs it.
Here is a basic implementation:
from django.conf import settings
from django.core.mail import send_mail
from django.views import debug
import traceback
import hashlib
import sys
class ExceptionMiddleware(object):
def process_exception(self, request, exception):
if isinstance(exception, Http404):
return
traceback = traceback.format_exc()
traceback_hash = hashlib.md5(traceback).hexdigest()
traceback_name = '%s.html' % traceback_hash
traceback_path = os.path.join(settings.EXCEPTIONS_DIR, traceback_name)
reporter = debug.ExceptionReporter(request, *sys.exc_info())
with open(traceback_path, 'w') as f:
f.write(reporter.get_traceback_html().encode("utf-8"))
send_mail(
'Error at %s' % request.path,
request.build_absolute_uri(reverse('exception', args=(traceback_name, ))),
FROM_EMAIL,
TO_EMAIL,
)
And the view
from django.conf import settings
from django.http import HttpResponse
def exception(request, traceback_name):
traceback_path = os.path.join(settings.EXCEPTIONS_DIR, traceback_name)
with open(traceback_path, 'r') as f:
response = HttpResponse(f.read())
return response
The full middleware is tailored to our needs but the basics are there. You should probably password protect the view somehow. Unless you return a response Django's own error handling will kick in and still send you an email but I'll leave that to you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With