1package logrus
2
3import (
4	"io"
5	"os"
6	"sync"
7	"sync/atomic"
8	"time"
9)
10
11type Logger struct {
12	// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
13	// file, or leave it default which is `os.Stderr`. You can also set this to
14	// something more adventurous, such as logging to Kafka.
15	Out io.Writer
16	// Hooks for the logger instance. These allow firing events based on logging
17	// levels and log entries. For example, to send errors to an error tracking
18	// service, log to StatsD or dump the core on fatal errors.
19	Hooks LevelHooks
20	// All log entries pass through the formatter before logged to Out. The
21	// included formatters are `TextFormatter` and `JSONFormatter` for which
22	// TextFormatter is the default. In development (when a TTY is attached) it
23	// logs with colors, but to a file it wouldn't. You can easily implement your
24	// own that implements the `Formatter` interface, see the `README` or included
25	// formatters for examples.
26	Formatter Formatter
27
28	// Flag for whether to log caller info (off by default)
29	ReportCaller bool
30
31	// The logging level the logger should log at. This is typically (and defaults
32	// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
33	// logged.
34	Level Level
35	// Used to sync writing to the log. Locking is enabled by Default
36	mu MutexWrap
37	// Reusable empty entry
38	entryPool sync.Pool
39	// Function to exit the application, defaults to `os.Exit()`
40	ExitFunc exitFunc
41}
42
43type exitFunc func(int)
44
45type MutexWrap struct {
46	lock     sync.Mutex
47	disabled bool
48}
49
50func (mw *MutexWrap) Lock() {
51	if !mw.disabled {
52		mw.lock.Lock()
53	}
54}
55
56func (mw *MutexWrap) Unlock() {
57	if !mw.disabled {
58		mw.lock.Unlock()
59	}
60}
61
62func (mw *MutexWrap) Disable() {
63	mw.disabled = true
64}
65
66// Creates a new logger. Configuration should be set by changing `Formatter`,
67// `Out` and `Hooks` directly on the default logger instance. You can also just
68// instantiate your own:
69//
70//    var log = &Logger{
71//      Out: os.Stderr,
72//      Formatter: new(JSONFormatter),
73//      Hooks: make(LevelHooks),
74//      Level: logrus.DebugLevel,
75//    }
76//
77// It's recommended to make this a global instance called `log`.
78func New() *Logger {
79	return &Logger{
80		Out:          os.Stderr,
81		Formatter:    new(TextFormatter),
82		Hooks:        make(LevelHooks),
83		Level:        InfoLevel,
84		ExitFunc:     os.Exit,
85		ReportCaller: false,
86	}
87}
88
89func (logger *Logger) newEntry() *Entry {
90	entry, ok := logger.entryPool.Get().(*Entry)
91	if ok {
92		return entry
93	}
94	return NewEntry(logger)
95}
96
97func (logger *Logger) releaseEntry(entry *Entry) {
98	entry.Data = map[string]interface{}{}
99	logger.entryPool.Put(entry)
100}
101
102// Adds a field to the log entry, note that it doesn't log until you call
103// Debug, Print, Info, Warn, Error, Fatal or Panic. It only creates a log entry.
104// If you want multiple fields, use `WithFields`.
105func (logger *Logger) WithField(key string, value interface{}) *Entry {
106	entry := logger.newEntry()
107	defer logger.releaseEntry(entry)
108	return entry.WithField(key, value)
109}
110
111// Adds a struct of fields to the log entry. All it does is call `WithField` for
112// each `Field`.
113func (logger *Logger) WithFields(fields Fields) *Entry {
114	entry := logger.newEntry()
115	defer logger.releaseEntry(entry)
116	return entry.WithFields(fields)
117}
118
119// Add an error as single field to the log entry.  All it does is call
120// `WithError` for the given `error`.
121func (logger *Logger) WithError(err error) *Entry {
122	entry := logger.newEntry()
123	defer logger.releaseEntry(entry)
124	return entry.WithError(err)
125}
126
127// Overrides the time of the log entry.
128func (logger *Logger) WithTime(t time.Time) *Entry {
129	entry := logger.newEntry()
130	defer logger.releaseEntry(entry)
131	return entry.WithTime(t)
132}
133
134func (logger *Logger) Tracef(format string, args ...interface{}) {
135	if logger.IsLevelEnabled(TraceLevel) {
136		entry := logger.newEntry()
137		entry.Tracef(format, args...)
138		logger.releaseEntry(entry)
139	}
140}
141
142func (logger *Logger) Debugf(format string, args ...interface{}) {
143	if logger.IsLevelEnabled(DebugLevel) {
144		entry := logger.newEntry()
145		entry.Debugf(format, args...)
146		logger.releaseEntry(entry)
147	}
148}
149
150func (logger *Logger) Infof(format string, args ...interface{}) {
151	if logger.IsLevelEnabled(InfoLevel) {
152		entry := logger.newEntry()
153		entry.Infof(format, args...)
154		logger.releaseEntry(entry)
155	}
156}
157
158func (logger *Logger) Printf(format string, args ...interface{}) {
159	entry := logger.newEntry()
160	entry.Printf(format, args...)
161	logger.releaseEntry(entry)
162}
163
164func (logger *Logger) Warnf(format string, args ...interface{}) {
165	if logger.IsLevelEnabled(WarnLevel) {
166		entry := logger.newEntry()
167		entry.Warnf(format, args...)
168		logger.releaseEntry(entry)
169	}
170}
171
172func (logger *Logger) Warningf(format string, args ...interface{}) {
173	if logger.IsLevelEnabled(WarnLevel) {
174		entry := logger.newEntry()
175		entry.Warnf(format, args...)
176		logger.releaseEntry(entry)
177	}
178}
179
180func (logger *Logger) Errorf(format string, args ...interface{}) {
181	if logger.IsLevelEnabled(ErrorLevel) {
182		entry := logger.newEntry()
183		entry.Errorf(format, args...)
184		logger.releaseEntry(entry)
185	}
186}
187
188func (logger *Logger) Fatalf(format string, args ...interface{}) {
189	if logger.IsLevelEnabled(FatalLevel) {
190		entry := logger.newEntry()
191		entry.Fatalf(format, args...)
192		logger.releaseEntry(entry)
193	}
194	logger.Exit(1)
195}
196
197func (logger *Logger) Panicf(format string, args ...interface{}) {
198	if logger.IsLevelEnabled(PanicLevel) {
199		entry := logger.newEntry()
200		entry.Panicf(format, args...)
201		logger.releaseEntry(entry)
202	}
203}
204
205func (logger *Logger) Trace(args ...interface{}) {
206	if logger.IsLevelEnabled(TraceLevel) {
207		entry := logger.newEntry()
208		entry.Trace(args...)
209		logger.releaseEntry(entry)
210	}
211}
212
213func (logger *Logger) Debug(args ...interface{}) {
214	if logger.IsLevelEnabled(DebugLevel) {
215		entry := logger.newEntry()
216		entry.Debug(args...)
217		logger.releaseEntry(entry)
218	}
219}
220
221func (logger *Logger) Info(args ...interface{}) {
222	if logger.IsLevelEnabled(InfoLevel) {
223		entry := logger.newEntry()
224		entry.Info(args...)
225		logger.releaseEntry(entry)
226	}
227}
228
229func (logger *Logger) Print(args ...interface{}) {
230	entry := logger.newEntry()
231	entry.Info(args...)
232	logger.releaseEntry(entry)
233}
234
235func (logger *Logger) Warn(args ...interface{}) {
236	if logger.IsLevelEnabled(WarnLevel) {
237		entry := logger.newEntry()
238		entry.Warn(args...)
239		logger.releaseEntry(entry)
240	}
241}
242
243func (logger *Logger) Warning(args ...interface{}) {
244	if logger.IsLevelEnabled(WarnLevel) {
245		entry := logger.newEntry()
246		entry.Warn(args...)
247		logger.releaseEntry(entry)
248	}
249}
250
251func (logger *Logger) Error(args ...interface{}) {
252	if logger.IsLevelEnabled(ErrorLevel) {
253		entry := logger.newEntry()
254		entry.Error(args...)
255		logger.releaseEntry(entry)
256	}
257}
258
259func (logger *Logger) Fatal(args ...interface{}) {
260	if logger.IsLevelEnabled(FatalLevel) {
261		entry := logger.newEntry()
262		entry.Fatal(args...)
263		logger.releaseEntry(entry)
264	}
265	logger.Exit(1)
266}
267
268func (logger *Logger) Panic(args ...interface{}) {
269	if logger.IsLevelEnabled(PanicLevel) {
270		entry := logger.newEntry()
271		entry.Panic(args...)
272		logger.releaseEntry(entry)
273	}
274}
275
276func (logger *Logger) Traceln(args ...interface{}) {
277	if logger.IsLevelEnabled(TraceLevel) {
278		entry := logger.newEntry()
279		entry.Traceln(args...)
280		logger.releaseEntry(entry)
281	}
282}
283
284func (logger *Logger) Debugln(args ...interface{}) {
285	if logger.IsLevelEnabled(DebugLevel) {
286		entry := logger.newEntry()
287		entry.Debugln(args...)
288		logger.releaseEntry(entry)
289	}
290}
291
292func (logger *Logger) Infoln(args ...interface{}) {
293	if logger.IsLevelEnabled(InfoLevel) {
294		entry := logger.newEntry()
295		entry.Infoln(args...)
296		logger.releaseEntry(entry)
297	}
298}
299
300func (logger *Logger) Println(args ...interface{}) {
301	entry := logger.newEntry()
302	entry.Println(args...)
303	logger.releaseEntry(entry)
304}
305
306func (logger *Logger) Warnln(args ...interface{}) {
307	if logger.IsLevelEnabled(WarnLevel) {
308		entry := logger.newEntry()
309		entry.Warnln(args...)
310		logger.releaseEntry(entry)
311	}
312}
313
314func (logger *Logger) Warningln(args ...interface{}) {
315	if logger.IsLevelEnabled(WarnLevel) {
316		entry := logger.newEntry()
317		entry.Warnln(args...)
318		logger.releaseEntry(entry)
319	}
320}
321
322func (logger *Logger) Errorln(args ...interface{}) {
323	if logger.IsLevelEnabled(ErrorLevel) {
324		entry := logger.newEntry()
325		entry.Errorln(args...)
326		logger.releaseEntry(entry)
327	}
328}
329
330func (logger *Logger) Fatalln(args ...interface{}) {
331	if logger.IsLevelEnabled(FatalLevel) {
332		entry := logger.newEntry()
333		entry.Fatalln(args...)
334		logger.releaseEntry(entry)
335	}
336	logger.Exit(1)
337}
338
339func (logger *Logger) Panicln(args ...interface{}) {
340	if logger.IsLevelEnabled(PanicLevel) {
341		entry := logger.newEntry()
342		entry.Panicln(args...)
343		logger.releaseEntry(entry)
344	}
345}
346
347func (logger *Logger) Exit(code int) {
348	runHandlers()
349	if logger.ExitFunc == nil {
350		logger.ExitFunc = os.Exit
351	}
352	logger.ExitFunc(code)
353}
354
355//When file is opened with appending mode, it's safe to
356//write concurrently to a file (within 4k message on Linux).
357//In these cases user can choose to disable the lock.
358func (logger *Logger) SetNoLock() {
359	logger.mu.Disable()
360}
361
362func (logger *Logger) level() Level {
363	return Level(atomic.LoadUint32((*uint32)(&logger.Level)))
364}
365
366// SetLevel sets the logger level.
367func (logger *Logger) SetLevel(level Level) {
368	atomic.StoreUint32((*uint32)(&logger.Level), uint32(level))
369}
370
371// GetLevel returns the logger level.
372func (logger *Logger) GetLevel() Level {
373	return logger.level()
374}
375
376// AddHook adds a hook to the logger hooks.
377func (logger *Logger) AddHook(hook Hook) {
378	logger.mu.Lock()
379	defer logger.mu.Unlock()
380	logger.Hooks.Add(hook)
381}
382
383// IsLevelEnabled checks if the log level of the logger is greater than the level param
384func (logger *Logger) IsLevelEnabled(level Level) bool {
385	return logger.level() >= level
386}
387
388// SetFormatter sets the logger formatter.
389func (logger *Logger) SetFormatter(formatter Formatter) {
390	logger.mu.Lock()
391	defer logger.mu.Unlock()
392	logger.Formatter = formatter
393}
394
395// SetOutput sets the logger output.
396func (logger *Logger) SetOutput(output io.Writer) {
397	logger.mu.Lock()
398	defer logger.mu.Unlock()
399	logger.Out = output
400}
401
402func (logger *Logger) SetReportCaller(reportCaller bool) {
403	logger.mu.Lock()
404	defer logger.mu.Unlock()
405	logger.ReportCaller = reportCaller
406}
407
408// ReplaceHooks replaces the logger hooks and returns the old ones
409func (logger *Logger) ReplaceHooks(hooks LevelHooks) LevelHooks {
410	logger.mu.Lock()
411	oldHooks := logger.Hooks
412	logger.Hooks = hooks
413	logger.mu.Unlock()
414	return oldHooks
415}
416