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