sentry未捕获panic的四大原因及对策:一是defer+recover吞掉panic,需手动captureexception;二是分布式上下文丢失,应通过beforesend注入trace_id;三是goroutine泄漏致hub不安全,须按请求新建hub;四是自定义panic类型被误判,需显式设level=fatal。

panic 信息没进 Sentry?检查 defer + recover 是否吞掉了它
Go 的 panic 默认会终止 goroutine 并打印堆栈到 stderr,但微服务里常有人用 recover 拦截 panic 做“优雅降级”——结果 Sentry 什么都没收到。根本原因:Sentry 的 Go SDK(sentry-go)只捕获未被 recover 的 panic;一旦你写了 defer func() { if r := recover(); r != nil { ... } }(),Sentry 就彻底失联。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 若必须 recover,手动调用
sentry.CaptureException(),传入fmt.Errorf("panic: %v", r)或更完整的包装(比如带上原始 stack trace) - 别在中间件或全局 handler 里无差别 recover —— 先判断是否真要吞掉这个 panic(比如业务校验失败不该 panic,而空指针解引用必须上报)
- 用
sentry.Flush()确保上报完成,尤其在 recover 后立即 exit 前(否则日志可能丢失)
分布式上下文丢失:panic 上报时看不到 trace_id 和 service name
Sentry 默认不会自动注入 HTTP 请求头里的 X-Trace-ID 或 OpenTracing 的 span context。你在日志里看到 trace_id,但在 Sentry issue 页面里查不到,说明上下文没透传过去。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 初始化 Sentry 时启用
AttachStacktrace: true,并设置BeforeSend钩子,在上报前从当前 goroutine 的 context(如req.Context())里提取trace_id、service、env等字段,写入event.Tags或event.Extra - 如果用的是
gRPC,需从metadata.MD中取 trace_id;HTTP 则从req.Header.Get("X-Trace-ID") - 避免在
init()函数里初始化 Sentry —— 此时还没加载配置,环境变量和 trace 工具都不可用
goroutine 泄漏导致 panic 上报失败:sentry-go 的 Hub 不是并发安全的
很多人直接在 HTTP handler 里调用 sentry.CaptureException(),看似正常,但高并发下会出现上报丢失、超时或 panic 自身被忽略。问题出在默认的全局 sentry.CurrentHub() 是单例,而它的 scope 是 goroutine-local 的——但没被正确隔离。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 每个请求 handler 应基于 request context 创建新 Hub:
hub := sentry.NewHub(sentry.CurrentHub().Client(), sentry.NewScope()),再用hub.ConfigureScope()绑定 trace_id 等信息 - 不要复用
sentry.Hub实例跨 goroutine,尤其不要把它塞进 struct 字段长期持有 - 确认
sentry.Init()里设置了合理的FlushTimeout(建议 5–10 秒),否则大量并发 panic 时 flush 可能超时丢数据
自定义 panic 类型不被捕获:比如 errors.New("xxx") 被当成普通 error 上报
sentry-go 默认只把未被 recover 的 panic 当作 fatal 事件,而像 panic(errors.New("db timeout")) 这种,虽然触发了 panic,但 Sentry 可能只记录为 level=error,而非 level=fatal,且不带完整 stack。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 显式调用
sentry.WithScope(func(scope *sentry.Scope) { scope.SetLevel(sentry.LevelFatal); sentry.CaptureException(err) }) - 避免用
errors.New或fmt.Errorf直接 panic,改用自定义 panic 类型(如type FatalError struct{ error }),并在BeforeSend中识别它,强制设为 fatal - 注意:
panic(nil)不会被 Sentry 捕获,也不触发 defer/recover —— 这是 Go 语言限制,得靠静态检查或 linter 提前拦截
最麻烦的其实是 panic 发生在第三方库的 goroutine 里(比如定时任务、消息消费协程),那些地方往往没接入 context,也没挂 Sentry Hub。这种 case 得靠 wrapper 包一层启动逻辑,而不是指望全局配置能兜住。










