How to manage error reporting

当您运行着公开的网页时,你应该把设置中的 DEBUG 选项设为关闭。 这将使您的服务器运行得更快,并且还可以防止恶意用户在错误页面中看到应用的详细信息。

然而,把配置 DEBUG 设置为 False, 这意味着你永远不会在你的网站上看到错误的细节,所有的人相应会看到你公开的错误页面。当你需要在部署的网页上追踪出现的错误,Django 可以通过设置去报告错误的细节。

通过邮件方式报告

服务器错误

DEBUGFalse 时,Django 会在代码抛出的异常未被捕获且导致一个内部的服务器错误(HTTP 状态码 500)时向 ADMINS 配置中列出的用户发送邮件。这让管理员立刻收到错误通知。 ADMINS 列表中的用户,会收到详细的错误描述,完整的 Python 调用栈,和引发此错误的 HTTP 请求的细节。

注解

要发送邮件,Django 要求一些配置项,告诉它如何连接至邮件服务器。最小配置下,你需要指定 EMAIL_HOSTEMAIL_HOST_USEREMAIL_HOST_PASSWORD,可能还有其它配置,这取决于邮件服务器的配置。查阅 Django 配置文档 获取邮件相关配置的完整列表。

默认情况下,Django 会从 root@localhost 发送邮件。然而,某些邮件服务商拒绝来自该地址的所有邮件。要使用不同的发件人地址,修改 SERVER_EMAIL 配置。

要启动该行为,将收件人的邮箱地址放入 ADMINS

参见

服务器错误邮件由 logging 框架发出,所以你能通过 自定义 logging 配置 自定义该行为。

404 错误

Django 也能配置成为已失效的连接发送错误邮件(404 "page not found" 错误)。Django 发送 404 错误邮件的条件:

If those conditions are met, Django will email the users listed in the MANAGERS setting whenever your code raises a 404 and the request has a referer. It doesn't bother to email for 404s that don't have a referer -- those are usually people typing in broken URLs or broken web bots. It also ignores 404s when the referer is equal to the requested URL, since this behavior is from broken web bots too.

注解

BrokenLinkEmailsMiddleware must appear before other middleware that intercepts 404 errors, such as LocaleMiddleware or FlatpageFallbackMiddleware. Put it toward the top of your MIDDLEWARE setting.

你可以通过调整配置项 IGNORABLE_404_URLS 让 Django 忽略某些 404s。内容应该是已编译的正则表达式的列表。例子:

import re
IGNORABLE_404_URLS = [
    re.compile(r'\.(php|cgi)$'),
    re.compile(r'^/phpmyadmin/'),
]

在本例中,对以 .php.cgi 结尾的 URL 的 404 错误将 不会 被报告。以 phpmyadmin 开头的也不会被报告。

下面的例子展示了如何排除一些浏览器和爬虫经常访问的传统 URL:

import re
IGNORABLE_404_URLS = [
    re.compile(r'^/apple-touch-icon.*\.png$'),
    re.compile(r'^/favicon\.ico$'),
    re.compile(r'^/robots\.txt$'),
]

(注意,这些都是正则表达式,所以我们用反斜杠转义了点号。)

若后续你想自定义 django.middleware.common.BrokenLinkEmailsMiddleware 的行为(例如忽略来自网络爬虫的请求),你需要继承该类,并重写方法。

参见

404 错误由 logging 框架报告。默认情况下,这些日志记录被忽略了,但你能通过编写一个处理器和 配置 logging 让它们报告错误。

过滤错误报告

警告

Filtering sensitive data is a hard problem, and it's nearly impossible to guarantee that sensitive data won't leak into an error report. Therefore, error reports should only be available to trusted team members and you should avoid transmitting error reports unencrypted over the internet (such as through email).

过滤敏感数据

错误报告对于调试错误来说超级有用,所以通常尽可能多的记录关键信息。例如,Django 默认记录了抛出异常的 完整调用栈,每个 调用栈层 的局部变量,以及 HttpRequest属性

然而,有时候某些信息会涉及敏感内容,因此不适合写入调用栈。例如,用户的密码或信用卡号。故此,为了过滤 DEBUG 文档中描述的敏感内容,Django 提供了一系列的函数装饰器,帮你控制在生产环境(即 DEBUGFalse)下应该从错误报告中过滤掉哪些信息:sensitive_variables()sensitive_post_parameters()

sensitive_variables(*variables)

若代码中的一个函数(也可能是视图或定期回调)使用容易包含敏感信息的本地变量,你可以用 sensitive_variables 装饰器阻止错误报告包含这些变量的值:

from django.views.decorators.debug import sensitive_variables

@sensitive_variables('user', 'pw', 'cc')
def process_info(user):
    pw = user.pass_word
    cc = user.credit_card_number
    name = user.name
    ...

上述例子中,错误报告中变量 userpwcc 的值会被以星开头的字符串(**********)替换 ,不过 name 变量的值不会被屏蔽。

要在错误日志中系统的隐藏所有的局部变量,不要向 sensitive_variables 装饰器传入任何参数:

@sensitive_variables()
def my_function():
    ...

同时使用多个装饰器时

