blob: 8eb45a8a6d6de08a6ff7fd19b17ca283096abedf [file] [log] [blame]
khenaidooac637102019-01-14 15:44:34 -05001// Package zerolog provides a lightweight logging library dedicated to JSON logging.
2//
3// A global Logger can be use for simple logging:
4//
5// import "github.com/rs/zerolog/log"
6//
7// log.Info().Msg("hello world")
8// // Output: {"time":1494567715,"level":"info","message":"hello world"}
9//
10// NOTE: To import the global logger, import the "log" subpackage "github.com/rs/zerolog/log".
11//
12// Fields can be added to log messages:
13//
14// log.Info().Str("foo", "bar").Msg("hello world")
15// // Output: {"time":1494567715,"level":"info","message":"hello world","foo":"bar"}
16//
17// Create logger instance to manage different outputs:
18//
19// logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
20// logger.Info().
21// Str("foo", "bar").
22// Msg("hello world")
23// // Output: {"time":1494567715,"level":"info","message":"hello world","foo":"bar"}
24//
25// Sub-loggers let you chain loggers with additional context:
26//
27// sublogger := log.With().Str("component": "foo").Logger()
28// sublogger.Info().Msg("hello world")
29// // Output: {"time":1494567715,"level":"info","message":"hello world","component":"foo"}
30//
31// Level logging
32//
33// zerolog.SetGlobalLevel(zerolog.InfoLevel)
34//
35// log.Debug().Msg("filtered out message")
36// log.Info().Msg("routed message")
37//
38// if e := log.Debug(); e.Enabled() {
39// // Compute log output only if enabled.
40// value := compute()
41// e.Str("foo": value).Msg("some debug message")
42// }
43// // Output: {"level":"info","time":1494567715,"routed message"}
44//
45// Customize automatic field names:
46//
47// log.TimestampFieldName = "t"
48// log.LevelFieldName = "p"
49// log.MessageFieldName = "m"
50//
51// log.Info().Msg("hello world")
52// // Output: {"t":1494567715,"p":"info","m":"hello world"}
53//
54// Log with no level and message:
55//
56// log.Log().Str("foo","bar").Msg("")
57// // Output: {"time":1494567715,"foo":"bar"}
58//
59// Add contextual fields to global Logger:
60//
61// log.Logger = log.With().Str("foo", "bar").Logger()
62//
63// Sample logs:
64//
65// sampled := log.Sample(&zerolog.BasicSampler{N: 10})
66// sampled.Info().Msg("will be logged every 10 messages")
67//
68// Log with contextual hooks:
69//
70// // Create the hook:
71// type SeverityHook struct{}
72//
73// func (h SeverityHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {
74// if level != zerolog.NoLevel {
75// e.Str("severity", level.String())
76// }
77// }
78//
79// // And use it:
80// var h SeverityHook
81// log := zerolog.New(os.Stdout).Hook(h)
82// log.Warn().Msg("")
83// // Output: {"level":"warn","severity":"warn"}
84//
85//
86// Caveats
87//
88// There is no fields deduplication out-of-the-box.
89// Using the same key multiple times creates new key in final JSON each time.
90//
91// logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
92// logger.Info().
93// Timestamp().
94// Msg("dup")
95// // Output: {"level":"info","time":1494567715,"time":1494567715,"message":"dup"}
96//
97// However, it’s not a big deal though as JSON accepts dup keys,
98// the last one prevails.
99package zerolog
100
101import (
102 "fmt"
103 "io"
104 "io/ioutil"
105 "os"
106 "strconv"
107)
108
109// Level defines log levels.
110type Level uint8
111
112const (
113 // DebugLevel defines debug log level.
114 DebugLevel Level = iota
115 // InfoLevel defines info log level.
116 InfoLevel
117 // WarnLevel defines warn log level.
118 WarnLevel
119 // ErrorLevel defines error log level.
120 ErrorLevel
121 // FatalLevel defines fatal log level.
122 FatalLevel
123 // PanicLevel defines panic log level.
124 PanicLevel
125 // NoLevel defines an absent log level.
126 NoLevel
127 // Disabled disables the logger.
128 Disabled
129)
130
131func (l Level) String() string {
132 switch l {
133 case DebugLevel:
134 return "debug"
135 case InfoLevel:
136 return "info"
137 case WarnLevel:
138 return "warn"
139 case ErrorLevel:
140 return "error"
141 case FatalLevel:
142 return "fatal"
143 case PanicLevel:
144 return "panic"
145 case NoLevel:
146 return ""
147 }
148 return ""
149}
150
151// ParseLevel converts a level string into a zerolog Level value.
152// returns an error if the input string does not match known values.
153func ParseLevel(levelStr string) (Level, error) {
154 switch levelStr {
155 case DebugLevel.String():
156 return DebugLevel, nil
157 case InfoLevel.String():
158 return InfoLevel, nil
159 case WarnLevel.String():
160 return WarnLevel, nil
161 case ErrorLevel.String():
162 return ErrorLevel, nil
163 case FatalLevel.String():
164 return FatalLevel, nil
165 case PanicLevel.String():
166 return PanicLevel, nil
167 case NoLevel.String():
168 return NoLevel, nil
169 }
170 return NoLevel, fmt.Errorf("Unknown Level String: '%s', defaulting to NoLevel", levelStr)
171}
172
173// A Logger represents an active logging object that generates lines
174// of JSON output to an io.Writer. Each logging operation makes a single
175// call to the Writer's Write method. There is no guaranty on access
176// serialization to the Writer. If your Writer is not thread safe,
177// you may consider a sync wrapper.
178type Logger struct {
179 w LevelWriter
180 level Level
181 sampler Sampler
182 context []byte
183 hooks []Hook
184}
185
186// New creates a root logger with given output writer. If the output writer implements
187// the LevelWriter interface, the WriteLevel method will be called instead of the Write
188// one.
189//
190// Each logging operation makes a single call to the Writer's Write method. There is no
191// guaranty on access serialization to the Writer. If your Writer is not thread safe,
192// you may consider using sync wrapper.
193func New(w io.Writer) Logger {
194 if w == nil {
195 w = ioutil.Discard
196 }
197 lw, ok := w.(LevelWriter)
198 if !ok {
199 lw = levelWriterAdapter{w}
200 }
201 return Logger{w: lw}
202}
203
204// Nop returns a disabled logger for which all operation are no-op.
205func Nop() Logger {
206 return New(nil).Level(Disabled)
207}
208
209// Output duplicates the current logger and sets w as its output.
210func (l Logger) Output(w io.Writer) Logger {
211 l2 := New(w)
212 l2.level = l.level
213 l2.sampler = l.sampler
214 if len(l.hooks) > 0 {
215 l2.hooks = append(l2.hooks, l.hooks...)
216 }
217 if l.context != nil {
218 l2.context = make([]byte, len(l.context), cap(l.context))
219 copy(l2.context, l.context)
220 }
221 return l2
222}
223
224// With creates a child logger with the field added to its context.
225func (l Logger) With() Context {
226 context := l.context
227 l.context = make([]byte, 0, 500)
228 if context != nil {
229 l.context = append(l.context, context...)
230 }
231 return Context{l}
232}
233
234// UpdateContext updates the internal logger's context.
235//
236// Use this method with caution. If unsure, prefer the With method.
237func (l *Logger) UpdateContext(update func(c Context) Context) {
238 if l == disabledLogger {
239 return
240 }
241 if cap(l.context) == 0 {
242 l.context = make([]byte, 0, 500)
243 }
244 c := update(Context{*l})
245 l.context = c.l.context
246}
247
248// Level creates a child logger with the minimum accepted level set to level.
249func (l Logger) Level(lvl Level) Logger {
250 l.level = lvl
251 return l
252}
253
254// Sample returns a logger with the s sampler.
255func (l Logger) Sample(s Sampler) Logger {
256 l.sampler = s
257 return l
258}
259
260// Hook returns a logger with the h Hook.
261func (l Logger) Hook(h Hook) Logger {
262 l.hooks = append(l.hooks, h)
263 return l
264}
265
266// Debug starts a new message with debug level.
267//
268// You must call Msg on the returned event in order to send the event.
269func (l *Logger) Debug() *Event {
270 return l.newEvent(DebugLevel, nil)
271}
272
273// Info starts a new message with info level.
274//
275// You must call Msg on the returned event in order to send the event.
276func (l *Logger) Info() *Event {
277 return l.newEvent(InfoLevel, nil)
278}
279
280// Warn starts a new message with warn level.
281//
282// You must call Msg on the returned event in order to send the event.
283func (l *Logger) Warn() *Event {
284 return l.newEvent(WarnLevel, nil)
285}
286
287// Error starts a new message with error level.
288//
289// You must call Msg on the returned event in order to send the event.
290func (l *Logger) Error() *Event {
291 return l.newEvent(ErrorLevel, nil)
292}
293
294// Fatal starts a new message with fatal level. The os.Exit(1) function
295// is called by the Msg method, which terminates the program immediately.
296//
297// You must call Msg on the returned event in order to send the event.
298func (l *Logger) Fatal() *Event {
299 return l.newEvent(FatalLevel, func(msg string) { os.Exit(1) })
300}
301
302// Panic starts a new message with panic level. The panic() function
303// is called by the Msg method, which stops the ordinary flow of a goroutine.
304//
305// You must call Msg on the returned event in order to send the event.
306func (l *Logger) Panic() *Event {
307 return l.newEvent(PanicLevel, func(msg string) { panic(msg) })
308}
309
310// WithLevel starts a new message with level. Unlike Fatal and Panic
311// methods, WithLevel does not terminate the program or stop the ordinary
312// flow of a gourotine when used with their respective levels.
313//
314// You must call Msg on the returned event in order to send the event.
315func (l *Logger) WithLevel(level Level) *Event {
316 switch level {
317 case DebugLevel:
318 return l.Debug()
319 case InfoLevel:
320 return l.Info()
321 case WarnLevel:
322 return l.Warn()
323 case ErrorLevel:
324 return l.Error()
325 case FatalLevel:
326 return l.newEvent(FatalLevel, nil)
327 case PanicLevel:
328 return l.newEvent(PanicLevel, nil)
329 case NoLevel:
330 return l.Log()
331 case Disabled:
332 return nil
333 default:
334 panic("zerolog: WithLevel(): invalid level: " + strconv.Itoa(int(level)))
335 }
336}
337
338// Log starts a new message with no level. Setting GlobalLevel to Disabled
339// will still disable events produced by this method.
340//
341// You must call Msg on the returned event in order to send the event.
342func (l *Logger) Log() *Event {
343 return l.newEvent(NoLevel, nil)
344}
345
346// Print sends a log event using debug level and no extra field.
347// Arguments are handled in the manner of fmt.Print.
348func (l *Logger) Print(v ...interface{}) {
349 if e := l.Debug(); e.Enabled() {
350 e.Msg(fmt.Sprint(v...))
351 }
352}
353
354// Printf sends a log event using debug level and no extra field.
355// Arguments are handled in the manner of fmt.Printf.
356func (l *Logger) Printf(format string, v ...interface{}) {
357 if e := l.Debug(); e.Enabled() {
358 e.Msg(fmt.Sprintf(format, v...))
359 }
360}
361
362// Write implements the io.Writer interface. This is useful to set as a writer
363// for the standard library log.
364func (l Logger) Write(p []byte) (n int, err error) {
365 n = len(p)
366 if n > 0 && p[n-1] == '\n' {
367 // Trim CR added by stdlog.
368 p = p[0 : n-1]
369 }
370 l.Log().Msg(string(p))
371 return
372}
373
374func (l *Logger) newEvent(level Level, done func(string)) *Event {
375 enabled := l.should(level)
376 if !enabled {
377 return nil
378 }
379 e := newEvent(l.w, level)
380 e.done = done
381 e.ch = l.hooks
382 if level != NoLevel {
383 e.Str(LevelFieldName, level.String())
384 }
385 if l.context != nil && len(l.context) > 0 {
386 e.buf = enc.AppendObjectData(e.buf, l.context)
387 }
388 return e
389}
390
391// should returns true if the log event should be logged.
392func (l *Logger) should(lvl Level) bool {
393 if lvl < l.level || lvl < GlobalLevel() {
394 return false
395 }
396 if l.sampler != nil && !samplingDisabled() {
397 return l.sampler.Sample(lvl)
398 }
399 return true
400}