blob: e37893ae5d697c7882201ba3c21a6c67fc3a456a [file] [log] [blame]
Serge Bazanski5faa2fc2020-09-07 14:09:30 +02001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package logtree
18
19import (
20 "fmt"
21 "strings"
22 "testing"
23 "time"
24)
25
Serge Bazanski12971d62020-11-17 12:12:58 +010026func expect(tree *LogTree, t *testing.T, dn DN, entries ...string) string {
27 t.Helper()
28 res, err := tree.Read(dn, WithChildren(), WithBacklog(BacklogAllAvailable))
29 if err != nil {
30 t.Fatalf("Read: %v", err)
31 }
Serge Bazanski8fab0142023-03-29 16:48:16 +020032 defer res.Close()
Serge Bazanski12971d62020-11-17 12:12:58 +010033 if want, got := len(entries), len(res.Backlog); want != got {
34 t.Fatalf("wanted %v backlog entries, got %v", want, got)
35 }
36 got := make(map[string]bool)
37 for _, entry := range res.Backlog {
38 if entry.Leveled != nil {
39 got[entry.Leveled.MessagesJoined()] = true
40 }
41 if entry.Raw != nil {
42 got[entry.Raw.Data] = true
43 }
44 }
45 for _, entry := range entries {
46 if !got[entry] {
47 return fmt.Sprintf("missing entry %q", entry)
48 }
49 }
50 return ""
51}
52
Serge Bazanski8fab0142023-03-29 16:48:16 +020053func readBacklog(tree *LogTree, t *testing.T, dn DN, backlog int, recursive bool) []string {
54 t.Helper()
55 opts := []LogReadOption{
56 WithBacklog(backlog),
57 }
58 if recursive {
59 opts = append(opts, WithChildren())
60 }
61 res, err := tree.Read(dn, opts...)
62 if err != nil {
63 t.Fatalf("Read: %v", err)
64 }
65 defer res.Close()
66
67 var lines []string
68 for _, e := range res.Backlog {
69 for _, msg := range e.Leveled.Messages() {
70 lines = append(lines, msg)
71 }
72 }
73 return lines
74}
75
Serge Bazanski12971d62020-11-17 12:12:58 +010076func TestMultiline(t *testing.T) {
77 tree := New()
78 // Two lines in a single message.
79 tree.MustLeveledFor("main").Info("foo\nbar")
80 // Two lines in a single message with a hanging newline that should get stripped.
81 tree.MustLeveledFor("main").Info("one\ntwo\n")
82
83 if res := expect(tree, t, "main", "foo\nbar", "one\ntwo"); res != "" {
84 t.Errorf("retrieval at main failed: %s", res)
85 }
86}
87
Serge Bazanski8fab0142023-03-29 16:48:16 +020088func TestBacklogAll(t *testing.T) {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020089 tree := New()
90 tree.MustLeveledFor("main").Info("hello, main!")
91 tree.MustLeveledFor("main.foo").Info("hello, main.foo!")
92 tree.MustLeveledFor("main.bar").Info("hello, main.bar!")
93 tree.MustLeveledFor("aux").Info("hello, aux!")
Serge Bazanskif68153c2020-10-26 13:54:37 +010094 // No newline at the last entry - shouldn't get propagated to the backlog.
95 fmt.Fprintf(tree.MustRawFor("aux.process"), "processing foo\nprocessing bar\nbaz")
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020096
Serge Bazanski12971d62020-11-17 12:12:58 +010097 if res := expect(tree, t, "main", "hello, main!", "hello, main.foo!", "hello, main.bar!"); res != "" {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020098 t.Errorf("retrieval at main failed: %s", res)
99 }
Serge Bazanski12971d62020-11-17 12:12:58 +0100100 if res := expect(tree, t, "", "hello, main!", "hello, main.foo!", "hello, main.bar!", "hello, aux!", "processing foo", "processing bar"); res != "" {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200101 t.Errorf("retrieval at root failed: %s", res)
102 }
Serge Bazanski12971d62020-11-17 12:12:58 +0100103 if res := expect(tree, t, "aux", "hello, aux!", "processing foo", "processing bar"); res != "" {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200104 t.Errorf("retrieval at aux failed: %s", res)
105 }
106}
107
Serge Bazanski8fab0142023-03-29 16:48:16 +0200108func TestBacklogExact(t *testing.T) {
109 tree := New()
110 tree.MustLeveledFor("main").Info("hello, main!")
111 tree.MustLeveledFor("main.foo").Info("hello, main.foo!")
112 tree.MustLeveledFor("main.bar").Info("hello, main.bar!")
113 tree.MustLeveledFor("main.bar.chatty").Info("hey there how are you")
114 tree.MustLeveledFor("main.bar.quiet").Info("fine how are you")
115 tree.MustLeveledFor("main.bar.chatty").Info("i've been alright myself")
116 tree.MustLeveledFor("main.bar.chatty").Info("but to tell you honestly...")
117 tree.MustLeveledFor("main.bar.chatty").Info("i feel like i'm stuck?")
118 tree.MustLeveledFor("main.bar.quiet").Info("mhm")
119 tree.MustLeveledFor("main.bar.chatty").Info("like you know what i'm saying, stuck in like")
120 tree.MustLeveledFor("main.bar.chatty").Info("like a go test?")
121 tree.MustLeveledFor("main.bar.quiet").Info("yeah totally")
122 tree.MustLeveledFor("main.bar.chatty").Info("it's hard to put my finger on it")
123 tree.MustLeveledFor("main.bar.chatty").Info("anyway, how's the wife doing?")
124
125 check := func(a []string, b ...string) {
126 t.Helper()
127 if len(a) != len(b) {
128 t.Errorf("Legth mismatch: wanted %d, got %d", len(b), len(a))
129 }
130 count := len(a)
131 if len(b) < count {
132 count = len(b)
133 }
134 for i := 0; i < count; i++ {
135 if want, got := b[i], a[i]; want != got {
136 t.Errorf("Message %d: wanted %q, got %q", i, want, got)
137 }
138 }
139 }
140
141 check(readBacklog(tree, t, "main", 3, true), "yeah totally", "it's hard to put my finger on it", "anyway, how's the wife doing?")
142 check(readBacklog(tree, t, "main.foo", 3, false), "hello, main.foo!")
143 check(readBacklog(tree, t, "main.bar.quiet", 2, true), "mhm", "yeah totally")
144}
145
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200146func TestStream(t *testing.T) {
147 tree := New()
148 tree.MustLeveledFor("main").Info("hello, backlog")
Serge Bazanskif68153c2020-10-26 13:54:37 +0100149 fmt.Fprintf(tree.MustRawFor("main.process"), "hello, raw backlog\n")
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200150
Serge Bazanskif68153c2020-10-26 13:54:37 +0100151 res, err := tree.Read("", WithBacklog(BacklogAllAvailable), WithChildren(), WithStream())
152 if err != nil {
153 t.Fatalf("Read: %v", err)
154 }
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200155 defer res.Close()
Serge Bazanskif68153c2020-10-26 13:54:37 +0100156 if want, got := 2, len(res.Backlog); want != got {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200157 t.Errorf("wanted %d backlog item, got %d", want, got)
158 }
159
160 tree.MustLeveledFor("main").Info("hello, stream")
Serge Bazanskif68153c2020-10-26 13:54:37 +0100161 fmt.Fprintf(tree.MustRawFor("main.raw"), "hello, raw stream\n")
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200162
Serge Bazanskif68153c2020-10-26 13:54:37 +0100163 entries := make(map[string]bool)
164 timeout := time.After(time.Second * 1)
165 for {
166 done := false
167 select {
168 case <-timeout:
169 done = true
170 case p := <-res.Stream:
171 if p.Leveled != nil {
Serge Bazanski12971d62020-11-17 12:12:58 +0100172 entries[p.Leveled.MessagesJoined()] = true
Serge Bazanskif68153c2020-10-26 13:54:37 +0100173 }
174 if p.Raw != nil {
175 entries[p.Raw.Data] = true
176 }
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200177 }
Serge Bazanskif68153c2020-10-26 13:54:37 +0100178 if done {
179 break
180 }
181 }
182 if entry := "hello, stream"; !entries[entry] {
183 t.Errorf("Missing entry %q", entry)
184 }
185 if entry := "hello, raw stream"; !entries[entry] {
186 t.Errorf("Missing entry %q", entry)
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200187 }
188}
189
190func TestVerbose(t *testing.T) {
191 tree := New()
192
193 tree.MustLeveledFor("main").V(10).Info("this shouldn't get logged")
194
Serge Bazanskif68153c2020-10-26 13:54:37 +0100195 reader, err := tree.Read("", WithBacklog(BacklogAllAvailable), WithChildren())
196 if err != nil {
197 t.Fatalf("Read: %v", err)
198 }
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200199 if want, got := 0, len(reader.Backlog); want != got {
200 t.Fatalf("expected nothing to be logged, got %+v", reader.Backlog)
201 }
202
203 tree.SetVerbosity("main", 10)
204 tree.MustLeveledFor("main").V(10).Info("this should get logged")
205
Serge Bazanskif68153c2020-10-26 13:54:37 +0100206 reader, err = tree.Read("", WithBacklog(BacklogAllAvailable), WithChildren())
207 if err != nil {
208 t.Fatalf("Read: %v", err)
209 }
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200210 if want, got := 1, len(reader.Backlog); want != got {
211 t.Fatalf("expected %d entries to get logged, got %d", want, got)
212 }
213}
214
215func TestMetadata(t *testing.T) {
216 tree := New()
217 tree.MustLeveledFor("main").Error("i am an error")
218 tree.MustLeveledFor("main").Warning("i am a warning")
219 tree.MustLeveledFor("main").Info("i am informative")
220 tree.MustLeveledFor("main").V(0).Info("i am a zero-level debug")
221
Serge Bazanskif68153c2020-10-26 13:54:37 +0100222 reader, err := tree.Read("", WithChildren(), WithBacklog(BacklogAllAvailable))
223 if err != nil {
224 t.Fatalf("Read: %v", err)
225 }
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200226 if want, got := 4, len(reader.Backlog); want != got {
227 t.Fatalf("expected %d entries, got %d", want, got)
228 }
229
230 for _, te := range []struct {
231 ix int
232 severity Severity
233 message string
234 }{
235 {0, ERROR, "i am an error"},
236 {1, WARNING, "i am a warning"},
237 {2, INFO, "i am informative"},
238 {3, INFO, "i am a zero-level debug"},
239 } {
240 p := reader.Backlog[te.ix]
Serge Bazanskif68153c2020-10-26 13:54:37 +0100241 if want, got := te.severity, p.Leveled.Severity(); want != got {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200242 t.Errorf("wanted element %d to have severity %s, got %s", te.ix, want, got)
243 }
Serge Bazanski12971d62020-11-17 12:12:58 +0100244 if want, got := te.message, p.Leveled.MessagesJoined(); want != got {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200245 t.Errorf("wanted element %d to have message %q, got %q", te.ix, want, got)
246 }
Serge Bazanskif68153c2020-10-26 13:54:37 +0100247 if want, got := "logtree_test.go", strings.Split(p.Leveled.Location(), ":")[0]; want != got {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200248 t.Errorf("wanted element %d to have file %q, got %q", te.ix, want, got)
249 }
250 }
251}
252
253func TestSeverity(t *testing.T) {
254 tree := New()
255 tree.MustLeveledFor("main").Error("i am an error")
256 tree.MustLeveledFor("main").Warning("i am a warning")
257 tree.MustLeveledFor("main").Info("i am informative")
258 tree.MustLeveledFor("main").V(0).Info("i am a zero-level debug")
259
Serge Bazanskif68153c2020-10-26 13:54:37 +0100260 reader, err := tree.Read("main", WithBacklog(BacklogAllAvailable), LeveledWithMinimumSeverity(WARNING))
261 if err != nil {
262 t.Fatalf("Read: %v", err)
263 }
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200264 if want, got := 2, len(reader.Backlog); want != got {
265 t.Fatalf("wanted %d entries, got %d", want, got)
266 }
Serge Bazanski12971d62020-11-17 12:12:58 +0100267 if want, got := "i am an error", reader.Backlog[0].Leveled.MessagesJoined(); want != got {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200268 t.Fatalf("wanted entry %q, got %q", want, got)
269 }
Serge Bazanski12971d62020-11-17 12:12:58 +0100270 if want, got := "i am a warning", reader.Backlog[1].Leveled.MessagesJoined(); want != got {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200271 t.Fatalf("wanted entry %q, got %q", want, got)
272 }
273}
Serge Bazanskid9775a62022-02-15 13:28:55 +0100274
275func TestAddedStackDepth(t *testing.T) {
276 tree := New()
277 helper := func(msg string) {
278 tree.MustLeveledFor("main").WithAddedStackDepth(1).Infof("oh no: %s", msg)
279 }
280
281 // The next three lines are tested to be next to each other.
282 helper("it failed")
283 tree.MustLeveledFor("main").Infof("something else")
284
285 reader, err := tree.Read("main", WithBacklog(BacklogAllAvailable))
286 if err != nil {
287 t.Fatalf("Read: %v", err)
288 }
289 if want, got := 2, len(reader.Backlog); want != got {
290 t.Fatalf("wanted %d entries, got %d", want, got)
291 }
292 if want, got := "oh no: it failed", reader.Backlog[0].Leveled.MessagesJoined(); want != got {
293 t.Errorf("wanted entry %q, got %q", want, got)
294 }
295 if want, got := "something else", reader.Backlog[1].Leveled.MessagesJoined(); want != got {
296 t.Errorf("wanted entry %q, got %q", want, got)
297 }
298 if first, second := reader.Backlog[0].Leveled.line, reader.Backlog[1].Leveled.line; first+1 != second {
299 t.Errorf("first entry at %d, second at %d, wanted one after the other", first, second)
300 }
301}
Serge Bazanski367ee272023-03-16 17:50:39 +0100302
303func TestLogEntry_ConciseString(t *testing.T) {
304 trim := func(s string) string {
305 return strings.Trim(s, "\n")
306 }
307 for i, te := range []struct {
308 entry *LogEntry
309 maxWidth int
310 want string
311 }{
312 {
313 &LogEntry{
314 Leveled: &LeveledPayload{
315 messages: []string{"Hello there!"},
316 severity: WARNING,
317 },
318 DN: "root.role.kubernetes.run.kubernetes.apiserver",
319 },
320 120,
321 " k8s apiserver W Hello there!",
322 },
323 {
324 &LogEntry{
325 Leveled: &LeveledPayload{
326 messages: []string{"Hello there!", "I am multiline."},
327 severity: WARNING,
328 },
329 DN: "root.role.kubernetes.run.kubernetes.apiserver",
330 },
331 120,
332 trim(`
333 k8s apiserver W Hello there!
334 | I am multiline.
335`),
336 },
337 {
338 &LogEntry{
339 Leveled: &LeveledPayload{
340 messages: []string{"Hello there! I am a very long string, and I will get wrapped to 120 columns because that's just how life is for long strings."},
341 severity: WARNING,
342 },
343 DN: "root.role.kubernetes.run.kubernetes.apiserver",
344 },
345 120,
346 trim(`
347 k8s apiserver W Hello there! I am a very long string, and I will get wrapped to 120 columns because that's just
348 | how life is for long strings.
349`),
350 },
351 {
352 &LogEntry{
353 Leveled: &LeveledPayload{
354 messages: []string{"Hello there!"},
355 severity: WARNING,
356 },
357 DN: "root.role.kubernetes.run.kubernetes.apiserver",
358 },
359 60,
360 trim(`
361 k8s apiserver W Hello there!
362`),
363 },
364 {
365 &LogEntry{
366 Leveled: &LeveledPayload{
367 messages: []string{"Hello there!"},
368 severity: WARNING,
369 },
370 DN: "root.role.kubernetes.run.kubernetes.apiserver",
371 },
372 40,
373 "W Hello there!",
374 },
375 } {
376 got := te.entry.ConciseString(MetropolisShortenDict, te.maxWidth)
377 for _, line := range strings.Split(got, "\n") {
378 if want, got := te.maxWidth, len(line); got > want {
379 t.Errorf("Case %d, line %q too long (%d bytes, wanted at most %d)", i, line, got, want)
380 }
381 }
382 if te.want != got {
383 t.Errorf("Case %d, message diff", i)
384 t.Logf("Wanted:\n%s", te.want)
385 t.Logf("Got:\n%s", got)
386 }
387 }
388}