blob: f6e062a3466c5ab9ad1b302b70d5a052a8300191 [file] [log] [blame]
khenaidoo59ce9dd2019-11-11 13:05:32 -05001package logrus
2
3import (
4 "bytes"
khenaidoo26721882021-08-11 17:42:52 -04005 "context"
khenaidoo59ce9dd2019-11-11 13:05:32 -05006 "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
khenaidoo26721882021-08-11 17:42:52 -040073 // Contains the context set by the user. Useful for hook processing etc.
74 Context context.Context
75
khenaidoo59ce9dd2019-11-11 13:05:32 -050076 // 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
khenaidoo26721882021-08-11 17:42:52 -040088// 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
khenaidoo59ce9dd2019-11-11 13:05:32 -050093// Returns the string representation from the reader and ultimately the
94// formatter.
95func (entry *Entry) String() (string, error) {
khenaidoo26721882021-08-11 17:42:52 -040096 serialized, err := entry.Bytes()
khenaidoo59ce9dd2019-11-11 13:05:32 -050097 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
khenaidoo26721882021-08-11 17:42:52 -0400109// Add a context to the Entry.
110func (entry *Entry) WithContext(ctx context.Context) *Entry {
111 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}
116}
117
khenaidoo59ce9dd2019-11-11 13:05:32 -0500118// 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 {
125 data := make(Fields, len(entry.Data)+len(fields))
126 for k, v := range entry.Data {
127 data[k] = v
128 }
khenaidoo26721882021-08-11 17:42:52 -0400129 fieldErr := entry.err
khenaidoo59ce9dd2019-11-11 13:05:32 -0500130 for k, v := range fields {
khenaidoo26721882021-08-11 17:42:52 -0400131 isErrField := false
132 if t := reflect.TypeOf(v); t != nil {
133 switch t.Kind() {
134 case reflect.Func:
135 isErrField = true
136 case reflect.Ptr:
137 isErrField = t.Elem().Kind() == reflect.Func
138 }
139 }
140 if isErrField {
141 tmp := fmt.Sprintf("can not add field %q", k)
142 if fieldErr != "" {
143 fieldErr = entry.err + ", " + tmp
144 } else {
145 fieldErr = tmp
khenaidoo59ce9dd2019-11-11 13:05:32 -0500146 }
147 } else {
148 data[k] = v
149 }
150 }
khenaidoo26721882021-08-11 17:42:52 -0400151 return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context}
khenaidoo59ce9dd2019-11-11 13:05:32 -0500152}
153
154// Overrides the time of the Entry.
155func (entry *Entry) WithTime(t time.Time) *Entry {
khenaidoo26721882021-08-11 17:42:52 -0400156 dataCopy := make(Fields, len(entry.Data))
157 for k, v := range entry.Data {
158 dataCopy[k] = v
159 }
160 return &Entry{Logger: entry.Logger, Data: dataCopy, Time: t, err: entry.err, Context: entry.Context}
khenaidoo59ce9dd2019-11-11 13:05:32 -0500161}
162
163// getPackageName reduces a fully qualified function name to the package name
164// There really ought to be to be a better way...
165func getPackageName(f string) string {
166 for {
167 lastPeriod := strings.LastIndex(f, ".")
168 lastSlash := strings.LastIndex(f, "/")
169 if lastPeriod > lastSlash {
170 f = f[:lastPeriod]
171 } else {
172 break
173 }
174 }
175
176 return f
177}
178
179// getCaller retrieves the name of the first non-logrus calling function
180func getCaller() *runtime.Frame {
khenaidoo26721882021-08-11 17:42:52 -0400181 // cache this package's fully-qualified name
182 callerInitOnce.Do(func() {
183 pcs := make([]uintptr, maximumCallerDepth)
184 _ = runtime.Callers(0, pcs)
185
186 // dynamic get the package name and the minimum caller depth
187 for i := 0; i < maximumCallerDepth; i++ {
188 funcName := runtime.FuncForPC(pcs[i]).Name()
189 if strings.Contains(funcName, "getCaller") {
190 logrusPackage = getPackageName(funcName)
191 break
192 }
193 }
194
195 minimumCallerDepth = knownLogrusFrames
196 })
197
khenaidoo59ce9dd2019-11-11 13:05:32 -0500198 // Restrict the lookback frames to avoid runaway lookups
199 pcs := make([]uintptr, maximumCallerDepth)
200 depth := runtime.Callers(minimumCallerDepth, pcs)
201 frames := runtime.CallersFrames(pcs[:depth])
202
khenaidoo59ce9dd2019-11-11 13:05:32 -0500203 for f, again := frames.Next(); again; f, again = frames.Next() {
204 pkg := getPackageName(f.Function)
205
206 // If the caller isn't part of this package, we're done
207 if pkg != logrusPackage {
khenaidoo26721882021-08-11 17:42:52 -0400208 return &f //nolint:scopelint
khenaidoo59ce9dd2019-11-11 13:05:32 -0500209 }
210 }
211
212 // if we got here, we failed to find the caller's context
213 return nil
214}
215
216func (entry Entry) HasCaller() (has bool) {
217 return entry.Logger != nil &&
218 entry.Logger.ReportCaller &&
219 entry.Caller != nil
220}
221
222// This function is not declared with a pointer value because otherwise
223// race conditions will occur when using multiple goroutines
224func (entry Entry) log(level Level, msg string) {
225 var buffer *bytes.Buffer
226
227 // Default to now, but allow users to override if they want.
228 //
229 // We don't have to worry about polluting future calls to Entry#log()
230 // with this assignment because this function is declared with a
231 // non-pointer receiver.
232 if entry.Time.IsZero() {
233 entry.Time = time.Now()
234 }
235
236 entry.Level = level
237 entry.Message = msg
khenaidoo26721882021-08-11 17:42:52 -0400238 entry.Logger.mu.Lock()
khenaidoo59ce9dd2019-11-11 13:05:32 -0500239 if entry.Logger.ReportCaller {
240 entry.Caller = getCaller()
241 }
khenaidoo26721882021-08-11 17:42:52 -0400242 entry.Logger.mu.Unlock()
khenaidoo59ce9dd2019-11-11 13:05:32 -0500243
244 entry.fireHooks()
245
246 buffer = bufferPool.Get().(*bytes.Buffer)
247 buffer.Reset()
248 defer bufferPool.Put(buffer)
249 entry.Buffer = buffer
250
251 entry.write()
252
253 entry.Buffer = nil
254
255 // To avoid Entry#log() returning a value that only would make sense for
256 // panic() to use in Entry#Panic(), we avoid the allocation by checking
257 // directly here.
258 if level <= PanicLevel {
259 panic(&entry)
260 }
261}
262
263func (entry *Entry) fireHooks() {
264 entry.Logger.mu.Lock()
265 defer entry.Logger.mu.Unlock()
266 err := entry.Logger.Hooks.Fire(entry.Level, entry)
267 if err != nil {
268 fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
269 }
270}
271
272func (entry *Entry) write() {
273 entry.Logger.mu.Lock()
274 defer entry.Logger.mu.Unlock()
275 serialized, err := entry.Logger.Formatter.Format(entry)
276 if err != nil {
277 fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
khenaidoo26721882021-08-11 17:42:52 -0400278 return
279 }
280 if _, err = entry.Logger.Out.Write(serialized); err != nil {
281 fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
282 }
283}
284
285func (entry *Entry) Log(level Level, args ...interface{}) {
286 if entry.Logger.IsLevelEnabled(level) {
287 entry.log(level, fmt.Sprint(args...))
khenaidoo59ce9dd2019-11-11 13:05:32 -0500288 }
289}
290
291func (entry *Entry) Trace(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400292 entry.Log(TraceLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500293}
294
295func (entry *Entry) Debug(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400296 entry.Log(DebugLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500297}
298
299func (entry *Entry) Print(args ...interface{}) {
300 entry.Info(args...)
301}
302
303func (entry *Entry) Info(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400304 entry.Log(InfoLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500305}
306
307func (entry *Entry) Warn(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400308 entry.Log(WarnLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500309}
310
311func (entry *Entry) Warning(args ...interface{}) {
312 entry.Warn(args...)
313}
314
315func (entry *Entry) Error(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400316 entry.Log(ErrorLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500317}
318
319func (entry *Entry) Fatal(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400320 entry.Log(FatalLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500321 entry.Logger.Exit(1)
322}
323
324func (entry *Entry) Panic(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400325 entry.Log(PanicLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500326 panic(fmt.Sprint(args...))
327}
328
329// Entry Printf family functions
330
khenaidoo26721882021-08-11 17:42:52 -0400331func (entry *Entry) Logf(level Level, format string, args ...interface{}) {
332 if entry.Logger.IsLevelEnabled(level) {
333 entry.Log(level, fmt.Sprintf(format, args...))
khenaidoo59ce9dd2019-11-11 13:05:32 -0500334 }
335}
336
khenaidoo26721882021-08-11 17:42:52 -0400337func (entry *Entry) Tracef(format string, args ...interface{}) {
338 entry.Logf(TraceLevel, format, args...)
339}
340
khenaidoo59ce9dd2019-11-11 13:05:32 -0500341func (entry *Entry) Debugf(format string, args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400342 entry.Logf(DebugLevel, format, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500343}
344
345func (entry *Entry) Infof(format string, args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400346 entry.Logf(InfoLevel, format, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500347}
348
349func (entry *Entry) Printf(format string, args ...interface{}) {
350 entry.Infof(format, args...)
351}
352
353func (entry *Entry) Warnf(format string, args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400354 entry.Logf(WarnLevel, format, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500355}
356
357func (entry *Entry) Warningf(format string, args ...interface{}) {
358 entry.Warnf(format, args...)
359}
360
361func (entry *Entry) Errorf(format string, args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400362 entry.Logf(ErrorLevel, format, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500363}
364
365func (entry *Entry) Fatalf(format string, args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400366 entry.Logf(FatalLevel, format, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500367 entry.Logger.Exit(1)
368}
369
370func (entry *Entry) Panicf(format string, args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400371 entry.Logf(PanicLevel, format, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500372}
373
374// Entry Println family functions
375
khenaidoo26721882021-08-11 17:42:52 -0400376func (entry *Entry) Logln(level Level, args ...interface{}) {
377 if entry.Logger.IsLevelEnabled(level) {
378 entry.Log(level, entry.sprintlnn(args...))
khenaidoo59ce9dd2019-11-11 13:05:32 -0500379 }
380}
381
khenaidoo26721882021-08-11 17:42:52 -0400382func (entry *Entry) Traceln(args ...interface{}) {
383 entry.Logln(TraceLevel, args...)
384}
385
khenaidoo59ce9dd2019-11-11 13:05:32 -0500386func (entry *Entry) Debugln(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400387 entry.Logln(DebugLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500388}
389
390func (entry *Entry) Infoln(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400391 entry.Logln(InfoLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500392}
393
394func (entry *Entry) Println(args ...interface{}) {
395 entry.Infoln(args...)
396}
397
398func (entry *Entry) Warnln(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400399 entry.Logln(WarnLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500400}
401
402func (entry *Entry) Warningln(args ...interface{}) {
403 entry.Warnln(args...)
404}
405
406func (entry *Entry) Errorln(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400407 entry.Logln(ErrorLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500408}
409
410func (entry *Entry) Fatalln(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400411 entry.Logln(FatalLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500412 entry.Logger.Exit(1)
413}
414
415func (entry *Entry) Panicln(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400416 entry.Logln(PanicLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500417}
418
419// Sprintlnn => Sprint no newline. This is to get the behavior of how
420// fmt.Sprintln where spaces are always added between operands, regardless of
421// their type. Instead of vendoring the Sprintln implementation to spare a
422// string allocation, we do the simplest thing.
423func (entry *Entry) sprintlnn(args ...interface{}) string {
424 msg := fmt.Sprintln(args...)
425 return msg[:len(msg)-1]
426}