Go标准库log包不支持日志轮转,必须用第三方库或手动封装;lvg/logrotator最轻量可控,仅一个文件、无依赖、按大小轮转、性能高,但需提前创建目录并显式Close。
go 标准库的 log 包本身不支持日志滚动,必须借助第三方库或手动封装 —— 直接用 log.setoutput() 接文件句柄而不轮转,必然导致单文件无限增长、磁盘打满、排查困难。
为什么 lvg/logrotator 是最轻量且可控的选择
社区常用方案有 lvg/logrotator、lestrrat-go/file-rotatelogs 和 gopkg.in/natefinch/lumberjack.v2。其中 lvg/logrotator 体积小(仅一个文件)、无依赖、行为透明,适合嵌入已有日志流程;而 lumberjack 虽稳定但默认启用压缩、归档逻辑较重,容易在低配环境触发 I/O 阻塞。
-
logrotator.New返回的是io.WriteCloser,可直接传给log.SetOutput - 它按大小轮转(非时间),触发条件明确:
MaxSize(字节) +MaxBackups(保留份数) - 不会自动创建父目录,
os.MkdirAll必须提前调用,否则首次写日志就 panic - 轮转时使用
os.Rename,不涉及拷贝,性能开销极低
如何把 logrotator 接入标准 log 并避免 panic
常见错误是忽略初始化失败或未处理关闭逻辑,导致程序退出时日志丢失或文件句柄泄漏。
- 轮转器必须在
log.SetOutput前完成初始化,并检查返回 error:rotator, err := logrotator.New("app.log", logrotator.MaxSize(10*1024*1024), logrotator.MaxBackups(5)) - 务必调用
os.MkdirAll(filepath.Dir("app.log"), 0755),否则open app.log: no such file or directory会直接 panic - 主程序退出前应显式调用
rotator.Close(),否则最后一批缓冲日志可能未刷盘 - 若需多日志文件(如 error.log / access.log),每个文件需独立实例,不可复用同一
rotator
用 lumberjack.v2 时最容易踩的三个坑
lumberjack 功能强但配置稍复杂,新手常因参数理解偏差导致轮转失效或误删日志。
-
LocalTime: true必须设为true,否则轮转文件名里的时间戳是 UTC,和系统日志时间对不上 -
MaxAge和MaxBackups是“或”关系:任一条件满足即轮转,但MaxAge=0表示禁用时间轮转,只靠大小;若两个都为 0,则永不轮转 - 轮转后旧文件权限默认是
0600,若需其他进程读取(如日志采集 agent),要显式设置Perm: 0644 - 它内部使用
sync.Mutex保护写操作,高并发下比logrotator略慢,但通常可忽略
轮转不是加个库就完事——关键在轮转时机是否与业务节奏匹配(比如大促期间调小 MaxSize)、归档路径是否隔离(避免和临时文件混在一起)、以及退出时 Close 是否被 defer 正确包裹。漏掉任意一点,都可能让日志在某个凌晨突然失联。


















