每一个编程语言都有不少日志库,由于记录日志在每一个项目中都是必须的。前面咱们介绍了标准日志库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】,共同窗习,一块儿进步~