log/slog 默认不支持日志分割,需借助 lumberjack 等库实现按大小/时间切分;lumberjack 配置需注意 maxsize 单位为 mb、maxbackups 应设具体值、localtime 必须开启,并在进程退出前调用 close 避免丢日志。

log/slog 默认不支持日志分割,得换库或自己写
Go 标准库的 log 和新推荐的 slog 都不提供文件滚动、按大小/时间切分功能。这不是遗漏,是设计取舍:它们专注结构化输出和基础路由,切割属于运维层逻辑。硬要在 slog 上加切割,要么套一层 Writer 包装器(容易漏 flush、竞态),要么直接换成熟方案。
用 lumberjack 实现按大小+备份数切割最稳
目前生产环境最常用的是 lumberjack —— 它轻量、无依赖、API 简洁,且和 log/slog 都能无缝对接。关键点不是“能不能用”,而是怎么配才不踩坑:
-
lumberjack.Logger的MaxSize单位是 MB(不是字节),设10就是 10MB,别写成10 * 1024 * 1024 -
MaxBackups控制保留几个旧文件,设0表示不限——但磁盘会涨满,线上务必设具体值(比如7) -
LocalTime: true必须开,否则压缩归档时用 UTC 时间,排查时对不上本地日志时间 - 如果用
slog.New(<handler>)</handler>,要把lumberjack.Logger传给os.File类型的Writer,别直接传给slog.TextHandler构造函数(它只认io.Writer)
示例片段:
lj := &lumberjack.Logger{
Filename: "app.log",
MaxSize: 10,
MaxBackups: 7,
MaxAge: 28,
LocalTime: true,
Compress: true,
}
logger := slog.New(slog.NewTextHandler(lj, nil))
按时间切割要小心时区和启动抖动
lumberjack 的 MaxAge 是按“最后修改时间”判断是否归档,不是按写入时间。这意味着:
- 服务长时间没写日志,
app.log可能卡在“未达 MaxAge”状态,旧文件不会自动压缩 - 如果服务器时钟跳变(如 NTP 校正),可能误触发归档或跳过归档
- 想严格按天切割(比如每天 00:00 新建),
lumberjack做不到,得用rotatelogs或自己起 goroutine 定时检查 +Rotate()
简单替代方案:用 systemd/logrotate 等外部工具接管切割,Go 进程只管往 stdout 写,靠容器或系统日志收集器做归档——反而更可靠。
并发写日志时 lumberjack 不会丢数据,但要注意 Close
lumberjack.Logger 内部用了 sync.RWMutex,多 goroutine 同时 Write 是安全的。真正容易出问题的是进程退出前:
- 没调
lj.Close(),最后几条日志可能卡在缓冲区没刷到磁盘 - 用
signal.Notify捕获SIGINT/SIGTERM时,要在 handler 里显式Close,不能只靠 defer(因为 main goroutine 退出太快) - 如果日志量极大(>10MB/s),
Compress: true会导致写入延迟升高,压缩过程阻塞后续写入——高吞吐场景建议关压缩,交给 logrotate 处理
事情说清了就结束。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











