Search code examples
gogo-gin

Go Gin framework to log in JSON


I am setting up a small API using Go Gin, however I am unable to convince the logger to output JSON. By default it's a key/value string, but I need it as json.

How can I achieve that? I feel it should be easily support but I've been struggling with a custom formatting function where I need to account for various parameters myself.

Alternatively, I also log manually using uber/zap logger but I haven't found a way to replace gin's logger with mine.

Any pointers would be appreciated as the documentation on gin's github wasn't too helpful.

Thanks!

EDIT: to clarify, adding a middlware helps with logging requests, but I'm looking for single point of setting JSON logging for Gin (eg: including logs not related to requests, such as debug / info logs of framework internals)


Solution

  • access logger

    https://github.com/sbecker/gin-api-demo/blob/master/middleware/json_logger.go

    
    // JSONLogMiddleware logs a gin HTTP request in JSON format, with some additional custom key/values
    func JSONLogMiddleware() gin.HandlerFunc {
        return func(c *gin.Context) {
            // Start timer
            start := time.Now()
    
            // Process Request
            c.Next()
    
            // Stop timer
            duration := util.GetDurationInMillseconds(start)
    
            entry := log.WithFields(log.Fields{
                "client_ip":  util.GetClientIP(c),
                "duration":   duration,
                "method":     c.Request.Method,
                "path":       c.Request.RequestURI,
                "status":     c.Writer.Status(),
                "user_id":    util.GetUserID(c),
                "referrer":   c.Request.Referer(),
                "request_id": c.Writer.Header().Get("Request-Id"),
                // "api_version": util.ApiVersion,
            })
    
            if c.Writer.Status() >= 500 {
                entry.Error(c.Errors.String())
            } else {
                entry.Info("")
            }
        }
    }
    

    debug logger

    Looking at the gin source code, it is found that the debug log is output to an io.Writer. Rewriting this object redirects the output to json, similar to the method of processing the output of http.Server.Errorlog.

    func debugPrint(format string, values ...interface{}) {
        if IsDebugging() {
            if !strings.HasSuffix(format, "\n") {
                format += "\n"
            }
            fmt.Fprintf(DefaultWriter, "[GIN-debug] "+format, values...)
        }
    }
    

    set debug write, this code is no test.

    // WriteFunc convert func to io.Writer.
    type WriteFunc func([]byte) (int, error)
    func (fn WriteFunc) Write(data []byte) (int, error) {
        return fn(data)
    }
    
    func NewLogrusWrite() io.Writer {
        return WriteFunc(func(data []byte) (int, error) {
            logrus.Debugf("%s", data)
            return 0, nil
        })
    }
    // set gin write to logrus debug.
    gin.DefaultWriter = NewLogrusWrite()
    

    get all http.Server Error log.

    htt.Server

    htt.Server outputs logs to a log.Logger, and creates a log.Logger output by the specified io.Writer to receive the Error log from http.Sever There is no detailed write gin to use custom Sever code, please check the gin documentation.

    srv := &http.Server{
        // log level is bebug, please create a error level io.Writer
        ErrorLog: log.New(NewLogrusWrite(), "", 0),
    }