每一個編程語言都有不少日誌庫,由於記錄日誌在每一個項目中都是必須的。前面咱們介紹了標準日誌庫log
、好用的logrus
和上一篇文章中介紹的由 uber 開源的高性能日誌庫zap
。zerolog
相比zap
更進了一步,它的 API 設計很是注重開發體驗和性能。zerolog
只專一於記錄 JSON 格式的日誌,號稱 0 內存分配!git
先安裝:github
$ go get github.com/rs/zerolog/log
後使用:golang
package main import "github.com/rs/zerolog/log" func main() { log.Print("hello world") }
常規使用與標準庫log
很是類似,只不過輸出的是 JSON 格式的日誌:編程
{"level":"debug","time":"2020-04-25T13:43:08+08:00","message":"hello world"}
咱們能夠在日誌中添加額外的字段信息,有助於調試和問題追蹤。與zap
同樣,zerolog
也區分字段類型,不一樣的是zerolog
採用鏈式調用的方式:微信
func main() { log.Debug(). Str("Scale", "833 cents"). Float64("Interval", 833.09). Msg("Fibonacci is everywhere") log.Debug(). Str("Name", "Tom"). Send() }
調用Msg()
或Send()
以後,日誌會被輸出:app
{"level":"debug","Scale":"833 cents","Interval":833.09,"time":"2020-04-25T13:55:44+08:00","message":"Fibonacci is everywhere"} {"level":"debug","Name":"Tom","time":"2020-04-25T13:55:44+08:00"}
記錄的字段能夠任意嵌套,這經過Dict()
來實現:編程語言
func main() { log.Info(). Dict("dict", zerolog.Dict(). Str("bar", "baz"). Int("n", 1), ).Msg("hello world") }
輸出中dict
字段爲嵌套結構:函數
{"level":"info","dict":{"bar":"baz","n":1},"time":"2020-04-25T14:34:51+08:00","message":"hello world"}
Logger
上面咱們使用log.Debug()
、log.Info()
調用的是全局的Logger
。全局的Logger
使用比較簡單,不須要額外建立。性能
每一個日誌庫都有日誌級別的概念,並且劃分基本上都差很少。zerolog
有panic/fatal/error/warn/info/debug/trace
這幾種級別。咱們能夠調用SetGlobalLevel()
設置全局Logger
的日誌級別。學習
func main() { debug := flag.Bool("debug", false, "sets log level to debug") flag.Parse() if *debug { zerolog.SetGlobalLevel(zerolog.DebugLevel) } else { zerolog.SetGlobalLevel(zerolog.InfoLevel) } log.Debug().Msg("This message appears only when log level set to debug") log.Info().Msg("This message appears when log level set to debug or info") if e := log.Debug(); e.Enabled() { e.Str("foo", "bar").Msg("some debug message") } }
在上面代碼中,咱們根據傳入的命令行選項設置日誌級別是Debug
仍是Info
。若是日誌級別爲Info
,Debug
的日誌是不會輸出的。也能夠調用Enabled()
方法來判斷日誌是否須要輸出,須要時再調用相應方法輸出,節省了添加字段和日誌信息的開銷:
if e := log.Debug(); e.Enabled() { e.Str("foo", "bar").Msg("some debug message") }
先不加命令行參數運行,默認爲Info
級別,Debug
日誌不會輸出:
$ go run main.go {"level":"info","time":"2020-04-25T14:13:34+08:00","message":"This message appears when log level set to debug or info"}
加上-debug
選項,Debug
和Info
日誌都輸出了:
$ go run main.go -debug {"level":"debug","time":"2020-04-25T14:18:19+08:00","message":"This message appears only when log level set to debug"} {"level":"info","time":"2020-04-25T14:18:19+08:00","message":"This message appears when log level set to debug or info"} {"level":"debug","foo":"bar","time":"2020-04-25T14:18:19+08:00","message":"some debug message"}
有時候咱們不想輸出日誌級別(即level
字段),這時可使用log.Log()
方法。有時,咱們沒有日誌信息可輸出,這時傳一個空字符串給Msg()
方法:
func main() { log.Log(). Str("foo", "bar"). Msg("") }
運行:
{"foo":"bar","time":"2020-04-25T14:19:48+08:00"}
Logger
上面咱們使用的都是全局的Logger
,這種方式有一個明顯的缺點:若是在某個地方修改了設置,將影響全局的日誌記錄。爲了消除這種影響,咱們須要建立新的Logger
:
func main() { logger := zerolog.New(os.Stderr) logger.Info().Str("foo", "bar").Msg("hello world") }
調用zerlog.New()
傳入一個io.Writer
做爲日誌寫入器便可。
Logger
基於當前的Logger
能夠建立一個子Logger
,子Logger
能夠在父Logger
上附加一些額外的字段。調用logger.With()
建立一個上下文,而後爲它添加字段,最後調用Logger()
返回一個新的Logger
:
func main() { logger := zerolog.New(os.Stderr) sublogger := logger.With(). Str("foo", "bar"). Logger() sublogger.Info().Msg("hello world") }
sublogger
會額外輸出"foo": "bar"
這個字段。
zerolog
提供了多種選項定製輸入日誌的行爲。
zerolog
提供了一個ConsoleWriter
可輸出便於咱們閱讀的,帶顏色的日誌。調用zerolog.Output()
來啓用ConsoleWriter
:
func main() { logger := log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) logger.Info().Str("foo", "bar").Msg("hello world") }
輸出:
咱們還能進一步對ConsoleWriter
進行配置,定製輸出的級別、信息、字段名、字段值的格式:
func main() { output := zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339} output.FormatLevel = func(i interface{}) string { return strings.ToUpper(fmt.Sprintf("| %-6s|", i)) } output.FormatMessage = func(i interface{}) string { return fmt.Sprintf("***%s****", i) } output.FormatFieldName = func(i interface{}) string { return fmt.Sprintf("%s:", i) } output.FormatFieldValue = func(i interface{}) string { return strings.ToUpper(fmt.Sprintf("%s", i)) } logger := log.Output(output).With().Timestamp().Logger() logger.Info().Str("foo", "bar").Msg("hello world") }
實際上就是對級別、信息、字段名和字段值設置鉤子,輸出前通過鉤子函數轉換一次:
ConsoleWriter
的性能不夠理想,建議只在開發環境中使用!
輸出的日誌中級別默認的字段名爲level
,信息默認爲message
,時間默認爲time
。能夠經過zerolog
中LevelFieldName/MessageFieldName/TimestampFieldName
來設置:
func main() { zerolog.TimestampFieldName = "t" zerolog.LevelFieldName = "l" zerolog.MessageFieldName = "m" logger := zerolog.New(os.Stderr).With().Timestamp().Logger() logger.Info().Msg("hello world") }
輸出:
{"l":"info","t":"2020-04-25T14:53:08+08:00","m":"hello world"}
注意,這個設置是全局的!!!
有時咱們須要輸出文件名和行號,以便能很快定位代碼位置,方便找出問題。這能夠經過在建立子Logger
時帶入Caller()
選項完成:
func main() { logger := zerolog.New(os.Stderr).With().Caller().Logger() logger.Info().Msg("hello world") }
輸出:
{"level":"info","caller":"d:/code/golang/src/github.com/darjun/go-daily-lib/zerolog/setting/file-line/main.go:11","message":"hello world"}
有時候日誌太多了反而對咱們排查問題形成干擾,zerolog
支持日誌採樣的功能,能夠每隔多少條日誌輸出一次,其餘日誌丟棄:
func main() { sampled := log.Sample(&zerolog.BasicSampler{N: 10}) for i := 0; i < 20; i++ { sampled.Info().Msg("will be logged every 10 message") } }
結果只輸出兩條:
{"level":"info","time":"2020-04-25T15:01:02+08:00","message":"will be logged every 10 message"} {"level":"info","time":"2020-04-25T15:01:02+08:00","message":"will be logged every 10 message"}
還有更高級的設置:
func main() { sampled := log.Sample(&zerolog.LevelSampler{ DebugSampler: &zerolog.BurstSampler{ Burst: 5, Period: time.Second, NextSampler: &zerolog.BasicSampler{N: 100}, }, }) sampled.Debug().Msg("hello world") }
上面代碼只採樣Debug
日誌,在 1s 內最多輸出 5 條日誌,超過 5條 時,每隔 100 條輸出一條。
zerolog
支持鉤子,咱們能夠針對不一樣的日誌級別添加一些額外的字段或進行其餘的操做:
type AddFieldHook struct { } func (AddFieldHook) Run(e *zerolog.Event, level zerolog.Level, msg string) { if level == zerolog.DebugLevel { e.Str("name", "dj") } } func main() { hooked := log.Hook(AddFieldHook{}) hooked.Debug().Msg("") }
若是是Debug
級別,額外輸出"name":"dj"
字段:
{"level":"debug","time":"2020-04-25T15:09:04+08:00","name":"dj"}
關於性能,GitHub 上有一份詳細的性能測試,與logrus/zap
等日誌庫的比較。感興趣能夠去看看:https://github.com/rs/zerolog#benchmarks。zerolog
的性能比zap
還要優秀!
正是由於有不少人不知足於現狀,才帶來了技術的進步和豐富多彩的開源世界!
你們若是發現好玩、好用的 Go 語言庫,歡迎到 Go 每日一庫 GitHub 上提交 issue😄
歡迎關注個人微信公衆號【GoUpUp】,共同窗習,一塊兒進步~