在gunicorn中使用Python日志模块的正确方法?

gis*_*gur 6 python logging flask gunicorn

我在使用 Python 3.6 ( Flask==0.11.1) 和gunicorn==19.9.0. 我正在使用structlog==18.1.0(用于 JSON 日志记录)并python-json-logger==0.1.9在 Flask 应用程序内部注销各种单独运行良好的东西,即在没有 gunicorn 的情况下运行 Flask 应用程序时。但是当在 gunicorn 中运行 Flask 应用程序时,一些日志问题随机开始出现,即来自 gunicorn 和 Flask 应用程序内部的访问日志都丢失了。

记录事件的一个例子是

import structlog

def some_flask_endpoint():
    logger = structlog.getLogger(__name__)
    logger.info('This is an info event', additional_data='some additional data')
Run Code Online (Sandbox Code Playgroud)

这应该向类似于此的日志输出 JSON 消息

{"message": "This is an info event", "additional_data": "some additional data"}
Run Code Online (Sandbox Code Playgroud)

目前,这一切都是通过日志 FileConfig(gunicorn 和 Flask 应用程序都在使用)完成的,如下所示:

[loggers]
keys = root,gunicorn.error,gunicorn.access

[handlers]
keys=console,logfile

[formatters]
keys=json

[logger_root]
level=INFO
handlers=console,logfile

[logger_gunicorn.error]
level = INFO
handlers = logfile
propagate = 1
qualname = gunicorn.error

[logger_gunicorn.access]
level = INFO
handlers = logfile
propagate = 0
qualname = gunicorn.access

[handler_console]
class=logging.StreamHandler
formatter=json
args=(sys.stdout, )

[handler_logfile]
class=logging.handlers.TimedRotatingFileHandler
formatter=json
args=('logs.log', 'midnight', )

[formatter_json]
format=%(message)s
class=pythonjsonlogger.jsonlogger.JsonFormatter
Run Code Online (Sandbox Code Playgroud)

例如,gunicorn 使用这个文件和参数--logconfig logconfig.ini,flask 使用它logging.config.fileConfig('logconfig.ini')来自logging包。

我意识到将根记录器输出到日志文件可能会导致来自其他记录器的某些事件被双重记录,但我主要担心的是,由于 gunicorn 启动了几个工人(在这种情况下为 4 个),因此每个工人structlog使用的地方可能会发生冲突(即多个进程试图访问单个文件)这可能是某些日志丢失的原因。

所以我想到的另一个选择是简单地将根记录器更改为仅使用console处理程序,或者只是创建一个structlog使用的新记录器,即

{"message": "This is an info event", "additional_data": "some additional data"}
Run Code Online (Sandbox Code Playgroud)
[logger_my_app]
level=INFO
handlers=console
Run Code Online (Sandbox Code Playgroud)

然后让gunicorn master从stdout中获取所有日志并将其写入日志文件并解决多进程单文件问题。然而,当为 gunicorn 提供这个日志配置文件时(以便它在 json 中输出它的日志,而且我不必为配置 gunicorn 提供无数的参数)它似乎完全忽略了该参数--capture-output,因此我的structlog日志只出现在控制台中但不会写入任何配置文件。

所以在这篇相当长但希望解释性的帖子之后,有没有人有关于在gunicorn内部存储日志logging/structlog到文件的正确方法的信息,因为我在网上找不到任何具体信息并且gunicorn文档在这个主题上相当不错?

Tl; dr:如何将标准输出发送到 gunicorn 中的日志文件,同时还向 gunicorn 提供日志配置文件。