最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一些關(guān)于Go程序錯誤處理的相關(guān)建議

 更新時間:2021年09月28日 09:51:32   作者:網(wǎng)管  
錯誤處理在每個語言中都是一項重要內(nèi)容,眾所周知,通常寫程序時遇到的分為異常與錯誤兩種,Golang中也不例外,這篇文章主要給大家介紹了一些關(guān)于Go程序錯誤處理的相關(guān)建議,需要的朋友可以參考下

前言

Go的錯誤處理這塊是日常被大家吐槽較多的地方,我在工作中也觀察到一些現(xiàn)象,比較嚴(yán)重的是在各層級的邏輯代碼中對錯誤的處理有些重復(fù)。

比如,有人寫代碼就會在每一層都判斷錯誤并記錄日志,從代碼層面看,貌似很嚴(yán)謹(jǐn),但是如果看日志會發(fā)現(xiàn)一堆重復(fù)的信息,等到排查問題時反而會造成干擾。

今天給大家總結(jié)三點Go代碼錯誤處理相關(guān)的最佳實踐給大家。

這些最佳實踐也是網(wǎng)上一些前輩分享的,我自己實踐后在這里用自己的語言描述出來,希望能對大家有所幫助。

認(rèn)識error

Go程序通過error類型的值表示錯誤

error類型是一個內(nèi)建接口類型,該接口只規(guī)定了一個返回字符串值的Error方法。

type error interface {
    Error() string
}

Go語言的函數(shù)經(jīng)常會返回一個error值,調(diào)用者通過測試error值是否是nil來進行錯誤處理。

i, err := strconv.Atoi("42")
if err != nil {
    fmt.Printf("couldn't convert number: %v\n", err)
    return
}
fmt.Println("Converted integer:", i)

error為nil時表示成功;非nil的error表示失敗。

自定義錯誤記得要實現(xiàn)error接口

我們經(jīng)常會定義符合自己需要的錯誤類型,但是記住要讓這些類型實現(xiàn)error接口,這樣就不用在調(diào)用方的程序里引入額外的類型。

比如下面我們自己定義了myError這個類型,如果不實現(xiàn)error接口的話,調(diào)用者的代碼中就會被myError這個類型侵入。比如下面的run函數(shù),在定義返回值類型時,直接定義成error即可。

package myerror

import (
 "fmt"
 "time"
)

type myError struct {
 Code int
 When time.Time
 What string
}

func (e *myError) Error() string {
 return fmt.Sprintf("at %v, %s, code %d",
  e.When, e.What, e.Code)
}

func run() error {
 return &MyError{
    1002,
    time.Now(),
    "it didn't work",
 }
}

func TryIt() {
 if err := run(); err != nil {
  fmt.Println(err)
 }
}

如果myError不實現(xiàn)error接口的話,這里的返回值類型就要定義成myError類型??上攵o接著調(diào)用者的程序里就要通過myError.Code == xxx 來判斷到底是哪種具體的錯誤(當(dāng)然想要這么干得先把myError改成導(dǎo)出的MyError)。

那調(diào)用者判斷自定義error是具體哪種錯誤的時候應(yīng)該怎么辦呢,myError并未向包外暴露,答案是通過向包外暴露檢查錯誤行為的方法來實現(xiàn)。

myerror.IsXXXError(err) 
...

抑或是通過比較error本身與包向外暴露的常量錯誤是否相等來判斷,比如操作文件時常用來判斷文件是否結(jié)束的io.EOF。

類似的還有g(shù)orm.ErrRecordNotFound等各種開源包對外暴露的錯誤常量。

if err != io.EOF {
    return err
}

錯誤處理常犯的錯誤

先看一段簡單的程序,看大家能不能發(fā)現(xiàn)一些細微的問題

func WriteAll(w io.Writer, buf []byte) error {
    _, err := w.Write(buf)
    if err != nil {
        log.Println("unable to write:", err) // annotated error goes to log file
        return err                           // unannotated error returned to caller
    }
    return nil
}

