TimedRotatingFileHandler 是 Python 标准库中实现按天滚动日志最稳妥的方式,需正确配置 when='D'、atTime=time(0,0,0)、utc=False 和 backupCount=7,否则易导致轮转不准、文件堆积或丢失日志。

用 TimedRotatingFileHandler 实现按天滚动
直接上结论:Python 标准库的 TimedRotatingFileHandler 是最稳妥的选择,它原生支持按天、按小时、按秒等时间维度轮转,无需第三方包。关键不是“能不能”,而是“怎么配才不丢日志、不重名、不卡死”。
常见错误是只设 when='D' 却忽略 backupCount 和 utc,导致日志文件无限增长,或跨时区时凌晨 0 点没切、甚至切两次。
-
when='D'表示每天轮转,但实际触发时间取决于首次写入日志的时间点(比如第一次写在 15:23,那第二天 15:23 才轮转),要精确到 0 点,必须配合atTime参数 -
atTime要传datetime.time(0, 0, 0),且建议显式指定utc=False(默认就是 False,但显式写更防错) -
backupCount=7表示最多保留 7 个历史文件,超过的自动删除;设为 0 则不删,容易撑爆磁盘 - 注意:Windows 下文件被占用时轮转可能失败,避免用记事本直接打开正在写的日志文件
为什么不能只靠 RotatingFileHandler
RotatingFileHandler 只按大小轮转,和“按天”无关。有人试图用它 + 每天重启程序来凑合,这会引入新问题:进程意外退出时日志中断、重启间隙日志丢失、多进程写同一个文件引发内容错乱。
还有人用定时器手动 doRollover(),但这是反模式——TimedRotatingFileHandler 内部已用线程安全方式处理了并发写入与轮转时机,自己调容易触发 RuntimeError: reentrant call inside <_io.bufferedwriter object></_io.bufferedwriter>。
立即学习“Python免费学习笔记(深入)”;
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
- 轮转本质是原子操作:重命名旧文件 + 新建空文件,
TimedRotatingFileHandler在底层做了锁保护 -
RotatingFileHandler的maxBytes和backupCount对时间维度完全无效 - 如果真需要“大小+时间”双约束,得自己封装逻辑,不是简单组合两个 Handler
配置后发现日志没按时切?检查这几个点
现象:日志文件一直不生成 .2024-05-20 这类带日期的备份,或者切早了/切晚了 1 小时。大概率是这几个地方出了问题:
- 程序启动后从未调用过
logger.info()等方法——TimedRotatingFileHandler是 lazy 初始化的,首次写日志才计算下次轮转时间 - 系统时区和 Python 解释器时区不一致,比如服务器是 UTC,代码里又没设
utc=True,会导致轮转时间偏移 8 小时 - 日志级别设太高(如只设
WARNING),而你只打INFO,看起来像“没日志”,其实是被过滤了 - 多个 logger 共享同一个 handler 实例时,其中一个调用了
close(),会导致其他 logger 后续写入失败,表现为“突然不写日志”
一个最小可运行示例
下面这段代码能直接跑,生成 app.log,每天 0 点切一次,保留最近 7 天:
import logging
from logging.handlers import TimedRotatingFileHandler
from datetime import time
<p>logger = logging.getLogger('my_app')
logger.setLevel(logging.INFO)</p><p>handler = TimedRotatingFileHandler(
filename='app.log',
when='D',
interval=1,
backupCount=7,
atTime=time(0, 0, 0),
utc=False,
)
handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
logger.addHandler(handler)</p><p>logger.info('This will go to app.log and rotate at midnight.')</p>注意:interval=1 和 when='D' 组合才是“每天”,不要写成 interval=24 —— 那是按小时轮转的参数逻辑,和 when 冲突。
真正容易被忽略的是:轮转动作本身不阻塞主线程,但首次写日志时会同步计算下一次轮转时间戳,如果程序在 23:59 启动,这个时间戳就定在第二天 0:00,而不是“立刻轮转”。所以别指望刚启动就看到带日期的归档文件。

















