为什么Flask应用程序在Gunicorn托管时不会创建任何日志?

Ars*_*nko 40 python logging flask gunicorn

我正在尝试将日志记录添加到使用Flask的Web应用程序.

使用内置服务器(即python3 server.py)托管时,日志记录有效.使用Gunicorn托管时,不会创建日志文件.

重现问题的最简单的代码是这样的:

#!/usr/bin/env python

import logging
from flask import Flask
flaskApp = Flask(__name__)


@flaskApp.route('/')
def index():
    flaskApp.logger.info('Log message')
    print('Direct output')
    return 'Hello World\n'


if __name__ == "__main__":
    logHandler = logging.FileHandler('/var/log/demo/app.log')
    logHandler.setLevel(logging.INFO)
    flaskApp.logger.addHandler(logHandler)
    flaskApp.logger.setLevel(logging.INFO)
    flaskApp.run()
Run Code Online (Sandbox Code Playgroud)

该应用程序使用:

gunicorn server:flaskApp -b :80 -w 4
    --access-gfile /var/log/demo/access.log
    --error-logfile /var/log/demo/error.log
Run Code Online (Sandbox Code Playgroud)

在对网站主页发出请求时,会发生以下情况:

  1. 我收到了预期的HTTP 200"Hello World \n"作为响应.

  2. 有一丝请求/var/log/demo/access.log.

  3. /var/log/demo/error.log 保持不变(只有引导事件).

  4. 终端中有"直接输出"线.

  5. 没有'/var/log/demo/app.log'.如果我在启动应用程序之前创建文件,则不会修改该文件.

注意:

  • 每个人/var/log/demo都可以访问(读,写,执行)目录,因此这不是权限问题.

  • 如果我添加StreamHandler为第二个处理程序,则终端和Gunicorn日志文件中仍然没有"日志消息"消息的痕迹.

  • Gunicorn是使用安装的pip3 install gunicorn,所以不应该与Python版本有任何不匹配.

发生了什么?

Jer*_*len 33

使用时,python3 server.py您正在运行server3.py脚本.

当您使用时,gunicorn server:flaskApp ...您正在运行gunicorn启动脚本,然后导入模块serverflaskApp在该模块中查找变量.

由于server.py正在导入,__name__var将包含"server",而不是"__main__"因此您没有运行日志处理程序设置代码.

您可以简单地将日志处理程序设置代码移动到该if __name__ == "__main__":节之外.但要确保你flaskApp.run()留在那里,因为你希望在gunicorn进口时运行server.

更多关于if __name__ == “__main__”:什么?

  • 另请注意,默认情况下,flask在gunicorn下以生产模式运行,其中app.logger级别设置为WARN,因此您不会看到app.logger.info()或app.logger.debug()。如果需要,请设置app.debug = True。 (2认同)

pko*_*out 27

这种方法适用于我:导入Python日志记录模块并向其添加gunicorn的错误处理程序.然后您的记录器将登录gunicorn错误日志文件:

import logging

app = Flask(__name__)

gunicorn_error_logger = logging.getLogger('gunicorn.error')
app.logger.handlers.extend(gunicorn_error_logger.handlers)
app.logger.setLevel(logging.DEBUG)
app.logger.debug('this will show in the log')
Run Code Online (Sandbox Code Playgroud)

我的Gunicorn启动脚本配置为将日志条目输出到文件,如下所示:

gunicorn main:app \
    --workers 4 \
    --bind 0.0.0.0:9000 \
    --log-file /app/logs/gunicorn.log \
    --log-level DEBUG \
    --reload
Run Code Online (Sandbox Code Playgroud)

  • @pdoherty926 有关双重日志记录问题,请参阅 https://medium.com/@trstringer/logging-flask-and-gunicorn-the-manageable-way-2e6f0b8beb2f (3认同)
  • 是否可以在脚本内部的命令行中添加日志文件的路径? (2认同)
  • 这是否会导致日志消息为您打印两次,@pkout?(对我来说确实如此。) (2认同)

Khu*_*alm 8

这背后有几个原因: Gunicorn 有自己的记录器,并且通过该机制控制日志级别。对此的解决方法是添加 app.logger.setLevel(logging.DEBUG)。
但是这种方法有什么问题呢?嗯,首先,这是硬编码到应用程序本身中的。是的,我们可以将它重构为一个环境变量,但是我们有两个不同的日志级别:一个用于 Flask 应用程序,但一个用于 Gunicorn 的完全独立的一个,它通过 --log-level 参数设置(值如“调试”、“信息”、“警告”、“错误”和“关键”)。

解决此问题的一个很好的解决方案是以下代码段:

import logging
from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/')
def default_route():
    """Default route"""
    app.logger.debug('this is a DEBUG message')
    app.logger.info('this is an INFO message')
    app.logger.warning('this is a WARNING message')
    app.logger.error('this is an ERROR message')
    app.logger.critical('this is a CRITICAL message')
    return jsonify('hello world')

if __name__ == '__main__':
    app.run(host=0.0.0.0, port=8000, debug=True)

else:
    gunicorn_logger = logging.getLogger('gunicorn.error')
    app.logger.handlers = gunicorn_logger.handlers
    app.logger.setLevel(gunicorn_logger.level)
Run Code Online (Sandbox Code Playgroud)

参考:代码和解释取自这里