1package 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
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	// 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
66	// Contains the context set by the user. Useful for hook processing etc.
67	Context context.Context
68
69	// 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
81func (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
89// 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
94// Returns the string representation from the reader and ultimately the
95// formatter.
96func (entry *Entry) String() (string, error) {
97	serialized, err := entry.Bytes()
98	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
110// 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
119// 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	}
130	fieldErr := entry.err
131	for k, v := range fields {
132		isErrField := false
133		if t := reflect.TypeOf(v); t != nil {
134			switch {
135			case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func:
136				isErrField = true
137			}
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
145			}
146		} else {
147			data[k] = v
148		}
149	}
150	return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context}
151}
152
153// Overrides the time of the Entry.
154func (entry *Entry) WithTime(t time.Time) *Entry {
155	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}
160}
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 {
180	// 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
197	// 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
202	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 {
207			return &f //nolint:scopelint
208		}
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
221func (entry *Entry) log(level Level, msg string) {
222	var buffer *bytes.Buffer
223
224	newEntry := entry.Dup()
225
226	if newEntry.Time.IsZero() {
227		newEntry.Time = time.Now()
228	}
229
230	newEntry.Level = level
231	newEntry.Message = msg
232
233	newEntry.Logger.mu.Lock()
234	reportCaller := newEntry.Logger.ReportCaller
235	newEntry.Logger.mu.Unlock()
236
237	if reportCaller {
238		newEntry.Caller = getCaller()
239	}
240
241	newEntry.fireHooks()
242
243	buffer = getBuffer()
244	defer func() {
245		newEntry.Buffer = nil
246		putBuffer(buffer)
247	}()
248	buffer.Reset()
249	newEntry.Buffer = buffer
250
251	newEntry.write()
252
253	newEntry.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(newEntry)
260	}
261}
262
263func (entry *Entry) fireHooks() {
264	var tmpHooks LevelHooks
265	entry.Logger.mu.Lock()
266	tmpHooks = make(LevelHooks, len(entry.Logger.Hooks))
267	for k, v := range entry.Logger.Hooks {
268		tmpHooks[k] = v
269	}
270	entry.Logger.mu.Unlock()
271
272	err := tmpHooks.Fire(entry.Level, entry)
273	if err != nil {
274		fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
275	}
276}
277
278func (entry *Entry) write() {
279	serialized, err := entry.Logger.Formatter.Format(entry)
280	if err != nil {
281		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
282		return
283	}
284	entry.Logger.mu.Lock()
285	defer entry.Logger.mu.Unlock()
286	if _, err := entry.Logger.Out.Write(serialized); err != nil {
287		fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
288	}
289}
290
291func (entry *Entry) Log(level Level, args ...interface{}) {
292	if entry.Logger.IsLevelEnabled(level) {
293		entry.log(level, fmt.Sprint(args...))
294	}
295}
296
297func (entry *Entry) Trace(args ...interface{}) {
298	entry.Log(TraceLevel, args...)
299}
300
301func (entry *Entry) Debug(args ...interface{}) {
302	entry.Log(DebugLevel, args...)
303}
304
305func (entry *Entry) Print(args ...interface{}) {
306	entry.Info(args...)
307}
308
309func (entry *Entry) Info(args ...interface{}) {
310	entry.Log(InfoLevel, args...)
311}
312
313func (entry *Entry) Warn(args ...interface{}) {
314	entry.Log(WarnLevel, args...)
315}
316
317func (entry *Entry) Warning(args ...interface{}) {
318	entry.Warn(args...)
319}
320
321func (entry *Entry) Error(args ...interface{}) {
322	entry.Log(ErrorLevel, args...)
323}
324
325func (entry *Entry) Fatal(args ...interface{}) {
326	entry.Log(FatalLevel, args...)
327	entry.Logger.Exit(1)
328}
329
330func (entry *Entry) Panic(args ...interface{}) {
331	entry.Log(PanicLevel, args...)
332}
333
334// Entry Printf family functions
335
336func (entry *Entry) Logf(level Level, format string, args ...interface{}) {
337	if entry.Logger.IsLevelEnabled(level) {
338		entry.Log(level, fmt.Sprintf(format, args...))
339	}
340}
341
342func (entry *Entry) Tracef(format string, args ...interface{}) {
343	entry.Logf(TraceLevel, format, args...)
344}
345
346func (entry *Entry) Debugf(format string, args ...interface{}) {
347	entry.Logf(DebugLevel, format, args...)
348}
349
350func (entry *Entry) Infof(format string, args ...interface{}) {
351	entry.Logf(InfoLevel, format, args...)
352}
353
354func (entry *Entry) Printf(format string, args ...interface{}) {
355	entry.Infof(format, args...)
356}
357
358func (entry *Entry) Warnf(format string, args ...interface{}) {
359	entry.Logf(WarnLevel, format, args...)
360}
361
362func (entry *Entry) Warningf(format string, args ...interface{}) {
363	entry.Warnf(format, args...)
364}
365
366func (entry *Entry) Errorf(format string, args ...interface{}) {
367	entry.Logf(ErrorLevel, format, args...)
368}
369
370func (entry *Entry) Fatalf(format string, args ...interface{}) {
371	entry.Logf(FatalLevel, format, args...)
372	entry.Logger.Exit(1)
373}
374
375func (entry *Entry) Panicf(format string, args ...interface{}) {
376	entry.Logf(PanicLevel, format, args...)
377}
378
379// Entry Println family functions
380
381func (entry *Entry) Logln(level Level, args ...interface{}) {
382	if entry.Logger.IsLevelEnabled(level) {
383		entry.Log(level, entry.sprintlnn(args...))
384	}
385}
386
387func (entry *Entry) Traceln(args ...interface{}) {
388	entry.Logln(TraceLevel, args...)
389}
390
391func (entry *Entry) Debugln(args ...interface{}) {
392	entry.Logln(DebugLevel, args...)
393}
394
395func (entry *Entry) Infoln(args ...interface{}) {
396	entry.Logln(InfoLevel, args...)
397}
398
399func (entry *Entry) Println(args ...interface{}) {
400	entry.Infoln(args...)
401}
402
403func (entry *Entry) Warnln(args ...interface{}) {
404	entry.Logln(WarnLevel, args...)
405}
406
407func (entry *Entry) Warningln(args ...interface{}) {
408	entry.Warnln(args...)
409}
410
411func (entry *Entry) Errorln(args ...interface{}) {
412	entry.Logln(ErrorLevel, args...)
413}
414
415func (entry *Entry) Fatalln(args ...interface{}) {
416	entry.Logln(FatalLevel, args...)
417	entry.Logger.Exit(1)
418}
419
420func (entry *Entry) Panicln(args ...interface{}) {
421	entry.Logln(PanicLevel, args...)
422}
423
424// Sprintlnn => Sprint no newline. This is to get the behavior of how
425// fmt.Sprintln where spaces are always added between operands, regardless of
426// their type. Instead of vendoring the Sprintln implementation to spare a
427// string allocation, we do the simplest thing.
428func (entry *Entry) sprintlnn(args ...interface{}) string {
429	msg := fmt.Sprintln(args...)
430	return msg[:len(msg)-1]
431}
432