Skip to content

Commit

Permalink
fix: refactor zapfilter to support various zapcore.Core helpers
Browse files Browse the repository at this point in the history
-> Check, Enabled, etc

Signed-off-by: Manfred Touron <[email protected]>
  • Loading branch information
moul committed Sep 9, 2020
1 parent 3f03e74 commit bdb78ba
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 28 deletions.
54 changes: 29 additions & 25 deletions zapfilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,59 +12,63 @@ import (
// FilterFunc is used to check whether to filter the given entry and filters out.
type FilterFunc func(zapcore.Entry, []zapcore.Field) bool

type filteringCore struct {
next zapcore.Core
filter FilterFunc
}

// NewFilteringCore returns a core middleware that uses the given filter function
// to decide whether to actually call Write on the next core in the chain.
// NewFilteringCore returns a core middleware that uses the given filter function to decide
// whether to actually call Write on the next core in the chain.
func NewFilteringCore(next zapcore.Core, filter FilterFunc) zapcore.Core {
if filter == nil {
filter = alwaysFalseFilter
}
return &filteringCore{next, filter}
}

func (core *filteringCore) Check(entry zapcore.Entry, checked *zapcore.CheckedEntry) *zapcore.CheckedEntry {
if core.Enabled(entry.Level) {
return checked.AddCore(entry, core)
type filteringCore struct {
next zapcore.Core
filter FilterFunc
}

// Check determines whether the supplied zapcore.Entry should be logged.
// If the entry should be logged, the filteringCore adds itself to the zapcore.CheckedEntry
// and returns the results.
func (core *filteringCore) Check(entry zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
// FIXME: consider calling downstream core.Check too, but need to document how to
// properly set logging level.
if core.filter(entry, nil) {
ce = ce.AddCore(entry, core)
}
return checked
return ce
}

// Write determines whether the supplied zapcore.Entry with provided []zapcore.Field should
// be logged, then calls the wrapped zapcore.Write.
func (core *filteringCore) Write(entry zapcore.Entry, fields []zapcore.Field) error {
if !core.filter(entry, fields) {
return nil
}
return core.next.Write(entry, fields)
}

// With adds structured context to the wrapped zapcore.Core.
func (core *filteringCore) With(fields []zapcore.Field) zapcore.Core {
clone := core.clone()
clone.next = clone.next.With(fields)
return clone
return &filteringCore{
next: core.next.With(fields),
filter: core.filter,
}
}

// Enabled asks the wrapped zapcore.Core to decide whether a given logging level is enabled
// when logging a message.
func (core *filteringCore) Enabled(level zapcore.Level) bool {
// FIXME: Maybe it's better to always return true and only rely on the Check() func?
// Another way to consider it is to keep the smaller log level configured on
// zapfilter.
return core.next.Enabled(level)
}

// Sync flushed buffered logs (if any).
func (core *filteringCore) Sync() error {
return core.next.Sync()
}

func (core *filteringCore) clone() *filteringCore {
return &filteringCore{
next: core.next,
filter: core.filter,
}
}

func (core *filteringCore) Core() zapcore.Core {
return core
}

// ByNamespaces takes a list of patterns to filter out logs based on their namespaces.
// Patterns are checked using path.Match.
func ByNamespaces(input string) FilterFunc {
Expand Down
44 changes: 41 additions & 3 deletions zapfilter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,7 @@ func ExampleFilterFunc_custom() {

// Output:
// {"level":"debug","msg":"hello city!"}
// {"level":"debug","msg":"hello region!"}
// {"level":"debug","msg":"hello universe!"}
// {"level":"debug","msg":"hello multiverse!"}
// {"level":"debug","msg":"hello solar system!"}
}

func ExampleByNamespaces() {
Expand Down Expand Up @@ -345,3 +343,43 @@ func TestParseRules(t *testing.T) {
})
}
}

func TestCheck(t *testing.T) {
cases := []struct {
rules string
namespace string
checked bool
}{
{"", "", false},
{"", "foo", false},
{"*", "", true},
{"*", "foo", true},
{"*:foo", "", false},
{"*:foo", "foo", true},
{"*:foo", "bar", false},
}
for _, tc := range cases {
name := fmt.Sprintf("%s-%s", tc.rules, tc.namespace)
t.Run(name, func(t *testing.T) {
next, _ := observer.New(zapcore.DebugLevel)
filter, err := zapfilter.ParseRules(tc.rules)
if err != nil {
return
}

core := zapfilter.NewFilteringCore(next, filter)
logger := zap.New(core)

if tc.namespace != "" {
logger = logger.Named(tc.namespace)
}

entry := logger.Check(zap.DebugLevel, "")
if tc.checked {
require.NotNil(t, entry)
} else {
require.Nil(t, entry)
}
})
}
}

0 comments on commit bdb78ba

Please sign in to comment.