如何将 golang 错误包装成不透明错误?
Posted
技术标签:
【中文标题】如何将 golang 错误包装成不透明错误?【英文标题】:How can I wrap a golang error into an opaque error? 【发布时间】:2022-01-20 01:18:24 【问题描述】:如何将错误包装成不透明的错误(如 Dave Cheney 在https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully 中所述)?另外,我希望不透明错误有一个堆栈跟踪,并通过返回链保留。
errors.Wrap()
使用堆栈跟踪创建一个新错误,但不是我的不透明类型。我该怎么做(添加堆栈跟踪并使其成为MyErr
,临时为true
)?
package main
import (
"fmt"
"github.com/pkg/errors"
)
type temporary interface
Temporary() bool
func IsTemporary(err error) bool
te, ok := err.(temporary)
return ok && te.Temporary()
type MyError struct
error
isTemporary bool
func (e MyError) Temporary() bool
return e.isTemporary
func f1() error // imitate a function from another package, that produces an error
return fmt.Errorf("f1 error")
func f2() error
err := f1()
myErr := errors.Wrap(err, "f2 error") // Wrap() adds the stacktrace
// how to wrap it as a temporary MyErr?
return myErr
func f3() error
err := f2()
return fmt.Errorf("f3 error: %+v", err) // don't Wrap() here or we get another stacktrace
func f4() error
err := f3()
return fmt.Errorf("f4 error: %+v", err) // the '+' isn't needed here but does no harm
func main()
err := f4()
if err != nil
if IsTemporary(err)
fmt.Println("temporary error")
fmt.Printf("oops: %+v\n", err)
这将打印以下内容:
oops: f4 error: f3 error: f1 error
f2 error
main.f2
/home/jlearman/projects/axon-internal/ibm/pocs/ibm-cloud/vmware-vms/err2.go:32
main.f3
/home/jlearman/projects/axon-internal/ibm/pocs/ibm-cloud/vmware-vms/err2.go:38
main.f4
/home/jlearman/projects/axon-internal/ibm/pocs/ibm-cloud/vmware-vms/err2.go:43
main.main
/home/jlearman/projects/axon-internal/ibm/pocs/ibm-cloud/vmware-vms/err2.go:48
runtime.main
/usr/local/go/src/runtime/proc.go:255
runtime.goexit
/usr/local/go/src/runtime/asm_amd64.s:1581
这是正确的,只是我想先看到“临时错误”。
假设f1
实际上在第三方或内置代码中,返回标准的error
类型。 f2
是我的代码中收到该错误的第一个函数,需要在适当的时候将其设为临时函数。 (如果最初是临时的,那将是一个后续问题,但我想我可以弄清楚。)
我希望处理从我们的代码返回的错误的模式在整个项目中保持一致,这将是相对较大的。
【问题讨论】:
f4
返回的错误不是Temporary
。使用fmt.Errorf
和%w
,然后您可以使用errors.Unwrap
来测试是否有任何错误是Temporary
。
我的问题是如何使它成为f2
中的临时对象。此外,在f3
中使用fmt.Errorf
和%w
会删除回溯。代码应该如何知道它有多少层? f3
和 f4
(以及 f5
等)的模式需要相同。
【参考方案1】:
github.com/pkg/errors
函数无法真正做到这一点。这是因为用于包装的错误类型未导出,因此您无法将其嵌入到您自己的自定义错误中。
但是,鉴于您不反对使用 stdlib errors
包以外的错误库,以下是使用 juju errors 包的方法(因为它的 Err 类型已导出):
package main
import (
"fmt"
"github.com/juju/errors"
)
type temporary interface
Temporary() bool
func IsTemporary(err error) bool
for
te, ok := err.(temporary)
if ok
return te.Temporary()
er, ok := err.(*errors.Err)
if ok
err = er.Underlying()
continue
return false
type MyError struct
errors.Err
isTemporary bool
func (e MyError) Temporary() bool
return e.isTemporary
func f1() error // imitate a function from another package, that produces an error
return errors.Errorf("f1 error")
func f2() error
err := f1()
wrappedErr := errors.Annotate(err, "f2 error")
return &MyError
Err: *wrappedErr.(*errors.Err),
isTemporary: true,
func f3() error
err := f2()
return errors.Annotate(err, "f3 error")
func f4() error
err := f3()
return errors.Annotate(err, "f4 error")
func main()
err := f4()
if err != nil
if IsTemporary(err)
fmt.Println("temporary error")
if e, ok := err.(*errors.Err); ok
fmt.Printf("oops: %+v\n", e.StackTrace())
【讨论】:
以上是关于如何将 golang 错误包装成不透明错误?的主要内容,如果未能解决你的问题,请参考以下文章
golang os.Create 导致“没有这样的文件或目录”错误