forked from go-playground/log
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlog.go
371 lines (308 loc) · 10.1 KB
/
log.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
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
package log
import (
"fmt"
"os"
"runtime"
"sync"
"time"
)
// FilenameDisplay is the type of file display output
type FilenameDisplay uint8
// FilenameDisplay options
const (
Lshortfile FilenameDisplay = iota
Llongfile
)
// HandlerChannels is an array of handler channels
type HandlerChannels []chan<- *Entry
// LevelHandlerChannels is a group of Handler channels mapped by Level
type LevelHandlerChannels map[Level]HandlerChannels
// DurationFormatFunc is the function called for parsing Trace Duration
type DurationFormatFunc func(time.Duration) string
type logger struct {
fieldPool *sync.Pool
entryPool *sync.Pool
tracePool *sync.Pool
channels LevelHandlerChannels
durationFunc DurationFormatFunc
timeFormat string
appID string
callerInfoLevels [9]bool
}
const (
// DefaultTimeFormat is the default time format when parsing Time values.
// it is exposed to allow handlers to use and not have to redefine
DefaultTimeFormat = "2006-01-02T15:04:05.000000000Z07:00"
)
// Logger is the default instance of the log package
var (
once sync.Once
Logger *logger
exitFunc = os.Exit
skipLevel = 2
stackTraceLimit = 7000
)
func init() {
once.Do(func() {
Logger = &logger{
fieldPool: &sync.Pool{New: func() interface{} {
return Field{}
}},
tracePool: &sync.Pool{New: func() interface{} {
return new(TraceEntry)
}},
channels: make(LevelHandlerChannels),
durationFunc: func(d time.Duration) string { return d.String() },
timeFormat: DefaultTimeFormat,
callerInfoLevels: [9]bool{
true,
false,
false,
false,
true,
true,
true,
true,
true,
},
}
Logger.entryPool = &sync.Pool{New: func() interface{} {
return &Entry{
wg: new(sync.WaitGroup),
ApplicationID: Logger.getApplicationID(),
}
}}
})
}
// LeveledLogger interface for logging by level
type LeveledLogger interface {
Debug(v ...interface{})
Trace(v ...interface{}) Traceable
Info(v ...interface{})
Notice(v ...interface{})
Warn(v ...interface{})
Error(v ...interface{})
Panic(v ...interface{})
Alert(v ...interface{})
Fatal(v ...interface{})
Debugf(msg string, v ...interface{})
Tracef(msg string, v ...interface{}) Traceable
Infof(msg string, v ...interface{})
Noticef(msg string, v ...interface{})
Warnf(msg string, v ...interface{})
Errorf(msg string, v ...interface{})
Panicf(msg string, v ...interface{})
Alertf(msg string, v ...interface{})
Fatalf(msg string, v ...interface{})
WithFields(...Field) LeveledLogger
StackTrace() LeveledLogger
}
var _ LeveledLogger = Logger
// Debug level formatted message.
func (l *logger) Debug(v ...interface{}) {
e := newEntry(DebugLevel, fmt.Sprint(v...), nil, skipLevel)
l.HandleEntry(e)
}
// Trace starts a trace & returns Traceable object to End + log.
// Example defer log.Trace(...).End()
func (l *logger) Trace(v ...interface{}) Traceable {
t := l.tracePool.Get().(*TraceEntry)
t.entry = newEntry(TraceLevel, fmt.Sprint(v...), make([]Field, 0), skipLevel)
t.start = time.Now().UTC()
return t
}
// Info level formatted message.
func (l *logger) Info(v ...interface{}) {
e := newEntry(InfoLevel, fmt.Sprint(v...), nil, skipLevel)
l.HandleEntry(e)
}
// Notice level formatted message.
func (l *logger) Notice(v ...interface{}) {
e := newEntry(NoticeLevel, fmt.Sprint(v...), nil, skipLevel)
l.HandleEntry(e)
}
// Warn level formatted message.
func (l *logger) Warn(v ...interface{}) {
e := newEntry(WarnLevel, fmt.Sprint(v...), nil, skipLevel)
l.HandleEntry(e)
}
// Error level formatted message.
func (l *logger) Error(v ...interface{}) {
e := newEntry(ErrorLevel, fmt.Sprint(v...), nil, skipLevel)
l.HandleEntry(e)
}
// Panic logs an Panic level formatted message and then panics
func (l *logger) Panic(v ...interface{}) {
s := fmt.Sprint(v...)
e := newEntry(PanicLevel, s, nil, skipLevel)
l.HandleEntry(e)
panic(s)
}
// Alert logs an Alert level formatted message and then panics
func (l *logger) Alert(v ...interface{}) {
s := fmt.Sprint(v...)
e := newEntry(AlertLevel, s, nil, skipLevel)
l.HandleEntry(e)
}
// Fatal level formatted message, followed by an exit.
func (l *logger) Fatal(v ...interface{}) {
e := newEntry(FatalLevel, fmt.Sprint(v...), nil, skipLevel)
l.HandleEntry(e)
exitFunc(1)
}
// Debugf level formatted message.
func (l *logger) Debugf(msg string, v ...interface{}) {
e := newEntry(DebugLevel, fmt.Sprintf(msg, v...), nil, skipLevel)
l.HandleEntry(e)
}
// Tracef starts a trace & returns Traceable object to End + log
func (l *logger) Tracef(msg string, v ...interface{}) Traceable {
t := l.tracePool.Get().(*TraceEntry)
t.entry = newEntry(TraceLevel, fmt.Sprintf(msg, v...), make([]Field, 0), skipLevel)
t.start = time.Now().UTC()
return t
}
// Infof level formatted message.
func (l *logger) Infof(msg string, v ...interface{}) {
e := newEntry(InfoLevel, fmt.Sprintf(msg, v...), nil, skipLevel)
l.HandleEntry(e)
}
// Noticef level formatted message.
func (l *logger) Noticef(msg string, v ...interface{}) {
e := newEntry(NoticeLevel, fmt.Sprintf(msg, v...), nil, skipLevel)
l.HandleEntry(e)
}
// Warnf level formatted message.
func (l *logger) Warnf(msg string, v ...interface{}) {
e := newEntry(WarnLevel, fmt.Sprintf(msg, v...), nil, skipLevel)
l.HandleEntry(e)
}
// Errorf level formatted message.
func (l *logger) Errorf(msg string, v ...interface{}) {
e := newEntry(ErrorLevel, fmt.Sprintf(msg, v...), nil, skipLevel)
l.HandleEntry(e)
}
// Panicf logs an Panic level formatted message and then panics
func (l *logger) Panicf(msg string, v ...interface{}) {
s := fmt.Sprintf(msg, v...)
e := newEntry(PanicLevel, s, nil, skipLevel)
l.HandleEntry(e)
panic(s)
}
// Alertf logs an Alert level formatted message and then panics
func (l *logger) Alertf(msg string, v ...interface{}) {
s := fmt.Sprintf(msg, v...)
e := newEntry(AlertLevel, s, nil, skipLevel)
l.HandleEntry(e)
}
// Fatalf level formatted message, followed by an exit.
func (l *logger) Fatalf(msg string, v ...interface{}) {
e := newEntry(FatalLevel, fmt.Sprintf(msg, v...), nil, skipLevel)
l.HandleEntry(e)
exitFunc(1)
}
// F creates a new field key + value entry
func (l *logger) F(key string, value interface{}) Field {
fld := Logger.fieldPool.Get().(Field)
fld.Key = key
fld.Value = value
return fld
}
// WithFields returns a log Entry with fields set
func (l *logger) WithFields(fields ...Field) LeveledLogger {
return newEntry(InfoLevel, "", fields, skipLevel)
}
// StackTrace creates a new log Entry with pre-populated field with stack trace.
func (l *logger) StackTrace() LeveledLogger {
trace := make([]byte, 1<<16)
n := runtime.Stack(trace, true)
if n > stackTraceLimit {
n = stackTraceLimit
}
return newEntry(DebugLevel, "", []Field{F("stack trace", string(trace[:n])+"\n")}, skipLevel)
}
func (l *logger) HandleEntry(e *Entry) {
// gather info if WarnLevel, ErrorLevel, PanicLevel, AlertLevel, FatalLevel or
// gathering info for all levels, but only if no line info already exists; we could
// be doing central logging.
if e.Line == 0 && l.callerInfoLevels[e.Level] {
_, e.File, e.Line, _ = runtime.Caller(e.calldepth)
}
// ---------
// |----------> | console |
// Addding this check for when you are doing centralized logging | ---------
// i.e. ----------------- ----------------- ------------- --------
// | app log handler | -- json --> | central log app | -- unmarshal json to Entry -> | log handler | --> | syslog |
// ----------------- ----------------- ------------- --------
// | ---------
// |----------> | DataDog |
// ---------
if e.wg == nil {
e.wg = new(sync.WaitGroup)
}
channels, ok := l.channels[e.Level]
if ok {
e.wg.Add(len(channels))
for _, ch := range channels {
ch <- e
}
e.wg.Wait()
}
// reclaim entry + fields
for _, f := range e.Fields {
l.fieldPool.Put(f)
}
l.entryPool.Put(e)
}
// RegisterHandler adds a new Log Handler and specifies what log levels
// the handler will be passed log entries for
func (l *logger) RegisterHandler(handler Handler, levels ...Level) {
ch := handler.Run()
for _, level := range levels {
channels, ok := l.channels[level]
if !ok {
channels = make(HandlerChannels, 0)
}
l.channels[level] = append(channels, ch)
}
}
// RegisterDurationFunc registers a custom duration function for Trace events
func (l *logger) RegisterDurationFunc(fn DurationFormatFunc) {
l.durationFunc = fn
}
// SetTimeFormat sets the time format used for Trace events
func (l *logger) SetTimeFormat(format string) {
l.timeFormat = format
}
// SetApplicationID tells the logger to set a constant application key
// that will be set on all log Entry objects. log does not care what it is,
// the application name, app name + hostname.... that's up to you
// it is needed by many logging platforms for separating logs by application
// and even by application server in a distributed app.
func (l *logger) SetApplicationID(id string) {
l.appID = id
}
// SetCallerInfoLevels tells the logger to gather and set file and line number
// information on Entry objects for the provided log levels.
// By defaut all but TraceLevel, InfoLevel and NoticeLevel are set to gather information.
func (l *logger) SetCallerInfoLevels(levels ...Level) {
for i := 0; i < len(l.callerInfoLevels); i++ {
l.callerInfoLevels[i] = false
}
for i := 0; i < len(levels); i++ {
l.callerInfoLevels[int(levels[i])] = true
}
}
// SetCallerSkipDiff adds the provided diff to the caller SkipLevel values.
// This is used when wrapping this library, you can set ths to increase the
// skip values passed to Caller that retrieves the file + line number info.
func (l *logger) SetCallerSkipDiff(diff uint8) {
skipLevel += int(diff)
}
// HasHandlers returns if any handlers have been registered.
func (l *logger) HasHandlers() bool {
return len(l.channels) != 0
}
func (l *logger) getApplicationID() string {
return l.appID
}