如何自定义Go Gin记录器格式?

b5buobof  于 2023-08-01  发布在  Go
关注(0)|答案(1)|浏览(138)

如何自定义Gin API应用程序的日志格式?
我试着用logrus包来做,但我的问题是:当我故意制造了404或400错误时,控制台中没有显示错误消息。
我还希望记录器在记录器中显示响应主体。

func requestLoggingMiddleware(logger *logrus.Logger) gin.HandlerFunc {
    return func(c *gin.Context) {
        // Read the request body
        bodyBytes, _ := ioutil.ReadAll(c.Request.Body)
        c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
        bodyString := string(bodyBytes)

        // Process request
        c.Next()

        // Log request details
        // Include Request error message
        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":         bodyString,
            // "res_error_1":        c.Errors.ByType(gin.ErrorTypePrivate).String(),
            "res_error_2": c.Errors.String(),
        }).Info("Request details")
    }
}

func main() {
    logrus.Info("Starting the application...")

    // 1. Create a new instance of the application.
    app := gin.New()

    // OPTION 1
    logger := logrus.New()
    logger.SetLevel(logrus.InfoLevel)
    app.Use(gin.LoggerWithWriter(logger.Writer()))
    app.Use(requestLoggingMiddleware(logger))
    ...
}

字符串
以下是控制台上显示的内容:

INFO[0015] Request details                               body="{\"d\":\"D\"}" error= method=POST path=/ping query_params="map[]" status=404

fcipmucu

fcipmucu1#

正如@PRATHEESH PC所建议的,我根据你的需要整理了一个小例子。该代码分为三个部分:中间件、HTTP处理程序和主程序包。让我们从HTTP处理程序开始。

handlers/handlers.go文件

package handlers

import (
    "net/http"

    "github.com/gin-gonic/gin"
    "github.com/gin-gonic/gin/binding"
)

type message struct {
    Name string `json:"name" binding:"required"`
}

func Ping(c *gin.Context) {
    var message message
    if err := c.ShouldBindBodyWith(&message, binding.JSON); err != nil {
        c.JSON(http.StatusBadRequest, err.Error())
        return
    }
    c.JSON(http.StatusOK, message)
}

字符串
这里唯一要提到的是ShouldBindBodyWith方法的使用,它使您有可能多次读取请求负载。实际上,第一次调用它(在中间件中)时,它会将请求体复制到上下文中。后续调用将从那里读取正文(例如,HTTP处理程序中的调用)。

middlewares/middlewares.go文件

package middlewares

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"

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

type ginBodyLogger struct {
    // get all the methods implementation from the original one
    // override only the Write() method
    gin.ResponseWriter
    body bytes.Buffer
}

func (g *ginBodyLogger) Write(b []byte) (int, error) {
    g.body.Write(b)
    return g.ResponseWriter.Write(b)
}

func RequestLoggingMiddleware(logger *logrus.Logger) gin.HandlerFunc {
    return func(ctx *gin.Context) {
        ginBodyLogger := &ginBodyLogger{
            body:           bytes.Buffer{},
            ResponseWriter: ctx.Writer,
        }
        ctx.Writer = ginBodyLogger
        var req interface{}
        if err := ctx.ShouldBindBodyWith(&req, binding.JSON); err != nil {
            ctx.JSON(http.StatusBadRequest, err.Error())
            return
        }
        data, err := json.Marshal(req)
        if err != nil {
            panic(fmt.Errorf("err while marshaling req msg: %v", err))
        }
        ctx.Next()
        logger.WithFields(log.Fields{
            "status":       ctx.Writer.Status(),
            "method":       ctx.Request.Method,
            "path":         ctx.Request.URL.Path,
            "query_params": ctx.Request.URL.Query(),
            "req_body":     string(data),
            "res_body":     ginBodyLogger.body.String(),
        }).Info("request details")
    }
}


在这里,我们主要做了三件事。
首先,我们定义了一个ginBodyLogger结构体,它嵌入了gin.ResponseWriter结构体,并添加了一个bytes.Buffer来记录我们所关心的响应有效负载。
然后,我们为方法Write提供了一个自定义实现,在写入响应流时将调用该方法。在写入之前,我们将把信息保存在属于ginBodyLogger结构的缓冲区中。
最后,我们通过提供给中间件函数的logger跟踪此信息。

main.go文件

package main

import (
    "ginlogger/handlers"
    "ginlogger/middlewares"

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

var logger *log.Logger

func init() {
    logger = logrus.New()
    logger.SetLevel(log.InfoLevel)
}

func main() {
    gin.SetMode(gin.DebugMode)
    r := gin.Default()

    r.Use(middlewares.RequestLoggingMiddleware(logger))
    r.Use(gin.LoggerWithWriter(logger.Writer()))
    r.POST("/ping", handlers.Ping)

    r.Run(":8000")
}


main包负责初始化我们程序中所需的一切。它涉及两个功能:initmain
init函数中,我们初始化记录器。
main函数中,我们初始化gin.Engine示例,对其进行检测,然后运行。
如果您运行代码,您应该具有所需的日志。如果不是这样的话,请告诉我,我会回来找你的,谢谢!

相关问题