Go 错误堆栈信息之 CockroachDB errors 库


1. Go 标准库 errors 太简单

由 fmt.Errorf() 构造的 Go 中最常见的“简单”错误对象类似于带有错误接口的包含在结构中的字符串:其 Error() 方法返回构造错误时设置的字符串。

// Go 标准库 errors
// 使用 fmt.Errorf() 构造 Go 中最常见的“简单”错误对象类似于带有错误接口的包含在结构中的字符串:打印错误对象只会显示该字符串。
// 使用 Go 的错误包 errors 的构造函数构建错误 errors.New() 结果一样。

func testSimpleCustomError() {
    
    
	err := fmt.Errorf("fmt.Errorf")
	fmt.Println(err) // "hello"

	err = errors.New("errors.New")
	fmt.Println(err) // "errors.New"
}

什么都没有,仅此而已。打印错误对象也会显示该字符串。顺便说一句,使用 Go 的错误包 errors 的构造函数构建错误 errors.New() 结果一样。



2. CockroachDB错误处理库

使用 Dave Cheney 的错误库[1],或者甚至更好的 CockroachDB 错误库[2](通过导github.com/cockroachdb/errors),则简单错误也会在构造错误时自动捕获堆栈跟踪。仅当详细打印错误时才显示堆栈跟踪。这样可以更轻松地排除错误的来源:


2.1 CockroachDB 新建堆栈错误

使用 Dave Cheney 的错误库[1],或者甚至更好的 CockroachDB 错误库[2](通过导入 ithub.com/cockroachdb/errors)则简单错误也会在构造错误时自动捕获堆栈跟踪。

建议使用CockroachDB库的以下方法代替GO的标准错误处理库:

errors.New():直接替换 Go 标准库的 errors.New(),但它会带有堆栈跟踪;
errors.Errorf() 或 errors.Newf():用堆栈跟踪的方式替换 Go 标准库的 fmt.Errorf();

func testCockroachdbError() {
    
    

	err := cockroachdb_errors.New("cockroachdb_errors.New")
	fmt.Println("只会显示错误字符串:", err)

	fmt.Println("显示错误堆栈信息:", err)
	fmt.Printf("%+v \n", err) // 推荐
}

在这里插入图片描述

2.2 CockroachDB 添加错误上下文前缀

当从多个位置调用相同的逻辑,并且可能因错误而失败时,则希望将消息前缀添加到任何返回的错误对象。

这有助于提供有关“错误发生的位置”的更多上下文,以便在运行时出现错误时(何时出现错误),可以清楚地了解哪个代码路径产生了错误。

在 channel 的场景出现错误是特别有用。

func foo() error {
    
     return errors.New("boo") }

func bar() error {
    
    
	if err := foo(); err != nil {
    
    
		return cockroachdb_errors.Wrap(err, "bar")
	}
	return nil
}

func baz() error {
    
    
	// 当提供 nil 错误作为输入时,errors.Wrap() 返回nil。这使我们可以消除 if err != nil 条件。
	return cockroachdb_errors.Wrap(foo(), "baz")
}

func testCockroachdbErrorWrap() {
    
    
	err1 := bar()
	fmt.Printf("%+v \n", err1)
	err2 := baz()
	fmt.Printf("%+v \n", err2)
}

在这里插入图片描述

1.3. CockroachDB 次要错误

如果在处理错误时遇到错误,该怎么办?

我们希望以某种方式返回有关这两个错误的详细信息,以帮助进行故障排除。
同时,出于原因分析的目的,我们要谨慎地将遇到的第一个错误保留为 “主要” 错误。

次要错误注解不会影响主要错误上返回的文本,代码的行为就像仅发生了主要错误一样。但是,在详细打印过程中会显示第二个错误的信息;

func testCockroachdbSecondaryError() {
    
    
	err := errors.New("主要错误")
	err = cockroachdb_errors.Wrap(err, "主要信息前缀")
	err = cockroachdb_errors.WithSecondaryError(err, cockroachdb_errors.New("次要错误"))

	fmt.Println(err) // 只打印 "主要错误"

	fmt.Printf("%+v \n", err) // 打印只要错误和次要错误的堆栈信息
}

在这里插入图片描述



总结

Go 库通过 Go 自己的错误包中的 fmt.Errorf() 和 errors.New() 提供了错误接口的简化实现。

改用 CockroachDB 错误库,代替 Go 的错误包和 Dave Cheney的 pkg/errors,可以获得更好的体验。

它的错误构造函数 errors.New()/errors.Newf()(别名为 errors.Errorf())自动在错误对象中包含堆栈跟踪,可以使用 fmt.Printf("%+v" ,err) 打印堆栈追踪。

它还提供了错误包装器的词汇表。最常见的是带有 errors.Wrap()/errors.Wrapf() 的消息前缀注释,用于注释从多个位置调用的函数的调用路径。这还包括幕后的堆栈跟踪。

另一个常见的包装器解决了在处理另一个错误时遇到错误时如何在 Go 中执行的令人困惑的问题:使用辅助原因注解,并使用 errors.WithSecondaryCause() 或 errors.CombineErrors() 附加,Go 代码可以保留两个错误,因此程序员在故障排除期间可以同时看到两者。

CockroachDB 错误库中的错误还提供了一致的行为,并且在详细格式化错误时提供了有用的显示结构,从而避免了巨大的 Go 错误打印灾难。

笔记源码: https://github.com/qiuyunzhao/go_basis/blob/master/12_%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86/02_%E8%87%AA%E5%AE%9A%E4%B9%89%E9%94%99%E8%AF%AF%26%E9%94%99%E8%AF%AF%E5%A0%86%E6%A0%88/main.go

猜你喜欢

转载自blog.csdn.net/QiuHaoqian/article/details/112005271