blob: 71cdbbc35d2143e25b4eb069413e3cd23e7ca957 [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 (
khenaidoo59ce9dd2019-11-11 13:05:32 -050016
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() {
khenaidoo59ce9dd2019-11-11 13:05:32 -050033 // start at the bottom of the stack before the package-name cache is primed
34 minimumCallerDepth = 1
35}
36
37// Defines the key when adding errors using WithError.
38var ErrorKey = "error"
39
40// An entry is the final or intermediate Logrus logging entry. It contains all
41// the fields passed with WithField{,s}. It's finally logged when Trace, Debug,
42// Info, Warn, Error, Fatal or Panic is called on it. These objects can be
43// reused and passed around as much as you wish to avoid field duplication.
44type Entry struct {
45 Logger *Logger
46
47 // Contains all the fields set by the user.
48 Data Fields
49
50 // Time at which the log entry was created
51 Time time.Time
52
53 // Level the log entry was logged at: Trace, Debug, Info, Warn, Error, Fatal or Panic
54 // This field will be set on entry firing and the value will be equal to the one in Logger struct field.
55 Level Level
56
57 // Calling method, with package name
58 Caller *runtime.Frame
59
60 // Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic
61 Message string
62
63 // When formatter is called in entry.log(), a Buffer may be set to entry
64 Buffer *bytes.Buffer
65
khenaidoo26721882021-08-11 17:42:52 -040066 // Contains the context set by the user. Useful for hook processing etc.
67 Context context.Context
68
khenaidoo59ce9dd2019-11-11 13:05:32 -050069 // err may contain a field formatting error
70 err string
71}
72
73func NewEntry(logger *Logger) *Entry {
74 return &Entry{
75 Logger: logger,
76 // Default is three fields, plus one optional. Give a little extra room.
77 Data: make(Fields, 6),
78 }
79}
80
Abhay Kumar40252eb2025-10-13 13:25:53 +000081func (entry *Entry) Dup() *Entry {
82 data := make(Fields, len(entry.Data))
83 for k, v := range entry.Data {
84 data[k] = v
85 }
86 return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: entry.Context, err: entry.err}
87}
88
khenaidoo26721882021-08-11 17:42:52 -040089// Returns the bytes representation of this entry from the formatter.
90func (entry *Entry) Bytes() ([]byte, error) {
91 return entry.Logger.Formatter.Format(entry)
92}
93
khenaidoo59ce9dd2019-11-11 13:05:32 -050094// Returns the string representation from the reader and ultimately the
95// formatter.
96func (entry *Entry) String() (string, error) {
khenaidoo26721882021-08-11 17:42:52 -040097 serialized, err := entry.Bytes()
khenaidoo59ce9dd2019-11-11 13:05:32 -050098 if err != nil {
99 return "", err
100 }
101 str := string(serialized)
102 return str, nil
103}
104
105// Add an error as single field (using the key defined in ErrorKey) to the Entry.
106func (entry *Entry) WithError(err error) *Entry {
107 return entry.WithField(ErrorKey, err)
108}
109
khenaidoo26721882021-08-11 17:42:52 -0400110// Add a context to the Entry.
111func (entry *Entry) WithContext(ctx context.Context) *Entry {
112 dataCopy := make(Fields, len(entry.Data))
113 for k, v := range entry.Data {
114 dataCopy[k] = v
115 }
116 return &Entry{Logger: entry.Logger, Data: dataCopy, Time: entry.Time, err: entry.err, Context: ctx}
117}
118
khenaidoo59ce9dd2019-11-11 13:05:32 -0500119// Add a single field to the Entry.
120func (entry *Entry) WithField(key string, value interface{}) *Entry {
121 return entry.WithFields(Fields{key: value})
122}
123
124// Add a map of fields to the Entry.
125func (entry *Entry) WithFields(fields Fields) *Entry {
126 data := make(Fields, len(entry.Data)+len(fields))
127 for k, v := range entry.Data {
128 data[k] = v
129 }
khenaidoo26721882021-08-11 17:42:52 -0400130 fieldErr := entry.err
khenaidoo59ce9dd2019-11-11 13:05:32 -0500131 for k, v := range fields {
khenaidoo26721882021-08-11 17:42:52 -0400132 isErrField := false
133 if t := reflect.TypeOf(v); t != nil {
Abhay Kumar40252eb2025-10-13 13:25:53 +0000134 switch {
135 case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func:
khenaidoo26721882021-08-11 17:42:52 -0400136 isErrField = true
khenaidoo26721882021-08-11 17:42:52 -0400137 }
138 }
139 if isErrField {
140 tmp := fmt.Sprintf("can not add field %q", k)
141 if fieldErr != "" {
142 fieldErr = entry.err + ", " + tmp
143 } else {
144 fieldErr = tmp
khenaidoo59ce9dd2019-11-11 13:05:32 -0500145 }
146 } else {
147 data[k] = v
148 }
149 }
khenaidoo26721882021-08-11 17:42:52 -0400150 return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context}
khenaidoo59ce9dd2019-11-11 13:05:32 -0500151}
152
153// Overrides the time of the Entry.
154func (entry *Entry) WithTime(t time.Time) *Entry {
khenaidoo26721882021-08-11 17:42:52 -0400155 dataCopy := make(Fields, len(entry.Data))
156 for k, v := range entry.Data {
157 dataCopy[k] = v
158 }
159 return &Entry{Logger: entry.Logger, Data: dataCopy, Time: t, err: entry.err, Context: entry.Context}
khenaidoo59ce9dd2019-11-11 13:05:32 -0500160}
161
162// getPackageName reduces a fully qualified function name to the package name
163// There really ought to be to be a better way...
164func getPackageName(f string) string {
165 for {
166 lastPeriod := strings.LastIndex(f, ".")
167 lastSlash := strings.LastIndex(f, "/")
168 if lastPeriod > lastSlash {
169 f = f[:lastPeriod]
170 } else {
171 break
172 }
173 }
174
175 return f
176}
177
178// getCaller retrieves the name of the first non-logrus calling function
179func getCaller() *runtime.Frame {
khenaidoo26721882021-08-11 17:42:52 -0400180 // cache this package's fully-qualified name
181 callerInitOnce.Do(func() {
182 pcs := make([]uintptr, maximumCallerDepth)
183 _ = runtime.Callers(0, pcs)
184
185 // dynamic get the package name and the minimum caller depth
186 for i := 0; i < maximumCallerDepth; i++ {
187 funcName := runtime.FuncForPC(pcs[i]).Name()
188 if strings.Contains(funcName, "getCaller") {
189 logrusPackage = getPackageName(funcName)
190 break
191 }
192 }
193
194 minimumCallerDepth = knownLogrusFrames
195 })
196
khenaidoo59ce9dd2019-11-11 13:05:32 -0500197 // Restrict the lookback frames to avoid runaway lookups
198 pcs := make([]uintptr, maximumCallerDepth)
199 depth := runtime.Callers(minimumCallerDepth, pcs)
200 frames := runtime.CallersFrames(pcs[:depth])
201
khenaidoo59ce9dd2019-11-11 13:05:32 -0500202 for f, again := frames.Next(); again; f, again = frames.Next() {
203 pkg := getPackageName(f.Function)
204
205 // If the caller isn't part of this package, we're done
206 if pkg != logrusPackage {
khenaidoo26721882021-08-11 17:42:52 -0400207 return &f //nolint:scopelint
khenaidoo59ce9dd2019-11-11 13:05:32 -0500208 }
209 }
210
211 // if we got here, we failed to find the caller's context
212 return nil
213}
214
215func (entry Entry) HasCaller() (has bool) {
216 return entry.Logger != nil &&
217 entry.Logger.ReportCaller &&
218 entry.Caller != nil
219}
220
Abhay Kumar40252eb2025-10-13 13:25:53 +0000221func (entry *Entry) log(level Level, msg string) {
khenaidoo59ce9dd2019-11-11 13:05:32 -0500222 var buffer *bytes.Buffer
223
Abhay Kumar40252eb2025-10-13 13:25:53 +0000224 newEntry := entry.Dup()
225
226 if newEntry.Time.IsZero() {
227 newEntry.Time = time.Now()
khenaidoo59ce9dd2019-11-11 13:05:32 -0500228 }
229
Abhay Kumar40252eb2025-10-13 13:25:53 +0000230 newEntry.Level = level
231 newEntry.Message = msg
232
233 newEntry.Logger.mu.Lock()
234 reportCaller := newEntry.Logger.ReportCaller
235 bufPool := newEntry.getBufferPool()
236 newEntry.Logger.mu.Unlock()
237
238 if reportCaller {
239 newEntry.Caller = getCaller()
khenaidoo59ce9dd2019-11-11 13:05:32 -0500240 }
241
Abhay Kumar40252eb2025-10-13 13:25:53 +0000242 newEntry.fireHooks()
243 buffer = bufPool.Get()
244 defer func() {
245 newEntry.Buffer = nil
246 buffer.Reset()
247 bufPool.Put(buffer)
248 }()
khenaidoo59ce9dd2019-11-11 13:05:32 -0500249 buffer.Reset()
Abhay Kumar40252eb2025-10-13 13:25:53 +0000250 newEntry.Buffer = buffer
khenaidoo59ce9dd2019-11-11 13:05:32 -0500251
Abhay Kumar40252eb2025-10-13 13:25:53 +0000252 newEntry.write()
khenaidoo59ce9dd2019-11-11 13:05:32 -0500253
Abhay Kumar40252eb2025-10-13 13:25:53 +0000254 newEntry.Buffer = nil
khenaidoo59ce9dd2019-11-11 13:05:32 -0500255
256 // To avoid Entry#log() returning a value that only would make sense for
257 // panic() to use in Entry#Panic(), we avoid the allocation by checking
258 // directly here.
259 if level <= PanicLevel {
Abhay Kumar40252eb2025-10-13 13:25:53 +0000260 panic(newEntry)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500261 }
262}
263
Abhay Kumar40252eb2025-10-13 13:25:53 +0000264func (entry *Entry) getBufferPool() (pool BufferPool) {
265 if entry.Logger.BufferPool != nil {
266 return entry.Logger.BufferPool
267 }
268 return bufferPool
269}
270
khenaidoo59ce9dd2019-11-11 13:05:32 -0500271func (entry *Entry) fireHooks() {
Abhay Kumar40252eb2025-10-13 13:25:53 +0000272 var tmpHooks LevelHooks
khenaidoo59ce9dd2019-11-11 13:05:32 -0500273 entry.Logger.mu.Lock()
Abhay Kumar40252eb2025-10-13 13:25:53 +0000274 tmpHooks = make(LevelHooks, len(entry.Logger.Hooks))
275 for k, v := range entry.Logger.Hooks {
276 tmpHooks[k] = v
277 }
278 entry.Logger.mu.Unlock()
279
280 err := tmpHooks.Fire(entry.Level, entry)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500281 if err != nil {
282 fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
283 }
284}
285
286func (entry *Entry) write() {
287 entry.Logger.mu.Lock()
288 defer entry.Logger.mu.Unlock()
289 serialized, err := entry.Logger.Formatter.Format(entry)
290 if err != nil {
291 fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
khenaidoo26721882021-08-11 17:42:52 -0400292 return
293 }
Abhay Kumar40252eb2025-10-13 13:25:53 +0000294 if _, err := entry.Logger.Out.Write(serialized); err != nil {
khenaidoo26721882021-08-11 17:42:52 -0400295 fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
296 }
297}
298
Abhay Kumar40252eb2025-10-13 13:25:53 +0000299// Log will log a message at the level given as parameter.
300// Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit.
301// For this behaviour Entry.Panic or Entry.Fatal should be used instead.
khenaidoo26721882021-08-11 17:42:52 -0400302func (entry *Entry) Log(level Level, args ...interface{}) {
303 if entry.Logger.IsLevelEnabled(level) {
304 entry.log(level, fmt.Sprint(args...))
khenaidoo59ce9dd2019-11-11 13:05:32 -0500305 }
306}
307
308func (entry *Entry) Trace(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400309 entry.Log(TraceLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500310}
311
312func (entry *Entry) Debug(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400313 entry.Log(DebugLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500314}
315
316func (entry *Entry) Print(args ...interface{}) {
317 entry.Info(args...)
318}
319
320func (entry *Entry) Info(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400321 entry.Log(InfoLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500322}
323
324func (entry *Entry) Warn(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400325 entry.Log(WarnLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500326}
327
328func (entry *Entry) Warning(args ...interface{}) {
329 entry.Warn(args...)
330}
331
332func (entry *Entry) Error(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400333 entry.Log(ErrorLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500334}
335
336func (entry *Entry) Fatal(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400337 entry.Log(FatalLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500338 entry.Logger.Exit(1)
339}
340
341func (entry *Entry) Panic(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400342 entry.Log(PanicLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500343}
344
345// Entry Printf family functions
346
khenaidoo26721882021-08-11 17:42:52 -0400347func (entry *Entry) Logf(level Level, format string, args ...interface{}) {
348 if entry.Logger.IsLevelEnabled(level) {
349 entry.Log(level, fmt.Sprintf(format, args...))
khenaidoo59ce9dd2019-11-11 13:05:32 -0500350 }
351}
352
khenaidoo26721882021-08-11 17:42:52 -0400353func (entry *Entry) Tracef(format string, args ...interface{}) {
354 entry.Logf(TraceLevel, format, args...)
355}
356
khenaidoo59ce9dd2019-11-11 13:05:32 -0500357func (entry *Entry) Debugf(format string, args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400358 entry.Logf(DebugLevel, format, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500359}
360
361func (entry *Entry) Infof(format string, args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400362 entry.Logf(InfoLevel, format, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500363}
364
365func (entry *Entry) Printf(format string, args ...interface{}) {
366 entry.Infof(format, args...)
367}
368
369func (entry *Entry) Warnf(format string, args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400370 entry.Logf(WarnLevel, format, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500371}
372
373func (entry *Entry) Warningf(format string, args ...interface{}) {
374 entry.Warnf(format, args...)
375}
376
377func (entry *Entry) Errorf(format string, args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400378 entry.Logf(ErrorLevel, format, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500379}
380
381func (entry *Entry) Fatalf(format string, args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400382 entry.Logf(FatalLevel, format, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500383 entry.Logger.Exit(1)
384}
385
386func (entry *Entry) Panicf(format string, args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400387 entry.Logf(PanicLevel, format, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500388}
389
390// Entry Println family functions
391
khenaidoo26721882021-08-11 17:42:52 -0400392func (entry *Entry) Logln(level Level, args ...interface{}) {
393 if entry.Logger.IsLevelEnabled(level) {
394 entry.Log(level, entry.sprintlnn(args...))
khenaidoo59ce9dd2019-11-11 13:05:32 -0500395 }
396}
397
khenaidoo26721882021-08-11 17:42:52 -0400398func (entry *Entry) Traceln(args ...interface{}) {
399 entry.Logln(TraceLevel, args...)
400}
401
khenaidoo59ce9dd2019-11-11 13:05:32 -0500402func (entry *Entry) Debugln(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400403 entry.Logln(DebugLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500404}
405
406func (entry *Entry) Infoln(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400407 entry.Logln(InfoLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500408}
409
410func (entry *Entry) Println(args ...interface{}) {
411 entry.Infoln(args...)
412}
413
414func (entry *Entry) Warnln(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400415 entry.Logln(WarnLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500416}
417
418func (entry *Entry) Warningln(args ...interface{}) {
419 entry.Warnln(args...)
420}
421
422func (entry *Entry) Errorln(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400423 entry.Logln(ErrorLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500424}
425
426func (entry *Entry) Fatalln(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400427 entry.Logln(FatalLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500428 entry.Logger.Exit(1)
429}
430
431func (entry *Entry) Panicln(args ...interface{}) {
khenaidoo26721882021-08-11 17:42:52 -0400432 entry.Logln(PanicLevel, args...)
khenaidoo59ce9dd2019-11-11 13:05:32 -0500433}
434
435// Sprintlnn => Sprint no newline. This is to get the behavior of how
436// fmt.Sprintln where spaces are always added between operands, regardless of
437// their type. Instead of vendoring the Sprintln implementation to spare a
438// string allocation, we do the simplest thing.
439func (entry *Entry) sprintlnn(args ...interface{}) string {
440 msg := fmt.Sprintln(args...)
441 return msg[:len(msg)-1]
442}