上一篇pythonlogging日志模块一万字详解,深入浅出地讲解了日志的基本原理和用法。但是还有一些内容没有涉及到,所以这篇文章是对上一篇文章的补充。希望这两篇文章能够帮助大家全面了解日志模块的使用,并在项目中游刃有余地使用日志。如果您还没有阅读上一篇文章,建议先阅读上一篇文章。1、为什么子logger不设置日志级别也能输出?如果未在记录器上显式设置级别,则其父记录器的级别将用作其有效级别。如果父记录器也没有设置级别,依此类推,则搜索父记录器的父记录器,直到找到具有明确设置的级别的记录器。默认的根记录器是WARNING级别的info("msg")输出msg这里我没有给child设置日志级别,他会从parentlogger中找日志级别,所以child也可以输出info级别的日志。logger的继承关系可以参考第一篇2.为什么有时候日志会输出两次?看下面的例子:importlogging#初始化日志,并设置日志级别(root设置为DEBUG级别,关联StreamHandler,设置BASIC_FORMAT格式)logging.basicConfig(level=logging.DEBUG)#Definerootloggerroot=logging.getLogger()#Definechildloggerchild=logging.getLogger("child")console_handler=logging.StreamHandler()#BindtochildProcessorchild.addHandler(console_handler)#recordaninfologchild.info("childinfo")输出childinfoINFO:child:childinfo代码明明只记录了一条日志,却输出了两次,而且两次日志的格式不一样,这是因为childlogger添加了一个叫console_handler的handler,而rootlogger默认也有自己的handler(也是aStreamHandlerinstance)print(root.handlers)#[(NOTSET)>]根据python中log模块的处理机制,childlogger记录的消息会自动传播到关联的processor父记录器。所以在这个例如,child记录的消息除了发送到自己的handler之外,还会传播到rootlogger的handler,因此最终输出了两次。流程图如下logging-flow.png如果不希望子logger将记录的消息传播到父logger,可以设置logger属性propagate为False关闭传播。child.propagate=False这样,最终输出到终端的日志只是child自己的processor输出的记录。childinfo配置处理器的最佳实践是为顶级记录器配置处理器,然后根据需要创建子记录器。因为记录最终会传播到父logger单独记录器。getLogger("parent.child")child.info("msg")对日志流程处理,python官方文档画了比较详细的流程图,第一次可以参考logging_flow.png,可能会有点晕,不过先看看我画的图再看看这张图,你就明白了。为了简化流程,我省略了filter和不断循环寻找parentlogger的过程。3、为什么我的pycharm中的日志输出是红色的?不知道你pycharm输出的日志是info信息还是error信息。无论如何,它是红色的。乍一看,我觉得整个屏幕都是错误的。将以下代码放入Pycharm中运行,看看效果:importlogginglogging.basicConfig(level=logging.DEBUG)logging.info("hello")这是因为在使用rootlogger记录日志时,默认配置的handler是一个流处理器。我们打开StreamHandler类的源码StreamHandler(Handler):"""Ahandlerclasswritesloggingrecords,appropriatelyformatted,toastream.注意这个类不关闭stream,assys.stdoutorsys.stderrmmaybeused.""terminator='\n'def__init__(self,stream=None):"the""handler.InitializeIfstreamisnotspecified,sys.stderrisused."""Handler.__init__(self)ifstreamisNone:stream=sys.stderrself.stream=stream初始化这个Handler时,会收到一个stream参数,如果不传,默认系统标准错误流会使用(sys.stderr)输出,pycharm将错误流输出的字体样式渲染成红色,如果换成sys.stdout,输出就不再是红色了。importloggingimportsyshandler=logging.StreamHandler(stream=sys.stdout)logging.basicConfig(level=logging.DEBUG,handlers=[handler])#或者指定流参数#logging.basicConfig(level=logging.DEBUG,stream=sys.stdout)logging.info("hello")4.如何生成以日期和时间命名的日志?在实际应用中,我们会对日志进行归档存储,每天生成一条日志。哪天出问题了,定位也方便,直接找到当天的日志文件就可以分析了。我们只需要向记录器添加一个TimedRotatingFileHandler处理器。file_handler=TimedRotatingFileHandler(''logs/api.log'),when="D",interval=1,backupCount=10,encoding="UTF-8",delay=False,utc=True)formatter=logging.Formatter('%(asctime)s-%(name)s-%(levelname)s-%(message)s')file_handler.setFormatter(formatter)file_handler.setLevel(logging.INFO)logger.addHandler(file_handler)5.为什么我的日志配置没有生效?可能和程序的加载顺序有关,看一个例子.INFO)logger.info("hello2")和上面的代码一样,最后只输出了hello2,但是在实际场景中,代码并没有这么简单。通常,日志框架配置在模块a中的一个函数中初始化,在模块b外层创建一个名为logger_b的logger,然后在a中导入b模块。这个时候日志配置还没有初始化,最后logger_b的配置变成了默认配置。所以有可能是日志没有生效。因此,最好的做法是尽早初始化日志配置。