Golang 異常/日誌處理

一、xerrors 異常

xerrors 包是一個很是棒的設計,不一樣於往常語言如java/php,由於go的errors只是一個string類型的映射,因此內存佔用空間不多。這在golang的核心庫和golang大多數開源模塊中使用,簡單,高效,穩定!好比:php

var myErr:=errors.New("error msg")
func act1()err{
     return myErr
}

        以上代很是高效,若是你常常作go的開發時。這種解決時常常會存在一個很頭疼的問題,就是異常觸發點很難定位,由於你很難去定位到異常發生的代碼文件及行數和調用的堆棧,但出現問題時很是必要知道是什麼狀況處罰的這個異常,只是一個message不足以讓開發人員去解決問題。java

        在最新的xerrors包中,引入了一個caller堆地址,經過使用Format接口能夠格式化出調用第一行的堆,但這每每不少時候不足以解決去定位問題。這時能夠考慮使用第三方包,請繼續往下看:)git

二、堆棧異常處理

go.modgithub

require github.com/pkg/errors latest
go mod download
package main

import(
    "fmt"
    "github.com/pkg/errors"
) 

func act1()error{
    return errors.New("hello world!")
}

func main(){
fmt.Printf("%+v\n",act1())
}

 

Output:golang

Hello world!框架

main.act1異步

/tmp/aa/main.go:9函數

main.main性能

/tmp/aa/main.go:13ui

runtime.main

/usr/local/Cellar/go/1.13.4/libexec/src/runtime/proc.go:203

runtime.goexit

/usr/local/Cellar/go/1.13.4/libexec/src/runtime/asm_amd64.s:1357

 

以上代碼中,首先引用pkg/errrors堆異常包,而後經過使用%+v格式化error,將堆棧數據打印到屏幕中,經過這種方式,經過調用堆棧你能夠快速定位異常的代碼位置,從而快速解決問題。

 

三、go原生日誌框架

      glog是Golang log的縮寫,開源於golang核心庫,因此應用比較普遍實現以下:

package main

import "log"

func main(){
log.Println("Info","hello world")
}

 

output:

2019/11/29 17:44:56 Info hello world

由於功能過於簡單,致使在golang中的日誌很難實現日誌的來源和日誌級別實現日誌的過濾功能。在不少模塊集成後,致使日誌繁雜錯亂很難閱讀,甚至到致不一樣模塊包括的日誌格式不一樣以及日誌的級別也不一致,致使日誌的分析帶來了很多挑戰!

四、midlog 日誌框架

midlog相似於java的slf4j框架,定義了通用的日誌級別、日誌來源鎖定、日誌重定向框架,而且提供日誌的重定義組件,能夠將全部日誌彙總處理,很是方便! 

midlog 日誌框架示 

4.一、midlog 包接取

go.mod

     require github.com/lingdor/midlog

or

    go get github.com/lingdor/midlog

4.二、midlog通用日誌函數

 建立日誌對象

var Logger Midlog = New("your module name")

 建議這個Logger聲明爲外部可訪問的,這樣若是想實現日誌的分流時,能夠經過這個Logger對象判斷日誌的來源模塊是哪來的,而後分流不一樣的日誌記錄。

 

Logger.Trace("hello")

Logger.Info("hello")

Logger.Warn("hello")

Logger.Debug("hello")

Logger.Error1("hello")

Logger.Error2("hello")

Logger.Error3("hello")

Logger.Log(LevelTrace, "hello")

 

Logger.Infof("hello %s","world!")

Logger.Tracef("hello %s","world!")

Logger.Warnf("hello %s","world!")

Logger.Debugf("hello %s","world!")

Logger.Error1f("hello %s","world!")

Logger.Error2f("hello %s","world!")

Logger.Error3f("hello %s","world!")

Logger.Logf(LevelTrace, "hello %s","world!")

 

能夠按照不一樣的日誌級別,選擇不一樣的函數,實現日誌的記錄!

 

4.三、對接go原生日誌框架

