You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何自定义Go Gin日志格式?解决logrus无错误信息及响应体显示问题

Gin + Logrus 日志问题解决方案

问题1:404/400错误无日志信息输出

Gin默认的404、400等错误不会自动写入c.Errors集合,且gin.New()初始化的实例也不会自动处理这类错误的日志记录。需要手动捕获这些错误场景,把错误信息注入到日志字段中。

问题2:日志中显示响应体

Gin的ResponseWriter是单向写入的,无法直接读取已返回的响应内容。必须自定义一个包装类,拦截响应的写入操作,同时保存响应体内容。


完整修改代码

1. 自定义ResponseWriter捕获响应体

import (
	"bytes"
	"io/ioutil"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
)

// responseRecorder 包装Gin的ResponseWriter,用于捕获响应体
type responseRecorder struct {
	gin.ResponseWriter
	body *bytes.Buffer
}

// Write 重写Write方法,同时把响应内容写入buffer
func (r *responseRecorder) Write(b []byte) (int, error) {
	r.body.Write(b)
	return r.ResponseWriter.Write(b)
}

2. 修改日志中间件

func requestLoggingMiddleware(logger *logrus.Logger) gin.HandlerFunc {
	return func(c *gin.Context) {
		// 读取并还原请求体
		bodyBytes, err := ioutil.ReadAll(c.Request.Body)
		if err != nil {
			logger.Error("读取请求体失败:", err)
		}
		c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
		reqBody := string(bodyBytes)

		// 替换ResponseWriter为自定义的recorder,捕获响应体
		recorder := &responseRecorder{
			ResponseWriter: c.Writer,
			body:           bytes.NewBufferString(""),
		}
		c.Writer = recorder

		// 执行后续请求处理
		c.Next()

		// 收集错误信息
		var errMsg string
		// 优先取c.Errors中的私有错误
		if len(c.Errors) > 0 {
			errMsg = c.Errors.ByType(gin.ErrorTypePrivate).String()
		} else {
			// 处理Gin默认的404/400等状态码对应的错误信息
			switch c.Writer.Status() {
			case http.StatusNotFound:
				errMsg = "404 页面不存在"
			case http.StatusBadRequest:
				errMsg = "400 请求参数错误"
			case http.StatusMethodNotAllowed:
				errMsg = "405 请求方法不允许"
			}
		}

		// 输出完整日志
		logger.WithFields(logrus.Fields{
			"status":        c.Writer.Status(),
			"method":        c.Request.Method,
			"path":          c.Request.URL.Path,
			"query_params":  c.Request.URL.Query(),
			"req_body":      reqBody,
			"res_body":      recorder.body.String(),
			"error_message": errMsg,
		}).Info("请求详情")
	}
}

3. 主函数配置

func main() {
	logrus.Info("应用启动中...")

	app := gin.New()

	logger := logrus.New()
	logger.SetLevel(logrus.InfoLevel)
	// 只使用自定义日志中间件,避免默认Logger重复输出
	app.Use(requestLoggingMiddleware(logger))

	// 自定义404路由处理,确保响应体被捕获
	app.NoRoute(func(c *gin.Context) {
		c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": "404 页面不存在"})
	})

	// 自定义请求方法不允许的处理
	app.NoMethod(func(c *gin.Context) {
		c.AbortWithStatusJSON(http.StatusMethodNotAllowed, gin.H{"error": "405 请求方法不允许"})
	})

	// 示例路由
	app.POST("/ping", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"message": "pong"})
	})

	_ = app.Run(":8080")
}

关键修改说明

  • 自定义responseRecorder实现了gin.ResponseWriter接口,重写Write方法同时保存响应体到buffer中
  • 中间件中替换默认的c.Writer,确保所有响应内容都被捕获
  • 手动处理404/400等状态码场景,补充错误信息到日志字段
  • 移除了默认的gin.LoggerWithWriter,避免日志重复输出

内容的提问来源于stack exchange,提问作者Jackk-Doe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 15:53:10