Gin: custom middleware logging the request body

Created on 28 Jun 2017  路  5Comments  路  Source: gin-gonic/gin

for debugging my program, I need to logging request body so write the below middleware
but it's wasn't called by gin

gin version f931d1ea80ae95a6fc739213cdd9399bd2967fb6

type MyReadCloser struct {
    rc io.ReadCloser
    w  io.Writer
}

func (rc *MyReadCloser) Read(p []byte) (n int, err error) {
    n, err = rc.rc.Read(p)
    log.Println("run here", n, err)
    if n > 0 {
        if n, err := rc.w.Write(p[:n]); err != nil {
            return n, err
        }
    }
    return n, err
}

func (rc *MyReadCloser) Close() error {
    return rc.rc.Close()
}

func DebugLogger() gin.HandlerFunc {
    return func(c *gin.Context) {
        log.Println(c.Request.RequestURI)
        if c.Request.Method == http.MethodPost {
            var buf bytes.Buffer
            newBody := &MyReadCloser{c.Request.Body, &buf}
            c.Request.Body = newBody
            c.Next()
            log.Println(buf.String())
        } else {
            c.Next()
        }
    }
}

r := gin.New()
if cfg.Debug {
    r.Use(middleware.DebugLogger())
}

Most helpful comment

@tfzxyinhao I prefer using gin model binding, but if you just want to logging the request body, you can create RequestLogger middleware as follows:

package main

import (
    "bytes"
    "fmt"
    "io"
    "io/ioutil"

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

func main() {
    r := gin.New()
    r.Use(RequestLogger())

    r.POST("/postpath", func(c *gin.Context) {
        //...
    })

    r.Run()
}

func RequestLogger() gin.HandlerFunc {
    return func(c *gin.Context) {
        buf, _ := ioutil.ReadAll(c.Request.Body)
        rdr1 := ioutil.NopCloser(bytes.NewBuffer(buf))
        rdr2 := ioutil.NopCloser(bytes.NewBuffer(buf)) //We have to create a new Buffer, because rdr1 will be read.

        fmt.Println(readBody(rdr1)) // Print request body

        c.Request.Body = rdr2
        c.Next()
    }
}

func readBody(reader io.Reader) string {
    buf := new(bytes.Buffer)
    buf.ReadFrom(reader)

    s := buf.String()
    return s
}

All 5 comments

Your MyReadCloser is not called because Read(b []byte) (n int, err error) is called before you set &MyReadCloser on c.Request.Body.

As you know, log.Println(c.Request.RequestURI) on DebugLogger() is working as you want.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] Environment variable PORT is undefined. Using port:8080 by default
[GIN-debug] Listening and serving HTTP on :8080
/test

@tfzxyinhao I prefer using gin model binding, but if you just want to logging the request body, you can create RequestLogger middleware as follows:

package main

import (
    "bytes"
    "fmt"
    "io"
    "io/ioutil"

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

func main() {
    r := gin.New()
    r.Use(RequestLogger())

    r.POST("/postpath", func(c *gin.Context) {
        //...
    })

    r.Run()
}

func RequestLogger() gin.HandlerFunc {
    return func(c *gin.Context) {
        buf, _ := ioutil.ReadAll(c.Request.Body)
        rdr1 := ioutil.NopCloser(bytes.NewBuffer(buf))
        rdr2 := ioutil.NopCloser(bytes.NewBuffer(buf)) //We have to create a new Buffer, because rdr1 will be read.

        fmt.Println(readBody(rdr1)) // Print request body

        c.Request.Body = rdr2
        c.Next()
    }
}

func readBody(reader io.Reader) string {
    buf := new(bytes.Buffer)
    buf.ReadFrom(reader)

    s := buf.String()
    return s
}

thanks @suzuken @easonlin404
i think replace c.Request.Body at first is the perfect way

Simple LoggingMiddleware

func RequestLoggerMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        var buf bytes.Buffer
        tee := io.TeeReader(c.Request.Body, &buf)
        body, _ := ioutil.ReadAll(tee)
        c.Request.Body = ioutil.NopCloser(&buf)
        log.Debug(string(body))
        log.Debug(c.Request.Header)
        c.Next()
    }
}

@BogdanRuzh do you know how to implement that using LoggerWithFormatter ? :eyes: e.g. https://github.com/gin-gonic/gin#custom-log-format

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mastrolinux picture mastrolinux  路  3Comments

rawoke083 picture rawoke083  路  3Comments

xpbliss picture xpbliss  路  3Comments

cxk280 picture cxk280  路  3Comments

ghost picture ghost  路  3Comments