必须在recover中主动调用debug.Stack()或runtime.Stack(buf, false)获取字节流并记录日志,而非依赖panic默认输出;需确保defer与panic同goroutine、缓冲区足够(建议64KB)、避免all=true导致卡顿。

崩溃时只看到“panic: runtime error”,没调用栈怎么办
默认 panic 输出被截断,只显示最后一层函数,根本没法定位到哪行代码触发的。必须在 recover() 里主动抓栈,而不是依赖 panic 默认行为。
-
debug.PrintStack()最快,但输出到os.Stderr,如果进程重定向了 stderr(比如 systemd 启动、容器日志驱动),就看不到——别在生产环境单独用它 - 真正要留痕,得用
debug.Stack()或runtime.Stack(buf, false)拿字节流,再交给 log 写入文件或上报系统 - buf 太小会截断,建议至少分配
make([]byte, 1024*64);runtime.Stack(nil, false)会自动扩容,更安全 - 别传
true给第二个参数——runtime.Stack(buf, true)会暂停所有 goroutine 扫描,线上服务可能卡死
recover 捕获不到 panic?检查 defer 是否在正确 goroutine 里
panic 只在当前 goroutine 内传播,recover() 必须和 panic 在同一个 goroutine 才生效。常见漏点是:goroutine 内 panic,但 defer+recover 写在 main 函数里。
- 每个可能 panic 的 goroutine 都得自己配
defer func() { recover() }() - HTTP handler、定时任务、消息消费循环这些长生命周期 goroutine,最容易漏加 recover
- 不要指望“全局 recover”——Go 没有 Java 那种线程级未捕获异常处理器,goroutine 是隔离的
- 启动时用
runtime.SetPanicHandler(Go 1.22+)可兜底,但它不替代业务层 recover,且无法获取完整栈
堆栈里没有 goroutine ID 和状态,怎么区分是哪个协程崩的
debug.Stack() 和 debug.PrintStack() 只输出调用帧,不带 goroutine ID。线上多个并发任务同时 panic,日志混在一起就分不清谁干的。
- 在
recover()前手动打点:log.Printf("goroutine %d panic start", goroutineID()) - 用
runtime.Stack(buf, false)抓当前 goroutine 栈,再配合runtime.GoroutineProfile或 pprof 接口查活跃 goroutine 列表(需开启http.ListenAndServe("localhost:6060", nil)) - 更直接的办法:panic 前加唯一 trace ID,比如用
context.WithValue(ctx, "trace_id", uuid.New())透传,recover 时从 context 提取 - 注意:
runtime.NumGoroutine()是总数,不能反推具体 goroutine,别误用
崩溃前内存状态想保存下来分析,怎么 dump heap
光有栈不够,有些问题(比如对象泄漏、指针错乱)必须看堆现场。Go 1.19+ 提供了 debug.WriteHeapDump,但默认不启用,且需要手动触发。
立即学习“go语言免费学习笔记(深入)”;
- 必须在 panic 前调用,
recover()之后再调就晚了——所以得用defer+recover()+debug.WriteHeapDump三连 - dump 文件是二进制格式,用
go tool trace或 Delve 加载才能分析分配路径和存活对象 - dump 耗时且占磁盘,建议只对关键服务开启,并限制频率(比如 24 小时最多 1 次)
- 别用
runtime.GC()强制回收后再 dump——GC 会改变堆布局,失真


















