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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26578733/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 00:42:08  来源:igfitidea点击:

Why is Flask application not creating any logs when hosted by Gunicorn?

pythonloggingflaskgunicorn

提问by Arseni Mourzenko

I'm trying to add logging to a web application which uses Flask.

我正在尝试向使用 Flask 的 Web 应用程序添加日志记录。

When hosted using the built-in server (i.e. python3 server.py), logging works. When hosted using Gunicorn, the log file is not created.

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

The simplest code which reproduces the problem is this one:

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

#!/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()

The application is called using:

该应用程序使用以下方式调用:

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

When doing a request to the home page of the site, the following happens:

对站点主页进行请求时,会发生以下情况:

  1. I receive the expected HTTP 200 "Hello World\n" in response.

  2. There is a trace of the request in /var/log/demo/access.log.

  3. /var/log/demo/error.logstays the same (there are just the boot events).

  4. There is the "Direct output" line in the terminal.

  5. There is no '/var/log/demo/app.log'. If I create the file prior to launching the application, the file is not modified.

  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”。如果我在启动应用程序之前创建文件,则不会修改该文件。

Note that:

注意:

  • The directory /var/log/democan be accessed (read, write, execute) by everyone, so this is not the permissions issue.

  • If I add StreamHandleras a second handler, there is still no trace of the "Log message" message neither in the terminal, nor in Gunicorn log files.

  • Gunicorn is installed using pip3 install gunicorn, so there shouldn't be any mismatch with Python versions.

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

  • 如果我添加StreamHandler为第二个处理程序,在终端和 Gunicorn 日志文件中仍然没有“日志消息”消息的踪迹。

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

What's happening?

发生了什么?

采纳答案by Jeremy Allen

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

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

When you use gunicorn server:flaskApp ...you are running the gunicorn startup script which then importsthe module serverand looks for the variable flaskAppin that module.

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

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

由于server.py正在导入,因此__name__var 将包含"server", not "__main__",因此您的日志处理程序设置代码未运行。

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 notwant that to be run when gunicorn imports server.

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

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

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

回答by pkout

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:

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

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

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

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

回答by Khushhalm

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”).

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

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

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