Django 调试视图,用于管理员的生产环境

Posted

技术标签:

【中文标题】Django 调试视图,用于管理员的生产环境【英文标题】:Django Debug View, in Production for Admins 【发布时间】:2021-03-30 02:06:42 【问题描述】:

我知道settings.DEBUG 在生产中应该是False

不过,如果当前用户被认证为管理员,我想在生产中展示出色的 django 调试视图。

我阅读了Error Reporting Docs,但找不到将其打开的设置。

如果启用不同的调试视图更容易,那就太好了。至少我希望看到一个不错的堆栈跟踪,以便快速了解错误的来源(无需查看日志)。

【问题讨论】:

【参考方案1】:

我宁愿选择像Sentry 这样的工具来跟踪生产环境中的错误。

Sentry 是一个应用监控平台,可以帮助每个开发者 诊断、修复和优化代码的性能。

Sentry 也支持Django monitoring-(doc)。此配置会将错误发送到 Sentry 仪表板,我们也将能够看到堆栈跟踪。

此外,这将跟踪整个应用程序,不仅针对视图,不仅针对超级用户等


恕我直言,更改 settings.DEBUG 是个坏主意,您迟早会因此面临许多问题。

【讨论】:

感谢您的提示。这是一个很好的答案,但我认为它与上述问题不匹配。【参考方案2】:

您可以在根 URLConf 中添加自定义错误处理视图,请参阅Django docs 了解更多信息。 500 错误的默认视图是 django.views.defaults.server_error,如果用户不是管理员,您可以返回它,否则返回 django.views.debug.technical_500_response。但是,请注意 django.views.debug.technical_500_response 并未在 Django 文档中公开记录,因此在未来的版本中可能会(或可能不会)更改。

所以,最后你的代码可能是这样的:

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)

请注意,DEBUG=True 时根本不会调用此处理程序

【讨论】:

【参考方案3】:

我也觉得有必要偶尔详细检查一下生产环境,并使用一些简单的策略:

(1) 您可能知道,您可以在项目设置中添加一个 ADMINS 列表,以通过电子邮件接收通常在开发中显示的“黄色调试屏幕”的副本;见:

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

(2)为了在生产中做更多的检查,我一般会安装django-debug-toolbar,配置如下:

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',
    ...

工具栏通常是隐藏的,除非我启用 DEBUG_SHOW_TOOLBAR 标志(我使用 django-constance 进行动态设置);将 DEBUG_SHOW_TOOLBAR 设置为 True,调试面板将显示给主管。

(3) 最后,我总是在我的项目中包含这个 sn-p:

文件'main/settings/debug.py'

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

(作为“main/setting/local.py”我的默认设置)

作为最后的手段,我可​​以快速临时通过 SSH 运行项目的调试实例,如下所示:

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

然后在端口 8000 上以 DEBUG 模式访问该站点

【讨论】:

【参考方案4】:

您可以使用 Django 的 @override_settings 装饰器动态更改 settings.DEBUG 值,例如:

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

由于您希望仅为管理员启用调试,您只需实现一个方法来计算是否应为当前用户启用调试,例如 @override_settings(DEBUG=cur_user_is_admin())


另一种方法是使用自定义 500 处理程序和内置 django 500 错误处理程序自己制作调试消息。 Please see this answer 了解更多信息。

【讨论】:

如果生产系统出现问题,我想要漂亮的调试视图。我只在 dev 和 ci 期间运行测试。抱歉,这不能回答问题。【参考方案5】:

为什么这是个坏主意

首先,我 100% 同意 @JPG 的回答,即这几乎可以肯定是一个坏主意(出于一些实用/调试/安全原因):

假设您的自定义 404 或 500 页面存在问题,并且您的测试没有发现它。您在生产中看到与客户不同的错误页面这一事实意味着在您的一位客户投诉之前您不会注意到此错误!! 如果恶意用户能够以管理员身份登录,那已经很糟糕了。但事实上他们会看到所有这些调试信息,这只会使非常糟糕的情况变得更糟一百倍。 同样,它会使其他潜在的攻击更加致命。想象一下,有人设置了一个与您类似的站点,并使用您的管理员用户(登录)凭据向您的服务器发送请求。希望您已经正确设置了 django 以防止这种情况发生。但是,如果这是可能的,您会发现这将是一次糟糕的攻击,更糟糕的是(因为现在他们免费获得了您的所有调试信息)。 我不会说所有自定义异常中间件都是一个坏主意,但它确实会引入异常处理程序本身出错的风险。这会使调试变得更加困难,因为它可以掩盖原始错误。所以异常中间件应该以简单为目标,而不是实现异常行为(我认为这属于这一类)。 一般来说,打开/关闭调试时可能会引发不同的错误。当您是管理员/不是管理员时,您也可能会遇到不同的错误。从调试的角度来看,您提出的解决方案可能会导致一种“admin 等同于debug == True”的思维方式。这只会很糟糕。 正如@Melvyn 在 cmets 中指出的那样,需要调试的人群和应该拥有管理员权限的人群不一定是不同的。随着时间的推移,为了便于调试,越来越多的人将获得管理员权限,而这本身就会成为一个安全问题。

我也会推荐 sentry(或类似于 sentry 的工具):

它非常容易与 django 集成,并且有非常清晰的文档说明如何做到这一点。 一个非常有用的功能是您可以在自定义 500 页面上显示错误编号,然后您的客户可以向您报告。您只需在 sentry 上查找它,您就可以看到完整的回溯、每个阶段的局部变量以及您的所有请求详细信息。调试非常棒。 您还可以使用它来获取有关前端/javascript 错误的报告(并在一处处理所有错误!!) 还有一个免费的付款计划可以帮助您入门。 (我和哨兵没有任何关系,我以前用过,感觉非常棒!!)

django 错误是如何工作的

说了这么多,我还是要说怎么做,只是因为它很有趣。您首先需要了解 django 在处理错误时所做的工作。

django 用一个函数来装饰每个中间件的get_reponse,在出现异常时调用response_for_exception。 以上将捕获中间件正向的任何错误 如果视图本身引发异常,中间件的任何 process_exception 视图都将以相反的顺序调用(直到返回响应 - 不会再调用 process_exceptions) 中间件以相反的顺序继续(记住每个仍然用response_for_exception 装饰)。 response_for_exception 是动作发生的地方。 Django: 调用您的自定义 400/500 视图(如果已设置) 如果调试已打开,则返回错误详细信息视图 否则应用默认(非调试)错误视图

在继续之前,请先查看response_for_exception。当调试打开/关闭时,您将看到哪些错误的处理方式不同。

如果你真的想这样做的解决方案

您将看到一些错误具有在调试打开时调用的特定视图。您可以实现自定义 400、404 和 500 视图来模拟该行为。

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)

【讨论】:

还有最被低估的安全问题:您需要管理员权限才能使用生产数据进行调试。这简直就是隐私的噩梦,随着时间的推移,越来越多的开发人员将“暂时”获得管理员权限,以便他们修复错误。 @Melvyn 是的,一个很好的观点。我会把它添加到列表中。

以上是关于Django 调试视图,用于管理员的生产环境的主要内容,如果未能解决你的问题,请参考以下文章

Django uwsgi Nginx 的生产环境部署

Django:Whitenoise 在调试错误的情况下无法在生产中工作

Django 管理员突然只在生产环境中注销(拒绝执行脚本,因为它的 MIME 类型('text/html')不可执行)

Django基本配置

64. Django 2 生产环境部署 uwsgi nginx

当我单击生产环境中的任何保存按钮时,在 django 的管理员中找不到带有 POST 请求的页面