Golang 異常/日志處理


1、xerrors 異常

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

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

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

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

2、堆棧異常處理

go.mod

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:

Hello world!

main.act1

/tmp/aa/main.go:9

main.main

/tmp/aa/main.go:13

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,將堆棧數據打印到屏幕中,通過這種方式,通過調用堆棧你可以快速定位異常的代碼位置,從而快速解決問題。

 

3、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中的日志很難實現日志的來源和日志級別實現日志的過濾功能。在很多模塊集成后,導致日志繁雜錯亂很難閱讀,甚至到致不同模塊包括的日志格式不同以及日志的級別也不一致,導致日志的分析帶來了不少挑戰!

4、midlog 日志框架

midlog類似於java的slf4j框架,定義了通用的日志級別、日志來源鎖定、日志重定向框架,並且提供日志的重定義組件,可以將所有日志匯總處理,非常方便! 

midlog 日志框架示 

4.1、midlog 包接取

go.mod

     require github.com/lingdor/midlog

or

    go get github.com/lingdor/midlog

4.2、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.3、對接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

 

5、總結 

     實際上midlog只是一個日志接口層,並沒有實現日志的滾動寫入、異步等日志實際操作,但通過這個中間層,可以靈活對接自己選擇的日志框架,和分流功能。然后通過zap/logrus/seelog/zerolog等模塊組合實現日志的存儲.

    不管是go的原生error還是log,都是一個非常棒的設計,很多時候可以解決最基礎的需求,這避免了很多時候因為不需要而避免的額外性能消耗。而還有一部份場景,只是簡單往往不足以解決問題,這導致簡單和復雜的沖突哲學層面的取舍。我們在選擇時,應該清楚了解自己的需求是什么,按照自己的需要去選擇合適的模塊,即可!

6、參考資料

midlog源碼

midlog-example源碼

zap源碼

logrus源碼

zap高性能日志

謝謝

來源: https://blog.csdn.net/obobby1/article/details/103316359


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM