发送错误

当您运行着公开的网页时,你应该把设置中的 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 错误邮件的条件:

  • DEBUGFalse
  • MIDDLEWARE 配置包括 django.middleware.common.BrokenLinkEmailsMiddleware。如以上条件均满足,Django 会在代码抛出 404 错误且请求有对应来源时向 MANAGERS 配置项中的用户发送邮件。Django 并不会为没有来源的 404s 发送邮件——这些通常是人们或失效的网络爬虫直接输入无效链接。Django 还忽略来源于所请求的 URL 相同的 404s,因为这往往是失效的网络爬虫干的。

注解

BrokenLinkEmailsMiddleware 必须出现在其它拦截 404 错误的中间层之前,例如 LocaleMiddlewareFlatpageFallbackMiddleware。将其置于 MIDDLEWARE 配置项的顶部。

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

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

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

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

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

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

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

参见

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

过滤错误报告

警告

过滤敏感数据是个极度困难的问题,且几乎不可能不将敏感数据泄露至错误报告。因此,错误报告应该只对可信任的团队成员开放,并且,你应该避免直接通过明文传输错误报告(例如通过邮件)。

过滤敏感数据

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

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

  • sensitivevariables(*variables_)[源代码]
  • 若代码中的一个函数(也可能是视图或定期回调)使用容易包含敏感信息的本地变量,你可以用 sensitive_variables 装饰器阻止错误报告包含这些变量的值:
  1. from django.views.decorators.debug import sensitive_variables
  2.  
  3. @sensitive_variables('user', 'pw', 'cc')
  4. def process_info(user):
  5. pw = user.pass_word
  6. cc = user.credit_card_number
  7. name = user.name
  8. ...

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

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

  1. @sensitive_variables()def my_function():

同时使用多个装饰器时

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

  1. @sensitive_variables('user', 'pw', 'cc')@some_decorator@another_decoratordef process_info(user):

  • sensitivepost_parameters(*parameters_)[源代码]
  • 若视图收到一个包含 POST 参数HttpRequest 对象,参数中极有可能内含敏感信息。你可以通过 sensitive_post_parameters 装饰器避免这些参数的值被包括在错误报告中:
  1. from django.views.decorators.debug import sensitive_post_parameters
  2.  
  3. @sensitive_post_parameters('pass_word', 'credit_card_number')
  4. def record_user_profile(request):
  5. UserProfile.create(
  6. user=request.user,
  7. password=request.POST['pass_word'],
  8. credit_card=request.POST['credit_card_number'],
  9. name=request.POST['name'],
  10. )
  11. ...

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

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

  1. @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 启用它:

  1. DEFAULT_EXCEPTION_REPORTER_FILTER = 'path.to.your.CustomExceptionReporterFilter'

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

  1. def my_view(request):
  2. if request.user.is_authenticated:
  3. request.exception_reporter_filter = CustomExceptionReporterFilter()
  4. ...

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

  • class SafeExceptionReporterFilter[源代码]
  • SafeExceptionReporterFilter.isactive(_request)[源代码]
  • 返回 True 将激活其它方法中的过滤操作。 DEBUGFalse 时,过滤器默认是激活的。

  • SafeExceptionReporterFilter.getpost_parameters(_request)[源代码]

  • 返回 POST 参数过滤后的字典。默认情况下,它将敏感参数的值替换成星号 (**)。

  • SafeExceptionReporterFilter.gettraceback_frame_variables(_request, tb_frame)[源代码]

  • 返回给定的调用栈层中的局部变量经过过滤后的字段。默认情况下,它将敏感参数的值替换成星号 (**)。

参见

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