如何将Flask的优秀调试日志消息写入生产中的文件?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (85)

我有一个能正常工作并偶尔产生错误的烧瓶应用程序。当我的应用程序处于调试模式时,使用:

if __name__ == '__main__':
    app.run(debug=True)

我收到有用的错误消息,例如:

Traceback (most recent call last):
  File "./main.py", line 871, in index_route

KeyError: 'stateIIIII'

在生产中运行应用程序时,我希望获得这样的错误消息,保存到文件中(使用Lightttpd+Quickcgi)。

在我的应用程序文件的顶部,有各种导入,后面跟着:

app = Flask(__name__)

if app.debug is not True:   
    import logging
    from logging.handlers import RotatingFileHandler
    file_handler = RotatingFileHandler('python.log', maxBytes=1024 * 1024 * 100, backupCount=20)
    file_handler.setLevel(logging.ERROR)
    app.logger.setLevel(logging.ERROR)
    app.logger.addHandler(file_handler)

然后,我将每个路由的代码放在try/EXT语句中,并使用回溯来计算错误来自哪一行,并打印一条很好的错误消息:

def some_route():
    try:
        # code for route in here (including a return statement)

    except:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        app.logger.error(traceback.print_exception(exc_type, exc_value, exc_traceback, limit=2))
        return render_template('error.html')

然后,在文件末尾移除DEBUG=True语句。虽然我不认为我需要这样做,因为当该应用程序在生产中运行时,该应用程序是由快速cgi服务器(?)运行的。我的应用程序代码的最后两行如下所示:

if __name__ == '__main__':
    app.run()

我正努力让这件事起作用。我认为我管理的最佳方法是使用(app.logger.Error(‘test Message“))将单个错误日志消息保存在文件中,但它只输出这一条消息。在该错误被忽略之后,直接记录另一个错误的尝试。

提问于
用户回答回答于

我不知道为什么不行,但我知道我是怎么做到的。

首先,不需要设置app.logger级别。所以移除这一行app.logger.setLevel()...

希望保存每个视图的异常并返回错误页。在任何地方编写这段代码都是一项很大的工作。定义这样的错误处理程序方法。

    @app.errorhandler(500)
    def internal_error(exception):
        app.logger.error(exception)
        return render_template('500.html'), 500

每当视图引发异常时,都会调用此方法并将其作为参数传递。PythonLogging提供了用于保存异常的完整跟踪的异常方法。

因为这样可以处理所有异常,所以甚至不需要将代码放入try/EXT块中。但是,如果想在调用错误处理程序之前执行一些操作,那么执行以下操作:

    try:
        #code
    except:
        #code
        raise

如果希望为日志文件中的每个条目添加日期和时间,可以使用以下代码(代替问题中的类似代码)。

if app.debug is not True:   
    import logging
    from logging.handlers import RotatingFileHandler
    file_handler = RotatingFileHandler('python.log', maxBytes=1024 * 1024 * 100, backupCount=20)
    file_handler.setLevel(logging.ERROR)
    formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
    file_handler.setFormatter(formatter)
    app.logger.addHandler(file_handler)
用户回答回答于

我认为最好将更多有用的信息放入错误信息中。URL、客户端IP、用户代理等.。记录异常(在app.debug==False(模式)与Flask.log_exception功能。所以,与其手动登录到@app.errorhandler我做了这样的事:

class MoarFlask(Flask):
    def log_exception(self, exc_info):
        """...description omitted..."""
        self.logger.error(
            """
Request:   {method} {path}
IP:        {ip}
User:      {user}
Agent:     {agent_platform} | {agent_browser} {agent_browser_version}
Raw Agent: {agent}
            """.format(
                method = request.method,
                path = request.path,
                ip = request.remote_addr,
                agent_platform = request.user_agent.platform,
                agent_browser = request.user_agent.browser,
                agent_browser_version = request.user_agent.version,
                agent = request.user_agent.string,
                user=user
            ), exc_info=exc_info
        )

然后,在配置时绑定FileHandlerapp.logger然后继续。我不用StreamHandler因为许多服务器(例如uWSGI)喜欢用自己的专有的、冗长的、无用的、不可关闭的消息来污染它。

扫码关注云+社区