Go-logging 不支持原生滚动归档,需搭配 lumberjack 实现:lumberjack.Logger 作为 io.WriteCloser 被 logging.NewLogBackend 封装为 backend,并通过 NewBackendFormatter 统一格式化;其 MaxSize 单位为 MB,MaxBackups/MaxAge 单位为天,Compress 需 Go ≥ 1.16。

Go-logging 不支持原生滚动归档,必须搭配 lumberjack
Go-logging(github.com/op/go-logging)本身只提供日志格式化和输出接口,SetBackend 接收的是 logging.Backend,但不内置文件切割逻辑。直接写文件会无限追加,磁盘迟早被打爆。真正实现按大小/时间滚动+压缩+保留数的,是 lumberjack.Logger —— 它是一个 io.WriteCloser 包装器,需手动桥接。
常见错误现象:log output file keeps growing forever 或 panic: interface conversion: io.Writer is nil(没给 lumberjack 设置 Filename)。
- 必须用
lumberjack.NewLogger构造一个io.WriteCloser实例,再通过logging.NewLogBackend封装为 backend -
lumberjack.Logger的MaxSize单位是 MB(不是字节),MaxBackups和MaxAge是天数,注意单位陷阱 - 若同时启用
Compress: true,需确保 Go 版本 ≥ 1.16(依赖archive/zip的内部行为)
如何把 lumberjack 写入适配成 logging.Backend
Go-logging 要求 backend 实现 Log 方法,而 lumberjack 只提供 Write。中间必须用 logging.NewLogBackend 转换 —— 它接受 io.Writer,内部自动处理格式化后写入。
关键点:不能直接传 *lumberjack.Logger 给 NewLogBackend,因为后者要求的是 io.Writer,而 lumberjack.Logger 实现了 io.WriteCloser(兼容 io.Writer),所以可直接传:
立即学习“go语言免费学习笔记(深入)”;
lj := &lumberjack.Logger{
Filename: "app.log",
MaxSize: 10, // MB
MaxBackups: 5,
MaxAge: 28, // days
Compress: true,
}
backend := logging.NewLogBackend(lj, "", 0) // 第三个参数是 flag,通常为 0
注意:lumberjack.Logger 不是线程安全的,但 logging 自身在写入时加锁,所以无需额外同步。
日志级别与输出格式怎么和 lumberjack 对齐
Go-logging 默认输出带前缀(如 INFO、DEBUG),而 lumberjack 只负责写原始字节。如果希望每行开头有时间戳+级别,必须在 backend 上设置 formatter,而不是依赖 lumberjack。
- 用
logging.NewBackendFormatter套一层:formatted := logging.NewBackendFormatter(backend, format) -
format是*logging.LogFormat,推荐用logging.MustStringFormatter(`%{time:2006-01-02T15:04:05.000Z} %{level:.4s} %{id:03x} %{message}`) - 不要在
lumberjack层做格式化 —— 它只管 IO,否则会导致多级时间戳或重复级别字段 - 若需 JSON 格式,得自己写 formatter,
logging不内置 JSON encoder
为什么 defer lj.Close() 通常没必要,但要留意进程退出场景
lumberjack.Logger 的 Close() 主要用于 flush buffer + 关闭当前文件句柄。Go-logging 在程序正常退出时不会自动调用它;但如果进程被 kill -9 或 panic 后未 recover,buffer 中最后几条日志大概率丢失。
实际建议:
- 大多数服务中可忽略
Close(),因为lumberjack在每次Write后都flush(除非设置了Buffered: true,但该字段已废弃) - 仅当使用
signal.Notify捕获SIGINT/SIGTERM时,才应在 handler 里显式调用lj.Close() - 测试时容易漏掉:用
go test运行完不关文件句柄,导致后续测试因 “file in use” 失败 —— 此时需在TestMain里 cleanup
滚动归档真正的复杂点不在配置,而在确认 log rotation 是否真被触发:改小 MaxSize 到 1MB,然后用循环打日志,观察是否生成 app.log.1.gz —— 光看代码对不上真实行为。


















