tezvyn:

Logging middleware wrapping an http.Handler in Go

AI-drafted, machine-checkedSource: interviewintermediate
WHAT IT TESTS

the http.Handler middleware pattern.

OUTLINE

middleware has signature func(http.Handler) http.Handler, records start time, calls next.ServeHTTP, then logs method, URL, and elapsed duration; chaining works because the wrapper is itself a…

WHAT THIS TESTS Whether you understand the idiomatic Go middleware shape, the decorator pattern over http.Handler, and why that signature enables composition.

A GOOD ANSWER COVERS The canonical signature is func(next http.Handler) http.Handler: it accepts the handler to wrap and returns a new handler. You implement the body by returning http.HandlerFunc, which adapts a plain function to the Handler interface. Inside that function you record start := time.Now() before delegating, call next.ServeHTTP(w, r) to execute the wrapped handler, and after it returns log the details: r.Method, r.URL.Path or r.URL.String, and the elapsed time via time.Since(start). The crucial property is that because the wrapper is itself an http.Handler, you can nest middlewares: mux := http.NewServeMux(); handler := Logging(Auth(mux)). Each layer wraps the next, forming an onion where the request passes inward and the response unwinds outward, which is exactly why measuring around next.ServeHTTP captures the full downstream processing time.

COMMON WRONG ANSWERS Writing a signature that takes an http.HandlerFunc but does not return http.Handler, breaking composition. Logging before calling next, so the timing excludes the work. Capturing the status code without a wrapped ResponseWriter and assuming you can read it from w directly. Forgetting that ServeHTTP must be called for the request to proceed.

LIKELY FOLLOW-UPS How do you also log the response status code, by wrapping http.ResponseWriter to capture WriteHeader? How do you order middlewares, and does order matter for auth versus logging? How do helper chaining utilities like a Chain function or third-party routers compose these? How do you propagate a request ID through context?

ONE CONCRETE EXAMPLE func Logging(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now(); next.ServeHTTP(w, r); log.Printf("%s %s %s", r.Method, r.URL, time.Since(start)) }) }. Wiring it as Logging(mux) logs each request's method, URL, and total handling duration, and the matching signature lets it stack with other middlewares.

Read the original → alexedwards.net

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.