zap 是 Uber 开源的 go语言的日志库,它的优势在于实时写结构化日志(Structured Logging)到文件有很好的性能。结构化日志就是说相比于直接输出日志文本,使用 json 或者其它编码方式使日志结构化,这样可以方便后续用各种工具分析处理和查找,比如用 ELK(Elasticsearch, Logstash and Kibana)。根据 zap 自己的基准库测试结果,它比其它结构化日志的库(比如我之前使用的 logrus )要有更好的性能。接下来主要介绍一下 zap 库的使用方法。
注:下文将忽略引用库的代码:
import "go.uber.org/zap"
全局的 logger
zap 的基础用法是创建一个 logger 实例,然后在所有要用它的地方将它作为参数传过去用:
logger, _ := zap.NewProduction()
defer logger.Sync() // 将 buffer 中的日志写到文件中
logger.Info("this is a test log")
zap.S()zap.L()ReplaceGlobals
logger := zap.NewExample()
defer logger.Sync()
undo := zap.ReplaceGlobals(logger)
defer undo()
zap.L().Info("replaced zap's global loggers")
Sugar 的作用
zap.Stringzap.Int
logger, _ := zap.NewDevelopment() // 忽略了错误
logger.Info("this is a test log", zap.String("name", "x"), zap.Int("age", 20))
但它提供了 Sugar(语法糖的糖),只要一点点额外的性能损失(但是仍比大部分库快),可以比较简单地格式化输出。
sugar := logger.Sugar()
sugar.Infof("name is %s", "x") // 格式化输出
sugar.Infow("this is a test log", "name", "x", "age", 20) // 第二个开始每一对是一个键值
// 使用全局的 SugaredLogger
name := "x"
zap.S().Info("this is a test log: name=", name) // 用法相当于 fmt.Print
定制 zap 提供了可配置的 logger,配置一个 logger 时至少需要以下设置:
logger, _ := zap.Config{
Encoding: "json", // 配置编码方式(json 或 console)
Level: zap.NewAtomicLevelAt(zapcore.DebugLevel), // 输出级别
OutputPaths: []string{"stdout"}, // 输出目的地
}.Build()
其中 OutputPaths 可以用来设置希望日志输出到的文件路径。不过上面这样设置后,message 信息就不能打印出来:
logger.Info("something to log") // 只会打印 {}
logger.Sugar().Infow("test", "name", "xxx") // 也只打印 {"name": "xxx"}
EncoderConfigMessageKey
logger, _ := zap.Config{
Encoding: "json",
Level: zap.NewAtomicLevelAt(zapcore.DebugLevel),
OutputPaths: []string{"stdout"},
EncoderConfig: zapcore.EncoderConfig{
MessageKey: "message",
LevelKey: "level",
EncodeLevel: zapcore.CapitalLevelEncoder,// INFO
TimeKey: "time",
EncodeTime: zapcore.ISO8601TimeEncoder,
CallerKey: "caller",
EncodeCaller: zapcore.ShortCallerEncoder,
},
}.Build()
其中:
- 时间可以设置为 ISO 8601 format,或者 Unix时间戳(秒、毫秒、纳秒)
- level 可以设置为 capital 或者 lowercase,还可设置有颜色的,但是json方式输出时不会生效。
- caller 就是调用者,可以设置 short (package/file:line)或者 full( /full/path/to/package/file:line)。
另外,也可以使用 zap 提供的 ProductionEncoderConfig 等配置,进行定制:
cfg := zap.NewProductionEncoderConfig()
cfg.EncodeTime = zapcore.ISO8601TimeEncoder
file, _ := os.Create(LogPath)
core := zapcore.NewTee(
zapcore.NewCore(zapcore.NewJSONEncoder(cfg), zapcore.AddSync(file), zapcore.InfoLevel),
zapcore.NewCore(zapcore.NewConsoleEncoder(cfg)), zapcore.Lock(os.Stdout), zapcore.DebugLevel),
)
logger := zap.New(core)
defer logger.Sync()
Sync 的作用
logger.Sync()logger.Sync()
为什么性能比较好?
interface{}sync.Pool
总结一下,使用 zap 不需要我们付出多少额外的工作量,却可以得到比较明显的性能提升,因此如果你的项目需要输出结构化的日志到文件,不妨使用 zap。