最轻量方案是用 file-rotatelogs 替换 gin.DefaultWriter 实现按天轮转,需预建目录、禁用 Windows 软链、注意时区偏移与权限问题。

直接用 gin.DefaultWriter 接一个按天轮转的 io.Writer 就能解决,不需要引入 logrus 或 zap —— 除非你 already 需要结构化日志、多级输出或字段注入等高级能力。
用 file-rotatelogs 替换 gin.DefaultWriter
这是最轻量、侵入最小的方案。Gin 的日志本质就是写入一个 io.Writer,只要这个 Writer 每天自动换文件,就完成了“按天分割”。
-
file-rotatelogs是纯时间驱动的轮转器,不依赖进程重启或手动触发,WithRotationTime(24 * time.Hour)即可保证每天零点(或首次写入后 24 小时)切新文件 - 注意
WithLinkName参数:它会创建软链接(如access.log→access.log.20260905),方便程序固定读取,但 Windows 不支持软链,此时应禁用该选项 - 路径必须可写,且父目录需提前存在;
os.MkdirAll要在rotatelogs.New之前调用,否则首次初始化失败会静默返回 nil writer - 别把
rotatelogs.WithMaxAge设太小(比如1 * time.Hour),它控制的是旧文件保留时长,不是切割周期 —— 切割由WithRotationTime决定
避免 gin.Logger() 中间件与自定义 Writer 冲突
Gin 的 Logger() 中间件默认使用 gin.DefaultWriter 输出,所以只需提前设置好它,中间件就会自动生效。但如果你手动注册了 Logger() 并传入自定义 io.Writer,就会覆盖全局设置,导致双写或漏写。
- 不要同时调用
router.Use(gin.LoggerWithWriter(...))和设置gin.DefaultWriter,二者选其一 - 若需终端+文件双输出,用
io.MultiWriter(fileWriter, os.Stdout),别用两个独立的Logger()实例 - 调试时发现日志没进文件?先检查
gin.DefaultWriter == nil是否为 true —— 这说明你没成功赋值,常见于rotatelogs.New报错后忽略 err
Windows 下绕过软链接 + 权限限制
Windows 不支持 WithLinkName 创建的符号链接,且 rotatelogs 在部分 NTFS 策略下可能因权限无法 rename 日志文件。这时得降级处理:
- 去掉
rotatelogs.WithLinkName(...),接受文件名带日期后缀(如access.20260905.log) - 确保运行用户对日志目录有“修改”权限,而非仅“写入”——rename 操作需要修改权限
- 如果仍报
Access is denied,改用lumberjack.Logger(配合 zap)或自己实现一个基于os.OpenFile+ 时间判断的简易轮转 Writer,它不 rename,只新建 - 注意:Windows 计划任务或服务方式运行 Gin 时,当前工作目录不一定是项目根目录,日志路径建议用绝对路径,如
D:\logs\gin\access.%Y%m%d.log
真正容易被忽略的是时间精度和时区:所有 %Y%m%d 类格式都按本地时区计算,如果你服务器设的是 UTC,而业务要求按北京时间切分,就得在生成文件名前手动加 8 小时偏移 —— file-rotatelogs 本身不提供时区参数。


















