Codebase list golang-go.uber-zap / 7cb1af2
zaptest/observer: Support filtering by message or field (#386) * zaptest/observer: Support filtering by message or field Add `Filter*` functions to filter a set of ObservedLogs and return a new set of ObservedLogs. This allows chaining of multiple filters such as `sink.FilterMessage(..).FilterField(..)` for easier use in tests. * Handle duplicate fields Prashant Varanasi authored 7 years ago Akshay Shah committed 7 years ago
2 changed file(s) with 95 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
7979 return ret
8080 }
8181
82 // FilterMessage filters entries to those that have the specified message.
83 func (o *ObservedLogs) FilterMessage(msg string) *ObservedLogs {
84 return o.filter(func(e LoggedEntry) bool {
85 return e.Message == msg
86 })
87 }
88
89 // FilterField filters entries to those that have the specified field.
90 func (o *ObservedLogs) FilterField(field zapcore.Field) *ObservedLogs {
91 return o.filter(func(e LoggedEntry) bool {
92 for _, ctxField := range e.Context {
93 if ctxField == field {
94 return true
95 }
96 }
97 return false
98 })
99 }
100
101 func (o *ObservedLogs) filter(match func(LoggedEntry) bool) *ObservedLogs {
102 o.mu.RLock()
103 defer o.mu.RUnlock()
104
105 var filtered []LoggedEntry
106 for _, entry := range o.logs {
107 if match(entry) {
108 filtered = append(filtered, entry)
109 }
110 }
111 return &ObservedLogs{logs: filtered}
112 }
113
82114 func (o *ObservedLogs) add(log LoggedEntry) {
83115 o.mu.Lock()
84116 o.logs = append(o.logs, log)
117117 },
118118 }, logs.All(), "expected no field sharing between With siblings")
119119 }
120
121 func TestFilters(t *testing.T) {
122 logs := []LoggedEntry{
123 {
124 Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "log a"},
125 Context: []zapcore.Field{zap.String("fStr", "1"), zap.Int("a", 1)},
126 },
127 {
128 Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "log a"},
129 Context: []zapcore.Field{zap.String("fStr", "2"), zap.Int("b", 2)},
130 },
131 {
132 Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "log b"},
133 Context: []zapcore.Field{zap.Int("a", 1), zap.Int("b", 2)},
134 },
135 {
136 Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "log c"},
137 Context: []zapcore.Field{zap.Int("a", 1), zap.Namespace("ns"), zap.Int("a", 2)},
138 },
139 }
140
141 logger, sink := New(zap.InfoLevel)
142 for _, log := range logs {
143 logger.Write(log.Entry, log.Context)
144 }
145
146 tests := []struct {
147 msg string
148 filtered *ObservedLogs
149 want []LoggedEntry
150 }{
151 {
152 msg: "filter by message",
153 filtered: sink.FilterMessage("log a"),
154 want: logs[0:2],
155 },
156 {
157 msg: "filter by field",
158 filtered: sink.FilterField(zap.String("fStr", "1")),
159 want: logs[0:1],
160 },
161 {
162 msg: "filter by message and field",
163 filtered: sink.FilterMessage("log a").FilterField(zap.Int("b", 2)),
164 want: logs[1:2],
165 },
166 {
167 msg: "filter by field with duplicate fields",
168 filtered: sink.FilterField(zap.Int("a", 2)),
169 want: logs[3:4],
170 },
171 {
172 msg: "filter doesn't match any messages",
173 filtered: sink.FilterMessage("no match"),
174 want: []LoggedEntry{},
175 },
176 }
177
178 for _, tt := range tests {
179 got := tt.filtered.AllUntimed()
180 assert.Equal(t, tt.want, got, tt.msg)
181 }
182 }