1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package klog
18
19 import (
20 "bytes"
21 "errors"
22 "flag"
23 "fmt"
24 "io"
25 "io/ioutil"
26 stdLog "log"
27 "os"
28 "path/filepath"
29 "reflect"
30 "regexp"
31 "runtime"
32 "strconv"
33 "strings"
34 "sync"
35 "testing"
36 "time"
37
38 "github.com/go-logr/logr"
39
40 "k8s.io/klog/v2/internal/buffer"
41 testingclock "k8s.io/klog/v2/internal/clock/testing"
42 "k8s.io/klog/v2/internal/severity"
43 "k8s.io/klog/v2/internal/test"
44 "k8s.io/klog/v2/internal/test/require"
45 )
46
47
48
49
50
51 func TestShortHostname(t *testing.T) {
52 for hostname, expect := range map[string]string{
53 "": "",
54 "host": "host",
55 "host.google.com": "host",
56 } {
57 if got := shortHostname(hostname); expect != got {
58 t.Errorf("shortHostname(%q): expected %q, got %q", hostname, expect, got)
59 }
60 }
61 }
62
63
64 type flushBuffer struct {
65 bytes.Buffer
66 }
67
68 func (f *flushBuffer) Flush() error {
69 return nil
70 }
71
72 func (f *flushBuffer) Sync() error {
73 return nil
74 }
75
76
77 func (l *loggingT) swap(writers [severity.NumSeverity]io.Writer) (old [severity.NumSeverity]io.Writer) {
78 l.mu.Lock()
79 defer l.mu.Unlock()
80 old = l.file
81 logging.file = writers
82 return
83 }
84
85
86 func (l *loggingT) newBuffers() [severity.NumSeverity]io.Writer {
87 return l.swap([severity.NumSeverity]io.Writer{new(flushBuffer), new(flushBuffer), new(flushBuffer), new(flushBuffer)})
88 }
89
90
91 func contents(s severity.Severity) string {
92 return logging.file[s].(*flushBuffer).String()
93 }
94
95
96 func contains(s severity.Severity, str string) bool {
97 return strings.Contains(contents(s), str)
98 }
99
100
101 func setFlags() {
102 logging.toStderr = false
103 logging.addDirHeader = false
104 }
105
106
107 func TestInfo(t *testing.T) {
108 defer CaptureState().Restore()
109 setFlags()
110 defer logging.swap(logging.newBuffers())
111 Info("test")
112 if !contains(severity.InfoLog, "I") {
113 t.Errorf("Info has wrong character: %q", contents(severity.InfoLog))
114 }
115 if !contains(severity.InfoLog, "test") {
116 t.Error("Info failed")
117 }
118 }
119
120 func TestInfoDepth(t *testing.T) {
121 defer CaptureState().Restore()
122 setFlags()
123 defer logging.swap(logging.newBuffers())
124
125 f := func() { InfoDepth(1, "depth-test1") }
126
127
128 _, _, wantLine, _ := runtime.Caller(0)
129 InfoDepth(0, "depth-test0")
130 f()
131
132 msgs := strings.Split(strings.TrimSuffix(contents(severity.InfoLog), "\n"), "\n")
133 if len(msgs) != 2 {
134 t.Fatalf("Got %d lines, expected 2", len(msgs))
135 }
136
137 for i, m := range msgs {
138 if !strings.HasPrefix(m, "I") {
139 t.Errorf("InfoDepth[%d] has wrong character: %q", i, m)
140 }
141 w := fmt.Sprintf("depth-test%d", i)
142 if !strings.Contains(m, w) {
143 t.Errorf("InfoDepth[%d] missing %q: %q", i, w, m)
144 }
145
146
147 msg := m[strings.LastIndex(m, ":")+1:]
148 x := strings.Index(msg, "]")
149 if x < 0 {
150 t.Errorf("InfoDepth[%d]: missing ']': %q", i, m)
151 continue
152 }
153 line, err := strconv.Atoi(msg[:x])
154 if err != nil {
155 t.Errorf("InfoDepth[%d]: bad line number: %q", i, m)
156 continue
157 }
158 wantLine++
159 if wantLine != line {
160 t.Errorf("InfoDepth[%d]: got line %d, want %d", i, line, wantLine)
161 }
162 }
163 }
164
165 func init() {
166 CopyStandardLogTo("INFO")
167 }
168
169
170 func TestCopyStandardLogToPanic(t *testing.T) {
171 defer func() {
172 if s, ok := recover().(string); !ok || !strings.Contains(s, "LOG") {
173 t.Errorf(`CopyStandardLogTo("LOG") should have panicked: %v`, s)
174 }
175 }()
176 CopyStandardLogTo("LOG")
177 }
178
179
180 func TestStandardLog(t *testing.T) {
181 defer CaptureState().Restore()
182 setFlags()
183 defer logging.swap(logging.newBuffers())
184 stdLog.Print("test")
185 if !contains(severity.InfoLog, "I") {
186 t.Errorf("Info has wrong character: %q", contents(severity.InfoLog))
187 }
188 if !contains(severity.InfoLog, "test") {
189 t.Error("Info failed")
190 }
191 }
192
193
194 func TestHeader(t *testing.T) {
195 defer CaptureState().Restore()
196 setFlags()
197 defer logging.swap(logging.newBuffers())
198 defer func(previous func() time.Time) { timeNow = previous }(timeNow)
199 timeNow = func() time.Time {
200 return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local)
201 }
202 buffer.Pid = 1234
203 Info("test")
204 var line int
205 format := "I0102 15:04:05.067890 1234 klog_test.go:%d] test\n"
206 n, err := fmt.Sscanf(contents(severity.InfoLog), format, &line)
207 if n != 1 || err != nil {
208 t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(severity.InfoLog))
209 }
210
211
212 want := fmt.Sprintf(format, line)
213 if contents(severity.InfoLog) != want {
214 t.Errorf("log format error: got:\n\t%q\nwant:\t%q", contents(severity.InfoLog), want)
215 }
216 }
217
218 func TestHeaderWithDir(t *testing.T) {
219 defer CaptureState().Restore()
220 setFlags()
221 logging.addDirHeader = true
222 defer logging.swap(logging.newBuffers())
223 defer func(previous func() time.Time) { timeNow = previous }(timeNow)
224 timeNow = func() time.Time {
225 return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local)
226 }
227 pid = 1234
228 Info("test")
229 re := regexp.MustCompile(`I0102 15:04:05.067890 1234 (klog|v2)/klog_test.go:(\d+)] test\n`)
230 if !re.MatchString(contents(severity.InfoLog)) {
231 t.Errorf("log format error: line does not match regex:\n\t%q\n", contents(severity.InfoLog))
232 }
233 }
234
235
236
237
238 func TestError(t *testing.T) {
239 defer CaptureState().Restore()
240 setFlags()
241 defer logging.swap(logging.newBuffers())
242 Error("test")
243 if !contains(severity.ErrorLog, "E") {
244 t.Errorf("Error has wrong character: %q", contents(severity.ErrorLog))
245 }
246 if !contains(severity.ErrorLog, "test") {
247 t.Error("Error failed")
248 }
249 str := contents(severity.ErrorLog)
250 if !contains(severity.WarningLog, str) {
251 t.Error("Warning failed")
252 }
253 if !contains(severity.InfoLog, str) {
254 t.Error("Info failed")
255 }
256 }
257
258
259
260
261 func TestErrorWithOneOutput(t *testing.T) {
262 defer CaptureState().Restore()
263 setFlags()
264 logging.oneOutput = true
265 defer logging.swap(logging.newBuffers())
266 Error("test")
267 if !contains(severity.ErrorLog, "E") {
268 t.Errorf("Error has wrong character: %q", contents(severity.ErrorLog))
269 }
270 if !contains(severity.ErrorLog, "test") {
271 t.Error("Error failed")
272 }
273 str := contents(severity.ErrorLog)
274 if contains(severity.WarningLog, str) {
275 t.Error("Warning failed")
276 }
277 if contains(severity.InfoLog, str) {
278 t.Error("Info failed")
279 }
280 }
281
282
283
284
285 func TestWarning(t *testing.T) {
286 defer CaptureState().Restore()
287 setFlags()
288 defer logging.swap(logging.newBuffers())
289 Warning("test")
290 if !contains(severity.WarningLog, "W") {
291 t.Errorf("Warning has wrong character: %q", contents(severity.WarningLog))
292 }
293 if !contains(severity.WarningLog, "test") {
294 t.Error("Warning failed")
295 }
296 str := contents(severity.WarningLog)
297 if !contains(severity.InfoLog, str) {
298 t.Error("Info failed")
299 }
300 }
301
302
303
304
305 func TestWarningWithOneOutput(t *testing.T) {
306 defer CaptureState().Restore()
307 setFlags()
308 logging.oneOutput = true
309 defer logging.swap(logging.newBuffers())
310 Warning("test")
311 if !contains(severity.WarningLog, "W") {
312 t.Errorf("Warning has wrong character: %q", contents(severity.WarningLog))
313 }
314 if !contains(severity.WarningLog, "test") {
315 t.Error("Warning failed")
316 }
317 str := contents(severity.WarningLog)
318 if contains(severity.InfoLog, str) {
319 t.Error("Info failed")
320 }
321 }
322
323
324 func TestV(t *testing.T) {
325 defer CaptureState().Restore()
326 setFlags()
327 defer logging.swap(logging.newBuffers())
328 require.NoError(t, logging.verbosity.Set("2"))
329 V(2).Info("test")
330 if !contains(severity.InfoLog, "I") {
331 t.Errorf("Info has wrong character: %q", contents(severity.InfoLog))
332 }
333 if !contains(severity.InfoLog, "test") {
334 t.Error("Info failed")
335 }
336 }
337
338
339 func TestVmoduleOn(t *testing.T) {
340 defer CaptureState().Restore()
341 setFlags()
342 defer logging.swap(logging.newBuffers())
343 require.NoError(t, logging.vmodule.Set("klog_test=2"))
344 if !V(1).Enabled() {
345 t.Error("V not enabled for 1")
346 }
347 if !V(2).Enabled() {
348 t.Error("V not enabled for 2")
349 }
350 if V(3).Enabled() {
351 t.Error("V enabled for 3")
352 }
353 V(2).Info("test")
354 if !contains(severity.InfoLog, "I") {
355 t.Errorf("Info has wrong character: %q", contents(severity.InfoLog))
356 }
357 if !contains(severity.InfoLog, "test") {
358 t.Error("Info failed")
359 }
360 }
361
362
363 func TestVmoduleOff(t *testing.T) {
364 defer CaptureState().Restore()
365 setFlags()
366 defer logging.swap(logging.newBuffers())
367 require.NoError(t, logging.vmodule.Set("notthisfile=2"))
368 for i := 1; i <= 3; i++ {
369 if V(Level(i)).Enabled() {
370 t.Errorf("V enabled for %d", i)
371 }
372 }
373 V(2).Info("test")
374 if contents(severity.InfoLog) != "" {
375 t.Error("V logged incorrectly")
376 }
377 }
378
379 func TestSetOutputDataRace(*testing.T) {
380 defer CaptureState().Restore()
381 setFlags()
382 defer logging.swap(logging.newBuffers())
383 var wg sync.WaitGroup
384 var daemons []*flushDaemon
385 for i := 1; i <= 50; i++ {
386 daemon := newFlushDaemon(logging.lockAndFlushAll, nil)
387 daemon.run(time.Second)
388 daemons = append(daemons, daemon)
389 }
390 for i := 1; i <= 50; i++ {
391 wg.Add(1)
392 go func() {
393 defer wg.Done()
394 SetOutput(ioutil.Discard)
395 }()
396 }
397 for i := 1; i <= 50; i++ {
398 daemon := newFlushDaemon(logging.lockAndFlushAll, nil)
399 daemon.run(time.Second)
400 daemons = append(daemons, daemon)
401 }
402 for i := 1; i <= 50; i++ {
403 wg.Add(1)
404 go func() {
405 defer wg.Done()
406 SetOutputBySeverity("INFO", ioutil.Discard)
407 }()
408 }
409 for i := 1; i <= 50; i++ {
410 daemon := newFlushDaemon(logging.lockAndFlushAll, nil)
411 daemon.run(time.Second)
412 daemons = append(daemons, daemon)
413 }
414 wg.Wait()
415 for _, d := range daemons {
416 d.stop()
417 }
418 }
419
420 func TestLogToOutput(t *testing.T) {
421 defer CaptureState().Restore()
422 logging.toStderr = true
423 defer logging.swap(logging.newBuffers())
424 buf := new(bytes.Buffer)
425 SetOutput(buf)
426 LogToStderr(false)
427
428 Info("Does logging to an output work?")
429
430 str := buf.String()
431 if !strings.Contains(str, "Does logging to an output work?") {
432 t.Fatalf("Expected %q to contain \"Does logging to an output work?\"", str)
433 }
434 }
435
436
437 var vGlobs = map[string]bool{
438
439 "klog_test=1": false,
440 "klog_test=2": true,
441 "klog_test=3": true,
442
443 "*=2": true,
444 "?l*=2": true,
445 "????_*=2": true,
446 "??[mno]?_*t=2": true,
447
448 "*x=2": false,
449 "m*=2": false,
450 "??_*=2": false,
451 "?[abc]?_*t=2": false,
452 }
453
454
455 func testVmoduleGlob(pat string, match bool, t *testing.T) {
456 defer CaptureState().Restore()
457 setFlags()
458 defer logging.swap(logging.newBuffers())
459 require.NoError(t, logging.vmodule.Set(pat))
460 if V(2).Enabled() != match {
461 t.Errorf("incorrect match for %q: got %#v expected %#v", pat, V(2), match)
462 }
463 }
464
465
466 func TestVmoduleGlob(t *testing.T) {
467 for glob, match := range vGlobs {
468 testVmoduleGlob(glob, match, t)
469 }
470 }
471
472 func TestRollover(t *testing.T) {
473 defer CaptureState().Restore()
474 setFlags()
475 var err error
476 defer func(previous func(error)) { logExitFunc = previous }(logExitFunc)
477 logExitFunc = func(e error) {
478 err = e
479 }
480 MaxSize = 512
481 Info("x")
482 info, ok := logging.file[severity.InfoLog].(*syncBuffer)
483 if !ok {
484 t.Fatal("info wasn't created")
485 }
486 if err != nil {
487 t.Fatalf("info has initial error: %v", err)
488 }
489 fname0 := info.file.Name()
490 Info(strings.Repeat("x", int(MaxSize)))
491 if err != nil {
492 t.Fatalf("info has error after big write: %v", err)
493 }
494
495
496
497
498
499
500
501 time.Sleep(1 * time.Second)
502
503 Info("x")
504 if err != nil {
505 t.Fatalf("error after rotation: %v", err)
506 }
507 fname1 := info.file.Name()
508 if fname0 == fname1 {
509 t.Errorf("info.f.Name did not change: %v", fname0)
510 }
511 if info.nbytes >= info.maxbytes {
512 t.Errorf("file size was not reset: %d", info.nbytes)
513 }
514 }
515
516 func TestOpenAppendOnStart(t *testing.T) {
517 const (
518 x string = "xxxxxxxxxx"
519 y string = "yyyyyyyyyy"
520 )
521
522 defer CaptureState().Restore()
523 setFlags()
524 var err error
525 defer func(previous func(error)) { logExitFunc = previous }(logExitFunc)
526 logExitFunc = func(e error) {
527 err = e
528 }
529
530 f, err := ioutil.TempFile("", "test_klog_OpenAppendOnStart")
531 if err != nil {
532 t.Fatalf("unexpected error: %v", err)
533 }
534 defer os.Remove(f.Name())
535 logging.logFile = f.Name()
536
537
538 for i := range logging.file {
539 logging.file[i] = nil
540 }
541
542
543 Info(x)
544 sb, ok := logging.file[severity.InfoLog].(*syncBuffer)
545 if !ok {
546 t.Fatal("info wasn't created")
547 }
548
549
550 needToSync := logging.flushAll()
551 if needToSync.num != 1 || needToSync.files[0] != sb.file {
552 t.Errorf("Should have received exactly the file from severity.InfoLog for syncing, got instead: %+v", needToSync)
553 }
554 logging.syncAll(needToSync)
555 b, err := ioutil.ReadFile(logging.logFile)
556 if err != nil {
557 t.Fatalf("unexpected error: %v", err)
558 }
559 if !strings.Contains(string(b), x) {
560 t.Fatalf("got %s, missing expected Info log: %s", string(b), x)
561 }
562
563
564 for i := range logging.file {
565 logging.file[i] = nil
566 }
567
568
569 Info(y)
570
571 logging.lockAndFlushAll()
572 b, err = ioutil.ReadFile(logging.logFile)
573 if err != nil {
574 t.Fatalf("unexpected error: %v", err)
575 }
576 if !strings.Contains(string(b), y) {
577 t.Fatalf("got %s, missing expected Info log: %s", string(b), y)
578 }
579
580 logging.lockAndFlushAll()
581 b, err = ioutil.ReadFile(logging.logFile)
582 if err != nil {
583 t.Fatalf("unexpected error: %v", err)
584 }
585 if !strings.Contains(string(b), x) {
586 t.Fatalf("got %s, missing expected Info log: %s", string(b), x)
587 }
588 }
589
590 func TestLogBacktraceAt(t *testing.T) {
591 defer CaptureState().Restore()
592 setFlags()
593 defer logging.swap(logging.newBuffers())
594
595
596 var infoLine string
597 setTraceLocation := func(file string, line int, ok bool, delta int) {
598 if !ok {
599 t.Fatal("could not get file:line")
600 }
601 _, file = filepath.Split(file)
602 infoLine = fmt.Sprintf("%s:%d", file, line+delta)
603 err := logging.traceLocation.Set(infoLine)
604 if err != nil {
605 t.Fatal("error setting log_backtrace_at: ", err)
606 }
607 }
608 {
609
610 _, file, line, ok := runtime.Caller(0)
611 setTraceLocation(file, line, ok, +2)
612 Info("we want a stack trace here")
613 }
614 numAppearances := strings.Count(contents(severity.InfoLog), infoLine)
615 if numAppearances < 2 {
616
617
618
619
620
621
622
623 t.Fatal("got no trace back; log is ", contents(severity.InfoLog))
624 }
625 }
626
627 func BenchmarkHeader(b *testing.B) {
628 for i := 0; i < b.N; i++ {
629 buf, _, _ := logging.header(severity.InfoLog, 0)
630 buffer.PutBuffer(buf)
631 }
632 }
633
634 func BenchmarkHeaderWithDir(b *testing.B) {
635 logging.addDirHeader = true
636 for i := 0; i < b.N; i++ {
637 buf, _, _ := logging.header(severity.InfoLog, 0)
638 buffer.PutBuffer(buf)
639 }
640 }
641
642
643 var result interface{}
644 var enabled bool
645
646 func BenchmarkV(b *testing.B) {
647 var v Verbose
648 for i := 0; i < b.N; i++ {
649 v = V(10)
650 }
651 enabled = v.Enabled()
652 }
653
654 func BenchmarkKRef(b *testing.B) {
655 var r ObjectRef
656 for i := 0; i < b.N; i++ {
657 r = KRef("namespace", "name")
658 }
659 result = r
660 }
661
662 func BenchmarkKObj(b *testing.B) {
663 a := test.KMetadataMock{Name: "a", NS: "a"}
664 var r ObjectRef
665 for i := 0; i < b.N; i++ {
666 r = KObj(&a)
667 }
668 result = r
669 }
670
671
672
673
674
675
676
677
678
679
680 func BenchmarkKObjs(b *testing.B) {
681 for length := 0; length <= 100; length += 10 {
682 b.Run(fmt.Sprintf("%d", length), func(b *testing.B) {
683 arg := make([]interface{}, length)
684 for i := 0; i < length; i++ {
685 arg[i] = test.KMetadataMock{Name: "a", NS: "a"}
686 }
687
688 b.Run("simple", func(b *testing.B) {
689 b.ResetTimer()
690 for i := 0; i < b.N; i++ {
691 V(5).InfoS("benchmark", "objs", KObjs(arg))
692 }
693 })
694
695 b.Run("conditional", func(b *testing.B) {
696 b.ResetTimer()
697 for i := 0; i < b.N; i++ {
698 if klogV := V(5); klogV.Enabled() {
699 klogV.InfoS("benchmark", "objs", KObjs(arg))
700 }
701 }
702 })
703 })
704 }
705 }
706
707
708 func BenchmarkKObjSlice(b *testing.B) {
709 for length := 0; length <= 100; length += 10 {
710 b.Run(fmt.Sprintf("%d", length), func(b *testing.B) {
711 arg := make([]interface{}, length)
712 for i := 0; i < length; i++ {
713 arg[i] = test.KMetadataMock{Name: "a", NS: "a"}
714 }
715
716 b.Run("simple", func(b *testing.B) {
717 b.ResetTimer()
718 for i := 0; i < b.N; i++ {
719 V(5).InfoS("benchmark", "objs", KObjSlice(arg))
720 }
721 })
722
723 b.Run("conditional", func(b *testing.B) {
724 b.ResetTimer()
725 for i := 0; i < b.N; i++ {
726 if klogV := V(5); klogV.Enabled() {
727 klogV.InfoS("benchmark", "objs", KObjSlice(arg))
728 }
729 }
730 })
731 })
732 }
733 }
734
735
736
737 func BenchmarkScalars(b *testing.B) {
738 b.Run("simple", func(b *testing.B) {
739 b.ResetTimer()
740 for i := 0; i < b.N; i++ {
741 V(5).InfoS("benchmark", "str", "hello world", "int", 42)
742 }
743 })
744
745 b.Run("conditional", func(b *testing.B) {
746 b.ResetTimer()
747 for i := 0; i < b.N; i++ {
748 if klogV := V(5); klogV.Enabled() {
749 klogV.InfoS("benchmark", "str", "hello world", "int", 42)
750 }
751 }
752 })
753 }
754
755
756
757 func BenchmarkScalarsWithLogger(b *testing.B) {
758 logger := Background()
759 b.Run("simple", func(b *testing.B) {
760 b.ResetTimer()
761 for i := 0; i < b.N; i++ {
762 logger.V(5).Info("benchmark", "str", "hello world", "int", 42)
763 }
764 })
765
766 b.Run("conditional", func(b *testing.B) {
767 b.ResetTimer()
768 for i := 0; i < b.N; i++ {
769 if loggerV := logger.V(5); loggerV.Enabled() {
770 loggerV.Info("benchmark", "str", "hello world", "int", 42)
771 }
772 }
773 })
774 }
775
776
777
778
779
780 func BenchmarkKObjSliceWithLogger(b *testing.B) {
781 logger := Background()
782 arg := []interface{}{test.KMetadataMock{Name: "a", NS: "a"}}
783 b.Run("simple", func(b *testing.B) {
784 b.ResetTimer()
785 for i := 0; i < b.N; i++ {
786 logger.V(5).Info("benchmark", "objs", KObjSlice(arg))
787 }
788 })
789
790 b.Run("conditional", func(b *testing.B) {
791 b.ResetTimer()
792 for i := 0; i < b.N; i++ {
793 if loggerV := logger.V(5); loggerV.Enabled() {
794 loggerV.Info("benchmark", "objs", KObjSlice(arg))
795 }
796 }
797 })
798 }
799
800 func BenchmarkLogs(b *testing.B) {
801 defer CaptureState().Restore()
802 setFlags()
803 defer logging.swap(logging.newBuffers())
804
805 testFile, err := ioutil.TempFile("", "test.log")
806 if err != nil {
807 b.Fatal("unable to create temporary file")
808 }
809 defer os.Remove(testFile.Name())
810
811 require.NoError(b, logging.verbosity.Set("0"))
812 logging.toStderr = false
813 logging.alsoToStderr = false
814 logging.stderrThreshold = severityValue{
815 Severity: severity.FatalLog,
816 }
817 logging.logFile = testFile.Name()
818 logging.swap([severity.NumSeverity]io.Writer{nil, nil, nil, nil})
819
820 for i := 0; i < b.N; i++ {
821 Error("error")
822 Warning("warning")
823 Info("info")
824 }
825 needToSync := logging.flushAll()
826 sb, ok := logging.file[severity.InfoLog].(*syncBuffer)
827 if !ok {
828 b.Fatal("info wasn't created")
829 }
830 if needToSync.num != 1 || needToSync.files[0] != sb.file {
831 b.Fatalf("Should have received exactly the file from severity.InfoLog for syncing, got instead: %+v", needToSync)
832 }
833 logging.syncAll(needToSync)
834 }
835
836 func BenchmarkFlush(b *testing.B) {
837 defer CaptureState().Restore()
838 setFlags()
839 defer logging.swap(logging.newBuffers())
840
841 testFile, err := ioutil.TempFile("", "test.log")
842 if err != nil {
843 b.Fatal("unable to create temporary file")
844 }
845 defer os.Remove(testFile.Name())
846
847 require.NoError(b, logging.verbosity.Set("0"))
848 logging.toStderr = false
849 logging.alsoToStderr = false
850 logging.stderrThreshold = severityValue{
851 Severity: severity.FatalLog,
852 }
853 logging.logFile = testFile.Name()
854 logging.swap([severity.NumSeverity]io.Writer{nil, nil, nil, nil})
855
856
857 Info("info")
858 needToSync := logging.flushAll()
859
860 if needToSync.num != 1 {
861 b.Fatalf("expected exactly one file to sync, got: %+v", needToSync)
862 }
863
864 b.ResetTimer()
865
866 for i := 0; i < b.N; i++ {
867 needToSync := logging.flushAll()
868 logging.syncAll(needToSync)
869 }
870 }
871
872
873 func TestFileSizeCheck(t *testing.T) {
874 defer CaptureState().Restore()
875 setFlags()
876 testData := map[string]struct {
877 testLogFile string
878 testLogFileMaxSizeMB uint64
879 testCurrentSize uint64
880 expectedResult bool
881 }{
882 "logFile not specified, exceeds max size": {
883 testLogFile: "",
884 testLogFileMaxSizeMB: 1,
885 testCurrentSize: 1024 * 1024 * 2000,
886 expectedResult: true,
887 },
888
889 "logFile not specified, not exceeds max size": {
890 testLogFile: "",
891 testLogFileMaxSizeMB: 1,
892 testCurrentSize: 1024 * 1024 * 1000,
893 expectedResult: false,
894 },
895 "logFile specified, exceeds max size": {
896 testLogFile: "/tmp/test.log",
897 testLogFileMaxSizeMB: 500,
898 testCurrentSize: 1024 * 1024 * 1000,
899 expectedResult: true,
900 },
901 "logFile specified, not exceeds max size": {
902 testLogFile: "/tmp/test.log",
903 testLogFileMaxSizeMB: 500,
904 testCurrentSize: 1024 * 1024 * 300,
905 expectedResult: false,
906 },
907 }
908
909 for name, test := range testData {
910 logging.logFile = test.testLogFile
911 logging.logFileMaxSizeMB = test.testLogFileMaxSizeMB
912 actualResult := test.testCurrentSize >= CalculateMaxSize()
913 if test.expectedResult != actualResult {
914 t.Fatalf("Error on test case '%v': Was expecting result equals %v, got %v",
915 name, test.expectedResult, actualResult)
916 }
917 }
918 }
919
920 func TestInitFlags(t *testing.T) {
921 defer CaptureState().Restore()
922 setFlags()
923
924 fs1 := flag.NewFlagSet("test1", flag.PanicOnError)
925 InitFlags(fs1)
926 require.NoError(t, fs1.Set("log_dir", "/test1"))
927 require.NoError(t, fs1.Set("log_file_max_size", "1"))
928 fs2 := flag.NewFlagSet("test2", flag.PanicOnError)
929 InitFlags(fs2)
930 if logging.logDir != "/test1" {
931 t.Fatalf("Expected log_dir to be %q, got %q", "/test1", logging.logDir)
932 }
933 require.NoError(t, fs2.Set("log_file_max_size", "2048"))
934 if logging.logFileMaxSizeMB != 2048 {
935 t.Fatal("Expected log_file_max_size to be 2048")
936 }
937 }
938
939 func TestCommandLine(t *testing.T) {
940 var fs flag.FlagSet
941 InitFlags(&fs)
942
943 expectedFlags := ` -add_dir_header
944 If true, adds the file directory to the header of the log messages
945 -alsologtostderr
946 log to standard error as well as files (no effect when -logtostderr=true)
947 -log_backtrace_at value
948 when logging hits line file:N, emit a stack trace
949 -log_dir string
950 If non-empty, write log files in this directory (no effect when -logtostderr=true)
951 -log_file string
952 If non-empty, use this log file (no effect when -logtostderr=true)
953 -log_file_max_size uint
954 Defines the maximum size a log file can grow to (no effect when -logtostderr=true). Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800)
955 -logtostderr
956 log to standard error instead of files (default true)
957 -one_output
958 If true, only write logs to their native severity level (vs also writing to each lower severity level; no effect when -logtostderr=true)
959 -skip_headers
960 If true, avoid header prefixes in the log messages
961 -skip_log_headers
962 If true, avoid headers when opening log files (no effect when -logtostderr=true)
963 -stderrthreshold value
964 logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=true) (default 2)
965 -v value
966 number for the log level verbosity
967 -vmodule value
968 comma-separated list of pattern=N settings for file-filtered logging
969 `
970
971 var output bytes.Buffer
972 fs.SetOutput(&output)
973 fs.PrintDefaults()
974 actualFlags := output.String()
975
976 if expectedFlags != actualFlags {
977 t.Fatalf("Command line changed.\nExpected:\n%q\nActual:\n%q\n", expectedFlags, actualFlags)
978 }
979 }
980
981 func TestInfoObjectRef(t *testing.T) {
982 defer CaptureState().Restore()
983 setFlags()
984 defer logging.swap(logging.newBuffers())
985
986 tests := []struct {
987 name string
988 ref ObjectRef
989 want string
990 }{
991 {
992 name: "with ns",
993 ref: ObjectRef{
994 Name: "test-name",
995 Namespace: "test-ns",
996 },
997 want: "test-ns/test-name",
998 },
999 {
1000 name: "without ns",
1001 ref: ObjectRef{
1002 Name: "test-name",
1003 Namespace: "",
1004 },
1005 want: "test-name",
1006 },
1007 {
1008 name: "empty",
1009 ref: ObjectRef{},
1010 want: "",
1011 },
1012 }
1013
1014 for _, tt := range tests {
1015 t.Run(tt.name, func(t *testing.T) {
1016 Info(tt.ref)
1017 if !contains(severity.InfoLog, tt.want) {
1018 t.Errorf("expected %v, got %v", tt.want, contents(severity.InfoLog))
1019 }
1020 })
1021 }
1022 }
1023
1024 func TestKObj(t *testing.T) {
1025 tests := []struct {
1026 name string
1027 obj KMetadata
1028 want ObjectRef
1029 }{
1030 {
1031 name: "nil passed as pointer KMetadata implementation",
1032 obj: (*test.PtrKMetadataMock)(nil),
1033 want: ObjectRef{},
1034 },
1035 {
1036 name: "empty struct passed as non-pointer KMetadata implementation",
1037 obj: test.KMetadataMock{},
1038 want: ObjectRef{},
1039 },
1040 {
1041 name: "nil pointer passed to non-pointer KMetadata implementation",
1042 obj: (*test.KMetadataMock)(nil),
1043 want: ObjectRef{},
1044 },
1045 {
1046 name: "nil",
1047 obj: nil,
1048 want: ObjectRef{},
1049 },
1050 {
1051 name: "with ns",
1052 obj: &test.KMetadataMock{Name: "test-name", NS: "test-ns"},
1053 want: ObjectRef{
1054 Name: "test-name",
1055 Namespace: "test-ns",
1056 },
1057 },
1058 {
1059 name: "without ns",
1060 obj: &test.KMetadataMock{Name: "test-name", NS: ""},
1061 want: ObjectRef{
1062 Name: "test-name",
1063 },
1064 },
1065 }
1066
1067 for _, tt := range tests {
1068 t.Run(tt.name, func(t *testing.T) {
1069 if KObj(tt.obj) != tt.want {
1070 t.Errorf("expected %v, got %v", tt.want, KObj(tt.obj))
1071 }
1072 })
1073 }
1074 }
1075
1076 func TestKRef(t *testing.T) {
1077 tests := []struct {
1078 testname string
1079 name string
1080 namespace string
1081 want ObjectRef
1082 }{
1083 {
1084 testname: "with ns",
1085 name: "test-name",
1086 namespace: "test-ns",
1087 want: ObjectRef{
1088 Name: "test-name",
1089 Namespace: "test-ns",
1090 },
1091 },
1092 {
1093 testname: "without ns",
1094 name: "test-name",
1095 want: ObjectRef{
1096 Name: "test-name",
1097 },
1098 },
1099 }
1100
1101 for _, tt := range tests {
1102 t.Run(tt.testname, func(t *testing.T) {
1103 if KRef(tt.namespace, tt.name) != tt.want {
1104 t.Errorf("expected %v, got %v", tt.want, KRef(tt.namespace, tt.name))
1105 }
1106 })
1107 }
1108 }
1109
1110
1111 func TestInfoS(t *testing.T) {
1112 defer CaptureState().Restore()
1113 setFlags()
1114 defer logging.swap(logging.newBuffers())
1115 timeNow = func() time.Time {
1116 return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local)
1117 }
1118 pid = 1234
1119 var testDataInfo = []struct {
1120 msg string
1121 format string
1122 keysValues []interface{}
1123 }{
1124 {
1125 msg: "test",
1126 format: "I0102 15:04:05.067890 1234 klog_test.go:%d] \"test\" pod=\"kubedns\"\n",
1127 keysValues: []interface{}{"pod", "kubedns"},
1128 },
1129 {
1130 msg: "test",
1131 format: "I0102 15:04:05.067890 1234 klog_test.go:%d] \"test\" replicaNum=20\n",
1132 keysValues: []interface{}{"replicaNum", 20},
1133 },
1134 {
1135 msg: "test",
1136 format: "I0102 15:04:05.067890 1234 klog_test.go:%d] \"test\" err=\"test error\"\n",
1137 keysValues: []interface{}{"err", errors.New("test error")},
1138 },
1139 {
1140 msg: "test",
1141 format: "I0102 15:04:05.067890 1234 klog_test.go:%d] \"test\" err=\"test error\"\n",
1142 keysValues: []interface{}{"err", errors.New("test error")},
1143 },
1144 }
1145
1146 functions := []func(msg string, keyAndValues ...interface{}){
1147 InfoS,
1148 myInfoS,
1149 }
1150 for _, f := range functions {
1151 for _, data := range testDataInfo {
1152 logging.file[severity.InfoLog] = &flushBuffer{}
1153 f(data.msg, data.keysValues...)
1154 var line int
1155 n, err := fmt.Sscanf(contents(severity.InfoLog), data.format, &line)
1156 if n != 1 || err != nil {
1157 t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(severity.InfoLog))
1158 }
1159 want := fmt.Sprintf(data.format, line)
1160 if contents(severity.InfoLog) != want {
1161 t.Errorf("InfoS has wrong format: \n got:\t%s\nwant:\t%s", contents(severity.InfoLog), want)
1162 }
1163 }
1164 }
1165 }
1166
1167
1168 func TestVInfoS(t *testing.T) {
1169 defer CaptureState().Restore()
1170 setFlags()
1171 defer logging.swap(logging.newBuffers())
1172 timeNow = func() time.Time {
1173 return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local)
1174 }
1175 pid = 1234
1176 myData := struct {
1177 Data string
1178 }{
1179 Data: `This is some long text
1180 with a line break.`,
1181 }
1182 var testDataInfo = []struct {
1183 msg string
1184 format string
1185 keysValues []interface{}
1186 }{
1187 {
1188 msg: "test",
1189 format: "I0102 15:04:05.067890 1234 klog_test.go:%d] \"test\" pod=\"kubedns\"\n",
1190 keysValues: []interface{}{"pod", "kubedns"},
1191 },
1192 {
1193 msg: "test",
1194 format: "I0102 15:04:05.067890 1234 klog_test.go:%d] \"test\" replicaNum=20\n",
1195 keysValues: []interface{}{"replicaNum", 20},
1196 },
1197 {
1198 msg: "test",
1199 format: "I0102 15:04:05.067890 1234 klog_test.go:%d] \"test\" err=\"test error\"\n",
1200 keysValues: []interface{}{"err", errors.New("test error")},
1201 },
1202 {
1203 msg: `first message line
1204 second message line`,
1205 format: `I0102 15:04:05.067890 1234 klog_test.go:%d] "first message line\nsecond message line" multiLine=<
1206 first value line
1207 second value line
1208 >
1209 `,
1210 keysValues: []interface{}{"multiLine", `first value line
1211 second value line`},
1212 },
1213 {
1214 msg: `message`,
1215 format: `I0102 15:04:05.067890 1234 klog_test.go:%d] "message" myData={"Data":"This is some long text\nwith a line break."}
1216 `,
1217 keysValues: []interface{}{"myData", myData},
1218 },
1219 }
1220
1221 require.NoError(t, logging.verbosity.Set("2"))
1222
1223 for l := Level(0); l < Level(4); l++ {
1224 for _, data := range testDataInfo {
1225 logging.file[severity.InfoLog] = &flushBuffer{}
1226
1227 V(l).InfoS(data.msg, data.keysValues...)
1228
1229 var want string
1230 var line int
1231 if l <= 2 {
1232 n, err := fmt.Sscanf(contents(severity.InfoLog), data.format, &line)
1233 if n != 1 || err != nil {
1234 t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(severity.InfoLog))
1235 }
1236
1237 want = fmt.Sprintf(data.format, line)
1238 } else {
1239 want = ""
1240 }
1241 if contents(severity.InfoLog) != want {
1242 t.Errorf("V(%d).InfoS has unexpected output:\ngot:\n%s\nwant:\n%s\n", l, contents(severity.InfoLog), want)
1243 }
1244 }
1245 }
1246 }
1247
1248
1249 func TestErrorS(t *testing.T) {
1250 defer CaptureState().Restore()
1251 setFlags()
1252 defer logging.swap(logging.newBuffers())
1253 timeNow = func() time.Time {
1254 return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local)
1255 }
1256 logging.logFile = ""
1257 pid = 1234
1258
1259 functions := []func(err error, msg string, keyAndValues ...interface{}){
1260 ErrorS,
1261 myErrorS,
1262 }
1263 for _, f := range functions {
1264 var errorList = []struct {
1265 err error
1266 format string
1267 }{
1268 {
1269 err: fmt.Errorf("update status failed"),
1270 format: "E0102 15:04:05.067890 1234 klog_test.go:%d] \"Failed to update pod status\" err=\"update status failed\" pod=\"kubedns\"\n",
1271 },
1272 {
1273 err: nil,
1274 format: "E0102 15:04:05.067890 1234 klog_test.go:%d] \"Failed to update pod status\" pod=\"kubedns\"\n",
1275 },
1276 }
1277 for _, e := range errorList {
1278 logging.file[severity.ErrorLog] = &flushBuffer{}
1279 f(e.err, "Failed to update pod status", "pod", "kubedns")
1280 var line int
1281 n, err := fmt.Sscanf(contents(severity.ErrorLog), e.format, &line)
1282 if n != 1 || err != nil {
1283 t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(severity.ErrorLog))
1284 }
1285 want := fmt.Sprintf(e.format, line)
1286 if contents(severity.ErrorLog) != want {
1287 t.Errorf("ErrorS has wrong format:\ngot:\n%s\nwant:\n%s\n", contents(severity.ErrorLog), want)
1288 }
1289 }
1290 }
1291 }
1292
1293 func createTestValueOfLoggingT() *loggingT {
1294 l := new(loggingT)
1295 l.toStderr = true
1296 l.alsoToStderr = false
1297 l.stderrThreshold = severityValue{
1298 Severity: severity.ErrorLog,
1299 }
1300 l.verbosity = Level(0)
1301 l.skipHeaders = false
1302 l.skipLogHeaders = false
1303 l.addDirHeader = false
1304 return l
1305 }
1306
1307 func createTestValueOfModulePat(p string, li bool, le Level) modulePat {
1308 m := modulePat{}
1309 m.pattern = p
1310 m.literal = li
1311 m.level = le
1312 return m
1313 }
1314
1315 func compareModuleSpec(a, b moduleSpec) bool {
1316 if len(a.filter) != len(b.filter) {
1317 return false
1318 }
1319
1320 for i := 0; i < len(a.filter); i++ {
1321 if a.filter[i] != b.filter[i] {
1322 return false
1323 }
1324 }
1325
1326 return true
1327 }
1328
1329 func TestSetVState(t *testing.T) {
1330
1331 want := createTestValueOfLoggingT()
1332 want.verbosity = Level(3)
1333 want.vmodule.filter = []modulePat{
1334 createTestValueOfModulePat("recordio", true, Level(2)),
1335 createTestValueOfModulePat("file", true, Level(1)),
1336 createTestValueOfModulePat("gfs*", false, Level(3)),
1337 createTestValueOfModulePat("gopher*", false, Level(3)),
1338 }
1339 want.filterLength = 4
1340
1341
1342 target := createTestValueOfLoggingT()
1343
1344 tf := []modulePat{
1345 createTestValueOfModulePat("recordio", true, Level(2)),
1346 createTestValueOfModulePat("file", true, Level(1)),
1347 createTestValueOfModulePat("gfs*", false, Level(3)),
1348 createTestValueOfModulePat("gopher*", false, Level(3)),
1349 }
1350
1351 target.setVState(Level(3), tf, true)
1352
1353 if want.verbosity != target.verbosity || !compareModuleSpec(want.vmodule, target.vmodule) || want.filterLength != target.filterLength {
1354 t.Errorf("setVState method doesn't configure loggingT values' verbosity, vmodule or filterLength:\nwant:\n\tverbosity:\t%v\n\tvmodule:\t%v\n\tfilterLength:\t%v\ngot:\n\tverbosity:\t%v\n\tvmodule:\t%v\n\tfilterLength:\t%v", want.verbosity, want.vmodule, want.filterLength, target.verbosity, target.vmodule, target.filterLength)
1355 }
1356 }
1357
1358 type sampleLogFilter struct{}
1359
1360 func (f *sampleLogFilter) Filter(args []interface{}) []interface{} {
1361 for i, arg := range args {
1362 v, ok := arg.(string)
1363 if ok && strings.Contains(v, "filter me") {
1364 args[i] = "[FILTERED]"
1365 }
1366 }
1367 return args
1368 }
1369
1370 func (f *sampleLogFilter) FilterF(format string, args []interface{}) (string, []interface{}) {
1371 return strings.Replace(format, "filter me", "[FILTERED]", 1), f.Filter(args)
1372 }
1373
1374 func (f *sampleLogFilter) FilterS(msg string, keysAndValues []interface{}) (string, []interface{}) {
1375 return strings.Replace(msg, "filter me", "[FILTERED]", 1), f.Filter(keysAndValues)
1376 }
1377
1378 func TestLogFilter(t *testing.T) {
1379 defer CaptureState().Restore()
1380 setFlags()
1381 defer logging.swap(logging.newBuffers())
1382 SetLogFilter(&sampleLogFilter{})
1383 funcs := []struct {
1384 name string
1385 logFunc func(args ...interface{})
1386 severity severity.Severity
1387 }{{
1388 name: "Info",
1389 logFunc: Info,
1390 severity: severity.InfoLog,
1391 }, {
1392 name: "InfoDepth",
1393 logFunc: func(args ...interface{}) {
1394 InfoDepth(1, args...)
1395 },
1396 severity: severity.InfoLog,
1397 }, {
1398 name: "Infoln",
1399 logFunc: Infoln,
1400 severity: severity.InfoLog,
1401 }, {
1402 name: "Infof",
1403 logFunc: func(args ...interface{}) {
1404
1405 Infof(args[0].(string), args[1:]...)
1406 },
1407 severity: severity.InfoLog,
1408 }, {
1409 name: "InfoS",
1410 logFunc: func(args ...interface{}) {
1411 InfoS(args[0].(string), args[1:]...)
1412 },
1413 severity: severity.InfoLog,
1414 }, {
1415 name: "Warning",
1416 logFunc: Warning,
1417 severity: severity.WarningLog,
1418 }, {
1419 name: "WarningDepth",
1420 logFunc: func(args ...interface{}) {
1421 WarningDepth(1, args...)
1422 },
1423 severity: severity.WarningLog,
1424 }, {
1425 name: "Warningln",
1426 logFunc: Warningln,
1427 severity: severity.WarningLog,
1428 }, {
1429 name: "Warningf",
1430 logFunc: func(args ...interface{}) {
1431 Warningf(args[0].(string), args[1:]...)
1432 },
1433 severity: severity.WarningLog,
1434 }, {
1435 name: "Error",
1436 logFunc: Error,
1437 severity: severity.ErrorLog,
1438 }, {
1439 name: "ErrorDepth",
1440 logFunc: func(args ...interface{}) {
1441 ErrorDepth(1, args...)
1442 },
1443 severity: severity.ErrorLog,
1444 }, {
1445 name: "Errorln",
1446 logFunc: Errorln,
1447 severity: severity.ErrorLog,
1448 }, {
1449 name: "Errorf",
1450 logFunc: func(args ...interface{}) {
1451 Errorf(args[0].(string), args[1:]...)
1452 },
1453 severity: severity.ErrorLog,
1454 }, {
1455 name: "ErrorS",
1456 logFunc: func(args ...interface{}) {
1457 ErrorS(errors.New("testerror"), args[0].(string), args[1:]...)
1458 },
1459 severity: severity.ErrorLog,
1460 }, {
1461 name: "V().Info",
1462 logFunc: func(args ...interface{}) {
1463 V(0).Info(args...)
1464 },
1465 severity: severity.InfoLog,
1466 }, {
1467 name: "V().Infoln",
1468 logFunc: func(args ...interface{}) {
1469 V(0).Infoln(args...)
1470 },
1471 severity: severity.InfoLog,
1472 }, {
1473 name: "V().Infof",
1474 logFunc: func(args ...interface{}) {
1475 V(0).Infof(args[0].(string), args[1:]...)
1476 },
1477 severity: severity.InfoLog,
1478 }, {
1479 name: "V().InfoS",
1480 logFunc: func(args ...interface{}) {
1481 V(0).InfoS(args[0].(string), args[1:]...)
1482 },
1483 severity: severity.InfoLog,
1484 }, {
1485 name: "V().Error",
1486 logFunc: func(args ...interface{}) {
1487 V(0).Error(errors.New("test error"), args[0].(string), args[1:]...)
1488 },
1489 severity: severity.ErrorLog,
1490 }, {
1491 name: "V().ErrorS",
1492 logFunc: func(args ...interface{}) {
1493 V(0).ErrorS(errors.New("test error"), args[0].(string), args[1:]...)
1494 },
1495 severity: severity.ErrorLog,
1496 }}
1497
1498 testcases := []struct {
1499 name string
1500 args []interface{}
1501 expectFiltered bool
1502 }{{
1503 args: []interface{}{"%s:%s", "foo", "bar"},
1504 expectFiltered: false,
1505 }, {
1506 args: []interface{}{"%s:%s", "foo", "filter me"},
1507 expectFiltered: true,
1508 }, {
1509 args: []interface{}{"filter me %s:%s", "foo", "bar"},
1510 expectFiltered: true,
1511 }}
1512
1513 for _, f := range funcs {
1514 for _, tc := range testcases {
1515 logging.newBuffers()
1516 f.logFunc(tc.args...)
1517 got := contains(f.severity, "[FILTERED]")
1518 if got != tc.expectFiltered {
1519 t.Errorf("%s filter application failed, got %v, want %v", f.name, got, tc.expectFiltered)
1520 }
1521 }
1522 }
1523 }
1524
1525 func TestInfoWithLogr(t *testing.T) {
1526 logger := new(testLogr)
1527
1528 testDataInfo := []struct {
1529 msg string
1530 expected testLogrEntry
1531 }{{
1532 msg: "foo",
1533 expected: testLogrEntry{
1534 severity: severity.InfoLog,
1535 msg: "foo",
1536 },
1537 }, {
1538 msg: "",
1539 expected: testLogrEntry{
1540 severity: severity.InfoLog,
1541 msg: "",
1542 },
1543 }}
1544
1545 for _, data := range testDataInfo {
1546 t.Run(data.msg, func(t *testing.T) {
1547 l := logr.New(logger)
1548 defer CaptureState().Restore()
1549 SetLogger(l)
1550 defer logger.reset()
1551
1552 Info(data.msg)
1553
1554 if !reflect.DeepEqual(logger.entries, []testLogrEntry{data.expected}) {
1555 t.Errorf("expected: %+v; but got: %+v", []testLogrEntry{data.expected}, logger.entries)
1556 }
1557 })
1558 }
1559 }
1560
1561 func TestInfoSWithLogr(t *testing.T) {
1562 logger := new(testLogr)
1563
1564 testDataInfo := []struct {
1565 msg string
1566 keysValues []interface{}
1567 expected testLogrEntry
1568 }{{
1569 msg: "foo",
1570 keysValues: []interface{}{},
1571 expected: testLogrEntry{
1572 severity: severity.InfoLog,
1573 msg: "foo",
1574 keysAndValues: []interface{}{},
1575 },
1576 }, {
1577 msg: "bar",
1578 keysValues: []interface{}{"a", 1},
1579 expected: testLogrEntry{
1580 severity: severity.InfoLog,
1581 msg: "bar",
1582 keysAndValues: []interface{}{"a", 1},
1583 },
1584 }}
1585
1586 for _, data := range testDataInfo {
1587 t.Run(data.msg, func(t *testing.T) {
1588 defer CaptureState().Restore()
1589 l := logr.New(logger)
1590 SetLogger(l)
1591 defer logger.reset()
1592
1593 InfoS(data.msg, data.keysValues...)
1594
1595 if !reflect.DeepEqual(logger.entries, []testLogrEntry{data.expected}) {
1596 t.Errorf("expected: %+v; but got: %+v", []testLogrEntry{data.expected}, logger.entries)
1597 }
1598 })
1599 }
1600 }
1601
1602 func TestErrorSWithLogr(t *testing.T) {
1603 logger := new(testLogr)
1604
1605 testError := errors.New("testError")
1606
1607 testDataInfo := []struct {
1608 err error
1609 msg string
1610 keysValues []interface{}
1611 expected testLogrEntry
1612 }{{
1613 err: testError,
1614 msg: "foo1",
1615 keysValues: []interface{}{},
1616 expected: testLogrEntry{
1617 severity: severity.ErrorLog,
1618 msg: "foo1",
1619 keysAndValues: []interface{}{},
1620 err: testError,
1621 },
1622 }, {
1623 err: testError,
1624 msg: "bar1",
1625 keysValues: []interface{}{"a", 1},
1626 expected: testLogrEntry{
1627 severity: severity.ErrorLog,
1628 msg: "bar1",
1629 keysAndValues: []interface{}{"a", 1},
1630 err: testError,
1631 },
1632 }, {
1633 err: nil,
1634 msg: "foo2",
1635 keysValues: []interface{}{},
1636 expected: testLogrEntry{
1637 severity: severity.ErrorLog,
1638 msg: "foo2",
1639 keysAndValues: []interface{}{},
1640 err: nil,
1641 },
1642 }, {
1643 err: nil,
1644 msg: "bar2",
1645 keysValues: []interface{}{"a", 1},
1646 expected: testLogrEntry{
1647 severity: severity.ErrorLog,
1648 msg: "bar2",
1649 keysAndValues: []interface{}{"a", 1},
1650 err: nil,
1651 },
1652 }}
1653
1654 for _, data := range testDataInfo {
1655 t.Run(data.msg, func(t *testing.T) {
1656 defer CaptureState().Restore()
1657 l := logr.New(logger)
1658 SetLogger(l)
1659 defer logger.reset()
1660
1661 ErrorS(data.err, data.msg, data.keysValues...)
1662
1663 if !reflect.DeepEqual(logger.entries, []testLogrEntry{data.expected}) {
1664 t.Errorf("expected: %+v; but got: %+v", []testLogrEntry{data.expected}, logger.entries)
1665 }
1666 })
1667 }
1668 }
1669
1670 func TestCallDepthLogr(t *testing.T) {
1671 logger := &callDepthTestLogr{}
1672 logger.resetCallDepth()
1673
1674 testCases := []struct {
1675 name string
1676 logFn func()
1677 }{
1678 {
1679 name: "Info log",
1680 logFn: func() { Info("info log") },
1681 },
1682 {
1683 name: "InfoDepth log",
1684 logFn: func() { InfoDepth(0, "infodepth log") },
1685 },
1686 {
1687 name: "InfoSDepth log",
1688 logFn: func() { InfoSDepth(0, "infoSDepth log") },
1689 },
1690 {
1691 name: "Warning log",
1692 logFn: func() { Warning("warning log") },
1693 },
1694 {
1695 name: "WarningDepth log",
1696 logFn: func() { WarningDepth(0, "warningdepth log") },
1697 },
1698 {
1699 name: "Error log",
1700 logFn: func() { Error("error log") },
1701 },
1702 {
1703 name: "ErrorDepth log",
1704 logFn: func() { ErrorDepth(0, "errordepth log") },
1705 },
1706 {
1707 name: "ErrorSDepth log",
1708 logFn: func() { ErrorSDepth(0, errors.New("some error"), "errorSDepth log") },
1709 },
1710 }
1711
1712 for _, tc := range testCases {
1713 t.Run(tc.name, func(t *testing.T) {
1714 l := logr.New(logger)
1715 defer ClearLogger()
1716 SetLogger(l)
1717 defer logger.reset()
1718 defer logger.resetCallDepth()
1719
1720
1721 _, wantFile, wantLine, _ := runtime.Caller(0)
1722 tc.logFn()
1723 wantLine++
1724
1725 if len(logger.entries) != 1 {
1726 t.Errorf("expected a single log entry to be generated, got %d", len(logger.entries))
1727 }
1728 checkLogrEntryCorrectCaller(t, wantFile, wantLine, logger.entries[0])
1729 })
1730 }
1731 }
1732
1733 func TestCallDepthLogrInfoS(t *testing.T) {
1734 logger := &callDepthTestLogr{}
1735 logger.resetCallDepth()
1736 l := logr.New(logger)
1737 defer CaptureState().Restore()
1738 SetLogger(l)
1739
1740
1741 logFunc := func() {
1742 InfoS("infoS log")
1743 }
1744
1745
1746 _, wantFile, wantLine, _ := runtime.Caller(0)
1747 logFunc()
1748 wantLine++
1749
1750 if len(logger.entries) != 1 {
1751 t.Errorf("expected a single log entry to be generated, got %d", len(logger.entries))
1752 }
1753 checkLogrEntryCorrectCaller(t, wantFile, wantLine, logger.entries[0])
1754 }
1755
1756 func TestCallDepthLogrErrorS(t *testing.T) {
1757 logger := &callDepthTestLogr{}
1758 logger.resetCallDepth()
1759 l := logr.New(logger)
1760 defer CaptureState().Restore()
1761 SetLogger(l)
1762
1763
1764 logFunc := func() {
1765 ErrorS(errors.New("some error"), "errorS log")
1766 }
1767
1768
1769 _, wantFile, wantLine, _ := runtime.Caller(0)
1770 logFunc()
1771 wantLine++
1772
1773 if len(logger.entries) != 1 {
1774 t.Errorf("expected a single log entry to be generated, got %d", len(logger.entries))
1775 }
1776 checkLogrEntryCorrectCaller(t, wantFile, wantLine, logger.entries[0])
1777 }
1778
1779 func TestCallDepthLogrGoLog(t *testing.T) {
1780 defer CaptureState().Restore()
1781 logger := &callDepthTestLogr{}
1782 logger.resetCallDepth()
1783 l := logr.New(logger)
1784 SetLogger(l)
1785 CopyStandardLogTo("INFO")
1786
1787
1788 logFunc := func() {
1789 stdLog.Print("some log")
1790 }
1791
1792
1793 _, wantFile, wantLine, _ := runtime.Caller(0)
1794 logFunc()
1795 wantLine++
1796
1797 if len(logger.entries) != 1 {
1798 t.Errorf("expected a single log entry to be generated, got %d", len(logger.entries))
1799 }
1800 checkLogrEntryCorrectCaller(t, wantFile, wantLine, logger.entries[0])
1801 fmt.Println(logger.entries[0])
1802 }
1803
1804
1805 func TestCallDepthTestLogr(t *testing.T) {
1806 logger := &callDepthTestLogr{}
1807 logger.resetCallDepth()
1808
1809 logFunc := func() {
1810 logger.Info(0, "some info log")
1811 }
1812
1813 _, wantFile, wantLine, _ := runtime.Caller(0)
1814 logFunc()
1815 wantLine++
1816
1817 if len(logger.entries) != 1 {
1818 t.Errorf("expected a single log entry to be generated, got %d", len(logger.entries))
1819 }
1820 checkLogrEntryCorrectCaller(t, wantFile, wantLine, logger.entries[0])
1821
1822 logger.reset()
1823
1824 logFunc = func() {
1825 logger.Error(errors.New("error"), "some error log")
1826 }
1827
1828 _, wantFile, wantLine, _ = runtime.Caller(0)
1829 logFunc()
1830 wantLine++
1831
1832 if len(logger.entries) != 1 {
1833 t.Errorf("expected a single log entry to be generated, got %d", len(logger.entries))
1834 }
1835 checkLogrEntryCorrectCaller(t, wantFile, wantLine, logger.entries[0])
1836 }
1837
1838 type testLogr struct {
1839 entries []testLogrEntry
1840 mutex sync.Mutex
1841 }
1842
1843 type testLogrEntry struct {
1844 severity severity.Severity
1845 msg string
1846 keysAndValues []interface{}
1847 err error
1848 }
1849
1850 func (l *testLogr) reset() {
1851 l.mutex.Lock()
1852 defer l.mutex.Unlock()
1853 l.entries = []testLogrEntry{}
1854 }
1855
1856 func (l *testLogr) Info(_ int, msg string, keysAndValues ...interface{}) {
1857 l.mutex.Lock()
1858 defer l.mutex.Unlock()
1859 l.entries = append(l.entries, testLogrEntry{
1860 severity: severity.InfoLog,
1861 msg: msg,
1862 keysAndValues: keysAndValues,
1863 })
1864 }
1865
1866 func (l *testLogr) Error(err error, msg string, keysAndValues ...interface{}) {
1867 l.mutex.Lock()
1868 defer l.mutex.Unlock()
1869 l.entries = append(l.entries, testLogrEntry{
1870 severity: severity.ErrorLog,
1871 msg: msg,
1872 keysAndValues: keysAndValues,
1873 err: err,
1874 })
1875 }
1876
1877 func (l *testLogr) Init(logr.RuntimeInfo) {}
1878 func (l *testLogr) Enabled(int) bool { return true }
1879 func (l *testLogr) V(int) logr.Logger { panic("not implemented") }
1880 func (l *testLogr) WithName(string) logr.LogSink { panic("not implemented") }
1881 func (l *testLogr) WithValues(...interface{}) logr.LogSink { panic("not implemented") }
1882 func (l *testLogr) WithCallDepth(int) logr.LogSink { return l }
1883
1884 var _ logr.LogSink = &testLogr{}
1885 var _ logr.CallDepthLogSink = &testLogr{}
1886
1887 type callDepthTestLogr struct {
1888 testLogr
1889 callDepth int
1890 }
1891
1892 func (l *callDepthTestLogr) resetCallDepth() {
1893 l.mutex.Lock()
1894 defer l.mutex.Unlock()
1895 l.callDepth = 0
1896 }
1897
1898 func (l *callDepthTestLogr) WithCallDepth(depth int) logr.LogSink {
1899 l.mutex.Lock()
1900 defer l.mutex.Unlock()
1901
1902
1903
1904 l.callDepth = depth + 1
1905 return l
1906 }
1907
1908 func (l *callDepthTestLogr) Info(_ int, msg string, keysAndValues ...interface{}) {
1909 l.mutex.Lock()
1910 defer l.mutex.Unlock()
1911
1912
1913 _, file, line, _ := runtime.Caller(l.callDepth + 2)
1914 l.entries = append(l.entries, testLogrEntry{
1915 severity: severity.InfoLog,
1916 msg: msg,
1917 keysAndValues: append([]interface{}{file, line}, keysAndValues...),
1918 })
1919 }
1920
1921 func (l *callDepthTestLogr) Error(err error, msg string, keysAndValues ...interface{}) {
1922 l.mutex.Lock()
1923 defer l.mutex.Unlock()
1924
1925
1926 _, file, line, _ := runtime.Caller(l.callDepth + 2)
1927 l.entries = append(l.entries, testLogrEntry{
1928 severity: severity.ErrorLog,
1929 msg: msg,
1930 keysAndValues: append([]interface{}{file, line}, keysAndValues...),
1931 err: err,
1932 })
1933 }
1934
1935 var _ logr.LogSink = &callDepthTestLogr{}
1936 var _ logr.CallDepthLogSink = &callDepthTestLogr{}
1937
1938 func checkLogrEntryCorrectCaller(t *testing.T, wantFile string, wantLine int, entry testLogrEntry) {
1939 t.Helper()
1940
1941 want := fmt.Sprintf("%s:%d", wantFile, wantLine)
1942
1943 got := fmt.Sprintf("%s:%d", entry.keysAndValues[0], entry.keysAndValues[1])
1944
1945 if want != got {
1946 t.Errorf("expected file and line %q but got %q", want, got)
1947 }
1948 }
1949
1950
1951 var existedFlag = map[string]struct{}{
1952 "log_dir": {},
1953 "add_dir_header": {},
1954 "alsologtostderr": {},
1955 "log_backtrace_at": {},
1956 "log_file": {},
1957 "log_file_max_size": {},
1958 "logtostderr": {},
1959 "one_output": {},
1960 "skip_headers": {},
1961 "skip_log_headers": {},
1962 "stderrthreshold": {},
1963 "v": {},
1964 "vmodule": {},
1965 }
1966
1967
1968 const KlogPrefix string = "klog"
1969
1970
1971 func TestKlogFlagPrefix(t *testing.T) {
1972 fs := &flag.FlagSet{}
1973 InitFlags(fs)
1974 fs.VisitAll(func(f *flag.Flag) {
1975 if _, found := existedFlag[f.Name]; !found {
1976 if !strings.HasPrefix(f.Name, KlogPrefix) {
1977 t.Errorf("flag %s not have klog prefix: %s", f.Name, KlogPrefix)
1978 }
1979 }
1980 })
1981 }
1982
1983 func TestKObjs(t *testing.T) {
1984 tests := []struct {
1985 name string
1986 obj interface{}
1987 want []ObjectRef
1988 }{
1989 {
1990 name: "test for KObjs function with KMetadata slice",
1991 obj: []test.KMetadataMock{
1992 {
1993 Name: "kube-dns",
1994 NS: "kube-system",
1995 },
1996 {
1997 Name: "mi-conf",
1998 },
1999 {},
2000 },
2001 want: []ObjectRef{
2002 {
2003 Name: "kube-dns",
2004 Namespace: "kube-system",
2005 },
2006 {
2007 Name: "mi-conf",
2008 },
2009 {},
2010 },
2011 },
2012 {
2013 name: "test for KObjs function with KMetadata pointer slice",
2014 obj: []*test.KMetadataMock{
2015 {
2016 Name: "kube-dns",
2017 NS: "kube-system",
2018 },
2019 {
2020 Name: "mi-conf",
2021 },
2022 nil,
2023 },
2024 want: []ObjectRef{
2025 {
2026 Name: "kube-dns",
2027 Namespace: "kube-system",
2028 },
2029 {
2030 Name: "mi-conf",
2031 },
2032 {},
2033 },
2034 },
2035 {
2036 name: "test for KObjs function with slice does not implement KMetadata",
2037 obj: []int{1, 2, 3, 4, 6},
2038 want: nil,
2039 },
2040 {
2041 name: "test for KObjs function with interface",
2042 obj: "test case",
2043 want: nil,
2044 },
2045 {
2046 name: "test for KObjs function with nil",
2047 obj: nil,
2048 want: nil,
2049 },
2050 }
2051
2052 for _, tt := range tests {
2053 t.Run(tt.name, func(t *testing.T) {
2054 if !reflect.DeepEqual(KObjs(tt.obj), tt.want) {
2055 t.Errorf("\nwant:\t %v\n got:\t %v", tt.want, KObjs(tt.obj))
2056 }
2057 })
2058 }
2059 }
2060
2061
2062 type structWithLock struct {
2063 m sync.Mutex
2064 n int64
2065 }
2066
2067 func BenchmarkWithoutDeferUnLock(b *testing.B) {
2068 s := structWithLock{}
2069 for i := 0; i < b.N; i++ {
2070 s.addWithoutDefer()
2071 }
2072 }
2073
2074 func BenchmarkWithDeferUnLock(b *testing.B) {
2075 s := structWithLock{}
2076 for i := 0; i < b.N; i++ {
2077 s.addWithDefer()
2078 }
2079 }
2080
2081 func (s *structWithLock) addWithoutDefer() {
2082 s.m.Lock()
2083 s.n++
2084 s.m.Unlock()
2085 }
2086
2087 func (s *structWithLock) addWithDefer() {
2088 s.m.Lock()
2089 defer s.m.Unlock()
2090 s.n++
2091 }
2092
2093 func TestFlushDaemon(t *testing.T) {
2094 for sev := severity.InfoLog; sev < severity.FatalLog; sev++ {
2095 flushed := make(chan struct{}, 1)
2096 spyFunc := func() {
2097 flushed <- struct{}{}
2098 }
2099 testClock := testingclock.NewFakeClock(time.Now())
2100 testLog := loggingT{
2101 settings: settings{
2102 flushInterval: time.Second,
2103 },
2104 flushD: newFlushDaemon(spyFunc, testClock),
2105 }
2106
2107
2108 testLog.print(sev, nil, nil, "x")
2109
2110 if !testLog.flushD.isRunning() {
2111 t.Error("expected flushD to be running")
2112 }
2113
2114 timer := time.NewTimer(10 * time.Second)
2115 defer timer.Stop()
2116 testClock.Step(time.Second)
2117 select {
2118 case <-flushed:
2119 case <-timer.C:
2120 t.Fatal("flushDaemon didn't call flush function on tick")
2121 }
2122
2123 timer = time.NewTimer(10 * time.Second)
2124 defer timer.Stop()
2125 testClock.Step(time.Second)
2126 select {
2127 case <-flushed:
2128 case <-timer.C:
2129 t.Fatal("flushDaemon didn't call flush function on second tick")
2130 }
2131
2132 timer = time.NewTimer(10 * time.Second)
2133 defer timer.Stop()
2134 testLog.flushD.stop()
2135 select {
2136 case <-flushed:
2137 case <-timer.C:
2138 t.Fatal("flushDaemon didn't call flush function one last time on stop")
2139 }
2140 }
2141 }
2142
2143 func TestStopFlushDaemon(t *testing.T) {
2144 logging.flushD.stop()
2145 logging.flushD = newFlushDaemon(func() {}, nil)
2146 logging.flushD.run(time.Second)
2147 if !logging.flushD.isRunning() {
2148 t.Error("expected flushD to be running")
2149 }
2150 StopFlushDaemon()
2151 if logging.flushD.isRunning() {
2152 t.Error("expected flushD to be stopped")
2153 }
2154 }
2155
2156 func TestCaptureState(t *testing.T) {
2157 var fs flag.FlagSet
2158 InitFlags(&fs)
2159
2160
2161 oldState := map[string]string{}
2162 fs.VisitAll(func(f *flag.Flag) {
2163 oldState[f.Name] = f.Value.String()
2164 })
2165 originalLogger := Background()
2166 file := logging.file
2167
2168
2169
2170 state := CaptureState()
2171 defer state.Restore()
2172
2173
2174 for name, value := range map[string]string{
2175
2176 "v": "10",
2177 "vmodule": "abc=2",
2178 "log_dir": "/tmp",
2179 "log_file_max_size": "10",
2180 "logtostderr": "false",
2181 "alsologtostderr": "true",
2182 "add_dir_header": "true",
2183 "skip_headers": "true",
2184 "one_output": "true",
2185 "skip_log_headers": "true",
2186 "stderrthreshold": "1",
2187 "log_backtrace_at": "foobar.go:100",
2188 } {
2189 f := fs.Lookup(name)
2190 if f == nil {
2191 t.Fatalf("could not look up %q", name)
2192 }
2193 currentValue := f.Value.String()
2194 if currentValue == value {
2195 t.Fatalf("%q is already set to non-default %q?!", name, value)
2196 }
2197 if err := f.Value.Set(value); err != nil {
2198 t.Fatalf("setting %q to %q: %v", name, value, err)
2199 }
2200 }
2201 StartFlushDaemon(time.Minute)
2202 if !logging.flushD.isRunning() {
2203 t.Error("Flush daemon should have been started.")
2204 }
2205 logger := logr.Discard()
2206 SetLoggerWithOptions(logger, ContextualLogger(true))
2207 actualLogger := Background()
2208 if logger != actualLogger {
2209 t.Errorf("Background logger should be %v, got %v", logger, actualLogger)
2210 }
2211 buffer := bytes.Buffer{}
2212 SetOutput(&buffer)
2213 if file == logging.file {
2214 t.Error("Output files should have been modified.")
2215 }
2216
2217
2218 state.Restore()
2219
2220
2221 fs.VisitAll(func(f *flag.Flag) {
2222 oldValue := oldState[f.Name]
2223 currentValue := f.Value.String()
2224 if oldValue != currentValue {
2225 t.Errorf("%q should have been restored to %q, is %q instead", f.Name, oldValue, currentValue)
2226 }
2227 })
2228 if logging.flushD.isRunning() {
2229 t.Error("Flush daemon should have been stopped.")
2230 }
2231 actualLogger = Background()
2232 if originalLogger != actualLogger {
2233 t.Errorf("Background logger should be %v, got %v", originalLogger, actualLogger)
2234 }
2235 if file != logging.file {
2236 t.Errorf("Output files should have been restored to %v, got %v", file, logging.file)
2237 }
2238 }
2239
2240 func TestSettingsDeepCopy(t *testing.T) {
2241 logger := logr.Discard()
2242
2243 settings := settings{
2244 logger: &logWriter{Logger: logger},
2245 vmodule: moduleSpec{
2246 filter: []modulePat{
2247 {pattern: "a"},
2248 {pattern: "b"},
2249 {pattern: "c"},
2250 },
2251 },
2252 }
2253 clone := settings.deepCopy()
2254 if !reflect.DeepEqual(settings, clone) {
2255 t.Fatalf("Copy not identical to original settings. Original:\n %+v\nCopy: %+v", settings, clone)
2256 }
2257 settings.vmodule.filter[1].pattern = "x"
2258 if clone.vmodule.filter[1].pattern == settings.vmodule.filter[1].pattern {
2259 t.Fatal("Copy should not have shared vmodule.filter.")
2260 }
2261 }
2262
View as plain text