go
52 lines · 7 steps
Capturing response bodies in Gin middleware
A Gin middleware wraps the response writer to tee output into a buffer, then logs a structured audit record after the handler runs.
Explained by
highlit
1package middleware
2
3import (
4 "bytes"
5 "time"
6
7 "github.com/gin-gonic/gin"
8 "github.com/rs/zerolog/log"
9)
10
11type responseCapture struct {
12 gin.ResponseWriter
13 body *bytes.Buffer
14}
15
16func (w *responseCapture) Write(b []byte) (int, error) {
17 w.body.Write(b)
18 return w.ResponseWriter.Write(b)
19}
20
21func (w *responseCapture) WriteString(s string) (int, error) {
22 w.body.WriteString(s)
23 return w.ResponseWriter.WriteString(s)
24}
25
26func AuditLogger(maxBody int) gin.HandlerFunc {
27 return func(c *gin.Context) {
28 capture := &responseCapture{
29 ResponseWriter: c.Writer,
30 body: bytes.NewBuffer(make([]byte, 0, 1024)),
31 }
32 c.Writer = capture
33
34 start := time.Now()
35 c.Next()
36
37 payload := capture.body.Bytes()
38 if len(payload) > maxBody {
39 payload = payload[:maxBody]
40 }
41
42 log.Info().
43 Str("method", c.Request.Method).
44 Str("path", c.FullPath()).
45 Int("status", c.Writer.Status()).
46 Int("bytes", capture.body.Len()).
47 Dur("latency", time.Since(start)).
48 Str("client_ip", c.ClientIP()).
49 Bytes("response", payload).
50 Msg("request audited")
51 }
52}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Embedding an interface lets you override just the methods you care about while delegating the rest.
- 2Swapping c.Writer before calling c.Next lets middleware observe everything downstream handlers produce.
- 3Truncating captured payloads before logging bounds memory and log volume for large responses.
Related explainers
go
package theme import ( "fmt"
Per-tenant HTML templates with Gin's renderer
multi-tenancy
concurrency
html-templates
Advanced
8 steps
go
package auth import ( "net/http"
Setting and reading secure session cookies in Go
cookies
session-management
security
Intermediate
6 steps
php
<?php namespace App\Http\Middleware;
Idempotent requests with Laravel middleware
idempotency
middleware
caching
Advanced
8 steps
go
package middleware import ( "net/http"
A maintenance-mode gate in Gin
middleware
atomic-flag
concurrency
Intermediate
8 steps
python
import time from flask import Flask, g, request
Timing requests with Flask hooks
middleware
request-lifecycle
instrumentation
Intermediate
5 steps
go
func ServeVideo(c *gin.Context) { id := c.Param("id") video, err := videoRepo.FindByID(c.Request.Context(), id) if err != nil {
Streaming video files with Gin
http streaming
range requests
file serving
Intermediate
6 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/capturing-response-bodies-in-gin-middleware-explained-go-13c6/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.