为什么在 运行 带有 werkzeug 的 Flask 应用程序时日志记录不起作用?

Why does logging not work when running a Flask app with werkzeug?

这里是一个复制粘贴示例,可重现该问题。

import logging

from flask import Flask
from werkzeug.serving import run_simple
from werkzeug.wsgi import DispatcherMiddleware

def app_builder(app_name, log_file):

    app = Flask(app_name)
    app.debug = True

    handler = logging.FileHandler(log_file)
    handler.setLevel(logging.DEBUG)
    app.logger.addHandler(handler)

    return app

def _simple(env, resp):
    resp(b'200 OK', [(b'Content-Type', b'text/plain')])
    return [b'root']

if __name__ == "__main__":

    app = app_builder(app_name='app', log_file='app.log')

    @app.route('/')
    def index():
        return '<a href="/app/error">click for error</a>'

    @app.route('/error')
    def error():
        1/0
        return 'error page'

    app2 = app_builder(app_name='app2', log_file='app2.log')

    @app2.route('/')
    def index():
        return 'you are getting responses from app2'

    app.debug = True
    app2.debug = True

    application = DispatcherMiddleware(_simple, {
        '/app':     app,
        '/app2':    app2
        })

    run_simple(hostname='localhost',
               port=5000,
               application=application,
               use_reloader=True,
               use_debugger=True)

要使错误显示导航到 http://localhost:5000/app/error,我想知道为什么堆栈跟踪没有显示在 app.log 文件中。我假设 DispatcherMiddlewarerun_simple 在记录异常之前以某种方式捕获了异常。如果我 运行 只有 app 实例使用 app.run() 错误记录工作正常。

我找到了一个 gist,它讨论了在 Flask 中登录。 andyxning 的评论(评论于 2015 年 4 月 18 日)提到了这一点 - if app.debug is True then all log level above DEBUG will be logged to stderr(StreamHandler)

评论还有一个link到flask/logging.py的源代码。 create_logger 方法创建了一个 DebugHandler 的实例,它继承自 StreamHandler class.

如果你打印app.logger.handlers你可以看到它有一个对象flask.logging.DebugHandler

print app.logger.handlers
[<flask.logging.DebugHandler object at 0x110315090>]

此 DebugHandler 可能在 app.debug is set to true 时使用,因此堆栈跟踪会打印在控制台上。

希望这就是您要找的。

app.debug = True 时不调用正常的异常处理程序。看着 在 Flask 中 app.py 的代码中:

def log_exception(self, exc_info):
    """Logs an exception.  This is called by :meth:`handle_exception`
    if debugging is disabled and right before the handler is called.
    ^^^^^^^^^^^^^^^^^^^^^^^^
    The default implementation logs the exception as error on the
    :attr:`logger`.

确实,设置 app.debug = True 时设置了异常传播 显式为 True,这会阻止 log_exception 被调用。这是文档的摘录(重点是我的):

PROPAGATE_EXCEPTIONS: explicitly enable or disable the propagation of exceptions. If not set or explicitly set to None this is implicitly true if either TESTING or DEBUG is true.

所以,我设法让 werkzeug 调试和日志记录工作愉快 连同一点点调整和以下代码:

import logging

from flask import Flask
from werkzeug.serving import run_simple
from werkzeug.wsgi import DispatcherMiddleware
## NEW CODE HERE
import functools
from flask._compat import reraise

def my_log_exception(exc_info, original_log_exception=None):
    original_log_exception(exc_info)
    exc_type, exc, tb = exc_info
    # re-raise for werkzeug
    reraise(exc_type, exc, tb)
## 

def app_builder(app_name, log_file):
    app = Flask(app_name)
    app.debug = True
    app.config.update(PROPAGATE_EXCEPTIONS=False)

    handler = logging.FileHandler(log_file)
    handler.setLevel(logging.DEBUG)
    app.logger.addHandler(handler)

    ## NEW CODE
    app.log_exception = functools.partial(my_log_exception,  original_log_exception=app.log_exception)
    ##

    return app

# rest of your code is unchanged