Python日志模块:自定义日志记录器
我在尝试创建一个自定义属性,用于记录日志(比如调用者的类名、模块名等),结果遇到了一个奇怪的错误,提示我在这个过程中创建的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
对象,而其他日志记录器对象则是根据当时的配置创建的。