zlog/log.go

291 lines
7.2 KiB
Go

// Package zerolog provides a lightweight logging library dedicated to JSON logging.
//
// A global Logger can be use for simple logging:
//
// import "github.com/rs/zerolog/log"
//
// log.Info().Msg("hello world")
// // Output: {"time":1494567715,"level":"info","message":"hello world"}
//
// NOTE: To import the global logger, import the "log" subpackage "github.com/rs/zerolog/log".
//
// Fields can be added to log messages:
//
// log.Info().Str("foo", "bar").Msg("hello world")
// // Output: {"time":1494567715,"level":"info","message":"hello world","foo":"bar"}
//
// Create logger instance to manage different outputs:
//
// logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
// logger.Info().
// Str("foo", "bar").
// Msg("hello world")
// // Output: {"time":1494567715,"level":"info","message":"hello world","foo":"bar"}
//
// Sub-loggers let you chain loggers with additional context:
//
// sublogger := log.With().Str("component": "foo").Logger()
// sublogger.Info().Msg("hello world")
// // Output: {"time":1494567715,"level":"info","message":"hello world","component":"foo"}
//
// Level logging
//
// zerolog.GlobalLevel = zerolog.InfoLevel
//
// log.Debug().Msg("filtered out message")
// log.Info().Msg("routed message")
//
// if e := log.Debug(); e.Enabled() {
// // Compute log output only if enabled.
// value := compute()
// e.Str("foo": value).Msg("some debug message")
// }
// // Output: {"level":"info","time":1494567715,"routed message"}
//
// Customize automatic field names:
//
// log.TimestampFieldName = "t"
// log.LevelFieldName = "p"
// log.MessageFieldName = "m"
//
// log.Info().Msg("hello world")
// // Output: {"t":1494567715,"p":"info","m":"hello world"}
//
// Log with no level and message:
//
// log.Log().Str("foo","bar").Msg("")
// // Output: {"time":1494567715,"foo":"bar"}
//
// Add contextual fields to global Logger:
//
// log.Logger = log.With().Str("foo", "bar").Logger()
//
// Sample logs:
//
// sampled := log.Sample(10)
// sampled.Info().Msg("will be logged every 10 messages")
//
package zerolog
import (
"io"
"os"
"sync/atomic"
)
type parentLogger interface {
addContextField(Event)
}
// Level defines log levels.
type Level uint8
const (
// DebugLevel defines debug log level.
DebugLevel Level = iota
// InfoLevel defines info log level.
InfoLevel
// WarnLevel defines warn log level.
WarnLevel
// ErrorLevel defines error log level.
ErrorLevel
// FatalLevel defines fatal log level.
FatalLevel
// PanicLevel defines panic log level.
PanicLevel
// Disabled disables the logger.
Disabled
)
func (l Level) String() string {
switch l {
case DebugLevel:
return "debug"
case InfoLevel:
return "info"
case WarnLevel:
return "warning"
case ErrorLevel:
return "error"
case FatalLevel:
return "fatal"
case PanicLevel:
return "panic"
}
return ""
}
const (
// Often samples log every 10 events.
Often = int64(10)
// Sometimes samples log every 100 events.
Sometimes = int64(100)
// Rarely samples log every 1000 events.
Rarely = int64(1000)
)
var (
// GlobalLevel defines the global override for log level. If this
// values is raised, all Loggers will use at least this value.
//
// To globally disable logs, set GlobalLevel to Disabled.
GlobalLevel = DebugLevel
// DisableSampling will disable sampling in all Loggers if true.
DisableSampling = false
)
// A Logger represents an active logging object that generates lines
// of JSON output to an io.Writer. Each logging operation makes a single
// call to the Writer's Write method. There is no guaranty on access
// serialization to the Writer. If your Writer is not thread safe,
// you may consider a sync wrapper.
type Logger struct {
root bool
parent parentLogger
w LevelWriter
field field
level Level
sample uint32
counter *uint32
}
// New creates a root logger with given output writer.
func New(w io.Writer) Logger {
if w == nil {
panic("w is nil")
}
lw, ok := w.(LevelWriter)
if !ok {
lw = levelWriterAdapter{w}
}
return Logger{
root: true,
w: lw,
}
}
// With creates a child logger with the field added to its context.
func (l Logger) With() Context {
return Context{l}
}
// Level crestes a child logger with the minium accepted level set to level.
func (l Logger) Level(lvl Level) Logger {
return Logger{
parent: l,
w: l.w,
level: lvl,
sample: l.sample,
counter: l.counter,
}
}
// Sample returns a logger that only let one message out of every to pass thru.
func (l Logger) Sample(every int) Logger {
if every == 0 {
// Create a child with no sampling.
return Logger{
parent: l,
w: l.w,
level: l.level,
}
}
return Logger{
parent: l,
w: l.w,
level: l.level,
sample: uint32(every),
counter: new(uint32),
}
}
// Debug starts a new message with debug level.
//
// You must call Msg on the returned event in order to send the event.
func (l Logger) Debug() Event {
return l.newEvent(DebugLevel, true, nil)
}
// Info starts a new message with info level.
//
// You must call Msg on the returned event in order to send the event.
func (l Logger) Info() Event {
return l.newEvent(InfoLevel, true, nil)
}
// Warn starts a new message with warn level.
//
// You must call Msg on the returned event in order to send the event.
func (l Logger) Warn() Event {
return l.newEvent(WarnLevel, true, nil)
}
// Error starts a new message with error level.
//
// You must call Msg on the returned event in order to send the event.
func (l Logger) Error() Event {
return l.newEvent(ErrorLevel, true, nil)
}
// Fatal starts a new message with fatal level. The os.Exit(1) function
// is called by the Msg method.
//
// You must call Msg on the returned event in order to send the event.
func (l Logger) Fatal() Event {
return l.newEvent(FatalLevel, true, func(msg string) { os.Exit(1) })
}
// Panic starts a new message with panic level. The message is also sent
// to the panic function.
//
// You must call Msg on the returned event in order to send the event.
func (l Logger) Panic() Event {
return l.newEvent(PanicLevel, true, func(msg string) { panic(msg) })
}
// Log starts a new message with no level. Setting GlobalLevel to Disabled
// will still disable events produced by this method.
//
// You must call Msg on the returned event in order to send the event.
func (l Logger) Log() Event {
return l.newEvent(ErrorLevel, false, nil)
}
func (l Logger) newEvent(level Level, addLevelField bool, done func(string)) Event {
lvl := InfoLevel
if addLevelField {
lvl = level
}
e := newEvent(l.w, lvl, l.should(level))
if addLevelField {
e.Str(LevelFieldName, level.String())
}
if l.sample > 0 && SampleFieldName != "" {
e.Uint32(SampleFieldName, l.sample)
}
l.addContextField(e)
return e
}
// should returns true if the log event should be logged.
func (l Logger) should(lvl Level) bool {
if lvl < l.level || lvl < GlobalLevel {
return false
}
if !DisableSampling && l.sample > 0 && l.counter != nil {
c := atomic.AddUint32(l.counter, 1)
return c%l.sample == 0
}
return true
}
func (l Logger) addContextField(e Event) {
if !l.root {
l.parent.addContextField(e)
}
if l.field.mode != zeroFieldMode {
e.append(l.field)
}
}