简体   繁体   English

自定义 Python 日志格式化程序适用于 fileConfig 但不适用于 dictConfig?

[英]Custom Python logging formatter works with fileConfig but not with dictConfig?

I've been pulling my hair out for hours trying to figure out why these two logging configs, which as far as I can tell should be 100% identical, are not resulting in the same behavior:我已经花了几个小时试图弄清楚为什么这两个日志配置,据我所知应该是 100% 相同的,不会导致相同的行为:

LOGGING_CONFIG = {
    'version': 1,
    'disable_existing_loggers': False,
    'root': {
        'handlers': ['console'],
        'level': 'INFO',
    },
    'gunicorn.access': {
        'level': 'INFO',
        'handlers': ['access_console'],
        'propagate': False,
        'qualname': 'gunicorn.access',
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'stream': 'ext://sys.stderr',
            'formatter': 'syslog',
        },
        'access_console': {
            'class': 'logging.StreamHandler',
            'stream': 'ext://sys.stdout',
            'formatter': 'docker_access',
        },
    },
    'formatters': {
        'syslog': {
            '()': 'citi.logging.DockerFormatter',
            'fmt': 'SYSLOG %(asctime)s [%(levelname)s] %(name)s: %(message)s',
        },
        'docker_access': {
            '()': 'citi.logging.DockerFormatter',
            'fmt': 'GUNICORN_ACCESS %(asctime)s %(message)s',
        },
    },
}
[loggers]
keys=root,gunicorn.access

[handlers]
keys=console,access_console

[formatters]
keys=syslog,docker_access

[logger_root]
level=INFO
handlers=console

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

[handler_console]
class=StreamHandler
formatter=syslog
args=(sys.stderr, )

[handler_access_console]
class=StreamHandler
formatter=docker_access
args=(sys.stdout, )

[formatter_syslog]
class=citi.logging.DockerFormatter
format=SYSLOG %(asctime)s [%(levelname)s] %(name)s: %(message)s

[formatter_docker_access]
class=citi.logging.DockerFormatter
format=GUNICORN_ACCESS %(asctime)s %(message)s

When I use logging.config.fileConfig and pass it the file that contains the second code block, it works fine.当我使用logging.config.fileConfig并将包含第二个代码块的文件传递给它时,它工作正常。 The gunicorn.access logger changes its formatter to the custom DockerFormatter class (which is a simple subclass of logging.Formatter ) with the specified format string. gunicorn.access记录器将其格式化程序更改为具有指定format字符串的自定义DockerFormatter类(它是logging.Formatter的简单子类)。 The access logs look like this:访问日志如下所示:

GUNICORN_ACCESS 2021-07-16 18:59:56,454 172.18.0.1 - - "GET /citi/server-status HTTP/1.0" 200 11 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36" 11766

But when I use logging.config.dictConfig and pass it the dict at the top, everything except the custom formatter works.但是当我使用logging.config.dictConfig并将它传递给顶部的 dict 时,除了自定义格式化程序之外的所有内容都可以工作。 The formatter appears to remain set to the original defaults provided by Gunicorn (maybe??).格式化程序似乎仍然设置为 Gunicorn 提供的原始默认值(也许??)。 The output is:输出是:

172.18.0.1 - - "GET /citi/server-status HTTP/1.0" 200 11 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36" 7549

By using the logging_tree package, I can tell that gunicorn.access 's formatter is some sort of default when using dictConfig .通过使用logging_tree包,我可以知道gunicorn.access的格式化程序在使用dictConfig时是某种默认dictConfig It should look like this:应该是这样的:

   o<--"gunicorn"
   |   Level NOTSET so inherits level INFO
   |   |
   |   o   "gunicorn.access"
   |   |   Level INFO
   |   |   Propagate OFF
   |   |   Handler Stream <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>
   |   |     Formatter <citi.logging.DockerFormatter object at 0x7f68240e7d90>

But instead it looks like this:但它看起来像这样:

   o<--"gunicorn"
   |   Level NOTSET so inherits level INFO
   |   |
   |   o   "gunicorn.access"
   |   |   Level INFO
   |   |   Propagate OFF
   |   |   Handler Stream <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>
   |   |     Formatter fmt='%(message)s' datefmt=None

The only difference I can detect in terms of how the DockerFormatter class is called is that the fileConfig form calls it with positional arguments, while dictConfig calls it with (correct) keyword arguments.DockerFormatter类的调用方式方面,我可以检测到的唯一区别是fileConfig表单使用位置参数调用它,而dictConfig使用(正确的)关键字参数调用它。

What appears to be happening is that the formatter is being constructed correctly, but not assigned correctly.似乎正在发生的是格式化程序正在正确构造,但没有正确分配 And I can't figure out WHY.我不知道为什么。

The most infuriating thing is that I've actually simplified my logging config quite a bit.最令人气愤的是,我实际上已经大大简化了我的日志配置。 In my full config, I set up a custom structlog -based formatter, configured in exactly the same manner as syslog and docker_access are with DockerFormatter , but it DOES work.在我的完整配置中,我设置了一个基于structlog的自定义格式化程序,其配置方式与syslogdocker_accessDockerFormatter配置方式DockerFormatter ,但它确实有效。 I am very frustrated.沮丧。 >_< >_<

And of course, my final, last ditch effort before leaving the office for the day (several hours late...) actually works.当然,我在离开办公室前的最后一次努力(迟到了几个小时......)确实有效。 Turns out I was right about Gunicorn's default settings staying in place, because I had completely missed the absolutely essential 'loggers' subdict in my dictConfig dictionary.事实证明,我对 Gunicorn 的默认设置保持原样是正确的,因为我完全错过了 dictConfig 字典中绝对必要的'loggers'子词。

It's supposed to look this THIS:它应该是这样的:

LOGGING_CONFIG = {
    'version': 1,
    'disable_existing_loggers': False,
    'root': {
        'handlers': ['console'],
        'level': 'INFO',
    },
    'loggers' {
        'gunicorn.access': {
            'level': 'INFO',
            'handlers': ['access_console'],
            'propagate': False,
            'qualname': 'gunicorn.access',
        },
    }
   ...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM