/
logger.go
193 lines (178 loc) · 5.58 KB
/
logger.go
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
package logger
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/hpf0532/go-webhook/extend/conf"
"github.com/hpf0532/go-webhook/utils"
"github.com/natefinch/lumberjack"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"net"
"net/http"
"net/http/httputil"
"os"
"path"
"runtime/debug"
"strings"
"time"
)
var level zapcore.Level
var Logger *zap.Logger
var SugarLogger *zap.SugaredLogger
func Setup() {
if ok, _ := utils.PathExists(conf.ZapConf.Director); !ok { // 判断是否有Director文件夹
fmt.Printf("create %v directory\n", conf.ZapConf.Director)
_ = os.Mkdir(conf.ZapConf.Director, os.ModePerm)
}
switch conf.ZapConf.Level { // 初始化配置文件的Level
case "debug":
level = zap.DebugLevel
case "info":
level = zap.InfoLevel
case "warn":
level = zap.WarnLevel
case "error":
level = zap.ErrorLevel
case "dpanic":
level = zap.DPanicLevel
case "panic":
level = zap.PanicLevel
case "fatal":
level = zap.FatalLevel
default:
level = zap.InfoLevel
}
if level == zap.DebugLevel || level == zap.ErrorLevel {
Logger = zap.New(getEncoderCore(), zap.AddStacktrace(level))
} else {
Logger = zap.New(getEncoderCore())
}
if conf.ZapConf.ShowLine {
Logger = Logger.WithOptions(zap.AddCaller())
}
SugarLogger = Logger.Sugar()
}
// getEncoderConfig 获取zapcore.EncoderConfig
func getEncoderConfig() (config zapcore.EncoderConfig) {
config = zapcore.EncoderConfig{
MessageKey: "message",
LevelKey: "level",
TimeKey: "time",
NameKey: "logger",
CallerKey: "caller",
StacktraceKey: conf.ZapConf.StacktraceKey,
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: CustomTimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.FullCallerEncoder,
}
switch {
case conf.ZapConf.EncodeLevel == "LowercaseLevelEncoder": // 小写编码器(默认)
config.EncodeLevel = zapcore.LowercaseLevelEncoder
case conf.ZapConf.EncodeLevel == "LowercaseColorLevelEncoder": // 小写编码器带颜色
config.EncodeLevel = zapcore.LowercaseColorLevelEncoder
case conf.ZapConf.EncodeLevel == "CapitalLevelEncoder": // 大写编码器
config.EncodeLevel = zapcore.CapitalLevelEncoder
case conf.ZapConf.EncodeLevel == "CapitalColorLevelEncoder": // 大写编码器带颜色
config.EncodeLevel = zapcore.CapitalColorLevelEncoder
default:
config.EncodeLevel = zapcore.LowercaseLevelEncoder
}
return config
}
// getEncoder 获取zapcore.Encoder
func getEncoder() zapcore.Encoder {
if conf.ZapConf.Format == "json" {
return zapcore.NewJSONEncoder(getEncoderConfig())
}
return zapcore.NewConsoleEncoder(getEncoderConfig())
}
// getEncoderCore 获取Encoder的zapcore.Core
func getEncoderCore() (core zapcore.Core) {
writer := GetWriteSyncer() // 使用lumberjack进行日志分割
return zapcore.NewCore(getEncoder(), writer, level)
}
// 自定义日志输出时间格式
func CustomTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(t.Format(conf.ZapConf.Prefix + "2006/01/02 - 15:04:05.000"))
}
// 加入Lumberjack支持日志文件滚动
func GetWriteSyncer() zapcore.WriteSyncer {
lumberJackLogger := &lumberjack.Logger{
Filename: path.Join(conf.ZapConf.Director, "webhook.log"),
MaxSize: 50, //日志文件最大容量
MaxBackups: 5, //最多保留五个文件
MaxAge: 30, //保留30天
LocalTime: true, //开启本地时间
Compress: true, //是否压缩
}
if conf.ZapConf.LogInConsole {
return zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(lumberJackLogger))
}
return zapcore.AddSync(lumberJackLogger)
}
// GinLogger 接收gin框架默认的日志
func GinLogger() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
rpath := c.Request.URL.Path
query := c.Request.URL.RawQuery
c.Next()
cost := time.Since(start)
Logger.Info(rpath,
zap.Int("status", c.Writer.Status()),
zap.String("method", c.Request.Method),
zap.String("path", rpath),
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,并使用zap记录相关日志
func GinRecovery(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()
}
}