zap 日志库的使用
标准包的 log 包使用虽然方便,但支持的功能也简单。本文介绍 UBer 开源的日志库 zap。
# 1. 使用 Gin 构建一个 Web 应用
我们仅使用 gin 框架构建一个简单的 Web 应用:
func main() {
r := gin.Default()
r.GET("/ping", ping)
r.Run()
}
func ping(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
}
2
3
4
5
6
7
8
9
10
11
阅读上面这段代码,访问 http://127.0.0.1:8080/ping
,返回结果是 {"message":"pong"}
。
然后,我们使用 zap
记录 ping
函数的请求日志。
# 2. Gin 框架使用 zap 日志库
Zap 支持两种模式,分别是 SugaredLogger
和 Logger
,其中 SugaredLogger
模式比 Logger
模式执行速度更快。
# 2.1 SugaredLogger 模式
使用 Zap 日志库,首先需要使用 New
函数创建一个 Logger
,代码如下:
func New(core zapcore.Core, options ...Option) *Logger
使用 New
函数,接收一个 zapcore.Core
类型的参数和一个 Option
类型的可选参数,返回一个 *Logger
。
其中 zap.Core
类型的参数,可以使用 NewCore
函数创建,接收三个参数,分别是 zapcore.Encoder
类型,zapcore.WriteSyncer
类型和 zapcore.LevelEnabler
类型,分别用于指定日志格式、日志路径和日志级别。
func NewCore(enc Encoder, ws WriteSyncer, enab LevelEnabler) Core
其中 zapcore.Encoder
类型的参数,可以使用 NewProductionEncoderConfig
函数创建,返回一个用于生产环境的固定日志编码配置。
// NewProductionEncoderConfig returns an opinionated EncoderConfig for
// production environments.
func NewProductionEncoderConfig() zapcore.EncoderConfig {
return 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.EpochTimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
我们可以修改任意配置选项的值。
其中 zapcore.WriteSyncer
类型的参数,可以使用 AddSync
函数创建,该函数接收一个 io.Writer
类型的参数。
func AddSync(w io.Writer) WriteSyncer
其中 zapcore.LevelEnabler
类型的参数,可以使用 zapcore
包定义的常量 zapcore.DebugLevel
,该常量是 zapcore.Level
类型,并且 zapcore.Level
类型实现了 zapcore.LevelEnabler
接口。
完整代码:
var sugaredLogger *zap.SugaredLogger
func main() {
InitLogger()
defer sugaredLogger.Sync()
r := gin.Default()
r.GET("/ping", ping)
r.Run()
}
func ping(c *gin.Context) {
sugaredLogger.Debug("call func ping")
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
}
func InitLogger() {
core := zapcore.NewCore(enc(), ws(), enab())
logger := zap.New(core)
sugaredLogger = logger.Sugar()
}
func enc() zapcore.Encoder {
cfg := zap.NewProductionEncoderConfig()
cfg.TimeKey = "time"
cfg.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05")
return zapcore.NewJSONEncoder(cfg)
}
func ws() zapcore.WriteSyncer {
logFileName := fmt.Sprintf("./%v.log", time.Now().Format("2006-01-02"))
logFile, err := os.Create(logFileName)
if err != nil {
log.Fatal(err)
}
return zapcore.AddSync(logFile)
}
func enab() zapcore.LevelEnabler {
return zapcore.DebugLevel
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
运行程序,执行 curl http://127.0.0.1:8080/ping
。
可以看到,生成的日志文件 xxx.log
,文件中是 json
格式的日志内容,我们可以根据实际需求修改为其他格式。
开发中,可能我们希望日志可以同时输出到日志文件和终端中,可以使用函数 NewMultiWriteSyncer
,代码如下:
func wsV2() zapcore.WriteSyncer {
return zapcore.NewMultiWriteSyncer(ws(), zapcore.AddSync(os.Stdout))
}
2
3
除了使用
zap.New()
创建Logger
之外,Zap 还提供了开箱即用的三种创建Logger
的方式,分别是函数NewProduction
,NewDevelopment
和Example()
,感兴趣的读者朋友们,可以试用一下。
# 2.2 Logger 模式
接下来,我们简单介绍一下 Logger
模式,它主要用于性能和类型安全比较重要的场景中,但是,它没有 SugaredLogger
模式简单易用,我们可以根据实际场景选择使用哪种模式。
我们修改一下现有代码,新创建 InitLoggerV2
函数,其中 enc
,ws
和 enab
函数的代码与 SugaredLogger
模式保持一致。
var loggerV2 *zap.Logger
func main() {
InitLoggerV2()
defer loggerV2.Sync()
r := gin.Default()
r.GET("/ping", ping)
r.Run()
}
func ping(c *gin.Context) {
loggerV2.Debug("call func ping", zap.Int("code", 200))
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
}
func InitLoggerV2() {
core := zapcore.NewCore(enc(), ws(), enab())
loggerV2 = zap.New(core)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
阅读上面这段代码,我们可以发现,在使用 zap
记录日志时,我们需要显示指定数据类型,一般用于性能和类型安全比较重要的场景中。
# 3. zap 日志库使用 lumberjack 库进行日志切割
Zap 日志库也不支持日志切割的功能,我们可以使用 lumberjack
日志切割库进行日志切割,关于 lumberjack
库的使用方式,我们在之前的文章介绍过,此处不再重复介绍,直接上代码:
func wsV3() zapcore.WriteSyncer {
logFileName := fmt.Sprintf("./%v.log", time.Now().Format("2006-01-02"))
lumberjackLogger := &lumberjack.Logger{
Filename: logFileName,
MaxSize: 1,
MaxBackups: 3,
MaxAge: 28,
Compress: false,
}
return zapcore.AddSync(lumberjackLogger)
}
2
3
4
5
6
7
8
9
10
11
lumberjack.Logger
的字段含义:
- Filename 日志保存文件路径
- MaxSize 日志文件大小,单位是
MB
- MaxBackups 保留的日志文件数量
- MaxAge 日志文件的最长保留时间,单位是天
- Compress 日志文件是否需要压缩
# 4. 总结
本文我们通过在 Gin 构建的应用中,使用 Zap 记录请求日志,介绍了 Zap 的使用方式,最后还通过 lumberjack
日志切割库进行切割日志。