1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package observer_test
22
23 import (
24 "testing"
25 "time"
26
27 "github.com/stretchr/testify/assert"
28 "github.com/stretchr/testify/require"
29
30 "go.uber.org/zap"
31 "go.uber.org/zap/zapcore"
32
33
34 . "go.uber.org/zap/zaptest/observer"
35 )
36
37 func assertEmpty(t testing.TB, logs *ObservedLogs) {
38 assert.Equal(t, 0, logs.Len(), "Expected empty ObservedLogs to have zero length.")
39 assert.Equal(t, []LoggedEntry{}, logs.All(), "Unexpected LoggedEntries in empty ObservedLogs.")
40 }
41
42 func TestObserver(t *testing.T) {
43 observer, logs := New(zap.InfoLevel)
44 assertEmpty(t, logs)
45
46 t.Run("LevelOf", func(t *testing.T) {
47 assert.Equal(t, zap.InfoLevel, zapcore.LevelOf(observer), "Observer reported the wrong log level.")
48 })
49
50 assert.NoError(t, observer.Sync(), "Unexpected failure in no-op Sync")
51
52 obs := zap.New(observer).With(zap.Int("i", 1))
53 obs.Info("foo")
54 obs.Debug("bar")
55 want := []LoggedEntry{{
56 Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "foo"},
57 Context: []zapcore.Field{zap.Int("i", 1)},
58 }}
59
60 assert.Equal(t, 1, logs.Len(), "Unexpected observed logs Len.")
61 assert.Equal(t, want, logs.AllUntimed(), "Unexpected contents from AllUntimed.")
62
63 all := logs.All()
64 require.Equal(t, 1, len(all), "Unexpected number of LoggedEntries returned from All.")
65 assert.NotEqual(t, time.Time{}, all[0].Time, "Expected non-zero time on LoggedEntry.")
66
67
68 untimed := append([]LoggedEntry{}, all...)
69 untimed[0].Time = time.Time{}
70 assert.Equal(t, want, untimed, "Unexpected LoggedEntries from All.")
71
72 assert.Equal(t, all, logs.TakeAll(), "Expected All and TakeAll to return identical results.")
73 assertEmpty(t, logs)
74 }
75
76 func TestObserverWith(t *testing.T) {
77 sf1, logs := New(zap.InfoLevel)
78
79
80
81
82 sf1 = sf1.With([]zapcore.Field{zap.Int("a", 1), zap.Int("b", 2)})
83
84 sf2 := sf1.With([]zapcore.Field{zap.Int("c", 3)})
85 sf3 := sf2.With([]zapcore.Field{zap.Int("d", 4)})
86 sf4 := sf2.With([]zapcore.Field{zap.Int("e", 5)})
87 ent := zapcore.Entry{Level: zap.InfoLevel, Message: "hello"}
88
89 for i, core := range []zapcore.Core{sf2, sf3, sf4} {
90 if ce := core.Check(ent, nil); ce != nil {
91 ce.Write(zap.Int("i", i))
92 }
93 }
94
95 assert.Equal(t, []LoggedEntry{
96 {
97 Entry: ent,
98 Context: []zapcore.Field{
99 zap.Int("a", 1),
100 zap.Int("b", 2),
101 zap.Int("c", 3),
102 zap.Int("i", 0),
103 },
104 },
105 {
106 Entry: ent,
107 Context: []zapcore.Field{
108 zap.Int("a", 1),
109 zap.Int("b", 2),
110 zap.Int("c", 3),
111 zap.Int("d", 4),
112 zap.Int("i", 1),
113 },
114 },
115 {
116 Entry: ent,
117 Context: []zapcore.Field{
118 zap.Int("a", 1),
119 zap.Int("b", 2),
120 zap.Int("c", 3),
121 zap.Int("e", 5),
122 zap.Int("i", 2),
123 },
124 },
125 }, logs.All(), "expected no field sharing between With siblings")
126 }
127
128 func TestFilters(t *testing.T) {
129 logs := []LoggedEntry{
130 {
131 Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "log a"},
132 Context: []zapcore.Field{zap.String("fStr", "1"), zap.Int("a", 1)},
133 },
134 {
135 Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "log a"},
136 Context: []zapcore.Field{zap.String("fStr", "2"), zap.Int("b", 2)},
137 },
138 {
139 Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "log b"},
140 Context: []zapcore.Field{zap.Int("a", 1), zap.Int("b", 2)},
141 },
142 {
143 Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "log c"},
144 Context: []zapcore.Field{zap.Int("a", 1), zap.Namespace("ns"), zap.Int("a", 2)},
145 },
146 {
147 Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "msg 1"},
148 Context: []zapcore.Field{zap.Int("a", 1), zap.Namespace("ns")},
149 },
150 {
151 Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "any map"},
152 Context: []zapcore.Field{zap.Any("map", map[string]string{"a": "b"})},
153 },
154 {
155 Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "any slice"},
156 Context: []zapcore.Field{zap.Any("slice", []string{"a"})},
157 },
158 {
159 Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "msg 2"},
160 Context: []zapcore.Field{zap.Int("b", 2), zap.Namespace("filterMe")},
161 },
162 {
163 Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "any slice"},
164 Context: []zapcore.Field{zap.Any("filterMe", []string{"b"})},
165 },
166 {
167 Entry: zapcore.Entry{Level: zap.WarnLevel, Message: "danger will robinson"},
168 Context: []zapcore.Field{zap.Int("b", 42)},
169 },
170 {
171 Entry: zapcore.Entry{Level: zap.ErrorLevel, Message: "warp core breach"},
172 Context: []zapcore.Field{zap.Int("b", 42)},
173 },
174 }
175
176 logger, sink := New(zap.InfoLevel)
177 for _, log := range logs {
178 assert.NoError(t, logger.Write(log.Entry, log.Context), "Unexpected error writing log entry.")
179 }
180
181 tests := []struct {
182 msg string
183 filtered *ObservedLogs
184 want []LoggedEntry
185 }{
186 {
187 msg: "filter by message",
188 filtered: sink.FilterMessage("log a"),
189 want: logs[0:2],
190 },
191 {
192 msg: "filter by field",
193 filtered: sink.FilterField(zap.String("fStr", "1")),
194 want: logs[0:1],
195 },
196 {
197 msg: "filter by message and field",
198 filtered: sink.FilterMessage("log a").FilterField(zap.Int("b", 2)),
199 want: logs[1:2],
200 },
201 {
202 msg: "filter by field with duplicate fields",
203 filtered: sink.FilterField(zap.Int("a", 2)),
204 want: logs[3:4],
205 },
206 {
207 msg: "filter doesn't match any messages",
208 filtered: sink.FilterMessage("no match"),
209 want: []LoggedEntry{},
210 },
211 {
212 msg: "filter by snippet",
213 filtered: sink.FilterMessageSnippet("log"),
214 want: logs[0:4],
215 },
216 {
217 msg: "filter by snippet and field",
218 filtered: sink.FilterMessageSnippet("a").FilterField(zap.Int("b", 2)),
219 want: logs[1:2],
220 },
221 {
222 msg: "filter for map",
223 filtered: sink.FilterField(zap.Any("map", map[string]string{"a": "b"})),
224 want: logs[5:6],
225 },
226 {
227 msg: "filter for slice",
228 filtered: sink.FilterField(zap.Any("slice", []string{"a"})),
229 want: logs[6:7],
230 },
231 {
232 msg: "filter field key",
233 filtered: sink.FilterFieldKey("filterMe"),
234 want: logs[7:9],
235 },
236 {
237 msg: "filter by arbitrary function",
238 filtered: sink.Filter(func(e LoggedEntry) bool {
239 return len(e.Context) > 1
240 }),
241 want: func() []LoggedEntry {
242
243 w := make([]LoggedEntry, 0, len(logs))
244 w = append(w, logs[0:5]...)
245 w = append(w, logs[7])
246 return w
247 }(),
248 },
249 {
250 msg: "filter level",
251 filtered: sink.FilterLevelExact(zap.WarnLevel),
252 want: logs[9:10],
253 },
254 }
255
256 for _, tt := range tests {
257 got := tt.filtered.AllUntimed()
258 assert.Equal(t, tt.want, got, tt.msg)
259 }
260 }
261
View as plain text