为了账号安全,请及时绑定邮箱和手机立即绑定

来自导入包的 goroutine 的堆栈跟踪?

来自导入包的 goroutine 的堆栈跟踪?

Go
qq_花开花谢_0 2022-03-07 16:01:57
如何获取最后一个(理想情况下)goroutine(应用程序有多个 goroutine)的堆栈跟踪,它恐慌并恢复并且只记录了一个没有太多描述性的错误消息?我不知道哪个程序恢复了。另外,请记住,我不会更改任何导入包的代码。这种恐慌发生在一些创建多个 go 例程的导入包中,所以我需要一种方法来获取最后恢复的例程的堆栈跟踪,以便找到它恐慌的位置。
查看完整描述

1 回答

?
猛跑小猪

TA贡献1858条经验 获得超8个赞

简短的回答是:不可能,但也有例外。


Golang 有一些堆栈控制方法和类型。


您可以使用runtime/debug/SetTraceback控制堆栈级别


func SetTraceback(level string)


SetTraceback 设置运行时在由于未恢复的恐慌或内部运行时错误而退出之前打印的回溯中打印的详细信息量。level 参数采用与 GOTRACEBACK 环境变量相同的值。例如, SetTraceback("all") 确保程序在崩溃时打印所有 goroutine。

有关详细信息,请参阅包运行时文档。如果使用低于环境变量的级别调用 SetTraceback,则忽略该调用。


您还可以使用runtime/debug/Stack打印堆栈 strace


func Stack() []byte


Stack 返回调用它的 goroutine 的格式化堆栈跟踪。它使用足够大的缓冲区调用 runtime.Stack 来捕获整个跟踪。


您还需要了解内置功能的recover工作原理。


恢复内置函数允许程序管理恐慌 goroutine 的行为。在延迟函数(但不是由它调用的任何函数)中执行恢复调用会通过恢复正常执行来停止恐慌序列,并检索传递给恐慌调用的错误值。如果在延迟函数之外调用恢复,它不会停止恐慌序列。在这种情况下,或者当 goroutine 没有恐慌时,或者如果提供给 panic 的参数是 nil,recover 返回 nil。因此,recover 的返回值报告了 goroutine 是否处于恐慌状态。


func recover() interface{}


工作示例

这个例子假设包没有调用recover(在另一节中详述)。


Golang 游乐场链接


package main


import (

    "log"

    "errors"

    "runtime/debug"

    "time"

)


func f2() {

    panic(errors.New("oops")) // line 11

}


func f1() {

    f2() // line 15

}


func main() {

    defer func() {

        if e := recover(); e != nil {

            log.Printf("%s: %s", e, debug.Stack()) // line 20

        }

    }()


    go f1() // line 25


    time.Sleep(time.Second * 1)

}

如果包调用恢复

如果代码从恐慌中恢复,您需要使用调试器或删除recover以了解正在发生的事情,如下面的示例所示,该示例表明已恢复的恐慌无法再次“恢复”。


Golang 游乐场链接


package main


import (

    "log"

    "errors"

    "runtime/debug"

    "time"

)


func f2() {

    panic(errors.New("oops")) // line 11

}


func f1() {

    defer func() {

        if e := recover(); e != nil {

            log.Printf("internal %s: %s", e, debug.Stack()) // line 20

        }

    }()

    f2() // line 15

}


func main() {

    defer func() {

        if e := recover(); e != nil {

            log.Printf("external %s: %s", e, debug.Stack()) // line 20

        } else {

            log.Println("Nothing to print")

        }

    }()


    go f1() // line 25


    time.Sleep(time.Second * 1)

}

少两害

使用Delve调试或临时编辑包,以便记录完整消息(一旦了解,您可以恢复更改)。


此外,如果您发现问题,请告知包作者,以便可以修复它。


查看完整回答
反对 回复 2022-03-07
  • 1 回答
  • 0 关注
  • 139 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信