DEV Community

Nando Theessen
Nando Theessen

Posted on

2

Logging Request Bodies For Golang REST APIs

Go does not support JSON natively, so our first step should be to learn how to decode the JSON format.
Thankfully Go comes with the "json" package which makes decoding easy and convenient to do.

A Basic Example

This simplified example comes from the Go docs:

const jsonStream = `{"Name": "Ed", "Text": "Knock knock."}`

type Message struct {
    Name, Text string
}
var m Message

dec := json.NewDecoder(strings.NewReader(jsonStream))
err := dec.Decode(&m)
if err != nil && err != io.EOF {
    // handle that error
}

fmt.Println(m.Name, m.Text)

After handing our JSON stream over to a Reader, we're using the json.NewDecoder() and dec.Decode(&m) to "translate" and read our data into the previously defined Message format.

The Request Body

When handling an HTTP request, we are doing something very similar already which helps us skip a step here.
I'll also show you a nifty tool, we'll be using for our logging output!

func (h *handlerShared) doSomething(w http.ResponseWriter, r *http.Request) {
    // we still need a type definition for the receiving struct! 

    type Request struct {
        CompanyName string
    }
    // create a pointer to said struct
    req := &Request{}

    // create Decoder to "translate" our data
    // you probably spotted the step we can skip already!
    // if you haven't: the request body already comes in a Reader,
    // we don't have to hand it to one!
    decoder := json.NewDecoder(r.Body)

    err := decoder.Decode(&req)
    if err != io.EOF {
        // handle the error 
    }

    // At this point, we can a) access the data we read into our Request
    // struct: 
    fmt.Println(req.CompanyName)

    // As well as log it using the logrus logger I've included on my handlers                    
    h.log.WithFields(logrus.Fields{
        "host":     r.URL.Host,
        "path":     r.URL.Path,
        "header":   r.Header,
        // as you can see, there is a lot the logger can do for us
        // however "body": r.Body will not work, and always log an empty string!
        "body":     req
        // this is why we'll log our crated struct instead.
    })
    // send a response!
}

This is a very easy example, but should give you a good idea how to implement logging for your endpoints.

I've seen a few examples that go an extra step and read the body into an input buffer, to then cast it to a string. That is not necessary.

If you know of an easier way to achieve this or have any other feedback please let me know. I'll update this article and am always eager to learn!

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay