blob: 9bf64e22ae921c802991db21692edd7a1c826a6c [file] [log] [blame]
sslobodrd046be82019-01-16 10:02:22 -05001package 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) Logf(level Level, format string, args ...interface{}) {
135 if logger.IsLevelEnabled(level) {
136 entry := logger.newEntry()
137 entry.Logf(level, format, args...)
138 logger.releaseEntry(entry)
139 }
140}
141
142func (logger *Logger) Tracef(format string, args ...interface{}) {
143 logger.Logf(TraceLevel, format, args...)
144}
145
146func (logger *Logger) Debugf(format string, args ...interface{}) {
147 logger.Logf(DebugLevel, format, args...)
148}
149
150func (logger *Logger) Infof(format string, args ...interface{}) {
151 logger.Logf(InfoLevel, format, args...)
152}
153
154func (logger *Logger) Printf(format string, args ...interface{}) {
155 entry := logger.newEntry()
156 entry.Printf(format, args...)
157 logger.releaseEntry(entry)
158}
159
160func (logger *Logger) Warnf(format string, args ...interface{}) {
161 logger.Logf(WarnLevel, format, args...)
162}
163
164func (logger *Logger) Warningf(format string, args ...interface{}) {
165 logger.Warnf(format, args...)
166}
167
168func (logger *Logger) Errorf(format string, args ...interface{}) {
169 logger.Logf(ErrorLevel, format, args...)
170}
171
172func (logger *Logger) Fatalf(format string, args ...interface{}) {
173 logger.Logf(FatalLevel, format, args...)
174 logger.Exit(1)
175}
176
177func (logger *Logger) Panicf(format string, args ...interface{}) {
178 logger.Logf(PanicLevel, format, args...)
179}
180
181func (logger *Logger) Log(level Level, args ...interface{}) {
182 if logger.IsLevelEnabled(level) {
183 entry := logger.newEntry()
184 entry.Log(level, args...)
185 logger.releaseEntry(entry)
186 }
187}
188
189func (logger *Logger) Trace(args ...interface{}) {
190 logger.Log(TraceLevel, args...)
191}
192
193func (logger *Logger) Debug(args ...interface{}) {
194 logger.Log(DebugLevel, args...)
195}
196
197func (logger *Logger) Info(args ...interface{}) {
198 logger.Log(InfoLevel, args...)
199}
200
201func (logger *Logger) Print(args ...interface{}) {
202 entry := logger.newEntry()
203 entry.Info(args...)
204 logger.releaseEntry(entry)
205}
206
207func (logger *Logger) Warn(args ...interface{}) {
208 logger.Log(WarnLevel, args...)
209}
210
211func (logger *Logger) Warning(args ...interface{}) {
212 logger.Warn(args...)
213}
214
215func (logger *Logger) Error(args ...interface{}) {
216 logger.Log(ErrorLevel, args...)
217}
218
219func (logger *Logger) Fatal(args ...interface{}) {
220 logger.Log(FatalLevel, args...)
221 logger.Exit(1)
222}
223
224func (logger *Logger) Panic(args ...interface{}) {
225 logger.Log(PanicLevel, args...)
226}
227
228func (logger *Logger) Logln(level Level, args ...interface{}) {
229 if logger.IsLevelEnabled(level) {
230 entry := logger.newEntry()
231 entry.Logln(level, args...)
232 logger.releaseEntry(entry)
233 }
234}
235
236func (logger *Logger) Traceln(args ...interface{}) {
237 logger.Logln(TraceLevel, args...)
238}
239
240func (logger *Logger) Debugln(args ...interface{}) {
241 logger.Logln(DebugLevel, args...)
242}
243
244func (logger *Logger) Infoln(args ...interface{}) {
245 logger.Logln(InfoLevel, args...)
246}
247
248func (logger *Logger) Println(args ...interface{}) {
249 entry := logger.newEntry()
250 entry.Println(args...)
251 logger.releaseEntry(entry)
252}
253
254func (logger *Logger) Warnln(args ...interface{}) {
255 logger.Logln(WarnLevel, args...)
256}
257
258func (logger *Logger) Warningln(args ...interface{}) {
259 logger.Warn(args...)
260}
261
262func (logger *Logger) Errorln(args ...interface{}) {
263 logger.Logln(ErrorLevel, args...)
264}
265
266func (logger *Logger) Fatalln(args ...interface{}) {
267 logger.Logln(FatalLevel, args...)
268 logger.Exit(1)
269}
270
271func (logger *Logger) Panicln(args ...interface{}) {
272 logger.Logln(PanicLevel, args...)
273}
274
275func (logger *Logger) Exit(code int) {
276 runHandlers()
277 if logger.ExitFunc == nil {
278 logger.ExitFunc = os.Exit
279 }
280 logger.ExitFunc(code)
281}
282
283//When file is opened with appending mode, it's safe to
284//write concurrently to a file (within 4k message on Linux).
285//In these cases user can choose to disable the lock.
286func (logger *Logger) SetNoLock() {
287 logger.mu.Disable()
288}
289
290func (logger *Logger) level() Level {
291 return Level(atomic.LoadUint32((*uint32)(&logger.Level)))
292}
293
294// SetLevel sets the logger level.
295func (logger *Logger) SetLevel(level Level) {
296 atomic.StoreUint32((*uint32)(&logger.Level), uint32(level))
297}
298
299// GetLevel returns the logger level.
300func (logger *Logger) GetLevel() Level {
301 return logger.level()
302}
303
304// AddHook adds a hook to the logger hooks.
305func (logger *Logger) AddHook(hook Hook) {
306 logger.mu.Lock()
307 defer logger.mu.Unlock()
308 logger.Hooks.Add(hook)
309}
310
311// IsLevelEnabled checks if the log level of the logger is greater than the level param
312func (logger *Logger) IsLevelEnabled(level Level) bool {
313 return logger.level() >= level
314}
315
316// SetFormatter sets the logger formatter.
317func (logger *Logger) SetFormatter(formatter Formatter) {
318 logger.mu.Lock()
319 defer logger.mu.Unlock()
320 logger.Formatter = formatter
321}
322
323// SetOutput sets the logger output.
324func (logger *Logger) SetOutput(output io.Writer) {
325 logger.mu.Lock()
326 defer logger.mu.Unlock()
327 logger.Out = output
328}
329
330func (logger *Logger) SetReportCaller(reportCaller bool) {
331 logger.mu.Lock()
332 defer logger.mu.Unlock()
333 logger.ReportCaller = reportCaller
334}
335
336// ReplaceHooks replaces the logger hooks and returns the old ones
337func (logger *Logger) ReplaceHooks(hooks LevelHooks) LevelHooks {
338 logger.mu.Lock()
339 oldHooks := logger.Hooks
340 logger.Hooks = hooks
341 logger.mu.Unlock()
342 return oldHooks
343}