RotatingFileHandler 和 TimedRotatingFileHandler 是生产环境底线要求,不配置会导致磁盘撑爆;maxBytes 单位为字节,需换算为 MB;when='D' 每24小时轮转而非按日历日,应选 'midnight' + atTime;backupCount 失效常因 basicConfig 提前调用;多进程下需避免共用文件名,推荐 stdout 输出交由容器日志系统轮转。

RotatingFileHandler 和 TimedRotatingFileHandler 不是锦上添花的配置,而是生产环境的底线要求。
不配轮转,日志文件会持续追加写入,直到磁盘被撑爆——这不是概率问题,是时间问题。我们见过太多案例:一个没配 backupCount 的 RotatingFileHandler,跑三个月后生成上百个 app.log.1 到 app.log.999;也见过 TimedRotatingFileHandler 在跨月时因时区或夏令时处理不当,连续两天写进同一个文件,单日日志突破 50GB。
真正要解决的不是“要不要轮转”,而是“怎么轮转才不翻车”。下面直击几个关键实操点:
RotatingFileHandler 的 maxBytes 单位必须是字节,不是 MB 或 KB
这是最常踩的坑:maxBytes=10 不是 10MB,而是 10 字节——日志写几条就轮转一次。
立即学习“Python免费学习笔记(深入)”;
-
maxBytes=10 * 1024 * 1024才是 10MB(推荐范围:5MB–50MB) - 别用
10_000_000这类魔数,易读性差且容易手误 - 如果应用日志量极不稳定(比如偶发大 dump),建议设低一点(如 5MB),避免单次写入直接冲破阈值导致频繁轮转
TimedRotatingFileHandler 的 when='D' 并不等于“每天零点”
when='D' 表示“每 24 小时轮转一次”,起始时间是首次创建文件的时间点,不是系统零点。
- 如果服务在 15:30 启动,第一个轮转发生在次日 15:30,而非 00:00
- 真正按日历日切分,要用
when='midnight',并配合atTime指定具体时刻(如datetime.time(0, 0)) - 注意时区:若进程运行在 UTC 容器里,但日志需按本地时间归档,必须显式设置
utc=False,否则可能错位一整天
backupCount 生效的前提是:不能先调用 logging.basicConfig()
一旦执行过 logging.basicConfig(),后续添加的 RotatingFileHandler 或 TimedRotatingFileHandler 会被静默忽略——日志照常输出,但轮转完全失效。
- 务必确保:所有 handler 添加操作都在
basicConfig()之前,或干脆不用它 - Uvicorn 等框架自带默认配置,若直接改
LOGGING_CONFIG字典,也要确认没触发内部的basicConfig调用路径 - 验证方式:启动后检查日志目录,看是否出现
app.log.1、app.log.2等文件;没有,大概率是被basicConfig截胡了
多进程场景下,RotatingFileHandler 无法保证原子性
RotatingFileHandler 内部只做简单重命名,不带文件锁。多个 worker 进程同时写同一日志文件时,轮转可能冲突,导致部分日志丢失或文件损坏。
- Uvicorn + UWSGI 等多进程模型下,不要让所有进程共用同一个
filename - 可行方案:用进程 ID 区分文件名,如
f'app.log.{os.getpid()}',再配合外部 logrotate 工具统一归档 - 更稳妥的做法是把日志输出到 stdout/stderr,由容器或 systemd 日志系统接管轮转(如 Docker 的
json-file驱动 +max-size)
轮转本身不难,难的是在真实部署环境中避开那些看似合理、实则致命的默认行为和隐含假设。尤其当服务跑在容器里、跨时区、多进程并发写入时,backupCount 设得再大也没用——因为轮转根本没触发,或者触发了却写乱了。


















