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