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