从导入的包中追溯go例程?

时间:2016-08-30 14:04:46

标签: go

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

1 个答案:

答案 0 :(得分:1)

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

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的工作原理。

  

recover内置函数允许程序管理a的行为   恐慌goroutine。执行调用以在延迟内部恢复   函数(但不是由它调用的任何函数)停止恐慌序列   通过恢复正常执行并检索传递给的错误值   恐慌的召唤。如果在延迟函数之外调用recover,它将会   不要停止恐慌的序列。在这种情况下,或者当goroutine不是时   恐慌,或者如果提供给恐慌的论点是零,则恢复回报   零。因此,恢复的返回值报告是否为goroutine   恐慌。

func recover() interface{}

工作示例

此示例假定程序包未调用recover(在另一部分中详细说明)。

Golang Playground Link

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 Playground Link

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进行调试或暂时编辑包,以便记录完整的消息(一旦了解,您就可以恢复更改)。

此外,如果您发现问题,请让包裹作者知道,以便修复。