Skip to main content

Flask 1.0 进阶 - 应用程序错误

Flask 1.0 进阶 - 应用程序错误

Flask 1.0 进阶 - 应用程序错误

应用程序失败,服务器失败迟早你会看到生产中的例外情况。即使您的代码100%正确,您仍会不时看到异常。为什么?因为所涉及的一切都会失败。以下是一些完美的代码可能导致服务器错误的情况:

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

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

但是你可以做的更多,我们将介绍一些更好的设置来处理错误。

错误记录工具

如果有足够多的用户遇到错误并且通常从不查看日志文件,那么发送错误邮件(即使仅针对关键邮件)也会变得无法控制。这就是我们建议使用Sentry处理应用程序错误的原因。它可以在GitHub上作为开源项目使用,也可以作为托管版本使用,您可以免费试用。Sentry聚合重复错误,捕获完整堆栈跟踪和本地变量以进行调试,并根据新错误或频率阈值向您发送邮件。

要使用Sentry,您需要安装具有额外Flask依赖性的raven客户端:

pip install raven[flask]

然后将其添加到Flask应用中:

from raven.contrib.flask import Sentry
sentry = Sentry(app, dsn='YOUR_DSN_HERE')

或者,如果您正在使用工厂,您也可以稍后启动它:

from raven.contrib.flask import Sentry
sentry = Sentry(dsn='YOUR_DSN_HERE')

def create_app():
    app = Flask(__name__)
    sentry.init_app(app)
    ...
    return app

需要使用从Sentry安装获得的DSN值替换YOUR_DSN_HERE值。

之后故障会自动报告给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(InsuffcientStorage, handle_507)

raise InsufficientStorage()

可以为任何异常类注册处理程序,而不仅仅是HTTPException子类或HTTP状态代码。可以为特定类或父类的所有子类注册处理程序。

处理

当Flask在处理请求时捕获到异常时,首先按代码查找。如果没有为代码注册处理程序,则按类层次结构查找;选择最具体的处理程序。如果未注册任何处理程序,则HTTPException 子类显示有关其代码的通用消息,而其他异常则转换为通用500内部服务器错误。

例如,如果引发了 ConnectionRefusedError 的实例,并且为 ConnectionError 和 ConnectionRefusedError 注册了处理程序,则使用异常实例调用更具体的 ConnectionRefusedError 处理程序以生成响应。

在blueprint上注册的处理程序优先于在应用程序上全局注册的处理程序,假设blueprint正在处理引发异常的请求。但是,blueprint无法处理404路由错误,因为404可以在确定blueprint之前发生在路由级别。

版本0.11: 处理程序的优先级取决于它们注册的异常类的特殊性,而不是它们注册的顺序

日志记录

有关如何记录异常的信息,请参阅记录,例如通过电子邮件将其发送给管理员。

调试应用程序错误

对于生产应用程序,使用应用程序错误中所述的日志记录和通知配置应用程序。本节提供了调试部署配置和深入挖掘全功能Python调试器的指针。

当有疑问时,手动运行

在为生产配置应用程序时遇到问题?如果您具有对主机的shell访问权限,请验证您是否可以从部署环境中的shell手动运行应用程序。请确保在与配置的部署相同的用户帐户下运行,以解决权限问题。您可以在生产主机上使用带有 debug = True 的Flask内置开发服务器,这有助于捕获配置问题,但 请务必在受控环境中暂时执行此操作 。不要使用 debug = True 在生产中运行。

使用调试器

为了深入挖掘,可能需要跟踪代码执行,Flask提供了一个开箱即用的调试器(参见调试模式)。如果您想使用其他Python调试器,请注意调试器会相互干扰。您必须设置一些选项才能使用您喜欢的调试器:

  • debug - 是否启用调试模式和捕获异常
  • use_debugger - 是否使用内部Flask调试器
  • use_reloader - 是否在异常时重新加载和分叉进程

debug必须为True(即必须捕获异常)才能使其他两个选项具有任何值。

如果您使用Aptana/Eclipse进行调试,则需要将use_debuggeruse_reloader都设置为False。

一个可能有用的配置模式是在config.yaml中设置以下内容(当然,根据您的应用程序更改块):

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")

    if app.debug: use_debugger = True
    try:
        # Disable Flask's debugger if external debugger is requested
        use_debugger = not(app.config.get('DEBUG_WITH_APTANA'))
    except:
        pass
        
    app.run(use_debugger=use_debugger, debug=app.debug,
            use_reloader=use_debugger, host='0.0.0.0')
版权声明

版权声明

张大鹏 创作并维护的 Walkerfree 博客采用 创作共用保留署名-非商业-禁止演绎4.0国际许可证。本文首发于 Walkerfree 博客(https://www.walkerfree.com/),版权所有,侵权必究。本文永久链接:https://www.walkerfree.com/article/165/