1// 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// In this case, many consumers will take the last value,
98// but this is not guaranteed; check yours if in doubt.
99package zerolog
100
101import (
102	"fmt"
103	"io"
104	"io/ioutil"
105	"os"
106	"strconv"
107)
108
109// Level defines log levels.
110type Level int8
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	// TraceLevel defines trace log level.
131	TraceLevel Level = -1
132)
133
134func (l Level) String() string {
135	switch l {
136	case TraceLevel:
137		return "trace"
138	case DebugLevel:
139		return "debug"
140	case InfoLevel:
141		return "info"
142	case WarnLevel:
143		return "warn"
144	case ErrorLevel:
145		return "error"
146	case FatalLevel:
147		return "fatal"
148	case PanicLevel:
149		return "panic"
150	case NoLevel:
151		return ""
152	}
153	return ""
154}
155
156// ParseLevel converts a level string into a zerolog Level value.
157// returns an error if the input string does not match known values.
158func ParseLevel(levelStr string) (Level, error) {
159	switch levelStr {
160	case LevelFieldMarshalFunc(TraceLevel):
161		return TraceLevel, nil
162	case LevelFieldMarshalFunc(DebugLevel):
163		return DebugLevel, nil
164	case LevelFieldMarshalFunc(InfoLevel):
165		return InfoLevel, nil
166	case LevelFieldMarshalFunc(WarnLevel):
167		return WarnLevel, nil
168	case LevelFieldMarshalFunc(ErrorLevel):
169		return ErrorLevel, nil
170	case LevelFieldMarshalFunc(FatalLevel):
171		return FatalLevel, nil
172	case LevelFieldMarshalFunc(PanicLevel):
173		return PanicLevel, nil
174	case LevelFieldMarshalFunc(NoLevel):
175		return NoLevel, nil
176	}
177	return NoLevel, fmt.Errorf("Unknown Level String: '%s', defaulting to NoLevel", levelStr)
178}
179
180// A Logger represents an active logging object that generates lines
181// of JSON output to an io.Writer. Each logging operation makes a single
182// call to the Writer's Write method. There is no guarantee on access
183// serialization to the Writer. If your Writer is not thread safe,
184// you may consider a sync wrapper.
185type Logger struct {
186	w       LevelWriter
187	level   Level
188	sampler Sampler
189	context []byte
190	hooks   []Hook
191}
192
193// New creates a root logger with given output writer. If the output writer implements
194// the LevelWriter interface, the WriteLevel method will be called instead of the Write
195// one.
196//
197// Each logging operation makes a single call to the Writer's Write method. There is no
198// guarantee on access serialization to the Writer. If your Writer is not thread safe,
199// you may consider using sync wrapper.
200func New(w io.Writer) Logger {
201	if w == nil {
202		w = ioutil.Discard
203	}
204	lw, ok := w.(LevelWriter)
205	if !ok {
206		lw = levelWriterAdapter{w}
207	}
208	return Logger{w: lw, level: TraceLevel}
209}
210
211// Nop returns a disabled logger for which all operation are no-op.
212func Nop() Logger {
213	return New(nil).Level(Disabled)
214}
215
216// Output duplicates the current logger and sets w as its output.
217func (l Logger) Output(w io.Writer) Logger {
218	l2 := New(w)
219	l2.level = l.level
220	l2.sampler = l.sampler
221	if len(l.hooks) > 0 {
222		l2.hooks = append(l2.hooks, l.hooks...)
223	}
224	if l.context != nil {
225		l2.context = make([]byte, len(l.context), cap(l.context))
226		copy(l2.context, l.context)
227	}
228	return l2
229}
230
231// With creates a child logger with the field added to its context.
232func (l Logger) With() Context {
233	context := l.context
234	l.context = make([]byte, 0, 500)
235	if context != nil {
236		l.context = append(l.context, context...)
237	} else {
238		// This is needed for AppendKey to not check len of input
239		// thus making it inlinable
240		l.context = enc.AppendBeginMarker(l.context)
241	}
242	return Context{l}
243}
244
245// UpdateContext updates the internal logger's context.
246//
247// Use this method with caution. If unsure, prefer the With method.
248func (l *Logger) UpdateContext(update func(c Context) Context) {
249	if l == disabledLogger {
250		return
251	}
252	if cap(l.context) == 0 {
253		l.context = make([]byte, 0, 500)
254	}
255	if len(l.context) == 0 {
256		l.context = enc.AppendBeginMarker(l.context)
257	}
258	c := update(Context{*l})
259	l.context = c.l.context
260}
261
262// Level creates a child logger with the minimum accepted level set to level.
263func (l Logger) Level(lvl Level) Logger {
264	l.level = lvl
265	return l
266}
267
268// GetLevel returns the current Level of l.
269func (l Logger) GetLevel() Level {
270	return l.level
271}
272
273// Sample returns a logger with the s sampler.
274func (l Logger) Sample(s Sampler) Logger {
275	l.sampler = s
276	return l
277}
278
279// Hook returns a logger with the h Hook.
280func (l Logger) Hook(h Hook) Logger {
281	l.hooks = append(l.hooks, h)
282	return l
283}
284
285// Trace starts a new message with trace level.
286//
287// You must call Msg on the returned event in order to send the event.
288func (l *Logger) Trace() *Event {
289	return l.newEvent(TraceLevel, nil)
290}
291
292// Debug starts a new message with debug level.
293//
294// You must call Msg on the returned event in order to send the event.
295func (l *Logger) Debug() *Event {
296	return l.newEvent(DebugLevel, nil)
297}
298
299// Info starts a new message with info level.
300//
301// You must call Msg on the returned event in order to send the event.
302func (l *Logger) Info() *Event {
303	return l.newEvent(InfoLevel, nil)
304}
305
306// Warn starts a new message with warn level.
307//
308// You must call Msg on the returned event in order to send the event.
309func (l *Logger) Warn() *Event {
310	return l.newEvent(WarnLevel, nil)
311}
312
313// Error starts a new message with error level.
314//
315// You must call Msg on the returned event in order to send the event.
316func (l *Logger) Error() *Event {
317	return l.newEvent(ErrorLevel, nil)
318}
319
320// Err starts a new message with error level with err as a field if not nil or
321// with info level if err is nil.
322//
323// You must call Msg on the returned event in order to send the event.
324func (l *Logger) Err(err error) *Event {
325	if err != nil {
326		return l.Error().Err(err)
327	}
328
329	return l.Info()
330}
331
332// Fatal starts a new message with fatal level. The os.Exit(1) function
333// is called by the Msg method, which terminates the program immediately.
334//
335// You must call Msg on the returned event in order to send the event.
336func (l *Logger) Fatal() *Event {
337	return l.newEvent(FatalLevel, func(msg string) { os.Exit(1) })
338}
339
340// Panic starts a new message with panic level. The panic() function
341// is called by the Msg method, which stops the ordinary flow of a goroutine.
342//
343// You must call Msg on the returned event in order to send the event.
344func (l *Logger) Panic() *Event {
345	return l.newEvent(PanicLevel, func(msg string) { panic(msg) })
346}
347
348// WithLevel starts a new message with level. Unlike Fatal and Panic
349// methods, WithLevel does not terminate the program or stop the ordinary
350// flow of a gourotine when used with their respective levels.
351//
352// You must call Msg on the returned event in order to send the event.
353func (l *Logger) WithLevel(level Level) *Event {
354	switch level {
355	case TraceLevel:
356		return l.Trace()
357	case DebugLevel:
358		return l.Debug()
359	case InfoLevel:
360		return l.Info()
361	case WarnLevel:
362		return l.Warn()
363	case ErrorLevel:
364		return l.Error()
365	case FatalLevel:
366		return l.newEvent(FatalLevel, nil)
367	case PanicLevel:
368		return l.newEvent(PanicLevel, nil)
369	case NoLevel:
370		return l.Log()
371	case Disabled:
372		return nil
373	default:
374		panic("zerolog: WithLevel(): invalid level: " + strconv.Itoa(int(level)))
375	}
376}
377
378// Log starts a new message with no level. Setting GlobalLevel to Disabled
379// will still disable events produced by this method.
380//
381// You must call Msg on the returned event in order to send the event.
382func (l *Logger) Log() *Event {
383	return l.newEvent(NoLevel, nil)
384}
385
386// Print sends a log event using debug level and no extra field.
387// Arguments are handled in the manner of fmt.Print.
388func (l *Logger) Print(v ...interface{}) {
389	if e := l.Debug(); e.Enabled() {
390		e.Msg(fmt.Sprint(v...))
391	}
392}
393
394// Printf sends a log event using debug level and no extra field.
395// Arguments are handled in the manner of fmt.Printf.
396func (l *Logger) Printf(format string, v ...interface{}) {
397	if e := l.Debug(); e.Enabled() {
398		e.Msg(fmt.Sprintf(format, v...))
399	}
400}
401
402// Write implements the io.Writer interface. This is useful to set as a writer
403// for the standard library log.
404func (l Logger) Write(p []byte) (n int, err error) {
405	n = len(p)
406	if n > 0 && p[n-1] == '\n' {
407		// Trim CR added by stdlog.
408		p = p[0 : n-1]
409	}
410	l.Log().Msg(string(p))
411	return
412}
413
414func (l *Logger) newEvent(level Level, done func(string)) *Event {
415	enabled := l.should(level)
416	if !enabled {
417		return nil
418	}
419	e := newEvent(l.w, level)
420	e.done = done
421	e.ch = l.hooks
422	if level != NoLevel {
423		e.Str(LevelFieldName, LevelFieldMarshalFunc(level))
424	}
425	if l.context != nil && len(l.context) > 1 {
426		e.buf = enc.AppendObjectData(e.buf, l.context)
427	}
428	return e
429}
430
431// should returns true if the log event should be logged.
432func (l *Logger) should(lvl Level) bool {
433	if lvl < l.level || lvl < GlobalLevel() {
434		return false
435	}
436	if l.sampler != nil && !samplingDisabled() {
437		return l.sampler.Sample(lvl)
438	}
439	return true
440}
441