杰瑞科技汇

Python logging 如何实现追加写入日志?

这里的“追加”通常有两个层面的含义:

  1. 追加到文件:这是最常见的需求,即每次程序运行时,新的日志信息都添加到日志文件的末尾,而不是覆盖旧内容。
  2. 日志处理器追加到日志器:在配置日志时,可以向一个日志器(Logger)对象添加多个处理器(Handler),让同一条日志信息可以同时输出到不同的地方(如文件、控制台、网络等)。

下面我将分别对这两个方面进行详细说明。


追加到文件 (FileHandler)

这是“追加”最直接的理解。logging 模块中的 FileHandler 默认就是以追加模式打开文件的。

核心要点

  • 默认行为:当你创建一个 FileHandler 并指定一个文件名时,它会以 'a' (append) 模式打开该文件,如果文件不存在,它会自动创建。
  • 如何确认:你可以查看 FileHandler 的源码,或者在创建后检查其 baseFilenamemode 属性。

示例代码

这个例子会清晰地展示日志是如何被追加到文件中的。

import logging
import time
import os
# 定义日志文件名
LOG_FILE = 'my_app.log'
# --- 第一次运行 ---
print("--- 第一次运行,创建日志文件并写入内容 ---")
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(LOG_FILE, mode='a', encoding='utf-8'), # 显式指定 mode='a'
        logging.StreamHandler() # 同时输出到控制台
    ]
)
logger = logging.getLogger(__name__)
logger.info("这是第一条日志信息 (第一次运行)")
logger.warning("这是一个警告信息 (第一次运行)")
time.sleep(1) # 模拟程序执行了一段时间
logger.error("这是一个错误信息 (第一次运行)")
print(f"请检查当前目录下的文件: {os.path.abspath(LOG_FILE)}")
print("-" * 30)
# --- 模拟第二次运行 ---
# 在实际应用中,这可能是你重新启动脚本后
print("\n--- 第二次运行,模拟追加日志 ---")
# 注意:这里我们不需要再次 basicConfig,因为如果已经配置过,它可能不会重新应用。
# 更好的做法是获取已配置的 logger 实例。
logger = logging.getLogger(__name__) # 获取同一个 logger 实例
logger.info("这是第一条日志信息 (第二次运行)")
logger.warning("这是一个警告信息 (第二次运行)")
print("再次检查日志文件,你会发现新的日志被追加到了末尾。")

运行结果分析:

  1. 第一次运行

    • 程序会创建一个名为 my_app.log 的文件。
    • 如下:
      2025-10-27 10:30:00,123 - INFO - 这是第一条日志信息 (第一次运行)
      2025-10-27 10:30:01,124 - WARNING - 这是一个警告信息 (第一次运行)
      2025-10-27 10:30:02,125 - ERROR - 这是一个错误信息 (第一次运行)
  2. 第二次运行

    • 程序再次运行,FileHandler 以追加模式打开 my_app.log
    • 被更新为:
      2025-10-27 10:30:00,123 - INFO - 这是第一条日志信息 (第一次运行)
      2025-10-27 10:30:01,124 - WARNING - 这是一个警告信息 (第一次运行)
      2025-10-27 10:30:02,125 - ERROR - 这是一个错误信息 (第一次运行)
      2025-10-27 10:30:05,130 - INFO - 这是第一条日志信息 (第二次运行)  <-- 新内容追加在这里
      2025-10-27 10:30:05,131 - WARNING - 这是一个警告信息 (第二次运行) <-- 新内容追加在这里

重要提醒:FileHandlermode 参数

虽然 FileHandler 默认是追加模式,但如果你显式地传入 mode='w',它就会覆盖文件内容。

# 警告:这会覆盖日志文件!
# logging.FileHandler(LOG_FILE, mode='w') 

在绝大多数情况下,你都应该使用默认的追加模式。


日志处理器追加到日志器

这是“追加”的第二个重要含义,即配置日志的“路由”,你可以将多个处理器“追加”到一个日志器上,实现日志的多路输出

