golang錯(cuò)誤處理加強(qiáng)版

為了方便分享,特地把blog抄到這里 = =
實(shí)在是對(duì)代碼中到處打印錯(cuò)誤日志的現(xiàn)象難以忍受, 于是琢磨了一個(gè)優(yōu)雅一些的錯(cuò)誤處理方式. 特地整理出來分享一下.
源碼地址: https://github.com/mozhata/merr

需求

  • 我想知道原始錯(cuò)誤出現(xiàn)在哪里, 還有對(duì)應(yīng)的函數(shù)調(diào)用棧是怎樣的
  • 我想給某個(gè)函數(shù)返回的錯(cuò)誤加上點(diǎn)其他信息再返回,但是又想保留原始錯(cuò)誤信息
  • 可能還需要一個(gè)狀態(tài)碼 用來標(biāo)識(shí)某一類錯(cuò)誤
  • 我想...算了,想太多不好

打印出來大概就是這么個(gè)效果

E500: err: new err
raw err: origin err
call stack: main.warpper
    practice/go/example/main.go:18
main.main
    practice/go/example/main.go:12
runtime.main
    runtime/proc.go:183
runtime.goexit
    runtime/asm_amd64.s:2086

(附打印函數(shù))

func logMerr(err error) {
    e := merr.WrapErr(err)
    fmt.Printf("E%d: err: %s\nraw err: %s\ncall stack: %s\n",
        e.StatusCode,
        e.Error(),
        e.RawErr(),
        e.CallStack(),
    )
}

關(guān)鍵代碼

首先要搞一個(gè)錯(cuò)誤類

type MErr struct {
    Message    string    // 保存自定義的錯(cuò)誤信息
    StatusCode int       // 錯(cuò)誤狀態(tài)碼
    rawErr     error     // 保存原始錯(cuò)誤信息
    stackPC    []uintptr // 保存函數(shù)調(diào)用棧指針
}

然后是一些關(guān)鍵的方法

func (e *MErr) Error() string {
    return e.Message
}

// RawErr the origin err
func (e MErr) RawErr() error {
    return e.rawErr
}

// CallStack get function call stack
func (e MErr) CallStack() string {
    frames := runtime.CallersFrames(e.stackPC)
    var (
        f      runtime.Frame
        more   bool
        result string
        index  int
    )
    for {
        f, more = frames.Next()
        if index = strings.Index(f.File, "src"); index != -1 {
            // trim GOPATH or GOROOT prifix
            f.File = string(f.File[index+4:])
        }
        result = fmt.Sprintf("%s%s\n\t%s:%d\n", result, f.Function, f.File, f.Line)
        if !more {
            break
        }
    }
    return result
}

其中CallStack()方法是用來吧函數(shù)調(diào)用棧指針轉(zhuǎn)換成字符串

還缺一個(gè)封裝方法

// maintain rawErr and update Message if fmtAndArgs is not empty
// update StatusCode to code if code is not 0
// notice: the returned value is used as error, so, should not return nil
func wrapErr(err error, code int, fmtAndArgs ...interface{}) *MErr {
    msg := fmtErrMsg(fmtAndArgs...)
    if err == nil {
        err = errors.New(msg)
    }
    if e, ok := err.(*MErr); ok {
        if msg != "" {
            e.Message = msg
        }
        if code != 0 {
            e.StatusCode = code
        }
        return e
    }

    pcs := make([]uintptr, 32)
    // skip the first 3 invocations
    count := runtime.Callers(3, pcs)
    e := &MErr{
        StatusCode: code,
        Message:    msg,
        rawErr:     err,
        stackPC:    pcs[:count],
    }
    if e.Message == "" {
        e.Message = err.Error()
    }
    return e
}

// fmtErrMsg used to format error message
func fmtErrMsg(msgs ...interface{}) string {
    if len(msgs) > 1 {
        return fmt.Sprintf(msgs[0].(string), msgs[1:]...)
    }
    if len(msgs) == 1 {
        if v, ok := msgs[0].(string); ok {
            return v
        }
        if v, ok := msgs[0].(error); ok {
            return v.Error()
        }
    }
    return ""
}

可以看出來 這個(gè)錯(cuò)誤處理的方法主要由fmtErrMsg wrapErr CallStack 這三部分實(shí)現(xiàn)

接下來封裝幾個(gè)方便用的函數(shù)

// WrapErr equal to InternalErr(err)
// notice: be careful, the returned value is *MErr, not error
func WrapErr(err error, fmtAndArgs ...interface{}) *MErr {
    return wrapErr(err, http.StatusInternalServerError, fmtAndArgs...)
}

// WrapErrWithCode if code is not 0, update StatusCode to code,
// if fmtAndArgs is not nil, update the Message according to fmtAndArgs
// notice: be careful, the returned value is *MErr, not error
func WrapErrWithCode(err error, code int, fmtAndArgs ...interface{}) *MErr {
    return wrapErr(err, code, fmtAndArgs...)
}

// NotFoundErr use http.StatusNotFound as StatusCode to express not found err
// if fmtAndArgs is not nil, update the Message according to fmtAndArgs
func NotFoundErr(err error, fmtAndArgs ...interface{}) error {
    return wrapErr(err, http.StatusNotFound, fmtAndArgs...)
}

這基本上就是全部的代碼了.沒有太多需要解釋的地方,基本看代碼就明白了. 更詳細(xì)的用法可以查看測(cè)試文件
這是源碼地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市撑教,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖粤铭,帶你破解...
    沈念sama閱讀 221,406評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件擅威,死亡現(xiàn)場(chǎng)離奇詭異咧栗,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)教藻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來右锨,“玉大人括堤,你說我怎么就攤上這事∩芤疲” “怎么了悄窃?”我有些...
    開封第一講書人閱讀 167,815評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蹂窖。 經(jīng)常有香客問我轧抗,道長(zhǎng),這世上最難降的妖魔是什么瞬测? 我笑而不...
    開封第一講書人閱讀 59,537評(píng)論 1 296
  • 正文 為了忘掉前任横媚,我火速辦了婚禮纠炮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘灯蝴。我一直安慰自己恢口,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,536評(píng)論 6 397
  • 文/花漫 我一把揭開白布穷躁。 她就那樣靜靜地躺著耕肩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪折砸。 梳的紋絲不亂的頭發(fā)上看疗,一...
    開封第一講書人閱讀 52,184評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音睦授,去河邊找鬼两芳。 笑死,一個(gè)胖子當(dāng)著我的面吹牛去枷,可吹牛的內(nèi)容都是我干的怖辆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼删顶,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼竖螃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起逗余,我...
    開封第一講書人閱讀 39,668評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤特咆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后录粱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腻格,經(jīng)...
    沈念sama閱讀 46,212評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,299評(píng)論 3 340
  • 正文 我和宋清朗相戀三年啥繁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了菜职。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,438評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡旗闽,死狀恐怖酬核,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情适室,我是刑警寧澤嫡意,帶...
    沈念sama閱讀 36,128評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站捣辆,受9級(jí)特大地震影響鹅很,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜罪帖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,807評(píng)論 3 333
  • 文/蒙蒙 一促煮、第九天 我趴在偏房一處隱蔽的房頂上張望邮屁。 院中可真熱鬧,春花似錦菠齿、人聲如沸佑吝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)芋忿。三九已至,卻和暖如春疾棵,著一層夾襖步出監(jiān)牢的瞬間戈钢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工是尔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留殉了,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,827評(píng)論 3 376
  • 正文 我出身青樓拟枚,卻偏偏與公主長(zhǎng)得像薪铜,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子恩溅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,446評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容