timedrotatingfilehandler 是按自然时间周期归档日志的最直接官方方案,但非进程安全、不持久化上次轮转时间、时区与多进程场景易致漏滚乱序。

TimedRotatingFileHandler 不是“必不可少”,而是在需要按自然时间周期归档日志的场景下,最直接、最低侵入的官方方案。它不解决所有问题,但能快速覆盖“每天一个文件”“每小时切一次”这类强时间语义需求。
when="D" 和 when="midnight" 的行为差异很关键
很多人以为 when="D" 就是“每天 0 点切”,其实不是:when="D" 表示“每天午夜”,但它的计算起点是**首次写入日志的时间点**,不是程序启动时刻;而 when="midnight" 才真正对齐本地时区的 00:00:00。
-
when="D":按 24 小时间隔滚动(例如 14:30 写第一条,下次就是次日 14:30) -
when="midnight":强制对齐当日 00:00:00,无论你几点启动或写日志 - 如果想固定在凌晨 2 点轮转,必须配合
atTime=datetime.time(2, 0, 0),且仅对when="D"或when="W0"等有效
backupCount 删除逻辑容易误判磁盘占用
backupCount=7 并不等于“保留最近 7 天的日志文件”,它只控制轮转后生成的备份文件数量(如 app.log.1, app.log.2…),而这些文件名里的日期戳是轮转发生时刻,不是创建时刻——如果某天没写日志,就不会生成对应文件,backupCount 也不会自动补空。
- 轮转文件命名依赖
strftime格式,默认是.%Y-%m-%d,但若程序长时间空闲,可能跳过某些日期 - 磁盘空间是否可控,取决于实际日志量 + 轮转频率,不能只靠
backupCount做容量规划 - 建议搭配外部监控(如
du -sh app.log*定时检查)或日志采集系统做兜底
多进程下 TimedRotatingFileHandler 会丢日志
标准库的 TimedRotatingFileHandler **不是进程安全的**。当多个进程共用同一个 filename 并同时触发轮转时,会出现文件重命名冲突、日志覆盖、甚至 OSError: [Errno 2] No such file or directory 错误。
- 它内部用
os.rename()移动文件,没有跨进程锁机制 -
delay=True只缓解首次打开冲突,不解决轮转时的竞争 - 生产环境若用 gunicorn/uwsgi/multiprocessing,必须改用
ConcurrentLogHandler或把日志输出到 syslog / Kafka / 文件队列
真正的难点不在配置参数,而在于:它不记录“上次轮转时间”到持久化存储,每次重启都重新推算;它不感知其他进程的存在;它默认用本地时区,但容器里可能没设 TZ 环境变量——这些细节叠加起来,才是线上日志“看起来正常,实则漏滚、乱序、错时”的根源。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











