为什么Flask应用在Gunicorn托管时不生成日志?

83 投票
3 回答
64674 浏览
提问于 2025-04-29 19:40

我正在尝试为一个使用Flask的网页应用添加日志记录。

当我使用内置服务器(也就是运行 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()

这个应用是通过以下方式调用的:

gunicorn server:flaskApp -b :80 -w 4
    --access-gfile /var/log/demo/access.log
    --error-logfile /var/log/demo/error.log

当我访问网站的主页时,会发生以下情况:

  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版本应该没有不匹配的问题。

这是怎么回事呢?

相关文章:

  • 暂无相关问题
暂无标签

3 个回答

16

这背后有几个原因:Gunicorn有自己的日志记录系统,它通过这个系统来控制日志的级别。解决这个问题的方法是添加app.logger.setLevel(logging.DEBUG)。
但这个方法有什么问题呢?首先,这个设置是写死在应用程序里的。没错,我们可以把它改成一个环境变量,但这样就会出现两个不同的日志级别:一个是Flask应用的日志级别,另一个是Gunicorn的日志级别,后者是通过--log-level参数来设置的(比如“debug”、“info”、“warning”、“error”和“critical”等级别)。

解决这个问题的一个好方法是下面的代码片段:

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)

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

61

这个方法对我有效:导入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')

我的Gunicorn启动脚本被设置成这样,可以把日志信息输出到一个文件里:

gunicorn main:app \
    --workers 4 \
    --bind 0.0.0.0:9000 \
    --log-file /app/logs/gunicorn.log \
    --log-level DEBUG \
    --reload
50

当你使用 python3 server.py 时,你是在运行一个叫做 server.py 的脚本。

而当你使用 gunicorn server:flaskApp ... 时,你是在运行 gunicorn 的启动脚本,这个脚本会导入 server 这个模块,并在里面查找一个叫做 flaskApp 的变量。

因为 server.py 被导入了,所以 __name__ 这个变量的值会是 "server",而不是 "__main__",所以你设置日志处理的代码不会被执行。

你可以简单地把日志处理的代码移到 if __name__ == "__main__": 这个判断语句之外。但要确保 flaskApp.run() 仍然放在里面,因为你不想在 gunicorn 导入 server 时执行它。

想了解更多关于 if __name__ == “__main__”: 的作用

撰写回答