1 三种情况下都能实现日志打印:
- 被库 A 调用,使用库 A 的日志配置。
- 被库 B 调用,使用库 B 的日志配置。
- 独立运行,使用自己的日志配置。
需要实现一个灵活的日志配置策略,使得日志记录器可以根据调用者或运行环境自动调整。
实现方案
- 定义模块级别的日志记录器:在 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()
详细说明
-
模块级别的日志记录器:
- 使用
logging.getLogger(__name__)
获取一个模块级别的日志记录器,__name__
保证了每个模块(文件)都有自己的日志记录器名称。 - 这使得日志记录器的名称会根据模块名称变化,如
my_module
。
- 使用
-
检查已有处理器:
- 使用
logger.hasHandlers()
检查是否已有处理器附加到日志记录器。 - 如果没有处理器(即此时没有任何库配置过该模块的日志),则添加默认的控制台处理器。这意味着在独立运行时,会使用这个默认处理器。
- 使用
-
独立运行时配置:
- 如果
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()
验证功能
-
独立运行
my_module.py
:- 直接运行
python my_module.py
,日志将输出到控制台,使用my_module
自己的配置。
- 直接运行
-
通过库 A 调用
my_module
:- 运行
python library_a.py
,日志将使用库 A 的配置。
- 运行
-
通过库 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_logger
的debug
消息没有被打印,因为继承的日志级别为INFO
。
如何配置和使用
-
父级日志记录器配置:
- 设置日志级别和处理器,将影响其所有子日志记录器,除非子日志记录器自己有设置。
-
子级日志记录器继承:
- 如果子级日志记录器没有自己设置处理器和级别,它将使用父级的处理器和级别。
- 可以为子级日志记录器添加额外的处理器或更改级别,覆盖继承的配置。
示例:为子级日志记录器添加自己的处理器
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
级别的日志。
实际应用中的意义
-
模块化日志管理:
- 使用层级日志记录器,可以在大型项目中按模块或功能分区管理日志记录器。
- 每个模块可以有自己的日志记录器,同时继承项目级的日志配置。
-
灵活配置:
- 通过设置父级日志记录器的配置,确保统一的日志格式和级别管理。
- 通过设置子级日志记录器的配置,提供更精细的控制。