真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

錯(cuò)誤處理(包括日志記錄)

錯(cuò)誤處理

簡(jiǎn)單的錯(cuò)誤處理是使用 Fprintf 和 %v 在標(biāo)準(zhǔn)錯(cuò)誤流上輸出一條消息,%v 可以使用默認(rèn)格式顯示任意類(lèi)型的值。
為了保持示例代碼簡(jiǎn)短,有時(shí)會(huì)對(duì)錯(cuò)誤處理有意進(jìn)行一定程度的忽略。明顯的錯(cuò)誤還是要處理的。但是有些出現(xiàn)概率很小的錯(cuò)誤,就忽略了,不過(guò)要標(biāo)記所跳過(guò)的錯(cuò)誤檢查,就是加上注釋。

創(chuàng)新互聯(lián)公司主營(yíng)孟連網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營(yíng)網(wǎng)站建設(shè)方案,成都app軟件開(kāi)發(fā)公司,孟連h5成都微信小程序搭建,孟連網(wǎng)站營(yíng)銷(xiāo)推廣歡迎孟連等地區(qū)企業(yè)咨詢(xún)

根據(jù)情形,將有許多可能的處理場(chǎng)景,接下來(lái)是5個(gè)例子。

一、將錯(cuò)誤傳遞下去

最常見(jiàn)的情形是將錯(cuò)誤傳遞下去,使得在子例程中發(fā)生的錯(cuò)誤變?yōu)橹髡{(diào)例程的錯(cuò)誤。
一種是不做任何操作立即向調(diào)用者返回錯(cuò)誤:

resp, err := http.Get(url)
if err != nil {
    return nil, err
}

還有一種,不會(huì)直接返回,因?yàn)殄e(cuò)誤信息中缺失一些關(guān)鍵信息:

doc, err := html.Parse(resp.Body)
resp.Body.Close()
if err != nil {
    return nil, fmt.Errorf("parsing %s as HTML: %v\n", url, err)
}

這里格式化了一條錯(cuò)誤消息并且返回一個(gè)新的錯(cuò)誤值??梢詾樵嫉腻e(cuò)誤消息不斷地添加上下文信息來(lái)建立一個(gè)可讀的錯(cuò)誤描述。當(dāng)錯(cuò)誤最終被程序的 main 函數(shù)處理時(shí),它應(yīng)該能夠提供一個(gè)從最根本問(wèn)題到總體故障的清晰因果鏈、這里有一個(gè) NASA 的事故調(diào)查的例子:

genesis: crashed: no parachute: G-switch failed: bad relay orientation

因?yàn)殄e(cuò)誤頻繁地串聯(lián)起來(lái),所以消息字符串首字母不應(yīng)該大寫(xiě)而且應(yīng)該避免換行。錯(cuò)誤結(jié)果可能會(huì)很長(zhǎng),但能能夠使用 grep 這樣的工具找到需要的信息。

需要添加的關(guān)鍵信息
有時(shí)候可以不用添加信息直接返回,有時(shí)候需要添加一些關(guān)鍵信息,因?yàn)殄e(cuò)誤信息里沒(méi)有。比如 os.Open 打開(kāi)文件時(shí),返回的錯(cuò)誤不僅僅包括錯(cuò)誤的信息,還包含文件的名字,因此調(diào)用者構(gòu)造錯(cuò)誤消息的時(shí)候不需要包含文件的名字這類(lèi)信息。具體哪些信息是缺少的關(guān)鍵信息需要在原始的錯(cuò)誤消息的基礎(chǔ)上添加?
一般地,f(x) 調(diào)用只負(fù)責(zé)報(bào)告函數(shù)的行為 f 和參數(shù)值 x,因?yàn)樗鼈兒湾e(cuò)誤的上下文相關(guān)。調(diào)用者則負(fù)責(zé)添加進(jìn)一步的信息,但是 f(x) 本身并不會(huì),并且在函數(shù)內(nèi)部也沒(méi)有這些信息。
比如上面的 html.Parse 返回的錯(cuò)誤信息里不可能有 url 的信息,但是,是關(guān)鍵信息需要添加。而 os.Open 中,文件名字也是關(guān)鍵信息,但是這個(gè)正是函數(shù)的參數(shù)值,所以函數(shù)本身會(huì)返回這個(gè)信息,不需要另外添加。

