Go 学习笔记(72)— Go 第三方库之 pkg/errors 带堆栈的错误处理
包 github.com/pkg/errors 讓開發人員很容易在 error 錯誤信息上帶上堆棧信息,可以更快更準確定位錯誤,例如行號等信息。
如果項目代碼比較復雜,且經常需要追蹤 Bug,建議使用 github.com/pkg/errors 這個錯誤處理包。
1. 安裝
go get -v github.com/pkg/errors
這個包和標準庫的 errors 包重名,并且都有 New() 函數,因此從標準庫的錯誤處理方式轉為帶堆棧的錯誤處理上來還是比較方便的。
2. 使用示例
2.1 默認方式
package mainimport ("errors""fmt"// "github.com/pkg/errors"
)func divFunc(a, b int) (int, error) {if b == 0 {err := errors.New("b is 0")return 0, err}return a / b, nil
}func main() {a := 1b := 0ret, err := divFunc(a, b)if err != nil {fmt.Printf("ret is %d, err is %+v", ret, err)}
}
運行結果為:
ret is 0, err is b is 0
替換為 github.com/pkg/errors 后的輸出結果
ret is 0, err is b is 0
main.divFunc/home/wohu/goProject/webserver/main.go:11
main.main/home/wohu/goProject/webserver/main.go:20
runtime.main/usr/local/go/src/runtime/proc.go:203
runtime.goexit/usr/local/go/src/runtime/asm_amd64.s:1357
2.2 進一步包裝
下面代碼使用 errors.Wrap() 函數對錯誤信息進行了包裝:
package mainimport ("fmt""github.com/pkg/errors"
)var ErrNameEmpty = errors.New("Name can't be empty!")func (s *Student) SetName(newName string) (err error) {if newName == "" || s.Name == "" {return ErrNameEmpty} else {s.Name = newNamereturn nil}
}type Student struct {Name stringAge int
}func NewStu() (err error) {stu := &Student{Age: 19}e := stu.SetName("")if e != nil {return errors.Wrap(e, "set name failed!")} else {return nil}
}func main() {e := NewStu()fmt.Printf("%+v\n", e)
}
最后通過 Printf() ,設置占位符
%+v顯示錯誤信息和堆棧信息,例如函數運行時的文件名、行數等信息;- 而占位符
%s則只顯示錯誤信息;
下面帶堆棧信息的錯誤提示很容易讓程序員定位問題發生的位置,比如 main.go:30 ,表明在 main.go 文件的 30 行。
Name can't be empty!
main.initmain.go:9
runtime.main/src/runtime/proc.go:189
runtime.goexit/src/runtime/asm_amd64.s:1333
set name failed!
main.NewStumain.go:30
main.mainmain.go:38
runtime.main/src/runtime/proc.go:201
runtime.goexit/src/runtime/asm_amd64.s:1333
Wrap()函數在已有錯誤基礎上同時附加堆棧信息和新提示信息,WithMessage()函數在已有錯誤基礎上附加新提示信息,WithStack()函數在已有錯誤基礎上附加堆棧信息。在實際中可根據情況選擇使用。
這個包比較簡潔實用,而且有可能會納入標準庫,所以建議在實際生產中使用。另外環境變量 GOTRACEBACK 對堆棧信息的輸出信息量有較大影響,不同的值有不同詳細程度的信息輸出。
- GOTRACEBACK = none
- GOTRACEBACK = single(默認值)
- GOTRACEBACK = all
- GOTRACEBACK = system
- GOTRACEBACK = crash
在程序中 debug.SetTraceback (level string) 函數也可以設置該變量。
3. 實現細節
github.com/pkg/errors 是怎么實現這個功能的呢?其實,它的原理非常簡單,它就是利用了 fmt 包的一個特性。fmt 包在打印 error 之前會判斷當前打印的對象是否實現了 Formatter 接口,這個 formatter 接口只有一個 format 方法。如果要輸出的對象實現了這個 Formatter 接口,則調用對象的 Format 方法來打印信息:
type Formatter interface {Format(f State, c rune)
}
而 github.com/pkg/errors 中提供的各種初始化 error 方法(包括 errors.New)封裝了一個 fundamental 結構,這個結構就是實現了 Formatter 接口:
// fundamental is an error that has a message and a stack, but no caller.
type fundamental struct {msg string*stack
}
我們可以看到,這個 fundamental 結構中帶著 error 的信息和堆棧信息。并且實現了 Format 方法。在 Format 方法中,判斷調用 fmt.Printf 函數的第一個參數,如果是 +v,則打印錯誤內容和堆棧信息,如果是 v 或者 s,則打印錯誤內容,如果是 q,則打印轉義后的信息:
func (f *fundamental) Format(s fmt.State, verb rune){switch verb {case 'v':if s.Flag('+') {io.WriteString(s, f.msg)f.stack.Format(s, verb)return}fallthroughcase 's':io.WriteString(s, f.msg)case 'q':fmt.Fprintf(s, "%q", f.msg) }
}
所以說,我們在實際的工作項目中,我建議你盡量使用 pkg/errors 而不是官方 error 庫,這樣我們能在錯誤出現的時候獲取更多的錯誤信息,更快地定位問題。
總結
以上是生活随笔為你收集整理的Go 学习笔记(72)— Go 第三方库之 pkg/errors 带堆栈的错误处理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2022-2028年中国FNG硅胶行业市
- 下一篇: 2022-2028年中国玻璃纤维毡热塑性