若你想隐藏的变量还是一个函数参数(下例中 'user’),且被装饰的函数已拥有好几个装饰器,确保将 @sensitive_variables 置于最上面。这样,它就也能隐藏函数参数了,因为它穿过其它装饰器:

@sensitive_variables('user', 'pw', 'cc')
@some_decorator
@another_decorator
def process_info(user):
    ...
sensitive_post_parameters(*parameters)

若视图收到一个包含 POST 参数HttpRequest 对象,参数中极有可能内含敏感信息。你可以通过 sensitive_post_parameters 装饰器避免这些参数的值被包括在错误报告中:

from django.views.decorators.debug import sensitive_post_parameters

@sensitive_post_parameters('pass_word', 'credit_card_number')
def record_user_profile(request):
    UserProfile.create(
        user=request.user,
        password=request.POST['pass_word'],
        credit_card=request.POST['credit_card_number'],
        name=request.POST['name'],
    )
    ...

上文例子中,POST 参数 pass_wordcredit_card_number 的值会被隐藏,通过在错误报告中展示请求信息的地方用星号开头的字符串 (**********)替换,而参数 name 的值不会被屏蔽。

要在错误报告中系统的屏蔽所有 POST 参数,不要给 sensitive_post_parameters 装饰器提供任何参数:

@sensitive_post_parameters()
def my_view(request):
    ...

为了避免敏感信息的泄露(例如账号密码), 所有传入认证视图 django.contrib.auth.viewsloginpassword_reset_confirmpassword_change, and add_viewauth 后台中的 user_change_password)的 POST 参数都在错误报告中经过系统过滤。

自定义错误报告

sensitive_variables()sensitive_post_parameters() 所做的全部工作是,前者用敏感变量名注解被装饰的函数,后者用 POST 参数中的敏感变量名注解 HttpRequest 对象,所以敏感信息能在后续错误发生时从报告中过滤出去。这项过滤操作者实际上是由 Django 的默认错误报告处理器类: django.views.debug.SafeExceptionReporterFilter。该过滤器在错误报告生成时,利用装饰器注解以星号 (**********) 替换指定值。若你想在全网页范围重写或自定义该默认行为,你需要定义你的过滤器类,并通过配置项 DEFAULT_EXCEPTION_REPORTER_FILTER 配置告诉 Django 启用它:

DEFAULT_EXCEPTION_REPORTER_FILTER = 'path.to.your.CustomExceptionReporterFilter'

你也能在任意视图内以一种更细碎的方式控制过滤器,通过设置 HttpRequestexception_reporter_filter 属性:

def my_view(request):
    if request.user.is_authenticated:
        request.exception_reporter_filter = CustomExceptionReporterFilter()
    ...

自定义过滤器类需要继承自类 :class:`django.views.debug.SafeExceptionReporterFilter`并重写以下属性和方法:

class SafeExceptionReporterFilter
cleansed_substitute

用于替换敏感值的字符串。默认情况下,它将敏感参数的值替换成星号 (**********)。

hidden_settings

一个编译了的用于匹配配置和 request.META 值的不忽略大写的正则表达式对象。默认相当于

import re

re.compile(r'API|TOKEN|KEY|SECRET|PASS|SIGNATURE', flags=re.IGNORECASE)
is_active(request)

返回 True 以在 get_post_parameters()get_traceback_frame_variables() 中激活过滤。默认情况下如果配置 DEBUGFalse 则过滤器会启动。请注意,就像文档中配置 DEBUG 描述的一样,敏感的 request.META 请求值总是会以配置好的值过滤掉。

get_post_parameters(request)

返回POST参数过滤后的字典。敏感参数的值会被替换成 cleansed_substitute

get_traceback_frame_variables(request, tb_frame)

返回给定的调用栈层中的局部变量经过过滤后的字段。敏感值会被替换成 cleansed_substitute

如果你需要在过滤器之外自定义错误报告,你可以指定一个自定义写在配置 DEFAULT_EXCEPTION_REPORTER setting:: 配置的错误报告类

DEFAULT_EXCEPTION_REPORTER = 'path.to.your.CustomExceptionReporter'

异常报告工具负责编译异常报告并将其整理成文本或HTML文件的形式。(当准备要报告的异常数据的时候异常报告工具使用配置 DEFAULT_EXCEPTION_REPORTER_FILTER

你的自定义的报告类需要继承自类 :class: django.views.debug.ExceptionReporter

class ExceptionReporter
html_template_path
New in Django 3.2.

返回一个 pathlib.Path 的类,该路径表示模板的绝对文件系统路径,用于呈现异常的 HTML 表示。 默认为 Django 提供的模板。

text_template_path
New in Django 3.2.

返回一个 pathlib.Path 的类,该路径表示模板的绝对文件系统路径,用于呈现异常的纯文本表示。 默认为 Django 提供的模板。

get_traceback_data()

返回一个包含回溯信息的字典。

这是自定义异常报告工具的主要扩展点,如下:

from django.views.debug import ExceptionReporter


class CustomExceptionReporter(ExceptionReporter):
    def get_traceback_data(self):
        data = super().get_traceback_data()
        # ... remove/add something here ...
        return data
get_traceback_html()

返回异常报告的HTML版本。

用于返回HTTP 500错误页面的HTML版本。

get_traceback_text()

返回异常报告的纯文本版本。

用于调试HTTP 500错误页面和相应邮件报告的纯文本版本。

和过滤类一样,你也能在任意视图内通过配置 HttpRequestexception_reporter_filter 属性来控制具体使用哪个错误报告类:

def my_view(request):
    if request.user.is_authenticated:
        request.exception_reporter_class = CustomExceptionReporter()
    ...

参见

你也能够通过自定义一系列的 异常中间层 定制错误报告行为。若你已经编写了自定义错误处理器,模仿 Django 内置的错误处理器,并只在 DEBUGFalse 时报告或记录错误是个不错的主意。