go.mod 增長依賴

require github.com/lingdor/midlog v1.0.0
require github.com/lingdor/glog2midlog v1.0.0
package main

 import (

_ "github.com/lingdor/glog2midlog"

"github.com/lingdor/midlog-examples/library1"

"log"

)

func main() {

log.Println("info", "hello world!")

}

output: \

2019-11-29 18:15:51 GLOG INFO  hello world!

 

4.4 對接logrus

go.mod

require github.com/lingdor/midlog v1.0.0
require github.com/lingdor/midlog2logrus v1.4.3

main.go

package main 

import (
"fmt"
"github.com/lingdor/midlog"
_ "github.com/lingdor/midlog2logrus"
)

var Logger = midlog.New("useLogrus")

func main() {
Logger.Info("hello world!")
Logger.Error1("logger errror log")
Logger.Ext(midlog.ExtMap{"123": "456"}).Error1("ext log")
fmt.Println("done")
}

output:

INFO[0000] hello world!                                 

ERRO[0000] logger errror log                            

ERRO[0000] ext log                                       123=456

done 

4.5 對接zap

go.mod

require github.com/lingdor/midlog v1.0.0
require github.com/lingdor/midlog2zap v1.13.0

 main.go

package main

import (
"fmt"
"github.com/lingdor/midlog"
_ "github.com/lingdor/midlog2zap"
)

var Logger = midlog.New("useLogrus")

func main() {
Logger.Info("hello world!")
Logger.Error1("logger errror log")
Logger.Ext(midlog.ExtMap{"123": "456"}).Info("ext log")
fmt.Println("done")
}

 output:

{"level":"info","ts":1575022710.237491,"caller":"midlog2zap@v1.13.0/zapWriter.go:36","msg":"hello world!"}

{"level":"error","ts":1575022710.23755,"caller":"midlog2zap@v1.13.0/zapWriter.go:37","msg":"logger errror log","stacktrace":"github.com/lingdor/midlog2zap.ZapWriter.Write\n\t/Users/bobby96333/go/pkg/mod/github.com/lingdor/midlog2zap@v1.13.0/zapWriter.go:37\ngithub.com/lingdor/midlog.(*midlogT).tryWriteLog\n\t/Users/bobby96333/go/pkg/mod/github.com/lingdor/midlog@v1.0.0/logWriter.go:19\ngithub.com/lingdor/midlog.(*midlogT).logWithCaller3\n\t/Users/bobby96333/go/pkg/mod/github.com/lingdor/midlog@v1.0.0/midlog.go:122\ngithub.com/lingdor/midlog.(*midlogT).Error1\n\t/Users/bobby96333/go/pkg/mod/github.com/lingdor/midlog@v1.0.0/midlog.go:69\nmain.main\n\t/Users/bobby96333/go/midlog-examples/useZap/main.go:13\nruntime.main\n\t/usr/local/Cellar/go/1.13.4/libexec/src/runtime/proc.go:203"}

{"level":"info","ts":1575022710.237587,"caller":"midlog2zap@v1.13.0/zapWriter.go:36","msg":"ext log","123":"456"}

done

 

五、總結 

     實際上midlog只是一個日誌接口層,並無實現日誌的滾動寫入、異步等日誌實際操做,但經過這個中間層,能夠靈活對接本身選擇的日誌框架,和分流功能。而後經過zap/logrus/seelog/zerolog等模塊組合實現日誌的存儲.

    不論是go的原生error仍是log,都是一個很是棒的設計,不少時候能夠解決最基礎的需求,這避免了不少時候由於不須要而避免的額外性能消耗。而還有一部份場景,只是簡單每每不足以解決問題,這致使簡單和複雜的衝突哲學層面的取捨。咱們在選擇時,應該清楚瞭解本身的需求是什麼,按照本身的須要去選擇合適的模塊,便可!

六、參考資料

midlog源碼

midlog-example源碼

zap源碼

logrus源碼

zap高性能日誌

謝謝

相關文章
相關標籤/搜索