直接修改 gin.defaultwriter 和 gin.defaulterrorwriter 即可控制 gin 日志输出,前者管访问日志,后者管 panic 堆栈;需用 os.openfile 追加写入、避免覆盖,推荐 lumberjack 实现轮转,并应弃用默认 logger() 以支持结构化日志。

直接改 gin.DefaultWriter 和 gin.DefaultErrorWriter 就行
Gin 的日志输出不是靠中间件“转发”,而是由两个全局 io.Writer 变量控制的:gin.DefaultWriter 负责 Logger() 中间件的访问日志,gin.DefaultErrorWriter 负责 Recovery() 捕获 panic 时的堆栈。只要在 main() 开头就赋值好,后续所有日志都会自动走你指定的目标。
常见错误是:只改了 DefaultWriter 却忘了 DefaultErrorWriter,结果 panic 日志仍打到终端;或者用 os.Create() 覆盖写入,导致每次重启日志清空。
- 用
os.OpenFile(..., os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)确保追加写入 - 为访问日志和错误日志分别开两个文件,避免混杂(比如
"access.log"和"error.log") - 不要在
defer f.Close()里关文件——main()退出才关,但服务长期运行时应统一管理生命周期(如用logrus或zap自带的文件轮转)
想同时输出到文件和控制台?用 io.MultiWriter
开发阶段常需要“文件存档 + 终端实时看”,这时别写两套逻辑,直接组合 io.Writer:
f, _ := os.OpenFile("access.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
gin.DefaultErrorWriter = io.MultiWriter(f, os.Stderr)
注意:io.MultiWriter 是并发安全的,但底层每个 writer 都会收到完整日志字节流。如果其中一个 writer 写得慢(比如网络日志服务),会拖慢整个 HTTP 请求处理——生产环境慎用,优先选异步写入方案(如 lumberjack + logrus)。
用 lumberjack 实现日志切割,避免单文件爆炸
线上服务跑几天,access.log 动辄几个 GB,查起来卡、备份麻烦、磁盘告警。Gin 本身不提供轮转,得靠第三方库补足。
gopkg.in/natefinch/lumberjack.v2 是最轻量可靠的选项,它实现了 io.WriteCloser,可直接赋给 DefaultWriter:
logger := &lumberjack.Logger{
Filename: "access.log",
MaxSize: 100, // MB
MaxBackups: 7,
MaxAge: 28, // days
Compress: true,
}
gin.DefaultWriter = logger
gin.DefaultErrorWriter = logger // 或单独配 error.log
关键点:
-
MaxSize单位是 MB,不是字节;设太小会导致频繁切片,影响性能 -
Compress: true会用 gzip 压缩归档文件,但首次压缩可能略卡(不影响主流程) - 它不支持按小时切片,只支持大小/天数/备份数三者触发,别硬凑“每小时一个文件”
别用默认 Logger() 中间件进生产环境
即使你把日志重定向到文件了,gin.Logger() 输出的仍是固定格式的空格分隔字符串,比如:
[GIN] 2026/09/04 - 22:05:12 | 200 | 12.345ms | 127.0.0.1 | GET /api/user
这种格式没法被 ELK、Loki 等日志系统结构化解析,也加不了 trace_id、user_id 等上下文字段。真正要上生产,必须绕过它:
- 用
gin.LoggerWithWriter()+ 自定义格式化 writer(解析原始日志再拼 JSON) - 或彻底弃用,手写中间件 +
logrus/zap,自己取c.Writer.Status()、c.ClientIP()等字段构造结构化日志 - 尤其注意:
Logger()把 404、400 都当普通日志打,而Recovery()只捕 panic——HTTP 错误码不会进 error 日志,这点容易误判问题严重性
日志路径和格式可以后期调整,但 writer 的生命周期、并发写入阻塞、结构化能力这三点,上线前就得定死。











