我希望gunicorn.error使用以下基于键值的日志格式,而不是gunicorn/glogging.py中定义的默认格式:
'format': 'timestamp=%(asctime)s pid=%(process)d loglevel=%(levelname)s msg=%(message)s'`
在我的gunicorn配置文件中:
import logging.config
workers = 2
bind = "127.0.0.1:8000"
loglevel = 'INFO'
LOGGING = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'key_value': {
'format': 'timestamp=%(asctime)s pid=%(process)d loglevel=%(levelname)s msg=%(message)s'
},
},
'handlers': {
'console': {
'level': 'INFO',
'class': 'logging.StreamHandler',
'formatter': 'key_value',
'stream': 'ext://sys.stdout'
}
},
'loggers': {
'gunicorn.error': {
'handlers': ['console'],
'level': 'INFO',
'propagate': False,
},
'flask.app': {
'handlers': ['console'],
'level': 'INFO',
'propagate': False,
}
},
}
logging.config.dictConfig(LOGGING)
Gunicorn以我的自定义格式和默认格式记录两次:
timestamp=2016-12-11 15:20:49,559 pid=22110 loglevel=INFO msg=Starting gunicorn 19.6.0
[2016-12-11 15:20:49 +0000] [22110] [INFO] Starting gunicorn 19.6.0
timestamp=2016-12-11 15:20:49,559 pid=22110 loglevel=INFO msg=Listening at: http://127.0.0.1:8000 (22110)
[2016-12-11 15:20:49 +0000] [22110] [INFO] Listening at: http://127.0.0.1:8000 (22110)
timestamp=2016-12-11 15:20:49,559 pid=22110 loglevel=INFO msg=Using worker: sync
[2016-12-11 15:20:49 +0000] [22110] [INFO] Using worker: sync
timestamp=2016-12-11 15:20:49,560 pid=22115 loglevel=INFO msg=Booting worker with pid: 22115
[2016-12-11 15:20:49 +0000] [22115] [INFO] Booting worker with pid: 22115
timestamp=2016-12-11 15:20:49,595 pid=22115 loglevel=INFO msg=Starting Flask application
timestamp=2016-12-11 15:20:49,659 pid=22120 loglevel=INFO msg=Booting worker with pid: 22120
[2016-12-11 15:20:49 +0000] [22120] [INFO] Booting worker with pid: 22120
timestamp=2016-12-11 15:20:49,693 pid=22120 loglevel=INFO msg=Starting Flask application
我使用logging_tree库来查看已配置的记录器,并且我看到2个gunicorn记录器向控制台发出信息:
<--""
Level WARNING
|
o<--"flask"
| Level NOTSET so inherits level WARNING
| |
| o "flask.app"
| Level INFO
| Propagate OFF
| Handler Stream <open file '<stdout>', mode 'w' at 0x7f86676b1150>
| Level INFO
| Formatter fmt='timestamp=%(asctime)s pid=%(process)d loglevel=%(levelname)s msg=%(message)s' datefmt=None
|
o<--"gunicorn"
Level NOTSET so inherits level WARNING
|
o "gunicorn.access"
| Level INFO
| Propagate OFF
|
o "gunicorn.error"
| Level INFO
| Propagate OFF
| Handler Stream <open file '<stdout>', mode 'w' at 0x7f86676b1150>
| Level INFO
| Formatter fmt='timestamp=%(asctime)s pid=%(process)d loglevel=%(levelname)s msg=%(message)s' datefmt=None
| Handler Stream <open file '<stderr>', mode 'w' at 0x7f86676b11e0>
| Formatter fmt='%(asctime)s [%(process)d] [%(levelname)s] %(message)s' datefmt='[%Y-%m-%d %H:%M:%S %z]'
|
o<--"gunicorn.http"
Level NOTSET so inherits level WARNING
|
o<--"gunicorn.http.wsgi"
Level NOTSET so inherits level WARNING
Gunicorn的docs说可以指定要使用的记录器类,但我不知道该怎么做。
答案 0 :(得分:1)
我通过指定自己的自定义日志记录类获得了很好的运气。您可以通过创建一个继承自Gunicorn的gunicorn.glogging.Logger
类的类,然后覆盖此类的setup(self, cfg)
方法来完成此操作。
例如:
import logging
from gunicorn import glogging
class CustomLogger(glogging.Logger):
"""Custom logger for Gunicorn log messages."""
def setup(self, cfg):
"""Configure Gunicorn application logging configuration."""
super().setup(cfg)
# Override Gunicorn's `error_log` configuration.
self._set_handler(
self.error_log, cfg.errorlog, logging.Formatter(
fmt=('timestamp=%(asctime)s pid=%(process)d '
'loglevel=%(levelname)s msg=%(message)s'))
现在,根据你如何推出Gunicorn,你可以提供这个课程的完整路径; IE:program.app.CustomLogger
通过--logger-class
选项,或者您可以通过您自己的客户Gunicorn应用程序类直接传递它:
from gunicorn.app import base
from program.app import app, CustomLogger
class WebServer(base.BaseApplication):
"""Gunicorn WSGI Web Server."""
def __init__(self, app, options):
"""Initialize server object."""
self.options = options or {}
self.application = app
super().__init__()
def load():
"""Return WSGI application."""
return self.application
def load_config():
"""Load configuration into Gunicorn."""
self.cfg.set('logger_class', CustomLogger)
if __name__ == '__main__':
WebServer(app, {}).run()
这应该可以实现您期望的目标,而不需要任何令人讨厌的INI文件,并且它是完全支持的配置样式。
答案 1 :(得分:0)
我认为您只需要使用您自己的配置提供配置文件(.ini
或.conf
)。确保覆盖默认值。
examples
目录中有examples/logging.conf
。
答案 2 :(得分:0)
我最近自己碰上了gunicorn
,还有其他一些python软件包,它们在导入时不遵循现有的日志记录配置。
以下功能将重置所有现有的日志记录配置并从字典中加载新配置。有关配置字典架构的更多信息,请参见logging.config.dictConfig。
在导入所有有问题的模块后只需调用reset_logging_configuration()
。
import logging
import logging.config
from my_logging_config import CONFIG # see `logging.config.dictConfig`
def reset_logging_configuration():
logging._acquireLock()
for logger in logging.Logger.manager.loggerDict.values():
if not isinstance(logger, logging.Logger):
continue
logger.handlers.clear() # unset all logger handlers
logger.filters.clear() # unset all logger filters
logger.level = logging.NOTSET # unset logger level
logger.propagate = True # enable logger propagation
logging._releaseLock()
logging.config.dictConfig(CONFIG) # load actual config
Beaware :该方法与logging
的内部结构混淆,并且比实际的解决方案更能解决问题。但是,许多模块在导入时会盲目地覆盖现有的logging
配置,因此您别无选择,只能在加载后覆盖其配置。谨慎使用!
此代码基于logging/config.py中的代码段。
答案 3 :(得分:0)
Gunicorn使用名为glogger glogger code的自定义记录器,该记录器内部具有Logger类,您可以使用记录器已经可用的属性来更改消息的格式。
具有自定义格式的gunicorn_config文件可能如下所示(属性不言自明)
from gunicorn import glogging
glogging.Logger.error_fmt = '{"AppName": "%(name)s", "logLevel": "%(levelname)s", "Timestamp": "%(created)f", "Class_Name":"%(module)s", "Method_name": "%(funcName)s", "process_id":%(process)d, "message": "%(message)s"}'
glogging.Logger.datefmt = ""
glogging.Logger.access_fmt = '{"AppName": "%(name)s", "logLevel": "%(levelname)s", "Timestamp": "%(created)f","Class_Name":"%(module)s", "Method_name": "%(funcName)s", "process_id":%(process)d, "message": "%(message)s"}'
glogging.Logger.syslog_fmt = '{"AppName": "%(name)s", "logLevel": "%(levelname)s", "Timestamp": "%(created)f","Class_Name":"%(module)s", "Method_name": "%(funcName)s", "process_id":%(process)d, "message": "%(message)s"}'
我希望这会有所帮助,我认为这是重写记录器格式的一种干净方法。
,但是如果您想在日志中添加自定义属性,则可能必须创建一个新的logger类实例,因为该logger类不支持过滤器。并分配记录器实例也无济于事。
答案 4 :(得分:0)
gunicorn
的日志记录问题在于它通常在您的代码之前在同一个 Python 进程中运行,configures logging 自己运行并在您的任何 WSGI 相关代码运行之前记录一些消息,并且您有机会按照自己的方式配置日志记录。幸运的是,如前所述,它的“记录器”(默认为 gunicorn.glogging.Logger
)是可配置的。
$ gunicorn --help | grep logger
--logger-class STRING
The logger you want to use to log events in Gunicorn.
请注意,不建议使用 incremental configuration(即 dictConfig
由 gunicorn
调用一次,然后由您调用),而且可能不是您想要的:
[...] 一旦配置被设置,在运行时任意改变记录器、处理程序、过滤器、格式化程序的对象图并没有令人信服的理由;记录器和处理程序的详细程度可以通过设置级别(以及在记录器的情况下,传播标志)来控制。在多线程环境中,以安全的方式任意更改对象图是有问题的;虽然并非不可能,但其带来的好处不值得增加实施的复杂性。
因此,我建议将 gunicorn
视为 regular library with regards to logging 并完全禁用其日志记录配置(以支持您的应用程序)。
这是一个简单的应用程序,它具有 HTTP 和控制台入口点,两者应具有相同的日志记录配置:
logging_cfg = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'KeyValueFormatter': {
'format': (
'timestamp=%(asctime)s pid=%(process)d '
'loglevel=%(levelname)s msg=%(message)s'
)
},
},
'handlers': {
'console': {
'level': 'INFO',
'class': 'logging.StreamHandler',
'formatter': 'KeyValueFormatter',
}
},
'loggers': {
'gunicorn.access': {
'propagate': True,
},
'gunicorn.error': {
'propagate': True,
},
},
'root': {
'level': 'INFO',
'handlers': ['console'],
}
}
这里是它的实现,app.py
。
import logging.config
from gunicorn import glogging
logger = logging.getLogger(__name__)
def get_result():
logger.info('Calculating result')
return b'Hello world!'
def wsgi_app(environ, start_response):
result = get_result()
start_response('200 OK', [('Content-Type', 'text/html')])
return [result]
def configure_logging():
logging.config.dictConfig(logging_cfg)
class UniformLogger(glogging.Logger):
def setup(self, cfg):
configure_logging()
if __name__ == '__main__':
configure_logging()
print(get_result())
如果你运行 python app.py
你应该得到:
timestamp=2021-07-25 12:09:04,488 pid=4599 loglevel=INFO msg=Calculating result
b'Hello world!'
如果您运行 gunicorn --logger-class=app.UniformLogger app:wsgi_app
后跟 curl localhost:8000
:
timestamp=2021-07-25 12:16:56,892 pid=4874 loglevel=INFO msg=Starting gunicorn 20.1.0
timestamp=2021-07-25 12:16:56,892 pid=4874 loglevel=INFO msg=Listening at: http://127.0.0.1:8000 (4874)
timestamp=2021-07-25 12:16:56,893 pid=4874 loglevel=INFO msg=Using worker: sync
timestamp=2021-07-25 12:16:56,894 pid=4876 loglevel=INFO msg=Booting worker with pid: 4876
timestamp=2021-07-25 12:17:06,926 pid=4876 loglevel=INFO msg=Calculating result