本次我们从官方例程的角度出发,来分析学习如何让zap日志框架动起来
一、 Zap官方例程
1、加糖版
logger, _ := zap.NewProduction()
defer logger.Sync() // flushes buffer, if any
sugar := logger.Sugar()
sugar.Infow("failed to fetch URL",
// Structured context as loosely typed key-value pairs.
"url", url,
"attempt", 3,
"backoff", time.Second,
)
sugar.Infof("Failed to fetch URL: %s", url)
2、正常高性能版
logger, _ := zap.NewProduction()
defer logger.Sync()
logger.Info("failed to fetch URL",
// Structured context as strongly typed Field values.
zap.String("url", url),
zap.Int("attempt", 3),
zap.Duration("backoff", time.Second),
)
官方例程中总共列举出了两种打log的方法,sugar
和logger
,这两种的区别是logger的性能优于sugar,但是sugar提供了格式化输出sugar.Infof("Failed to fetch URL: %s", url)
,而logger不支持,所以根据自己的需要,来选择两种不同的log方式。
1、新建一个logger对象
logger, _ := zap.NewProduction()
通过zap.newxxx的方法来生成一个logger对象,我们在编程时可以看到,zap总共提供了三种默认的模式和一种自定义模式
//1、生产模式
func NewProduction(options ...Option) (*Logger, error)
//2、开发模式
func NewDevelopment(options ...Option) (*Logger, error)
//3、例子
func NewExample(options ...Option) *Logger
//4、自定义模式
func New(core zapcore.Core, options ...Option) *Logger
四种模式对比分析
1&2&3
因为1、2、3只有配置不同,其他都相同,所以合并分析
在源码中我们看到两种模式都调用了NewxxxConfig()的函数。
进入两个函数可以看到都返回了一个EncoderConfig结构体,3中的example方法其实也是一样的,把我们定义的Encoder返回回来。然后程序根据这些参数运行build函数
查看build函数,可以看到程序把上面的参数,以及另外两个sink和level带入了一个zapcore.Newcore
的函数,根据函数名推断,应该是把这些配置写入zap的core代码中。
func NewCore(enc Encoder, ws WriteSyncer, enab LevelEnabler) Core
对于NewCore函数来说,现在第一次参数已经有了,我们需要设置第二个和第三个参数,第二个参数的意思就是写的方法,这个地方要和lumberjack日志归档挂钩,这里可以先只填入日志的保存地址,归档问题后面讨论。
第三个参数就是日志等级。
这样就实现了一个使用配置的logger对象了
4、自定义日志输出格式
根据上面的分析我们可以看出来,4与1、2、3相比较只是多了三步
- encoderConfig的配置(日志的输出格式)
- writerSyncer(日志的输出地址)
- level(日志的输出等级)
所以我们模仿可以写出下列代码
func SetupZapLogger() *zap.Logger {
encoder := getEncoder()
writeSyncer := getWriteSyncer()
return zap.New(zapcore.NewCore(encoder,writeSyncer,zapcore.DebugLevel))
}
func getEncoder() zapcore.Encoder {
encoderConfig := zapcore.EncoderConfig{
TimeKey: "ts",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
FunctionKey: zapcore.OmitKey,
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.FullCallerEncoder,
EncodeName: zapcore.FullNameEncoder,
}
return zapcore.NewConsoleEncoder(encoderConfig)
}
func getWriteSyncer() zapcore.WriteSyncer {
file, _ := os.Create("./zapLog.log")
return zapcore.AddSync(file)
}
上述例子中我们使用ConsoleEncoder
来生成了一个我们自定义的模板类型,这样自定义参数中的zapcore.Core
参数就配置完成了。
将zapcore.Core的返回值带入zap.New就可以生成我们自定义的日志了
二、lumberjack日志归档库的分析使用
刚才在前面讲过,在配置writerSyncer
的时候我们只添加了一个日志输出地址,但是如果要实现日志的归档分割的话,还需要使用lumberjack库。而lumberjack的配置需要添加到writerSyncer
中
hook := lumberjack.Logger{
Filename: "./zapLog" +time.Now().Format(timeLayout) + ".log",
MaxSize: 1,//日志最大的大小(M)
MaxBackups: 2,//备份个数
MaxAge: 7,//最大保存天数(day)
Compress: true,//是否压缩
LocalTime: false,
}
return zapcore.NewMultiWriteSyncer(
zapcore.AddSync(os.Stdout), //日志同时输出到控制台
zapcore.AddSync(&hook)//配置的hook
)
对于lumberjack的配置我们可以把它当做一个hook函数,我们把对应的配置填入其中,最后使用zapcore.NewMultiWriteSyncer
将多个配置融合到一起返回即可。