golang.org/x/exp@v0.0.0-20240506185415-9bf2ced13842/slog/benchmarks/handlers.go (about) 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. 4 5 package benchmarks 6 7 // Handlers for benchmarking. 8 9 import ( 10 "context" 11 "fmt" 12 "io" 13 "strconv" 14 "time" 15 16 "golang.org/x/exp/slog" 17 "golang.org/x/exp/slog/internal/buffer" 18 ) 19 20 // A fastTextHandler writes a Record to an io.Writer in a format similar to 21 // slog.TextHandler, but without quoting or locking. It has a few other 22 // performance-motivated shortcuts, like writing times as seconds since the 23 // epoch instead of strings. 24 // 25 // It is intended to represent a high-performance Handler that synchronously 26 // writes text (as opposed to binary). 27 type fastTextHandler struct { 28 w io.Writer 29 } 30 31 func newFastTextHandler(w io.Writer) slog.Handler { 32 return &fastTextHandler{w: w} 33 } 34 35 func (h *fastTextHandler) Enabled(context.Context, slog.Level) bool { return true } 36 37 func (h *fastTextHandler) Handle(_ context.Context, r slog.Record) error { 38 buf := buffer.New() 39 defer buf.Free() 40 41 if !r.Time.IsZero() { 42 buf.WriteString("time=") 43 h.appendTime(buf, r.Time) 44 buf.WriteByte(' ') 45 } 46 buf.WriteString("level=") 47 *buf = strconv.AppendInt(*buf, int64(r.Level), 10) 48 buf.WriteByte(' ') 49 buf.WriteString("msg=") 50 buf.WriteString(r.Message) 51 r.Attrs(func(a slog.Attr) bool { 52 buf.WriteByte(' ') 53 buf.WriteString(a.Key) 54 buf.WriteByte('=') 55 h.appendValue(buf, a.Value) 56 return true 57 }) 58 buf.WriteByte('\n') 59 _, err := h.w.Write(*buf) 60 return err 61 } 62 63 func (h *fastTextHandler) appendValue(buf *buffer.Buffer, v slog.Value) { 64 switch v.Kind() { 65 case slog.KindString: 66 buf.WriteString(v.String()) 67 case slog.KindInt64: 68 *buf = strconv.AppendInt(*buf, v.Int64(), 10) 69 case slog.KindUint64: 70 *buf = strconv.AppendUint(*buf, v.Uint64(), 10) 71 case slog.KindFloat64: 72 *buf = strconv.AppendFloat(*buf, v.Float64(), 'g', -1, 64) 73 case slog.KindBool: 74 *buf = strconv.AppendBool(*buf, v.Bool()) 75 case slog.KindDuration: 76 *buf = strconv.AppendInt(*buf, v.Duration().Nanoseconds(), 10) 77 case slog.KindTime: 78 h.appendTime(buf, v.Time()) 79 case slog.KindAny: 80 a := v.Any() 81 switch a := a.(type) { 82 case error: 83 buf.WriteString(a.Error()) 84 default: 85 buf.WriteString(fmt.Sprint(a)) 86 } 87 default: 88 panic(fmt.Sprintf("bad kind: %s", v.Kind())) 89 } 90 } 91 92 func (h *fastTextHandler) appendTime(buf *buffer.Buffer, t time.Time) { 93 *buf = strconv.AppendInt(*buf, t.Unix(), 10) 94 } 95 96 func (h *fastTextHandler) WithAttrs([]slog.Attr) slog.Handler { 97 panic("fastTextHandler: With unimplemented") 98 } 99 100 func (*fastTextHandler) WithGroup(string) slog.Handler { 101 panic("fastTextHandler: WithGroup unimplemented") 102 } 103 104 // An asyncHandler simulates a Handler that passes Records to a 105 // background goroutine for processing. 106 // Because sending to a channel can be expensive due to locking, 107 // we simulate a lock-free queue by adding the Record to a ring buffer. 108 // Omitting the locking makes this little more than a copy of the Record, 109 // but that is a worthwhile thing to measure because Records are on the large 110 // side. 111 type asyncHandler struct { 112 ringBuffer [100]slog.Record 113 next int 114 } 115 116 func newAsyncHandler() *asyncHandler { 117 return &asyncHandler{} 118 } 119 120 func (*asyncHandler) Enabled(context.Context, slog.Level) bool { return true } 121 122 func (h *asyncHandler) Handle(_ context.Context, r slog.Record) error { 123 h.ringBuffer[h.next] = r.Clone() 124 h.next = (h.next + 1) % len(h.ringBuffer) 125 return nil 126 } 127 128 func (*asyncHandler) WithAttrs([]slog.Attr) slog.Handler { 129 panic("asyncHandler: With unimplemented") 130 } 131 132 func (*asyncHandler) WithGroup(string) slog.Handler { 133 panic("asyncHandler: WithGroup unimplemented") 134 } 135 136 type disabledHandler struct{} 137 138 func (disabledHandler) Enabled(context.Context, slog.Level) bool { return false } 139 func (disabledHandler) Handle(context.Context, slog.Record) error { panic("should not be called") } 140 141 func (disabledHandler) WithAttrs([]slog.Attr) slog.Handler { 142 panic("disabledHandler: With unimplemented") 143 } 144 145 func (disabledHandler) WithGroup(string) slog.Handler { 146 panic("disabledHandler: WithGroup unimplemented") 147 }