
python的logging模块是一个强大而灵活的日志系统,它由几个核心组件构成:
日志消息的生命周期大致如下:应用程序调用Logger的某个方法(如logger.info())生成一个日志记录(LogRecord)。该记录首先由Logger自身进行级别检查。如果通过,它将被传递给Logger附加的所有Handler,每个Handler再根据自己的级别设置决定是否处理该记录,并最终通过Formatter进行格式化输出。
在logging模块中,日志级别(如DEBUG, INFO, WARNING, ERROR, CRITICAL)是控制日志输出的关键。一个常见的误解是,只要将Handler的级别设置为INFO,所有INFO及更高级别的日志就会被输出。然而,日志记录的过滤实际上发生在两个主要阶段:
Logger级别的过滤: 当一个日志记录被创建时,它首先会与生成它的Logger实例的有效级别进行比较。如果日志记录的级别低于Logger的有效级别,那么该记录将被直接丢弃,不会传递给任何Handler。 关键点:新创建的Logger实例(通过logging.getLogger('name'))默认的级别是WARNING。这意味着,如果你不显式设置Logger的级别,那么DEBUG和INFO级别的日志消息将永远不会到达Handler,因为它们在Logger层面就被过滤掉了。
Handler级别的过滤: 如果日志记录通过了Logger的级别检查,它将被传递给Logger附加的所有Handler。每个Handler也会根据自己的级别设置对日志记录进行第二次检查。如果日志记录的级别低于Handler的级别,那么该Handler将不会处理这条记录。
因此,要确保INFO级别的日志能够被输出,你需要同时满足两个条件:
原始代码中,setup_logger函数为FileHandler和StreamHandler都设置了相应的级别(例如info_log.setLevel(logging.INFO)),但却没有为Logger实例本身设置级别。由于新创建的Logger实例默认级别是WARNING,因此当logger.info('This is an info message')被调用时,INFO级别的消息在到达Logger时就被其默认的WARNING级别过滤掉了,无法传递给任何Handler,最终导致INFO日志未被打印。
立即学习“Python免费学习笔记(深入)”;
解决方案:在setup_logger函数中,为logger实例显式设置一个合适的级别,例如logging.INFO或logging.DEBUG,以确保所有期望的低级别日志能够通过Logger的初步检查。
以下是修正后的setup_logger函数和使用示例:
import logging
import os
# 确保日志目录存在
LOG_DIR = './logs'
os.makedirs(LOG_DIR, exist_ok=True)
def setup_logger(dtc_name, log_dir, is_debug=False, is_interactive=False):
"""
配置并返回一个Logger实例。
:param dtc_name: Logger的名称。
:param log_dir: 日志文件存放的目录。
:param is_debug: 是否启用调试模式,影响控制台输出级别。
:param is_interactive: 是否启用交互模式,影响控制台输出级别。
:return: 配置好的Logger实例。
"""
formatter = logging.Formatter("%(asctime)s %(process)s %(levelname)s - %(message)s")
logger = logging.getLogger(dtc_name)
# 关键修正:设置Logger自身的级别
# 根据需求,如果需要看到INFO或DEBUG日志,Logger级别必须设置得足够低
logger.setLevel(logging.DEBUG) # 设置Logger的最低处理级别为DEBUG,以确保所有消息都能通过
# 避免重复添加Handler,这在多次调用setup_logger时很有用
if logger.handlers:
for handler in logger.handlers[:]:
logger.removeHandler(handler)
# INFO级别日志文件处理器
info_log_path = os.path.join(log_dir, f"{dtc_name}.log")
info_log = logging.FileHandler(info_log_path)
info_log.setFormatter(formatter)
info_log.setLevel(logging.INFO) # 文件处理器只记录INFO及以上级别
logger.addHandler(info_log)
# ERROR级别日志文件处理器
error_log_path = os.path.join(log_dir, f"{dtc_name}_error.log")
error_log = logging.FileHandler(error_log_path)
error_log.setFormatter(formatter)
error_log.setLevel(logging.ERROR) # 错误文件处理器只记录ERROR及以上级别
logger.addHandler(error_log)
# 控制台日志处理器
console_log = logging.StreamHandler()
console_log.setFormatter(formatter)
# 根据is_debug和is_interactive设置控制台输出级别
if is_debug:
console_log.setLevel(logging.DEBUG) # 调试模式下控制台输出DEBUG及以上
elif is_interactive:
console_log.setLevel(logging.INFO) # 交互模式下控制台输出INFO及以上
else:
console_log.setLevel(logging.WARNING) # 默认情况下控制台输出WARNING及以上
logger.addHandler(console_log)
return logger
# 使用示例
def main():
# 设置logger,is_debug和is_interactive都为False,控制台默认输出WARNING及以上
# 但由于Logger本身级别设置为DEBUG,所有日志(DEBUG, INFO, WARNING, ERROR, CRITICAL)
# 都会被Logger接收并传递给Handler
logger = setup_logger('ExampleLogger', LOG_DIR, is_debug=False, is_interactive=False)
print("--- 当前Logger和Handlers的级别 ---")
print(f"Logger Level: {logging.getLevelName(logger.level)}")
for handler in logger.handlers:
print(f"Handler: {handler} | Level: {logging.getLevelName(handler.level)}")
print("----------------------------------")
# 记录不同级别的消息
logger.debug('This is a debug message')
logger.info('This is an info message')
logger.warning('This is a warning message')
logger.error('This is an error message')
logger.critical('This is a critical message')
if __name__ == "__main__":
main()运行上述代码,你将看到如下输出(具体时间戳和进程ID会有所不同):
--- 当前Logger和Handlers的级别 --- Logger Level: DEBUG Handler: <FileHandler ./logs/ExampleLogger.log (INFO)> | Level: INFO Handler: <FileHandler ./logs/ExampleLogger_error.log (ERROR)> | Level: ERROR Handler: <StreamHandler <stderr> (WARNING)> | Level: WARNING ---------------------------------- 2023-XX-XX XX:XX:XX,XXX XXX WARNING - This is a warning message 2023-XX-XX XX:XX:XX,XXX XXX ERROR - This is an error message 2023-XX-XX XX:XX:XX,XXX XXX CRITICAL - This is a critical message
在./logs/ExampleLogger.log文件中,你将看到:
2023-XX-XX XX:XX:XX,XXX XXX INFO - This is an info message 2023-XX-XX XX:XX:XX,XXX XXX WARNING - This is a warning message 2023-XX-XX XX:XX:XX,XXX XXX ERROR - This is an error message 2023-XX-XX XX:XX:XX,XXX XXX CRITICAL - This is a critical message
通过上述输出可以看出,虽然控制台只显示了WARNING及以上级别的消息(因为console_log的级别被设置为WARNING),但INFO级别的消息已经成功写入了ExampleLogger.log文件,这证明了Logger本身的级别设置是关键。
通过正确理解并配置Logger实例的级别,你可以确保Python logging模块能够按照预期捕获和输出所有重要日志信息,从而更有效地进行程序调试和监控。
以上就是Python logging模块深度解析:为何INFO日志不显示及其解决方案的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号