Logging and Request ID Middleware
Structured logging and trace ID injection
Logging and Request ID Middleware is a free Go Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Go Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Why structured logging?
Plain text logs are hard to parse at scale. Structured logging (JSON or key=value) lets log aggregators (Datadog, Loki, ELK) filter and correlate entries by field.
Request ID generation
Generate a unique ID per request (UUID or ULID) in middleware and attach it to the response header and context:
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 == "" { id = uuid.New().String() }
w.Header().Set("X-Request-ID", id)
ctx := context.WithValue(r.Context(), reqIDKey{}, id)
next.ServeHTTP(w, r.WithContext(ctx))
})
}Logging middleware with slog
Go 1.21 added log/slog for structured logging. Use it inside middleware:
func StructuredLogger(logger *slog.Logger) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
cw := &captureWriter{ResponseWriter: w}
next.ServeHTTP(cw, r)
logger.Info("request",
"method", r.Method,
"path", r.URL.Path,
"status", cw.status,
"latency_ms", time.Since(start).Milliseconds(),
"request_id", r.Context().Value(reqIDKey{}),
)
})
}
}captureWriter
Wrap the ResponseWriter to capture the status code written by the handler:
type captureWriter struct {
http.ResponseWriter
status int
}
func (cw *captureWriter) WriteHeader(code int) {
cw.status = code
cw.ResponseWriter.WriteHeader(code)
}
func (cw *captureWriter) Status() int {
if cw.status == 0 { return http.StatusOK }
return cw.status
}Adding request ID to every log line
Build a child logger per request with the ID pre-populated, then store it in the context:
reqLogger := logger.With("request_id", id)
ctx := context.WithValue(r.Context(), loggerKey{}, reqLogger)Retrieving the logger from context
Helper to get the request-scoped logger from any handler or service:
func LoggerFromContext(ctx context.Context) *slog.Logger {
l, ok := ctx.Value(loggerKey{}).(*slog.Logger)
if !ok { return slog.Default() }
return l
}Log levels
Use appropriate levels: Debug for development verbosity, Info for request summaries, Warn for recoverable anomalies, Error for actionable failures. Do not log sensitive fields (tokens, passwords).
Latency buckets
Include request latency in every log line. Aggregate into histogram buckets in your metrics system (Prometheus) for P50/P95/P99 percentile tracking.
Correlation IDs in microservices
Forward the X-Request-ID header from incoming requests to all outgoing HTTP and gRPC calls to enable distributed tracing without a full tracing system.
Log sampling
For high-traffic services, sample debug/info logs (e.g., log 1 in 100 requests at debug level) to reduce storage costs while keeping error logs complete.
zap vs slog
zap (Uber) is faster than slog for high-throughput services but requires more setup. slog is standard library, portable, and sufficient for most services. Prefer slog unless benchmarks show a bottleneck.
Quick Check
Why is a captureWriter needed in logging middleware?
Recap: Logging and Request ID
Key points:
- Generate/forward X-Request-ID in middleware; store in context
- Use slog for structured logs with key-value fields
- captureWriter intercepts status code for post-handler logging
- Forward correlation ID to all downstream calls
Frequently asked questions
Is the “Logging and Request ID Middleware” lesson free?
Yes — the full text of “Logging and Request ID Middleware” is free to read here on the web, and the Go Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Go Academy course, upgrade to CoddyKit PRO.
What will I learn in “Logging and Request ID Middleware”?
Structured logging and trace ID injection You practise Go Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Go Academy?
No prior experience is required. Go Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Logging and Request ID Middleware” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Go Academy lesson?
Yes. Every Go Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- HTTP Middleware Chain Fundamentals
- Logging and Request ID Middleware
- Auth and Rate Limiting Middleware
- CORS and Panic Recovery Middleware