From 71e1f5e052d017a22320883199723057e89f3ec8 Mon Sep 17 00:00:00 2001 From: Olivier Poitrey Date: Thu, 26 Jul 2018 15:53:02 -0700 Subject: [PATCH] Add the ability to discard an event from a hook The Discard method has been added to the Event type so it can be called from a hook to prevent the event from behing printed. This new method works outside of the context of a hook too. Fixes #90 --- event.go | 18 ++- hook.go | 9 ++ hook_test.go | 319 ++++++++++++++++++++------------------------------- 3 files changed, 147 insertions(+), 199 deletions(-) diff --git a/event.go b/event.go index 8468e41..71f0a9a 100644 --- a/event.go +++ b/event.go @@ -60,10 +60,12 @@ func (e *Event) write() (err error) { if e == nil { return nil } - e.buf = enc.AppendEndMarker(e.buf) - e.buf = enc.AppendLineBreak(e.buf) - if e.w != nil { - _, err = e.w.WriteLevel(e.level, e.buf) + if e.level != Disabled { + e.buf = enc.AppendEndMarker(e.buf) + e.buf = enc.AppendLineBreak(e.buf) + if e.w != nil { + _, err = e.w.WriteLevel(e.level, e.buf) + } } eventPool.Put(e) return @@ -72,7 +74,13 @@ func (e *Event) write() (err error) { // Enabled return false if the *Event is going to be filtered out by // log level or sampling. func (e *Event) Enabled() bool { - return e != nil + return e != nil && e.level != Disabled +} + +// Discard disables the event so Msg(f) won't print it. +func (e *Event) Discard() *Event { + e.level = Disabled + return nil } // Msg sends the *Event with msg added as the message field if not empty. diff --git a/hook.go b/hook.go index 549d85a..08133ac 100644 --- a/hook.go +++ b/hook.go @@ -6,6 +6,15 @@ type Hook interface { Run(e *Event, level Level, message string) } +// HookFunc is an adaptor to allow the use of an ordinary function +// as a Hook. +type HookFunc func(e *Event, level Level, message string) + +// Run implements the Hook interface. +func (h HookFunc) Run(e *Event, level Level, message string) { + h(e, level, message) +} + // LevelHook applies a different hook for each level. type LevelHook struct { NoLevelHook, DebugHook, InfoHook, WarnHook, ErrorHook, FatalHook, PanicHook Hook diff --git a/hook_test.go b/hook_test.go index d051bdc..19bde9d 100644 --- a/hook_test.go +++ b/hook_test.go @@ -6,204 +6,135 @@ import ( "testing" ) -type LevelNameHook struct{} - -func (h LevelNameHook) Run(e *Event, level Level, msg string) { - levelName := level.String() - if level == NoLevel { - levelName = "nolevel" - } - e.Str("level_name", levelName) -} - -type SimpleHook struct{} - -func (h SimpleHook) Run(e *Event, level Level, msg string) { - e.Bool("has_level", level != NoLevel) - e.Str("test", "logged") -} - -type CopyHook struct{} - -func (h CopyHook) Run(e *Event, level Level, msg string) { - hasLevel := level != NoLevel - e.Bool("copy_has_level", hasLevel) - if hasLevel { - e.Str("copy_level", level.String()) - } - e.Str("copy_msg", msg) -} - -type NopHook struct{} - -func (h NopHook) Run(e *Event, level Level, msg string) { -} - var ( - levelNameHook LevelNameHook - simpleHook SimpleHook - copyHook CopyHook - nopHook NopHook + levelNameHook = HookFunc(func(e *Event, level Level, msg string) { + levelName := level.String() + if level == NoLevel { + levelName = "nolevel" + } + e.Str("level_name", levelName) + }) + simpleHook = HookFunc(func(e *Event, level Level, msg string) { + e.Bool("has_level", level != NoLevel) + e.Str("test", "logged") + }) + copyHook = HookFunc(func(e *Event, level Level, msg string) { + hasLevel := level != NoLevel + e.Bool("copy_has_level", hasLevel) + if hasLevel { + e.Str("copy_level", level.String()) + } + e.Str("copy_msg", msg) + }) + nopHook = HookFunc(func(e *Event, level Level, message string) { + }) + discardHook = HookFunc(func(e *Event, level Level, message string) { + e.Discard() + }) ) func TestHook(t *testing.T) { - t.Run("Message", func(t *testing.T) { - out := &bytes.Buffer{} - log := New(out).Hook(levelNameHook) - log.Log().Msg("test message") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"level_name":"nolevel","message":"test message"}`+"\n"; got != want { - t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) - } - }) - t.Run("NoLevel", func(t *testing.T) { - out := &bytes.Buffer{} - log := New(out).Hook(levelNameHook) - log.Log().Msg("") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"level_name":"nolevel"}`+"\n"; got != want { - t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) - } - }) - t.Run("Print", func(t *testing.T) { - out := &bytes.Buffer{} - log := New(out).Hook(levelNameHook) - log.Print("") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"debug","level_name":"debug"}`+"\n"; got != want { - t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) - } - }) - t.Run("Error", func(t *testing.T) { - out := &bytes.Buffer{} - log := New(out).Hook(levelNameHook) - log.Error().Msg("") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"error","level_name":"error"}`+"\n"; got != want { - t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) - } - }) - t.Run("Copy/1", func(t *testing.T) { - out := &bytes.Buffer{} - log := New(out).Hook(copyHook) - log.Log().Msg("") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"copy_has_level":false,"copy_msg":""}`+"\n"; got != want { - t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) - } - }) - t.Run("Copy/2", func(t *testing.T) { - out := &bytes.Buffer{} - log := New(out).Hook(copyHook) - log.Info().Msg("a message") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"info","copy_has_level":true,"copy_level":"info","copy_msg":"a message","message":"a message"}`+"\n"; got != want { - t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) - } - }) - t.Run("Multi", func(t *testing.T) { - out := &bytes.Buffer{} - log := New(out).Hook(levelNameHook).Hook(simpleHook) - log.Error().Msg("") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"error","level_name":"error","has_level":true,"test":"logged"}`+"\n"; got != want { - t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) - } - }) - t.Run("Multi/Message", func(t *testing.T) { - out := &bytes.Buffer{} - log := New(out).Hook(levelNameHook).Hook(simpleHook) - log.Error().Msg("a message") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"error","level_name":"error","has_level":true,"test":"logged","message":"a message"}`+"\n"; got != want { - t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) - } - }) - t.Run("Output/single/pre", func(t *testing.T) { - ignored := &bytes.Buffer{} - out := &bytes.Buffer{} - log := New(ignored).Hook(levelNameHook).Output(out) - log.Error().Msg("") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"error","level_name":"error"}`+"\n"; got != want { - t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) - } - }) - t.Run("Output/single/post", func(t *testing.T) { - ignored := &bytes.Buffer{} - out := &bytes.Buffer{} - log := New(ignored).Output(out).Hook(levelNameHook) - log.Error().Msg("") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"error","level_name":"error"}`+"\n"; got != want { - t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) - } - }) - t.Run("Output/multi/pre", func(t *testing.T) { - ignored := &bytes.Buffer{} - out := &bytes.Buffer{} - log := New(ignored).Hook(levelNameHook).Hook(simpleHook).Output(out) - log.Error().Msg("") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"error","level_name":"error","has_level":true,"test":"logged"}`+"\n"; got != want { - t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) - } - }) - t.Run("Output/multi/post", func(t *testing.T) { - ignored := &bytes.Buffer{} - out := &bytes.Buffer{} - log := New(ignored).Output(out).Hook(levelNameHook).Hook(simpleHook) - log.Error().Msg("") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"error","level_name":"error","has_level":true,"test":"logged"}`+"\n"; got != want { - t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) - } - }) - t.Run("Output/mixed", func(t *testing.T) { - ignored := &bytes.Buffer{} - out := &bytes.Buffer{} - log := New(ignored).Hook(levelNameHook).Output(out).Hook(simpleHook) - log.Error().Msg("") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"error","level_name":"error","has_level":true,"test":"logged"}`+"\n"; got != want { - t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) - } - }) - t.Run("With/single/pre", func(t *testing.T) { - out := &bytes.Buffer{} - log := New(out).Hook(levelNameHook).With().Str("with", "pre").Logger() - log.Error().Msg("") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"error","with":"pre","level_name":"error"}`+"\n"; got != want { - t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) - } - }) - t.Run("With/single/post", func(t *testing.T) { - out := &bytes.Buffer{} - log := New(out).With().Str("with", "post").Logger().Hook(levelNameHook) - log.Error().Msg("") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"error","with":"post","level_name":"error"}`+"\n"; got != want { - t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) - } - }) - t.Run("With/multi/pre", func(t *testing.T) { - out := &bytes.Buffer{} - log := New(out).Hook(levelNameHook).Hook(simpleHook).With().Str("with", "pre").Logger() - log.Error().Msg("") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"error","with":"pre","level_name":"error","has_level":true,"test":"logged"}`+"\n"; got != want { - t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) - } - }) - t.Run("With/multi/post", func(t *testing.T) { - out := &bytes.Buffer{} - log := New(out).With().Str("with", "post").Logger().Hook(levelNameHook).Hook(simpleHook) - log.Error().Msg("") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"error","with":"post","level_name":"error","has_level":true,"test":"logged"}`+"\n"; got != want { - t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) - } - }) - t.Run("With/mixed", func(t *testing.T) { - out := &bytes.Buffer{} - log := New(out).Hook(levelNameHook).With().Str("with", "mixed").Logger().Hook(simpleHook) - log.Error().Msg("") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"error","with":"mixed","level_name":"error","has_level":true,"test":"logged"}`+"\n"; got != want { - t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) - } - }) - t.Run("None", func(t *testing.T) { - out := &bytes.Buffer{} - log := New(out) - log.Error().Msg("") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"error"}`+"\n"; got != want { - t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) - } - }) + tests := []struct { + name string + want string + test func(log Logger) + }{ + {"Message", `{"level_name":"nolevel","message":"test message"}` + "\n", func(log Logger) { + log = log.Hook(levelNameHook) + log.Log().Msg("test message") + }}, + {"NoLevel", `{"level_name":"nolevel"}` + "\n", func(log Logger) { + log = log.Hook(levelNameHook) + log.Log().Msg("") + }}, + {"Print", `{"level":"debug","level_name":"debug"}` + "\n", func(log Logger) { + log = log.Hook(levelNameHook) + log.Print("") + }}, + {"Error", `{"level":"error","level_name":"error"}` + "\n", func(log Logger) { + log = log.Hook(levelNameHook) + log.Error().Msg("") + }}, + {"Copy/1", `{"copy_has_level":false,"copy_msg":""}` + "\n", func(log Logger) { + log = log.Hook(copyHook) + log.Log().Msg("") + }}, + {"Copy/2", `{"level":"info","copy_has_level":true,"copy_level":"info","copy_msg":"a message","message":"a message"}` + "\n", func(log Logger) { + log = log.Hook(copyHook) + log.Info().Msg("a message") + }}, + {"Multi", `{"level":"error","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) { + log = log.Hook(levelNameHook).Hook(simpleHook) + log.Error().Msg("") + }}, + {"Multi/Message", `{"level":"error","level_name":"error","has_level":true,"test":"logged","message":"a message"}` + "\n", func(log Logger) { + log = log.Hook(levelNameHook).Hook(simpleHook) + log.Error().Msg("a message") + }}, + {"Output/single/pre", `{"level":"error","level_name":"error"}` + "\n", func(log Logger) { + ignored := &bytes.Buffer{} + log = New(ignored).Hook(levelNameHook).Output(log.w) + log.Error().Msg("") + }}, + {"Output/single/post", `{"level":"error","level_name":"error"}` + "\n", func(log Logger) { + ignored := &bytes.Buffer{} + log = New(ignored).Output(log.w).Hook(levelNameHook) + log.Error().Msg("") + }}, + {"Output/multi/pre", `{"level":"error","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) { + ignored := &bytes.Buffer{} + log = New(ignored).Hook(levelNameHook).Hook(simpleHook).Output(log.w) + log.Error().Msg("") + }}, + {"Output/multi/post", `{"level":"error","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) { + ignored := &bytes.Buffer{} + log = New(ignored).Output(log.w).Hook(levelNameHook).Hook(simpleHook) + log.Error().Msg("") + }}, + {"Output/mixed", `{"level":"error","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) { + ignored := &bytes.Buffer{} + log = New(ignored).Hook(levelNameHook).Output(log.w).Hook(simpleHook) + log.Error().Msg("") + }}, + {"With/single/pre", `{"level":"error","with":"pre","level_name":"error"}` + "\n", func(log Logger) { + log = log.Hook(levelNameHook).With().Str("with", "pre").Logger() + log.Error().Msg("") + }}, + {"With/single/post", `{"level":"error","with":"post","level_name":"error"}` + "\n", func(log Logger) { + log = log.With().Str("with", "post").Logger().Hook(levelNameHook) + log.Error().Msg("") + }}, + {"With/multi/pre", `{"level":"error","with":"pre","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) { + log = log.Hook(levelNameHook).Hook(simpleHook).With().Str("with", "pre").Logger() + log.Error().Msg("") + }}, + {"With/multi/post", `{"level":"error","with":"post","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) { + log = log.With().Str("with", "post").Logger().Hook(levelNameHook).Hook(simpleHook) + log.Error().Msg("") + }}, + {"With/mixed", `{"level":"error","with":"mixed","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) { + log = log.Hook(levelNameHook).With().Str("with", "mixed").Logger().Hook(simpleHook) + log.Error().Msg("") + }}, + {"Discard", "", func(log Logger) { + log = log.Hook(discardHook) + log.Log().Msg("test message") + }}, + {"None", `{"level":"error"}` + "\n", func(log Logger) { + log.Error().Msg("") + }}, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + out := &bytes.Buffer{} + log := New(out) + tt.test(log) + if got, want := decodeIfBinaryToString(out.Bytes()), tt.want; got != want { + t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) + } + }) + } } func BenchmarkHooks(b *testing.B) {