diff --git a/README.md b/README.md index a97bb06..718681a 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,7 @@ func main() { * warn (`zerolog.WarnLevel`, 2) * info (`zerolog.InfoLevel`, 1) * debug (`zerolog.DebugLevel`, 0) +* trace (`zerolog.TraceLevel`, -1) You can set the Global logging level to any of these options using the `SetGlobalLevel` function in the zerolog package, passing in one of the given constants above, e.g. `zerolog.InfoLevel` would be the "info" level. Whichever level is chosen, all logs with a level greater than or equal to that level will be written. To turn off logging entirely, pass the `zerolog.Disabled` constant. diff --git a/binary_test.go b/binary_test.go index b4b52d9..79cfa9c 100644 --- a/binary_test.go +++ b/binary_test.go @@ -108,6 +108,19 @@ func ExampleLogger_Printf() { // Output: {"level":"debug","message":"hello world"} } +func ExampleLogger_Trace() { + dst := bytes.Buffer{} + log := New(&dst) + + log.Trace(). + Str("foo", "bar"). + Int("n", 123). + Msg("hello world") + + fmt.Println(decodeIfBinaryToString(dst.Bytes())) + // Output: {"level":"trace","foo":"bar","n":123,"message":"hello world"} +} + func ExampleLogger_Debug() { dst := bytes.Buffer{} log := New(&dst) diff --git a/console.go b/console.go index 15bd063..75f6e45 100644 --- a/console.go +++ b/console.go @@ -317,6 +317,8 @@ func consoleDefaultFormatLevel(noColor bool) Formatter { var l string if ll, ok := i.(string); ok { switch ll { + case "trace": + l = colorize("TRC", colorMagenta, noColor) case "debug": l = colorize("DBG", colorYellow, noColor) case "info": diff --git a/ctx.go b/ctx.go index 2b7a682..ce18a32 100644 --- a/ctx.go +++ b/ctx.go @@ -7,6 +7,7 @@ import ( var disabledLogger *Logger func init() { + SetGlobalLevel(TraceLevel) l := Nop() disabledLogger = &l } diff --git a/globals.go b/globals.go index f7f87be..421429a 100644 --- a/globals.go +++ b/globals.go @@ -2,9 +2,9 @@ package zerolog import ( "strconv" + "sync/atomic" "time" ) -import "sync/atomic" const ( // TimeFormatUnix defines a time format that makes time fields to be @@ -83,8 +83,8 @@ var ( ) var ( - gLevel = new(uint32) - disableSampling = new(uint32) + gLevel = new(int32) + disableSampling = new(int32) ) // SetGlobalLevel sets the global override for log level. If this @@ -92,23 +92,23 @@ var ( // // To globally disable logs, set GlobalLevel to Disabled. func SetGlobalLevel(l Level) { - atomic.StoreUint32(gLevel, uint32(l)) + atomic.StoreInt32(gLevel, int32(l)) } // GlobalLevel returns the current global log level func GlobalLevel() Level { - return Level(atomic.LoadUint32(gLevel)) + return Level(atomic.LoadInt32(gLevel)) } // DisableSampling will disable sampling in all Loggers if true. func DisableSampling(v bool) { - var i uint32 + var i int32 if v { i = 1 } - atomic.StoreUint32(disableSampling, i) + atomic.StoreInt32(disableSampling, i) } func samplingDisabled() bool { - return atomic.LoadUint32(disableSampling) == 1 + return atomic.LoadInt32(disableSampling) == 1 } diff --git a/hook.go b/hook.go index 08133ac..ec6effc 100644 --- a/hook.go +++ b/hook.go @@ -17,12 +17,16 @@ func (h HookFunc) Run(e *Event, level Level, message string) { // LevelHook applies a different hook for each level. type LevelHook struct { - NoLevelHook, DebugHook, InfoHook, WarnHook, ErrorHook, FatalHook, PanicHook Hook + NoLevelHook, TraceHook, DebugHook, InfoHook, WarnHook, ErrorHook, FatalHook, PanicHook Hook } // Run implements the Hook interface. func (h LevelHook) Run(e *Event, level Level, message string) { switch level { + case TraceLevel: + if h.TraceHook != nil { + h.TraceHook.Run(e, level, message) + } case DebugLevel: if h.DebugHook != nil { h.DebugHook.Run(e, level, message) diff --git a/journald/journald.go b/journald/journald.go index ce3a382..058a68a 100644 --- a/journald/journald.go +++ b/journald/journald.go @@ -48,6 +48,8 @@ func levelToJPrio(zLevel string) journal.Priority { lvl, _ := zerolog.ParseLevel(zLevel) switch lvl { + case zerolog.TraceLevel: + return journal.PriDebug case zerolog.DebugLevel: return journal.PriDebug case zerolog.InfoLevel: diff --git a/log.go b/log.go index 025221f..c0507de 100644 --- a/log.go +++ b/log.go @@ -107,9 +107,11 @@ import ( ) // Level defines log levels. -type Level uint8 +type Level int8 const ( + // TraceLevel defines trace log level. + TraceLevel Level = -1 // DebugLevel defines debug log level. DebugLevel Level = iota // InfoLevel defines info log level. @@ -130,6 +132,8 @@ const ( func (l Level) String() string { switch l { + case TraceLevel: + return "trace" case DebugLevel: return "debug" case InfoLevel: @@ -152,6 +156,8 @@ func (l Level) String() string { // returns an error if the input string does not match known values. func ParseLevel(levelStr string) (Level, error) { switch levelStr { + case LevelFieldMarshalFunc(TraceLevel): + return TraceLevel, nil case LevelFieldMarshalFunc(DebugLevel): return DebugLevel, nil case LevelFieldMarshalFunc(InfoLevel): @@ -198,7 +204,7 @@ func New(w io.Writer) Logger { if !ok { lw = levelWriterAdapter{w} } - return Logger{w: lw} + return Logger{w: lw, level: TraceLevel} } // Nop returns a disabled logger for which all operation are no-op. @@ -268,6 +274,13 @@ func (l Logger) Hook(h Hook) Logger { return l } +// Trace starts a new message with trace level. +// +// You must call Msg on the returned event in order to send the event. +func (l *Logger) Trace() *Event { + return l.newEvent(TraceLevel, nil) +} + // Debug starts a new message with debug level. // // You must call Msg on the returned event in order to send the event. @@ -331,6 +344,8 @@ func (l *Logger) Panic() *Event { // You must call Msg on the returned event in order to send the event. func (l *Logger) WithLevel(level Level) *Event { switch level { + case TraceLevel: + return l.Trace() case DebugLevel: return l.Debug() case InfoLevel: diff --git a/log/log.go b/log/log.go index dd92ab9..d3e12d0 100644 --- a/log/log.go +++ b/log/log.go @@ -37,6 +37,13 @@ func Hook(h zerolog.Hook) zerolog.Logger { return Logger.Hook(h) } +// Trace starts a new message with trace level. +// +// You must call Msg on the returned event in order to send the event. +func Trace() *zerolog.Event { + return Logger.Trace() +} + // Debug starts a new message with debug level. // // You must call Msg on the returned event in order to send the event. diff --git a/log/log_example_test.go b/log/log_example_test.go index 4938435..3127e51 100644 --- a/log/log_example_test.go +++ b/log/log_example_test.go @@ -54,6 +54,14 @@ func ExampleLog() { // Output: {"time":1199811905,"message":"hello world"} } +// Example of a log at a particular "level" (in this case, "trace") +func ExampleTrace() { + setup() + log.Trace().Msg("hello world") + + // Output: {"level":"trace","time":1199811905,"message":"hello world"} +} + // Example of a log at a particular "level" (in this case, "debug") func ExampleDebug() { setup() diff --git a/log_example_test.go b/log_example_test.go index 7344745..71fd217 100644 --- a/log_example_test.go +++ b/log_example_test.go @@ -95,6 +95,17 @@ func ExampleLogger_Printf() { // Output: {"level":"debug","message":"hello world"} } +func ExampleLogger_Trace() { + log := zerolog.New(os.Stdout) + + log.Trace(). + Str("foo", "bar"). + Int("n", 123). + Msg("hello world") + + // Output: {"level":"trace","foo":"bar","n":123,"message":"hello world"} +} + func ExampleLogger_Debug() { log := zerolog.New(os.Stdout) diff --git a/log_test.go b/log_test.go index dfd2737..dee2916 100644 --- a/log_test.go +++ b/log_test.go @@ -526,30 +526,34 @@ func TestLevelWriter(t *testing.T) { }{}, } log := New(lw) + log.Trace().Msg("0") log.Debug().Msg("1") log.Info().Msg("2") log.Warn().Msg("3") log.Error().Msg("4") log.Log().Msg("nolevel-1") - log.WithLevel(DebugLevel).Msg("5") - log.WithLevel(InfoLevel).Msg("6") - log.WithLevel(WarnLevel).Msg("7") - log.WithLevel(ErrorLevel).Msg("8") + log.WithLevel(TraceLevel).Msg("5") + log.WithLevel(DebugLevel).Msg("6") + log.WithLevel(InfoLevel).Msg("7") + log.WithLevel(WarnLevel).Msg("8") + log.WithLevel(ErrorLevel).Msg("9") log.WithLevel(NoLevel).Msg("nolevel-2") want := []struct { l Level p string }{ + {TraceLevel, `{"level":"trace","message":"0"}` + "\n"}, {DebugLevel, `{"level":"debug","message":"1"}` + "\n"}, {InfoLevel, `{"level":"info","message":"2"}` + "\n"}, {WarnLevel, `{"level":"warn","message":"3"}` + "\n"}, {ErrorLevel, `{"level":"error","message":"4"}` + "\n"}, {NoLevel, `{"message":"nolevel-1"}` + "\n"}, - {DebugLevel, `{"level":"debug","message":"5"}` + "\n"}, - {InfoLevel, `{"level":"info","message":"6"}` + "\n"}, - {WarnLevel, `{"level":"warn","message":"7"}` + "\n"}, - {ErrorLevel, `{"level":"error","message":"8"}` + "\n"}, + {TraceLevel, `{"level":"trace","message":"5"}` + "\n"}, + {DebugLevel, `{"level":"debug","message":"6"}` + "\n"}, + {InfoLevel, `{"level":"info","message":"7"}` + "\n"}, + {WarnLevel, `{"level":"warn","message":"8"}` + "\n"}, + {ErrorLevel, `{"level":"error","message":"9"}` + "\n"}, {NoLevel, `{"message":"nolevel-2"}` + "\n"}, } if got := lw.ops; !reflect.DeepEqual(got, want) { diff --git a/sampler.go b/sampler.go index 7b0923e..a99629e 100644 --- a/sampler.go +++ b/sampler.go @@ -104,11 +104,15 @@ func (s *BurstSampler) inc() uint32 { // LevelSampler applies a different sampler for each level. type LevelSampler struct { - DebugSampler, InfoSampler, WarnSampler, ErrorSampler Sampler + TraceSampler, DebugSampler, InfoSampler, WarnSampler, ErrorSampler Sampler } func (s LevelSampler) Sample(lvl Level) bool { switch lvl { + case TraceLevel: + if s.TraceSampler != nil { + return s.TraceSampler.Sample(lvl) + } case DebugLevel: if s.DebugSampler != nil { return s.DebugSampler.Sample(lvl) diff --git a/syslog.go b/syslog.go index 82b470e..31c97a5 100644 --- a/syslog.go +++ b/syslog.go @@ -10,6 +10,7 @@ import ( // SyslogWriter is an interface matching a syslog.Writer struct. type SyslogWriter interface { io.Writer + Trace(m string) error Debug(m string) error Info(m string) error Warning(m string) error @@ -35,6 +36,8 @@ func (sw syslogWriter) Write(p []byte) (n int, err error) { // WriteLevel implements LevelWriter interface. func (sw syslogWriter) WriteLevel(level Level, p []byte) (n int, err error) { switch level { + case TraceLevel: + err = sw.w.Trace(string(p)) case DebugLevel: err = sw.w.Debug(string(p)) case InfoLevel: diff --git a/syslog_test.go b/syslog_test.go index 94d15d9..aa8d41e 100644 --- a/syslog_test.go +++ b/syslog_test.go @@ -17,6 +17,10 @@ type syslogTestWriter struct { func (w *syslogTestWriter) Write(p []byte) (int, error) { return 0, nil } +func (w *syslogTestWriter) Trace(m string) error { + w.events = append(w.events, syslogEvent{"Trace", m}) + return nil +} func (w *syslogTestWriter) Debug(m string) error { w.events = append(w.events, syslogEvent{"Debug", m}) return nil @@ -45,12 +49,14 @@ func (w *syslogTestWriter) Crit(m string) error { func TestSyslogWriter(t *testing.T) { sw := &syslogTestWriter{} log := New(SyslogLevelWriter(sw)) + log.Trace().Msg("trace") log.Debug().Msg("debug") log.Info().Msg("info") log.Warn().Msg("warn") log.Error().Msg("error") log.Log().Msg("nolevel") want := []syslogEvent{ + {"Trace", `{"level":"trace","message":"trace"}` + "\n"}, {"Debug", `{"level":"debug","message":"debug"}` + "\n"}, {"Info", `{"level":"info","message":"info"}` + "\n"}, {"Warning", `{"level":"warn","message":"warn"}` + "\n"},