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)
}
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))
})
}
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))
})
}
Top comments (0)