gin 的 panic 无法被 sentry 捕获是因为默认 recovery() 中间件提前 recover 并返回 500,导致 sentry 收不到原始 panic;需自定义 sentryrecovery 中间件,在 defer recover 后调用 sentry.captureexception() 并绑定 gin.context 到 sentry scope。

为什么 Gin 的 panic 无法被 Sentry 捕获?
默认情况下,Gin 的 Recovery() 中间件会提前 recover 掉 panic,并返回 500 页面——这导致 Sentry 完全收不到原始 panic 信息。Sentry 需要的是未被拦截的 panic 或显式调用的 Sentry.CaptureException(),而不是 HTTP 错误响应体。
解决思路是:要么禁用 Recovery(),要么在它的 handler 里主动上报;更稳妥的做法是保留 Recovery(),但替换其内部逻辑,插入 Sentry 上报。
- 不要直接删掉
Recovery(),否则 panic 会直接 crash 进程 - 不要只依赖
defer Sentry.Recover(),它对 Gin 内部 panic(如路由解析失败)无效 - 推荐方式:用自定义中间件替代默认
Recovery(),并在 recover 后调用Sentry.CaptureException()
如何写一个兼容 Gin v1.9+ 的 Sentry Recovery 中间件?
Gin v1.9 起支持 gin.RecoveryWithWriter(),但 Sentry 需要拿到 panic 值和堆栈,所以得自己实现 recover 逻辑。核心是捕获 panic → 构造 error → 传给 Sentry.CaptureException() → 再按需写响应。
func SentryRecovery() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
var stack string
for i := 0; ; i++ {
pc, file, line, ok := runtime.Caller(i)
if !ok {
break
}
stack += fmt.Sprintf("%s:%d %s\n", file, line, runtime.FuncForPC(pc).Name())
}
// 将 panic 转为 error 并上报
Sentry.CaptureException(fmt.Errorf("panic: %v\n%s", err, stack))
c.AbortWithStatus(http.StatusInternalServerError)
}
}()
c.Next()
}
}
- 必须放在
gin.Default()之后、路由注册之前,否则不生效 - 注意
runtime.Caller()起始索引从 0 开始,才能拿到 panic 发生点,不是中间件自身位置 - 如果用了
gin.DebugMode,建议加if gin.Mode() != gin.DebugMode判断,避免开发时重复上报
Sentry 初始化时容易漏掉的关键配置
仅调用 Sentry.Init() 不足以让错误带上 Gin 上下文(如请求路径、method、用户 IP)。必须手动把 *gin.Context 绑定到 Sentry scope,否则告警里看不到 request 相关字段。
常见错误是:初始化后没做 Sentry.SetHubOnContext(),或在中间件里忘了调用 Sentry.WithScope()。
- 初始化时务必设置
Environment和Release,否则不同环境/版本的错误混在一起 - 在中间件中,用
Sentry.SetHubOnContext(c.Request.Context(), Sentry.CurrentHub())把 Hub 注入 request context - 若需附加请求数据,在 recover 前调用
Sentry.WithScope(func(scope *Sentry.Scope) { ... }),往 scope 添加scope.SetTag("path", c.Request.URL.Path)等
如何验证告警是否真能“分钟级”送达?
Sentry 默认使用异步发送(Transport 是 HTTPTransport),但网络延迟、DNS 解析、Sentry SDK 缓存策略都会影响实际到达时间。实测发现:从 panic 发生到 Sentry Web UI 显示,通常 10–45 秒;但邮件/Slack 告警可能延迟 2–5 分钟,取决于你配置的 Alert Rule 触发条件。
- 不要依赖 “Sentry 收到就等于你收到”,必须测试下游通知通道(比如发个 test alert 到 Slack)
- 在
Sentry.Init()中设Debug: true,观察日志是否有event submitted to transport,确认 SDK 层已发出 - 如果告警总延迟超 3 分钟,优先检查 Alert Rule 的 “Trigger on” 是否设成了 “every event”,还是 “after N events in M minutes”
真正卡住分钟级响应的,往往不是 Gin 或 Sentry SDK,而是你的通知渠道配置和 Sentry SaaS 的 rate limit —— 尤其刚上线时高频触发,会被临时限流。











