| // Copyright 2022 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package zap_benchmarks |
| |
| import ( |
| "io" |
| "sync" |
| "time" |
| |
| "go.uber.org/zap" |
| "go.uber.org/zap/buffer" |
| "go.uber.org/zap/zapcore" |
| ) |
| |
| // from zap/internal/ztest |
| |
| // A syncer is a spy for the Sync portion of zapcore.WriteSyncer. |
| type syncer struct { |
| err error |
| called bool |
| } |
| |
| // SetError sets the error that the Sync method will return. |
| func (s *syncer) SetError(err error) { |
| s.err = err |
| } |
| |
| // Sync records that it was called, then returns the user-supplied error (if |
| // any). |
| func (s *syncer) Sync() error { |
| s.called = true |
| return s.err |
| } |
| |
| // Called reports whether the Sync method was called. |
| func (s *syncer) Called() bool { |
| return s.called |
| } |
| |
| // A discarder sends all writes to ioutil.Discard. |
| type discarder struct{ syncer } |
| |
| // Write implements io.Writer. |
| func (d *discarder) Write(b []byte) (int, error) { |
| return io.Discard.Write(b) |
| } |
| |
| // fastTextEncoder mimics slog/benchmarks.fastTextHandler. |
| type fastTextEncoder struct { |
| buf *buffer.Buffer |
| zapcore.ObjectEncoder |
| } |
| |
| var bufferPool = buffer.NewPool() |
| |
| var tePool = sync.Pool{ |
| New: func() any { return &fastTextEncoder{} }, |
| } |
| |
| func newFastTextEncoder() *fastTextEncoder { |
| e := tePool.Get().(*fastTextEncoder) |
| e.buf = bufferPool.Get() |
| return e |
| } |
| |
| func (c *fastTextEncoder) Clone() zapcore.Encoder { |
| clone := newFastTextEncoder() |
| if c.buf != nil { |
| panic("buf should be nil") |
| clone.buf.Write(c.buf.Bytes()) |
| } |
| return clone |
| } |
| |
| func (c *fastTextEncoder) EncodeEntry(e zapcore.Entry, fs []zap.Field) (*buffer.Buffer, error) { |
| c2 := newFastTextEncoder() |
| if !e.Time.IsZero() { |
| c2.buf.AppendString("time=") |
| c2.appendTime(e.Time) |
| c2.buf.AppendByte(' ') |
| } |
| c2.buf.AppendString("level=") |
| c2.buf.AppendInt(int64(e.Level)) |
| c2.buf.AppendByte(' ') |
| c2.buf.AppendString("msg=") |
| c2.buf.AppendString(e.Message) |
| for _, f := range fs { |
| c2.buf.AppendByte(' ') |
| f.AddTo(c2) |
| } |
| c2.buf.AppendString("\n") |
| buf := c2.buf |
| tePool.Put(c2) |
| return buf, nil |
| } |
| |
| func (c *fastTextEncoder) AddString(key, value string) { |
| c.buf.AppendString(key) |
| c.buf.AppendByte('=') |
| c.buf.AppendString(value) |
| } |
| |
| func (c *fastTextEncoder) AddTime(key string, value time.Time) { |
| c.buf.AppendString(key) |
| c.buf.AppendByte('=') |
| c.appendTime(value) |
| } |
| |
| func (c *fastTextEncoder) AddDuration(key string, value time.Duration) { |
| c.buf.AppendString(key) |
| c.buf.AppendByte('=') |
| c.buf.AppendInt(value.Nanoseconds()) |
| } |
| |
| func (c *fastTextEncoder) AddInt64(key string, value int64) { |
| c.buf.AppendString(key) |
| c.buf.AppendByte('=') |
| c.buf.AppendInt(value) |
| } |
| func (c *fastTextEncoder) appendTime(t time.Time) { |
| c.buf.AppendInt(t.Unix()) |
| } |
| |
| // asyncCore mimics slog/benchmarks.asyncHandler. |
| type asyncCore struct { |
| ringBuffer [100]entryAndFields |
| next int |
| } |
| |
| type entryAndFields struct { |
| e zapcore.Entry |
| f []zap.Field |
| } |
| |
| func (*asyncCore) Enabled(zapcore.Level) bool { return true } |
| func (c *asyncCore) With([]zap.Field) zapcore.Core { return c } |
| func (*asyncCore) Sync() error { return nil } |
| |
| // Also needed to make this non-trivial. |
| func (c *asyncCore) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry { |
| return ce.AddCore(ent, c) |
| } |
| |
| func (c *asyncCore) Write(e zapcore.Entry, f []zap.Field) error { |
| c.ringBuffer[c.next] = entryAndFields{e, f} |
| c.next = (c.next + 1) % len(c.ringBuffer) |
| return nil |
| } |