高性能日志库zap
# 高性能日志库zap
doc: https://github.com/uber-go/zap
- 安装和基本使用
// go get -u go.uber.org/zap package main import ( "go.uber.org/zap" ) func main() { logger, _ := zap.NewProduction() defer logger.Sync() // flushes buffer, if any url := "https://imooc.com" sugar := logger.Sugar() sugar.Infow("failed to fetch URL", // Structured context as loosely typed key-value pairs. "url", url, "attempt", 3, ) sugar.Infof("Failed to fetch URL: %s", url) }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19Zap提供了两种类型的日志记录器—
Sugared Logger
和Logger
。在性能很好但不是很关键的上下文中,使用
SugaredLogger
。它比其他结构化日志记录包快4-10倍,并且支持结构化和printf风格的日志记录。在每一微秒和每一次内存分配都很重要的上下文中,使用
Logger
。它甚至比SugaredLogger
更快,内存分配次数也更少,但它只支持强类型的结构化日志记录- 写入日志文件
package main import ( "go.uber.org/zap" "time" ) func NewLogger() (*zap.Logger, error) { cfg := zap.NewProductionConfig() cfg.OutputPaths = []string{ "./myproject.log", // 输出到文件 "stdout",// 输出到控制台 "stderr",//输出到控制台err } return cfg.Build() } func main() { //logger, _ := zap.NewProduction() logger, err := NewLogger() if err != nil { panic(err) //panic("初始化logger失败") } su := logger.Sugar() defer su.Sync() url := "https://imooc.com" su.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), ) }
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高级用法
/* 1.S()可以获取一个全局的 sugar,可以让我们已设置一个全局的1oger, 而且S()是安全的 2.日志是分级别的, debug,info,Warn, error, fetal 3.zap.S()函数和zap.L()函数很有用 -- S()= sugure --L() = logger() */ logger, _ := zap.NewDevelopment() zap.ReplaceGlobals(logger) // 初始化router Router := initialize.Routers() zap.S().Info("启动服务端 %d", port) zap.S().Panic("启动失败:", err.Error())
1
2
3
4
5
6
7
8
9
10
11
12
# 案例1-gin
package main
import (
"net"
"net/http"
"net/http/httputil"
"os"
"runtime/debug"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/natefinch/lumberjack"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
var logger *zap.Logger
var sugarLogger *zap.SugaredLogger
//func main() {
// InitLogger()
// defer logger.Sync()
//
// for i:=0;i<10000;i++{
// logger.Info("test for log rotate...")
// }
// simpleHttpGet("www.sogo.com")
// simpleHttpGet("http://www.sogo.com")
//}
func main() {
InitLogger()
//r := gin.Default()
r := gin.New()
// 这样每次请求都会先走日志函数,先记录路径和请求参数,再调用Next函数,等其他函数执行完再去记录全部的信息
r.Use(GinLogger(logger), GinRecovery(logger, true))
r.GET("/hello", func(c *gin.Context) {
c.String(http.StatusOK, "hello q1mi!")
logger.Info("测试日志")
})
r.Run()
}
func InitLogger() {
writeSyncer := getLogWriter()
encoder := getEncoder()
core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
logger = zap.New(core, zap.AddCaller())
sugarLogger = logger.Sugar()
}
func getEncoder() zapcore.Encoder {
//return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
encoderConfig := zapcore.EncoderConfig{
TimeKey: "ts",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
}
return zapcore.NewConsoleEncoder(encoderConfig)
}
//func getLogWriter() zapcore.WriteSyncer {
// file, _ := os.OpenFile("./test.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0744)
// return zapcore.AddSync(file)
//}
func getLogWriter() zapcore.WriteSyncer {
lumberJackLogger := &lumberjack.Logger{
Filename: "./test.log", // 这里定义保存日志的位置
MaxSize: 1, // 在进行切割之前,日志文件的最大大小(以MB为单位)
MaxBackups: 5, // 保留旧文件的最大个数
MaxAge: 30, // 保留旧文件的最大天数
Compress: false, // 是否压缩/归档旧文件
}
return zapcore.AddSync(lumberJackLogger)
}
func simpleHttpGet(url string) {
resp, err := http.Get(url)
if err != nil {
sugarLogger.Error(
"Error fetching url..",
zap.String("url", url),
zap.Error(err))
} else {
sugarLogger.Info("Success..",
zap.String("statusCode", resp.Status),
zap.String("url", url))
resp.Body.Close()
}
}
// GinLogger 接收gin框架默认的日志
func GinLogger(logger *zap.Logger) gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
path := c.Request.URL.Path
query := c.Request.URL.RawQuery
c.Next()
cost := time.Since(start)
logger.Info(path,
zap.Int("status", c.Writer.Status()),
zap.String("method", c.Request.Method),
zap.String("path", path),
zap.String("query", query),
zap.String("ip", c.ClientIP()),
zap.String("user-agent", c.Request.UserAgent()),
zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
zap.Duration("cost", cost),
)
}
}
// GinRecovery recover掉项目可能出现的panic
func GinRecovery(logger *zap.Logger, stack bool) gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
// Check for a broken connection, as it is not really a
// condition that warrants a panic stack trace.
var brokenPipe bool
if ne, ok := err.(*net.OpError); ok {
if se, ok := ne.Err.(*os.SyscallError); ok {
if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
brokenPipe = true
}
}
}
httpRequest, _ := httputil.DumpRequest(c.Request, false)
if brokenPipe {
logger.Error(c.Request.URL.Path,
zap.Any("error", err),
zap.String("request", string(httpRequest)),
)
// If the connection is dead, we can't write a status to it.
c.Error(err.(error)) // nolint: errcheck
c.Abort()
return
}
if stack {
logger.Error("[Recovery from panic]",
zap.Any("error", err),
zap.String("request", string(httpRequest)),
zap.String("stack", string(debug.Stack())),
)
} else {
logger.Error("[Recovery from panic]",
zap.Any("error", err),
zap.String("request", string(httpRequest)),
)
}
c.AbortWithStatus(http.StatusInternalServerError)
}
}()
c.Next()
}
}
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# 案例2
logcore
/** * 获取日志 * filePath 日志文件路径 * level 日志级别 * maxSize 每个日志文件保存的最大尺寸 单位:M * maxBackups 日志文件最多保存多少个备份 * maxAge 文件最多保存多少天 * compress 是否压缩 * serviceName 服务名 */ func NewLogger(filePath string, level zapcore.Level, maxSize int, maxBackups int, maxAge int, compress bool, serviceName string) *zap.Logger { core := newCore(filePath, level, maxSize, maxBackups, maxAge, compress) return zap.New(core, zap.AddCaller(), zap.Development(), zap.Fields(zap.String("serviceName", serviceName))) } /** * zapcore构造 */ func newCore(filePath string, level zapcore.Level, maxSize int, maxBackups int, maxAge int, compress bool) zapcore.Core { //日志文件路径配置2 hook := lumberjack.Logger{ Filename: filePath, // 日志文件路径 MaxSize: maxSize, // 每个日志文件保存的最大尺寸 单位:M MaxBackups: maxBackups, // 日志文件最多保存多少个备份 MaxAge: maxAge, // 文件最多保存多少天 Compress: compress, // 是否压缩 } // 设置日志级别 atomicLevel := zap.NewAtomicLevel() atomicLevel.SetLevel(level) //公用编码器 encoderConfig := zapcore.EncoderConfig{ TimeKey: "time", LevelKey: "level", NameKey: "logger", CallerKey: "linenum", MessageKey: "msg", StacktraceKey: "stacktrace", LineEnding: zapcore.DefaultLineEnding, EncodeLevel: zapcore.LowercaseLevelEncoder, // 小写编码器 EncodeTime: zapcore.ISO8601TimeEncoder, // ISO8601 UTC 时间格式 EncodeDuration: zapcore.SecondsDurationEncoder, // EncodeCaller: zapcore.FullCallerEncoder, // 全路径编码器 EncodeName: zapcore.FullNameEncoder, } return zapcore.NewCore( zapcore.NewJSONEncoder(encoderConfig), // 编码器配置 zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(&hook)), // 打印到控制台和文件 atomicLevel, // 日志级别 ) }
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
39
40
41
42
43
44
45
46
47
48
49
50
51log
var MainLogger *zap.Logger var GatewayLogger *zap.Logger func init() { MainLogger = NewLogger("./logs/main.log", zapcore.InfoLevel, 128, 30, 7, true, "Main") GatewayLogger = NewLogger("./logs/gateway.log", zapcore.DebugLevel, 128, 30, 7, true, "Gateway") }
1
2
3
4
5
6
7
8app
func main() { fmt.Println("init main") log.MainLogger.Debug("hello main Debug") log.MainLogger.Info("hello main Info") log.GatewayLogger.Debug("Hi Gateway Im Debug") log.GatewayLogger.Info("Hi Gateway Im Info") }
1
2
3
4
5
6
7
上次更新: 2023/04/16, 18:35:33