Python 日志打印之logging.config.dictConfig使用总结
日志打印之logging.config.dictConfig使用总结
By:授客 QQ:1033553122
#实践环境
WIN 10
Python 3.6.5
#函数说明
logging.config.dictConfig(config)
dictConfig函数位于logging.config模块,该函数通过字典参数config对logging进行配置。3.2版本新增的函数
##参数说明
config 字典类型,包含以下key:
- version - 表示版本,该键值为从1开始的整数。该key必选,除此之外,其它key都是可选。
- formatters - 日志格式化器,其value值为一个字典,该字典的每个键值对都代表一个Formatter,键值对中,key代表Formatter ID(自定义ID),value为字典,描述如何配置相应的Formatter实例。默认格式为 ‘%(message)s’
- filters - 日志过滤器,其value值为一个字典,该字典的每个键值对都代表一个Filter,键值对中,key代表Filter ID(自定义ID),value为字典,描述如何配置相应的Filter实例。
- handlers - 日志处理器,其value值为一个字典,该字典的每个键值对都代表一个Handler,键值对中,key代表Handler ID(自定义ID),value为字典,描述如何配置相应的Handler实例,包含以下配置key:
- class (必选). 日志处理器类全称
- level (可选). 指定该日志处理器需要处理哪些级别的日志,低于该级别的日志将不被该handler处理。level可以为代表日志级别的整数或者表大写字符串,字符串日志级别和数字日志级别对应关系如下:
CRITICAL = 50
FATAL = CRITICAL
ERROR = 40
WARNING = 30
WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0
下同,不再赘述.
- formatter (可选). 指定该日志处理器使用的日志格式化器
- filters (可选). 制定该日志处理器使用的日志过滤器
# 上述的class配置项的值,可以使用自定义Handler类,此时,如果自定义Handler类的__init__构造函数还需要其它参数来初始化类实例,可以继续添自定义参数,这些自定义参数被当做关键字参数会自动传递给构造函数。
一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | "handlers" : { "console" :{ "class" : "study.MyLogHandler" , "formatter" : "brief" , "level" : "INFO" }, "file" : { "class" : "logging.handlers.RotatingFileHandler" , "formatter" : "precise" , "filename" : "logconfig.log" , "maxBytes" : 1024 , "backupCount" : 3 } } |
id为console的日志处理器被实例化为一个logging.StreamHandler,使用sys.stout作为基础实例流。id为file的日志处理器则被实例化为具有关键字参数filename ='logconfig.log',maxBytes = 1024,backupCount = 3的 logging.handlers.RotatingFileHandler
- loggers - 日志记录器,其value值为一个字典,该字典的每个键值对都代表一个Handler,键值对中,key代表Handler ID,value为字典,描述如何配置相应的Logger实例,包含以下配置key:
- level (可选). 指定该日志记录器需要记录哪些级别的日志,低于该级别的日志将不被该logger记录。
- propagate (可选). 指定该日志记录器的propagation配置,为布尔值,即True 或 False,用于控制是否向上遍历父辈日志打印器,进而控制当前日志打印器是否共享父辈打印器的日志处理器。True,向上遍历,否则不向上遍历。
- filters (可选). 指定该日志记录器使用的日志过滤器
- handlers (可选). 制定该日志记录器使用的日志处理器
- root - root logger配置。除了不支持propagate配置项以外,该配置的处理过程同处理其它logger的配置一样,配置规则也一样
- incremental - 用于判断该config配置是否解释为现有配置的增量配置,还是覆盖原有配置。默认为False,即使用现有fileConfig()API使用的相同语义替换现有配置
- disable_existing_loggers - 其value为布尔值,表示是否禁用现有日志记录器(root logger除外),默认值为True,即禁用。如果incremental 键值为True,则忽略该配置项
#代码示例1
study.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | study.py #!/usr/bin/env python # -*- coding:utf-8 -*- ''' @CreateTime: 2020/12/29 14:08 @Author : shouke ''' import logging import logging.config LOGGING_CONFIG = { "version" : 1 , "formatters" : { "default" : { 'format' : '%(asctime)s %(filename)s %(lineno)s %(levelname)s %(message)s' , }, "plain" : { "format" : "%(message)s" , }, }, "handlers" : { "console" : { "class" : "logging.StreamHandler" , "level" : "INFO" , "formatter" : "default" , }, "console_plain" : { "class" : "logging.StreamHandler" , "level" :logging.INFO, "formatter" : "plain" }, "file" :{ "class" : "logging.FileHandler" , "level" : 20 , "filename" : "./log.txt" , "formatter" : "default" , } }, "loggers" : { "console_logger" : { "handlers" : [ "console" ], "level" : "INFO" , "propagate" : False , }, "console_plain_logger" : { "handlers" : [ "console_plain" ], "level" : "DEBUG" , "propagate" : False , }, "file_logger" :{ "handlers" : [ "file" ], "level" : "INFO" , "propagate" : False , } }, "disable_existing_loggers" : True , } # 运行测试 logging.config.dictConfig(LOGGING_CONFIG) logger = logging.getLogger( "console_logger" ) logger.debug( 'debug message' ) logger.info( 'info message' ) logger.warn( 'warning message' ) logger.error( 'error message' ) logger.critical( 'critical message' ) |
运行study.py,结果输出如下
2021-01-09 10:01:59,123 study.py 66 INFO info message
2021-01-09 10:01:59,123 study.py 67 WARNING warning message
2021-01-09 10:01:59,123 study.py 68 ERROR error message
2021-01-09 10:01:59,123 study.py 69 CRITICAL critical message
#代码示例2
基于代码示例1,修改LOGGING_CONFIG及getLogger函数参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | LOGGING_CONFIG = { "version" : 1 , "formatters" : { "default" : { 'format' : '%(asctime)s %(filename)s %(lineno)s %(levelname)s %(message)s' , } }, "handlers" : { "console" : { "class" : "logging.StreamHandler" , "level" : "INFO" , "formatter" : "default" , } }, "disable_existing_loggers" : True , "root" : { "handlers" : [ "console" ], "level" : "DEBUG" }, } # 运行测试 logging.config.dictConfig(LOGGING_CONFIG) logger = logging.getLogger( "root" ) logger.debug( 'debug message' ) logger.info( 'info message' ) logger.warn( 'warning message' ) logger.error( 'error message' ) logger.critical( 'critical message' ) |
运行study.py,结果输出如下
2021-01-09 10:33:03,456 study.py 38 INFO info message
2021-01-09 10:33:03,456 study.py 39 WARNING warning message
2021-01-09 10:33:03,456 study.py 40 ERROR error message
2021-01-09 10:33:03,456 study.py 41 CRITICAL critical message
# 源码的角度分析propagate配置项
Logger类,位于logging/__init__.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | class Logger(Filterer): #...略 def debug( self , msg, * args, * * kwargs): """ Log 'msg % args' with severity 'DEBUG'. To pass exception information, use the keyword argument exc_info with a true value, e.g. logger.debug("Houston, we have a %s", "thorny problem", exc_info=1) """ if self .isEnabledFor(DEBUG): self ._log(DEBUG, msg, args, * * kwargs) def info( self , msg, * args, * * kwargs): """ Log 'msg % args' with severity 'INFO'. To pass exception information, use the keyword argument exc_info with a true value, e.g. logger.info("Houston, we have a %s", "interesting problem", exc_info=1) """ if self .isEnabledFor(INFO): self ._log(INFO, msg, args, * * kwargs) #...略 def _log( self , level, msg, args, exc_info = None , extra = None , stack_info = False ): """ Low-level logging routine which creates a LogRecord and then calls all the handlers of this logger to handle the record. """ sinfo = None if _srcfile: #IronPython doesn't track Python frames, so findCaller raises an #exception on some versions of IronPython. We trap it here so that #IronPython can use logging. try : fn, lno, func, sinfo = self .findCaller(stack_info) except ValueError: # pragma: no cover fn, lno, func = "(unknown file)" , 0 , "(unknown function)" else : # pragma: no cover fn, lno, func = "(unknown file)" , 0 , "(unknown function)" if exc_info: if isinstance (exc_info, BaseException): exc_info = ( type (exc_info), exc_info, exc_info.__traceback__) elif not isinstance (exc_info, tuple ): exc_info = sys.exc_info() record = self .makeRecord( self .name, level, fn, lno, msg, args, exc_info, func, extra, sinfo) self .handle(record) def handle( self , record): """ Call the handlers for the specified record. This method is used for unpickled records received from a socket, as well as those created locally. Logger-level filtering is applied. """ if ( not self .disabled) and self . filter (record): self .callHandlers(record) def hasHandlers( self ): """ See if this logger has any handlers configured. Loop through all handlers for this logger and its parents in the logger hierarchy. Return True if a handler was found, else False. Stop searching up the hierarchy whenever a logger with the "propagate" attribute set to zero is found - that will be the last logger which is checked for the existence of handlers. """ c = self rv = False while c: if c.handlers: rv = True break if not c.propagate: break else : c = c.parent return rv def callHandlers( self , record): """ Pass a record to all relevant handlers. Loop through all handlers for this logger and its parents in the logger hierarchy. If no handler was found, output a one-off error message to sys.stderr. Stop searching up the hierarchy whenever a logger with the "propagate" attribute set to zero is found - that will be the last logger whose handlers are called. """ c = self found = 0 while c: for hdlr in c.handlers: found = found + 1 if record.levelno > = hdlr.level: hdlr.handle(record) if not c.propagate: c = None #break out else : c = c.parent if (found = = 0 ): if lastResort: if record.levelno > = lastResort.level: lastResort.handle(record) elif raiseExceptions and not self .manager.emittedNoHandlerWarning: sys.stderr.write( "No handlers could be found for logger" " \"%s\"\n" % self .name) self .manager.emittedNoHandlerWarning = True |
默认的,当通过logger.debug,logger.info的方式打印日志时,会先判断对应日志级别是否开启,如果开启,则调用logger实例的_log方法,接着经过一连串的函数调用(self._log() -> self.handle -> self.callHandlers),如上,self.callHandlers中,会先遍历当前日志打印器自身的所有日志处理器,处理日志消息,然后判断propagate属性是否为True,如果为True,则获取上级日志打印器,继续遍历其日志处理器,处理消息,否则不遍历上级
另外,查看hasHandlers函数可知,判断一个logger是否有日志处理器,也用到了propagate,如果propagate为True,则遍历父级日志打印器,看其是否存在日志处理器,如果父级或者父辈日志打印器存在日志处理器,则判断该logger拥有日志处理器。
由此可见,propagate功能就是用于控制是否向上遍历父辈日志打印器,进而控制当前日志打印器是否共享父辈打印器的日志处理器。
作者:授客
微信/QQ:1033553122
全国软件测试QQ交流群:7156436
Git地址:https://gitee.com/ishouke
友情提示:限于时间仓促,文中可能存在错误,欢迎指正、评论!
作者五行缺钱,如果觉得文章对您有帮助,请扫描下边的二维码打赏作者,金额随意,您的支持将是我继续创作的源动力,打赏后如有任何疑问,请联系我!!!
微信打赏
支付宝打赏 全国软件测试交流QQ群
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库