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