1.介绍
Zap
是uber
开源的日志库,支持日志级别分级 、结构化记录,对性能和内存分配做了极致的优化。目前 Star 12.8 源码地址: https://github.com/uber-go/zap
官方性能测试图
2.安装
1
| go get -u go.uber.org/zap
|
3.日志记录器
Zap
提供了两种类型的日志记录器: SugaredLogger
和Logger
,两者对比如下:
SugaredLogger
: 在性能很好但不是很关键的上下文中使用,它比其他结构化日志记录包快4-10倍,并且支持结构化和printf
风格的日志记录。与 log15
和 go-kit
一样,SugaredLogger
的结构化日志 api
类型灵活,并接受可变的键值对的数量。
Logger
: 在每一微秒和每一次内存分配都很重要的上下文中,使用Logger
。它甚至比SugaredLogger
更快,内存分配次数也更少,但它只支持强类型的结构化日志记录。
4.创建Logger
4.1 创建Logger几种方式
在Zap
中通过调用zap.NewProduction()
、zap.NewDevelopment()
或者zap.Example()
可创建一个Logger
。
他们创建的Logger
,唯一的区别在于它将记录的信息不同。
使用场景如下:
zap.NewProduction()
: 在生产环境中使用
zap.NewDevelopment()
: 在开发环境中使用
zap.Example()
: 适合用在测试代码中
4.2 使用示例
a.代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| func TestCreateLogger(t *testing.T) { logger := zap.NewExample() defer logger.Sync() logger.Info("NewExample", zap.String("name","张三"), zap.Int("age",18), ) productionLogger, _ := zap.NewProduction() defer productionLogger.Sync() productionLogger.Info("NewProduction", zap.String("name","张三"), zap.Int("age",18), ) devLogger, _ := zap.NewDevelopment() defer devLogger.Sync() devLogger.Info("NewDevelopment", zap.String("name","张三"), zap.Int("age",18), ) }
|
b. 输出
1 2 3 4 5 6
| === RUN TestCreateLogger {"level":"info","msg":"NewExample","name":"张三","age":18} {"level":"info","ts":1624005421.7035909,"caller":"test/zap_test.go:25","msg":"NewProduction","name":"张三","age":18} 2021-06-18T16:37:01.703+0800 INFO test/zap_test.go:31 NewDevelopment {"name": "张三", "age": 18} --- PASS: TestCreateLogger (0.00s) PASS
|
zap
底层 API 可以设置缓存,所以一般使用defer logger.Sync()
将缓存同步到文件中
4.3 总结
- 使用
NewProduction()
记录日志,默认会记录调用函数信息、日期和时间。
NewExample
和NewProduction()
默认都是使用json
格式记录日志,而NewDevelopment
不是。
- 默认情况下日志都会打印到应用程序的控制台界面。
- 记录日志时,尽量调用
zap.T(key,val)
对应的类型方法,这也是zap
高性能原因的一部分。
5.记录日志
5.1 使用默认记录器(Logger
)
a.代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| func TestRecordLogWithDefault(t *testing.T) { logger := zap.NewExample() defer logger.Sync() logger.Debug("这是debug日志") logger.Debug("这是debug日志",zap.String("name","张三")) logger.Info("这是info日志",zap.Int("age",18)) logger.Error("这是error日志",zap.Int("line",130),zap.Error(fmt.Errorf("错误示例"))) logger.Warn("这是Warn日志") }
|
b.输出
1 2 3 4 5 6 7
| === RUN TestRecordLogWithDefault {"level":"debug","msg":"这是debug日志"} {"level":"debug","msg":"这是debug日志","name":"张三"} {"level":"info","msg":"这是info日志","age":18} {"level":"error","msg":"这是error日志","line":130,"error":"错误示例"} {"level":"warn","msg":"这是Warn日志"} --- PASS: TestRecordLogWithDefault (0.00s)
|
5.2 使用默认记录器(Sugar
)
a.代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| func TestRecordLogWithSuage(t *testing.T) { logger := zap.NewExample() sugarLogger := logger.Sugar() defer sugarLogger.Sync() sugarLogger.Debug("这是debug日志 ",zap.String("name","张三")) sugarLogger.Debugf("这是Debugf日志 name:%s ","张三") sugarLogger.Info("这是info日志",zap.Int("age",18)) sugarLogger.Infof("这是Infof日志 内容:%v",map[string]string{"爱好":"动漫"}) sugarLogger.Error("这是error日志",zap.Int("line",130),zap.Error(fmt.Errorf("错误示例"))) sugarLogger.Errorf("这是Errorf日志,错误信息:%s","错误报告!") sugarLogger.Warn("这是Warn日志") sugarLogger.Warnf("这是Warnf日志 %v",[]int{1,2,4,5}) }
|
b.输出
1 2 3 4 5 6 7 8 9 10 11
| === RUN TestRecordLogWithSuage {"level":"debug","msg":"这是debug日志 {name 15 0 张三 <nil>}"} {"level":"debug","msg":"这是Debugf日志 name:张三 "} {"level":"info","msg":"这是info日志{age 11 18 <nil>}"} {"level":"info","msg":"这是Infof日志 内容:map[爱好:动漫]"} {"level":"error","msg":"这是error日志{line 11 130 <nil>} {error 26 0 错误示例}"} {"level":"error","msg":"这是Errorf日志,错误信息:错误报告!"} {"level":"warn","msg":"这是Warn日志"} {"level":"warn","msg":"这是Warnf日志 [1 2 4 5]"} --- PASS: TestRecordLogWithSuage (0.00s) PASS
|
6.定制Logger
除了zap.NewProduction()
、zap.NewDevelopment()
、zap.Example()
还可以通过zap.New(...)
创建一个Logger
。
6.1 定制一: 输出到文件
a.代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| func Test2File(t *testing.T) { fileHandle, _ := os.Create("./test.log") writeFile := zapcore.AddSync(fileHandle) encoder := zapcore.NewJSONEncoder(zap.NewDevelopmentEncoderConfig()) zcore := zapcore.NewCore(encoder, zapcore.Lock(writeFile), zap.DebugLevel) logger := zap.New(zcore) defer logger.Sync() logger.Info("输出日志到文件", zap.String("name", "张三")) }
|
6.2 定制二: 同时输入文件和控制台
a.代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| func TestPrintFileAndStd(t *testing.T) { fileHandle, _ := os.Create("./test.log") writeFile := zapcore.NewMultiWriteSyncer(fileHandle,os.Stdout) encoder := zapcore.NewJSONEncoder(zap.NewDevelopmentEncoderConfig()) zcore := zapcore.NewCore(encoder, zapcore.Lock(writeFile), zap.DebugLevel) logger := zap.New(zcore) defer logger.Sync() logger.Info("输出日志到文件", zap.String("name", "张三")) }
|
7.切割日志
Zap
本身不支持文件切割和日志归档,好在开源强大,贡献出Lumberjack,它是一个Go
包,用于将日志写入滚动文件。
7.1 安装Lumberjack
1
| go get -u github.com/natefinch/lumberjack
|
7.2 集成到Zap
a. 代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| func getLumberjackConfig() zapcore.WriteSyncer { lumberjackLogger := &lumberjack.Logger{ Filename: "./zap.log", MaxSize: 1, MaxBackups: 3, MaxAge: 1, Compress: false, } return zapcore.AddSync(lumberjackLogger) }
func TestCutAndArchive(t *testing.T) { encoder := zapcore.NewJSONEncoder(zap.NewDevelopmentEncoderConfig()) core := zapcore.NewCore(encoder, getLumberjackConfig(), zap.DebugLevel) sugarLogger := zap.New(core).Sugar() defer sugarLogger.Sync() sugarLogger.Infof("日志内容:%s",strings.Repeat("日志",90000)) }
|
b. 效果
在代码中设置单文件最大容量为1MB,如上图所示当文件超过1MB(527+527 > 1024)
时,则分割。