目录

go 使用 recover 捕获 panic 并且打印代码堆栈日志

目录

背景

用过 golang 的相信都知道,我们在开发中为了防止程序出现 panic ,导致整个程序宕机停止,我们通常都会使用 recover 对 panic 进行捕获,这样子程序就不会因为某个 bug panic 导致终止了,

Go中的panic是“运行时异常”,比如空指针引用、数组越界、除以零等,一旦触发且未处理,会导致:

  • 程序直接崩溃:如果是线上服务,会导致服务不可用,影响用户体验。

  • 无详细错误链路:默认只打印简单错误信息(如“panic: runtime error: nil pointer dereference”),没有调用堆栈,很难定位具体哪行代码出错。

recover的作用就是“拦截”panic,让程序从异常中恢复,同时配合runtime包打印堆栈日志,实现“不崩溃+可追溯”。核心原则是:recover必须在defer函数中调用,因为defer函数会在panic触发后、程序退出前执行。

尤其是在开启 goroutine 协程时,recover 机制显得非常好用,也是程序安全的一种手段之一

但是同时也存在一个问题,recover() 的结果在打印出来时仅有错误❌原因,并没有具体代码调用堆栈信息,这让我们在问题排查上显得有些许吃力

那么如何在 recover 的同时打印调用堆栈信息呢?实际上是可以进行打印的

实现

我们可以使用官方提供的 recover() 机制对程序 panic 进行捕获,在捕获的同时,使用 runtime.Stack 打印代码调用堆栈:

具体代码实现如下:

func main(){
  defer func() {
        if rec := recover(); rec != nil {
            buf := make([]byte, 1<<16)  // 分配 64KB 缓冲区
            runtime.Stack(buf, true) // 获取完整的调用栈
            log.Errorf("recover result ", string(buf)) // 记录调用栈详情
        }
    }()
  panic("test err")
}

为什么选择 1<<16(64KB)?

普通程序的调用栈通常不会超过 64KB,这个大小能覆盖绝大多数情况

当然也不建议太大,打印日志也是有一定性能耗时的,堆栈信息足够排查问题即可

并且:在 Go 的标准库和社区实践中,1<<16是常见的默认值(如 net/http的调试日志)

关于 panic、recover 的具体使用,请移步文章:

一文学会 golang 的 panic 、recover 概念和实战(集成 gin 框架/打印堆栈)

版权声明

未经授权,禁止转载本文章。
如需转载请保留原文链接并注明出处。即视为默认获得授权。
未保留原文链接未注明出处或删除链接将视为侵权,必追究法律责任!

本文原文链接: https://fiveyoboy.com/articles/go-recover-stack-log/

备用原文链接: https://blog.fiveyoboy.com/articles/go-recover-stack-log/