-
Notifications
You must be signed in to change notification settings - Fork 0
/
logger.go
217 lines (196 loc) · 6.39 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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// Package logit 简单封装了在日常使用 zap 打日志时的常用方法。
//
// 提供快速使用 zap 打印日志的全部方法,所有日志打印方法开箱即用
//
// 提供多种快速创建 baseLogger 的方法
//
// 支持从 context.Context/gin.Context 中创建、获取带有 Trace ID 的 baseLogger
package logit
import (
"log"
"net"
"strings"
"sync"
"syscall"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// AtomicLevelServerOption AtomicLevel server 相关配置
type AtomicLevelServerOption struct {
Addr string // http 动态修改日志级别服务运行地址
Path string // 设置 url path ,可选
Username string // 请求时设置 basic auth 认证的用户名,可选
Password string // 请求时设置 basic auth 认证的密码,可选,与 username 同时存在才开启 basic auth
}
// Options new baseLogger options
type Options struct {
Name string // baseLogger 名称
Level string // 日志级别 debug, info, warn, error dpanic, panic, fatal
Format string // 日志格式 console, json
OutputPaths []string // 日志输出位置
InitialFields map[string]interface{} // 日志初始字段
DisableCaller bool // 是否关闭打印 caller
DisableStacktrace bool // 是否关闭打印 stackstrace
EncoderConfig *zapcore.EncoderConfig // 配置日志字段 key 的名称
}
const (
// defaultLoggerName 默认 baseLogger name 为 logit
defaultLoggerName = "logit"
)
var (
// global zap Logger with pid field
baseLogger *zap.Logger
// outPaths zap 日志默认输出位置
outPaths = []string{"stdout"}
// initialFields 默认初始字段为进程 id
initialFields = map[string]interface{}{
"pid": syscall.Getpid(),
"server_ip": ServerIP(),
}
// atomicLevel 默认 baseLogger atomic level 级别默认为 debug
atomicLevel = zap.NewAtomicLevelAt(zap.DebugLevel)
// defaultEncoderConfig 默认的日志字段名配置
defaultEncoderConfig = zapcore.EncoderConfig{
TimeKey: "time",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.CapitalLevelEncoder,
EncodeTime: TimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: CallerEncoder,
}
// 读写锁
rwMutex sync.RWMutex
)
// init the global baseLogger
func init() {
var err error
options := Options{
Name: defaultLoggerName,
Level: "debug",
Format: "json",
OutputPaths: outPaths,
InitialFields: initialFields,
DisableCaller: false,
DisableStacktrace: true,
EncoderConfig: &defaultEncoderConfig,
}
baseLogger, err = NewLogger(options)
if err != nil {
log.Panicln(err)
}
}
// NewLogger return a zap Logger instance
func NewLogger(options Options) (*zap.Logger, error) {
cfg := zap.Config{}
// 设置日志级别
lvl := strings.ToLower(options.Level)
if _, exists := AtomicLevelMap[lvl]; !exists {
cfg.Level = atomicLevel
} else {
cfg.Level = AtomicLevelMap[lvl]
atomicLevel = cfg.Level
}
// 设置 encoding 默认为 json
if strings.ToLower(options.Format) == "console" {
cfg.Encoding = "console"
} else {
cfg.Encoding = "json"
}
// 设置 output 没有传参默认全部输出到 stderr
if len(options.OutputPaths) == 0 {
cfg.OutputPaths = outPaths
cfg.ErrorOutputPaths = outPaths
} else {
cfg.OutputPaths = options.OutputPaths
cfg.ErrorOutputPaths = options.OutputPaths
}
// 设置 InitialFields 没有传参使用默认字段
// 传了就添加到现有的初始化字段中
if len(options.InitialFields) > 0 {
for k, v := range options.InitialFields {
initialFields[k] = v
}
}
cfg.InitialFields = initialFields
// 设置 disable caller
cfg.DisableCaller = options.DisableCaller
// 设置 disable stacktrace
cfg.DisableStacktrace = options.DisableStacktrace
// 设置 encoderConfig
if options.EncoderConfig == nil {
cfg.EncoderConfig = defaultEncoderConfig
} else {
cfg.EncoderConfig = *options.EncoderConfig
}
// Sampling 实现了日志的流控功能,或者叫采样配置,主要有两个配置参数, Initial 和 Thereafter ,实现的效果是在 1s 的时间单位内,如果某个日志级别下同样内容的日志输出数量超过了 Initial 的数量,那么超过之后,每隔 Thereafter 的数量,才会再输出一次。是一个对日志输出的保护功能。
cfg.Sampling = &zap.SamplingConfig{
Initial: 100,
Thereafter: 100,
}
var err error
// 生成 baseLogger
logger, err := cfg.Build()
if err != nil {
return nil, err
}
// 设置 baseLogger 名字,没有传参使用默认名字
if options.Name != "" {
logger = logger.Named(options.Name)
} else {
logger = logger.Named(defaultLoggerName)
}
return logger, nil
}
// CloneLogger return the global baseLogger copy which add a new name
func CloneLogger(name string, fields ...zap.Field) *zap.Logger {
rwMutex.RLock()
defer rwMutex.RUnlock()
copyLogger := *baseLogger
clogger := ©Logger
clogger = clogger.Named(name)
if len(fields) > 0 {
clogger = clogger.With(fields...)
}
return clogger
}
// AttachCore add a core to zap baseLogger
func AttachCore(l *zap.Logger, c zapcore.Core) *zap.Logger {
return l.WithOptions(zap.WrapCore(func(core zapcore.Core) zapcore.Core {
return zapcore.NewTee(core, c)
}))
}
// ReplaceLogger 替换默认的全局 baseLogger 为传入的新 baseLogger
// 返回函数,调用它可以恢复全局 baseLogger 为上一次的 baseLogger
func ReplaceLogger(newLogger *zap.Logger) func() {
rwMutex.Lock()
defer rwMutex.Unlock()
// 备份原始 baseLogger 以便恢复
prevLogger := baseLogger
// 替换为新 baseLogger
baseLogger = newLogger
return func() { ReplaceLogger(prevLogger) }
}
// TextLevel 返回默认 baseLogger 的 字符串 level
func TextLevel() string {
b, _ := atomicLevel.MarshalText()
return string(b)
}
// SetLevel 使用字符串级别设置默认 baseLogger 的 atomic level
func SetLevel(lvl string) error {
return atomicLevel.UnmarshalText([]byte(strings.ToLower(lvl)))
}
// ServerIP 获取当前 IP
func ServerIP() string {
conn, err := net.Dial("udp", "8.8.8.8:80")
if err != nil {
return ""
}
defer conn.Close()
localAddr := conn.LocalAddr().(*net.UDPAddr)
return localAddr.IP.String()
}