应用错误处理

0.3 新版功能.

Changelog

应用程序失败,服务器失败。或早或晚,你会遇到产品出错。即使您的代码是100%正确的,您仍然会不时看到异常。为什么?其他相关东西会出错。以下是一些在代码完全正确的条件下服务器出错的情况:

  • 客户端提前终止了请求,应用程序仍在读取传入数据
  • 数据库服务器超载,无法处理查询
  • 文件系统已满
  • 硬盘坏了
  • 后台服务器过载
  • 正在使用的库出现程序错误
  • 服务器与另一个系统的网络连接失败

以上只是你可能遇到的一小部分问题。那么,我们如何处理这类问题呢?默认情况下,如果应用程序以生产模式运行,flask会显示一个非常简单出错页面,并将异常记录到logger。

但可做的还不只这些,下面介绍一些更好的方法来处理错误。

错误日志记录工具

当足够多的用户触发了错误时,发送关于出错信息的邮件,即使仅包含严重错误的邮件也会是一场灾难。更不用提从来不会去看的日志文件了。因此,推荐使用 Sentry 来处理应用错误。它可 以在一个开源项目on GitHub 中获得,也可以在 hosted version 中免费试用。Sentry 统计重复错误,捕获堆栈数据和本地变量用于排错,并在发生新的或者指定 频度的错误时发送电子邮件。

要使用Sentry,你需要安装带有 flask依赖的raven 客户端:

$ pip install sentry-sdk[flask]

然后把下面内容添加到你的Flask应用程序:

import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration

sentry_sdk.init('YOUR_DSN_HERE',integrations=[FlaskIntegration()])

这个 YOUR_DSN_HERE 需要被替换为从Sentry安装中获得的DSN值。

安装后,导致内部服务器错误的故障会自动报告给Sentry,从那里你可以收到错误通知。

后续阅读:

错误处理

发生错误时,你可能希望向用户显示自定义错误页。注册错误处理器可以做到这点。

错误处理程序是返回响应的普通视图函数,但是不同之在于它不是用于路由的,而是用于一个异常或者当尝试处理请求时抛出 HTTP 状态码。

注册

通过使errorhandler修饰函数来注册处理程序或稍后使用register_error_handler注册。返回响应时请记住设置错误代码:

@app.errorhandler(werkzeug.exceptions.BadRequest)
def handle_bad_request(e):
    return 'bad request!', 400

# or, without the decorator
app.register_error_handler(400, handle_bad_request)

当注册时,werkzeug.exceptions.HTTPException 的子类,如 BadRequest ,它们的HTTP代码在注册处理程序时是可互换的。( BadRequest.code == 400

非标准HTTP代码不能通过代码注册,因为Werkzeug无法识别这些代码。相反,使用适当的代码定义一个 HTTPException 子类,注册并抛出异常类:

class InsufficientStorage(werkzeug.exceptions.HTTPException):
    code = 507
    description = 'Not enough storage space.'

app.register_error_handler(InsufficientStorage, handle_507)

raise InsufficientStorage()

除了HTTPException 子类或者 HTTP 状态码,出错处理器可被用于任何异常类的注册。出错处理器可被用于特定类的注册,也可用于一个父类的所有子类的注册。

处理

当处理请求时,当 Flask 捕捉到一个异常时,它首先根据代码检索。如果该代码没有注册处理器,它将按其类层次结构进行查找,确定最合适的注册处理器。如果找不到已注册的处理器,那么 HTTPException 子类会显示一个关于代码的通用消息。没有代码的异常会被转化为一个通用的 500 内部服务器 错误。

例如,如果一个 ConnectionRefusedError 的实例被抛出,并且一个出错处理器注册到 ConnectionError 和 ConnectionRefusedError,那么会使用更合适的 ConnectionRefusedError 来处理异常实例,生成响应。

当一个蓝图在处理抛出异常的请求时,在蓝图中注册的出错处理器优先于在应用中全局注册的出错处理器。但是,蓝图无法处理404路由错误,因为404发生在路由级别,无法检测到蓝图。

在 0.11 版更改: 处理程序根据其注册的异常类的特殊性而不是注册的顺序来确定优先级。

Changelog

日志

如何记录异常的信息,例如通过电子邮件将异常发送给管理员,可参见日志。

排除应用错误

应用错误处理一文所讲的是如何为生产应用设置日志和出错通知。本文要讲的是部署中配置调试的要点和如何使用全功能的 Pytho 调试器深挖错误。

有疑问时,请手动运行

在为生产配置应用程序时遇到问题?如果你有shell访问主机的权限,那么首先请 在部署环境中验证是否可以通过 shell 手动运行你的应用。请确保验证时使用的帐户与配置的相同,以解决权限问题。可以在你的生产服务器上,使用 Flask 内建的开发服务器,并且设置 debug=True,这样有助于找到配置问题。但是,请只能在可控的情况下临时这样做,绝不能在生产时使用 debug=True 。

使用调试器

为了深入挖掘错误,可能是为了跟踪代码执行,Flask提供了一个现成的调试器(请参见 调试模式 )。如果你想使用其他Python调试器,请注意调试器之间的干扰问题。在使用你自己的调试器前要做一些参数调整:

  • debug -是否启用调试模式并捕获异常
  • use_debugger -是否使用 Flask 内建的调试器
  • use_reloader -出现异常后是否重载或者派生进程

debug 必须设置为 True(即必须捕获异常),另两个随便。

如果你正在使用 Aptana 或 Eclipse 排错,那么 use_debugger 和 use_reloader 都必须设置为 False 。

一个有用的配置模式如下(当然要根据你的应用调整缩进):

FLASK:
    DEBUG: True
    DEBUG_WITH_APTANA: True

然后,在应用程序的入口点(main.py)中,修改如下:

if __name__ == "__main__":
    # To allow aptana to receive errors, set use_debugger=False
    app = create_app(config="config.yaml")

    use_debugger = app.debug and not(app.config.get('DEBUG_WITH_APTANA'))
    app.run(use_debugger=use_debugger, debug=app.debug,
            use_reloader=use_debugger, host='0.0.0.0')