func WriteConfig(w io.Writer, conf *Config) error {
    buf, err := json.Marshal(conf)
    if err != nil {
        log.Printf("could not marshal config: %v", err)
        return err
    }
    if err := WriteAll(w, buf); err != nil {
        log.Println("could not write config: %v", err)
        return err
    }
    return nil
}

func main() {
    err := WriteConfig(f, &conf)
    fmt.Println(err) // io.EOF
}

錯誤處理常犯的兩個問題

上面程序的錯誤處理暴露了兩個問題:

1.底層函數(shù)WriteAll在發(fā)生錯誤后,除了向上層返回錯誤外還向日志里記錄了錯誤,上層調(diào)用者做了同樣的事情,記錄日志然后把錯誤再返回給程序頂層。

因此在日志文件中得到一堆重復(fù)的內(nèi)容

unable to write: io.EOF
could not write config: io.EOF
...

2. 在程序的頂部,雖然得到了原始錯誤,但沒有相關(guān)內(nèi)容,換句話說沒有把WriteAll、WriteConfig記錄到 log 里的那些信息包裝到錯誤里,返回給上層。

針對這兩個問題的解決方案可以是,在底層函數(shù)WriteAll、WriteConfig中為發(fā)生的錯誤添加上下文信息,然后將錯誤返回上層,由上層程序最后處理這些錯誤。

一種簡單的包裝錯誤的方法是使用fmt.Errorf函數(shù),給錯誤添加信息。

func WriteConfig(w io.Writer, conf *Config) error {
    buf, err := json.Marshal(conf)
    if err != nil {
        return fmt.Errorf("could not marshal config: %v", err)
    }
    if err := WriteAll(w, buf); err != nil {
        return fmt.Errorf("could not write config: %v", err)
    }
    return nil
}
func WriteAll(w io.Writer, buf []byte) error {
    _, err := w.Write(buf)
    if err != nil {
        return fmt.Errorf("write failed: %v", err)
    }
    return nil
}

給錯誤附加上下文信息

fmt.Errorf只是給錯誤添加了簡單的注解信息,如果你想在添加信息的同時還加上錯誤的調(diào)用棧,可以借助github.com/pkg/errors這個包提供的錯誤包裝能力。

//只附加新的信息
func WithMessage(err error, message string) error

//只附加調(diào)用堆棧信息
func WithStack(err error) error

//同時附加堆棧和信息
func Wrap(err error, message string) error

有包裝方法,就有對應(yīng)的解包方法,Cause方法會返回包裝錯誤對應(yīng)的最原始錯誤--即會遞歸地進行解包。

func Cause(err error) error

下面是使用github.com/pkg/errors改寫后的錯誤處理程序

func ReadFile(path string) ([]byte, error) {
    f, err := os.Open(path)
    if err != nil {
        return nil, errors.Wrap(err, "open failed")
    }
    defer f.Close()
    buf, err := ioutil.ReadAll(f)
    if err != nil {
        return nil, errors.Wrap(err, "read failed")
    }
    return buf, nil
}
func ReadConfig() ([]byte, error) {
    home := os.Getenv("HOME")
    config, err := ReadFile(filepath.Join(home, ".settings.xml"))
    return config, errors.WithMessage(err, "could not read config")
}


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

上面格式化字符串時用的 %+v 是在 % v 基礎(chǔ)上,對值進行展開,即展開復(fù)合類型值,比如結(jié)構(gòu)體的字段值等明細。

這樣既能給錯誤添加調(diào)用棧信息,又能保留對原始錯誤的引用,通過Cause可以還原到最初始引發(fā)錯誤的原因。

總結(jié)

總結(jié)一下,錯誤處理的原則就是:

錯誤只在邏輯的最外層處理一次,底層只返回錯誤。
底層除了返回錯誤外,要對原始錯誤進行包裝,增加錯誤信息、調(diào)用棧等這些有利于排查的上下文信息。

