Gin 默认日志不满足生产需求,因其输出到os.Stdout、无级别区分、不支持滚动切割、缺失trace ID与代码位置信息,且无法分离access log和app log。

为什么 Gin 默认日志不满足生产需求
Gin 自带的 gin.DefaultWriter 会把日志输出到 os.Stdout,且格式固定、无日志级别区分、不支持滚动切割。线上服务一旦并发上来,日志混杂、无法按 level 过滤、丢失时间精度,排查问题时基本靠猜。
- 默认日志不带 trace ID,上下游请求无法串联
- 没有
WARN/ERROR级别标识,告警难触发 - 日志行不包含文件名和行号,定位代码慢
- 所有日志写入同一 stdout 流,无法分离 access log 和 app log
用 gin.LoggerWithWriter 替换默认中间件
Gin 提供了替换日志中间件的能力,但必须在 gin.New() 或 gin.Default() 后立即设置,否则 gin.Logger() 已被自动注册,再覆盖无效。
router := gin.New() // 必须在 Use 之前配置 writer,否则无效 gin.DefaultWriter = io.MultiWriter(os.Stdout, file) router.Use(gin.LoggerWithWriter(gin.DefaultWriter))
-
gin.LoggerWithWriter接收一个io.Writer,可传入os.Stdout、os.Stderr或自定义*os.File - 若需同时输出到控制台和文件,用
io.MultiWriter组合多个 writer - 不要调用
router.Use(gin.Logger()),它内部硬编码使用gin.DefaultWriter,改写DefaultWriter才生效
用 logrus 或 zap 实现结构化日志
Gin 原生日志是纯文本,没法打结构字段(如 user_id、req_id)。推荐用 zap(性能高)或 logrus(生态熟),配合自定义中间件注入上下文日志。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
func LoggerZap(zapLogger *zap.Logger) gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
path := c.Request.URL.Path
c.Next()
<pre class="brush:php;toolbar:false;"> duration := time.Since(start)
zapLogger.Info("http request",
zap.String("path", path),
zap.Int("status", c.Writer.Status()),
zap.Duration("duration", duration),
zap.String("method", c.Request.Method),
zap.String("ip", c.ClientIP()),
)
}}
- 中间件里不要直接调用
c.AbortWithError()后再打日志,可能 panic;应统一在c.Next()后取c.Writer.Status() - 若需携带 trace ID,提前在中间件中从 header 解析并存入
c.Set("trace_id", ...),再在日志中取出 -
zap的Info/Error方法不自动加时间戳,需确保 logger 初始化时已启用zap.AddCaller()和zap.AddStacktrace()
避免日志阻塞 HTTP 请求
日志写入磁盘或网络(比如发到 Loki)如果同步执行,会拖慢接口响应。尤其 zap 默认是同步写,高频请求下易成瓶颈。
立即学习“go语言免费学习笔记(深入)”;
- 使用
zap.NewAsync包装 logger,异步写入(注意:异步可能导致进程退出时日志丢失,需调用logger.Sync()) - 文件日志建议用
lumberjack库做轮转:lumberjack.Logger{Filename: "app.log", MaxSize: 100, MaxBackups: 5} - 不要在日志字段里传大对象(如整个
c.Request.Body),会触发深拷贝和 GC 压力 - Gin 中间件日志粒度宜粗不宜细:记录路径、状态码、耗时、IP 即可;详细参数应在业务逻辑里按需打 debug 日志,而非全量进 access log
实际部署时,最常被忽略的是日志权限和磁盘满风险——app.log 路径若设为 /var/log/myapp/,务必确保运行用户有写权限;MaxBackups 设太小会导致旧日志被删,太大则占满磁盘。

















