66
|
1 package logrus
|
|
2
|
|
3 import (
|
|
4 "bytes"
|
|
5 "context"
|
|
6 "fmt"
|
|
7 "os"
|
|
8 "reflect"
|
|
9 "runtime"
|
|
10 "strings"
|
|
11 "sync"
|
|
12 "time"
|
|
13 )
|
|
14
|
|
15 var (
|
|
16
|
|
17 // qualified package name, cached at first use
|
|
18 logrusPackage string
|
|
19
|
|
20 // Positions in the call stack when tracing to report the calling method
|
|
21 minimumCallerDepth int
|
|
22
|
|
23 // Used for caller information initialisation
|
|
24 callerInitOnce sync.Once
|
|
25 )
|
|
26
|
|
27 const (
|
|
28 maximumCallerDepth int = 25
|
|
29 knownLogrusFrames int = 4
|
|
30 )
|
|
31
|
|
32 func init() {
|
|
33 // start at the bottom of the stack before the package-name cache is primed
|
|
34 minimumCallerDepth = 1
|
|
35 }
|
|
36
|
|
37 // Defines the key when adding errors using WithError.
|
|
38 var ErrorKey = "error"
|
|
39
|
|
40 // An entry is the final or intermediate Logrus logging entry. It contains all
|
|
41 // the fields passed with WithField{,s}. It's finally logged when Trace, Debug,
|
|
42 // Info, Warn, Error, Fatal or Panic is called on it. These objects can be
|
|
43 // reused and passed around as much as you wish to avoid field duplication.
|
|
44 type Entry struct {
|
|
45 Logger *Logger
|
|
46
|
|
47 // Contains all the fields set by the user.
|
|
48 Data Fields
|
|
49
|
|
50 // Time at which the log entry was created
|
|
51 Time time.Time
|
|
52
|
|
53 // Level the log entry was logged at: Trace, Debug, Info, Warn, Error, Fatal or Panic
|
|
54 // This field will be set on entry firing and the value will be equal to the one in Logger struct field.
|
|
55 Level Level
|
|
56
|
|
57 // Calling method, with package name
|
|
58 Caller *runtime.Frame
|
|
59
|
|
60 // Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic
|
|
61 Message string
|
|
62
|
|
63 // When formatter is called in entry.log(), a Buffer may be set to entry
|
|
64 Buffer *bytes.Buffer
|
|
65
|
|
66 // Contains the context set by the user. Useful for hook processing etc.
|
|
67 Context context.Context
|
|
68
|
|
69 // err may contain a field formatting error
|
|
70 err string
|
|
71 }
|
|
72
|
|
73 func NewEntry(logger *Logger) *Entry {
|
|
74 return &Entry{
|
|
75 Logger: logger,
|
|
76 // Default is three fields, plus one optional. Give a little extra room.
|
|
77 Data: make(Fields, 6),
|
|
78 }
|
|
79 }
|
|
80
|
|
81 func (entry *Entry) Dup() *Entry {
|
|
82 data := make(Fields, len(entry.Data))
|
|
83 for k, v := range entry.Data {
|
|
84 data[k] = v
|
|
85 }
|
|
86 return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: entry.Context, err: entry.err}
|
|
87 }
|
|
88
|
|
89 // Returns the bytes representation of this entry from the formatter.
|
|
90 func (entry *Entry) Bytes() ([]byte, error) {
|
|
91 return entry.Logger.Formatter.Format(entry)
|
|
92 }
|
|
93
|
|
94 // Returns the string representation from the reader and ultimately the
|
|
95 // formatter.
|
|
96 func (entry *Entry) String() (string, error) {
|
|
97 serialized, err := entry.Bytes()
|
|
98 if err != nil {
|
|
99 return "", err
|
|
100 }
|
|
101 str := string(serialized)
|
|
102 return str, nil
|
|
103 }
|
|
104
|
|
105 // Add an error as single field (using the key defined in ErrorKey) to the Entry.
|
|
106 func (entry *Entry) WithError(err error) *Entry {
|
|
107 return entry.WithField(ErrorKey, err)
|
|
108 }
|
|
109
|
|
110 // Add a context to the Entry.
|
|
111 func (entry *Entry) WithContext(ctx context.Context) *Entry {
|
|
112 dataCopy := make(Fields, len(entry.Data))
|
|
113 for k, v := range entry.Data {
|
|
114 dataCopy[k] = v
|
|
115 }
|
|
116 return &Entry{Logger: entry.Logger, Data: dataCopy, Time: entry.Time, err: entry.err, Context: ctx}
|
|
117 }
|
|
118
|
|
119 // Add a single field to the Entry.
|
|
120 func (entry *Entry) WithField(key string, value interface{}) *Entry {
|
|
121 return entry.WithFields(Fields{key: value})
|
|
122 }
|
|
123
|
|
124 // Add a map of fields to the Entry.
|
|
125 func (entry *Entry) WithFields(fields Fields) *Entry {
|
|
126 data := make(Fields, len(entry.Data)+len(fields))
|
|
127 for k, v := range entry.Data {
|
|
128 data[k] = v
|
|
129 }
|
|
130 fieldErr := entry.err
|
|
131 for k, v := range fields {
|
|
132 isErrField := false
|
|
133 if t := reflect.TypeOf(v); t != nil {
|
|
134 switch {
|
|
135 case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func:
|
|
136 isErrField = true
|
|
137 }
|
|
138 }
|
|
139 if isErrField {
|
|
140 tmp := fmt.Sprintf("can not add field %q", k)
|
|
141 if fieldErr != "" {
|
|
142 fieldErr = entry.err + ", " + tmp
|
|
143 } else {
|
|
144 fieldErr = tmp
|
|
145 }
|
|
146 } else {
|
|
147 data[k] = v
|
|
148 }
|
|
149 }
|
|
150 return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context}
|
|
151 }
|
|
152
|
|
153 // Overrides the time of the Entry.
|
|
154 func (entry *Entry) WithTime(t time.Time) *Entry {
|
|
155 dataCopy := make(Fields, len(entry.Data))
|
|
156 for k, v := range entry.Data {
|
|
157 dataCopy[k] = v
|
|
158 }
|
|
159 return &Entry{Logger: entry.Logger, Data: dataCopy, Time: t, err: entry.err, Context: entry.Context}
|
|
160 }
|
|
161
|
|
162 // getPackageName reduces a fully qualified function name to the package name
|
|
163 // There really ought to be to be a better way...
|
|
164 func getPackageName(f string) string {
|
|
165 for {
|
|
166 lastPeriod := strings.LastIndex(f, ".")
|
|
167 lastSlash := strings.LastIndex(f, "/")
|
|
168 if lastPeriod > lastSlash {
|
|
169 f = f[:lastPeriod]
|
|
170 } else {
|
|
171 break
|
|
172 }
|
|
173 }
|
|
174
|
|
175 return f
|
|
176 }
|
|
177
|
|
178 // getCaller retrieves the name of the first non-logrus calling function
|
|
179 func getCaller() *runtime.Frame {
|
|
180 // cache this package's fully-qualified name
|
|
181 callerInitOnce.Do(func() {
|
|
182 pcs := make([]uintptr, maximumCallerDepth)
|
|
183 _ = runtime.Callers(0, pcs)
|
|
184
|
|
185 // dynamic get the package name and the minimum caller depth
|
|
186 for i := 0; i < maximumCallerDepth; i++ {
|
|
187 funcName := runtime.FuncForPC(pcs[i]).Name()
|
|
188 if strings.Contains(funcName, "getCaller") {
|
|
189 logrusPackage = getPackageName(funcName)
|
|
190 break
|
|
191 }
|
|
192 }
|
|
193
|
|
194 minimumCallerDepth = knownLogrusFrames
|
|
195 })
|
|
196
|
|
197 // Restrict the lookback frames to avoid runaway lookups
|
|
198 pcs := make([]uintptr, maximumCallerDepth)
|
|
199 depth := runtime.Callers(minimumCallerDepth, pcs)
|
|
200 frames := runtime.CallersFrames(pcs[:depth])
|
|
201
|
|
202 for f, again := frames.Next(); again; f, again = frames.Next() {
|
|
203 pkg := getPackageName(f.Function)
|
|
204
|
|
205 // If the caller isn't part of this package, we're done
|
|
206 if pkg != logrusPackage {
|
|
207 return &f //nolint:scopelint
|
|
208 }
|
|
209 }
|
|
210
|
|
211 // if we got here, we failed to find the caller's context
|
|
212 return nil
|
|
213 }
|
|
214
|
|
215 func (entry Entry) HasCaller() (has bool) {
|
|
216 return entry.Logger != nil &&
|
|
217 entry.Logger.ReportCaller &&
|
|
218 entry.Caller != nil
|
|
219 }
|
|
220
|
|
221 func (entry *Entry) log(level Level, msg string) {
|
|
222 var buffer *bytes.Buffer
|
|
223
|
|
224 newEntry := entry.Dup()
|
|
225
|
|
226 if newEntry.Time.IsZero() {
|
|
227 newEntry.Time = time.Now()
|
|
228 }
|
|
229
|
|
230 newEntry.Level = level
|
|
231 newEntry.Message = msg
|
|
232
|
|
233 newEntry.Logger.mu.Lock()
|
|
234 reportCaller := newEntry.Logger.ReportCaller
|
|
235 bufPool := newEntry.getBufferPool()
|
|
236 newEntry.Logger.mu.Unlock()
|
|
237
|
|
238 if reportCaller {
|
|
239 newEntry.Caller = getCaller()
|
|
240 }
|
|
241
|
|
242 newEntry.fireHooks()
|
|
243 buffer = bufPool.Get()
|
|
244 defer func() {
|
|
245 newEntry.Buffer = nil
|
|
246 buffer.Reset()
|
|
247 bufPool.Put(buffer)
|
|
248 }()
|
|
249 buffer.Reset()
|
|
250 newEntry.Buffer = buffer
|
|
251
|
|
252 newEntry.write()
|
|
253
|
|
254 newEntry.Buffer = nil
|
|
255
|
|
256 // To avoid Entry#log() returning a value that only would make sense for
|
|
257 // panic() to use in Entry#Panic(), we avoid the allocation by checking
|
|
258 // directly here.
|
|
259 if level <= PanicLevel {
|
|
260 panic(newEntry)
|
|
261 }
|
|
262 }
|
|
263
|
|
264 func (entry *Entry) getBufferPool() (pool BufferPool) {
|
|
265 if entry.Logger.BufferPool != nil {
|
|
266 return entry.Logger.BufferPool
|
|
267 }
|
|
268 return bufferPool
|
|
269 }
|
|
270
|
|
271 func (entry *Entry) fireHooks() {
|
|
272 var tmpHooks LevelHooks
|
|
273 entry.Logger.mu.Lock()
|
|
274 tmpHooks = make(LevelHooks, len(entry.Logger.Hooks))
|
|
275 for k, v := range entry.Logger.Hooks {
|
|
276 tmpHooks[k] = v
|
|
277 }
|
|
278 entry.Logger.mu.Unlock()
|
|
279
|
|
280 err := tmpHooks.Fire(entry.Level, entry)
|
|
281 if err != nil {
|
|
282 fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
|
283 }
|
|
284 }
|
|
285
|
|
286 func (entry *Entry) write() {
|
|
287 entry.Logger.mu.Lock()
|
|
288 defer entry.Logger.mu.Unlock()
|
|
289 serialized, err := entry.Logger.Formatter.Format(entry)
|
|
290 if err != nil {
|
|
291 fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
|
292 return
|
|
293 }
|
|
294 if _, err := entry.Logger.Out.Write(serialized); err != nil {
|
|
295 fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
|
296 }
|
|
297 }
|
|
298
|
|
299 // Log will log a message at the level given as parameter.
|
|
300 // Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit.
|
|
301 // For this behaviour Entry.Panic or Entry.Fatal should be used instead.
|
|
302 func (entry *Entry) Log(level Level, args ...interface{}) {
|
|
303 if entry.Logger.IsLevelEnabled(level) {
|
|
304 entry.log(level, fmt.Sprint(args...))
|
|
305 }
|
|
306 }
|
|
307
|
|
308 func (entry *Entry) Trace(args ...interface{}) {
|
|
309 entry.Log(TraceLevel, args...)
|
|
310 }
|
|
311
|
|
312 func (entry *Entry) Debug(args ...interface{}) {
|
|
313 entry.Log(DebugLevel, args...)
|
|
314 }
|
|
315
|
|
316 func (entry *Entry) Print(args ...interface{}) {
|
|
317 entry.Info(args...)
|
|
318 }
|
|
319
|
|
320 func (entry *Entry) Info(args ...interface{}) {
|
|
321 entry.Log(InfoLevel, args...)
|
|
322 }
|
|
323
|
|
324 func (entry *Entry) Warn(args ...interface{}) {
|
|
325 entry.Log(WarnLevel, args...)
|
|
326 }
|
|
327
|
|
328 func (entry *Entry) Warning(args ...interface{}) {
|
|
329 entry.Warn(args...)
|
|
330 }
|
|
331
|
|
332 func (entry *Entry) Error(args ...interface{}) {
|
|
333 entry.Log(ErrorLevel, args...)
|
|
334 }
|
|
335
|
|
336 func (entry *Entry) Fatal(args ...interface{}) {
|
|
337 entry.Log(FatalLevel, args...)
|
|
338 entry.Logger.Exit(1)
|
|
339 }
|
|
340
|
|
341 func (entry *Entry) Panic(args ...interface{}) {
|
|
342 entry.Log(PanicLevel, args...)
|
|
343 }
|
|
344
|
|
345 // Entry Printf family functions
|
|
346
|
|
347 func (entry *Entry) Logf(level Level, format string, args ...interface{}) {
|
|
348 if entry.Logger.IsLevelEnabled(level) {
|
|
349 entry.Log(level, fmt.Sprintf(format, args...))
|
|
350 }
|
|
351 }
|
|
352
|
|
353 func (entry *Entry) Tracef(format string, args ...interface{}) {
|
|
354 entry.Logf(TraceLevel, format, args...)
|
|
355 }
|
|
356
|
|
357 func (entry *Entry) Debugf(format string, args ...interface{}) {
|
|
358 entry.Logf(DebugLevel, format, args...)
|
|
359 }
|
|
360
|
|
361 func (entry *Entry) Infof(format string, args ...interface{}) {
|
|
362 entry.Logf(InfoLevel, format, args...)
|
|
363 }
|
|
364
|
|
365 func (entry *Entry) Printf(format string, args ...interface{}) {
|
|
366 entry.Infof(format, args...)
|
|
367 }
|
|
368
|
|
369 func (entry *Entry) Warnf(format string, args ...interface{}) {
|
|
370 entry.Logf(WarnLevel, format, args...)
|
|
371 }
|
|
372
|
|
373 func (entry *Entry) Warningf(format string, args ...interface{}) {
|
|
374 entry.Warnf(format, args...)
|
|
375 }
|
|
376
|
|
377 func (entry *Entry) Errorf(format string, args ...interface{}) {
|
|
378 entry.Logf(ErrorLevel, format, args...)
|
|
379 }
|
|
380
|
|
381 func (entry *Entry) Fatalf(format string, args ...interface{}) {
|
|
382 entry.Logf(FatalLevel, format, args...)
|
|
383 entry.Logger.Exit(1)
|
|
384 }
|
|
385
|
|
386 func (entry *Entry) Panicf(format string, args ...interface{}) {
|
|
387 entry.Logf(PanicLevel, format, args...)
|
|
388 }
|
|
389
|
|
390 // Entry Println family functions
|
|
391
|
|
392 func (entry *Entry) Logln(level Level, args ...interface{}) {
|
|
393 if entry.Logger.IsLevelEnabled(level) {
|
|
394 entry.Log(level, entry.sprintlnn(args...))
|
|
395 }
|
|
396 }
|
|
397
|
|
398 func (entry *Entry) Traceln(args ...interface{}) {
|
|
399 entry.Logln(TraceLevel, args...)
|
|
400 }
|
|
401
|
|
402 func (entry *Entry) Debugln(args ...interface{}) {
|
|
403 entry.Logln(DebugLevel, args...)
|
|
404 }
|
|
405
|
|
406 func (entry *Entry) Infoln(args ...interface{}) {
|
|
407 entry.Logln(InfoLevel, args...)
|
|
408 }
|
|
409
|
|
410 func (entry *Entry) Println(args ...interface{}) {
|
|
411 entry.Infoln(args...)
|
|
412 }
|
|
413
|
|
414 func (entry *Entry) Warnln(args ...interface{}) {
|
|
415 entry.Logln(WarnLevel, args...)
|
|
416 }
|
|
417
|
|
418 func (entry *Entry) Warningln(args ...interface{}) {
|
|
419 entry.Warnln(args...)
|
|
420 }
|
|
421
|
|
422 func (entry *Entry) Errorln(args ...interface{}) {
|
|
423 entry.Logln(ErrorLevel, args...)
|
|
424 }
|
|
425
|
|
426 func (entry *Entry) Fatalln(args ...interface{}) {
|
|
427 entry.Logln(FatalLevel, args...)
|
|
428 entry.Logger.Exit(1)
|
|
429 }
|
|
430
|
|
431 func (entry *Entry) Panicln(args ...interface{}) {
|
|
432 entry.Logln(PanicLevel, args...)
|
|
433 }
|
|
434
|
|
435 // Sprintlnn => Sprint no newline. This is to get the behavior of how
|
|
436 // fmt.Sprintln where spaces are always added between operands, regardless of
|
|
437 // their type. Instead of vendoring the Sprintln implementation to spare a
|
|
438 // string allocation, we do the simplest thing.
|
|
439 func (entry *Entry) sprintlnn(args ...interface{}) string {
|
|
440 msg := fmt.Sprintln(args...)
|
|
441 return msg[:len(msg)-1]
|
|
442 }
|