原文連接:www.flysnow.org/2019/01/01/…
微信公衆號:flysnow_org(飛雪無情)html
對於Go語言(golang)的錯誤設計,相信不少人已經體驗過了,它是經過返回值的方式,來強迫調用者對錯誤進行處理,要麼你忽略,要麼你處理(處理也能夠是繼續返回給調用者),對於golang這種設計方式,咱們會在代碼中寫大量的if
判斷,以便作出決定。java
func main() {
conent,err:=ioutil.ReadFile("filepath")
if err !=nil{
//錯誤處理
}else {
fmt.Println(string(conent))
}
}
複製代碼
這類代碼,在咱們編碼中是很是的,大部分狀況下error
都是nil
,也就是沒有任何錯誤,可是非nil
的時候,意味着錯誤就出現了,咱們須要對他進行處理。git
error
其實一個接口,內置的,咱們看下它的定義github
// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
Error() string
}
複製代碼
它只有一個方法 Error
,只要實現了這個方法,就是實現了error
。如今咱們本身定義一個錯誤試試。golang
type fileError struct {
}
func (fe *fileError) Error() string {
return "文件錯誤"
}
複製代碼
自定義了一個fileError
類型,實現了error
接口。如今測試下看看效果。bash
func main() {
conent, err := openFile()
if err != nil {
fmt.Println(err)
} else {
fmt.Println(string(conent))
}
}
//只是模擬一個錯誤
func openFile() ([]byte, error) {
return nil, &fileError{}
}
複製代碼
咱們運行模擬的代碼,能夠看到文件錯誤
的通知。微信
在實際的使用過程當中,咱們可能遇到不少錯誤,他們的區別是錯誤信息不同,一種作法是每種錯誤都相似上面同樣定義一個錯誤類型,可是這樣太麻煩了。咱們發現Error
返回的實際上是個字符串,咱們能夠修改下,讓這個字符串能夠設置就能夠了。函數
type fileError struct {
s string
}
func (fe *fileError) Error() string {
return fe.s
}
複製代碼
恩,這樣改造後,咱們就能夠在聲明fileError
的時候,設置好要提示的錯誤文字,就能夠知足咱們不一樣的須要了。測試
//只是模擬一個錯誤
func openFile() ([]byte, error) {
return nil, &fileError{"文件錯誤,自定義"}
}
複製代碼
恩,能夠了,已經達到了咱們的目的。如今咱們能夠把它變的更通用一些,好比修改fileError
的名字,再建立一個輔助函數,便於咱們建立不一樣的錯誤類型。網站
//blog:www.flysnow.org
//wechat:flysnow_org
func New(text string) error {
return &errorString{text}
}
type errorString struct {
s string
}
func (e *errorString) Error() string {
return e.s
}
複製代碼
變成以上這樣,咱們就能夠經過New
函數,輔助咱們建立不一樣的錯誤了,這其實就是咱們常常用到的errors.New
函數,被咱們一步步剖析演化而來,如今你們對Go語言(golang)內置的錯誤error
有了一個清晰的認知了。
雖然Go語言對錯誤的設計很是簡潔,可是對於咱們開發者來講,很明顯是不足的,好比咱們須要知道出錯的更多信息,在什麼文件的,哪一行代碼?只有這樣咱們才更容易的定位問題。
還有好比,咱們想對返回的error
附加更多的信息後再返回,好比以上的例子,咱們怎麼作呢?咱們只能先經過Error
方法,取出原來的錯誤信息,而後本身再拼接,再使用errors.New
函數生成新錯誤返回。
若是咱們之前作過java開發,咱們知道Java的異常是能夠嵌套的,也就是說,經過這個,咱們很容易知道錯誤的根本緣由,由於Java的異常,是一層層的嵌套返回的,無論中間經歷了多少包裝,咱們能夠經過cause
找到根本錯誤的緣由。
若是要解決以上的問題,那麼首先咱們必須再繼續擴充咱們的errorString
,再增長一些字段來存儲更多的信息。好比咱們要記錄堆棧信息。
type stack []uintptr
type errorString struct {
s string
*stack
}
複製代碼
歡迎關注微信公衆號flysnow_org
或者博客網站 www.flysnow.org/ 查看更多原創文章。
有了存儲堆棧信息的stack
字段,咱們在生成錯誤的時候,就能夠把調用的堆棧信息存儲在這個字段裏。
//blog:www.flysnow.org
//wechat:flysnow_org
func callers() *stack {
const depth = 32
var pcs [depth]uintptr
n := runtime.Callers(3, pcs[:])
var st stack = pcs[0:n]
return &st
}
func New(text string) error {
return &errorString{
s: text,
stack: callers(),
}
}
複製代碼
完美解決,如今若是再解決,對現有的錯誤附加一些信息的問題呢?相信你們應該有思路了。
type withMessage struct {
cause error
msg string
}
func WithMessage(err error, message string) error {
if err == nil {
return nil
}
return &withMessage{
cause: err,
msg: message,
}
}
複製代碼
使用WithMessage
函數,對原來的error
包裝下,就能夠生成一個新的帶有包裝信息的錯誤了。
以上咱們在解決問題是,採起的方法是否是比較熟悉?尤爲是看源代碼,沒錯,這就是github.com/pkg/errors
這個錯誤處理庫的源代碼。
由於Go語言提供的錯誤太簡單了,以致於簡單的咱們沒法更好的處理問題,甚至不能爲咱們處理錯誤,提供更有用的信息,因此誕生了不少對錯誤處理的庫,github.com/pkg/errors
是比較簡潔的同樣,而且功能很是強大,受到了大量開發者的歡迎,使用者不少。
它的使用很是簡單,若是咱們要新生成一個錯誤,可使用New
函數,生成的錯誤,自帶調用堆棧信息。
func New(message string) error 複製代碼
若是有一個現成的error
,咱們須要對他進行再次包裝處理,這時候有三個函數能夠選擇。
//只附加新的信息
func WithMessage(err error, message string) error //只附加調用堆棧信息 func WithStack(err error) error //同時附加堆棧和信息 func Wrap(err error, message string) error 複製代碼
其實上面的包裝,很相似於Java的異常包裝,被包裝的error
,其實就是Cause
,在前面的章節提到錯誤的根本緣由,就是這個Cause
。因此這個錯誤處理庫爲咱們提供了Cause
函數讓咱們能夠得到最根本的錯誤緣由。
func Cause(err error) error {
type causer interface {
Cause() error
}
for err != nil {
cause, ok := err.(causer)
if !ok {
break
}
err = cause.Cause()
}
return err
}
複製代碼
使用for
循環一直找到最根本(最底層)的那個error
。
以上的錯誤咱們都包裝好了,也收集好了,那麼怎麼把他們裏面存儲的堆棧、錯誤緣由等這些信息打印出來呢?其實,這個錯誤處理庫的錯誤類型,都實現了Formatter
接口,咱們能夠經過fmt.Printf
函數輸出對應的錯誤信息。
%s,%v //功能同樣,輸出錯誤信息,不包含堆棧
%q //輸出的錯誤信息帶引號,不包含堆棧
%+v //輸出錯誤信息和堆棧
複製代碼
以上若是有循環包裝錯誤類型的話,會遞歸的把這些錯誤都會輸出。
經過使用這個 github.com/pkg/errors
錯誤庫,咱們能夠收集更多的信息,可讓咱們更容易的定位問題。
咱們收集的這些信息不止能夠輸出到控制檯,也能夠當作日誌,使用輸出到相應的Log
日誌裏,便於分析問題。
聽說這個庫,會被加入到Golang 標準 SDK 裏,期待着,若是加入的話,應該就是補充如今標準庫裏的errors
這個package了。
本文爲原創文章,轉載註明出處,歡迎掃碼關注公衆號
flysnow_org
或者網站asf www.flysnow.org/ ,第一時間看後續精彩文章。以爲好的話,請猛擊文章右下角「好看」,感謝支持。