文章目录

  • 一. 快速开始
  • 二. Logger
  • RootLogger
  • 三. Handler
  • 缺省handler
  • 不同的输出流位置
  • 四. Formatter
  • 五. 多模块logger如何组织
  • 例子
  • 六. 如何查看三方包的日志
  • 参考

一. 快速开始

官方文档大而全
logging 是 python 自带的 日志模块.

import logging
logging.basicConfig(format='%(asctime)s - [%(levelname)s] - %(message)s',level=logging.INFO)
logging.info("hello")
"""
2019-05-26 20:22:36,579 - [INFO] - hello
"""

使用步骤通常为:
创建 logger -> 创建 handler 并定义 formatter -> 给 logger 添加 handler.

当使用默认全局 logger 时, 设置项为:

  • logging.basicConfig(**kwargs)用来设置想要的输出效果.
  • format
    %(asctime)s - %(name)s - %(filename)s:%(lineno)s - [%(levelname)s] - %(message)s
  • level
    日志输出级别阈值. 默认为 warning. 级别采用整数标识, DEBUG=10, INFO=20 .
  • stream
    默认是 标准错误流sys.stderr(控制台显示为砖红色), 可以改为标准输出流 sys.stdout(标准黑色).

二. Logger

  • logging.getLogger(name=__name__) 不传参数时, 会返回默认的 root logger, level = WARNING.
  • logging.info(msg, *args, **kwargs) msg中有占位符, args参数依次与之对应.
logger.log('hi %s, you are no.%d',yichu, 321)

py3后, 支持 f"xx{var}xx" 的写法, 可替代上行写法.

RootLogger

存在一个全局的默认logger, 是一个 RootLogger 实例, 直接 logging.info() 时就会使用该logger.

  • class RootLogger(Logger), 可以看到是 Logger 子类.

其自带一个 StreamHandler 实例, 其级别是 NOTSET = 0, 对应标准错误流.

三. Handler

如果说 logger 是记录员, 负责记录内容, handler 则用于明确 记录位置及格式.

handler 也有 logLevel, 若 logger 级别低于 handler 级别, 照样不会输出.

pytest中的main收集不到用例 pytest logging不能用_logging


图: 附一张官网的图, 较直观

  • logging.Handler.setFormatter(self, fmt:Formatter)

缺省handler

一个 logger 可以绑定若干个 handler, 当不分配时, 会递归查找 父logger, 用它的 handler.

# 如果当前 logger 没有对应的 handler, 那么就递归用 父类logger对象的 handler
while c:
    for hdlr in c.handlers:
        if record.levelno >= hdlr.level:
            hdlr.handle(record)
    c = c.parent

不同的输出流位置

  • 控制台handler
    logging.StreamHandler()
  • 文件handler
    logging.FileHandler('xx_log.txt',, encoding="utf-8") 日志文件可以由 logging 包动态创建, 但其所在目录必需保证已经存在.
    注意utf避免中文乱码。默认是追加模式.

四. Formatter

formatter = logging.Formatter(format:str), 常用变量有:

  • filename, 源文件名
  • lineno, 源文件内的行号
  • name, logger name
  • levelname, 大写的日志级别, IFO, DEBUG 等
  • message, 日志正文

常用搭配:

  • '%(name)s - [%(levelname)s] - %(message)s'
  • '%(filename)s:%(lineno)s: %(name)s - [%(levelname)s] - %(message)s'

五. 多模块logger如何组织

项目开发中, 自己会有若干个模块,记为 self_A,self_B; 同时还有大量三方包, 如 numba, tensorflow.
那么,
Q:如何给不同的模块设置不同的输出级别, 有的放矢 地打日志呢?
只建 logger 和定义level是不够的, 还要配上各自的 handler, 见下:

例子

import logging

con_han = logging.StreamHandler()
con_han.setLevel(logging.DEBUG)
formatter=logging.Formatter('%(name)s - [%(levelname)s] - %(message)s')
con_han.setFormatter(formatter)

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger.addHandler(con_han)

logger.debug("I'm debug")
"""
__main__ - [DEBUG] - I'm debug
"""

六. 如何查看三方包的日志

以 shap 包为例, 创建 logger 并记录不同等级的日志.

log = logging.getLogger('shap')
log.info("num_full_subsets = {0}".format(num_full_subsets))
log.debug("samples_left = {0}".format(samples_left))

因为 默认级别是 warning, 所以信息出不来, 那就需要下面的代码啦:

con_han = logging.StreamHandler()
con_han.setLevel(logging.DEBUG)

shap_logger = logging.getLogger('shap')
shap_logger.setLevel(logging.DEBUG)
shap_logger.addHandler(con_han)

参考

  1. 官方doc, logging