golang error與panic處理

error與panic

error:可預(yù)見(jiàn)的錯(cuò)誤

panic:不可預(yù)見(jiàn)的異常

panic處理

通過(guò)panic,defer,recover來(lái)處理異常

如下示例代碼,當(dāng)一個(gè)http鏈接到來(lái)時(shí),golang會(huì)調(diào)用serve函數(shù),serve函數(shù)會(huì)解析http協(xié)議,然后交給上層的handler處理,如果上層的handler內(nèi)拋出異常的話,會(huì)被defer里面的recover函數(shù)捕獲,打印出當(dāng)前的堆棧信息,這樣就可以防止一個(gè)http請(qǐng)求發(fā)生異常導(dǎo)致整個(gè)程序崩潰了。

func (c *conn)serve() {
    defer func() {
        if err := recover(); err != nil {
            const size = 64 << 10
            buf := make([]byte, size)
            buf = buf[:runtime.Stack(buf, false)]
            fmt.Printlf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
        }
    }()
    w := c.readRequest()
    ServeHTTP(w, w.req)
} 

error處理

在defer中集中處理錯(cuò)誤

func demo() {
    var err error 
    defer func() {
        if err != nil {
            //  處理發(fā)生的錯(cuò)誤,打印日志等信息
            return 
        }
    }
    err = func1()
    if err != nil {
        return 
    }
    err = func2()
    if err != nil {
        return 
    }
    .....
} 

all-or-nothing check

如果一個(gè)完整的任務(wù)內(nèi)有多個(gè)小的任務(wù),我們沒(méi)有必要執(zhí)行完每個(gè)小的任務(wù)都檢查下是否有錯(cuò),我們可以在所有的小任務(wù)執(zhí)行完成后再去判斷整個(gè)任務(wù)的執(zhí)行過(guò)程中有沒(méi)有錯(cuò)誤。

//冗長(zhǎng)的代碼,滿篇重復(fù)的 if err != nil 
_, err = fd.Write(p0[a:b])
if err != nil {
    return err
}
_, err = fd.Write(p1[c:d])
if err != nil {
    return err
}
_, err = fd.Write(p2[e:f])
if err != nil {
    return err
}
// and so on

我們可以這樣簡(jiǎn)化代碼

type errWriter struct {
    w   io.Writer
    err error
}
func (ew *errWriter) write(buf []byte) {
    if ew.err != nil {
        return
    }
    _, ew.err = ew.w.Write(buf)
}
ew := &errWriter{w: fd}
ew.write(p0[a:b])
ew.write(p1[c:d])
ew.write(p2[e:f])
// 只需要在最后檢查一次
if ew.err != nil {
    return ew.err
}

這種處理方式有一個(gè)明顯的問(wèn)題是不知道整個(gè)任務(wù)是在哪一個(gè)小的任務(wù)執(zhí)行的時(shí)候發(fā)生錯(cuò)誤,如果我們需要關(guān)注每個(gè)小任務(wù)的進(jìn)度則這種方式就不適合了。

錯(cuò)誤上下文

error是一個(gè)接口,任何實(shí)現(xiàn)了Error()函數(shù)的類型都滿足該接口,errors.New()實(shí)際上返回的是一個(gè)errorString類型,該類型內(nèi)僅包含一個(gè)string字符串,沒(méi)有錯(cuò)誤發(fā)生的上下文信息,當(dāng)我們把error不斷向上拋,在上層做統(tǒng)一處理時(shí),只能輸出一個(gè)字符串信息而不知道錯(cuò)誤是在什么地方什么情況下發(fā)生的。

type error interface {
    Error() string
}
type errorString struct {
    s string
}
func (e *errorString) Error() string {
    return e.s
}
func New(text string) error {
    return &errorString{text}
}

既然error是一個(gè)接口,那我們也可以自己實(shí)現(xiàn)一個(gè)滿足該接口的類型,并記錄下發(fā)生錯(cuò)誤的文件,函數(shù),堆棧等信息,更方便錯(cuò)誤的查找。

package stackerr
import (
    "fmt"
    "runtime"
    "strings"
)
type StackErr struct {
    Filename      string
    CallingMethod string
    Line          int
    ErrorMessage  string
    StackTrace    string
}
func New(err interface{}) *StackErr {
    var errMessage string
    switch t := err.(type) {
    case *StackErr:
        return t
    case string:
        errMessage = t
    case error:
        errMessage = t.Error()
    default:
        errMessage = fmt.Sprintf("%v", t)
    }
    stackErr := &StackErr{}
    stackErr.ErrorMessage = errMessage
    _, file, line, ok := runtime.Caller(1)
    if ok {
        stackErr.Line = line
        components := strings.Split(file, "/")
        stackErr.Filename = components[(len(components) - 1)]
    }
    const size = 1 << 12
    buf := make([]byte, size)
    n := runtime.Stack(buf, false)
    stackErr.StackTrace = string(buf[:n])
    return stackErr
}
func (this *StackErr) Error() string {
    return this.ErrorMessage
}
func (this *StackErr) Stack() string {
    return fmt.Sprintf("{%s:%d} %s\nStack Info:\n %s", this.Filename, this.Line, this.ErrorMessage, this.StackTrace)
}
func (this *StackErr) Detail() string {
    return fmt.Sprintf("{%s:%d} %s", this.Filename, this.Line, this.ErrorMessage)
}

第三方錯(cuò)誤庫(kù)推薦

github.com/pkg/errors是一款提供了日志上下文封裝的error庫(kù),可以非常方便的給error加上額外的信息、記錄stack信息等等。詳細(xì)使用說(shuō)明可以參考github或者源碼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 序言 錯(cuò)誤和異常是兩個(gè)不同的概念,非常容易混淆。很多程序員習(xí)慣將一切非正常情況都看做錯(cuò)誤,而不區(qū)分錯(cuò)誤和異常,即使...
    _張曉龍_閱讀 79,371評(píng)論 16 137
  • error code(錯(cuò)誤代碼)=0是操作成功完成。error code(錯(cuò)誤代碼)=1是功能錯(cuò)誤。error c...
    Heikki_閱讀 3,531評(píng)論 1 9
  • 前言 本規(guī)范是針對(duì) Go 語(yǔ)言的編碼規(guī)范,目的是為了統(tǒng)一項(xiàng)目的編碼風(fēng)格,提高源程序的可讀性、可靠性和可重用性,從而...
    _張曉龍_閱讀 2,039評(píng)論 5 21
  • error code(錯(cuò)誤代碼)=2000是無(wú)效的像素格式。error code(錯(cuò)誤代碼)=2001是指定的驅(qū)動(dòng)...
    Heikki_閱讀 2,188評(píng)論 0 4
  • 本周的工作計(jì)劃主要有三項(xiàng): 一、繼確定研究方向之后,進(jìn)一步明確我的研究目的和內(nèi)容,調(diào)研現(xiàn)有研究方向下哪些領(lǐng)域比較受...
    IceySu閱讀 343評(píng)論 1 1

友情鏈接更多精彩內(nèi)容