1package logrus
2
3import (
4	"bytes"
5	"fmt"
6	"os"
7	"reflect"
8	"runtime"
9	"strings"
10	"sync"
11	"time"
12)
13
14var (
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)
31
32func init() {
33	bufferPool = &sync.Pool{
34		New: func() interface{} {
35			return new(bytes.Buffer)
36		},
37	}
38
39	// start at the bottom of the stack before the package-name cache is primed
40	minimumCallerDepth = 1
41}
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
47// 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.
50type 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
59	// 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.
61	Level Level
62
63	// Calling method, with package name
64	Caller *runtime.Frame
65
66	// Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic
67	Message string
68
69	// When formatter is called in entry.log(), a Buffer may be set to entry
70	Buffer *bytes.Buffer
71
72	// err may contain a field formatting error
73	err string
74}
75
76func NewEntry(logger *Logger) *Entry {
77	return &Entry{
78		Logger: logger,
79		// Default is three fields, plus one optional.  Give a little extra room.
80		Data: make(Fields, 6),
81	}
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	}
111	var field_err string
112	for k, v := range fields {
113		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		}
121	}
122	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
179}
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
185
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
195	entry.Level = level
196	entry.Message = msg
197	if entry.Logger.ReportCaller {
198		entry.Caller = getCaller()
199	}
200
201	entry.fireHooks()
202
203	buffer = bufferPool.Get().(*bytes.Buffer)
204	buffer.Reset()
205	defer bufferPool.Put(buffer)
206	entry.Buffer = buffer
207
208	entry.write()
209
210	entry.Buffer = nil
211
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
220func (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
249func (entry *Entry) Debug(args ...interface{}) {
250	if entry.Logger.IsLevelEnabled(DebugLevel) {
251		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{}) {
260	if entry.Logger.IsLevelEnabled(InfoLevel) {
261		entry.log(InfoLevel, fmt.Sprint(args...))
262	}
263}
264
265func (entry *Entry) Warn(args ...interface{}) {
266	if entry.Logger.IsLevelEnabled(WarnLevel) {
267		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{}) {
276	if entry.Logger.IsLevelEnabled(ErrorLevel) {
277		entry.log(ErrorLevel, fmt.Sprint(args...))
278	}
279}
280
281func (entry *Entry) Fatal(args ...interface{}) {
282	if entry.Logger.IsLevelEnabled(FatalLevel) {
283		entry.log(FatalLevel, fmt.Sprint(args...))
284	}
285	entry.Logger.Exit(1)
286}
287
288func (entry *Entry) Panic(args ...interface{}) {
289	if entry.Logger.IsLevelEnabled(PanicLevel) {
290		entry.log(PanicLevel, fmt.Sprint(args...))
291	}
292	panic(fmt.Sprint(args...))
293}
294
295// Entry Printf family functions
296
297func (entry *Entry) Tracef(format string, args ...interface{}) {
298	if entry.Logger.IsLevelEnabled(TraceLevel) {
299		entry.Trace(fmt.Sprintf(format, args...))
300	}
301}
302
303func (entry *Entry) Debugf(format string, args ...interface{}) {
304	if entry.Logger.IsLevelEnabled(DebugLevel) {
305		entry.Debug(fmt.Sprintf(format, args...))
306	}
307}
308
309func (entry *Entry) Infof(format string, args ...interface{}) {
310	if entry.Logger.IsLevelEnabled(InfoLevel) {
311		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{}) {
320	if entry.Logger.IsLevelEnabled(WarnLevel) {
321		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{}) {
330	if entry.Logger.IsLevelEnabled(ErrorLevel) {
331		entry.Error(fmt.Sprintf(format, args...))
332	}
333}
334
335func (entry *Entry) Fatalf(format string, args ...interface{}) {
336	if entry.Logger.IsLevelEnabled(FatalLevel) {
337		entry.Fatal(fmt.Sprintf(format, args...))
338	}
339	entry.Logger.Exit(1)
340}
341
342func (entry *Entry) Panicf(format string, args ...interface{}) {
343	if entry.Logger.IsLevelEnabled(PanicLevel) {
344		entry.Panic(fmt.Sprintf(format, args...))
345	}
346}
347
348// Entry Println family functions
349
350func (entry *Entry) Traceln(args ...interface{}) {
351	if entry.Logger.IsLevelEnabled(TraceLevel) {
352		entry.Trace(entry.sprintlnn(args...))
353	}
354}
355
356func (entry *Entry) Debugln(args ...interface{}) {
357	if entry.Logger.IsLevelEnabled(DebugLevel) {
358		entry.Debug(entry.sprintlnn(args...))
359	}
360}
361
362func (entry *Entry) Infoln(args ...interface{}) {
363	if entry.Logger.IsLevelEnabled(InfoLevel) {
364		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{}) {
373	if entry.Logger.IsLevelEnabled(WarnLevel) {
374		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{}) {
383	if entry.Logger.IsLevelEnabled(ErrorLevel) {
384		entry.Error(entry.sprintlnn(args...))
385	}
386}
387
388func (entry *Entry) Fatalln(args ...interface{}) {
389	if entry.Logger.IsLevelEnabled(FatalLevel) {
390		entry.Fatal(entry.sprintlnn(args...))
391	}
392	entry.Logger.Exit(1)
393}
394
395func (entry *Entry) Panicln(args ...interface{}) {
396	if entry.Logger.IsLevelEnabled(PanicLevel) {
397		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}
409