二、嘗試重試

對(duì)于不固定或者不可預(yù)測(cè)的錯(cuò)誤,在短暫的間隔后對(duì)操作進(jìn)行重試是合乎情理的。超出一定的重試次數(shù)和限定的時(shí)間后再報(bào)錯(cuò)退出。
下面給出了完整的代碼,暫時(shí)只看 WaitForServer 函數(shù):

package main

import (
    "fmt"
    "log"
    "net/http"
    "os"
    "time"
)

// 嘗試連接 url 對(duì)應(yīng)的服務(wù)器
// 在一分鐘內(nèi)使用指數(shù)退避策略進(jìn)行重試
// 所有的嘗試失敗后返回錯(cuò)誤
func WaitForServer(url string) error {
    const timeout = 1 * time.Minute
    deadline := time.Now().Add(timeout)
    for tries := 0; time.Now().Before(deadline); tries++ {
        _, err := http.Head(url)
        if err == nil {
            return nil // 成功
        }
        log.Printf("server not responding (%s); retrying...", err)
        time.Sleep(time.Second << uint(tries)) // 指數(shù)退避策略
    }
    return fmt.Errorf("server %s failed to respond after %s", url, timeout)
}

func main() {
    if len(os.Args) != 2 {
        fmt.Fprintf(os.Stderr, "需要提供 url 參數(shù)\n")
        os.Exit(1)
    }
    url := os.Args[1]
    if err := WaitForServer(url); err != nil {
        fmt.Fprintf(os.Stderr, "Site is down: %v\n", err)
        os.Exit(1)
    }
}

這里的指數(shù)退避策略,以及嘗試多次簡(jiǎn)單的超時(shí)退出的實(shí)現(xiàn)也很有意思。

三、輸出日志并退出

接著看上面的代碼,如果多次重試后依然不能成功,調(diào)用者能夠輸出錯(cuò)誤然后優(yōu)雅地停止程序,但一般這樣的處理應(yīng)該留給主程序部分:

if err := WaitForServer(url); err != nil {
    fmt.Fprintf(os.Stderr, "Site is down: %v\n", err)
    os.Exit(1)
}

通常,如果是庫(kù)函數(shù),應(yīng)該將錯(cuò)誤傳遞給調(diào)用者,除非這個(gè)錯(cuò)誤表示一個(gè)內(nèi)部的一致性錯(cuò)誤,這意味著庫(kù)內(nèi)部存在 bug。
這里還有一個(gè)更加方便的方法是通過(guò)調(diào)用 log.Fatalf 實(shí)現(xiàn)上面相同的效果。和所有的日志函數(shù)一樣,它默認(rèn)會(huì)將時(shí)間和日期作為前綴添加到錯(cuò)誤消息前:

if err := WaitForServer(url); err != nil {
    log.Fatalf("Site is down: %v\n", err)
}

這種帶日期時(shí)間的默認(rèn)格式有助于長(zhǎng)期運(yùn)行的服務(wù)器,而對(duì)于交互式的命令行工具則意義不大。
還可以自定義命令的名稱(chēng)作為 log 包的前綴,并且將日期和時(shí)間略去:

log.SetPrefix("wait: ")
log.SetFlags(0)

四、記錄log日志

在一些錯(cuò)誤情況下,只記錄下錯(cuò)誤信息然后程序繼續(xù)運(yùn)行。同樣地,可以選擇使用 log 包來(lái)增加日志的常用前綴:

if err := Ping(): err != nil {
    log.Printf("Ping failed: %v; networking disabled", err)
}

所有 log 函數(shù)都會(huì)為缺少換行符的日志補(bǔ)充一個(gè)換行符。
或者是,直接輸出到標(biāo)準(zhǔn)錯(cuò)誤流:

