DEV Community

Cover image for Adding Request ID to Go's requests
Peter Paravinja
Peter Paravinja

Posted on

Adding Request ID to Go's requests

Why

Why would you use request IDs?
Request IDs can help you diagnose problems by correlating log entries for a given web request across many log entries or files.
It is a way to track and "brand" the request that travels across your application(s).

Tools

xid tool that helps us create a new unique id
chi lightweight router

Creating a ctx utility

First we create an utility function that helps us set and get request values in (or out of) context.

I usually save it inside <root project>/utils/ctx/ctx.go, but whatever floats your boat.

package ctx

import (
    "context"
)

const keyRequestID key = "requestID"

type key string

func RequestID(ctx context.Context) string {
    requestID, _ := ctx.Value(keyRequestID).(string)

    return requestID
}

func SetRequestID(ctx context.Context, requestID string) context.Context {
    return context.WithValue(ctx, keyRequestID, requestID)
}

Enter fullscreen mode Exit fullscreen mode

Creating middleware function

This is the middleware function that we will plug in the chi router.

What we're doing is first checking if X-Request-ID is set - and we use that ID to 'drag it' across our request. We can then append it to every log statement that we do. Every (not really sure if every) logging go library enables adding more context to log entry - so we just append it to everything.

If it doesn't exist - we just create a new unique ID.

Whichever requestID we get - we set use our set ctx function from before.

With our enriched ctx we just call next serve HTTP with enriched ctx.

package middleware

import (
    "net/http"

    "github.com/rs/xid"

    ctxUtil "github.com/example/example-repo/utils/ctx"
)

const requestIDHeaderKey = "X-Request-ID"

func RequestID(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ctx := r.Context()

        requestID := r.Header.Get(requestIDHeaderKey)
        if requestID == "" {
            requestID = xid.New().String()
        }

        ctx = ctxUtil.SetRequestID(ctx, requestID)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}
Enter fullscreen mode Exit fullscreen mode

Router and route example

Just a simple example of a chi router with a route that shows how we actually pull the request ID out of context and use it as a value inside the route.

package main

import (
    "net/http"

    "github.com/go-chi/chi/v5"
    "github.com/example/example-repo/api/middleware"
    ctxUtil "github.com/example/example-repo/utils/ctx"
)

func main() {
    chiRouter := chi.NewRouter()
    chiRouter.Use(middleware.RequestID)

    chiRouter.Get("/", func(w http.ResponseWriter, r *http.Request) {
        reqID := ctxUtil.RequestID(r.Context())
        w.Write([]byte(reqID))
    })
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)