日志打印之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__構造函數還需要其它參數來初始化類實例,可以繼續添自定義參數,這些自定義參數被當做關鍵字參數會自動傳遞給構造函數。
一個例子:
"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
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函數參數
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
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功能就是用於控制是否向上遍歷父輩日志打印器,進而控制當前日志打印器是否共享父輩打印器的日志處理器。
