简介
在很早之前的文章中,我们介绍过 Go 标准日志库log
和结构化的日志库logrus
。在热点函数中记录日志对日志库的执行性能有较高的要求,不能影响正常逻辑的执行时间。uber
开源的日志库zap
,对性能和内存分配做了极致的优化。
快速使用
先安装:
$ go get go.uber.org/zap
后使用:
package main
import (
"time"
"go.uber.org/zap"
)
func main() {
logger := zap.NewExample()
defer logger.Sync()
url := "http://example.org/api"
logger.Info("failed to fetch URL",
zap.String("url", url),
zap.Int("attempt", 3),
zap.Duration("backoff", time.Second),
)
sugar := logger.Sugar()
sugar.Infow("failed to fetch URL",
"url", url,
"attempt", 3,
"backoff", time.Second,
)
sugar.Infof("Failed to fetch URL: %s", url)
}
zap
库的使用与其他的日志库非常相似。先创建一个logger
,然后调用各个级别的方法记录日志(Debug/Info/Error/Warn
)。zap
提供了几个快速创建logger
的方法,zap.NewExample()
、zap.NewDevelopment()
、zap.NewProduction()
,还有高度定制化的创建方法zap.New()
。创建前 3 个logger
时,zap
会使用一些预定义的设置,它们的使用场景也有所不同。Example
适合用在测试代码中,Development
在开发环境中使用,Production
用在生成环境。
zap
底层 API 可以设置缓存,所以一般使用defer logger.Sync()
将缓存同步到文件中。
由于fmt.Printf
之类的方法大量使用interface{}
和反射,会有不少性能损失,并且增加了内存分配的频次。zap
为了提高性能、减少内存分配次数,没有使用反射,而且默认的Logger
只支持强类型的、结构化的日志。必须使用zap
提供的方法记录字段。zap
为 Go 语言中所有的基本类型和其他常见类型都提供了方法。这些方法的名称也比较好记忆,zap.Type
(Type
为bool/int/uint/float64/complex64/time.Time/time.Duration/error
等)就表示该类型的字段,zap.Typep
以p
结尾表示该类型指针的字段,zap.Types
以s
结尾表示该类型切片的字段。如:
zap.Bool(key string, val bool) Field
:bool
字段zap.Boolp(key string, val *bool) Field
:bool
指针字段;zap.Bools(key string, val []bool) Field
:bool
切片字段。
当然也有一些特殊类型的字段:
zap.Any(key string, value interface{}) Field
:任意类型的字段;zap.Binary(key string, val []byte) Field
:二进制串的字段。
当然,每个字段都用方法包一层用起来比较繁琐。zap
也提供了便捷的方法SugarLogger
,可以使用printf
格式符的方式。调用logger.Sugar()
即可创建SugaredLogger
。SugaredLogger
的使用比Logger
简单,只是性能比Logger
低 50% 左右,可以用在非热点函数中。调用SugarLogger
以f
结尾的方法与fmt.Printf
没什么区别,如例子中的Infof
。同时SugarLogger
还支持以w
结尾的方法,这种方式不需要先创建字段对象,直接将字段名和值依次放在参数中即可,如例子中的Infow
。
默认情况下,Example
输出的日志为 JSON 格式:
{
"level":"info","msg":"failed to fetch URL","url":"http://example.org/api","attempt":3,"backoff":"1s"}
{
"level":"info","msg":"failed to fetch URL","url":"http://example.org/api","attempt":3,"backoff":"1s"}
{
"level":"info","msg":"Failed to fetch