Go语言高性能日志库zap使用
Go语言高性能日志库zap使用
Zap简介
zap是一个非常快的、结构化的,分日志级别的Go日志库。
Github仓库地址:https://github.com/uber-go/zap
Zap性能
记录一条含有10个字段的日志
Package | Time | Time % to zap | Objects Allocated |
---|---|---|---|
⚡️ zap | 862 ns/op | +0% | 5 allocs/op |
⚡️ zap (sugared) | 1250 ns/op | +45% | 11 allocs/op |
zerolog | 4021 ns/op | +366% | 76 allocs/op |
go-kit | 4542 ns/op | +427% | 105 allocs/op |
apex/log | 26785 ns/op | +3007% | 115 allocs/op |
logrus | 29501 ns/op | +3322% | 125 allocs/op |
log15 | 29906 ns/op | +3369% | 122 allocs/op |
使用已有10个上下文字段的记录器记录消息
Package | Time | Time % to zap | Objects Allocated |
---|---|---|---|
⚡️ zap | 126 ns/op | +0% | 0 allocs/op |
⚡️ zap (sugared) | 187 ns/op | +48% | 2 allocs/op |
zerolog | 88 ns/op | -30% | 0 allocs/op |
go-kit | 5087 ns/op | +3937% | 103 allocs/op |
log15 | 18548 ns/op | +14621% | 73 allocs/op |
apex/log | 26012 ns/op | +20544% | 104 allocs/op |
logrus | 27236 ns/op | +21516% | 113 allocs/op |
记录一个静态字符串,没有任何上下文或printf
样式的模板
Package | Time | Time % to zap | Objects Allocated |
---|---|---|---|
⚡️ zap | 118 ns/op | +0% | 0 allocs/op |
⚡️ zap (sugared) | 191 ns/op | +62% | 2 allocs/op |
zerolog | 93 ns/op | -21% | 0 allocs/op |
go-kit | 280 ns/op | +137% | 11 allocs/op |
standard library | 499 ns/op | +323% | 2 allocs/op |
apex/log | 1990 ns/op | +1586% | 10 allocs/op |
logrus | 3129 ns/op | +2552% | 24 allocs/op |
log15 | 3887 ns/op | +3194% | 23 allocs/op |
Zap安装
1go get -u go.uber.org/zap
快速入门
配置Zap Logger
Zap提供了两种类型的日志记录器—SugaredLogger
和Logger
。
在性能很好但不是很关键的上下文中,使用SugaredLogger
。它比其他结构化日志记录包快4-10倍,并且支持结构化和printf风格的日志记录。
在每一微秒和每一次内存分配都很重要的上下文中,使用Logger
。它甚至比SugaredLogger
更快,内存分配次数也更少,但它只支持强类型的结构化日志记录。
Logger
通过调用zap.NewProduction()
、zap.NewDevelopment()
、`zap.Example()其中一个创建Logger,3中模式日志格式有所不同。默认情况下日志都输出在控制台下。
1package main
2
3import (
4 "go.uber.org/zap"
5)
6
7func main() {
8 logger, _ := zap.NewProduction()
9 //Sync刷新任何缓冲的日志条目。
10 defer logger.Sync()
11 logger.Info("this is a test log")
12 logger.Warn("this is a test log")
13 logger.Error("this is a test log")
14 logger.Fatal("this is a test log")
15
16}
输出结果
1{"level":"info","ts":1583479612.40354,"caller":"log/main.go:10","msg":"this is a test log"}
2{"level":"warn","ts":1583479612.403604,"caller":"log/main.go:11","msg":"this is a test log"}
3{"level":"error","ts":1583479612.4036129,"caller":"log/main.go:12","msg":"this is a test log","stacktrace":"main.main\n\t/Users/jerry/go/src/utils/log/main.go:12\nruntime.main\n\t/usr/local/go/src/runtime/proc.go:203"}
SugaredLogger
1package main
2
3import (
4 "go.uber.org/zap"
5)
6
7func main() {
8 logger, _ := zap.NewProduction()
9 //Sync刷新任何缓冲的日志条目。
10 defer logger.Sync()
11 sugar := logger.Sugar()
12 sugar.Info("this is a test log")
13 sugar.Warn("this is a test log")
14 sugar.Error("this is a test log")
15 sugar.Fatal("this is a test log")
16
17}
输出结果
1{"level":"info","ts":1583479949.807585,"caller":"log/main.go:11","msg":"this is a test log"}
2{"level":"warn","ts":1583479949.807671,"caller":"log/main.go:12","msg":"this is a test log"}
3{"level":"error","ts":1583479949.807691,"caller":"log/main.go:13","msg":"this is a test log","stacktrace":"main.main\n\t/Users/jerry/go/src/utils/log/main.go:13\nruntime.m
4ain\n\t/usr/local/go/src/runtime/proc.go:203"}
5{"level":"fatal","ts":1583479949.8077528,"caller":"log/main.go:14","msg":"this is a test log","stacktrace":"main.main\n\t/Users/jerry/go/src/utils/log/main.go:14\nruntime.
6main\n\t/usr/local/go/src/runtime/proc.go:203"}
7exit status 1
大体上是差不多的,唯一的区别就是更加了格式化输出功能,像Print
和Printf
自定义Logger
很多时候我们的应用程序在都是后台跑着的,日志在控制台输出只是调试的时候用的到,我们一般都是将日志记录到本地的文件当中。
自带的日志库
1package main
2
3import (
4 "log"
5 "os"
6)
7
8func main() {
9 fileName := "my.log"
10 logFile, err := os.Create(fileName)
11 if err != nil {
12 log.Fatalln("open file failed")
13 }
14 defer logFile.Close()
15 debugLog := log.New(logFile, "[Info]", log.Llongfile)
16 debugLog.Println("A Info message here")
17 debugLog.SetPrefix("[Debug]")
18 debugLog.Println("A Debug Message here")
19
20}
输出结果
1[Info]/Users/jerry/go/src/utils/log/main.go:16: A Info message here
2[Debug]/Users/jerry/go/src/utils/log/main.go:18: A Debug Message here
使用Zap日志库写入到文件
1package main
2
3import (
4 "os"
5
6 "go.uber.org/zap"
7 "go.uber.org/zap/zapcore"
8)
9
10func initLogger(logpath string, loglevel string) *zap.Logger {
11 file, _ := os.Create(logpath)
12 w := zapcore.AddSync(file)
13 var level zapcore.Level
14 switch loglevel {
15 case "debug":
16 level = zap.DebugLevel
17 case "info":
18 level = zap.InfoLevel
19 case "error":
20 level = zap.ErrorLevel
21 default:
22 level = zap.InfoLevel
23 }
24 encoderConfig := zap.NewProductionEncoderConfig()
25 encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
26 core := zapcore.NewCore(
27 zapcore.NewConsoleEncoder(encoderConfig),
28 w,
29 level,
30 )
31 logger := zap.New(core)
32 logger.Info("DefaultLogger init success")
33 return logger
34}
35
36func main() {
37
38 logger := initLogger("my.log", "info")
39 logger.Info("this is a test log", zap.Int("line", 47))
40 logger.Warn("this is a test log", zap.Int("line", 47))
41
42}
输出结果
1cat my.log
22020-03-06T16:00:25.380+0800 info DefaultLogger init success
32020-03-06T16:00:25.380+0800 info this is a test log {"line": 47}
42020-03-06T16:00:25.380+0800 warn this is a test log {"line": 47}
已经记录到文件中了
将日志进行序列化文件
lumberjack:支持文件按大小或者时间归档 GitHub地址:https://github.com/natefinch/lumberjack
安装lumberjack
1go get github.com/natefinch/lumberjack
使用lumberjack
使用方法也很简单,在initlog调用即可
1package main
2
3import (
4 "github.com/natefinch/lumberjack"
5 "go.uber.org/zap"
6 "go.uber.org/zap/zapcore"
7)
8
9func initLogger(logpath string, loglevel string) *zap.Logger {
10 hook := lumberjack.Logger{
11 Filename: logpath, // ⽇志⽂件路径
12 MaxSize: 1, // megabytes
13 MaxBackups: 3, // 最多保留3个备份
14 MaxAge: 7, //days
15 Compress: true, // 是否压缩 disabled by default
16 }
17 w := zapcore.AddSync(&hook)
18 var level zapcore.Level
19 switch loglevel {
20 case "debug":
21 level = zap.DebugLevel
22 case "info":
23 level = zap.InfoLevel
24 case "error":
25 level = zap.ErrorLevel
26 default:
27 level = zap.InfoLevel
28 }
29 encoderConfig := zap.NewProductionEncoderConfig()
30 encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
31 core := zapcore.NewCore(
32 zapcore.NewConsoleEncoder(encoderConfig),
33 w,
34 level,
35 )
36 logger := zap.New(core)
37 logger.Info("DefaultLogger init success")
38 return logger
39}
40
41func main() {
42
43 logger := initLogger("my.log", "info")
44 for i := 0; i <= 500000; i++ {
45 logger.Info("this is a test log", zap.Int("line", 47))
46 logger.Warn("this is a test log", zap.Int("line", 47))
47 }
48
49}
运行结果跟上面一样,只是到达指定大小之后会对文件进行切割。为演示方便我设置成1M大小进行切割
1(☸ docker-desktop:default)➜ log ls -lh
2total 18256
3-rw-r--r-- 1 jerry staff 117B 3 6 16:13 go.mod
4-rw-r--r-- 1 jerry staff 4.5K 3 6 16:13 go.sum
5-rwxr-xr-x 1 jerry staff 7.2M 3 6 16:15 log
6-rw-r--r-- 1 jerry staff 1.2K 3 6 16:15 main.go
7-rw-r--r-- 1 jerry staff 48K 3 6 16:16 my-2020-03-06T08-16-43.417.log.gz
8-rw-r--r-- 1 jerry staff 1.0M 3 6 16:17 my.log
9(☸ docker-desktop:default)➜ log ls -lh
10total 16352
11-rw-r--r-- 1 jerry staff 117B 3 6 16:13 go.mod
12-rw-r--r-- 1 jerry staff 4.5K 3 6 16:13 go.sum
13-rwxr-xr-x 1 jerry staff 7.2M 3 6 16:15 log
14-rw-r--r-- 1 jerry staff 1.2K 3 6 16:15 main.go
15-rw-r--r-- 1 jerry staff 48K 3 6 16:16 my-2020-03-06T08-16-43.417.log.gz
16-rw-r--r-- 1 jerry staff 48K 3 6 16:17 my-2020-03-06T08-17-29.852.log.gz
17-rw-r--r-- 1 jerry staff 27K 3 6 16:17 my.log
参考
- 原文作者:黄忠德
- 原文链接:https://huangzhongde.cn/post/Golang/2020-03-07-golang_logger_zap/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。