blob: 54eabb788c1c18a81ef33f9c0b215b2047178109 [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 {
Tim Windelschmidte1acfe32024-04-18 23:02:22 +020069 lines = append(lines, e.Leveled.Messages()...)
Serge Bazanski8fab0142023-03-29 16:48:16 +020070 }
71 return lines
72}
73
Serge Bazanski12971d62020-11-17 12:12:58 +010074func TestMultiline(t *testing.T) {
75 tree := New()
76 // Two lines in a single message.
77 tree.MustLeveledFor("main").Info("foo\nbar")
78 // Two lines in a single message with a hanging newline that should get stripped.
79 tree.MustLeveledFor("main").Info("one\ntwo\n")
80
81 if res := expect(tree, t, "main", "foo\nbar", "one\ntwo"); res != "" {
82 t.Errorf("retrieval at main failed: %s", res)
83 }
84}
85
Serge Bazanski8fab0142023-03-29 16:48:16 +020086func TestBacklogAll(t *testing.T) {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020087 tree := New()
88 tree.MustLeveledFor("main").Info("hello, main!")
89 tree.MustLeveledFor("main.foo").Info("hello, main.foo!")
90 tree.MustLeveledFor("main.bar").Info("hello, main.bar!")
91 tree.MustLeveledFor("aux").Info("hello, aux!")
Serge Bazanskif68153c2020-10-26 13:54:37 +010092 // No newline at the last entry - shouldn't get propagated to the backlog.
93 fmt.Fprintf(tree.MustRawFor("aux.process"), "processing foo\nprocessing bar\nbaz")
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020094
Serge Bazanski12971d62020-11-17 12:12:58 +010095 if res := expect(tree, t, "main", "hello, main!", "hello, main.foo!", "hello, main.bar!"); res != "" {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020096 t.Errorf("retrieval at main failed: %s", res)
97 }
Serge Bazanski12971d62020-11-17 12:12:58 +010098 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 +020099 t.Errorf("retrieval at root failed: %s", res)
100 }
Serge Bazanski12971d62020-11-17 12:12:58 +0100101 if res := expect(tree, t, "aux", "hello, aux!", "processing foo", "processing bar"); res != "" {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200102 t.Errorf("retrieval at aux failed: %s", res)
103 }
104}
105
Serge Bazanski8fab0142023-03-29 16:48:16 +0200106func TestBacklogExact(t *testing.T) {
107 tree := New()
108 tree.MustLeveledFor("main").Info("hello, main!")
109 tree.MustLeveledFor("main.foo").Info("hello, main.foo!")
110 tree.MustLeveledFor("main.bar").Info("hello, main.bar!")
111 tree.MustLeveledFor("main.bar.chatty").Info("hey there how are you")
112 tree.MustLeveledFor("main.bar.quiet").Info("fine how are you")
113 tree.MustLeveledFor("main.bar.chatty").Info("i've been alright myself")
114 tree.MustLeveledFor("main.bar.chatty").Info("but to tell you honestly...")
115 tree.MustLeveledFor("main.bar.chatty").Info("i feel like i'm stuck?")
116 tree.MustLeveledFor("main.bar.quiet").Info("mhm")
117 tree.MustLeveledFor("main.bar.chatty").Info("like you know what i'm saying, stuck in like")
118 tree.MustLeveledFor("main.bar.chatty").Info("like a go test?")
119 tree.MustLeveledFor("main.bar.quiet").Info("yeah totally")
120 tree.MustLeveledFor("main.bar.chatty").Info("it's hard to put my finger on it")
121 tree.MustLeveledFor("main.bar.chatty").Info("anyway, how's the wife doing?")
122
123 check := func(a []string, b ...string) {
124 t.Helper()
125 if len(a) != len(b) {
126 t.Errorf("Legth mismatch: wanted %d, got %d", len(b), len(a))
127 }
128 count := len(a)
129 if len(b) < count {
130 count = len(b)
131 }
132 for i := 0; i < count; i++ {
133 if want, got := b[i], a[i]; want != got {
134 t.Errorf("Message %d: wanted %q, got %q", i, want, got)
135 }
136 }
137 }
138
139 check(readBacklog(tree, t, "main", 3, true), "yeah totally", "it's hard to put my finger on it", "anyway, how's the wife doing?")
140 check(readBacklog(tree, t, "main.foo", 3, false), "hello, main.foo!")
141 check(readBacklog(tree, t, "main.bar.quiet", 2, true), "mhm", "yeah totally")
142}
143
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200144func TestStream(t *testing.T) {
145 tree := New()
146 tree.MustLeveledFor("main").Info("hello, backlog")
Serge Bazanskif68153c2020-10-26 13:54:37 +0100147 fmt.Fprintf(tree.MustRawFor("main.process"), "hello, raw backlog\n")
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200148
Serge Bazanskif68153c2020-10-26 13:54:37 +0100149 res, err := tree.Read("", WithBacklog(BacklogAllAvailable), WithChildren(), WithStream())
150 if err != nil {
151 t.Fatalf("Read: %v", err)
152 }
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200153 defer res.Close()
Serge Bazanskif68153c2020-10-26 13:54:37 +0100154 if want, got := 2, len(res.Backlog); want != got {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200155 t.Errorf("wanted %d backlog item, got %d", want, got)
156 }
157
158 tree.MustLeveledFor("main").Info("hello, stream")
Serge Bazanskif68153c2020-10-26 13:54:37 +0100159 fmt.Fprintf(tree.MustRawFor("main.raw"), "hello, raw stream\n")
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200160
Serge Bazanskif68153c2020-10-26 13:54:37 +0100161 entries := make(map[string]bool)
162 timeout := time.After(time.Second * 1)
163 for {
164 done := false
165 select {
166 case <-timeout:
167 done = true
168 case p := <-res.Stream:
169 if p.Leveled != nil {
Serge Bazanski12971d62020-11-17 12:12:58 +0100170 entries[p.Leveled.MessagesJoined()] = true
Serge Bazanskif68153c2020-10-26 13:54:37 +0100171 }
172 if p.Raw != nil {
173 entries[p.Raw.Data] = true
174 }
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200175 }
Serge Bazanskif68153c2020-10-26 13:54:37 +0100176 if done {
177 break
178 }
179 }
180 if entry := "hello, stream"; !entries[entry] {
181 t.Errorf("Missing entry %q", entry)
182 }
183 if entry := "hello, raw stream"; !entries[entry] {
184 t.Errorf("Missing entry %q", entry)
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200185 }
186}
187
188func TestVerbose(t *testing.T) {
189 tree := New()
190
191 tree.MustLeveledFor("main").V(10).Info("this shouldn't get logged")
192
Serge Bazanskif68153c2020-10-26 13:54:37 +0100193 reader, err := tree.Read("", WithBacklog(BacklogAllAvailable), WithChildren())
194 if err != nil {
195 t.Fatalf("Read: %v", err)
196 }
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200197 if want, got := 0, len(reader.Backlog); want != got {
198 t.Fatalf("expected nothing to be logged, got %+v", reader.Backlog)
199 }
200
201 tree.SetVerbosity("main", 10)
202 tree.MustLeveledFor("main").V(10).Info("this should get logged")
203
Serge Bazanskif68153c2020-10-26 13:54:37 +0100204 reader, err = tree.Read("", WithBacklog(BacklogAllAvailable), WithChildren())
205 if err != nil {
206 t.Fatalf("Read: %v", err)
207 }
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200208 if want, got := 1, len(reader.Backlog); want != got {
209 t.Fatalf("expected %d entries to get logged, got %d", want, got)
210 }
211}
212
213func TestMetadata(t *testing.T) {
214 tree := New()
215 tree.MustLeveledFor("main").Error("i am an error")
216 tree.MustLeveledFor("main").Warning("i am a warning")
217 tree.MustLeveledFor("main").Info("i am informative")
218 tree.MustLeveledFor("main").V(0).Info("i am a zero-level debug")
219
Serge Bazanskif68153c2020-10-26 13:54:37 +0100220 reader, err := tree.Read("", WithChildren(), WithBacklog(BacklogAllAvailable))
221 if err != nil {
222 t.Fatalf("Read: %v", err)
223 }
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200224 if want, got := 4, len(reader.Backlog); want != got {
225 t.Fatalf("expected %d entries, got %d", want, got)
226 }
227
228 for _, te := range []struct {
229 ix int
230 severity Severity
231 message string
232 }{
233 {0, ERROR, "i am an error"},
234 {1, WARNING, "i am a warning"},
235 {2, INFO, "i am informative"},
236 {3, INFO, "i am a zero-level debug"},
237 } {
238 p := reader.Backlog[te.ix]
Serge Bazanskif68153c2020-10-26 13:54:37 +0100239 if want, got := te.severity, p.Leveled.Severity(); want != got {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200240 t.Errorf("wanted element %d to have severity %s, got %s", te.ix, want, got)
241 }
Serge Bazanski12971d62020-11-17 12:12:58 +0100242 if want, got := te.message, p.Leveled.MessagesJoined(); want != got {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200243 t.Errorf("wanted element %d to have message %q, got %q", te.ix, want, got)
244 }
Serge Bazanskif68153c2020-10-26 13:54:37 +0100245 if want, got := "logtree_test.go", strings.Split(p.Leveled.Location(), ":")[0]; want != got {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200246 t.Errorf("wanted element %d to have file %q, got %q", te.ix, want, got)
247 }
248 }
249}
250
251func TestSeverity(t *testing.T) {
252 tree := New()
253 tree.MustLeveledFor("main").Error("i am an error")
254 tree.MustLeveledFor("main").Warning("i am a warning")
255 tree.MustLeveledFor("main").Info("i am informative")
256 tree.MustLeveledFor("main").V(0).Info("i am a zero-level debug")
257
Serge Bazanskif68153c2020-10-26 13:54:37 +0100258 reader, err := tree.Read("main", WithBacklog(BacklogAllAvailable), LeveledWithMinimumSeverity(WARNING))
259 if err != nil {
260 t.Fatalf("Read: %v", err)
261 }
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200262 if want, got := 2, len(reader.Backlog); want != got {
263 t.Fatalf("wanted %d entries, got %d", want, got)
264 }
Serge Bazanski12971d62020-11-17 12:12:58 +0100265 if want, got := "i am an error", reader.Backlog[0].Leveled.MessagesJoined(); want != got {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200266 t.Fatalf("wanted entry %q, got %q", want, got)
267 }
Serge Bazanski12971d62020-11-17 12:12:58 +0100268 if want, got := "i am a warning", reader.Backlog[1].Leveled.MessagesJoined(); want != got {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200269 t.Fatalf("wanted entry %q, got %q", want, got)
270 }
271}
Serge Bazanskid9775a62022-02-15 13:28:55 +0100272
273func TestAddedStackDepth(t *testing.T) {
274 tree := New()
275 helper := func(msg string) {
276 tree.MustLeveledFor("main").WithAddedStackDepth(1).Infof("oh no: %s", msg)
277 }
278
279 // The next three lines are tested to be next to each other.
280 helper("it failed")
281 tree.MustLeveledFor("main").Infof("something else")
282
283 reader, err := tree.Read("main", WithBacklog(BacklogAllAvailable))
284 if err != nil {
285 t.Fatalf("Read: %v", err)
286 }
287 if want, got := 2, len(reader.Backlog); want != got {
288 t.Fatalf("wanted %d entries, got %d", want, got)
289 }
290 if want, got := "oh no: it failed", reader.Backlog[0].Leveled.MessagesJoined(); want != got {
291 t.Errorf("wanted entry %q, got %q", want, got)
292 }
293 if want, got := "something else", reader.Backlog[1].Leveled.MessagesJoined(); want != got {
294 t.Errorf("wanted entry %q, got %q", want, got)
295 }
296 if first, second := reader.Backlog[0].Leveled.line, reader.Backlog[1].Leveled.line; first+1 != second {
297 t.Errorf("first entry at %d, second at %d, wanted one after the other", first, second)
298 }
299}
Serge Bazanski367ee272023-03-16 17:50:39 +0100300
301func TestLogEntry_ConciseString(t *testing.T) {
302 trim := func(s string) string {
303 return strings.Trim(s, "\n")
304 }
305 for i, te := range []struct {
306 entry *LogEntry
307 maxWidth int
308 want string
309 }{
310 {
311 &LogEntry{
312 Leveled: &LeveledPayload{
313 messages: []string{"Hello there!"},
314 severity: WARNING,
315 },
316 DN: "root.role.kubernetes.run.kubernetes.apiserver",
317 },
318 120,
319 " k8s apiserver W Hello there!",
320 },
321 {
322 &LogEntry{
323 Leveled: &LeveledPayload{
324 messages: []string{"Hello there!", "I am multiline."},
325 severity: WARNING,
326 },
327 DN: "root.role.kubernetes.run.kubernetes.apiserver",
328 },
329 120,
330 trim(`
331 k8s apiserver W Hello there!
332 | I am multiline.
333`),
334 },
335 {
336 &LogEntry{
337 Leveled: &LeveledPayload{
338 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."},
339 severity: WARNING,
340 },
341 DN: "root.role.kubernetes.run.kubernetes.apiserver",
342 },
343 120,
344 trim(`
345 k8s apiserver W Hello there! I am a very long string, and I will get wrapped to 120 columns because that's just
346 | how life is for long strings.
347`),
348 },
349 {
350 &LogEntry{
351 Leveled: &LeveledPayload{
352 messages: []string{"Hello there!"},
353 severity: WARNING,
354 },
355 DN: "root.role.kubernetes.run.kubernetes.apiserver",
356 },
357 60,
358 trim(`
359 k8s apiserver W Hello there!
360`),
361 },
362 {
363 &LogEntry{
364 Leveled: &LeveledPayload{
365 messages: []string{"Hello there!"},
366 severity: WARNING,
367 },
368 DN: "root.role.kubernetes.run.kubernetes.apiserver",
369 },
370 40,
371 "W Hello there!",
372 },
373 } {
374 got := te.entry.ConciseString(MetropolisShortenDict, te.maxWidth)
375 for _, line := range strings.Split(got, "\n") {
376 if want, got := te.maxWidth, len(line); got > want {
377 t.Errorf("Case %d, line %q too long (%d bytes, wanted at most %d)", i, line, got, want)
378 }
379 }
380 if te.want != got {
381 t.Errorf("Case %d, message diff", i)
382 t.Logf("Wanted:\n%s", te.want)
383 t.Logf("Got:\n%s", got)
384 }
385 }
386}