杰瑞科技汇

Python日志如何按天自动分割?

核心方案:使用 TimedRotatingFileHandler

这是 Python 标准库 logging 模块提供的专门用于按时间轮转日志的处理器。

Python日志如何按天自动分割?-图1
(图片来源网络,侵删)

简单示例

这个例子会创建一个日志文件 app.log,并在每天午夜 (when='midnight') 自动轮转,将旧日志重命名为 app.log.2025-10-27 这样的格式。

import logging
from logging.handlers import TimedRotatingFileHandler
import os
# 1. 创建一个 logger 实例
logger = logging.getLogger('my_app')
logger.setLevel(logging.DEBUG)  # 设置最低日志级别
# 2. 创建一个 TimedRotatingFileHandler
#   - 'app.log': 日志文件名
#   - when='D': 表示按天轮转
#   - interval=1: 与 'when' 配合,表示每天轮转一次 (当 when='D' 时, interval 通常为1)
#   - backupCount=7: 保留7天的日志,超过这个数量的旧日志将被删除
#   - encoding='utf-8': 避免编码问题
handler = TimedRotatingFileHandler(
    filename='app.log',
    when='D',
    interval=1,
    backupCount=7,
    encoding='utf-8'
)
# 3. 设置日志格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
# 4. 将 handler 添加到 logger
logger.addHandler(handler)
# --- 测试 ---
if __name__ == '__main__':
    logger.debug("这是一条调试信息。")
    logger.info("应用程序启动成功。")
    logger.warning("这是一个警告。")
    logger.error("发生了一个错误!")
    logger.critical("这是一个严重错误!")
    # 模拟多次运行,观察日志文件的变化
    # 第一次运行: app.log
    # 第二天运行: app.log 会被重命名为 app.log.2025-10-27,然后创建新的 app.log

更完整的最佳实践示例

在实际项目中,我们通常会将日志配置封装成一个函数,并确保日志目录存在,这个版本更健壮,可以直接复制到你的项目中使用。

import logging
import logging.handlers
import os
from datetime import datetime
def setup_logger(log_dir='logs', log_level=logging.INFO, backup_count=30):
    """
    设置一个每天轮转的日志记录器。
    :param log_dir: 日志文件存放的目录
    :param log_level: 日志级别
    :param backup_count: 保留的日志文件数量
    :return: 配置好的 logger 实例
    """
    # 确保日志目录存在
    if not os.path.exists(log_dir):
        os.makedirs(log_dir)
    # 创建 logger 实例
    logger = logging.getLogger('daily_logger')
    logger.setLevel(log_level)
    # 定义日志格式
    formatter = logging.Formatter(
        fmt='%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S'
    )
    # 1. 文件处理器 - 按天轮转
    # when='D' 表示按天轮转,'midnight' 表示在午夜轮转
    file_handler = logging.handlers.TimedRotatingFileHandler(
        filename=os.path.join(log_dir, 'app.log'),
        when='midnight',
        interval=1,
        backupCount=backup_count,
        encoding='utf-8'
    )
    file_handler.setFormatter(formatter)
    file_handler.suffix = "%Y-%m-%d.log"  # 自定义日志轮转后的文件名后缀,默认是 '.rotate'
    # 2. 控制台处理器 - 同时在终端输出
    console_handler = logging.StreamHandler()
    console_handler.setFormatter(formatter)
    # 将处理器添加到 logger
    logger.addHandler(file_handler)
    logger.addHandler(console_handler)
    return logger
# --- 使用示例 ---
if __name__ == '__main__':
    # 获取配置好的 logger
    logger = setup_logger(log_dir='my_app_logs', log_level=logging.DEBUG)
    logger.debug("调试信息:程序开始执行。")
    logger.info("信息:用户 'Alice' 登录成功。")
    try:
        result = 1 / 0
    except ZeroDivisionError:
        logger.error("错误:发生了除以零的异常!", exc_info=True) # exc_info=True 会记录堆栈信息
    logger.info("信息:程序执行完毕。")

TimedRotatingFileHandler 关键参数详解

