从导入的包中执行go例程的堆栈跟踪?

从导入的包中执行go例程的堆栈跟踪?,go,Go,如何获取最后一个(理想情况下是所有)go例程(应用程序有多个go例程)的堆栈跟踪,该例程惊慌失措并恢复,只记录了一条不太具描述性的错误消息?我不知道哪种例行程序恢复了。另外,请记住,我不会更改任何导入包的代码。这种恐慌发生在一些导入的包中,这些包创建了多个go例程,因此我需要一种方法来抓取上次恢复的例程的堆栈跟踪,以便找到它恐慌的位置。简短的回答是:不可能,但存在异常 Golang有一些堆栈控制方法和类型 您可以使用以下命令控制堆栈级别: func设置回溯(级别字符串) SetTraceback

如何获取最后一个(理想情况下是所有)go例程(应用程序有多个go例程)的堆栈跟踪,该例程惊慌失措并恢复,只记录了一条不太具描述性的错误消息?我不知道哪种例行程序恢复了。另外,请记住,我不会更改任何导入包的代码。这种恐慌发生在一些导入的包中,这些包创建了多个go例程,因此我需要一种方法来抓取上次恢复的例程的堆栈跟踪,以便找到它恐慌的位置。

简短的回答是:不可能,但存在异常

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

您可以使用以下命令控制堆栈级别:

func设置回溯(级别字符串)

SetTraceback设置运行时打印的详细信息量 在回溯中,它在退出之前打印,因为 未恢复的死机或内部运行时错误。 level参数采用与GOTRACEBACK相同的值 环境变量。例如,SetTraceback(“all”)确保 程序崩溃时打印所有goroutine。
有关详细信息,请参阅包运行时文档。如果 使用低于的级别调用SetTraceback 环境变量,则忽略该调用

也可以使用打印堆栈strace

func Stack()[]字节

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

您还需要了解内置函数recover的工作原理

恢复内置功能允许程序管理服务器的行为 惊慌失措的戈罗廷。执行调用以在延迟 函数(但不是它调用的任何函数)停止恐慌序列 通过恢复正常执行并检索传递给 恐慌的召唤。如果在deferred函数之外调用recover,它将 不能阻止一个恐慌的序列。在这种情况下,或者当goroutine不是 恐慌,或者如果提供给恐慌的参数为零,则恢复返回 无因此,recover的返回值报告goroutine是否为 惊慌失措

func recover()接口{}

工作示例 本例假设包不调用recover(在另一节中详细介绍)

如果包调用恢复 如果代码正在从死机中恢复,则需要使用调试器或删除
recover
,以了解正在发生的情况,如下面的示例所示,该示例表明恢复的死机无法再次“恢复”

两害相轻 使用调试或临时编辑包,使其记录完整消息(一旦理解,您可以恢复更改)


此外,如果发现问题,请告知软件包作者,以便解决问题。

为什么不能更改软件包代码?如果你能构建程序,你就有了代码。我会尝试,但我要求提供一种更方便的方法来获取主程序的跟踪:)如果代码从死机中恢复,你就看不到死机。您可以尝试在调试器中设置断点,但考虑到调试器的状态,注释掉
recover
并获取堆栈跟踪可能要快得多。
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)
}
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)
}