Observability Middleware (RED metrics)
Source: Custom
Topics: HTTP middleware, ResponseWriter wrapping, atomics, RED metrics
Problem
Build an http middleware that records the RED signals for every request — Rate,
Errors, Duration — plus a live in-flight gauge. This is the observability layer every
gateway wraps around its handlers.
Requirements:
New() *Metrics
(*Metrics) Middleware(next http.Handler) http.Handler — for each request:
- increment an in-flight gauge on entry, decrement on exit (even if the handler panics? — keep it simple: on normal return via
defer),
- capture the response status code (defaulting to
200 if the handler never calls WriteHeader),
- record count and total duration keyed by
method + " " + path + " " + status.
(*Metrics) Snapshot() Snapshot — return a consistent copy: per-key request count, per-key average latency, and current in-flight count.
type Stat struct {
Count int64
AvgTime time.Duration
}
type Snapshot struct {
InFlight int64
Stats map[string]Stat // key: "GET /users 200"
}
type Metrics struct { ... }
func New() *Metrics
func (m *Metrics) Middleware(next http.Handler) http.Handler
func (m *Metrics) Snapshot() Snapshot
Key concepts
- Wrapping ResponseWriter: to learn the status code you must intercept
WriteHeader. Embed
http.ResponseWriter in a struct, record the code, and forward the call. Default to 200
because a handler that only calls Write never calls WriteHeader.
- atomic gauge: the in-flight count is incremented/decremented concurrently —
atomic.Int64
avoids a mutex on the hot path. The per-key maps need a mutex (maps aren't goroutine-safe).
- RED method: Rate (count), Errors (count of 5xx keys), Duration (avg/percentiles) — the
standard request-level signals for a service, popularized for microservice/gateway monitoring.
Run
go test -v -race -bench=. ./challenges/networking/metrics-middleware/
Sign in to submit your solution.