Ziz*_* Wu 5 error-handling stack-trace go
考虑以下代码(https://go.dev/play/p/hDOyP3W_lqW)
package main
import (
"log"
"github.com/pkg/errors"
)
func myError() error {
return errors.New("failing unconditionally")
}
func myError1() error {
return errors.Errorf("annotate with additional debug info: %+v", myError())
}
func myError2() error {
return errors.Errorf("extra debug info: %+v", myError1())
}
func main() {
if err := myError2(); err != nil {
log.Printf("%+v", err)
}
}
Run Code Online (Sandbox Code Playgroud)
我使用 引发错误errors.New并使用附加信息对其进行注释errors.Errorf。
它做了我想要的事情——记录并打印堆栈跟踪和行号。然而,问题是 的输出log.Printf("%+v", err)是冗长且重复的:
2009/11/10 23:00:00 extra debug info: annotate with additional debug info: failing unconditionally
main.myError
/tmp/sandbox3329712514/prog.go:10
main.myError1
/tmp/sandbox3329712514/prog.go:14
main.myError2
/tmp/sandbox3329712514/prog.go:18
main.main
/tmp/sandbox3329712514/prog.go:22
runtime.main
/usr/local/go-faketime/src/runtime/proc.go:250
runtime.goexit
/usr/local/go-faketime/src/runtime/asm_amd64.s:1571
main.myError1
/tmp/sandbox3329712514/prog.go:14
main.myError2
/tmp/sandbox3329712514/prog.go:18
main.main
/tmp/sandbox3329712514/prog.go:22
runtime.main
/usr/local/go-faketime/src/runtime/proc.go:250
runtime.goexit
/usr/local/go-faketime/src/runtime/asm_amd64.s:1571
main.myError2
/tmp/sandbox3329712514/prog.go:18
main.main
/tmp/sandbox3329712514/prog.go:22
runtime.main
/usr/local/go-faketime/src/runtime/proc.go:250
runtime.goexit
/usr/local/go-faketime/src/runtime/asm_amd64.s:1571
Run Code Online (Sandbox Code Playgroud)
iiuc,errors每次注释错误时,包都会将堆栈跟踪的附加副本附加到错误中,如下面的代码片段所示
// repetitive (thrice) error stack
main.myError
/tmp/sandbox3329712514/prog.go:10
main.myError1
/tmp/sandbox3329712514/prog.go:14
main.myError2
/tmp/sandbox3329712514/prog.go:18
main.main
/tmp/sandbox3329712514/prog.go:22
runtime.main
/usr/local/go-faketime/src/runtime/proc.go:250
runtime.goexit
/usr/local/go-faketime/src/runtime/asm_amd64.s:1571
main.myError1
/tmp/sandbox3329712514/prog.go:14
main.myError2
/tmp/sandbox3329712514/prog.go:18
main.main
/tmp/sandbox3329712514/prog.go:22
runtime.main
/usr/local/go-faketime/src/runtime/proc.go:250
runtime.goexit
/usr/local/go-faketime/src/runtime/asm_amd64.s:1571
main.myError2
/tmp/sandbox3329712514/prog.go:18
main.main
/tmp/sandbox3329712514/prog.go:22
runtime.main
/usr/local/go-faketime/src/runtime/proc.go:250
runtime.goexit
/usr/local/go-faketime/src/runtime/asm_amd64.s:1571
Run Code Online (Sandbox Code Playgroud)
我想要的输出是
// Desired output
2009/11/10 23:00:00 extra debug info: annotate with additional debug info: failing unconditionally
main.myError
/tmp/sandbox3329712514/prog.go:10
main.myError1
/tmp/sandbox3329712514/prog.go:14
main.myError2
/tmp/sandbox3329712514/prog.go:18
main.main
/tmp/sandbox3329712514/prog.go:22
runtime.main
/usr/local/go-faketime/src/runtime/proc.go:250
runtime.goexit
/usr/local/go-faketime/src/runtime/asm_amd64.s:1571
Run Code Online (Sandbox Code Playgroud)
实现此目的的一种方法是仅使用errors包来引发错误,然后使用fmt.Errorfwith%+v在调用堆栈中添加其他信息(例如https://go.dev/play/p/OrWe6KUIL_m)。然而,它很容易出错,并且很难强制每个开发人员在大型代码库中使用这种模式。开发人员必须记住使用errors包来引发错误并fmt正确使用 with%+v %s来打印堆栈跟踪。
我想知道这是否是所需的行为(冗长且重复)。是否可以一致地使用errors包来注释调用堆栈上的错误,而不必担心附加重复的堆栈跟踪副本(例如,神奇errors地知道错误已经有堆栈跟踪)?
有v 用于打印错误的格式说明符。
%s - 打印错误。如果错误有原因,\n它将被递归打印。
\n%v \xe2\x80\x93 它将仅打印值。不会打印字段名称。这是使用 Println\n 时打印结构的默认方式(打印错误,如果错误有原因,则会递归打印。)
\n%+v \xe2\x80\x93 它将打印字段和值。\n(扩展格式。将详细打印错误的 StackTrace 的每一帧。)
\n在你的情况下:
\nfunc myerror() error {\n return errors.New("failing unconditionally") // 1\xef\xb8\x8f\xe2\x83\xa3\n}\n\nfunc myerror1() error {\n return errors.Errorf("annotate with additional debug info: %+v", myerror()) // 2\xef\xb8\x8f\xe2\x83\xa3\n}\n\nfunc myerror2() error {\n return errors.WithStack(myerror1()) // 3\xef\xb8\x8f\xe2\x83\xa3\n}\nRun Code Online (Sandbox Code Playgroud)\n1\xef\xb8\x8f\xe2\x83\xa3error使用堆栈文件创建新的(errors.New)
2\xef\xb8\x8f\xe2\x83\xa3error使用“格式化”消息和此error堆栈创建新的(errors.Errorf)
3\xef\xb8\x8f\xe2\x83\xa3error使用此error堆栈创建新的(errors.WithStack)
2022/07/13 11:42:03 annotate with additional debug info: failing unconditionally\ngithub.com/kozmod/idea-tests/core/errors.myerror\n /Users/19798572/GolandProjects/idea-tests/core/errors/stack_test.go:10 // 1\xef\xb8\x8f\xe2\x83\xa3\ngithub.com/kozmod/idea-tests/core/errors.myerror1\n /Users/19798572/GolandProjects/idea-tests/core/errors/stack_test.go:14\ngithub.com/kozmod/idea-tests/core/errors.myerror2\n /Users/19798572/GolandProjects/idea-tests/core/errors/stack_test.go:18\ngithub.com/kozmod/idea-tests/core/errors.TestStack.func1\n /Users/19798572/GolandProjects/idea-tests/core/errors/stack_test.go:35\ntesting.tRunner\n /Library/GoLang/go1.18.2.darwin-amd64/src/testing/testing.go:1439\nruntime.goexit\n /Library/GoLang/go1.18.2.darwin-amd64/src/runtime/asm_amd64.s:1571\ngithub.com/kozmod/idea-tests/core/errors.myerror1\n /Users/19798572/GolandProjects/idea-tests/core/errors/stack_test.go:14 // 2\xef\xb8\x8f\xe2\x83\xa3\ngithub.com/kozmod/idea-tests/core/errors.myerror2\n /Users/19798572/GolandProjects/idea-tests/core/errors/stack_test.go:18\ngithub.com/kozmod/idea-tests/core/errors.TestStack.func1\n /Users/19798572/GolandProjects/idea-tests/core/errors/stack_test.go:35\ntesting.tRunner\n /Library/GoLang/go1.18.2.darwin-amd64/src/testing/testing.go:1439\nruntime.goexit\n /Library/GoLang/go1.18.2.darwin-amd64/src/runtime/asm_amd64.s:1571\ngithub.com/kozmod/idea-tests/core/errors.myerror2\n /Users/19798572/GolandProjects/idea-tests/core/errors/stack_test.go:18 // 3\xef\xb8\x8f\xe2\x83\xa3\ngithub.com/kozmod/idea-tests/core/errors.TestStack.func1\n /Users/19798572/GolandProjects/idea-tests/core/errors/stack_test.go:35\ntesting.tRunner\n /Library/GoLang/go1.18.2.darwin-amd64/src/testing/testing.go:1439\nruntime.goexit\n /Library/GoLang/go1.18.2.darwin-amd64/src/runtime/asm_amd64.s:1571\nRun Code Online (Sandbox Code Playgroud)\n1\xef\xb8\x8f\xe2\x83\xa3 第一个的开始error堆栈的开始
2\xef\xb8\x8f\xe2\x83\xa3 第二个的开始error堆栈的开始
3\xef\xb8\x8f\xe2\x83\xa3 第三个开始error堆栈的开始
您可以简单地使用堆栈创建“root”错误,然后添加消息(换行)“root”错误
\nfunc myerror3() error {\n return errors.New("failing unconditionally")\n}\n\nfunc myerror4() error {\n return errors.WithMessage(myerror3(), "annotate with additional debug info")\n}\n\nfunc myerror5() error {\n return errors.WithMessage(myerror4(), "myerror5")\n}\n\nfunc main() {\n if err := myerror5(); err != nil {\n log.Printf("%+v", err)\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n或者
\nfunc myError() error {\n // create error (github.com/pkg/errors + fmt) with stack (message)\n return fmt.Errorf("%+v", errors.New("failing unconditionally"))\n}\n\nfunc myError1() error {\n return fmt.Errorf("annotate with additional debug info: %v", myError())\n}\n\nfunc myError2() error {\n return fmt.Errorf("extra debug info: %v", myError1())\n}\n\nfunc main() {\n if err := myError2(); err != nil {\n log.Printf("%v", err)\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n