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

我试图添加日志到一个使用 Flask 的 Web 应用程序。

当使用内置服务器(即 python3 server.py)托管时,日志记录可以正常工作。

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

#!/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 版本有任何不匹配。

发生什么事了?

55096 次浏览

When you use python3 server.py you are running the server3.py script.

When you use gunicorn server:flaskApp ... you are running the gunicorn startup script which then imports the module server and looks for the variable flaskApp in that module.

Since server.py is being imported the __name__ var will contain "server", not "__main__" and therefore you log handler setup code is not being run.

You could simply move the log handler setup code outside of the if __name__ == "__main__": stanza. But ensure that you keep flaskApp.run() in there since you do not want that to be run when gunicorn imports server.

More about what does if __name__ == “__main__”: do?

This approach works for me: Import the Python logging module and add gunicorn's error handlers to it. Then your logger will log into the gunicorn error log file:

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

My Gunicorn startup script is configured to output log entries to a file like so:

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

There are a couple of reasons behind this: Gunicorn has its own loggers, and it’s controlling log level through that mechanism. A fix for this would be to add app.logger.setLevel(logging.DEBUG).
But what’s the problem with this approach? Well, first off, that’s hard-coded into the application itself. Yes, we could refactor that out into an environment variable, but then we have two different log levels: one for the Flask application, but a totally separate one for Gunicorn, which is set through the --log-level parameter (values like “debug”, “info”, “warning”, “error”, and “critical”).

A great solution to solve this problem is the following snippet:

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)

Refrence: Code and Explanation is taken from here