在 Go 中快速查找文件和行号抛出错误的技巧?

Trick to quickly find file & line number throwing an error in Go?

在我的 go 之旅中发现没有堆栈跟踪。因此,每当出现问题时,我们都会收到一条简单的字符串错误消息,但没有任何信息这是从哪里来的。这与我习惯的其他语言形成鲜明对比 seing detailed stacktraces

例如,下面是来自apex

的错误信息
$ cat event.json | apex invoke --logs webhook                                  
   ⨯ error parsing response: json: cannot unmarshal array into Go value of type map[string]interface {}

这里告诉我解组到地图是行不通的,因为 event.json 是一个数组。我们已解组 interface{} 以支持两个数组和 maps.However,它没有告诉我哪个 file/line 导致了此错误。

问题:

  1. 有什么方法可以快速找到 file/line 这个错误的来源?
  2. 一般来说,是否有 tips/tricks gophers 用来从这个字符串错误消息中快速找到问题的根源?
  3. 大多数 go 项目的堆栈跟踪是这样的还是应该遵循任何最佳实践?

Go 会在 panic 发生时产生堆栈跟踪,使程序崩溃。如果代码直接调用 panic() 就会发生这种情况,通常是在以下情况:

if err != nil {
    panic("it broke")
}

或者,当发生运行时错误时:

a := []int{1, 2, 3}
b := a[12] // index out of range

这是一个最小的例子:

package main

func main() {
    panic("wtf?!")
}

输出:

panic: wtf?!

goroutine 1 [running]:
panic(0x94e60, 0x1030a040)
    /usr/local/go/src/runtime/panic.go:464 +0x700
main.main()
    /tmp/sandbox366642315/main.go:4 +0x80

注意 main.go:4 表示文件名和行号。

在您的示例中,程序 没有 恐慌,而是选择调用(我猜)os.Exit(1)log.Fatal("error message")(调用 os.Exit(1)).或者,恐慌只是从调用函数中恢复。 不幸的是,如果您不是代码的作者,您对此无能为力。

我建议阅读 Golang 博客上的 Defer, Panic, and Recover 以了解更多相关信息。

您尝试的解决方案找到产生错误的代码段来修复代码。

你的实际问题: event.json.

的内容

这叫做X-Y-Problem


Invoke 需要一个 json 对象,您正在传递一个 json 数组。解决这个问题,您的问题就迎刃而解了!

$ echo -n '{ "value": "Tobi the ferret" }' | apex invoke uppercase

文档的相关部分:Invoking Functions

这就是产生错误的代码:Github


是的,Go 确实有堆栈跟踪!阅读 Dave Cheneys blog post on errors and exceptions.

What is way to quickly find which file/line this error coming from?

除非是未恢复的恐慌,否则不会打印默认堆栈。

In General, Are there tips/tricks which gophers use to get to the source of problem quickly from this string error message? is this how stack traces are for most go projects or there are any best practices that should be followed?

一般来说,您需要检查大多数函数调用的错误 returns。有不止一种方法可以做到这一点。 我通常使用标准库包 log 来打印带有文件和行号的错误日志,以便在简单程序中轻松调试。例如:

package main

import "log"
import "errors"

func init() { log.SetFlags(log.Lshortfile | log.LstdFlags) }

func callFunc() error {
    return errors.New("error")
}

func main() {
    if err := callFunc(); err != nil {
        log.Println(err)
    }
}

http://play.golang.org/p/0iytNw7eZ7

输出:

2009/11/10 23:00:00 main.go:14: error

此外,在标准库 runtime/debug 中还有一些函数可以让您打印或检索当前堆栈,例如https://golang.org/pkg/runtime/debug/#PrintStack

社区在简化错误处理方面做出了很多努力,您可以在 GoDoc 中搜索错误:https://godoc.org/?q=error

在 main() 中设置 log.SetFlags(log.LstdFlags | log.Lshortfile) 应该可以。

例如,log.Printf("Deadline!") 将打印:

03/11/2020 23:59:59 liberty_test.go:42: Deadline!