Python 日志打印之logging.config.dictConfig使用总结

简介: Python 日志打印之logging.config.dictConfig使用总结

日志打印之logging.config.dictConfig使用总结


#实践环境

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功能就是用于控制是否向上遍历父辈日志打印器,进而控制当前日志打印器是否共享父辈打印器的日志处理器。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
3天前
|
C++ 开发者 Python
实现Python日志点击跳转到代码位置的方法
本文介绍了如何在Python日志中实现点击跳转到代码位置的功能,以提升调试效率。通过结合`logging`模块的`findCaller()`方法记录代码位置信息,并使用支持点击跳转的日志查看工具(如VS Code、PyCharm),开发者可以从日志直接点击链接定位到出错代码,加快问题排查。
12 2
|
3天前
|
监控 Python
Python中精通异常日志记录
Python中精通异常日志记录
11 0
|
3天前
|
SQL DataWorks 安全
DataWorks产品使用合集之DataWorks资源里python运行时候,查看中途打印日志如何解决
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
26 0
|
3天前
|
监控 安全 中间件
Python 日志处理详解:从基础到实战
【2月更文挑战第5天】Python 日志处理详解:从基础到实战
73 2
|
3天前
|
消息中间件 测试技术 Python
Python使用多线程解析超大日志文件
Python使用多线程解析超大日志文件
92 0
|
3天前
|
存储 数据库 Python
Loguru:Python中强大的日志库
Loguru:Python中强大的日志库
126 2
|
3天前
|
存储 安全 算法
Python如何在打印日志时隐藏明文密码?
Python如何在打印日志时隐藏明文密码?
99 0
|
3天前
|
监控 Python
Python生成日志文件
Python生成日志文件
31 0
|
3天前
|
监控 IDE 开发工具
Python中的日志记录与调试技巧
Python中的日志记录与调试技巧
|
3天前
|
存储 BI 网络安全
正在等待继续编辑 - Python - 基础知识专题 - 配置文件与日志管理
正在等待继续编辑 - Python - 基础知识专题 - 配置文件与日志管理
24 0