go 42 lines · 6 steps

Building a request logger middleware in Gin

A Gin middleware that times each request and logs its outcome at a severity matching the response status.

Explained by highlit
1package middleware
2 
3import (
4 "time"
5 
6 "github.com/gin-gonic/gin"
7 "github.com/rs/zerolog"
8)
9 
10func RequestLogger(logger zerolog.Logger) gin.HandlerFunc {
11 return func(c *gin.Context) {
12 start := time.Now()
13 path := c.Request.URL.Path
14 if raw := c.Request.URL.RawQuery; raw != "" {
15 path = path + "?" + raw
16 }
17 
18 c.Next()
19 
20 latency := time.Since(start)
21 status := c.Writer.Status()
22 
23 evt := logger.Info()
24 if len(c.Errors) > 0 {
25 evt = logger.Error().Str("errors", c.Errors.String())
26 } else if status >= 500 {
27 evt = logger.Error()
28 } else if status >= 400 {
29 evt = logger.Warn()
30 }
31 
32 evt.
33 Str("method", c.Request.Method).
34 Str("path", path).
35 Int("status", status).
36 Dur("latency", latency).
37 Int("bytes", c.Writer.Size()).
38 Str("client_ip", c.ClientIP()).
39 Str("request_id", c.GetString("request_id")).
40 Msg("handled request")
41 }
42}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Middleware can wrap the rest of the chain by capturing state before `c.Next()` and reading results after it returns.
  2. 2Choosing log level from status code and errors keeps noise low while surfacing real failures.
  3. 3Returning a closure over a dependency like a logger is Go's idiomatic way to inject configuration into a handler.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building a request logger middleware in Gin — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code