if err := Ping(): err != nil {
    fmt.Fprintf(os.Stderr, "Ping failed: %v; networking disabled\n", err)
}

沒(méi)有用 log 函數(shù),所以沒(méi)有時(shí)間日期,當(dāng)然也不需要。上面說(shuō)了,對(duì)于交互式的命令工具意義不大。

五、忽略錯(cuò)誤

在某些罕見(jiàn)的情況下,還可以直接安全地忽略掉整個(gè)日志:

dir, err := ioutil.TempDir("", "scratch")
if err != nil {
    return fmt.Errorf("failed to create temp dir: %v", err)
}
// 使用臨時(shí)的目錄
os.RemoveAll(dir)  // 忽略錯(cuò)誤,$TMPDIR 會(huì)被周期性刪除

調(diào)用 os.RemoveAll 可能會(huì)失敗,但程序忽略了這個(gè)錯(cuò)誤,原因是操作系統(tǒng)會(huì)周期性地清理臨時(shí)目錄。在這個(gè)例子中,有意的拋棄了錯(cuò)誤,但程序的邏輯看上去就和忘記去處理一樣了。要習(xí)慣考慮到每一個(gè)函數(shù)調(diào)用可能發(fā)生的出錯(cuò)情況,當(dāng)有意忽略一個(gè)錯(cuò)誤的時(shí)候,要清楚地注釋一下你的意圖。

error 接口

之前已經(jīng)使用過(guò) error 類(lèi)型了,實(shí)際上它是一個(gè)接口類(lèi)型,包含一個(gè)返回錯(cuò)誤消息的方法:

type error interface {
    Error() string
}

errors 包

構(gòu)造 error 最簡(jiǎn)單的方法是調(diào)用 errors.New,它會(huì)返回一個(gè)包含指定錯(cuò)誤消息的新 error 實(shí)例。
完整的 errors 包其實(shí)只有如下的4行代碼:

package errors

func New(text string) error { return &errorString{text} }

type errorString struct { s string }

func (e *errorString) Error() string { return e.s }

底層的 errorString 類(lèi)型是一個(gè)結(jié)構(gòu)體,而不是像其他包里那樣定義字符串的別名類(lèi)型。這主要是為了保護(hù)它所表示的錯(cuò)誤值無(wú)意間的(或者也可能是故意的)更新。
定義的 Error 方法是指針?lè)椒ǎ皇侵捣椒?。這樣每次 New 分配的 error 實(shí)例都互不相等,即使是同樣的錯(cuò)誤值,也是不同的地址:

fmt.Println(errors.New("TEST") == errors.New("TEST")) // false

這樣可以避免比如像 io.EOF 這樣重要的錯(cuò)誤,與僅僅只是包含同樣錯(cuò)誤消息的一個(gè)錯(cuò)誤相等。

fmt.Errorf

直接調(diào)用 errors.New 的情況比較少,只在直接能取得錯(cuò)誤值的字符串信息的時(shí)候使用:

func startCPUProfile(w io.Writer) error {
    if w == nil {
        return errors.New("nil File")
    }
    return pprof.StartCPUProfile(w)
}

更多的情況是會(huì)得到一個(gè)錯(cuò)誤值 err,而我們可以在這個(gè)錯(cuò)誤值之上做一點(diǎn)包裝,還需要做字符串格式化。有一個(gè)更易用的封裝函數(shù) fmt.Errorf,它額外還提供了字符串格式化的功能,所以一般都是用這個(gè):

doc, err := html.Parse(resp.Body)
if err != nil {
    return fmt.Errorf("parseing %s as HTML: %v", url, err)
}

本文標(biāo)題:錯(cuò)誤處理(包括日志記錄)
當(dāng)前路徑:http://weahome.cn/article/pejcdd.html

其他資訊

在線(xiàn)咨詢(xún)

微信咨詢(xún)

電話(huà)咨詢(xún)

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部