如何让堆栈跟踪指向Golang中的实际错误原因?

ser*_*jja 14 go

假设我有一些像这样的代码:

value, err := some3rdpartylib.DoSomething()
if err != nil {
    panic(err)
}
Run Code Online (Sandbox Code Playgroud)

万一err != nil我会得到这样的东西:

panic: some error explanation here

goroutine 1 [running]:
main.main()
    /tmp/blabla/main.go:6 +0x80
Run Code Online (Sandbox Code Playgroud)

这个堆栈跟踪是完全合法的,但有时这些错误消息可能无法澄清发生了什么,所以我想深入研究第三方库的源代码,以调查究竟是什么原因导致返回此错误.但是,当我的代码像这样恐慌时,无法获得返回此错误的实际位置.

稍微澄清一点:因为我来自JVM世界,抛出异常,我可以完全跟踪抛出异常的确切代码行,从而可以轻松找到该位置,看看出了什么问题.Go堆栈跟踪正好在我的代码恐慌的地方结束,因此在我的情况下不太有用.

我在这里创建了一个游乐场,理想情况下我希望能够将错误追踪到它实际返回的地方,而不是恐慌.(例如到第17行return "", errors.New("some error explanation here"))

这甚至可能吗?

Kok*_*kos 8

简而言之:这是不可能的.由于错误是值,因此不会以任何特殊方式处理它们.因此,当函数(通常)返回时,堆栈不再可用(即,另一个函数调用可能会覆盖返回错误函数'堆栈所使用的内存).

有一个名为trace的工具,它是用go1.5引入的,但是目前还没有全面的教程可用,我发现的任何一个都没有说会包含这种功能.

  • Trace完全与这个问题无关,不确定为什么你提到它. (7认同)

Ism*_*l H 8

我认为有一种更简单的方法可以实现这一目标。您可以尝试包装错误使用golang“默认”的错误包:

您需要定义要由您实现的接口error

type stackTracer interface {
    StackTrace() errors.StackTrace
}
Run Code Online (Sandbox Code Playgroud)

然后在包装/处理错误时使用它:

err, ok := errors.(stackTracer) // ok is false if errors doesn't implement stackTracer

stack := err.StackTrace()
fmt.Println(stack) // here you'll have your stack trace
Run Code Online (Sandbox Code Playgroud)

  • 除非我误解了什么,否则我必须说,堆栈跟踪只能通过第三方库(现已存档)获得,这一事实确实令人失望。对于大型应用程序或不使用“pkg/errors”的第三方软件包,这个问题尤其令人痛苦。此外,如果您正在开发自己的第三方包,那么包含“pkg/errors”作为依赖项似乎是必须的。我很困惑 Go 错误在 2022 年不提供堆栈跟踪。 (68认同)
  • Go标准库的错误包是 https://pkg.go.dev/errors https://pkg.go.dev/github.com/pkg/errors 是第三方库 (4认同)

Gel*_*ler 6

正如其他人指出的那样,跟踪 go 中的错误并不是微不足道的。有一些项目,如juju/errgo,允许您包装错误,然后追溯这些错误。为了使其工作顺利,您必须在整个项目中一致地使用它们,这不会帮助您解决第 3 方库中的错误或已处理但永远不会返回的错误。

因为这是一个很常见的问题,而且我真的对此很恼火,所以我编写了一个小型调试实用程序,它将添加调试代码到 go 文件中,记录每个返回的错误(实现的值error)以及返回到 STDOUT 的函数(如果您需要更高级的日志记录,只需破解项目中的记录器即可,这非常简单)。

安装

go get github.com/gellweiler/errgotrace
Run Code Online (Sandbox Code Playgroud)

用法

调试当前目录中的所有文件:

$ find . -name '*.go' -print0 | xargs -0 errgotrace -w
Run Code Online (Sandbox Code Playgroud)

要从 go 文件中删除添加的调试代码:

$ find . -name '*.go' -print0 | xargs -0 errgotrace -w -r
Run Code Online (Sandbox Code Playgroud)

然后只需编译并运行您的代码或测试用例即可。

样本输出

[...]
2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.objectKey: EOF token found
2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.objectItem: EOF token found
2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.objectKey: EOF token found
2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.objectItem: EOF token found
2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.objectKey: At 3:4: nested object expected: LBRACE got: ASSIGN
2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.objectItem: At 3:4: nested object expected: LBRACE got: ASSIGN
2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.objectList: At 3:4: nested object expected: LBRACE got: ASSIGN
2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.Parse: At 2:31: literal not terminated
2017/12/13 00:54:39 [ERRGOTRACE] parser.Parse: At 2:31: literal not terminated
2017/12/13 00:54:39 [ERRGOTRACE] hcl.parse: At 2:31: literal not terminated
2017/12/13 00:54:39 [ERRGOTRACE] hcl.ParseBytes: At 2:31: literal not terminated
2017/12/13 00:54:39 [ERRGOTRACE] formula.parse: parsing failed
[...]
Run Code Online (Sandbox Code Playgroud)

正如您从这个输出中看到的,很容易判断错误最初发生在哪个函数中。一旦了解了这一点,您就可以使用调试器来获取更多上下文。


小智 5

看看https://github.com/ztrue/tracerr

我创建这个包是为了拥有堆栈跟踪和源代码片段,以便能够更快地调试并记录更多详细信息的错误。

这是一个代码示例:

package main

import (
    "io/ioutil"
    "github.com/ztrue/tracerr"
)

func main() {
    if err := read(); err != nil {
        tracerr.PrintSourceColor(err)
    }
}

func read() error {
    return readNonExistent()
}

func readNonExistent() error {
    _, err := ioutil.ReadFile("/tmp/non_existent_file")
    // Add stack trace to existing error, no matter if it's nil.
    return tracerr.Wrap(err)
}
Run Code Online (Sandbox Code Playgroud)

这是输出: golang 错误堆栈跟踪


qua*_*nta 5

package main

import (
    "errors"
    "fmt"
)

func main() {
    value, err := DoSomething()
    if err != nil {
        panic(err)
    }
    fmt.Println(value)
}

func DoSomething() (string, error) {
    return "", errors.New("some error explanation here")
}
Run Code Online (Sandbox Code Playgroud)

问题是标准包errors没有在发生时附加堆栈跟踪。您可以使用github.com/pkg/errors来执行此操作:

package main

import (
    "github.com/pkg/errors"
    "fmt"
)

func main() {
    value, err := DoSomething()
    if err != nil {
        fmt.Printf("%+v", err)
    }
    fmt.Println(value)
}

func DoSomething() (string, error) {
    return "", errors.New("some error explanation here")
}
Run Code Online (Sandbox Code Playgroud)
$ go run stacktrace.go
some error explanation here
main.DoSomething
    /Users/quanta/go/src/github.com/quantonganh/errors/stacktrace.go:18
main.main
    /Users/quanta/go/src/github.com/quantonganh/errors/stacktrace.go:10
runtime.main
    /usr/local/Cellar/go/1.15.2/libexec/src/runtime/proc.go:204
runtime.goexit
    /usr/local/Cellar/go/1.15.2/libexec/src/runtime/asm_amd64.s:1374
Run Code Online (Sandbox Code Playgroud)

更多详细信息:https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package

  • 注意:使用“%+v”变体通过堆栈跟踪打印错误。 (4认同)