如何自定义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
相关产品推荐
相关产品推荐

