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
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Middleware can wrap the rest of the chain by capturing state before `c.Next()` and reading results after it returns.
- 2Choosing log level from status code and errors keeps noise low while surfacing real failures.
- 3Returning a closure over a dependency like a logger is Go's idiomatic way to inject configuration into a handler.
Related explainers
javascript
const cache = new Map(); const inflight = new Map(); async function fetchResults(query) {
Building a typeahead with an LRU cache
caching
lru-eviction
request-deduplication
Intermediate
9 steps
go
package handlers import ( "fmt"
Handling multipart profile uploads in Gin
form binding
file upload
validation
Intermediate
7 steps
typescript
import { ArgumentsHost, Catch, ExceptionFilter,
A catch-all exception filter in NestJS
error-handling
exception-filter
http
Intermediate
8 steps
go
package metrics import ( "sync/atomic"
A lock-free request counter in Go
concurrency
atomics
lock-free
Intermediate
6 steps
javascript
export function createSearchClient(baseUrl) { let inFlight = null; async function search(query, { signal } = {}) {
Cancelling stale requests in a search client
closures
abortcontroller
async
Intermediate
8 steps
go
package fetch import ( "context"
Concurrent API fetches with errgroup in Go
concurrency
goroutines
error-handling
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/building-a-request-logger-middleware-in-gin-explained-go-ac68/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.