1 // Copyright 2022 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.
8 "log/slog/internal/buffer"
16 func TestRecordAttrs(t *testing.T) {
17 as := []Attr{Int("k1", 1), String("k2", "foo"), Int("k3", 3),
18 Int64("k4", -1), Float64("f", 3.1), Uint64("u", 999)}
19 r := newRecordWithAttrs(as)
20 if g, w := r.NumAttrs(), len(as); g != w {
21 t.Errorf("NumAttrs: got %d, want %d", g, w)
23 if got := attrsSlice(r); !attrsEqual(got, as) {
24 t.Errorf("got %v, want %v", got, as)
29 r.Attrs(func(a Attr) bool {
34 if !attrsEqual(got, want) {
35 t.Errorf("got %v, want %v", got, want)
39 func TestRecordSourceLine(t *testing.T) {
40 // Zero call depth => empty file/line
41 for _, test := range []struct {
48 {1, "record_test.go", true}, // 1: caller of NewRecord
49 {2, "testing.go", true},
53 pc = callerPC(test.depth + 1)
55 r := NewRecord(time.Time{}, 0, "", pc)
56 gotFile, gotLine := sourceLine(r)
57 if i := strings.LastIndexByte(gotFile, '/'); i >= 0 {
58 gotFile = gotFile[i+1:]
60 if gotFile != test.wantFile || (gotLine > 0) != test.wantLinePositive {
61 t.Errorf("depth %d: got (%q, %d), want (%q, %t)",
62 test.depth, gotFile, gotLine, test.wantFile, test.wantLinePositive)
67 func TestAliasingAndClone(t *testing.T) {
68 intAttrs := func(from, to int) []Attr {
70 for i := from; i < to; i++ {
71 as = append(as, Int("k", i))
76 check := func(r Record, want []Attr) {
79 if !attrsEqual(got, want) {
80 t.Errorf("got %v, want %v", got, want)
84 // Create a record whose Attrs overflow the inline array,
85 // creating a slice in r.back.
86 r1 := NewRecord(time.Time{}, 0, "", 0)
87 r1.AddAttrs(intAttrs(0, nAttrsInline+1)...)
88 // Ensure that r1.back's capacity exceeds its length.
89 b := make([]Attr, len(r1.back), len(r1.back)+1)
92 // Make a copy that shares state.
94 // Adding to both should panic.
95 r1.AddAttrs(Int("p", 0))
96 if !panics(func() { r2.AddAttrs(Int("p", 1)) }) {
97 t.Error("expected panic")
99 r1Attrs := attrsSlice(r1)
100 // Adding to a clone is fine.
103 r2.AddAttrs(Int("p", 2))
104 check(r1, r1Attrs) // r1 is unchanged
105 check(r2, append(slices.Clip(r1Attrs), Int("p", 2)))
108 func newRecordWithAttrs(as []Attr) Record {
109 r := NewRecord(time.Now(), LevelInfo, "", 0)
114 func attrsSlice(r Record) []Attr {
115 s := make([]Attr, 0, r.NumAttrs())
116 r.Attrs(func(a Attr) bool { s = append(s, a); return true })
120 func attrsEqual(as1, as2 []Attr) bool {
121 return slices.EqualFunc(as1, as2, Attr.Equal)
124 // Currently, pc(2) takes over 400ns, which is too expensive
125 // to call it for every log message.
126 func BenchmarkPC(b *testing.B) {
127 for depth := 0; depth < 5; depth++ {
128 b.Run(strconv.Itoa(depth), func(b *testing.B) {
131 for i := 0; i < b.N; i++ {
139 func BenchmarkSourceLine(b *testing.B) {
140 r := NewRecord(time.Now(), LevelInfo, "", 5)
141 b.Run("alone", func(b *testing.B) {
142 for i := 0; i < b.N; i++ {
143 file, line := sourceLine(r)
148 b.Run("stringifying", func(b *testing.B) {
149 for i := 0; i < b.N; i++ {
150 file, line := sourceLine(r)
152 buf.WriteString(file)
154 buf.WritePosInt(line)
162 func BenchmarkRecord(b *testing.B) {
163 const nAttrs = nAttrsInline * 10
166 for i := 0; i < b.N; i++ {
167 r := NewRecord(time.Time{}, LevelInfo, "", 0)
168 for j := 0; j < nAttrs; j++ {
169 r.AddAttrs(Int("k", j))
171 r.Attrs(func(b Attr) bool { a = b; return true })