1 package glog
2
3 import (
4 "bytes"
5 "context"
6 "flag"
7 "fmt"
8 "io/ioutil"
9 stdLog "log"
10 "path/filepath"
11 "runtime"
12 "strconv"
13 "strings"
14 "testing"
15 "time"
16
17 "github.com/golang/glog/internal/logsink"
18 )
19
20
21 func TestShortHostname(t *testing.T) {
22 for hostname, expect := range map[string]string{
23 "": "",
24 "host": "host",
25 "host.google.com": "host",
26 "host.corp.google.com": "host",
27 } {
28 if got := shortHostname(hostname); expect != got {
29 t.Errorf("shortHostname(%q): expected %q, got %q", hostname, expect, got)
30 }
31 }
32 }
33
34
35 type flushBuffer struct {
36 bytes.Buffer
37 }
38
39 func (f *flushBuffer) Flush() error {
40 f.Buffer.Reset()
41 return nil
42 }
43
44 func (f *flushBuffer) Sync() error {
45 return nil
46 }
47
48 func (f *flushBuffer) filenames() []string {
49 return []string{"<local name>"}
50 }
51
52
53 func (s *fileSink) swap(writers severityWriters) (old severityWriters) {
54 s.mu.Lock()
55 defer s.mu.Unlock()
56 old = s.file
57 for i, w := range writers {
58 s.file[i] = w
59 }
60 return
61 }
62
63
64 func (s *fileSink) newBuffers() severityWriters {
65 return s.swap(severityWriters{new(flushBuffer), new(flushBuffer), new(flushBuffer), new(flushBuffer)})
66 }
67
68 func (s *fileSink) resetBuffers() {
69 s.mu.Lock()
70 defer s.mu.Unlock()
71 for _, buf := range s.file {
72 if buf != nil {
73 buf.Flush()
74 }
75 }
76 }
77
78
79 func contents(s logsink.Severity) string {
80 return sinks.file.file[s].(*flushBuffer).String()
81 }
82
83
84 func contains(s logsink.Severity, str string, t *testing.T) bool {
85 return strings.Contains(contents(s), str)
86 }
87
88
89 func setFlags() {
90 toStderr = false
91 }
92
93
94 func TestInfo(t *testing.T) {
95 setFlags()
96 defer sinks.file.swap(sinks.file.newBuffers())
97 funcs := []func(args ...any){
98 Info,
99 func(args ...any) { InfoContext(context.Background(), args) },
100 }
101
102 for _, f := range funcs {
103 sinks.file.resetBuffers()
104 f("test")
105 if !contains(logsink.Info, "I", t) {
106 t.Errorf("Info has wrong character: %q", contents(logsink.Info))
107 }
108 if !contains(logsink.Info, "test", t) {
109 t.Error("Info failed")
110 }
111 }
112 }
113
114 func TestInfoDepth(t *testing.T) {
115 setFlags()
116 defer sinks.file.swap(sinks.file.newBuffers())
117
118 funcs := []func(d int, args ...any){
119 InfoDepth,
120 func(d int, args ...any) { InfoContextDepth(context.Background(), d+1, args) },
121 }
122
123 for _, infoDepth := range funcs {
124 sinks.file.resetBuffers()
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(logsink.Info), "\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
166 func init() {
167 CopyStandardLogTo("INFO")
168 }
169
170
171 func TestCopyStandardLogToPanic(t *testing.T) {
172 defer func() {
173 if s, ok := recover().(string); !ok || !strings.Contains(s, "LOG") {
174 t.Errorf(`CopyStandardLogTo("LOG") should have panicked: %v`, s)
175 }
176 }()
177 CopyStandardLogTo("LOG")
178 }
179
180
181 func TestStandardLog(t *testing.T) {
182 setFlags()
183 defer sinks.file.swap(sinks.file.newBuffers())
184 stdLog.Print("test")
185 if !contains(logsink.Info, "I", t) {
186 t.Errorf("Info has wrong character: %q", contents(logsink.Info))
187 }
188 if !contains(logsink.Info, "test", t) {
189 t.Error("Info failed")
190 }
191 }
192
193
194 func TestHeader(t *testing.T) {
195 setFlags()
196 defer func(previous func() time.Time) { timeNow = previous }(timeNow)
197 timeNow = func() time.Time {
198 return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local)
199 }
200
201 oldPID := pid
202 defer func() { pid = oldPID }()
203 pid = 1234
204
205 defer sinks.file.swap(sinks.file.newBuffers())
206
207 Info("testHeader")
208 var line int
209 format := "I0102 15:04:05.067890 %7d glog_test.go:%d] testHeader\n"
210 var gotPID int64
211 n, err := fmt.Sscanf(contents(logsink.Info), format, &gotPID, &line)
212 if n != 2 || err != nil {
213 t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(logsink.Info))
214 }
215
216 if want := int64(pid); gotPID != want {
217 t.Errorf("expected log line to be logged with process ID %d, got %d", want, gotPID)
218 }
219
220
221
222 want := fmt.Sprintf(format, gotPID, line)
223 if contents(logsink.Info) != want {
224 t.Errorf("log format error: got:\n\t%q\nwant:\n\t%q", contents(logsink.Info), want)
225 }
226
227 }
228
229
230
231
232 func TestError(t *testing.T) {
233 setFlags()
234 defer sinks.file.swap(sinks.file.newBuffers())
235
236 funcs := []func(args ...any){
237 Error,
238 func(args ...any) { ErrorContext(context.Background(), args) },
239 }
240
241 for _, error := range funcs {
242 sinks.file.resetBuffers()
243 error("test")
244 if !contains(logsink.Error, "E", t) {
245 t.Errorf("Error has wrong character: %q", contents(logsink.Error))
246 }
247 if !contains(logsink.Error, "test", t) {
248 t.Error("Error failed")
249 }
250 str := contents(logsink.Error)
251 if !contains(logsink.Warning, str, t) {
252 t.Error("Warning failed")
253 }
254 if !contains(logsink.Info, str, t) {
255 t.Error("Info failed")
256 }
257 }
258 }
259
260
261
262
263 func TestWarning(t *testing.T) {
264 setFlags()
265 defer sinks.file.swap(sinks.file.newBuffers())
266
267 funcs := []func(args ...any){
268 Warning,
269 func(args ...any) { WarningContext(context.Background(), args) },
270 }
271
272 for _, warning := range funcs {
273 sinks.file.resetBuffers()
274 warning("test")
275 if !contains(logsink.Warning, "W", t) {
276 t.Errorf("Warning has wrong character: %q", contents(logsink.Warning))
277 }
278 if !contains(logsink.Warning, "test", t) {
279 t.Error("Warning failed")
280 }
281 str := contents(logsink.Warning)
282 if !contains(logsink.Info, str, t) {
283 t.Error("Info failed")
284 }
285 }
286 }
287
288
289 func TestV(t *testing.T) {
290 setFlags()
291 defer sinks.file.swap(sinks.file.newBuffers())
292 if err := flag.Lookup("v").Value.Set("2"); err != nil {
293 t.Fatalf("Failed to set -v=2: %v", err)
294 }
295 defer flag.Lookup("v").Value.Set("0")
296
297 funcs := []func(args ...any){
298 V(2).Info,
299 func(args ...any) { V(2).InfoContext(context.Background(), args) },
300 }
301 for _, info := range funcs {
302 sinks.file.resetBuffers()
303 info("test")
304 if !contains(logsink.Info, "I", t) {
305 t.Errorf("Info has wrong character: %q", contents(logsink.Info))
306 }
307 if !contains(logsink.Info, "test", t) {
308 t.Error("Info failed")
309 }
310 }
311 }
312
313
314
315 func TestVFlagUpdates(t *testing.T) {
316 setFlags()
317 defer sinks.file.swap(sinks.file.newBuffers())
318
319
320 if err := flag.Lookup("vmodule").Value.Set("non_existent_module=3"); err != nil {
321 t.Fatalf("Failed to set -vmodule=log_test=3: %v", err)
322 }
323 defer flag.Lookup("vmodule").Value.Set("")
324 if err := flag.Lookup("v").Value.Set("3"); err != nil {
325 t.Fatalf("Failed to set -v=3: %v", err)
326 }
327 defer flag.Lookup("v").Value.Set("0")
328
329 if !V(2) {
330 t.Error("V(2) not enabled for 2")
331 }
332 if !V(3) {
333 t.Error("V(3) not enabled for 3")
334 }
335
336
337 if err := flag.Lookup("v").Value.Set("2"); err != nil {
338 t.Fatalf("Failed to set -v=2: %v", err)
339 }
340 if !V(2) {
341 t.Error("V(2) not enabled for 2")
342 }
343 if V(3) {
344 t.Error("V(3) enabled for 3")
345 }
346 }
347
348
349 func TestLevel(t *testing.T) {
350 setFlags()
351 defer sinks.file.swap(sinks.file.newBuffers())
352 if err := flag.Lookup("v").Value.Set("3"); err != nil {
353 t.Fatalf("Failed to set -v=3: %v", err)
354 }
355 defer flag.Lookup("v").Value.Set("0")
356
357 var l Level
358 if got, want := l.String(), "0"; got != want {
359 t.Errorf("l.String() = %q, want %q", got, want)
360 }
361 if err := l.Set("2"); err != nil {
362 t.Fatalf("l.Set(2) failed: %v", err)
363 }
364 if got, want := l.String(), "2"; got != want {
365 t.Errorf("l.String() = %q, want %q", got, want)
366 }
367
368 if got, want := flag.Lookup("v").Value.String(), "3"; got != want {
369 t.Errorf("-v=%v, want %v", got, want)
370 }
371 }
372
373
374 func TestVmoduleOn(t *testing.T) {
375 setFlags()
376 defer sinks.file.swap(sinks.file.newBuffers())
377 if err := flag.Lookup("vmodule").Value.Set("glog_test=2"); err != nil {
378 t.Fatalf("Failed to set -vmodule=log_test=2: %v", err)
379 }
380 defer flag.Lookup("vmodule").Value.Set("")
381
382 if !V(1) {
383 t.Error("V not enabled for 1")
384 }
385 if !V(2) {
386 t.Error("V not enabled for 2")
387 }
388 if V(3) {
389 t.Error("V enabled for 3")
390 }
391 V(2).Info("test")
392 if !contains(logsink.Info, "I", t) {
393 t.Errorf("Info has wrong character: %q", contents(logsink.Info))
394 }
395 if !contains(logsink.Info, "test", t) {
396 t.Error("Info failed")
397 }
398 }
399
400
401 func TestVDepth(t *testing.T) {
402 setFlags()
403 defer sinks.file.swap(sinks.file.newBuffers())
404 if err := flag.Lookup("vmodule").Value.Set("glog_test=3"); err != nil {
405 t.Fatalf("Failed to set -vmodule=glog_test=3: %v", err)
406 }
407 defer flag.Lookup("vmodule").Value.Set("")
408
409 if !V(3) {
410 t.Error("V not enabled for 3")
411 }
412 if !VDepth(0, 2) {
413 t.Error("VDepth(0) not enabled for 2")
414 }
415 if !VDepth(0, 3) {
416 t.Error("VDepth(0) not enabled for 3")
417 }
418 if VDepth(0, 4) {
419 t.Error("VDepth(0) enabled for 4")
420 }
421
422
423
424
425
426 if !runInAnotherModule(func() bool { return bool(VDepth(0, 3)) }) {
427 t.Error("VDepth(0) in closure not enabled for 3")
428 }
429 if runInAnotherModule(func() bool { return bool(VDepth(1, 3)) }) {
430 t.Error("VDepth(1) in closure enabled for 3")
431 }
432 if !runInAnotherModule(func() bool { return bool(VDepth(2, 3)) }) {
433 t.Error("VDepth(2) in closure not enabled for 3")
434 }
435 }
436
437
438 func TestVmoduleOff(t *testing.T) {
439 setFlags()
440 defer sinks.file.swap(sinks.file.newBuffers())
441 if err := flag.Lookup("vmodule").Value.Set("notthisfile=2"); err != nil {
442 t.Fatalf("Failed to set -vmodule=notthisfile=2: %v", err)
443 }
444 defer flag.Lookup("vmodule").Value.Set("")
445
446 for i := 1; i <= 3; i++ {
447 if V(Level(i)) {
448 t.Errorf("V enabled for %d", i)
449 }
450 }
451 V(2).Info("test")
452 if contents(logsink.Info) != "" {
453 t.Error("V logged incorrectly")
454 }
455 }
456
457
458 var vGlobs = map[string]bool{
459
460 "glog_test=1": false,
461 "glog_test=2": true,
462 "glog_test=3": true,
463
464 "*=2": true,
465 "?l*=2": true,
466 "????_*=2": true,
467 "??[mno]?_*t=2": true,
468
469 "*x=2": false,
470 "m*=2": false,
471 "??_*=2": false,
472 "?[abc]?_*t=2": false,
473 }
474
475
476 func testVmoduleGlob(pat string, match bool, t *testing.T) {
477 t.Helper()
478 setFlags()
479 defer sinks.file.swap(sinks.file.newBuffers())
480 if err := flag.Lookup("vmodule").Value.Set(pat); err != nil {
481 t.Errorf("Failed to set -vmodule=%s: %v", pat, err)
482 }
483 defer flag.Lookup("vmodule").Value.Set("")
484
485 if V(2) != Verbose(match) {
486 t.Errorf("incorrect match for %q: got %t expected %t", pat, V(2), match)
487 }
488 }
489
490
491 func TestVmoduleGlob(t *testing.T) {
492 for glob, match := range vGlobs {
493 testVmoduleGlob(glob, match, t)
494 }
495 }
496
497
498 func TestVmoduleFullGlob(t *testing.T) {
499 _, file, _, _ := runtime.Caller(0)
500 for glob, match := range vGlobs {
501 testVmoduleGlob(filepath.Join(filepath.Dir(file), glob), match, t)
502 }
503 }
504
505
506 func TestVmoduleFullGlobMultipleDirectories(t *testing.T) {
507
508
509 _, file, _, _ := runtime.Caller(0)
510 dir := filepath.Dir(filepath.Dir(file))
511 testVmoduleGlob(filepath.Join(dir, "*/glog_test=2"), true, t)
512 testVmoduleGlob(filepath.Join(dir, "*/glog_????=2"), true, t)
513 }
514
515 func logAtVariousLevels() {
516 V(3).Infof("level 3 message")
517 V(2).Infof("level 2 message")
518 V(1).Infof("level 1 message")
519 Infof("default level message")
520 }
521
522 func TestRollover(t *testing.T) {
523 setFlags()
524
525 Info("x")
526 info, ok := sinks.file.file[logsink.Info].(*syncBuffer)
527 if !ok {
528 t.Fatal("info wasn't created")
529 }
530
531
532
533
534
535
536
537 time.Sleep(1 * time.Second)
538
539
540 info.Flush()
541 fi, err := info.file.Stat()
542 if err != nil {
543 t.Fatalf("Unable to stat log file %s: %v", info.file.Name(), err)
544 }
545
546
547 longMessage := strings.Repeat("x", 1024)
548 defer func(previous uint64) { MaxSize = previous }(MaxSize)
549 MaxSize = uint64(fi.Size()) + uint64(2*len(longMessage)) - 1
550
551 fname0 := info.file.Name()
552
553
554 Info(longMessage)
555 Info(longMessage)
556 info.Flush()
557
558 fname1 := info.file.Name()
559 if fname0 == fname1 {
560 t.Errorf("info.f.Name did not change: %v", fname0)
561 }
562 if info.nbytes >= MaxSize {
563 t.Errorf("file size was not reset: %d", info.nbytes)
564 }
565
566
567 f0, err := ioutil.ReadFile(fname0)
568 if err != nil {
569 t.Fatalf("Unable to read file %s: %v", fname0, err)
570 }
571 if !bytes.HasSuffix(f0, []byte(footer)) {
572 t.Errorf("%v: Missing footer %q", fname0, footer)
573 }
574 found := false
575 for _, l := range bytes.Split(f0, []byte("\n")) {
576 var file string
577 _, err = fmt.Sscanf(string(l), "Next log: %s\n", &file)
578 if err != nil {
579 continue
580 }
581 if file != fname1 {
582 t.Errorf("%v: Wanted next filename %s, got %s", fname0, fname1, file)
583 }
584 found = true
585 }
586 if !found {
587 t.Errorf("%v: Next log footer not found", fname0)
588 }
589
590
591 f1, err := ioutil.ReadFile(fname1)
592 if err != nil {
593 t.Fatalf("Unable to read file %s: %v", fname1, err)
594 }
595 found = false
596 for _, l := range bytes.Split(f1, []byte("\n")) {
597 var file string
598 _, err = fmt.Sscanf(string(l), "Previous log: %s\n", &file)
599 if err != nil {
600 continue
601 }
602 if file != fname0 {
603 t.Errorf("%v: Wanted previous filename %s, got %s", fname1, fname0, file)
604 }
605 found = true
606 }
607 if !found {
608 t.Errorf("%v: Previous log header not found", fname1)
609 }
610
611
612 n, err := Names("INFO")
613 if len(n) != 2 && err != nil && n[0] != fname0 && n[1] != fname1 {
614 t.Errorf("Names(INFO) wanted [%s, %s]/nil, got %v/%v", fname0, fname1, n, err)
615 }
616
617 if t.Failed() {
618 t.Logf("%v:\n%q", fname0, f0)
619 t.Logf("%v:\n%q", fname1, f1)
620 }
621 }
622
623 func TestLogBacktraceAt(t *testing.T) {
624 setFlags()
625 defer sinks.file.swap(sinks.file.newBuffers())
626
627
628 var infoLine string
629 setTraceLocation := func(file string, line int, ok bool, delta int) {
630 if !ok {
631 t.Fatal("could not get file:line")
632 }
633 _, file = filepath.Split(file)
634 infoLine = fmt.Sprintf("%s:%d", file, line+delta)
635 err := logBacktraceAt.Set(infoLine)
636 if err != nil {
637 t.Fatal("error setting log_backtrace_at: ", err)
638 }
639 }
640 {
641
642 _, file, line, ok := runtime.Caller(0)
643 setTraceLocation(file, line, ok, +2)
644 Info("we want a stack trace here")
645 }
646 numAppearances := strings.Count(contents(logsink.Info), infoLine)
647 if numAppearances < 2 {
648
649
650
651
652
653
654
655 t.Fatal("got no trace back; log is ", contents(logsink.Info))
656 }
657 }
658
659 func TestNewStandardLoggerLogBacktraceAt(t *testing.T) {
660 setFlags()
661 defer sinks.file.swap(sinks.file.newBuffers())
662 s := NewStandardLogger("INFO")
663
664
665 var infoLine string
666 setTraceLocation := func(file string, line int, ok bool, delta int) {
667 if !ok {
668 t.Fatal("could not get file:line")
669 }
670 _, file = filepath.Split(file)
671 infoLine = fmt.Sprintf("%s:%d", file, line+delta)
672 err := logBacktraceAt.Set(infoLine)
673 if err != nil {
674 t.Fatal("error setting log_backtrace_at: ", err)
675 }
676 }
677 {
678
679 _, file, line, ok := runtime.Caller(0)
680 setTraceLocation(file, line, ok, +2)
681 s.Printf("we want a stack trace here")
682 }
683 infoContents := contents(logsink.Info)
684 if strings.Contains(infoContents, infoLine+"] [") {
685 t.Fatal("got extra bracketing around log line contents; log is ", infoContents)
686 }
687 numAppearances := strings.Count(infoContents, infoLine)
688 if numAppearances < 2 {
689
690
691
692
693
694
695
696 t.Fatal("got no trace back; log is ", infoContents)
697 }
698 }
699
700
701 func TestLogNames(t *testing.T) {
702 setFlags()
703 defer sinks.file.swap(sinks.file.newBuffers())
704 n, e := Names("FOO")
705 if e == nil {
706 t.Errorf("Names(FOO) was %v/nil, should be []/error", n)
707 }
708
709
710 h := sinks.file.file[logsink.Info]
711 sinks.file.file[logsink.Info] = nil
712 n, e = Names("INFO")
713 if e != ErrNoLog {
714 t.Errorf("Names(INFO) was %v/%v, should be [], ErrNoLog", n, e)
715 }
716 sinks.file.file[logsink.Info] = h
717
718
719 Info("test")
720 n, e = Names("INFO")
721 if len(n) != 1 && n[0] != "<local name>" {
722 t.Errorf("Names(INFO) got %s, want <local name>", n)
723 }
724 }
725
726 func TestLogLength(t *testing.T) {
727 setFlags()
728 defer sinks.file.swap(sinks.file.newBuffers())
729 Info(strings.Repeat("X", logsink.MaxLogMessageLen*2))
730 if c := contents(logsink.Info); len(c) != logsink.MaxLogMessageLen {
731 t.Errorf("Info was not truncated: got length %d, want %d, contents %q",
732 len(c), logsink.MaxLogMessageLen, c)
733 }
734 }
735
View as plain text