go
36 lines · 6 steps
A panic-recovery middleware in Gin
A Gin middleware that recovers from panics, logs them with structured context, and tells broken connections apart from real server errors.
Explained by
highlit
1func RecoveryHandler(logger *zap.Logger) gin.HandlerFunc {
2 return gin.CustomRecovery(func(c *gin.Context, recovered any) {
3 var brokenPipe bool
4 if ne, ok := recovered.(*net.OpError); ok {
5 var se *os.SyscallError
6 if errors.As(ne, &se) {
7 msg := strings.ToLower(se.Error())
8 if strings.Contains(msg, "broken pipe") || strings.Contains(msg, "connection reset by peer") {
9 brokenPipe = true
10 }
11 }
12 }
13
14 stack := string(debug.Stack())
15 fields := []zap.Field{
16 zap.Any("error", recovered),
17 zap.String("method", c.Request.Method),
18 zap.String("path", c.Request.URL.Path),
19 zap.String("client_ip", c.ClientIP()),
20 zap.String("request_id", requestid.Get(c)),
21 }
22
23 if brokenPipe {
24 logger.Warn("connection interrupted", fields...)
25 c.Error(recovered.(error)) //nolint:errcheck
26 c.Abort()
27 return
28 }
29
30 logger.Error("panic recovered", append(fields, zap.String("stack", stack))...)
31 c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
32 "error": "internal server error",
33 "request_id": requestid.Get(c),
34 })
35 })
36}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Recovery middleware turns a panic anywhere in the handler chain into a controlled HTTP response instead of a crashed server.
- 2Broken-pipe errors mean the client already left, so there's no point returning a status — logging a warning is enough.
- 3Attaching request context like method, path, and request ID to every log entry makes production panics traceable.
Related explainers
go
package handlers type ListFilters struct { Status string `form:"status" binding:"omitempty,oneof=active archived all"`
Cross-field query validation in Gin
validation
struct-tags
query-binding
Intermediate
9 steps
rust
use std::collections::HashMap; #[derive(Debug)] pub struct RequestHead {
Parsing an HTTP request head in Rust
parsing
error-handling
iterators
Intermediate
9 steps
typescript
import { CallHandler, ExecutionContext, Injectable,
Recording HTTP metrics with a NestJS interceptor
interceptor
observability
prometheus
Intermediate
5 steps
go
package editor import ( "context"
How a debouncer coalesces bursts in Go
debounce
concurrency
timers
Intermediate
8 steps
go
package humanize import ( "fmt"
Parsing human-readable byte sizes in Go
parsing
regex
lookup-table
Intermediate
8 steps
javascript
const ROLE_PERMISSIONS = { admin: ['users:read', 'users:write', 'billing:read', 'billing:write'], manager: ['users:read', 'billing:read'], member: ['users:read'],
Role-based permissions middleware in Express
authorization
middleware
rbac
Intermediate
9 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/a-panic-recovery-middleware-in-gin-explained-go-bdca/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.