Python logging未配置basicConfig时低级别日志不输出的原因
Python logging 子日志器受根日志器影响的核心原理
该现象来自logging模块的两个核心设计规则:
- 日志器层级与事件传播机制
所有通过logging.getLogger(name)创建的自定义日志器,都存在树形层级关系:没有命名点分隔的日志器(比如foo)直接是根日志器(root logger)的子节点,带分隔符的(比如foo.bar)是对应父命名日志器的子节点。默认情况下子日志器的propagate属性为True,日志事件产生后,首先会在当前日志器做级别校验:如果当前日志器显式设置了级别,就直接用该级别做阈值判断;如果当前日志器级别为默认的NOTSET,就会沿着父链向上查找第一个显式设置了级别的祖先日志器,用它的级别做阈值。校验通过后,事件会先交给当前日志器绑定的所有处理器处理,之后逐层向上传递给所有父节点的处理器处理,传递过程中父节点不会重复做日志器级别校验,直接将事件交给自身绑定的处理器。 - 无处理器时的兜底逻辑
自定义日志器默认不会绑定任何处理器。如果日志事件沿着父链向上传递直到根日志器,全程没有找到任何绑定的处理器,Python 3.2+版本会触发内置的兜底逻辑:调用全局的lastResort处理器处理事件。这个兜底处理器默认绑定stderr输出,且仅处理WARNING及以上级别的日志,低于这个级别的日志会被直接丢弃。
对应两段代码的具体解释
第一段代码无法输出test的原因
logger = logging.getLogger('foo') logger.setLevel(logging.DEBUG) logger.debug('test')
- 你给
foo日志器设置了DEBUG级别,debug('test')调用时首先通过了当前日志器的级别校验; foo日志器本身没有绑定任何处理器,事件向上传递到根日志器;- 此时你没有调用过
logging.basicConfig(),根日志器也没有绑定任何处理器,事件最终走到lastResort兜底处理器; - 兜底处理器仅处理WARNING及以上级别日志,DEBUG级别的事件直接被丢弃,因此没有输出。
调用logging.basicConfig()后生效的原因
logging.basicConfig()的核心作用就是给根日志器做初始化配置:默认给根日志器添加一个输出到stderr的StreamHandler,配置基础日志格式,这个处理器的默认级别为NOTSET(接收所有级别的日志事件),同时会将根日志器的默认级别设置为WARNING。
调用该方法后,根日志器有了正式绑定的处理器,foo日志器传递上来的DEBUG级事件不会被根日志器的级别拦截(传播过程不校验父节点日志器级别),会直接被根上的处理器正常处理输出,不会再走到兜底逻辑,因此可以正常打印test。
注意:
basicConfig()有一个幂等判断:如果根日志器已经存在绑定的处理器,调用它不会做任何修改。如果在调用basicConfig()之前直接调用了logging.info()/logging.warning()这类根日志器的快捷输出方法,会自动给根日志器添加默认级别为WARNING的处理器,后续再调用basicConfig()修改配置就会不生效。
第二段代码不需要调用basicConfig()的原因
logger = logging.getLogger('my_logger') f_handler = logging.StreamHandler() logger.addHandler(f_handler) logger.setLevel(logging.DEBUG) logger.info('test')
你给my_logger日志器本身手动绑定了独立的StreamHandler,日志事件在当前日志器层就有可处理的处理器,不需要向上传递到根日志器找处理器,因此无论根日志器有没有配置,都可以正常输出。
内容的提问来源于stack exchange,提问作者meg hidey
相关产品推荐
相关产品推荐