核心要点

  • Logger 对象:日志系统的入口点,它负责产生日志消息。
  • Handler 对象:日志消息的“分发器”,它决定日志消息最终要去哪里(文件、控制台、网络等)。
  • 关系:一个 Logger 可以有多个 Handler,当日志产生时,Logger 会将消息传递给它所拥有的所有 Handler

示例代码

这个例子将展示如何将日志同时输出到文件和控制台。

import logging
# 1. 创建一个 Logger
# 注意:如果直接使用 logging.getLogger() 不传名字,会获取到 root logger。
# 为了更好的模块化管理,推荐使用 __name__。
logger = logging.getLogger("my_module")
logger.setLevel(logging.DEBUG)  # 设置日志器级别,DEBUG 级别及以上的日志都会被处理
# 2. 创建多个 Handler
# 2.1 文件 Handler (追加模式)
file_handler = logging.FileHandler('module.log', mode='a', encoding='utf-8')
file_handler.setLevel(logging.INFO)  # 文件处理器只处理 INFO 级别及以上的日志
# 2.2 控制台 Handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.WARNING) # 控制台处理器只处理 WARNING 级别及以上的日志
# 3. 创建并设置 Formatter (日志格式)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# 将 Formatter 添加到 Handler
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
# 4. 将 Handler 追加到 Logger (这是关键步骤)
logger.addHandler(file_handler)
logger.addHandler(console_handler)
# 5. 现在开始记录日志
logger.debug("这是一个调试信息,只有文件会记录。") # DEBUG 级别,低于 file_handler 的 INFO,所以文件不记录,也低于 console_handler 的 WARNING,所以控制台不记录。
logger.info("这是一个信息,只有文件会记录。")   # INFO 级别,文件会记录,但低于控制台的 WARNING,所以控制台不记录。
logger.warning("这是一个警告,文件和控制台都会记录。") # INFO 级别以上,文件和控制台都会记录。
logger.error("这是一个错误,文件和控制台都会记录。")   # INFO 级别以上,文件和控制台都会记录。

运行结果分析:

  • 控制台输出 (只有 WARNINGERROR 级别):
    2025-10-27 10:35:00,500 - my_module - WARNING - 这是一个警告,文件和控制台都会记录。
    2025-10-27 10:35:00,501 - my_module - ERROR - 这是一个错误,文件和控制台都会记录。
  • module.log 文件内容 (只有 INFOWARNINGERROR 级别):
    2025-10-27 10:35:00,501 - my_module - INFO - 这是一个信息,只有文件会记录。
    2025-10-27 10:35:00,502 - my_module - WARNING - 这是一个警告,文件和控制台都会记录。
    2025-10-27 10:35:00,503 - my_module - ERROR - 这是一个错误,文件和控制台都会记录。

这个例子完美地展示了如何通过“追加”多个 Handler 到一个 Logger,实现灵活的日志输出策略。


总结与最佳实践

概念 作用 关键点
追加 确保日志历史不被覆盖,持续记录。 使用 FileHandler,其默认模式为 'a' (append)。
处理器追加 实现日志的多路输出,如同时写文件和打印到控制台。 使用 logger.addHandler(handler) 方法。

最佳实践建议

  1. 使用 logging.config.dictConfig:对于复杂的日志配置,使用字典配置比在代码中硬编码 HandlerFormatter 更清晰、更易于维护。
  2. 为不同模块使用不同的 Logger:使用 logging.getLogger(__name__) 是一个好习惯,这样可以根据模块名来定位日志来源。
  3. 合理设置日志级别
    • Logger 的级别决定了哪些级别的日志消息可以被处理。
    • Handler 的级别决定了哪些级别的日志消息最终会被输出
    • 消息要被最终输出,必须同时满足 Logger 和其所连接的 Handler 的级别要求。
  4. 考虑使用 RotatingFileHandler:对于长期运行的服务,日志文件会变得非常大。RotatingFileHandler 可以在文件达到一定大小时自动创建新文件,并保留一定数量的备份文件,避免单个日志文件无限增长。

希望这个详细的解释能帮助你完全理解 Python logging 模块中的“追加”功能!

分享:
扫描分享到社交APP
上一篇
下一篇