到此這篇關(guān)于Go程序錯誤處理的相關(guān)建議的文章就介紹到這了,更多相關(guān)Go程序錯誤處理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語言實現(xiàn)Snowflake雪花算法

    Go語言實現(xiàn)Snowflake雪花算法

    雪花算法產(chǎn)生的背景當(dāng)然是twitter高并發(fā)環(huán)境下對唯一ID生成的需求,得益于twitter內(nèi)部牛的技術(shù),雪花算法能夠流傳于至今并且被廣泛使用,本文就詳細的介紹一下,感興趣的可以了解一下
    2021-06-06
  • 淺析Go語言bitset的實現(xiàn)原理

    淺析Go語言bitset的實現(xiàn)原理

    bitset包是一個將非負(fù)整數(shù)映射到布爾值的位的集合,這篇文章主要通過開源包bitset來為大家分析一下位集合的設(shè)計和實現(xiàn),感興趣的可以學(xué)習(xí)一下
    2023-08-08
  • 使用Go語言實現(xiàn)發(fā)送微信群消息

    使用Go語言實現(xiàn)發(fā)送微信群消息

    這篇文章主要為大家詳細介紹了如何使用Go語言實現(xiàn)發(fā)送微信群消息,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-01-01
  • golang實現(xiàn)跨域訪問的方法

    golang實現(xiàn)跨域訪問的方法

    這篇文章主要介紹了golang實現(xiàn)跨域訪問的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-01-01
  • Golang中map的深入探究

    Golang中map的深入探究

    Go中Map是一個KV對集合,下面這篇文章主要給大家介紹了關(guān)于Golang中map探究的相關(guān)資料,文中通過實例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2022-09-09
  • golang敏感詞過濾的實現(xiàn)

    golang敏感詞過濾的實現(xiàn)

    本文主要介紹了golang敏感詞過濾的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • 基于Golang 高并發(fā)問題的解決方案

    基于Golang 高并發(fā)問題的解決方案

    這篇文章主要介紹了Golang 高并發(fā)問題的解決方案,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05
  • Go1.20最新資訊go?arena手動管理內(nèi)存鴿了

    Go1.20最新資訊go?arena手動管理內(nèi)存鴿了

    由于過于繁雜,Go?核心團隊成員@Ian?Lance?Taylor,也表態(tài):目前尚未做出任何決定,也不可能在短期內(nèi)做出任何決定,可以認(rèn)為這個提案基本鴿了,今天這篇文章就是給大家同步目前的情況
    2023-11-11
  • 使用Go語言實現(xiàn)LRU緩存的代碼詳解

    使用Go語言實現(xiàn)LRU緩存的代碼詳解

    在日常開發(fā)中,緩存是提高系統(tǒng)性能的重要手段,LRU緩存是一種基于“最近最少使用”策略的緩存系統(tǒng),其目的是在空間受限的情況下保留最新、最常用的數(shù)據(jù),本文將詳細講解如何使用?Go?語言實現(xiàn)一個?LRU?緩存,需要的朋友可以參考下
    2024-11-11
  • Golang精編49面試題匯總(選擇題)

    Golang精編49面試題匯總(選擇題)

    這篇文章主要介紹了Golang精編49面試題匯總(選擇題),本文章內(nèi)容詳細,具有很好的參考價值,希望對大家有所幫助,需要的朋友可以參考下
    2023-01-01

最新評論

丹棱县| 扬中市| 抚顺县| 安义县| 秀山| 平度市| 乌拉特后旗| 柘荣县| 那坡县| 周宁县| 鹿泉市| 洪洞县| 宜兰市| 江北区| 阜康市| 石门县| 新宁县| 泰和县| 长宁县| 伊吾县| 长春市| 同德县| 山西省| 邯郸县| 多伦县| 济宁市| 神农架林区| 台州市| 慈溪市| 景宁| 龙川县| 大邑县| 略阳县| 抚远县| 隆尧县| 临沧市| 浦城县| 天门市| 北海市| 广水市| 洪雅县|