本文作者:陈进坚
个人博客:https://jian1098.github.io
CSDN博客:https://blog.csdn.net/c_jian
简书:https://www.jianshu.com/u/8ba9ac5706b6
联系方式:jian1098@qq.com
摘要
zap
是Uber
开发的非常快的、结构化的,分日志级别的Go日志库。根据Uber-go Zap的文档,它的性能比类似的结构化日志包更好,也比标准库更快。具体的性能测试可以去github
上看到。
github地址:https://github.com/uber-go/zap
创建实例
通过调用zap.NewProduction()
/zap.NewDevelopment()
或者zap.Example()
创建一个Logger。这三个方法的区别在于它将记录的信息不同,参数只能是string
类型
//代码
var log *zap.Logger
log = zap.NewExample()
log, _ := zap.NewDevelopment()
log, _ := zap.NewProduction()
log.Debug("This is a DEBUG message")
log.Info("This is an INFO message")
//Example 输出
{"level":"debug","msg":"This is a DEBUG message"}
{"level":"info","msg":"This is an INFO message"}
//Development 输出
2018-10-30T17:14:22.459+0800 DEBUG development/main.go:7 This is a DEBUG message
2018-10-30T17:14:22.459+0800 INFO development/main.go:8 This is an INFO message
//Production 输出
{"level":"info","ts":1540891173.3190675,"caller":"production/main.go:8","msg":"This is an INFO message"}
{"level":"info","ts":1540891173.3191047,"caller":"production/main.go:9","msg":"This is an INFO message with fields","region":["us-west"],"id":2}
三种创建方式对比:
-
Example
和Production
使用的是json
格式输出,Development
使用行的形式输出 -
Development
- 从警告级别向上打印到堆栈中来跟踪
- 始终打印包/文件/行(方法)
- 在行尾添加任何额外字段作为json字符串
- 以大写形式打印级别名称
- 以毫秒为单位打印ISO8601格式的时间戳
-
Production
- 调试级别消息不记录
- Error,Dpanic级别的记录,会在堆栈中跟踪文件,Warn不会
- 始终将调用者添加到文件中
- 以时间戳格式打印日期
- 以小写形式打印级别名称
格式化输出
zap有两种类型,分别是*zap.Logger
和*zap.SugaredLogger
,它们惟一的区别是,我们通过调用主logger的. Sugar()
方法来获取一个SugaredLogger
,然后使用SugaredLogger
以printf
格式记录语句,例如
var sugarLogger *zap.SugaredLogger
func InitLogger() {
logger, _ := zap.NewProduction()
sugarLogger = logger.Sugar()
}
func main() {
InitLogger()
defer sugarLogger.Sync()
sugarLogger.Errorf("Error fetching URL %s : Error = %s", url, err)
}
写入文件
默认情况下日志都会打印到应用程序的console界面,但是为了方便查询,可以将日志写入文件,但是我们不能再使用前面创建实例的3个方法,而是使用zap.New()
package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"os"
)
var log *zap.Logger
func main() {
writeSyncer, _ := os.Create("./info.log") //日志文件存放目录
encoderConfig := zap.NewProductionEncoderConfig() //指定时间格式
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
encoder := zapcore.NewConsoleEncoder(encoderConfig) //获取编码器,NewJSONEncoder()输出json格式,NewConsoleEncoder()输出普通文本格式
core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel) //第三个及之后的参数为写入文件的日志级别,ErrorLevel模式只记录error级别的日志
log = zap.New(core,zap.AddCaller()) //AddCaller()为显示文件名和行号
log.Info("hello world")
log.Error("hello world")
}
日志文件输出结果:
2020-12-16T17:53:30.466+0800 INFO geth/main.go:18 hello world
2020-12-16T17:53:30.486+0800 ERROR geth/main.go:19 hello world
同时输出控制台和文件
如果需要同时输出控制台和文件,只需要改造一下zapcore.NewCore
即可,示例:
package main
import (
"github.com/natefinch/lumberjack"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"os"
)
var log *zap.Logger
func main() {
//获取编码器,NewJSONEncoder()输出json格式,NewConsoleEncoder()输出普通文本格式
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder //指定时间格式
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
encoder := zapcore.NewConsoleEncoder(encoderConfig)
//文件writeSyncer
fileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{
Filename: "./info.log", //日志文件存放目录
MaxSize: 1, //文件大小限制,单位MB
MaxBackups: 5, //最大保留日志文件数量
MaxAge: 30, //日志文件保留天数
Compress: false, //是否压缩处理
})
fileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(fileWriteSyncer,zapcore.AddSync(os.Stdout)), zapcore.DebugLevel) //第三个及之后的参数为写入文件的日志级别,ErrorLevel模式只记录error级别的日志
log = zap.New(fileCore, zap.AddCaller()) //AddCaller()为显示文件名和行号
log.Info("hello world")
log.Error("hello world")
}
文件切割
日志文件会随时间越来越大,为了避免日志文件把硬盘空间占满,需要按条件对日志文件进行切割,zap包本身不提供文件切割的功能,但是可以用zap官方推荐的lumberjack
包处理
//文件writeSyncer
fileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{
Filename: "./info.log", //日志文件存放目录,如果文件夹不存在会自动创建
MaxSize: 1, //文件大小限制,单位MB
MaxBackups: 5, //最大保留日志文件数量
MaxAge: 30, //日志文件保留天数
Compress: false, //是否压缩处理
})
按级别写入文件
为了管理人员的查询方便,一般我们需要将低于error级别的放到info.log,error及以上严重级别日志存放到error.log文件中,我们只需要改造一下zapcore.NewCore
方法的第3个参数,然后将文件WriteSyncer
拆成info
和error
两个即可,示例:
package main
import (
"github.com/natefinch/lumberjack"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"os"
)
var log *zap.Logger
func main() {
var coreArr []zapcore.Core
//获取编码器
encoderConfig := zap.NewProductionEncoderConfig() //NewJSONEncoder()输出json格式,NewConsoleEncoder()输出普通文本格式
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder //指定时间格式
encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder //按级别显示不同颜色,不需要的话取值zapcore.CapitalLevelEncoder就可以了
//encoderConfig.EncodeCaller = zapcore.FullCallerEncoder //显示完整文件路径
encoder := zapcore.NewConsoleEncoder(encoderConfig)
//日志级别
highPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool{ //error级别
return lev >= zap.ErrorLevel
})
lowPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool { //info和debug级别,debug级别是最低的
return lev < zap.ErrorLevel && lev >= zap.DebugLevel
})
//info文件writeSyncer
infoFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{
Filename: "./log/info.log", //日志文件存放目录,如果文件夹不存在会自动创建
MaxSize: 1, //文件大小限制,单位MB
MaxBackups: 5, //最大保留日志文件数量
MaxAge: 30, //日志文件保留天数
Compress: false, //是否压缩处理
})
infoFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(infoFileWriteSyncer,zapcore.AddSync(os.Stdout)), lowPriority) //第三个及之后的参数为写入文件的日志级别,ErrorLevel模式只记录error级别的日志
//error文件writeSyncer
errorFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{
Filename: "./log/error.log", //日志文件存放目录
MaxSize: 1, //文件大小限制,单位MB
MaxBackups: 5, //最大保留日志文件数量
MaxAge: 30, //日志文件保留天数
Compress: false, //是否压缩处理
})
errorFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(errorFileWriteSyncer,zapcore.AddSync(os.Stdout)), highPriority) //第三个及之后的参数为写入文件的日志级别,ErrorLevel模式只记录error级别的日志
coreArr = append(coreArr, infoFileCore)
coreArr = append(coreArr, errorFileCore)
log = zap.New(zapcore.NewTee(coreArr...), zap.AddCaller()) //zap.AddCaller()为显示文件名和行号,可省略
log.Info("hello info")
log.Debug("hello debug")
log.Error("hello error")
}
这样修改之后,info
和debug
级别的日志就存放到info.log
,error
级别的日志单独放到error.log
文件中了
控制台按级别显示颜色
指定编码器的EncodeLevel
即可,
//获取编码器
encoderConfig := zap.NewProductionEncoderConfig() //NewJSONEncoder()输出json格式,NewConsoleEncoder()输出普通文本格式
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder //指定时间格式
encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder //按级别显示不同颜色,不需要的话取值zapcore.CapitalLevelEncoder就可以了
encoder := zapcore.NewConsoleEncoder(encoderConfig)
显示文件路径和行号
前面说到要显示文件路径和行号,只需要zap.New
方法添加参数zap.AddCaller()
即可,如果要显示完整的路径,需要在编码器配置中指定
//获取编码器
encoderConfig := zap.NewProductionEncoderConfig() //NewJSONEncoder()输出json格式,NewConsoleEncoder()输出普通文本格式
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder //指定时间格式
encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder //按级别显示不同颜色,不需要的话取值zapcore.CapitalLevelEncoder就可以了
encoderConfig.EncodeCaller = zapcore.FullCallerEncoder //显示完整文件路径
encoder := zapcore.NewConsoleEncoder(encoderConfig)
完整代码
package main
import (
"github.com/natefinch/lumberjack"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"os"
)
var log *zap.Logger
func main() {
var coreArr []zapcore.Core
//获取编码器
encoderConfig := zap.NewProductionEncoderConfig() //NewJSONEncoder()输出json格式,NewConsoleEncoder()输出普通文本格式
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder //指定时间格式
encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder //按级别显示不同颜色,不需要的话取值zapcore.CapitalLevelEncoder就可以了
//encoderConfig.EncodeCaller = zapcore.FullCallerEncoder //显示完整文件路径
encoder := zapcore.NewConsoleEncoder(encoderConfig)
//日志级别
highPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool{ //error级别
return lev >= zap.ErrorLevel
})
lowPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool { //info和debug级别,debug级别是最低的
return lev < zap.ErrorLevel && lev >= zap.DebugLevel
})
//info文件writeSyncer
infoFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{
Filename: "./log/info.log", //日志文件存放目录,如果文件夹不存在会自动创建
MaxSize: 2, //文件大小限制,单位MB
MaxBackups: 100, //最大保留日志文件数量
MaxAge: 30, //日志文件保留天数
Compress: false, //是否压缩处理
})
infoFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(infoFileWriteSyncer,zapcore.AddSync(os.Stdout)), lowPriority) //第三个及之后的参数为写入文件的日志级别,ErrorLevel模式只记录error级别的日志
//error文件writeSyncer
errorFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{
Filename: "./log/error.log", //日志文件存放目录
MaxSize: 1, //文件大小限制,单位MB
MaxBackups: 5, //最大保留日志文件数量
MaxAge: 30, //日志文件保留天数
Compress: false, //是否压缩处理
})
errorFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(errorFileWriteSyncer,zapcore.AddSync(os.Stdout)), highPriority) //第三个及之后的参数为写入文件的日志级别,ErrorLevel模式只记录error级别的日志
coreArr = append(coreArr, infoFileCore)
coreArr = append(coreArr, errorFileCore)
log = zap.New(zapcore.NewTee(coreArr...), zap.AddCaller()) //zap.AddCaller()为显示文件名和行号,可省略
log.Info("hello info")
log.Debug("hello debug")
log.Error("hello error")
}
参考文章
在Go语言项目中使用Zap日志库 - 知乎 (zhihu.com)
golang高性能日志库zap配置示例 - ExplorerMan - 博客园 (cnblogs.com)
有疑问加站长微信联系(非本文作者)