Python日志配置策略

1 三种情况下都能实现日志打印:

  1. 被库 A 调用,使用库 A 的日志配置。
  2. 被库 B 调用,使用库 B 的日志配置。
  3. 独立运行,使用自己的日志配置。

需要实现一个灵活的日志配置策略,使得日志记录器可以根据调用者或运行环境自动调整。

实现方案

  • 定义模块级别的日志记录器:在 Python 文件中定义一个模块级别的日志记录器。
  • 检查已有的处理器:在模块级别日志记录器配置时检查是否已有处理器(即,是否已经由库 A 或库 B 配置了日志)。
  • 独立运行时配置默认日志:如果没有已有的处理器,则添加默认的日志配置(如输出到控制台)。

示例代码

假设这个 Python 文件名为 my_module.py,以下是如何实现上述功能的完整代码:

python 复制代码
# my_module.py

import logging
import sys

# 创建模块级别的日志记录器
logger = logging.getLogger(__name__)

# 检查是否已有处理器
if not logger.hasHandlers():
    # 如果没有处理器,添加一个默认的控制台处理器
    logger.setLevel(logging.DEBUG)
    console_handler = logging.StreamHandler(sys.stdout)
    console_handler.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    console_handler.setFormatter(formatter)
    logger.addHandler(console_handler)

# 模块功能示例
def do_something():
    logger.debug("Debug message from my_module")
    logger.info("Info message from my_module")
    logger.warning("Warning message from my_module")
    logger.error("Error message from my_module")
    logger.critical("Critical message from my_module")

# 检查是否作为脚本运行
if __name__ == '__main__':
    do_something()

详细说明

  1. 模块级别的日志记录器

    • 使用 logging.getLogger(__name__) 获取一个模块级别的日志记录器,__name__ 保证了每个模块(文件)都有自己的日志记录器名称。
    • 这使得日志记录器的名称会根据模块名称变化,如 my_module
  2. 检查已有处理器

    • 使用 logger.hasHandlers() 检查是否已有处理器附加到日志记录器。
    • 如果没有处理器(即此时没有任何库配置过该模块的日志),则添加默认的控制台处理器。这意味着在独立运行时,会使用这个默认处理器。
  3. 独立运行时配置

    • 如果 my_module.py 作为主脚本运行(即 __name__ == '__main__'),将调用 do_something(),触发日志输出。
    • 当被其他库调用时,假设这些库已经配置了日志处理器,则不会添加新的处理器,使用调用者(库 A 或库 B)的日志配置。

调用库 A 和库 B 的示例

假设有两个库,库 A 和库 B,各自配置了自己的日志记录器,然后调用 my_module

python 复制代码
# library_a.py

import logging
import my_module

# 配置库 A 的日志记录器
logger = logging.getLogger('library_a')
logger.setLevel(logging.DEBUG)
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter('%(asctime)s - library_a - %(levelname)s - %(message)s'))
logger.addHandler(console_handler)

# 调用 my_module
my_module.do_something()
python 复制代码
# library_b.py

import logging
import my_module

# 配置库 B 的日志记录器
logger = logging.getLogger('library_b')
logger.setLevel(logging.DEBUG)
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter('%(asctime)s - library_b - %(levelname)s - %(message)s'))
logger.addHandler(console_handler)

# 调用 my_module
my_module.do_something()

验证功能

  1. 独立运行 my_module.py

    • 直接运行 python my_module.py,日志将输出到控制台,使用 my_module 自己的配置。
  2. 通过库 A 调用 my_module

    • 运行 python library_a.py,日志将使用库 A 的配置。
  3. 通过库 B 调用 my_module

    • 运行 python library_b.py,日志将使用库 B 的配置。

logging.getLogger('AITestCaseGenerator')logging.getLogger('AITestCaseGenerator.ll') 获取的是具有层级关系的日志记录器,它们在日志管理和配置上有不同的作用。下面解释这两个日志记录器的区别:

2 日志记录器层级关系

在 Python 的 logging 模块中,日志记录器(logger)是分层级的。日志记录器的层级结构类似于文件系统的目录结构,这使得日志记录器可以继承父级日志记录器的配置和处理器。

获取日志记录器
  • logging.getLogger('AITestCaseGenerator'):

    • 获取名称为 'AITestCaseGenerator' 的日志记录器。
    • 它是该层级的主日志记录器。
  • logging.getLogger('AITestCaseGenerator.ll'):

    • 获取名称为 'AITestCaseGenerator.ll' 的日志记录器。
    • 这是 'AITestCaseGenerator' 日志记录器的子级,继承其配置。

继承关系

子日志记录器会继承父日志记录器的处理器和级别,除非子日志记录器自己设置了处理器或日志级别。

示例
python 复制代码
import logging

# 获取父日志记录器
parent_logger = logging.getLogger('AITestCaseGenerator')
parent_logger.setLevel(logging.INFO)

# 添加一个处理器到父日志记录器
parent_handler = logging.StreamHandler()
parent_handler.setFormatter(logging.Formatter('%(name)s - %(levelname)s - %(message)s'))
parent_logger.addHandler(parent_handler)

# 获取子日志记录器
child_logger = logging.getLogger('AITestCaseGenerator.ll')

# 记录日志
parent_logger.info("This is a message from the parent logger.")
child_logger.info("This is a message from the child logger.")
child_logger.debug("This is a debug message from the child logger.")

输出

复制代码
AITestCaseGenerator - INFO - This is a message from the parent logger.
AITestCaseGenerator.ll - INFO - This is a message from the child logger.
  • 解释
    • parent_logger 打印的消息显示其名称和日志级别。
    • child_logger 也显示其名称和日志级别,并继承了父日志记录器的处理器和日志级别,因此它的信息消息也被打印。
    • child_loggerdebug 消息没有被打印,因为继承的日志级别为 INFO

如何配置和使用

  1. 父级日志记录器配置

    • 设置日志级别和处理器,将影响其所有子日志记录器,除非子日志记录器自己有设置。
  2. 子级日志记录器继承

    • 如果子级日志记录器没有自己设置处理器和级别,它将使用父级的处理器和级别。
    • 可以为子级日志记录器添加额外的处理器或更改级别,覆盖继承的配置。
示例:为子级日志记录器添加自己的处理器
python 复制代码
child_handler = logging.StreamHandler()
child_handler.setFormatter(logging.Formatter('%(name)s - %(levelname)s - %(message)s'))
child_logger.addHandler(child_handler)
child_logger.setLevel(logging.DEBUG)

现在,child_logger 将使用自己的处理器,并可以打印 DEBUG 级别的日志。

实际应用中的意义

  • 模块化日志管理

    • 使用层级日志记录器,可以在大型项目中按模块或功能分区管理日志记录器。
    • 每个模块可以有自己的日志记录器,同时继承项目级的日志配置。
  • 灵活配置

    • 通过设置父级日志记录器的配置,确保统一的日志格式和级别管理。
    • 通过设置子级日志记录器的配置,提供更精细的控制。
相关推荐
databook8 小时前
Manim实现闪光轨迹特效
后端·python·动效
Juchecar10 小时前
解惑:NumPy 中 ndarray.ndim 到底是什么?
python
用户83562907805110 小时前
Python 删除 Excel 工作表中的空白行列
后端·python
Json_10 小时前
使用python-fastApi框架开发一个学校宿舍管理系统-前后端分离项目
后端·python·fastapi
数据智能老司机17 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机18 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机18 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机18 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
c8i18 小时前
drf初步梳理
python·django
每日AI新事件18 小时前
python的异步函数
python