1

I know that settings.DEBUG should be False in production.

Nevertheless I would like to show the great django debug view in production, if the current user is authenticated as an admin.

I read the Error Reporting Docs, but could not find a setting to turn this on.

If it is easier to enable a different debug-view, this would be great, too. At least I want to see a nice stacktrace to understand quickly where the error comes from (without looking at logs).

guettli
  • 25,042
  • 81
  • 346
  • 663

5 Answers5

3

I would rather choose a tool like Sentry to track the errors in a production environment.

Sentry is an application monitoring platform helps every developer diagnose, fix, and optimize the performance of their code.

Sentry supports the Django monitoring-(doc) as well. This configuration will send the errors to the Sentry dashboard and we will be able to see the stack trace as well.

Moreover, this will keep track of entire application, not only for the views, not only for superusers, etc


IMHO, altering the settings.DEBUG is a bad idea, sooner or later you will probably face many issues because of that.

JPG
  • 82,442
  • 19
  • 127
  • 206
2

You may add custom error handling view in your root URLConf, see Django docs for more. The default view for 500 errors is django.views.defaults.server_error and you may return it if user is not admin or return django.views.debug.technical_500_response otherwise. But, please pay attention that django.views.debug.technical_500_response is not publicly documented on Django docs, and thus may be (or may not ) changed in the future releases.

So, finally your code may look like this:

your_project_name/urls.py

urlpatterns = [
    ...
]

from django.views.debug import technical_500_response
from django.views.defaults import server_error

def handler500(request):
    if request.user.is_superuser:
        return technical_500_response(request, *sys.exc_info())
    else:
        return server_error(request)

Please, be noted that this handler is not called at all when DEBUG=True

datosula
  • 1,496
  • 5
  • 10
2

I too feel the need to occasionally inspect the production environment in details, and use a few simple strategies:

(1) As you probably know, you can add an ADMINS list to the project's settings, to receive via email a copy of the "yellow debug screen" which is normally shown in development; see:

https://docs.djangoproject.com/en/3.1/ref/settings/#admins

(2) For greater inspection in production, I normally install django-debug-toolbar and configure it as follows:

def show_toolbar(request):
    from constance import config
    try:
        if not config.DEBUG_SHOW_TOOLBAR:
            return False
    except:
        return False
    return request.user.is_superuser


