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