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