-
Notifications
You must be signed in to change notification settings - Fork 2
/
logger.go
325 lines (274 loc) · 7.22 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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
package logger
import (
"context"
"io"
"sync/atomic"
"time"
"github.com/sirupsen/logrus"
)
// 定义键名
const (
StartedAtKey = "started_at"
TraceIDKey = "trace_id"
UserIDKey = "user_id"
SpanIDKey = "span_id"
SpanTitleKey = "span_title"
SpanFunctionKey = "span_function"
VersionKey = "version"
TimeConsumingKey = "time_consuming"
)
// TraceIDFunc 定义获取跟踪ID的函数
type TraceIDFunc func() string
var (
version string
traceIDFunc TraceIDFunc
)
// Logger 定义日志别名
type Logger = logrus.Logger
// Hook 定义日志钩子别名
type Hook = logrus.Hook
// StandardLogger 获取标准日志
func StandardLogger() *Logger {
return logrus.StandardLogger()
}
// SetLevel 设定日志级别
func SetLevel(level int) {
logrus.SetLevel(logrus.Level(level))
}
// SetFormatter 设定日志输出格式
func SetFormatter(format string) {
switch format {
case "json":
logrus.SetFormatter(new(logrus.JSONFormatter))
default:
logrus.SetFormatter(new(logrus.TextFormatter))
}
}
// SetOutput 设定日志输出
func SetOutput(out io.Writer) {
logrus.SetOutput(out)
}
// SetVersion 设定版本
func SetVersion(v string) {
version = v
}
// SetTraceIDFunc 设定追踪ID的处理函数
func SetTraceIDFunc(fn TraceIDFunc) {
traceIDFunc = fn
}
// AddHook 增加日志钩子
func AddHook(hook Hook) {
logrus.AddHook(hook)
}
func getTraceID() string {
if traceIDFunc != nil {
return traceIDFunc()
}
return time.Now().Format("2006.01.02.15.04.05.000")
}
type (
traceIDContextKey struct{}
spanIDContextKey struct{}
userIDContextKey struct{}
)
// NewTraceIDContext 创建跟踪ID上下文
func NewTraceIDContext(ctx context.Context, traceID string) context.Context {
return context.WithValue(ctx, traceIDContextKey{}, traceID)
}
// FromTraceIDContext 从上下文中获取跟踪ID
func FromTraceIDContext(ctx context.Context) string {
v := ctx.Value(traceIDContextKey{})
if v != nil {
if s, ok := v.(string); ok {
return s
}
}
return getTraceID()
}
// NewSpanIDContext 创建跟踪单元ID上下文
func NewSpanIDContext(ctx context.Context, spanID string) context.Context {
return context.WithValue(ctx, spanIDContextKey{}, spanID)
}
// FromSpanIDContext 从上下文中获取跟踪单元ID
func FromSpanIDContext(ctx context.Context) string {
v := ctx.Value(spanIDContextKey{})
if v != nil {
if s, ok := v.(string); ok {
return s
}
}
return getTraceID()
}
// NewUserIDContext 创建用户ID上下文
func NewUserIDContext(ctx context.Context, userID string) context.Context {
return context.WithValue(ctx, userIDContextKey{}, userID)
}
// FromUserIDContext 从上下文中获取用户ID
func FromUserIDContext(ctx context.Context) string {
v := ctx.Value(userIDContextKey{})
if v != nil {
if s, ok := v.(string); ok {
return s
}
}
return ""
}
type spanOptions struct {
Title string
FuncName string
}
// SpanOption 定义跟踪单元的数据项
type SpanOption func(*spanOptions)
// SetSpanTitle 设置跟踪单元的标题
func SetSpanTitle(title string) SpanOption {
return func(o *spanOptions) {
o.Title = title
}
}
// SetSpanFuncName 设置跟踪单元的函数名
func SetSpanFuncName(funcName string) SpanOption {
return func(o *spanOptions) {
o.FuncName = funcName
}
}
// StartSpan 开始一个追踪单元
func StartSpan(ctx context.Context, opts ...SpanOption) *Entry {
if ctx == nil {
ctx = context.Background()
}
var o spanOptions
for _, opt := range opts {
opt(&o)
}
fields := map[string]interface{}{
StartedAtKey: time.Now(),
UserIDKey: FromUserIDContext(ctx),
TraceIDKey: FromTraceIDContext(ctx),
SpanIDKey: FromSpanIDContext(ctx),
SpanTitleKey: o.Title,
SpanFunctionKey: o.FuncName,
VersionKey: version,
}
return newEntry(logrus.WithFields(fields))
}
// StartSpanWithCall 开始一个追踪单元(回调执行)
func StartSpanWithCall(ctx context.Context, opts ...SpanOption) func() *Entry {
return func() *Entry {
return StartSpan(ctx, opts...)
}
}
// Debugf 写入调试日志
func Debugf(ctx context.Context, format string, args ...interface{}) {
StartSpan(ctx).Debugf(format, args...)
}
// Infof 写入消息日志
func Infof(ctx context.Context, format string, args ...interface{}) {
StartSpan(ctx).Infof(format, args...)
}
// Printf 写入消息日志
func Printf(ctx context.Context, format string, args ...interface{}) {
StartSpan(ctx).Printf(format, args...)
}
// Warnf 写入警告日志
func Warnf(ctx context.Context, format string, args ...interface{}) {
StartSpan(ctx).Warnf(format, args...)
}
// Errorf 写入错误日志
func Errorf(ctx context.Context, format string, args ...interface{}) {
StartSpan(ctx).Errorf(format, args...)
}
// Fatalf 写入重大错误日志
func Fatalf(ctx context.Context, format string, args ...interface{}) {
StartSpan(ctx).Fatalf(format, args...)
}
func newEntry(entry *logrus.Entry) *Entry {
return &Entry{entry: entry}
}
// Entry 定义统一的日志写入方式
type Entry struct {
entry *logrus.Entry
finish int32
}
// Finish 完成,如果没有触发写入则手动触发Info级别的日志写入
func (e *Entry) Finish() {
if atomic.CompareAndSwapInt32(&e.finish, 0, 1) {
e.done()
e.entry.Info()
}
}
func (e *Entry) checkAndDelete(fields map[string]interface{}, keys ...string) {
for _, key := range keys {
if _, ok := fields[key]; ok {
delete(fields, key)
}
}
}
// WithFields 结构化字段写入
func (e *Entry) WithFields(fields map[string]interface{}) *Entry {
e.checkAndDelete(fields,
StartedAtKey,
TraceIDKey,
SpanIDKey,
SpanTitleKey,
SpanFunctionKey,
VersionKey,
TimeConsumingKey)
return newEntry(e.entry.WithFields(fields))
}
// WithField 结构化字段写入
func (e *Entry) WithField(key string, value interface{}) *Entry {
return e.WithFields(map[string]interface{}{key: value})
}
// Fatalf 重大错误日志
func (e *Entry) Fatalf(format string, args ...interface{}) {
e.done()
e.entry.Fatalf(format, args...)
}
// Errorf 错误日志
func (e *Entry) Errorf(format string, args ...interface{}) {
e.done()
e.entry.Errorf(format, args...)
}
// Warnf 警告日志
func (e *Entry) Warnf(format string, args ...interface{}) {
e.done()
e.entry.Warnf(format, args...)
}
// Infof 消息日志
func (e *Entry) Infof(format string, args ...interface{}) {
e.done()
e.entry.Infof(format, args...)
}
// Printf 消息日志
func (e *Entry) Printf(format string, args ...interface{}) {
e.done()
e.entry.Printf(format, args...)
}
// Debugf 写入调试日志
func (e *Entry) Debugf(format string, args ...interface{}) {
e.done()
e.entry.Debugf(format, args...)
}
func (e *Entry) copyEntry(entry *logrus.Entry) *logrus.Entry {
newEntry := logrus.NewEntry(entry.Logger)
newEntry.Data = make(logrus.Fields)
newEntry.Time = entry.Time
newEntry.Level = entry.Level
newEntry.Message = entry.Message
for k, v := range entry.Data {
newEntry.Data[k] = v
}
return newEntry
}
func (e *Entry) done() {
entry := e.copyEntry(e.entry)
entry.Time = time.Now()
if v, ok := entry.Data[StartedAtKey]; ok {
if startedAt, ok := v.(time.Time); ok {
entry.Data[TimeConsumingKey] = entry.Time.Sub(startedAt).Nanoseconds() / 1e3
delete(entry.Data, StartedAtKey)
}
}
e.entry = entry
atomic.CompareAndSwapInt32(&e.finish, 0, 1)
}