Go标准库log包缺乏自动切分归档能力,直接写文件易撑爆磁盘;lumberjack通过同步Write内切分、显式参数控制及LocalTime配置解决时区、竞态与归档问题。

Go 标准库的 log 包不支持自动切分和归档,直接用它写日志到文件,跑几天就可能撑爆磁盘——这不是配置问题,是设计缺失。
为什么不能只靠 os.OpenFile + log.SetOutput
很多人试过把 log.SetOutput 指向一个每天重开的 *os.File,结果发现:日志错乱、文件句柄泄漏、凌晨切换时丢日志。根本原因是 log 包本身无锁写入且不感知文件生命周期。
-
log写入是异步缓冲的,Close()前没 flush 就换文件,最后几条日志直接消失 - 多 goroutine 并发写同一个
*os.File时,Write调用可能被中断或覆盖(尤其在 Linux 的O_APPEND未生效时) - 没有归档逻辑:旧文件不会压缩、不会按大小轮转、更不会自动删除过期文件
用 lumberjack.Logger 替代手写轮子
社区事实标准 lumberjack 不是“增强版 log”,而是一个可嵌入的 io.WriteCloser 实现,专治切分与归档的边界条件。
- 所有切分动作(按大小/时间)都在
Write内部同步完成,不依赖外部定时器,避免竞态 - 内置
Rotate方法可手动触发归档,适合配合signal.Notify做平滑 reload - 关键参数必须显式设:
MaxSize(MB)、MaxAge(天)、MaxBackups(保留份数),缺一不可,否则默认值会默默吃掉你所有磁盘空间
示例:
立即学习“go语言免费学习笔记(深入)”;
lw := &lumberjack.Logger{
Filename: "/var/log/app.log",
MaxSize: 100, // MB
MaxAge: 7, // days
MaxBackups: 30,
LocalTime: true,
}
log.SetOutput(lw)
时间切分必须搭配 LocalTime: true 和 UTC 部署确认
如果你的日志文件名含日期(如 app.log.2024-05-20),但服务器时区是 UTC,而业务方看日志习惯本地时间,就会出现“日志写在昨天文件里”的错觉。
-
lumberjack默认用time.Now().UTC()生成归档名,设LocalTime: true才用本地时区 - Kubernetes Pod 或 Docker 容器默认无时区配置,
docker run -e TZ=Asia/Shanghai或挂载/etc/localtime必须同步做 - 不要依赖
log.Printf输出的时间戳做切分判断——切分依据是Write调用时刻,不是日志内容里的字符串时间
归档后压缩需额外 hook,lumberjack 不处理
lumberjack 只负责生成 .log.2024-05-20 这类文件,gzip 压缩得自己加。
- 最简方案:起个
cron每小时扫*.log.*文件,用gzip -f压缩,再改后缀为.log.2024-05-20.gz - 如果要求实时压缩,得在
lumberjack的Rotate后 hook —— 它提供Compress: true参数,但仅限于go 1.20+且只支持 gzip,旧版本得自己监听fsnotify或轮询 - 注意:压缩后的文件仍计入
MaxBackups计数,lumberjack不识别.gz后缀,所以备份数要预留压缩文件位置
真正的难点不在代码行数,而在时区、权限、磁盘配额这三处——它们不会报错,只会某天凌晨开始静默丢日志。


















