RotatingFileHandler 不够用是因为 backupCount 仅控制保留数量,无法保证磁盘空间可控;TimedRotatingFileHandler 按时间轮转,也不支持固定配额下的循环覆盖;需自定义 CyclingFileHandler 实现写满即覆盖最老文件的硬性循环逻辑。

为什么直接用 logging.handlers.RotatingFileHandler 不够用?
默认的 RotatingFileHandler 确实能按大小轮转,但「循环滚动」常指固定数量日志文件、旧文件被自动覆盖(而非追加或无限保留)。它的 backupCount 参数只控制保留几个备份,不保证磁盘空间可控——比如设了 backupCount=5,但每个日志动辄 200MB,总占用就超 1GB。真正需要的是「写满即覆盖最老文件」的硬性循环逻辑。
用 logging.handlers.TimedRotatingFileHandler 能否替代?
不能。它按时间切分(如每天一个文件),不解决「固定磁盘配额 + 循环复用」问题。而且它默认不会删除最老的 .1 文件来腾空间,只是不断新增 .2、.3……直到 backupCount 触顶后删掉最老的——这仍是软性轮转,不是循环覆盖。
手动实现循环滚动的关键三步
核心思路:把日志文件名固化为带序号的模板(如 app.log.0、app.log.1),写入时总写向 app.log,轮转时将现有文件依次重命名,并用 os.remove() 清掉超出 backupCount 的最老文件。
- 用
os.path.getsize()检查当前主日志是否超限,触发轮转 - 轮转时从最大序号倒序重命名:
app.log.4 → app.log.5,app.log.3 → app.log.4……最后把app.log重命名为app.log.0 - 重命名完成后,检查是否存在
app.log.{backupCount},存在则os.remove()——这才是真正循环 - 注意:必须用
os.rename()(非shutil.move()),否则跨文件系统会失败且不原子
一个轻量可靠的自定义 Handler 示例
以下代码可直接嵌入项目,无需第三方依赖:
import logging
import os
import glob
<p>class CyclingFileHandler(logging.Handler):
def <strong>init</strong>(self, filename, maxBytes=0, backupCount=0):
super().<strong>init</strong>()
self.filename = filename
self.maxBytes = maxBytes
self.backupCount = backupCount
self.baseFilename = os.path.abspath(filename)</p><pre class="brush:python;toolbar:false;">def emit(self, record):
try:
if self.maxBytes > 0 and os.path.exists(self.baseFilename):
if os.path.getsize(self.baseFilename) >= self.maxBytes:
self.doRollover()
self.stream = open(self.baseFilename, 'a')
msg = self.format(record)
self.stream.write(msg + '\n')
self.stream.close()
except Exception:
self.handleError(record)
def doRollover(self):
# 删除最老的备份(如果存在)
if self.backupCount > 0:
old = f"{self.baseFilename}.{self.backupCount}"
if os.path.exists(old):
os.remove(old)
# 倒序重命名
for i in range(self.backupCount - 1, -1, -1):
sfn = f"{self.baseFilename}.{i}"
dfn = f"{self.baseFilename}.{i + 1}"
if os.path.exists(sfn):
os.replace(sfn, dfn)
# 归档当前主文件
if os.path.exists(self.baseFilename):
os.replace(self.baseFilename, f"{self.baseFilename}.0")
使用时:handler = CyclingFileHandler("app.log", maxBytes=10*1024*1024, backupCount=3) 表示单个日志最大 10MB,最多保留 4 个文件(app.log + .0~.3),超出后 .3 被删,其余上移,app.log 变成 .0。
注意:多进程下该 Handler 不安全,需配合 logging.handlers.QueueHandler 或外部日志代理;另外 os.replace() 在 Windows 上对正在读取的文件可能报错,生产环境建议加 try/except 重试逻辑。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











