go
49 lines · 8 steps
How a panic-recovery middleware works in Gin
A Gin middleware that catches panics, reports them with request context, and returns a clean 500.
Explained by
highlit
1package middleware
2
3import (
4 "fmt"
5 "net/http"
6 "runtime"
7
8 "github.com/gin-gonic/gin"
9)
10
11type ErrorReporter interface {
12 CaptureException(err error, ctx map[string]interface{})
13}
14
15func Recovery(reporter ErrorReporter) gin.HandlerFunc {
16 return func(c *gin.Context) {
17 defer func() {
18 rec := recover()
19 if rec == nil {
20 return
21 }
22
23 err, ok := rec.(error)
24 if !ok {
25 err = fmt.Errorf("%v", rec)
26 }
27
28 stack := make([]byte, 8<<10)
29 stack = stack[:runtime.Stack(stack, false)]
30
31 reporter.CaptureException(err, map[string]interface{}{
32 "method": c.Request.Method,
33 "path": c.FullPath(),
34 "query": c.Request.URL.RawQuery,
35 "client_ip": c.ClientIP(),
36 "user_agent": c.Request.UserAgent(),
37 "request_id": c.GetString("request_id"),
38 "stack": string(stack),
39 })
40
41 c.Error(err) //nolint:errcheck
42 c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
43 "error": "internal server error",
44 })
45 }()
46
47 c.Next()
48 }
49}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A deferred function paired with recover() converts a panic into a normal error you can handle instead of crashing.
- 2Capturing request metadata and a stack trace at panic time makes production failures diagnosable after the fact.
- 3Aborting with a generic JSON 500 keeps internal details out of the client response while still logging the real cause.
Related explainers
go
package streaming import ( "bufio"
Streaming NDJSON logs over HTTP in Go
http-streaming
channels
select
Advanced
10 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
javascript
function evaluate(expression) { const tokens = tokenize(expression); let pos = 0;
Building a recursive descent calculator
parsing
recursion
operator-precedence
Intermediate
8 steps
go
package api import ( "crypto/sha256"
ETag conditional requests in Gin
http-caching
etag
conditional-requests
Intermediate
6 steps
go
func (w *Watcher) resetDebounce(d time.Duration) { if !w.timer.Stop() { select { case <-w.timer.C:
Debouncing a stream of events in Go
debounce
timers
channels
Advanced
7 steps
go
package logging import ( "context"
Deduplicating log attributes in Go's slog
decorator-pattern
structured-logging
immutability
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/how-a-panic-recovery-middleware-works-in-gin-explained-go-f865/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.