lumberjack 不支持日志分级,因其仅为 io.WriteCloser,只负责文件写入与轮转,不解析日志级别;分级必须由上层日志库(如 slog.Handler、logrus、zap)在写入前完成过滤。

Go 标准库的 slog 和 log 本身不支持日志分级(按 level 过滤)+ 轮转(按大小/时间切分)组合能力,必须靠第三方 Writer 包装器实现,而 lumberjack 只负责轮转,不处理分级——分级得由上层日志库(如 zap、logrus)或 slog.Handler 自己做。
为什么 lumberjack 单独配不上日志分级
lumberjack.Logger 是个 io.WriteCloser,只管“把字节写到哪、什么时候切文件”,它完全不知道哪条是 INFO、哪条是 ERROR。你直接把它塞给 slog.NewTextHandler 或 log.SetOutput,所有级别日志都会无差别写入,分级过滤必须在它上游完成。
- 用
slog:得自己写一个slog.Handler实现,比如包装slog.TextHandler,在Handle方法里判断r.Level再决定是否调用底层lumberjack.Write - 用
logrus:直接设logrus.SetLevel(logrus.ErrorLevel),再logrus.SetOutput(&lumberjack.Logger{...}),分级由 logrus 做,轮转由 lumberjack 做 - 用
zap:通过zapcore.NewCore的第三个参数enab(LevelEnabler)控制,例如传zapcore.ErrorLevel,再把lumberjack.Logger封进zapcore.AddSync作为WriteSyncer
slog + lumberjack 怎么加分级(不依赖其他库)
标准 slog 没内置分级 Handler,但你可以轻量封装一个带过滤的 Handler:
type levelFilterHandler struct {
handler slog.Handler
minLevel slog.Level
}
<p>func (h levelFilterHandler) Handle(r slog.Record) error {
if r.Level < h.minLevel {
return nil // 丢弃低于阈值的日志
}
return h.handler.Handle(r)
}</p><p>// 使用示例:
lj := &lumberjack.Logger{
Filename: "/var/log/app.log",
MaxSize: 10,
MaxBackups: 7,
LocalTime: true,
}
logger := slog.New(levelFilterHandler{
handler: slog.NewTextHandler(lj, nil),
minLevel: slog.LevelError, // 只写 ERROR 及以上
})
注意:slog.Level 是 int 类型,slog.LevelInfo == 0,slog.LevelError == 12,数值越大级别越高;别写反比较方向。
立即学习“go语言免费学习笔记(深入)”;
logrus/zap 配 lumberjack 时分级失效的常见原因
不是 lumberjack 的锅,而是注入位置错了:
-
logrus:必须用logrus.SetOutput(&lumberjack.Logger{}),不能只改logrus.StandardLogger().Out或包一层io.MultiWriter,否则分级逻辑被绕过 -
zap:必须确保zapcore.NewCore的enab参数生效,且WriteSyncer是zapcore.AddSync(&lumberjack.Logger{}),不是os.File或其他中间 writer - 共性坑:多个 logger 实例各自配了不同
lumberjack.Logger,导致轮转状态不一致、文件权限混乱、甚至write on closed filepanic
MaxSize 单位和轮转时机容易误判
lumberjack.MaxSize 单位是 MB(整数),但触发条件是「当前文件大小 ≥ MaxSize * 1024 * 1024 字节」,且只在每次 Write() 开头检查——所以实际文件可能略超(比如设 10MB,最终是 10.2MB 才切)。这不是 bug,是设计使然。
- 不要指望精确卡在 10MB 切,更别在循环里每条日志都
Flush()强制检查(性能崩) - 如果日志单条很大(比如 dump 二进制数据),一次
Write()就超限,会立刻切;如果单条很小,可能累积几十万条才触发 -
MaxAge和MaxBackups是“或”关系:任一条件满足就清理旧文件,不是两个都得满足
真正难搞的是跨进程协作:比如你用 logrotate 配合 SIGHUP 重载,又同时开了 lumberjack.MaxAge,旧文件可能被两边同时操作,导致丢失或 text file busy。线上建议二选一,别混用。


















