Logging and Request ID Middleware
Structured logging and trace ID injection
Logging and Request ID Middleware is a free Go Academy lesson on CoddyKit. This is lesson 2 of 4. Any 3 lessons of this learning path are free to read in full — after that, CoddyKit PRO unlocks every lesson, plus hands-on practice with a built-in code editor and a 24/7 AI tutor. It is part of the Go Academy learning path, and your progress syncs across the web and the CoddyKit app. The Go Academy course includes 4 lessons in total.
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
Learn Go with an AI tutor — free
Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.
- Courses
- 51
- Lessons
- 203
Frequently Asked Questions
Is the “Logging and Request ID Middleware” lesson free?
Yes — any 3 lessons of the Go Academy learning path, including “Logging and Request ID Middleware”, are free to read in full here on the web. After that, CoddyKit PRO unlocks every lesson, plus interactive practice with a built-in code editor and a 24/7 AI tutor. The Go Academy course includes 4 lessons in total.
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, so you can start here or from the beginning and move at your own pace. This is lesson 2 of 4.
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