From aefe3c4cb6dbb9c140d048523a4f2bba566cb74a Mon Sep 17 00:00:00 2001 From: Josh Kline Date: Sat, 15 May 2021 13:18:56 -0700 Subject: [PATCH 1/3] Expose zaptest.observer.(*ObservedLogs).Filter Export method `observer.(*ObservedLogs).Filter` to allow consumers to filter logs by arbitrary functions for testing. 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. --- zaptest/observer/observer.go | 14 ++++++++------ zaptest/observer/observer_test.go | 15 ++++++++++++++- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/zaptest/observer/observer.go b/zaptest/observer/observer.go index 6ae58f5d6..cdd7f5f21 100644 --- a/zaptest/observer/observer.go +++ b/zaptest/observer/observer.go @@ -80,21 +80,21 @@ func (o *ObservedLogs) AllUntimed() []LoggedEntry { // 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 @@ -106,7 +106,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 @@ -116,13 +116,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) } } diff --git a/zaptest/observer/observer_test.go b/zaptest/observer/observer_test.go index 4e8139493..815e71d16 100644 --- a/zaptest/observer/observer_test.go +++ b/zaptest/observer/observer_test.go @@ -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 @@ -219,6 +219,19 @@ 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 := []LoggedEntry{} + w = append(w, logs[0:5]...) + w = append(w, logs[7]) + return w + }(), + }, } for _, tt := range tests { From 130ef27d8fe50714d8671e172988c09a87b41e7d Mon Sep 17 00:00:00 2001 From: Josh Kline Date: Sat, 15 May 2021 14:17:02 -0700 Subject: [PATCH 2/3] Add zaptest.observer.(*ObservedLogs).FilterLevelExact Method `observer.(*ObservedLogs).FilterLevelExact` allows consumers to select entries logged at a given level, for testing. --- zaptest/observer/observer.go | 7 +++++++ zaptest/observer/observer_test.go | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/zaptest/observer/observer.go b/zaptest/observer/observer.go index cdd7f5f21..f3f6116b6 100644 --- a/zaptest/observer/observer.go +++ b/zaptest/observer/observer.go @@ -78,6 +78,13 @@ 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 { diff --git a/zaptest/observer/observer_test.go b/zaptest/observer/observer_test.go index 815e71d16..615948812 100644 --- a/zaptest/observer/observer_test.go +++ b/zaptest/observer/observer_test.go @@ -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) @@ -232,6 +240,11 @@ func TestFilters(t *testing.T) { return w }(), }, + { + msg: "filter level", + filtered: sink.FilterLevelExact(zap.WarnLevel), + want: logs[9:10], + }, } for _, tt := range tests { From 4223fa671d967841e5ab33f1d570352193a40f16 Mon Sep 17 00:00:00 2001 From: Abhinav Gupta Date: Mon, 7 Jun 2021 16:22:06 -0700 Subject: [PATCH 3/3] observer_test: Pre-allocate slice --- zaptest/observer/observer_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zaptest/observer/observer_test.go b/zaptest/observer/observer_test.go index 615948812..9f179d02e 100644 --- a/zaptest/observer/observer_test.go +++ b/zaptest/observer/observer_test.go @@ -234,7 +234,7 @@ func TestFilters(t *testing.T) { }), want: func() []LoggedEntry { // Do not modify logs slice. - w := []LoggedEntry{} + w := make([]LoggedEntry, 0, len(logs)) w = append(w, logs[0:5]...) w = append(w, logs[7]) return w