lumberjack无法真正按日期切割日志,MaxAge仅按文件mtime删除旧备份而非按写入日期归档;需封装DateRotatingWriter拦截Write()检查日期变更,或改用stdout+外部工具轮转。

按日期 + 大小双维度自动归档,lumberjack 本身做不到,必须组合封装或换方案——它只认 MaxSize 和 MaxAge,而 MaxAge 是按文件最后修改时间删旧,不是按写入日期归档。
lumberjack 的 MaxAge 不等于“按日期切割”
很多人误以为设置 MaxAge: 1 就能每天一个文件,其实不是:
• MaxAge 只控制“超过 N 天没被修改的备份文件是否删除”,不触发新文件创建
• 它判断依据是文件系统 mtime,不是日志写入时间
• 若进程长时间无日志(比如凌晨停写),当天日志可能被 MaxAge 误删;若 NTP 校正导致时钟跳变,又可能漏删或错删
• Filename 固定为字符串,无法动态插入选项如 "app-%s.log" —— 它不解析模板
想严格按天切文件?得自己包一层 DateRotatingWriter
核心是拦截每次 Write(),检查日期是否变更,再触发 Rotate():
• 必须显式调用 lumberjack.Logger.Close(),否则缓冲区日志丢失
• Filename 字段要 runtime 更新(lumberjack 允许改,但需先 Close)
• 设置 MaxAge: 0 关闭 lumberjack 自带的过期清理,避免和你的日期逻辑冲突
• 检查频率别太高,用 sync.RWMutex + 缓存 today 字符串,每 10 秒查一次就够了
• 切换瞬间要 RWMutex.Lock(),防止 goroutine 写到已关闭的文件句柄,报 write: bad file descriptor
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
更稳的选择:别在 Go 进程里做轮转
把轮转交给外部工具,Go 只管往 stdout 写:
• systemd 服务配 StandardOutput=journal + logrotate 或 journald 自动归档
• Docker/K8s 环境下用 stdout + fluentd / filebeat 收集,按时间/大小切片压缩
• 避开所有 Go 层面的竞态、Close 遗漏、时区漂移问题
• 运维视角更统一:日志路径、权限、压缩策略全由 infra 控制,应用代码零耦合
• 唯一代价:失去结构化日志字段的原生文件落地(但 JSON 行格式仍可被 collector 解析)
立即学习“go语言免费学习笔记(深入)”;
真正容易被忽略的是:lumberjack.Logger 的 Close() 必须在进程退出前同步执行,defer 在 main goroutine 快速退出时大概率来不及——得用 signal.Notify 捕获 SIGTERM 后手动调。还有,Windows 下 os.Rename 对打开的文件失败,而 lumberjack 在 Windows 上默认用 rename,没 fallback,这点文档根本不提。

