DEBUG_TOOLBAR_PATCH_SETTINGS = False
INTERNAL_IPS = ('127.0.0.1', )
DEBUG_TOOLBAR_CONFIG = {
    'SHOW_TOOLBAR_CALLBACK': 'main.settings.settings.show_toolbar',
}
DEBUG_TOOLBAR_PANELS = [
    'debug_toolbar.panels.versions.VersionsPanel',
    ...

The toolbar is normally hidden, unless I enable a DEBUG_SHOW_TOOLBAR flag (I'm using django-constance for dynamic settings); with DEBUG_SHOW_TOOLBAR set to True, the debug panels are shown to the supervisors.

(3) Finally, I always include this snippet in my projects:

file 'main/settings/debug.py'

from main.settings.local import *
DEBUG = True
ALLOWED_HOSTS = ['*', ]

(being "main/setting/local.py" my default setting)

so as a last resort I can quickly and temporary run a debug instance of the project via SSH as follows:

python manage.py runserver --settings=main.settings.debug 0.0.0.0:8000

then visit the site in DEBUG mode on port 8000

Mario Orlandi
  • 5,629
  • 26
  • 29
1

You can dynamically change the settings.DEBUG value with Django's @override_settings decorator, for example:

from django.test.utils import override_settings
from django.conf import settings
class TestSomething(TestCase):
    @override_settings(DEBUG=True)
    def test_debug(self):
        assert settings.DEBUG

As you would like debug to be enabled only for admins, you can just implement a method which will calculate if the debug should be enabled for current user, something like @override_settings(DEBUG=cur_user_is_admin())


Another way is to craft debug messages by yourself using custom 500 handler and built-in django 500 error handler. Please see this answer for more information.

Dmitry S.
  • 43
  • 1
  • 10
  • I want the nice debug view if something is wrong in the production system. I run tests only on dev and during ci. Sorry, this does not answer the question. – guettli Dec 26 '20 at 19:03
1

Why this is a bad idea

First off, I 100% agree with @JPG's answer that this is almost certainly a bad idea (for a few practicalty/debugging/security reasons):

  • Suppose there is a problem with your custom 404 or 500 pages, and somehow it's not picked up by your tests. The fact that you are seeing different error pages to your customers on production will mean you won't notice this error until one of your customers complains!!
  • If a malicious user was able to login as an admin, that's already pretty bad. But the fact they would see all this debug info, would just make a very bad situation somehow a hundred times worse.
  • Likewise, it would make other potential attacks, more deadly. Imagine someone sets up a site similar to yours, and uses your admin users (logged in) credentials to send requests to your server. Hopefully you will have set up django correctly to prevent this. But if somehow this was possible, you can see it would make a bad attack, alot worse (because now they get all your debug info for free).
  • I wouldn't say all custom exception middleware is a bad idea, but it does introduce the risk of an error with the exception handler itself. This can make debugging more difficult, as it can obfiscate the original error. So exception middleware should aim to be simple, and not implement unusual behaviour (I think this falls into that category).
  • In general, different errors are potentially raised when debug is on/off. You can also get different errors when you are an admin/not an admin. Your proposed solution will potentially lead to a way of thinking that "admin is equivalent to debug == True" from a debugging perspective. This can only be bad.
  • As @Melvyn pointed out in the comments, the group of people who will need to debug, and the group of people who should have admin rights are not necessarily distinct. Over time, more people will get admin rights than aught to, in order to facilitate debugging, and this will become a security issue in its own right.

I would also recommend sentry (or a tool similar to sentry):

  • It's super easy to integrate with django, and has very clear docs as to how you can do this.
  • A really useful feature is that you can display an error number on your custom 500 page, and that way your customers can then report back to you. You simply look it up on sentry and you can see the full traceback, local variables at each stage, all your request details. It's amazing for debugging.
  • You can also use it to get reports on your frontend/javascript errors too (and deal with all your errors in one place!!)
  • There is also a free payment plan to get you started. (I have no affiliation with sentry, I've just used in the past and found it to be really excellent!!)

How django erros work

Having said all of the above, I'm still going to say how to do this, just because it's quite interesting. You'll first need to understand what django does under the cover to handle errors.

  • django decorates the get_reponse of each middleware with a function that calls response_for_exception in case of an exception.
  • The above will catch any errors in the forward direction of the middleware
  • If the view itself raises an exception, any process_exception views of the middleware will be called in reverse order (until one returns a response - no further process_exceptions will be called)
  • The middlware continues in reverse order (remember each one still decorated with response_for_exception).
  • response_for_exception is where the action happens. Django either:
    • calls your custom 400/500 views if set
    • if debug is on returns error detail views
    • otherwise applies default (non-debug) error views

Before going any further take a look at response_for_exception. You will see which errors are handled differently when debug is on/off.

A solution if you really want to do this

You will see that some errors have particular views that are called when debug is on. You can implement custom 400, 404 and 500 views to emulate that behaviour.

from django.views import debug, defaults

def handler400(request):
    if request.user.is_superuser:
        # Be sure to apply the correct code:
        return debug.technical_500_response(request, *sys.exc_info(), status_code=400)
    else:
        # You should probably have your own 400 page here
        # but otherwise:
        return defaults.bad_request(request)

def handler404(request):
    if request.user.is_superuser:
        return debug.technical_404_response(request, exc)
    else:
        # Likewise, you should probably have a pretty 404 page here
        # but otherwise:
        return defaults.page_not_found(request)

def handler500(request):
    if request.user.is_superuser:
        return debug.technical_500_response(request, *sys.exc_info())
    else:
        # Likewise, you should probably have a pretty 500 page here
        # but otherwise:
        return defaults.server_error(request)
tim-mccurrach
  • 6,395
  • 4
  • 23
  • 41
  • And the most underestimated security issue: you need admin privileges to be able to debug things with production data. That's quite the privacy nightmare and over time, more and more developers will "temporarily" gain admin priviliges so they can fix their bugs. –  Dec 25 '20 at 07:09
  • @Melvyn Yes, a very good point. I'll add it to the list. – tim-mccurrach Dec 26 '20 at 11:58