在我的 go 之旅中发现没有堆栈跟踪。因此,每当出现问题时,我们都会收到一条简单的字符串错误消息,而没有任何信息来自何处。这与我习惯的其他语言形成鲜明对比
例如,以下是来自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{}
以支持数组和映射。但是,它并没有告诉我哪个文件/行导致了此错误。
问题:
有什么方法可以快速找到这个错误来自哪个文件/行?
除非是未恢复的恐慌,否则不会打印默认堆栈。
一般来说,地鼠是否可以使用一些技巧/技巧来从该字符串错误消息中快速找到问题的根源?大多数 Go 项目的堆栈跟踪都是如此,还是应该遵循任何最佳实践?
一般来说,您需要检查大多数函数调用的错误返回。有不止一种方法可以做到这一点。 我通常使用标准库包
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
您尝试的解决方案:找到产生错误的代码段来修复代码。
您的实际问题:
event.json
的内容。
这称为 X-Y 问题
Invoke 需要一个 json 对象,您正在传递一个 json 数组。解决这个问题,你的问题就消失了!
$ echo -n '{ "value": "Tobi the ferret" }' | apex invoke uppercase
文档的相关部分:调用函数
这就是产生错误的代码片段:Github
是的,Go 确实有堆栈跟踪!阅读 Dave Cheney 关于错误和异常的博客文章。
panic
发生时,Go
会产生堆栈跟踪,导致程序崩溃。如果代码直接调用
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 博客上的
延迟、恐慌和恢复,了解更多相关信息。
log.SetFlags(log.LstdFlags | log.Lshortfile)
就可以了。例如,
log.Printf("Deadline!")
将打印:
03/11/2020 23:59:59 liberty_test.go:42:截止日期!
decoder := json.NewDecoder(f)
switch err := decoder.Decode(&cfg).(type) {
case *json.SyntaxError:
log.Panicf("Parse Error @%d\n", err.Offset)
}
我还为文件编写了一个帮助程序来读取文件并将偏移量映射到行号和位置:
type FilePos struct {
line int
pos int
}
func findPos(file *bufio.Reader, offset int) FilePos {
p := FilePos{line: 1, pos: offset}
var lineLen int
for line, err := file.ReadBytes('\n'); len(line) > 0 && err == nil; line, err = file.ReadBytes('\n') {
if p.pos < len(line) {
return p
}
lineLen += len(line)
if line[len(line)-1] == '\n' {
p.line += 1
p.pos -= lineLen
lineLen = 0
}
}
return p
}
可以按如下方式使用:
f, err := os.Open("myfile.json")
...
decoder := json.NewDecoder(f)
switch err := decoder.Decode(&cfg).(type) {
case *json.SyntaxError:
f.Seek(0, io.SeekStart)
pos := findPos(bufio.NewReader(f), int(err.Offset))
log.Panicf("Unable to decode configuration file (Line: %d, Pos: %d); - %v\n", pos.line, pos.pos, err.Error())
}
这只适用于可搜索的流,例如文件。对于网络数据,您必须在缓冲区中保留完整数据才能使用它。