Go zap 库
zap
Go 中快速、结构化、分级的日志记录。
在许多 Go 语言项目中,我们需要一个好的日志记录器能够提供下面这些功能:
- 能够将事件记录到文件中,而不是应用程序控制台。
- 日志切割 - 能够根据文件大小、时间或间隔等来切割日志文件。
- 支持不同的日志级别。例如 INFO,DEBUG,ERROR 等。
- 能够打印基本信息,如调用文件 / 函数名和行号,日志时间等。
快速开始
安装
|
|
默认的 Go Logger
在介绍 Uber-go 的 zap 包之前,让我们先看看 Go 语言提供的基本日志功能。
Go 语言提供的默认日志包是 golang.org/pkg/log/
实现一个 Go 语言中的日志记录器非常简单:创建一个新的日志文件,然后设置它为日志的输出位置
|
|
当我们执行上面的代码,我们能看到一个 test.log
文件被创建,下面的内容会被添加到这个日志文件中
|
|
优势
- 它最大的优点是使用非常简单。我们可以设置任何 io.Writer 作为日志记录输出并向其发送要写入的日志。
劣势
- 仅限基本的日志级别
- 只有一个 Print 选项。不支持 INFO/DEBUG 等多个级别。
- 对于错误日志,它有 Fatal 和 Panic
- Fatal 日志通过调用 os.Exit (1) 来结束程序
- Panic 日志在写入日志消息之后抛出一个 panic
- 但是它缺少一个 ERROR 日志级别,这个级别可以在不抛出 panic 或退出程序的情况下而记录错误 缺乏日志格式化的能力,例如记录调用者的函数名和行号,格式化日期和时间格式。等等。
- 不提供日志切割的能力。
示例
In contexts where performance is nice, but not critical, use the SugaredLogger
. It’s 4-10x faster than other structured logging packages and includes both structured and printf
-style APIs.
|
|
When performance and type safety are critical, use the Logger
. It’s even faster than the SugaredLogger
and allocates far less, but it only supports structured logging.
|
|
创建 logger
Zap 提供了两种类型的日志记录器 — Sugared Logger
和 Logger
在性能很好但不是很关键的上下文中,使用 SugaredLogger:它比其他结构化日志记录包快 4-10 倍,并且支持结构化和 printf 风格的日志记录。
在每一微秒和每一次内存分配都很重要的上下文中,使用 Logger:它甚至比 SugaredLogger 更快,内存分配次数也更少,但它只支持强类型的结构化日志记录。
创建 Logger
通过调用 zap.NewProduction()
/ zap.NewDevelopment()
或者 zap.Example()
创建一个 Logger。
上面的每一个函数都将创建一个 logger。唯一的区别在于它将记录的信息不同。例如 production logger 默认记录调用函数信息、日期和时间等。
通过 Logger 调用 Info/Error 等。
默认情况下日志都会打印到应用程序的 console 界面。
|
|
测试执行输出结果:
|
|
创建 Sugared Logger
现在让我们使用 Sugared Logger 来实现相同的功能。
大部分的实现基本都相同,惟一的区别是,我们通过调用主 logger 的 Sugar()
方法来获取一个 SugaredLogger
。
然后使用 SugaredLogger
来 printf 格式记录语句
|
|
当你执行上面的代码会得到如下输出:
|
|
定制 logger
将日志写入文件而不是终端
我们要做的第一个更改是把日志写入文件,而不是打印到应用程序控制台。
我们将使用 zap.New()
方法来手动传递所有配置,而不是使用像 zap.NewProduction()
这样的预置方法来创建 logger
|
|
zapcore.Core 需要三个配置:
Encoder 编码器 (如何写入日志)
我们将使用开箱即用的
NewJSONEncoder()
,并使用预先设置的ProductionEncoderConfig()
1
zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
WriteSyncer
指定日志将写到哪里去我们使用
zapcore.AddSync()
函数并且将打开的文件句柄传进去1 2
file, _ := os.Create("./test.log") writeSyncer := zapcore.AddSync(file)
LogLevel
哪种级别的日志将被写入1 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
package main import ( "fmt" "go.uber.org/zap" "go.uber.org/zap/zapcore" "os" ) /* setJSONEncoder 设置logger编码 */ func setJSONEncoder() zapcore.Encoder { return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()) } /* setLoggerWrite 设置logger写入文件 */ func setLoggerWrite() zapcore.WriteSyncer { create, err := os.OpenFile("./test.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0755) if err != nil { fmt.Println(err) } return zapcore.AddSync(create) } func main() { var logger *zap.SugaredLogger core := zapcore.NewCore(setJSONEncoder(), setLoggerWrite(), zap.InfoLevel) logger = zap.New(core).Sugar() for i := 0; i < 10; i++ { logger.Infof("Info%d", i) logger.Errorf("Error%d", i) logger.Debugf("Debug%d", i) logger.Warnf("Warn%d", i) } }
上面代码执行后,发现当前目录多了一个 test.log,发现只写入了 Info,Error,Warn 级别的 log,Debug 级别没有写入
1 2 3 4 5 6 7 8 9
{"level":"info","ts":1635837845.908652,"msg":"Info0"} {"level":"error","ts":1635837845.908742,"msg":"Error0"} {"level":"warn","ts":1635837845.9087548,"msg":"Warn0"} {"level":"info","ts":1635837845.9087648,"msg":"Info1"} {"level":"error","ts":1635837845.908774,"msg":"Error1"} {"level":"warn","ts":1635837845.9087832,"msg":"Warn1"} {"level":"info","ts":1635837845.908791,"msg":"Info2"} {"level":"error","ts":1635837845.908799,"msg":"Error2"} ...
将 JSON Encoder 更改为普通的 Log Encoder
现在,我们希望将编码器从 JSON Encoder 更改为普通 Encoder。
为此,我们需要将 NewJSONEncoder()
更改为 NewConsoleEncoder()
|
|
当使用这些修改过的 logger 配置调用上述部分的 main() 函数时,以下输出将打印在文件 ——test.log 中。
|
|
更改编码(格式化时间,日志级别大写)
鉴于我们对配置所做的更改,有下面问题:
- 时间是以非人类可读的方式展示,例如 1.572161051846623e+09
我们要做的第一件事是覆盖默认的 ProductionConfig()
,并进行以下更改:
- 修改时间编码器
- 在日志文件中使用大写字母记录日志级别
|
|
修改编码配置后,重新执行后,test.log 的内容:可以看到时间也被调整了,日志级别的格式也都为大写
|
|
添加调用者详细信息
我们将修改 zap logger 代码,添加将调用函数信息记录到日志中的功能。为此,我们将在 zap.New()
函数中添加一个 Option:
|
|
执行后 test.log 的内容,加入了对应目录 / 文件 行数的日志信息
|
|
使用 Lumberjack 进行日志切割归档
Zap 本身不支持切割归档日志文件
为了添加日志切割归档功能,我们将使用第三方库 Lumberjack 来实现。
安装执行下面的命令安装 Lumberjack
|
|
要在 zap 中加入 Lumberjack 支持,我们需要修改 WriteSyncer
代码。我们将按照下面的代码修改 getLogWriter()
函数:
|
|