Request ID middleware for net/http services

Go
package main

import (
	"context"
	"crypto/rand"
	"encoding/hex"
	"log"
	"net/http"
)

type ctxKey string

const RequestIDKey ctxKey = "request-id"

// RequestID reuses an incoming X-Request-ID header or generates a new one.
func RequestID(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		id := r.Header.Get("X-Request-ID")
		if id == "" {
			buf := make([]byte, 8)
			if _, err := rand.Read(buf); err != nil {
				http.Error(w, "cannot generate id", http.StatusInternalServerError)
				return
			}
			id = hex.EncodeToString(buf)
		}
		w.Header().Set("X-Request-ID", id)
		next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), RequestIDKey, id)))
	})
}

func main() {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Printf("request_id=%v path=%s", r.Context().Value(RequestIDKey), r.URL.Path)
		_, _ = w.Write([]byte("ok\n"))
	})
	http.Handle("/", RequestID(handler))
	log.Fatal(http.ListenAndServe(":8080", nil))
}