7.2.2. 使用 github.com/pkg/errors 包装 errors

fmt.Errorf 模式适用于注释错误 message,但这样做的代价是模糊了原始错误的类型。 我认为将错误视为不透明值对于松散耦合的软件非常重要,因此如果你使用错误值做的唯一事情是原始错误的类型应该无关紧要的面孔

  1. 检查它是否为 nil
  2. 输出或记录它。

但是在某些情况下,我认为它们并不常见,您需要恢复原始错误。 在这种情况下,使用类似我的 errors 包来注释这样的错误, 如下

  1. func ReadFile(path string) ([]byte, error) {
  2. f, err := os.Open(path)
  3. if err != nil {
  4. return nil, errors.Wrap(err, "open failed")
  5. }
  6. defer f.Close()
  7. buf, err := ioutil.ReadAll(f)
  8. if err != nil {
  9. return nil, errors.Wrap(err, "read failed")
  10. }
  11. return buf, nil
  12. }
  13. func ReadConfig() ([]byte, error) {
  14. home := os.Getenv("HOME")
  15. config, err := ReadFile(filepath.Join(home, ".settings.xml"))
  16. return config, errors.WithMessage(err, "could not read config")
  17. }
  18. func main() {
  19. _, err := ReadConfig()
  20. if err != nil {
  21. fmt.Println(err)
  22. os.Exit(1)
  23. }
  24. }

现在报告的错误就是 K&D [11]样式错误,

  1. could not read config: open failed: open /Users/dfc/.settings.xml: no such file or directory

并且错误值保留对原始原因的引用。

  1. func main() {
  2. _, err := ReadConfig()
  3. if err != nil {
  4. fmt.Printf("original error: %T %v\n", errors.Cause(err), errors.Cause(err))
  5. fmt.Printf("stack trace:\n%+v\n", err)
  6. os.Exit(1)
  7. }
  8. }

因此,你可以恢复原始错误并打印堆栈跟踪;

  1. original error: *os.PathError open /Users/dfc/.settings.xml: no such file or directory
  2. stack trace:
  3. open /Users/dfc/.settings.xml: no such file or directory
  4. open failed
  5. main.ReadFile
  6. /Users/dfc/devel/practical-go/src/errors/readfile2.go:16
  7. main.ReadConfig
  8. /Users/dfc/devel/practical-go/src/errors/readfile2.go:29
  9. main.main
  10. /Users/dfc/devel/practical-go/src/errors/readfile2.go:35
  11. runtime.main
  12. /Users/dfc/go/src/runtime/proc.go:201
  13. runtime.goexit
  14. /Users/dfc/go/src/runtime/asm_amd64.s:1333
  15. could not read config

使用 errors 包,你可以以人和机器都可检查的方式向错误值添加上下文。 如果昨天你来听我的演讲,你会知道这个库在被移植到即将发布的 Go 语言版本的标准库中。