Python Logging模块:自定义记录器

9

我正在尝试创建一个用于记录日志的自定义属性(调用者的类名、模块名等),但却遇到了一个奇怪的异常,告诉我在创建LogRecord实例时缺少必要的属性。经过一番测试,我最终得出了以下结论:

import logging

class MyLogger(logging.getLoggerClass()):
    value = None

logging.setLoggerClass(MyLogger)

loggers = [
    logging.getLogger(),
    logging.getLogger(""),
    logging.getLogger("Name")
]

for logger in loggers:
    print(isinstance(logger, MyLogger), hasattr(logger, "value"))

这段看似正确的代码会产生如下结果:
False False
False False
True True

是Bug还是功能?

2个回答

6

查看源代码,我们可以看到以下内容:

root = RootLogger(WARNING)
def getLogger(name=None):
    if name:
        return Logger.manager.getLogger(name)
    else:
        return root

也就是说,当导入该模块时,默认会创建一个根记录器(root logger)。因此,每次查找根记录器时(传递 false 值,例如空字符串),无论是否调用了 logging.setLoggerClass,都将返回 logging.RootLogger 对象。

关于所使用的记录器类,我们可以看到:

_loggerClass = None
def setLoggerClass(klass):
    ...
    _loggerClass = klass

这意味着一个全局变量保存了将来要使用的记录器类。

除此之外,看一下logging.Manager(由logging.getLogger使用),我们可以看到以下内容:

def getLogger(self, name):
    ...
            rv = (self.loggerClass or _loggerClass)(name)

也就是说,如果self.loggerClass没有设置(除非你已经明确设置过它),将使用全局变量中的类。
因此,这是一个特性。根记录器始终是一个logging.RootLogger对象,并且在那个时候基于配置创建其他记录器对象。

2
logging.getLogger()logging.getLogger("") 不会返回一个MyLogger,因为它们返回的是日志层次结构的根记录器,详见logging文档

logging.getLogger([name])

如果指定了名称,则返回具有指定名称的记录器;否则返回层次结构的根记录器。

因此,根据您的设置:

>>> logging.getLogger()
<logging.RootLogger object at 0x7d9450>
>>> logging.getLogger("foo")
<test3.MyLogger object at 0x76d9f0>

我认为这与您在帖子开头提到的 KeyError 无关。您应该发布导致该异常抛出的代码(test.py)。


谢谢您的评论,我已经编辑了问题。 但是我认为这种行为并不是人们所期望的。 - Pastafarianist
为什么它不是你所期望的结果?这正是文档在未指定名称时所说的结果。 - David Robinson
5
尽管文件说明是这样写的,但这个做法还是有些违反直觉。如果为记录器设置一个类,我会期望所有的记录器都拥有这个类,包括根记录器和尚未实例化的记录器,但不包括已经实例化的记录器。 - Pastafarianist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接