Logrus 本身不支持自动滚动,必须搭配 rotatelogs 或 lumberjack 等第三方 writer;rotatelogs 原生支持 %Y%m%d 时间格式实现按天命名(如 app-20260813.log),而 lumberjack 的 Filename 不解析时间占位符,仅静态命名且默认当前日志无日期。

Logrus 本身不支持自动滚动,必须搭配第三方轮转 writer(如 rotatelogs 或 lumberjack)才能实现文件按大小或日期滚动;直接调用 logrus.SetOutput() 传一个固定 *os.File 会导致单文件无限增长。
rotatelogs 实现按日期命名 + 自动滚动
这是目前最贴近“按天切分”需求的方案:rotatelogs 原生支持 %Y%m%d 这类 strftime 格式,每次滚动都会生成带当天日期的新文件(如 app-20260813.log),并可软链接最新日志。
-
rotatelogs.New()返回的 writer 必须提前确保目录存在,否则日志写入静默失败 —— 建议在初始化前调用os.MkdirAll("logs", 0755) - 文件名模板必须含时间占位符,例如
"logs/app-%Y%m%d.log";%Y是年、%m是月、%d是日,不是 Go 的time.Format风格 -
WithRotationTime(24 * time.Hour)控制滚动周期,但实际触发依赖系统时钟精度,建议配合WithLinkName("logs/app-current.log")指向最新日志 - Windows 下软链接可能失效,生产环境需测试;若不可用,可改用
WithRotationCount(7)限制保留个数
lumberjack 只能按大小/保留天数滚动,不能按日期命名
lumberjack.Logger 的 Filename 字段是静态字符串,不解析时间格式 —— 给它设成 "app-%Y%m%d.log" 不会生效,只会创建字面名为 app-%Y%m%d.log 的文件。
- 它的
MaxAge是“保留多少天的归档文件”,不是“每天新建一个当前日志”;默认当前日志始终叫foo.log,归档后才带时间戳(如foo.log.2026-08-12) - 若硬要模拟“每日零点切分”,得自己起 goroutine 定时调用
lumberjack.Rotate(),且必须设LocalTime: true,否则时间戳错乱 -
MaxSize: 0可禁用大小触发,只靠MaxAge和手动Rotate()驱动,但要注意并发调用Rotate()会 panic,需加锁或串行 channel - 别把
lumberjack.Logger直接丢给logrus.SetOutput()就完事 —— 它的Close()行为可能导致缓冲未刷完,推荐用signal.Notify捕获退出信号后显式关闭
Gin 中集成 Logrus + rotatelogs 的关键陷阱
Gin 默认日志走 gin.DefaultWriter,而 Logrus 是独立 logger;二者混用时容易漏日志或格式错乱。常见错误是只配了 Logrus 输出,却忘了 Gin 的中间件日志仍打到控制台。
- 想统一管理,应禁用 Gin 默认 Logger:启动前设
gin.SetMode(gin.ReleaseMode),再用gin.DefaultWriter = io.Discard - 自定义 Gin 日志中间件时,务必复用同一个
*logrus.Logger实例,避免多个 writer 冲突 -
logrus.SetReportCaller(true)开启行号需注意性能损耗,调试期开,生产可关;同时确保logrus.SetFormatter()返回的格式兼容rotatelogs的 writer 接口 - 如果同时要控制台+文件双输出,用
io.MultiWriter包裹rotatelogs.Writer和os.Stdout即可,但注意MultiWriter是阻塞写,任一 writer 卡住会影响全部
真正按日期滚动的核心不在 Logrus,而在 writer —— rotatelogs 是少数能原生解析时间模板的选项,lumberjack 更适合稳态大流量场景;选哪个取决于你是否强依赖“文件名带当天日期”这个视觉标识。后者容易忽略的是:所有轮转库都要求路径可写、磁盘有空间、程序有权限创建文件,这些基础检查常被跳过,直到日志突然不写了才发现。











