gin 默认不捕获 panic 是因为其 recovery 中间件仅作用于主 goroutine,异步 goroutine 中的 panic 需手动 recover;且 recovery 仅记录日志并返回 500 响应,不调用 sentry 上报,若未将 sentrygin 中间件置于 recovery 之前或未启用 repanic,panic 会被提前吞掉导致 sentry 无法捕获。

为什么 Gin 默认不捕获 panic?
Gin 的中间件机制默认只处理 return 出来的错误,而未主动 recover panic。一旦路由 handler 内部触发 panic(比如空指针解引用、数组越界),Gin 会直接终止当前请求并打印堆栈到标准输出,但不会上报到 Sentry——因为 panic 在到达 Sentry 中间件前就已中断执行流。
- 必须在
gin.Recovery() 之前注册 Sentry 中间件,否则 recovery 会提前 recover 掉 panic,Sentry 拿不到原始上下文
- Gin 的
Recovery() 中间件本身只是 log + 500 响应,它不调用 sentry.CaptureException()
- 如果你用了自定义 error handler(如
c.AbortWithStatusJSON()),它不会触发 panic,Sentry 也收不到——这类业务错误需手动调用 sentry.CaptureException()
如何让 Sentry 捕获 Gin 的 panic 和 HTTP 错误?
核心是两层捕获:一层在 panic 发生时抓取,一层在业务逻辑中主动上报。推荐使用官方维护的 sentry-go + gin-gonic/gin 组合:
- 在
gin.New() 后立即注册 sentrygin.New(sentrygin.Options{}) 中间件,且必须放在 gin.Recovery() 之前
- 使用
sentry.CaptureException(err) 手动上报业务错误(如数据库超时、第三方 API 返回非 2xx)
- 为每个请求注入 trace ID:在中间件中调用
sentry.ConfigureScope(func(scope *sentry.Scope) { scope.SetTag("request_id", c.Request.Header.Get("X-Request-ID")) })
- 注意
sentrygin.Options 中的 Repanic: true,确保 panic 不被吞掉,后续中间件(如 recovery)仍可处理
router := gin.New()
router.Use(sentrygin.New(sentrygin.Options{
Repanic: true,
}))
router.Use(gin.Recovery()) // 必须放 sentry 之后,但仍在其他业务中间件之前
Gin context 信息怎么自动带上 Sentry?
Sentry 默认只记录 panic 堆栈,不带 HTTP 方法、URL、查询参数等。要补全这些,得靠 sentrygin 提供的自动 enrich 功能,但要注意开关和粒度:
-
sentrygin.Options 中启用 DisableErrorReporting: false(默认就是 false,别误设为 true)
- 设置
TransactionNameFunc 可以把路由名(如 /api/users/:id)作为 transaction 名,而非原始 URL
- 敏感字段如
password、auth_token 会被自动 scrub,但自定义 body 解析(如 c.ShouldBindJSON(&req))后的结构体字段不会自动过滤,需手动调用 scope.AddEventProcessor 过滤
- 请求 body 默认不上传(避免泄露),如需调试,仅在开发环境开启
AttachRequestBody: true,生产环境禁用
本地开发时 Sentry 不上报?排查这三点
常见现象是代码写完了,但本地 curl 触发 panic 后 Sentry 控制台没新事件:
- 检查
SENTRY_DSN 环境变量是否为空或拼写错误(注意不是 SENTRY_URL 或 DSN)
- 确认初始化时调用了
sentry.Init(),且返回 err 为 nil;若 DSN 格式错误,Init() 会静默失败(无 panic,但后续所有 CaptureXXX 无效)
- Gin 启动时加了
--disable-sentry 类似 flag?检查是否在 init() 阶段根据环境跳过了 sentry.Init(),导致中间件虽注册但 client 为 nil
gin.Recovery() 之前注册 Sentry 中间件,否则 recovery 会提前 recover 掉 panic,Sentry 拿不到原始上下文 Recovery() 中间件本身只是 log + 500 响应,它不调用 sentry.CaptureException() c.AbortWithStatusJSON()),它不会触发 panic,Sentry 也收不到——这类业务错误需手动调用 sentry.CaptureException()
sentry-go + gin-gonic/gin 组合:
- 在
gin.New()后立即注册sentrygin.New(sentrygin.Options{})中间件,且必须放在gin.Recovery()之前 - 使用
sentry.CaptureException(err)手动上报业务错误(如数据库超时、第三方 API 返回非 2xx) - 为每个请求注入 trace ID:在中间件中调用
sentry.ConfigureScope(func(scope *sentry.Scope) { scope.SetTag("request_id", c.Request.Header.Get("X-Request-ID")) }) - 注意
sentrygin.Options中的Repanic: true,确保 panic 不被吞掉,后续中间件(如 recovery)仍可处理
router := gin.New()
router.Use(sentrygin.New(sentrygin.Options{
Repanic: true,
}))
router.Use(gin.Recovery()) // 必须放 sentry 之后,但仍在其他业务中间件之前
Gin context 信息怎么自动带上 Sentry?
Sentry 默认只记录 panic 堆栈,不带 HTTP 方法、URL、查询参数等。要补全这些,得靠 sentrygin 提供的自动 enrich 功能,但要注意开关和粒度:
-
sentrygin.Options 中启用 DisableErrorReporting: false(默认就是 false,别误设为 true)
- 设置
TransactionNameFunc 可以把路由名(如 /api/users/:id)作为 transaction 名,而非原始 URL
- 敏感字段如
password、auth_token 会被自动 scrub,但自定义 body 解析(如 c.ShouldBindJSON(&req))后的结构体字段不会自动过滤,需手动调用 scope.AddEventProcessor 过滤
- 请求 body 默认不上传(避免泄露),如需调试,仅在开发环境开启
AttachRequestBody: true,生产环境禁用
本地开发时 Sentry 不上报?排查这三点
常见现象是代码写完了,但本地 curl 触发 panic 后 Sentry 控制台没新事件:
- 检查
SENTRY_DSN 环境变量是否为空或拼写错误(注意不是 SENTRY_URL 或 DSN)
- 确认初始化时调用了
sentry.Init(),且返回 err 为 nil;若 DSN 格式错误,Init() 会静默失败(无 panic,但后续所有 CaptureXXX 无效)
- Gin 启动时加了
--disable-sentry 类似 flag?检查是否在 init() 阶段根据环境跳过了 sentry.Init(),导致中间件虽注册但 client 为 nil
sentrygin.Options 中启用 DisableErrorReporting: false(默认就是 false,别误设为 true) TransactionNameFunc 可以把路由名(如 /api/users/:id)作为 transaction 名,而非原始 URL password、auth_token 会被自动 scrub,但自定义 body 解析(如 c.ShouldBindJSON(&req))后的结构体字段不会自动过滤,需手动调用 scope.AddEventProcessor 过滤 AttachRequestBody: true,生产环境禁用 curl 触发 panic 后 Sentry 控制台没新事件:
- 检查
SENTRY_DSN环境变量是否为空或拼写错误(注意不是SENTRY_URL或DSN) - 确认初始化时调用了
sentry.Init(),且返回 err 为 nil;若 DSN 格式错误,Init()会静默失败(无 panic,但后续所有CaptureXXX无效) - Gin 启动时加了
--disable-sentry类似 flag?检查是否在init()阶段根据环境跳过了sentry.Init(),导致中间件虽注册但 client 为 nil
Sentry 的 Go SDK 在 client 未初始化时会丢弃所有事件,且不报错——这是最容易被忽略的静默失效点。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











