go 中用 time.ticker 清理日志需严格对齐 lumberjack 命名规则(如 "logs/app.log.*.gz"),校验 isregular(),分批删除+gosched,独立 goroutine 限流执行,且清理须与轮转生命周期解耦。

Go 程序里用 time.Ticker 触发文件扫描和清理,不是加个 goroutine 就完事——扫错目录、删漏文件、阻塞主线程、并发 panic 都是高频事故。
用 filepath.Glob 匹配日志文件时必须限定命名规则
lumberjack 默认生成 app.log.1、app.log.2.gz 这类带序号或时间戳的文件;如果直接 filepath.Glob("logs/*.log"),可能匹配到 access.log、error.log 甚至临时文件 app.log.tmp,一删全崩。
- 必须和轮转库的命名格式对齐:比如 lumberjack 设置了
LocalTime: true和Compress: true,实际文件是app.log.2024-05-01_02.gz,那 glob 模式就得写成"logs/app.log.*.gz"或"logs/app.log.*" - 匹配后务必用
os.Stat校验是否为常规文件:info.Mode().IsRegular(),跳过符号链接、目录、socket 文件 - 避免用
filepath.Walk扫整个logs/目录——它会递归进子目录,而 lumberjack 不会建子目录,纯属多此一举还慢
os.Remove 不能在主流程里同步调用
单次 os.Remove 看似轻量,但遇到 NFS 挂载点、ext4 日志模式异常、或磁盘 IO 高峰时,可能卡住 100ms+;如果一次要删 200 个文件,串行删就是 20 秒停顿。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 改用分批 + 调度让出:
for i, f := range files { os.Remove(f); if i%50 == 0 { runtime.Gosched() } } - 对 >10MB 的单个日志文件,先
os.Truncate(f, 0)清空内容再os.Remove,减轻 page cache 压力 - 绝对不要在 HTTP handler、gRPC 方法、或
slog.Handler.Handle里调用清理逻辑;必须扔进独立 goroutine,并控制最大并发数(如用带缓冲 channel 限流)
清理时机必须和日志轮转生命周期解耦
很多人把 lumberjack.MaxAge 当成“自动删除开关”,其实它只影响是否触发下一次轮转——app.log.2024-05-01 即使过了 30 天,只要没新日志写入,lumberjack 就不会碰它,更不会删。
- 轮转(write-time)和清理(delete-time)是两个正交动作:前者由写入量或时间驱动,后者必须靠独立定时器驱动
- 清理 goroutine 启动时机不能早于日志文件首次创建——否则
filepath.Glob返回空 slice,后续永远不触发;建议放在log.SetOutput设置完成之后 - 停止清理 goroutine 必须显式做:
defer ticker.Stop()+done chan struct{}控制退出,否则服务 reload 或测试时 goroutine 泄漏
真正难的不是写个 for 循环删文件,而是让扫描不误删、删除不卡顿、启停不失控——每一步都得和你的日志路径、轮转配置、部署环境对齐,差一个点就在线上掉坑里。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










