blob: df6d188defe161e3f0fee8e7f6e4e345ec8c63f7 [file] [log] [blame]
khenaidooac637102019-01-14 15:44:34 -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 fieldErr := entry.err
112 for k, v := range fields {
113 isErrField := false
114 if t := reflect.TypeOf(v); t != nil {
115 switch t.Kind() {
116 case reflect.Func:
117 isErrField = true
118 case reflect.Ptr:
119 isErrField = t.Elem().Kind() == reflect.Func
120 }
121 }
122 if isErrField {
123 tmp := fmt.Sprintf("can not add field %q", k)
124 if fieldErr != "" {
125 fieldErr = entry.err + ", " + tmp
126 } else {
127 fieldErr = tmp
128 }
129 } else {
130 data[k] = v
131 }
132 }
133 return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr}
134}
135
136// Overrides the time of the Entry.
137func (entry *Entry) WithTime(t time.Time) *Entry {
138 return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t, err: entry.err}
139}
140
141// getPackageName reduces a fully qualified function name to the package name
142// There really ought to be to be a better way...
143func getPackageName(f string) string {
144 for {
145 lastPeriod := strings.LastIndex(f, ".")
146 lastSlash := strings.LastIndex(f, "/")
147 if lastPeriod > lastSlash {
148 f = f[:lastPeriod]
149 } else {
150 break
151 }
152 }
153
154 return f
155}
156
157// getCaller retrieves the name of the first non-logrus calling function
158func getCaller() *runtime.Frame {
159 // Restrict the lookback frames to avoid runaway lookups
160 pcs := make([]uintptr, maximumCallerDepth)
161 depth := runtime.Callers(minimumCallerDepth, pcs)
162 frames := runtime.CallersFrames(pcs[:depth])
163
164 // cache this package's fully-qualified name
165 callerInitOnce.Do(func() {
166 logrusPackage = getPackageName(runtime.FuncForPC(pcs[0]).Name())
167
168 // now that we have the cache, we can skip a minimum count of known-logrus functions
169 // XXX this is dubious, the number of frames may vary store an entry in a logger interface
170 minimumCallerDepth = knownLogrusFrames
171 })
172
173 for f, again := frames.Next(); again; f, again = frames.Next() {
174 pkg := getPackageName(f.Function)
175
176 // If the caller isn't part of this package, we're done
177 if pkg != logrusPackage {
178 return &f
179 }
180 }
181
182 // if we got here, we failed to find the caller's context
183 return nil
184}
185
186func (entry Entry) HasCaller() (has bool) {
187 return entry.Logger != nil &&
188 entry.Logger.ReportCaller &&
189 entry.Caller != nil
190}
191
192// This function is not declared with a pointer value because otherwise
193// race conditions will occur when using multiple goroutines
194func (entry Entry) log(level Level, msg string) {
195 var buffer *bytes.Buffer
196
197 // Default to now, but allow users to override if they want.
198 //
199 // We don't have to worry about polluting future calls to Entry#log()
200 // with this assignment because this function is declared with a
201 // non-pointer receiver.
202 if entry.Time.IsZero() {
203 entry.Time = time.Now()
204 }
205
206 entry.Level = level
207 entry.Message = msg
208 if entry.Logger.ReportCaller {
209 entry.Caller = getCaller()
210 }
211
212 entry.fireHooks()
213
214 buffer = bufferPool.Get().(*bytes.Buffer)
215 buffer.Reset()
216 defer bufferPool.Put(buffer)
217 entry.Buffer = buffer
218
219 entry.write()
220
221 entry.Buffer = nil
222
223 // To avoid Entry#log() returning a value that only would make sense for
224 // panic() to use in Entry#Panic(), we avoid the allocation by checking
225 // directly here.
226 if level <= PanicLevel {
227 panic(&entry)
228 }
229}
230
231func (entry *Entry) fireHooks() {
232 entry.Logger.mu.Lock()
233 defer entry.Logger.mu.Unlock()
234 err := entry.Logger.Hooks.Fire(entry.Level, entry)
235 if err != nil {
236 fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
237 }
238}
239
240func (entry *Entry) write() {
241 entry.Logger.mu.Lock()
242 defer entry.Logger.mu.Unlock()
243 serialized, err := entry.Logger.Formatter.Format(entry)
244 if err != nil {
245 fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
246 } else {
247 _, err = entry.Logger.Out.Write(serialized)
248 if err != nil {
249 fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
250 }
251 }
252}
253
254func (entry *Entry) Log(level Level, args ...interface{}) {
255 if entry.Logger.IsLevelEnabled(level) {
256 entry.log(level, fmt.Sprint(args...))
257 }
258}
259
260func (entry *Entry) Trace(args ...interface{}) {
261 entry.Log(TraceLevel, args...)
262}
263
264func (entry *Entry) Debug(args ...interface{}) {
265 entry.Log(DebugLevel, args...)
266}
267
268func (entry *Entry) Print(args ...interface{}) {
269 entry.Info(args...)
270}
271
272func (entry *Entry) Info(args ...interface{}) {
273 entry.Log(InfoLevel, args...)
274}
275
276func (entry *Entry) Warn(args ...interface{}) {
277 entry.Log(WarnLevel, args...)
278}
279
280func (entry *Entry) Warning(args ...interface{}) {
281 entry.Warn(args...)
282}
283
284func (entry *Entry) Error(args ...interface{}) {
285 entry.Log(ErrorLevel, args...)
286}
287
288func (entry *Entry) Fatal(args ...interface{}) {
289 entry.Log(FatalLevel, args...)
290 entry.Logger.Exit(1)
291}
292
293func (entry *Entry) Panic(args ...interface{}) {
294 entry.Log(PanicLevel, args...)
295 panic(fmt.Sprint(args...))
296}
297
298// Entry Printf family functions
299
300func (entry *Entry) Logf(level Level, format string, args ...interface{}) {
301 entry.Log(level, fmt.Sprintf(format, args...))
302}
303
304func (entry *Entry) Tracef(format string, args ...interface{}) {
305 entry.Logf(TraceLevel, format, args...)
306}
307
308func (entry *Entry) Debugf(format string, args ...interface{}) {
309 entry.Logf(DebugLevel, format, args...)
310}
311
312func (entry *Entry) Infof(format string, args ...interface{}) {
313 entry.Logf(InfoLevel, format, args...)
314}
315
316func (entry *Entry) Printf(format string, args ...interface{}) {
317 entry.Infof(format, args...)
318}
319
320func (entry *Entry) Warnf(format string, args ...interface{}) {
321 entry.Logf(WarnLevel, format, args...)
322}
323
324func (entry *Entry) Warningf(format string, args ...interface{}) {
325 entry.Warnf(format, args...)
326}
327
328func (entry *Entry) Errorf(format string, args ...interface{}) {
329 entry.Logf(ErrorLevel, format, args...)
330}
331
332func (entry *Entry) Fatalf(format string, args ...interface{}) {
333 entry.Logf(FatalLevel, format, args...)
334 entry.Logger.Exit(1)
335}
336
337func (entry *Entry) Panicf(format string, args ...interface{}) {
338 entry.Logf(PanicLevel, format, args...)
339}
340
341// Entry Println family functions
342
343func (entry *Entry) Logln(level Level, args ...interface{}) {
344 if entry.Logger.IsLevelEnabled(level) {
345 entry.Log(level, entry.sprintlnn(args...))
346 }
347}
348
349func (entry *Entry) Traceln(args ...interface{}) {
350 entry.Logln(TraceLevel, args...)
351}
352
353func (entry *Entry) Debugln(args ...interface{}) {
354 entry.Logln(DebugLevel, args...)
355}
356
357func (entry *Entry) Infoln(args ...interface{}) {
358 entry.Logln(InfoLevel, args...)
359}
360
361func (entry *Entry) Println(args ...interface{}) {
362 entry.Infoln(args...)
363}
364
365func (entry *Entry) Warnln(args ...interface{}) {
366 entry.Logln(WarnLevel, args...)
367}
368
369func (entry *Entry) Warningln(args ...interface{}) {
370 entry.Warnln(args...)
371}
372
373func (entry *Entry) Errorln(args ...interface{}) {
374 entry.Logln(ErrorLevel, args...)
375}
376
377func (entry *Entry) Fatalln(args ...interface{}) {
378 entry.Logln(FatalLevel, args...)
379 entry.Logger.Exit(1)
380}
381
382func (entry *Entry) Panicln(args ...interface{}) {
383 entry.Logln(PanicLevel, args...)
384}
385
386// Sprintlnn => Sprint no newline. This is to get the behavior of how
387// fmt.Sprintln where spaces are always added between operands, regardless of
388// their type. Instead of vendoring the Sprintln implementation to spare a
389// string allocation, we do the simplest thing.
390func (entry *Entry) sprintlnn(args ...interface{}) string {
391 msg := fmt.Sprintln(args...)
392 return msg[:len(msg)-1]
393}