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
This commit is contained in:
Olivier Poitrey 2018-07-26 15:53:02 -07:00
parent bae001d86b
commit 71e1f5e052
3 changed files with 147 additions and 199 deletions

View File

@ -60,11 +60,13 @@ func (e *Event) write() (err error) {
if e == nil { if e == nil {
return nil return nil
} }
if e.level != Disabled {
e.buf = enc.AppendEndMarker(e.buf) e.buf = enc.AppendEndMarker(e.buf)
e.buf = enc.AppendLineBreak(e.buf) e.buf = enc.AppendLineBreak(e.buf)
if e.w != nil { if e.w != nil {
_, err = e.w.WriteLevel(e.level, e.buf) _, err = e.w.WriteLevel(e.level, e.buf)
} }
}
eventPool.Put(e) eventPool.Put(e)
return return
} }
@ -72,7 +74,13 @@ func (e *Event) write() (err error) {
// Enabled return false if the *Event is going to be filtered out by // Enabled return false if the *Event is going to be filtered out by
// log level or sampling. // log level or sampling.
func (e *Event) Enabled() bool { 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. // Msg sends the *Event with msg added as the message field if not empty.

View File

@ -6,6 +6,15 @@ type Hook interface {
Run(e *Event, level Level, message string) 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. // LevelHook applies a different hook for each level.
type LevelHook struct { type LevelHook struct {
NoLevelHook, DebugHook, InfoHook, WarnHook, ErrorHook, FatalHook, PanicHook Hook NoLevelHook, DebugHook, InfoHook, WarnHook, ErrorHook, FatalHook, PanicHook Hook

View File

@ -6,205 +6,136 @@ import (
"testing" "testing"
) )
type LevelNameHook struct{} var (
levelNameHook = HookFunc(func(e *Event, level Level, msg string) {
func (h LevelNameHook) Run(e *Event, level Level, msg string) {
levelName := level.String() levelName := level.String()
if level == NoLevel { if level == NoLevel {
levelName = "nolevel" levelName = "nolevel"
} }
e.Str("level_name", levelName) e.Str("level_name", levelName)
} })
simpleHook = HookFunc(func(e *Event, level Level, msg string) {
type SimpleHook struct{}
func (h SimpleHook) Run(e *Event, level Level, msg string) {
e.Bool("has_level", level != NoLevel) e.Bool("has_level", level != NoLevel)
e.Str("test", "logged") e.Str("test", "logged")
} })
copyHook = HookFunc(func(e *Event, level Level, msg string) {
type CopyHook struct{}
func (h CopyHook) Run(e *Event, level Level, msg string) {
hasLevel := level != NoLevel hasLevel := level != NoLevel
e.Bool("copy_has_level", hasLevel) e.Bool("copy_has_level", hasLevel)
if hasLevel { if hasLevel {
e.Str("copy_level", level.String()) e.Str("copy_level", level.String())
} }
e.Str("copy_msg", msg) e.Str("copy_msg", msg)
} })
nopHook = HookFunc(func(e *Event, level Level, message string) {
type NopHook struct{} })
discardHook = HookFunc(func(e *Event, level Level, message string) {
func (h NopHook) Run(e *Event, level Level, msg string) { e.Discard()
} })
var (
levelNameHook LevelNameHook
simpleHook SimpleHook
copyHook CopyHook
nopHook NopHook
) )
func TestHook(t *testing.T) { func TestHook(t *testing.T) {
t.Run("Message", func(t *testing.T) { tests := []struct {
out := &bytes.Buffer{} name string
log := New(out).Hook(levelNameHook) 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") 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) {"NoLevel", `{"level_name":"nolevel"}` + "\n", func(log Logger) {
} log = log.Hook(levelNameHook)
})
t.Run("NoLevel", func(t *testing.T) {
out := &bytes.Buffer{}
log := New(out).Hook(levelNameHook)
log.Log().Msg("") 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) {"Print", `{"level":"debug","level_name":"debug"}` + "\n", func(log Logger) {
} log = log.Hook(levelNameHook)
})
t.Run("Print", func(t *testing.T) {
out := &bytes.Buffer{}
log := New(out).Hook(levelNameHook)
log.Print("") 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) {"Error", `{"level":"error","level_name":"error"}` + "\n", func(log Logger) {
} log = log.Hook(levelNameHook)
})
t.Run("Error", func(t *testing.T) {
out := &bytes.Buffer{}
log := New(out).Hook(levelNameHook)
log.Error().Msg("") 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) {"Copy/1", `{"copy_has_level":false,"copy_msg":""}` + "\n", func(log Logger) {
} log = log.Hook(copyHook)
})
t.Run("Copy/1", func(t *testing.T) {
out := &bytes.Buffer{}
log := New(out).Hook(copyHook)
log.Log().Msg("") 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) {"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)
})
t.Run("Copy/2", func(t *testing.T) {
out := &bytes.Buffer{}
log := New(out).Hook(copyHook)
log.Info().Msg("a message") 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) {"Multi", `{"level":"error","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) {
} log = log.Hook(levelNameHook).Hook(simpleHook)
})
t.Run("Multi", func(t *testing.T) {
out := &bytes.Buffer{}
log := New(out).Hook(levelNameHook).Hook(simpleHook)
log.Error().Msg("") 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) {"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)
})
t.Run("Multi/Message", func(t *testing.T) {
out := &bytes.Buffer{}
log := New(out).Hook(levelNameHook).Hook(simpleHook)
log.Error().Msg("a message") 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) {"Output/single/pre", `{"level":"error","level_name":"error"}` + "\n", func(log Logger) {
}
})
t.Run("Output/single/pre", func(t *testing.T) {
ignored := &bytes.Buffer{} ignored := &bytes.Buffer{}
out := &bytes.Buffer{} log = New(ignored).Hook(levelNameHook).Output(log.w)
log := New(ignored).Hook(levelNameHook).Output(out)
log.Error().Msg("") 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) {"Output/single/post", `{"level":"error","level_name":"error"}` + "\n", func(log Logger) {
}
})
t.Run("Output/single/post", func(t *testing.T) {
ignored := &bytes.Buffer{} ignored := &bytes.Buffer{}
out := &bytes.Buffer{} log = New(ignored).Output(log.w).Hook(levelNameHook)
log := New(ignored).Output(out).Hook(levelNameHook)
log.Error().Msg("") 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) {"Output/multi/pre", `{"level":"error","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) {
}
})
t.Run("Output/multi/pre", func(t *testing.T) {
ignored := &bytes.Buffer{} ignored := &bytes.Buffer{}
out := &bytes.Buffer{} log = New(ignored).Hook(levelNameHook).Hook(simpleHook).Output(log.w)
log := New(ignored).Hook(levelNameHook).Hook(simpleHook).Output(out)
log.Error().Msg("") 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) {"Output/multi/post", `{"level":"error","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) {
}
})
t.Run("Output/multi/post", func(t *testing.T) {
ignored := &bytes.Buffer{} ignored := &bytes.Buffer{}
out := &bytes.Buffer{} log = New(ignored).Output(log.w).Hook(levelNameHook).Hook(simpleHook)
log := New(ignored).Output(out).Hook(levelNameHook).Hook(simpleHook)
log.Error().Msg("") 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) {"Output/mixed", `{"level":"error","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) {
}
})
t.Run("Output/mixed", func(t *testing.T) {
ignored := &bytes.Buffer{} ignored := &bytes.Buffer{}
out := &bytes.Buffer{} log = New(ignored).Hook(levelNameHook).Output(log.w).Hook(simpleHook)
log := New(ignored).Hook(levelNameHook).Output(out).Hook(simpleHook)
log.Error().Msg("") 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) {"With/single/pre", `{"level":"error","with":"pre","level_name":"error"}` + "\n", func(log Logger) {
} log = log.Hook(levelNameHook).With().Str("with", "pre").Logger()
})
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("") 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) {"With/single/post", `{"level":"error","with":"post","level_name":"error"}` + "\n", func(log Logger) {
} log = log.With().Str("with", "post").Logger().Hook(levelNameHook)
})
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("") 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) {"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()
})
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("") 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) {"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)
})
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("") 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) {"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)
})
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("") 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) {"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 {
t.Run("None", func(t *testing.T) { tt := tt
t.Run(tt.name, func(t *testing.T) {
out := &bytes.Buffer{} out := &bytes.Buffer{}
log := New(out) log := New(out)
log.Error().Msg("") tt.test(log)
if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"error"}`+"\n"; got != want { if got, want := decodeIfBinaryToString(out.Bytes()), tt.want; got != want {
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
} }
}) })
} }
}
func BenchmarkHooks(b *testing.B) { func BenchmarkHooks(b *testing.B) {
logger := New(ioutil.Discard) logger := New(ioutil.Discard)