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