Python日志模块:自定义日志记录器

9 投票
2 回答
5170 浏览
提问于 2025-04-17 13:08

我在尝试创建一个自定义属性,用于记录日志(比如调用者的类名、模块名等),结果遇到了一个奇怪的错误,提示我在这个过程中创建的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 个回答

2

logging.getLogger()logging.getLogger("") 这两个方法不会返回一个叫 MyLogger 的记录器,因为它们返回的是日志系统的根记录器,具体情况可以参考日志文档

logging.getLogger([name])

返回一个指定名称的记录器,如果没有指定名称,则返回日志层级的根记录器。

所以,按照你设置的记录器:

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

我觉得这和你帖子开头提到的 KeyError 没什么关系。你应该把导致这个异常的代码(test.py)发出来。

6

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

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

也就是说,当这个模块被导入时,默认会创建一个根日志记录器。因此,每次你寻找根日志记录器(传入一个假值,比如空字符串)时,不管有没有调用过 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 对象,而其他日志记录器对象则是根据当时的配置创建的。

撰写回答