You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用logging的TimedRotatingFileHandler生成.log格式的日志文件

如何让TimedRotatingFileHandler生成file_name.日期.log格式的日志

完全可以实现这个需求!默认的TimedRotatingFileHandler确实会把时间后缀追加在原文件的扩展名之后,导致生成类似file_name.log.2019-10-09的文件,但我们可以通过继承并重写它的核心方法,自定义日志文件的命名逻辑。

核心思路

默认的轮转逻辑是直接把时间后缀加在原文件名末尾,我们需要拆分原文件名的“主名称”和“扩展名”,把时间后缀插入到两者之间,再重新拼接成目标文件名。

自定义处理器实现

下面是一个完整的自定义处理器代码,重写了doRollover方法(负责日志轮转的核心逻辑):

import logging
from logging.handlers import TimedRotatingFileHandler
import os
import time

class CustomTimedRotatingFileHandler(TimedRotatingFileHandler):
    def doRollover(self):
        # 关闭当前打开的日志流
        if self.stream:
            self.stream.close()
            self.stream = None
        
        # 计算轮转的时间戳和对应的时间元组
        currentTime = int(time.time())
        dstNow = time.localtime(currentTime)[-1]
        t = self.rolloverAt - self.interval
        
        if self.utc:
            timeTuple = time.gmtime(t)
        else:
            timeTuple = time.localtime(t)
            dstThen = timeTuple[-1]
            # 处理夏令时变化的情况
            if dstNow != dstThen:
                addend = 3600 if dstNow else -3600
                timeTuple = time.localtime(t + addend)
        
        # 拆分原文件名的主名称和扩展名,拼接成目标格式
        base_dir, base_name = os.path.split(self.baseFilename)
        name, ext = os.path.splitext(base_name)
        # 这里按需求生成文件名:主名称.时间后缀.扩展名
        dfn = os.path.join(base_dir, f"{name}.{time.strftime(self.suffix, timeTuple)}{ext}")
        
        # 如果目标文件已存在,先删除
        if os.path.exists(dfn):
            os.remove(dfn)
        
        # 重命名当前日志文件到目标路径
        os.rename(self.baseFilename, dfn)
        
        # 清理超过备份数量的旧日志(保持默认逻辑)
        if self.backupCount > 0:
            for s in self.getFilesToDelete():
                os.remove(s)
        
        # 重新打开新的日志文件
        self.mode = 'a'
        self.stream = self._open()
        
        # 计算下一次轮转的时间
        newRolloverAt = self.computeRollover(currentTime)
        while newRolloverAt <= currentTime:
            newRolloverAt += self.interval
        
        # 再次处理夏令时对轮转时间的影响
        if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc:
            dstAtRollover = time.localtime(newRolloverAt)[-1]
            if dstNow != dstAtRollover:
                addend = -3600 if not dstNow else 3600
                newRolloverAt += addend
        self.rolloverAt = newRolloverAt

使用自定义处理器

接下来就可以像使用普通TimedRotatingFileHandler一样使用这个自定义类了:

# 初始化日志记录器
logger = logging.getLogger("my_app_logger")
logger.setLevel(logging.INFO)

# 配置自定义处理器:按天轮转,保留7天日志
handler = CustomTimedRotatingFileHandler(
    filename="app.log",
    when="D",  # 轮转周期:D=天,H=小时,M=分钟等
    interval=1,
    backupCount=7,
    encoding="utf-8"
)

# 设置日志格式
log_formatter = logging.Formatter(
    "%(asctime)s - %(levelname)s - %(message)s"
)
handler.setFormatter(log_formatter)

# 将处理器添加到日志记录器
logger.addHandler(handler)

# 测试输出日志
logger.info("这是一条测试日志,验证自定义轮转格式")

注意事项

  • 如果你需要按小时/分钟轮转,记得调整when参数和对应的suffix格式(比如when="H"时,suffix="%Y-%m-%d_%H",生成app.2019-10-09_14.log)。
  • 代码中保留了默认的夏令时处理逻辑,确保时间轮转的准确性。
  • 如果不需要备份日志,可以将backupCount设为0。

内容的提问来源于stack exchange,提问作者Michał Zawadzki

火山引擎 最新活动