golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/quic/qlog/qlog_test.go (about) 1 // Copyright 2023 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build go1.21 6 7 package qlog 8 9 import ( 10 "bytes" 11 "encoding/json" 12 "fmt" 13 "io" 14 "log/slog" 15 "reflect" 16 "testing" 17 "time" 18 ) 19 20 // QLog tests are mostly in the quic package, where we can test event generation 21 // and serialization together. 22 23 func TestQLogHandlerEvents(t *testing.T) { 24 for _, test := range []struct { 25 name string 26 f func(*slog.Logger) 27 want []map[string]any // events, not counting the trace header 28 }{{ 29 name: "various types", 30 f: func(log *slog.Logger) { 31 log.Info("message", 32 "bool", true, 33 "duration", time.Duration(1*time.Second), 34 "float", 0.0, 35 "int", 0, 36 "string", "value", 37 "uint", uint64(0), 38 slog.Group("group", 39 "a", 0, 40 ), 41 ) 42 }, 43 want: []map[string]any{{ 44 "name": "message", 45 "data": map[string]any{ 46 "bool": true, 47 "duration": float64(1000), 48 "float": float64(0.0), 49 "int": float64(0), 50 "string": "value", 51 "uint": float64(0), 52 "group": map[string]any{ 53 "a": float64(0), 54 }, 55 }, 56 }}, 57 }, { 58 name: "WithAttrs", 59 f: func(log *slog.Logger) { 60 log = log.With( 61 "with_a", "a", 62 "with_b", "b", 63 ) 64 log.Info("m1", "field", "1") 65 log.Info("m2", "field", "2") 66 }, 67 want: []map[string]any{{ 68 "name": "m1", 69 "data": map[string]any{ 70 "with_a": "a", 71 "with_b": "b", 72 "field": "1", 73 }, 74 }, { 75 "name": "m2", 76 "data": map[string]any{ 77 "with_a": "a", 78 "with_b": "b", 79 "field": "2", 80 }, 81 }}, 82 }, { 83 name: "WithGroup", 84 f: func(log *slog.Logger) { 85 log = log.With( 86 "with_a", "a", 87 "with_b", "b", 88 ) 89 log.Info("m1", "field", "1") 90 log.Info("m2", "field", "2") 91 }, 92 want: []map[string]any{{ 93 "name": "m1", 94 "data": map[string]any{ 95 "with_a": "a", 96 "with_b": "b", 97 "field": "1", 98 }, 99 }, { 100 "name": "m2", 101 "data": map[string]any{ 102 "with_a": "a", 103 "with_b": "b", 104 "field": "2", 105 }, 106 }}, 107 }} { 108 var out bytes.Buffer 109 opts := HandlerOptions{ 110 Level: slog.LevelDebug, 111 NewTrace: func(TraceInfo) (io.WriteCloser, error) { 112 return nopCloseWriter{&out}, nil 113 }, 114 } 115 h, err := newJSONTraceHandler(opts, []slog.Attr{ 116 slog.String("group_id", "group"), 117 slog.Group("vantage_point", 118 slog.String("type", "client"), 119 ), 120 }) 121 if err != nil { 122 t.Fatal(err) 123 } 124 log := slog.New(h) 125 test.f(log) 126 got := []map[string]any{} 127 for i, e := range bytes.Split(out.Bytes(), []byte{0x1e}) { 128 // i==0: empty string before the initial record separator 129 // i==1: trace header; not part of this test 130 if i < 2 { 131 continue 132 } 133 var val map[string]any 134 if err := json.Unmarshal(e, &val); err != nil { 135 panic(fmt.Errorf("log unmarshal failure: %v\n%q", err, string(e))) 136 } 137 delete(val, "time") 138 got = append(got, val) 139 } 140 if !reflect.DeepEqual(got, test.want) { 141 t.Errorf("event mismatch\ngot: %v\nwant: %v", got, test.want) 142 } 143 } 144 145 } 146 147 type nopCloseWriter struct { 148 io.Writer 149 } 150 151 func (nopCloseWriter) Close() error { return nil }