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,10 +60,12 @@ func (e *Event) write() (err error) {
if e == nil { if e == nil {
return nil return nil
} }
e.buf = enc.AppendEndMarker(e.buf) if e.level != Disabled {
e.buf = enc.AppendLineBreak(e.buf) e.buf = enc.AppendEndMarker(e.buf)
if e.w != nil { e.buf = enc.AppendLineBreak(e.buf)
_, err = e.w.WriteLevel(e.level, e.buf) if e.w != nil {
_, 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,204 +6,135 @@ import (
"testing" "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 ( var (
levelNameHook LevelNameHook levelNameHook = HookFunc(func(e *Event, level Level, msg string) {
simpleHook SimpleHook levelName := level.String()
copyHook CopyHook if level == NoLevel {
nopHook NopHook 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) { 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
log.Log().Msg("test message") test func(log Logger)
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) {"Message", `{"level_name":"nolevel","message":"test message"}` + "\n", func(log Logger) {
} log = log.Hook(levelNameHook)
}) log.Log().Msg("test message")
t.Run("NoLevel", func(t *testing.T) { }},
out := &bytes.Buffer{} {"NoLevel", `{"level_name":"nolevel"}` + "\n", func(log Logger) {
log := New(out).Hook(levelNameHook) log = log.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)
}) log.Print("")
t.Run("Print", func(t *testing.T) { }},
out := &bytes.Buffer{} {"Error", `{"level":"error","level_name":"error"}` + "\n", func(log Logger) {
log := New(out).Hook(levelNameHook) log = log.Hook(levelNameHook)
log.Print("") log.Error().Msg("")
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) {"Copy/1", `{"copy_has_level":false,"copy_msg":""}` + "\n", func(log Logger) {
} log = log.Hook(copyHook)
}) log.Log().Msg("")
t.Run("Error", func(t *testing.T) { }},
out := &bytes.Buffer{} {"Copy/2", `{"level":"info","copy_has_level":true,"copy_level":"info","copy_msg":"a message","message":"a message"}` + "\n", func(log Logger) {
log := New(out).Hook(levelNameHook) log = log.Hook(copyHook)
log.Error().Msg("") log.Info().Msg("a message")
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) {"Multi", `{"level":"error","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) {
} log = log.Hook(levelNameHook).Hook(simpleHook)
}) log.Error().Msg("")
t.Run("Copy/1", func(t *testing.T) { }},
out := &bytes.Buffer{} {"Multi/Message", `{"level":"error","level_name":"error","has_level":true,"test":"logged","message":"a message"}` + "\n", func(log Logger) {
log := New(out).Hook(copyHook) log = log.Hook(levelNameHook).Hook(simpleHook)
log.Log().Msg("") log.Error().Msg("a message")
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) {"Output/single/pre", `{"level":"error","level_name":"error"}` + "\n", func(log Logger) {
} ignored := &bytes.Buffer{}
}) log = New(ignored).Hook(levelNameHook).Output(log.w)
t.Run("Copy/2", func(t *testing.T) { log.Error().Msg("")
out := &bytes.Buffer{} }},
log := New(out).Hook(copyHook) {"Output/single/post", `{"level":"error","level_name":"error"}` + "\n", func(log Logger) {
log.Info().Msg("a message") ignored := &bytes.Buffer{}
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 { log = New(ignored).Output(log.w).Hook(levelNameHook)
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) log.Error().Msg("")
} }},
}) {"Output/multi/pre", `{"level":"error","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) {
t.Run("Multi", func(t *testing.T) { ignored := &bytes.Buffer{}
out := &bytes.Buffer{} log = New(ignored).Hook(levelNameHook).Hook(simpleHook).Output(log.w)
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 { {"Output/multi/post", `{"level":"error","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) {
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) ignored := &bytes.Buffer{}
} log = New(ignored).Output(log.w).Hook(levelNameHook).Hook(simpleHook)
}) log.Error().Msg("")
t.Run("Multi/Message", func(t *testing.T) { }},
out := &bytes.Buffer{} {"Output/mixed", `{"level":"error","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) {
log := New(out).Hook(levelNameHook).Hook(simpleHook) ignored := &bytes.Buffer{}
log.Error().Msg("a message") log = New(ignored).Hook(levelNameHook).Output(log.w).Hook(simpleHook)
if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"error","level_name":"error","has_level":true,"test":"logged","message":"a message"}`+"\n"; got != want { log.Error().Msg("")
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("Output/single/pre", func(t *testing.T) { log.Error().Msg("")
ignored := &bytes.Buffer{} }},
out := &bytes.Buffer{} {"With/single/post", `{"level":"error","with":"post","level_name":"error"}` + "\n", func(log Logger) {
log := New(ignored).Hook(levelNameHook).Output(out) log = log.With().Str("with", "post").Logger().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) {"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("")
t.Run("Output/single/post", func(t *testing.T) { }},
ignored := &bytes.Buffer{} {"With/multi/post", `{"level":"error","with":"post","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) {
out := &bytes.Buffer{} log = log.With().Str("with", "post").Logger().Hook(levelNameHook).Hook(simpleHook)
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 { {"With/mixed", `{"level":"error","with":"mixed","level_name":"error","has_level":true,"test":"logged"}` + "\n", func(log Logger) {
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) log = log.Hook(levelNameHook).With().Str("with", "mixed").Logger().Hook(simpleHook)
} log.Error().Msg("")
}) }},
t.Run("Output/multi/pre", func(t *testing.T) { {"Discard", "", func(log Logger) {
ignored := &bytes.Buffer{} log = log.Hook(discardHook)
out := &bytes.Buffer{} log.Log().Msg("test message")
log := New(ignored).Hook(levelNameHook).Hook(simpleHook).Output(out) }},
log.Error().Msg("") {"None", `{"level":"error"}` + "\n", func(log Logger) {
if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"error","level_name":"error","has_level":true,"test":"logged"}`+"\n"; got != want { log.Error().Msg("")
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) }},
} }
}) for _, tt := range tests {
t.Run("Output/multi/post", func(t *testing.T) { tt := tt
ignored := &bytes.Buffer{} t.Run(tt.name, func(t *testing.T) {
out := &bytes.Buffer{} out := &bytes.Buffer{}
log := New(ignored).Output(out).Hook(levelNameHook).Hook(simpleHook) log := New(out)
log.Error().Msg("") tt.test(log)
if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"error","level_name":"error","has_level":true,"test":"logged"}`+"\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)
} }
}) })
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)
}
})
} }
func BenchmarkHooks(b *testing.B) { func BenchmarkHooks(b *testing.B) {