blob: cc85d3aab49e2803604b09a03cf45e4f3e51179e [file] [log] [blame]
Jonathan Hartf86817b2018-08-17 10:35:54 -07001package logrus
2
3import (
4 "bytes"
5 "fmt"
6 "os"
Matteo Scandolo1f49bf52018-11-20 13:56:45 -08007 "reflect"
8 "runtime"
9 "strings"
Jonathan Hartf86817b2018-08-17 10:35:54 -070010 "sync"
11 "time"
12)
13
Matteo Scandolo1f49bf52018-11-20 13:56:45 -080014var (
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)
Jonathan Hartf86817b2018-08-17 10:35:54 -070031
32func init() {
33 bufferPool = &sync.Pool{
34 New: func() interface{} {
35 return new(bytes.Buffer)
36 },
37 }
Matteo Scandolo1f49bf52018-11-20 13:56:45 -080038
39 // start at the bottom of the stack before the package-name cache is primed
40 minimumCallerDepth = 1
Jonathan Hartf86817b2018-08-17 10:35:54 -070041}
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
Matteo Scandolo1f49bf52018-11-20 13:56:45 -080047// 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.
Jonathan Hartf86817b2018-08-17 10:35:54 -070050type 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
Matteo Scandolo1f49bf52018-11-20 13:56:45 -080059 // 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.
Jonathan Hartf86817b2018-08-17 10:35:54 -070061 Level Level
62
Matteo Scandolo1f49bf52018-11-20 13:56:45 -080063 // Calling method, with package name
64 Caller *runtime.Frame
65
66 // Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic
Jonathan Hartf86817b2018-08-17 10:35:54 -070067 Message string
68
Matteo Scandolo1f49bf52018-11-20 13:56:45 -080069 // When formatter is called in entry.log(), a Buffer may be set to entry
Jonathan Hartf86817b2018-08-17 10:35:54 -070070 Buffer *bytes.Buffer
Matteo Scandolo1f49bf52018-11-20 13:56:45 -080071
72 // err may contain a field formatting error
73 err string
Jonathan Hartf86817b2018-08-17 10:35:54 -070074}
75
76func NewEntry(logger *Logger) *Entry {
77 return &Entry{
78 Logger: logger,
Matteo Scandolo1f49bf52018-11-20 13:56:45 -080079 // Default is three fields, plus one optional. Give a little extra room.
80 Data: make(Fields, 6),
Jonathan Hartf86817b2018-08-17 10:35:54 -070081 }
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 }
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800111 var field_err string
Jonathan Hartf86817b2018-08-17 10:35:54 -0700112 for k, v := range fields {
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800113 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 }
Jonathan Hartf86817b2018-08-17 10:35:54 -0700121 }
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800122 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
Jonathan Hartf86817b2018-08-17 10:35:54 -0700179}
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
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800185
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
Jonathan Hartf86817b2018-08-17 10:35:54 -0700195 entry.Level = level
196 entry.Message = msg
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800197 if entry.Logger.ReportCaller {
198 entry.Caller = getCaller()
Jonathan Hartf86817b2018-08-17 10:35:54 -0700199 }
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800200
201 entry.fireHooks()
202
Jonathan Hartf86817b2018-08-17 10:35:54 -0700203 buffer = bufferPool.Get().(*bytes.Buffer)
204 buffer.Reset()
205 defer bufferPool.Put(buffer)
206 entry.Buffer = buffer
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800207
208 entry.write()
209
Jonathan Hartf86817b2018-08-17 10:35:54 -0700210 entry.Buffer = nil
Jonathan Hartf86817b2018-08-17 10:35:54 -0700211
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
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800220func (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
Jonathan Hartf86817b2018-08-17 10:35:54 -0700249func (entry *Entry) Debug(args ...interface{}) {
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800250 if entry.Logger.IsLevelEnabled(DebugLevel) {
Jonathan Hartf86817b2018-08-17 10:35:54 -0700251 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{}) {
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800260 if entry.Logger.IsLevelEnabled(InfoLevel) {
Jonathan Hartf86817b2018-08-17 10:35:54 -0700261 entry.log(InfoLevel, fmt.Sprint(args...))
262 }
263}
264
265func (entry *Entry) Warn(args ...interface{}) {
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800266 if entry.Logger.IsLevelEnabled(WarnLevel) {
Jonathan Hartf86817b2018-08-17 10:35:54 -0700267 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{}) {
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800276 if entry.Logger.IsLevelEnabled(ErrorLevel) {
Jonathan Hartf86817b2018-08-17 10:35:54 -0700277 entry.log(ErrorLevel, fmt.Sprint(args...))
278 }
279}
280
281func (entry *Entry) Fatal(args ...interface{}) {
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800282 if entry.Logger.IsLevelEnabled(FatalLevel) {
Jonathan Hartf86817b2018-08-17 10:35:54 -0700283 entry.log(FatalLevel, fmt.Sprint(args...))
284 }
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800285 entry.Logger.Exit(1)
Jonathan Hartf86817b2018-08-17 10:35:54 -0700286}
287
288func (entry *Entry) Panic(args ...interface{}) {
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800289 if entry.Logger.IsLevelEnabled(PanicLevel) {
Jonathan Hartf86817b2018-08-17 10:35:54 -0700290 entry.log(PanicLevel, fmt.Sprint(args...))
291 }
292 panic(fmt.Sprint(args...))
293}
294
295// Entry Printf family functions
296
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800297func (entry *Entry) Tracef(format string, args ...interface{}) {
298 if entry.Logger.IsLevelEnabled(TraceLevel) {
299 entry.Trace(fmt.Sprintf(format, args...))
300 }
301}
302
Jonathan Hartf86817b2018-08-17 10:35:54 -0700303func (entry *Entry) Debugf(format string, args ...interface{}) {
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800304 if entry.Logger.IsLevelEnabled(DebugLevel) {
Jonathan Hartf86817b2018-08-17 10:35:54 -0700305 entry.Debug(fmt.Sprintf(format, args...))
306 }
307}
308
309func (entry *Entry) Infof(format string, args ...interface{}) {
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800310 if entry.Logger.IsLevelEnabled(InfoLevel) {
Jonathan Hartf86817b2018-08-17 10:35:54 -0700311 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{}) {
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800320 if entry.Logger.IsLevelEnabled(WarnLevel) {
Jonathan Hartf86817b2018-08-17 10:35:54 -0700321 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{}) {
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800330 if entry.Logger.IsLevelEnabled(ErrorLevel) {
Jonathan Hartf86817b2018-08-17 10:35:54 -0700331 entry.Error(fmt.Sprintf(format, args...))
332 }
333}
334
335func (entry *Entry) Fatalf(format string, args ...interface{}) {
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800336 if entry.Logger.IsLevelEnabled(FatalLevel) {
Jonathan Hartf86817b2018-08-17 10:35:54 -0700337 entry.Fatal(fmt.Sprintf(format, args...))
338 }
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800339 entry.Logger.Exit(1)
Jonathan Hartf86817b2018-08-17 10:35:54 -0700340}
341
342func (entry *Entry) Panicf(format string, args ...interface{}) {
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800343 if entry.Logger.IsLevelEnabled(PanicLevel) {
Jonathan Hartf86817b2018-08-17 10:35:54 -0700344 entry.Panic(fmt.Sprintf(format, args...))
345 }
346}
347
348// Entry Println family functions
349
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800350func (entry *Entry) Traceln(args ...interface{}) {
351 if entry.Logger.IsLevelEnabled(TraceLevel) {
352 entry.Trace(entry.sprintlnn(args...))
353 }
354}
355
Jonathan Hartf86817b2018-08-17 10:35:54 -0700356func (entry *Entry) Debugln(args ...interface{}) {
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800357 if entry.Logger.IsLevelEnabled(DebugLevel) {
Jonathan Hartf86817b2018-08-17 10:35:54 -0700358 entry.Debug(entry.sprintlnn(args...))
359 }
360}
361
362func (entry *Entry) Infoln(args ...interface{}) {
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800363 if entry.Logger.IsLevelEnabled(InfoLevel) {
Jonathan Hartf86817b2018-08-17 10:35:54 -0700364 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{}) {
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800373 if entry.Logger.IsLevelEnabled(WarnLevel) {
Jonathan Hartf86817b2018-08-17 10:35:54 -0700374 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{}) {
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800383 if entry.Logger.IsLevelEnabled(ErrorLevel) {
Jonathan Hartf86817b2018-08-17 10:35:54 -0700384 entry.Error(entry.sprintlnn(args...))
385 }
386}
387
388func (entry *Entry) Fatalln(args ...interface{}) {
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800389 if entry.Logger.IsLevelEnabled(FatalLevel) {
Jonathan Hartf86817b2018-08-17 10:35:54 -0700390 entry.Fatal(entry.sprintlnn(args...))
391 }
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800392 entry.Logger.Exit(1)
Jonathan Hartf86817b2018-08-17 10:35:54 -0700393}
394
395func (entry *Entry) Panicln(args ...interface{}) {
Matteo Scandolo1f49bf52018-11-20 13:56:45 -0800396 if entry.Logger.IsLevelEnabled(PanicLevel) {
Jonathan Hartf86817b2018-08-17 10:35:54 -0700397 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}