日志中间件panic必须兜底:最外层加defer-recover并调用c.Abort(),补500响应、完整记录panic堆栈;避免I/O阻塞,fallback生成request_id需无panic,日志写入失败不得影响HTTP流程。

日志中间件里 panic 了怎么不丢响应
日志中间件本身 panic,会导致整个请求链路中断:后续 handler 不执行、c.JSON 不写出、连接挂起、前端收不到任何响应。这不是“报错”,而是“静默失败”。
- 必须在日志中间件最外层加
defer func() { recover() },且recover()后立即调用c.Abort() - 不能只打印 panic 日志就结束——得补一个兜底响应,比如
c.AbortWithStatusJSON(500, gin.H{"msg": "service unavailable"}) - 堆栈要用
debug.Stack()或fmt.Printf("%+v", r)记录完整,r.Error()会丢调用链 - 如果中间件里启了 goroutine(比如异步写日志),goroutine 内 panic 是捕不到的,必须单独加
defer/recover
log.Write 失败时怎么避免影响 HTTP 流程
结构化日志库(如 zap)的 Info()、Error() 方法默认不返回 error,但底层 I/O 可能失败:磁盘满、stdout 被重定向到挂掉的 fluentd、logger 已关闭……这些都会让写入静默失败或 panic。
- 别在中间件里直接调用
logger.Info()同步写入;应只构造log.Entry结构体(含request_id、status、cost等字段) - 真正写入交给独立 goroutine 或 buffer pool,中间件内绝不让 I/O 错误阻塞
c.Next() - 若必须同步降级,可用
fmt.Fprint(os.Stderr, ...)替代,但需确保os.Stderr未被重定向失效 - 用
logrus时注意它默认 write error 会panic,要提前设logrus.ExitFunc = func(int) {}
request_id 生成失败导致日志断链怎么办
很多日志中间件从 X-Request-Id header 读取 ID,但用户不带、网关没透传、代理截断时,fallback 逻辑若出错(比如对空指针解引用、time.Now().MarshalText() panic),就会让整个中间件崩溃。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- fallback 生成
request_id必须无 panic:推荐用uuid.NewString()或fmt.Sprintf("%d-%d", time.Now().UnixNano(), rand.Int63()) - 不要依赖
c.Request的任意字段——测试 mock 下它可能是nil,访问前先判空 - 生成 ID 的代码要包裹在自己的
defer/recover里,和主日志流程隔离 - 把 fallback ID 和原始缺失原因一起记进错误日志,例如
"missing X-Request-Id, generated fallback: %s (reason: header not found)"
为什么 Gin 默认 Recovery 中间件不够用
gin.Recovery() 默认只调 log.Printf 打印 panic,不写响应体、不设状态码、不记录 request_id、不触发监控告警——线上等于“盲跑”。
立即学习“go语言免费学习笔记(深入)”;
- 它不调
c.Abort(),panic 后仍可能执行后续中间件(比如 JWT 验证又跑一遍) - 它不记录完整堆栈,
log.Printf("%v", r)会丢调用帧,查问题只能靠猜 - 它不区分 panic 来源:是业务代码 bug?还是日志中间件自己崩了?无法归因
- 生产环境必须替换为自定义 recovery,至少包含:记录 traceID、写 JSON 响应、发告警(如邮件)、落盘堆栈快照

















