runtime.stack常返回空或截断,因默认缓冲区仅4kb且第二个参数为false(只捕获当前goroutine);应设buf≥64kb、all=true,并避免在defer panic后调用。

runtime.Stack 为什么总是返回空或截断?
根本不是函数坏了,而是调用方式错了:缓冲区太小 + 默认只抓当前 goroutine。
默认缓冲区仅 4KB,HTTP handler 深层调用链(比如 handler → service → db.Query)轻松溢出;第二个参数为 false(默认值)时,只写入当前 goroutine 的栈帧——若它刚启动、还没几层调用,runtime.Stack 就真可能返回 0 字节。
- 缓冲区至少设为
make([]byte, 64*1024);死锁诊断建议用2 * 1024 * 1024 - 要抓全部 goroutine,第二个参数必须为
true:runtime.Stack(buf, true) - 别在
defer中 panic 后调用——此时部分 goroutine 已退出,堆栈结构不稳定
debug.Stack() 和 panic 日志里的堆栈对不上怎么办?
debug.Stack() 只捕获「调用它的那一刻」当前 goroutine 的栈,不是错误源头。比如底层 io.Read panic 了,但你在顶层 handler 的 recover 里调 debug.Stack(),日志里显示的是 handler 的位置,不是 Read 那一行。
- 真正定位原始错误点,得在错误生成处包装堆栈,例如用
github.com/pkg/errors.Wrap(err, "xxx") - 生产环境必须用
debug.Stack()(返回[]byte),而非debug.PrintStack()(直写os.Stderr,常被 systemd 或容器日志驱动丢弃) - 若只能改日志逻辑,至少配合
log.SetFlags(log.Lshortfile),交叉验证调用位置
/debug/pprof/goroutine?debug=2 和 runtime.Stack 有什么区别?
不是谁替代谁,是用途不同:/debug/pprof/goroutine?debug=2 是封装好的 runtime.Stack,输出带文件名和行号的完整文本堆栈;而 pprof.Lookup("goroutine").WriteTo(w, 1) 输出的是采样摘要,不含源码位置,适合做 goroutine 泄漏分析(多次快照 diff)。
-
/debug/pprof/goroutine?debug=2默认已处理 buffer 分配和 charset,开箱即用,适合人工触发排查 -
runtime.Stack(buf, true)更底层,可嵌入自定义诊断接口或信号 handler(如SIGUSR1) -
pprof.Lookup("goroutine").WriteTo的debug=1参数只输出 running/syscall/waiting 状态的 goroutine,debug=2才全量
fmt.Printf("%+v", err) 为啥不打印堆栈?
因为 errors.New("xxx") 创建的 error 根本没存堆栈帧,%+v 对它无效。只有构造时主动调用 runtime.Callers 的 error 类型才支持展开——比如 github.com/pkg/errors 或 Go 1.20+ 的 fmt.Errorf("msg: %w", err)。
- 混用
errors.New和pkg/errors.Wrap:前者无堆栈,包装后也只从Wrap调用处开始记,上游位置丢失 - 写成
return errors.New(err.Error()):原始 error 堆栈彻底丢弃 - 日志里用
log.Print(err)或fmt.Sprintf("%v", err):只触发Error()方法,%+v根本不生效
goroutine ID 不在任何堆栈输出里,需要额外解析或换用 runtime.Stack(buf, true) ——但它开销大,别放 panic 日志路径里。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