参数 说明 示例
filename 日志文件的基准名称。 'my_app.log'
when 轮转的时间单位,可以是:'S' (秒), 'M' (分), 'H' (小时), 'D' (天), 'midnight' (在午夜轮转), 'W0-W6' (周一至周日)。 'D''midnight'
interval 轮转的时间间隔,当 when='D' 时,interval=1 表示每天轮转一次。 1
backupCount 保留的日志文件数量,当超过这个数量时,最旧的日志文件将被删除。 7 (保留7天日志)
atTime 一个 datetime.time 对象,指定轮转的具体时间,只有当 when='H''D' 时有效。 datetime.time(hour=23, minute=59) (在每天23:59轮转)
suffix 轮转后日志文件的后缀,默认是 '%Y-%m-%d_%H-%M-%S',可以自定义,'%Y-%m-%d.log' '%Y-%m-%d.log'

常见问题与解决方案

日志文件名乱码或编码问题

问题:在某些系统上,日志文件名可能显示为乱码。 解决:在创建 TimedRotatingFileHandler 时,明确指定 encoding='utf-8',这能确保日志内容和文件名都使用 UTF-8 编码。

如何让日志文件名更清晰?

问题:默认的轮转文件名可能很长,如 app.log.2025-10-27_10-00-00解决:使用 suffix 参数自定义后缀。

Python日志如何按天自动分割?-图2
(图片来源网络,侵删)
# 创建一个在每天午夜轮转,且文件名后缀为年-月-日.log的处理器
handler = TimedRotatingFileHandler(
    filename='app.log',
    when='midnight',
    backupCount=7,
    encoding='utf-8'
)
handler.suffix = "%Y-%m-%d.log" 
# 轮转后,app.log 会变成 app.log.2025-10-27.log,然后创建新的 app.log

为什么日志没有按预期轮转?

原因:这通常是因为 logger 的级别设置过高,或者 handler 的级别设置过高。 解决:确保 logger.setLevel()handler.setLevel() 的级别足够低,能够捕获到你想要记录的日志。

# 正确的做法
logger = logging.getLogger()
logger.setLevel(logging.DEBUG) # logger级别设为最低
# 然后为不同的handler设置不同的级别
file_handler = TimedRotatingFileHandler(...)
file_handler.setLevel(logging.INFO) # 文件只记录INFO及以上级别的日志
console_handler = logging.StreamHandler(...)
console_handler.setLevel(logging.DEBUG) # 控制台打印所有级别的日志
logger.addHandler(file_handler)
logger.addHandler(console_handler)

如何在多个模块中共享同一个 logger?

问题:在大型项目中,每个模块都调用 setup_logger() 会创建多个重复的 handler,导致日志重复输出。 解决:不要在每个模块中都调用 setup_logger(),应该在程序的入口点(main.pyapp.py)中调用一次,然后在其他模块中通过 getLogger() 获取同一个实例。

# main.py
import logging
from logging_handlers import setup_logger # 假设 setup_logger 在另一个文件中
# 只在这里配置一次全局 logger
logger = setup_logger()
# app.py
import logging
# 直接获取在 main.py 中配置好的 logger,而不是重新创建
logger = logging.getLogger('daily_logger')
# other_module.py
import logging
logger = logging.getLogger('daily_logger')

对于“每天创建一个新日志文件”的需求,logging.handlers.TimedRotatingFileHandler 是最标准、最有效的解决方案。

推荐的最佳实践步骤:

Python日志如何按天自动分割?-图3
(图片来源网络,侵删)
  1. 封装配置:将日志配置(创建目录、创建 handler、设置格式、添加 handler)封装成一个函数 setup_logger
  2. 单例模式:在程序的入口点(如 main.py)调用一次 setup_logger,获取 logger 实例。
  3. 全局获取:在其他所有需要记录日志的模块中,通过 logging.getLogger('你定义的logger_name') 来获取同一个 logger 实例。
  4. 灵活配置:通过参数控制日志级别、保留天数、日志格式等,使配置更灵活。
分享:
扫描分享到社交APP
上一篇
下一篇