Skip to content

Commit

Permalink
zaptest/ObservedLogs: Expose more filters (#943)
Browse files Browse the repository at this point in the history
Currently consumers can call `(*ObservedLogs).All` and proceed to filter
using `[]LoggedEntry` directly, but the result is then incompatible with
existing methods such as `FilterMessage` because there is now way to
re-construct an `*ObservedLogs` object.

Add ObservedLogs.Filter and ObservedLogs.FilterLevelExact to allow
filtering by arbitrary functions, or by exact level matches.
  • Loading branch information
jkanywhere committed Jun 8, 2021
1 parent 084fb2a commit c05967d
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 7 deletions.
21 changes: 15 additions & 6 deletions zaptest/observer/observer.go
Expand Up @@ -78,23 +78,30 @@ func (o *ObservedLogs) AllUntimed() []LoggedEntry {
return ret
}

// FilterLevelExact filters entries to those logged at exactly the given level.
func (o *ObservedLogs) FilterLevelExact(level zapcore.Level) *ObservedLogs {
return o.Filter(func(e LoggedEntry) bool {
return e.Level == level
})
}

// FilterMessage filters entries to those that have the specified message.
func (o *ObservedLogs) FilterMessage(msg string) *ObservedLogs {
return o.filter(func(e LoggedEntry) bool {
return o.Filter(func(e LoggedEntry) bool {
return e.Message == msg
})
}

// FilterMessageSnippet filters entries to those that have a message containing the specified snippet.
func (o *ObservedLogs) FilterMessageSnippet(snippet string) *ObservedLogs {
return o.filter(func(e LoggedEntry) bool {
return o.Filter(func(e LoggedEntry) bool {
return strings.Contains(e.Message, snippet)
})
}

// FilterField filters entries to those that have the specified field.
func (o *ObservedLogs) FilterField(field zapcore.Field) *ObservedLogs {
return o.filter(func(e LoggedEntry) bool {
return o.Filter(func(e LoggedEntry) bool {
for _, ctxField := range e.Context {
if ctxField.Equals(field) {
return true
Expand All @@ -106,7 +113,7 @@ func (o *ObservedLogs) FilterField(field zapcore.Field) *ObservedLogs {

// FilterFieldKey filters entries to those that have the specified key.
func (o *ObservedLogs) FilterFieldKey(key string) *ObservedLogs {
return o.filter(func(e LoggedEntry) bool {
return o.Filter(func(e LoggedEntry) bool {
for _, ctxField := range e.Context {
if ctxField.Key == key {
return true
Expand All @@ -116,13 +123,15 @@ func (o *ObservedLogs) FilterFieldKey(key string) *ObservedLogs {
})
}

func (o *ObservedLogs) filter(match func(LoggedEntry) bool) *ObservedLogs {
// Filter returns a copy of this ObservedLogs containing only those entries
// for which the provided function returns true.
func (o *ObservedLogs) Filter(keep func(LoggedEntry) bool) *ObservedLogs {
o.mu.RLock()
defer o.mu.RUnlock()

var filtered []LoggedEntry
for _, entry := range o.logs {
if match(entry) {
if keep(entry) {
filtered = append(filtered, entry)
}
}
Expand Down
28 changes: 27 additions & 1 deletion zaptest/observer/observer_test.go
Expand Up @@ -55,7 +55,7 @@ func TestObserver(t *testing.T) {
assert.Equal(t, want, logs.AllUntimed(), "Unexpected contents from AllUntimed.")

all := logs.All()
require.Equal(t, 1, len(all), "Unexpected numbed of LoggedEntries returned from All.")
require.Equal(t, 1, len(all), "Unexpected number of LoggedEntries returned from All.")
assert.NotEqual(t, time.Time{}, all[0].Time, "Expected non-zero time on LoggedEntry.")

// copy & zero time for stable assertions
Expand Down Expand Up @@ -157,6 +157,14 @@ func TestFilters(t *testing.T) {
Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "any slice"},
Context: []zapcore.Field{zap.Any("filterMe", []string{"b"})},
},
{
Entry: zapcore.Entry{Level: zap.WarnLevel, Message: "danger will robinson"},
Context: []zapcore.Field{zap.Int("b", 42)},
},
{
Entry: zapcore.Entry{Level: zap.ErrorLevel, Message: "warp core breach"},
Context: []zapcore.Field{zap.Int("b", 42)},
},
}

logger, sink := New(zap.InfoLevel)
Expand Down Expand Up @@ -219,6 +227,24 @@ func TestFilters(t *testing.T) {
filtered: sink.FilterFieldKey("filterMe"),
want: logs[7:9],
},
{
msg: "filter by arbitrary function",
filtered: sink.Filter(func(e LoggedEntry) bool {
return len(e.Context) > 1
}),
want: func() []LoggedEntry {
// Do not modify logs slice.
w := make([]LoggedEntry, 0, len(logs))
w = append(w, logs[0:5]...)
w = append(w, logs[7])
return w
}(),
},
{
msg: "filter level",
filtered: sink.FilterLevelExact(zap.WarnLevel),
want: logs[9:10],
},
}

for _, tt := range tests {
Expand Down

0 comments on commit c05967d

Please sign in to comment.