summaryrefslogtreecommitdiffstats
path: root/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore
diff options
context:
space:
mode:
Diffstat (limited to 'dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore')
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/buffered_write_syncer.go219
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/buffered_write_syncer_bench_test.go51
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/buffered_write_syncer_test.go139
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/clock.go48
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/clock_test.go44
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/console_encoder.go157
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/console_encoder_bench_test.go49
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/console_encoder_test.go91
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/core.go122
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/core_test.go165
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/doc.go24
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/encoder.go451
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/encoder_test.go730
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/entry.go299
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/entry_test.go149
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/error.go132
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/error_test.go177
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/field.go233
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/field_test.go318
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/hook.go77
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/hook_test.go82
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/increase_level.go75
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/increase_level_test.go129
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/json_encoder.go562
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/json_encoder_bench_test.go101
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/json_encoder_impl_test.go659
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/json_encoder_test.go268
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/leak_test.go31
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/level.go229
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/level_strings.go46
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/level_strings_test.go38
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/level_test.go249
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/marshaler.go61
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/memory_encoder.go179
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/memory_encoder_test.go364
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/reflected_encoder.go41
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/sampler.go230
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/sampler_bench_test.go285
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/sampler_test.go328
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/tee.go96
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/tee_logger_bench_test.go62
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/tee_test.go191
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/write_syncer.go122
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/write_syncer_bench_test.go90
-rw-r--r--dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/write_syncer_test.go136
45 files changed, 8329 insertions, 0 deletions
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/buffered_write_syncer.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/buffered_write_syncer.go
new file mode 100644
index 0000000..a40e93b
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/buffered_write_syncer.go
@@ -0,0 +1,219 @@
+// Copyright (c) 2021 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "bufio"
+ "sync"
+ "time"
+
+ "go.uber.org/multierr"
+)
+
+const (
+ // _defaultBufferSize specifies the default size used by Buffer.
+ _defaultBufferSize = 256 * 1024 // 256 kB
+
+ // _defaultFlushInterval specifies the default flush interval for
+ // Buffer.
+ _defaultFlushInterval = 30 * time.Second
+)
+
+// A BufferedWriteSyncer is a WriteSyncer that buffers writes in-memory before
+// flushing them to a wrapped WriteSyncer after reaching some limit, or at some
+// fixed interval--whichever comes first.
+//
+// BufferedWriteSyncer is safe for concurrent use. You don't need to use
+// zapcore.Lock for WriteSyncers with BufferedWriteSyncer.
+//
+// To set up a BufferedWriteSyncer, construct a WriteSyncer for your log
+// destination (*os.File is a valid WriteSyncer), wrap it with
+// BufferedWriteSyncer, and defer a Stop() call for when you no longer need the
+// object.
+//
+// func main() {
+// ws := ... // your log destination
+// bws := &zapcore.BufferedWriteSyncer{WS: ws}
+// defer bws.Stop()
+//
+// // ...
+// core := zapcore.NewCore(enc, bws, lvl)
+// logger := zap.New(core)
+//
+// // ...
+// }
+//
+// By default, a BufferedWriteSyncer will buffer up to 256 kilobytes of logs,
+// waiting at most 30 seconds between flushes.
+// You can customize these parameters by setting the Size or FlushInterval
+// fields.
+// For example, the following buffers up to 512 kB of logs before flushing them
+// to Stderr, with a maximum of one minute between each flush.
+//
+// ws := &BufferedWriteSyncer{
+// WS: os.Stderr,
+// Size: 512 * 1024, // 512 kB
+// FlushInterval: time.Minute,
+// }
+// defer ws.Stop()
+type BufferedWriteSyncer struct {
+ // WS is the WriteSyncer around which BufferedWriteSyncer will buffer
+ // writes.
+ //
+ // This field is required.
+ WS WriteSyncer
+
+ // Size specifies the maximum amount of data the writer will buffered
+ // before flushing.
+ //
+ // Defaults to 256 kB if unspecified.
+ Size int
+
+ // FlushInterval specifies how often the writer should flush data if
+ // there have been no writes.
+ //
+ // Defaults to 30 seconds if unspecified.
+ FlushInterval time.Duration
+
+ // Clock, if specified, provides control of the source of time for the
+ // writer.
+ //
+ // Defaults to the system clock.
+ Clock Clock
+
+ // unexported fields for state
+ mu sync.Mutex
+ initialized bool // whether initialize() has run
+ stopped bool // whether Stop() has run
+ writer *bufio.Writer
+ ticker *time.Ticker
+ stop chan struct{} // closed when flushLoop should stop
+ done chan struct{} // closed when flushLoop has stopped
+}
+
+func (s *BufferedWriteSyncer) initialize() {
+ size := s.Size
+ if size == 0 {
+ size = _defaultBufferSize
+ }
+
+ flushInterval := s.FlushInterval
+ if flushInterval == 0 {
+ flushInterval = _defaultFlushInterval
+ }
+
+ if s.Clock == nil {
+ s.Clock = DefaultClock
+ }
+
+ s.ticker = s.Clock.NewTicker(flushInterval)
+ s.writer = bufio.NewWriterSize(s.WS, size)
+ s.stop = make(chan struct{})
+ s.done = make(chan struct{})
+ s.initialized = true
+ go s.flushLoop()
+}
+
+// Write writes log data into buffer syncer directly, multiple Write calls will be batched,
+// and log data will be flushed to disk when the buffer is full or periodically.
+func (s *BufferedWriteSyncer) Write(bs []byte) (int, error) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ if !s.initialized {
+ s.initialize()
+ }
+
+ // To avoid partial writes from being flushed, we manually flush the existing buffer if:
+ // * The current write doesn't fit into the buffer fully, and
+ // * The buffer is not empty (since bufio will not split large writes when the buffer is empty)
+ if len(bs) > s.writer.Available() && s.writer.Buffered() > 0 {
+ if err := s.writer.Flush(); err != nil {
+ return 0, err
+ }
+ }
+
+ return s.writer.Write(bs)
+}
+
+// Sync flushes buffered log data into disk directly.
+func (s *BufferedWriteSyncer) Sync() error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ var err error
+ if s.initialized {
+ err = s.writer.Flush()
+ }
+
+ return multierr.Append(err, s.WS.Sync())
+}
+
+// flushLoop flushes the buffer at the configured interval until Stop is
+// called.
+func (s *BufferedWriteSyncer) flushLoop() {
+ defer close(s.done)
+
+ for {
+ select {
+ case <-s.ticker.C:
+ // we just simply ignore error here
+ // because the underlying bufio writer stores any errors
+ // and we return any error from Sync() as part of the close
+ _ = s.Sync()
+ case <-s.stop:
+ return
+ }
+ }
+}
+
+// Stop closes the buffer, cleans up background goroutines, and flushes
+// remaining unwritten data.
+func (s *BufferedWriteSyncer) Stop() (err error) {
+ var stopped bool
+
+ // Critical section.
+ func() {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ if !s.initialized {
+ return
+ }
+
+ stopped = s.stopped
+ if stopped {
+ return
+ }
+ s.stopped = true
+
+ s.ticker.Stop()
+ close(s.stop) // tell flushLoop to stop
+ <-s.done // and wait until it has
+ }()
+
+ // Don't call Sync on consecutive Stops.
+ if !stopped {
+ err = s.Sync()
+ }
+
+ return err
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/buffered_write_syncer_bench_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/buffered_write_syncer_bench_test.go
new file mode 100644
index 0000000..1e3db59
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/buffered_write_syncer_bench_test.go
@@ -0,0 +1,51 @@
+// Copyright (c) 2021 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "os"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func BenchmarkBufferedWriteSyncer(b *testing.B) {
+ b.Run("write file with buffer", func(b *testing.B) {
+ file, err := os.CreateTemp(b.TempDir(), "test.log")
+ require.NoError(b, err)
+
+ defer func() {
+ assert.NoError(b, file.Close())
+ }()
+
+ w := &BufferedWriteSyncer{
+ WS: AddSync(file),
+ }
+ defer w.Stop()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ w.Write([]byte("foobarbazbabble"))
+ }
+ })
+ })
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/buffered_write_syncer_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/buffered_write_syncer_test.go
new file mode 100644
index 0000000..8a36ad6
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/buffered_write_syncer_test.go
@@ -0,0 +1,139 @@
+// Copyright (c) 2021 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "bytes"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "go.uber.org/zap/internal/ztest"
+)
+
+func TestBufferWriter(t *testing.T) {
+ // If we pass a plain io.Writer, make sure that we still get a WriteSyncer
+ // with a no-op Sync.
+ t.Run("sync", func(t *testing.T) {
+ buf := &bytes.Buffer{}
+ ws := &BufferedWriteSyncer{WS: AddSync(buf)}
+
+ requireWriteWorks(t, ws)
+ assert.Empty(t, buf.String(), "Unexpected log calling a no-op Write method.")
+ assert.NoError(t, ws.Sync(), "Unexpected error calling a no-op Sync method.")
+ assert.Equal(t, "foo", buf.String(), "Unexpected log string")
+ assert.NoError(t, ws.Stop())
+ })
+
+ t.Run("stop", func(t *testing.T) {
+ buf := &bytes.Buffer{}
+ ws := &BufferedWriteSyncer{WS: AddSync(buf)}
+ requireWriteWorks(t, ws)
+ assert.Empty(t, buf.String(), "Unexpected log calling a no-op Write method.")
+ assert.NoError(t, ws.Stop())
+ assert.Equal(t, "foo", buf.String(), "Unexpected log string")
+ })
+
+ t.Run("stop twice", func(t *testing.T) {
+ ws := &BufferedWriteSyncer{WS: &ztest.FailWriter{}}
+ _, err := ws.Write([]byte("foo"))
+ require.NoError(t, err, "Unexpected error writing to WriteSyncer.")
+ assert.Error(t, ws.Stop(), "Expected stop to fail.")
+ assert.NoError(t, ws.Stop(), "Expected stop to not fail.")
+ })
+
+ t.Run("wrap twice", func(t *testing.T) {
+ buf := &bytes.Buffer{}
+ bufsync := &BufferedWriteSyncer{WS: AddSync(buf)}
+ ws := &BufferedWriteSyncer{WS: bufsync}
+ requireWriteWorks(t, ws)
+ assert.Empty(t, buf.String(), "Unexpected log calling a no-op Write method.")
+ require.NoError(t, ws.Sync())
+ assert.Equal(t, "foo", buf.String())
+ assert.NoError(t, ws.Stop())
+ assert.NoError(t, bufsync.Stop())
+ assert.Equal(t, "foo", buf.String(), "Unexpected log string")
+ })
+
+ t.Run("small buffer", func(t *testing.T) {
+ buf := &bytes.Buffer{}
+ ws := &BufferedWriteSyncer{WS: AddSync(buf), Size: 5}
+
+ requireWriteWorks(t, ws)
+ assert.Equal(t, "", buf.String(), "Unexpected log calling a no-op Write method.")
+ requireWriteWorks(t, ws)
+ assert.Equal(t, "foo", buf.String(), "Unexpected log string")
+ assert.NoError(t, ws.Stop())
+ })
+
+ t.Run("with lockedWriteSyncer", func(t *testing.T) {
+ buf := &bytes.Buffer{}
+ ws := &BufferedWriteSyncer{WS: Lock(AddSync(buf)), Size: 5}
+
+ requireWriteWorks(t, ws)
+ assert.Equal(t, "", buf.String(), "Unexpected log calling a no-op Write method.")
+ requireWriteWorks(t, ws)
+ assert.Equal(t, "foo", buf.String(), "Unexpected log string")
+ assert.NoError(t, ws.Stop())
+ })
+
+ t.Run("flush error", func(t *testing.T) {
+ ws := &BufferedWriteSyncer{WS: &ztest.FailWriter{}, Size: 4}
+ n, err := ws.Write([]byte("foo"))
+ require.NoError(t, err, "Unexpected error writing to WriteSyncer.")
+ require.Equal(t, 3, n, "Wrote an unexpected number of bytes.")
+ ws.Write([]byte("foo"))
+ assert.Error(t, ws.Stop(), "Expected stop to fail.")
+ })
+
+ t.Run("flush timer", func(t *testing.T) {
+ buf := &bytes.Buffer{}
+ clock := ztest.NewMockClock()
+ ws := &BufferedWriteSyncer{
+ WS: AddSync(buf),
+ Size: 6,
+ FlushInterval: time.Microsecond,
+ Clock: clock,
+ }
+ requireWriteWorks(t, ws)
+ clock.Add(10 * time.Microsecond)
+ assert.Equal(t, "foo", buf.String(), "Unexpected log string")
+
+ // flush twice to validate loop logic
+ requireWriteWorks(t, ws)
+ clock.Add(10 * time.Microsecond)
+ assert.Equal(t, "foofoo", buf.String(), "Unexpected log string")
+ assert.NoError(t, ws.Stop())
+ })
+}
+
+func TestBufferWriterWithoutStart(t *testing.T) {
+ t.Run("stop", func(t *testing.T) {
+ ws := &BufferedWriteSyncer{WS: AddSync(new(bytes.Buffer))}
+ assert.NoError(t, ws.Stop(), "Stop must not fail")
+ })
+
+ t.Run("Sync", func(t *testing.T) {
+ ws := &BufferedWriteSyncer{WS: AddSync(new(bytes.Buffer))}
+ assert.NoError(t, ws.Sync(), "Sync must not fail")
+ })
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/clock.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/clock.go
new file mode 100644
index 0000000..422fd82
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/clock.go
@@ -0,0 +1,48 @@
+// Copyright (c) 2021 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import "time"
+
+// DefaultClock is the default clock used by Zap in operations that require
+// time. This clock uses the system clock for all operations.
+var DefaultClock = systemClock{}
+
+// Clock is a source of time for logged entries.
+type Clock interface {
+ // Now returns the current local time.
+ Now() time.Time
+
+ // NewTicker returns *time.Ticker that holds a channel
+ // that delivers "ticks" of a clock.
+ NewTicker(time.Duration) *time.Ticker
+}
+
+// systemClock implements default Clock that uses system time.
+type systemClock struct{}
+
+func (systemClock) Now() time.Time {
+ return time.Now()
+}
+
+func (systemClock) NewTicker(duration time.Duration) *time.Ticker {
+ return time.NewTicker(duration)
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/clock_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/clock_test.go
new file mode 100644
index 0000000..0dff349
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/clock_test.go
@@ -0,0 +1,44 @@
+// Copyright (c) 2021 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "testing"
+ "time"
+
+ "go.uber.org/zap/internal/ztest"
+)
+
+// Verify that the mock clock satisfies the Clock interface.
+var _ Clock = (*ztest.MockClock)(nil)
+
+func TestSystemClock_NewTicker(t *testing.T) {
+ want := 3
+
+ var n int
+ timer := DefaultClock.NewTicker(time.Millisecond)
+ for range timer.C {
+ n++
+ if n == want {
+ return
+ }
+ }
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/console_encoder.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/console_encoder.go
new file mode 100644
index 0000000..1aa5dc3
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/console_encoder.go
@@ -0,0 +1,157 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "fmt"
+ "sync"
+
+ "go.uber.org/zap/buffer"
+ "go.uber.org/zap/internal/bufferpool"
+)
+
+var _sliceEncoderPool = sync.Pool{
+ New: func() interface{} {
+ return &sliceArrayEncoder{elems: make([]interface{}, 0, 2)}
+ },
+}
+
+func getSliceEncoder() *sliceArrayEncoder {
+ return _sliceEncoderPool.Get().(*sliceArrayEncoder)
+}
+
+func putSliceEncoder(e *sliceArrayEncoder) {
+ e.elems = e.elems[:0]
+ _sliceEncoderPool.Put(e)
+}
+
+type consoleEncoder struct {
+ *jsonEncoder
+}
+
+// NewConsoleEncoder creates an encoder whose output is designed for human -
+// rather than machine - consumption. It serializes the core log entry data
+// (message, level, timestamp, etc.) in a plain-text format and leaves the
+// structured context as JSON.
+//
+// Note that although the console encoder doesn't use the keys specified in the
+// encoder configuration, it will omit any element whose key is set to the empty
+// string.
+func NewConsoleEncoder(cfg EncoderConfig) Encoder {
+ if cfg.ConsoleSeparator == "" {
+ // Use a default delimiter of '\t' for backwards compatibility
+ cfg.ConsoleSeparator = "\t"
+ }
+ return consoleEncoder{newJSONEncoder(cfg, true)}
+}
+
+func (c consoleEncoder) Clone() Encoder {
+ return consoleEncoder{c.jsonEncoder.Clone().(*jsonEncoder)}
+}
+
+func (c consoleEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, error) {
+ line := bufferpool.Get()
+
+ // We don't want the entry's metadata to be quoted and escaped (if it's
+ // encoded as strings), which means that we can't use the JSON encoder. The
+ // simplest option is to use the memory encoder and fmt.Fprint.
+ //
+ // If this ever becomes a performance bottleneck, we can implement
+ // ArrayEncoder for our plain-text format.
+ arr := getSliceEncoder()
+ if c.TimeKey != "" && c.EncodeTime != nil {
+ c.EncodeTime(ent.Time, arr)
+ }
+ if c.LevelKey != "" && c.EncodeLevel != nil {
+ c.EncodeLevel(ent.Level, arr)
+ }
+ if ent.LoggerName != "" && c.NameKey != "" {
+ nameEncoder := c.EncodeName
+
+ if nameEncoder == nil {
+ // Fall back to FullNameEncoder for backward compatibility.
+ nameEncoder = FullNameEncoder
+ }
+
+ nameEncoder(ent.LoggerName, arr)
+ }
+ if ent.Caller.Defined {
+ if c.CallerKey != "" && c.EncodeCaller != nil {
+ c.EncodeCaller(ent.Caller, arr)
+ }
+ if c.FunctionKey != "" {
+ arr.AppendString(ent.Caller.Function)
+ }
+ }
+ for i := range arr.elems {
+ if i > 0 {
+ line.AppendString(c.ConsoleSeparator)
+ }
+ fmt.Fprint(line, arr.elems[i])
+ }
+ putSliceEncoder(arr)
+
+ // Add the message itself.
+ if c.MessageKey != "" {
+ c.addSeparatorIfNecessary(line)
+ line.AppendString(ent.Message)
+ }
+
+ // Add any structured context.
+ c.writeContext(line, fields)
+
+ // If there's no stacktrace key, honor that; this allows users to force
+ // single-line output.
+ if ent.Stack != "" && c.StacktraceKey != "" {
+ line.AppendByte('\n')
+ line.AppendString(ent.Stack)
+ }
+
+ line.AppendString(c.LineEnding)
+ return line, nil
+}
+
+func (c consoleEncoder) writeContext(line *buffer.Buffer, extra []Field) {
+ context := c.jsonEncoder.Clone().(*jsonEncoder)
+ defer func() {
+ // putJSONEncoder assumes the buffer is still used, but we write out the buffer so
+ // we can free it.
+ context.buf.Free()
+ putJSONEncoder(context)
+ }()
+
+ addFields(context, extra)
+ context.closeOpenNamespaces()
+ if context.buf.Len() == 0 {
+ return
+ }
+
+ c.addSeparatorIfNecessary(line)
+ line.AppendByte('{')
+ line.Write(context.buf.Bytes())
+ line.AppendByte('}')
+}
+
+func (c consoleEncoder) addSeparatorIfNecessary(line *buffer.Buffer) {
+ if line.Len() > 0 {
+ line.AppendString(c.ConsoleSeparator)
+ }
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/console_encoder_bench_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/console_encoder_bench_test.go
new file mode 100644
index 0000000..62feaea
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/console_encoder_bench_test.go
@@ -0,0 +1,49 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "testing"
+
+ . "go.uber.org/zap/zapcore"
+)
+
+func BenchmarkZapConsole(b *testing.B) {
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ enc := NewConsoleEncoder(humanEncoderConfig())
+ enc.AddString("str", "foo")
+ enc.AddInt64("int64-1", 1)
+ enc.AddInt64("int64-2", 2)
+ enc.AddFloat64("float64", 1.0)
+ enc.AddString("string1", "\n")
+ enc.AddString("string2", "💩")
+ enc.AddString("string3", "🤔")
+ enc.AddString("string4", "🙊")
+ enc.AddBool("bool", true)
+ buf, _ := enc.EncodeEntry(Entry{
+ Message: "fake",
+ Level: DebugLevel,
+ }, nil)
+ buf.Free()
+ }
+ })
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/console_encoder_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/console_encoder_test.go
new file mode 100644
index 0000000..b03f1a7
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/console_encoder_test.go
@@ -0,0 +1,91 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+package zapcore_test
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ . "go.uber.org/zap/zapcore"
+)
+
+var (
+ testEntry = Entry{
+ LoggerName: "main",
+ Level: InfoLevel,
+ Message: `hello`,
+ Time: _epoch,
+ Stack: "fake-stack",
+ Caller: EntryCaller{Defined: true, File: "foo.go", Line: 42, Function: "foo.Foo"},
+ }
+)
+
+func TestConsoleSeparator(t *testing.T) {
+ tests := []struct {
+ desc string
+ separator string
+ wantConsole string
+ }{
+ {
+ desc: "space console separator",
+ separator: " ",
+ wantConsole: "0 info main foo.go:42 foo.Foo hello\nfake-stack\n",
+ },
+ {
+ desc: "default console separator",
+ separator: "",
+ wantConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\nfake-stack\n",
+ },
+ {
+ desc: "tag console separator",
+ separator: "\t",
+ wantConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\nfake-stack\n",
+ },
+ {
+ desc: "dash console separator",
+ separator: "--",
+ wantConsole: "0--info--main--foo.go:42--foo.Foo--hello\nfake-stack\n",
+ },
+ }
+
+ for _, tt := range tests {
+ console := NewConsoleEncoder(encoderTestEncoderConfig(tt.separator))
+ t.Run(tt.desc, func(t *testing.T) {
+ entry := testEntry
+ consoleOut, err := console.EncodeEntry(entry, nil)
+ if !assert.NoError(t, err) {
+ return
+ }
+ assert.Equal(
+ t,
+ tt.wantConsole,
+ consoleOut.String(),
+ "Unexpected console output",
+ )
+ })
+
+ }
+}
+
+func encoderTestEncoderConfig(separator string) EncoderConfig {
+ testEncoder := testEncoderConfig()
+ testEncoder.ConsoleSeparator = separator
+ return testEncoder
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/core.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/core.go
new file mode 100644
index 0000000..9dfd640
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/core.go
@@ -0,0 +1,122 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+// Core is a minimal, fast logger interface. It's designed for library authors
+// to wrap in a more user-friendly API.
+type Core interface {
+ LevelEnabler
+
+ // With adds structured context to the Core.
+ With([]Field) Core
+ // Check determines whether the supplied Entry should be logged (using the
+ // embedded LevelEnabler and possibly some extra logic). If the entry
+ // should be logged, the Core adds itself to the CheckedEntry and returns
+ // the result.
+ //
+ // Callers must use Check before calling Write.
+ Check(Entry, *CheckedEntry) *CheckedEntry
+ // Write serializes the Entry and any Fields supplied at the log site and
+ // writes them to their destination.
+ //
+ // If called, Write should always log the Entry and Fields; it should not
+ // replicate the logic of Check.
+ Write(Entry, []Field) error
+ // Sync flushes buffered logs (if any).
+ Sync() error
+}
+
+type nopCore struct{}
+
+// NewNopCore returns a no-op Core.
+func NewNopCore() Core { return nopCore{} }
+func (nopCore) Enabled(Level) bool { return false }
+func (n nopCore) With([]Field) Core { return n }
+func (nopCore) Check(_ Entry, ce *CheckedEntry) *CheckedEntry { return ce }
+func (nopCore) Write(Entry, []Field) error { return nil }
+func (nopCore) Sync() error { return nil }
+
+// NewCore creates a Core that writes logs to a WriteSyncer.
+func NewCore(enc Encoder, ws WriteSyncer, enab LevelEnabler) Core {
+ return &ioCore{
+ LevelEnabler: enab,
+ enc: enc,
+ out: ws,
+ }
+}
+
+type ioCore struct {
+ LevelEnabler
+ enc Encoder
+ out WriteSyncer
+}
+
+var (
+ _ Core = (*ioCore)(nil)
+ _ leveledEnabler = (*ioCore)(nil)
+)
+
+func (c *ioCore) Level() Level {
+ return LevelOf(c.LevelEnabler)
+}
+
+func (c *ioCore) With(fields []Field) Core {
+ clone := c.clone()
+ addFields(clone.enc, fields)
+ return clone
+}
+
+func (c *ioCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
+ if c.Enabled(ent.Level) {
+ return ce.AddCore(ent, c)
+ }
+ return ce
+}
+
+func (c *ioCore) Write(ent Entry, fields []Field) error {
+ buf, err := c.enc.EncodeEntry(ent, fields)
+ if err != nil {
+ return err
+ }
+ _, err = c.out.Write(buf.Bytes())
+ buf.Free()
+ if err != nil {
+ return err
+ }
+ if ent.Level > ErrorLevel {
+ // Since we may be crashing the program, sync the output. Ignore Sync
+ // errors, pending a clean solution to issue #370.
+ c.Sync()
+ }
+ return nil
+}
+
+func (c *ioCore) Sync() error {
+ return c.out.Sync()
+}
+
+func (c *ioCore) clone() *ioCore {
+ return &ioCore{
+ LevelEnabler: c.LevelEnabler,
+ enc: c.enc.Clone(),
+ out: c.out,
+ }
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/core_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/core_test.go
new file mode 100644
index 0000000..1311097
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/core_test.go
@@ -0,0 +1,165 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "errors"
+ "os"
+ "testing"
+ "time"
+
+ "go.uber.org/zap/internal/ztest"
+ . "go.uber.org/zap/zapcore"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func makeInt64Field(key string, val int) Field {
+ return Field{Type: Int64Type, Integer: int64(val), Key: key}
+}
+
+func TestNopCore(t *testing.T) {
+ entry := Entry{
+ Message: "test",
+ Level: InfoLevel,
+ Time: time.Now(),
+ LoggerName: "main",
+ Stack: "fake-stack",
+ }
+ ce := &CheckedEntry{}
+
+ allLevels := []Level{
+ DebugLevel,
+ InfoLevel,
+ WarnLevel,
+ ErrorLevel,
+ DPanicLevel,
+ PanicLevel,
+ FatalLevel,
+ }
+ core := NewNopCore()
+ assert.Equal(t, core, core.With([]Field{makeInt64Field("k", 42)}), "Expected no-op With.")
+ for _, level := range allLevels {
+ assert.False(t, core.Enabled(level), "Expected all levels to be disabled in no-op core.")
+ assert.Equal(t, ce, core.Check(entry, ce), "Expected no-op Check to return checked entry unchanged.")
+ assert.NoError(t, core.Write(entry, nil), "Expected no-op Writes to always succeed.")
+ assert.NoError(t, core.Sync(), "Expected no-op Syncs to always succeed.")
+ }
+}
+
+func TestIOCore(t *testing.T) {
+ temp, err := os.CreateTemp(t.TempDir(), "test.log")
+ require.NoError(t, err)
+
+ // Drop timestamps for simpler assertions (timestamp encoding is tested
+ // elsewhere).
+ cfg := testEncoderConfig()
+ cfg.TimeKey = ""
+
+ core := NewCore(
+ NewJSONEncoder(cfg),
+ temp,
+ InfoLevel,
+ ).With([]Field{makeInt64Field("k", 1)})
+ defer assert.NoError(t, core.Sync(), "Expected Syncing a temp file to succeed.")
+
+ t.Run("LevelOf", func(t *testing.T) {
+ assert.Equal(t, InfoLevel, LevelOf(core), "Incorrect Core Level")
+ })
+
+ if ce := core.Check(Entry{Level: DebugLevel, Message: "debug"}, nil); ce != nil {
+ ce.Write(makeInt64Field("k", 2))
+ }
+ if ce := core.Check(Entry{Level: InfoLevel, Message: "info"}, nil); ce != nil {
+ ce.Write(makeInt64Field("k", 3))
+ }
+ if ce := core.Check(Entry{Level: WarnLevel, Message: "warn"}, nil); ce != nil {
+ ce.Write(makeInt64Field("k", 4))
+ }
+
+ logged, err := os.ReadFile(temp.Name())
+ require.NoError(t, err, "Failed to read from temp file.")
+ require.Equal(
+ t,
+ `{"level":"info","msg":"info","k":1,"k":3}`+"\n"+
+ `{"level":"warn","msg":"warn","k":1,"k":4}`+"\n",
+ string(logged),
+ "Unexpected log output.",
+ )
+}
+
+func TestIOCoreSyncFail(t *testing.T) {
+ sink := &ztest.Discarder{}
+ err := errors.New("failed")
+ sink.SetError(err)
+
+ core := NewCore(
+ NewJSONEncoder(testEncoderConfig()),
+ sink,
+ DebugLevel,
+ )
+
+ assert.Equal(
+ t,
+ err,
+ core.Sync(),
+ "Expected core.Sync to return errors from underlying WriteSyncer.",
+ )
+}
+
+func TestIOCoreSyncsOutput(t *testing.T) {
+ tests := []struct {
+ entry Entry
+ shouldSync bool
+ }{
+ {Entry{Level: DebugLevel}, false},
+ {Entry{Level: InfoLevel}, false},
+ {Entry{Level: WarnLevel}, false},
+ {Entry{Level: ErrorLevel}, false},
+ {Entry{Level: DPanicLevel}, true},
+ {Entry{Level: PanicLevel}, true},
+ {Entry{Level: FatalLevel}, true},
+ }
+
+ for _, tt := range tests {
+ sink := &ztest.Discarder{}
+ core := NewCore(
+ NewJSONEncoder(testEncoderConfig()),
+ sink,
+ DebugLevel,
+ )
+
+ core.Write(tt.entry, nil)
+ assert.Equal(t, tt.shouldSync, sink.Called(), "Incorrect Sync behavior.")
+ }
+}
+
+func TestIOCoreWriteFailure(t *testing.T) {
+ core := NewCore(
+ NewJSONEncoder(testEncoderConfig()),
+ Lock(&ztest.FailWriter{}),
+ DebugLevel,
+ )
+ err := core.Write(Entry{}, nil)
+ // Should log the error.
+ assert.Error(t, err, "Expected writing Entry to fail.")
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/doc.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/doc.go
new file mode 100644
index 0000000..31000e9
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/doc.go
@@ -0,0 +1,24 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// Package zapcore defines and implements the low-level interfaces upon which
+// zap is built. By providing alternate implementations of these interfaces,
+// external packages can extend zap's capabilities.
+package zapcore // import "go.uber.org/zap/zapcore"
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/encoder.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/encoder.go
new file mode 100644
index 0000000..5769ff3
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/encoder.go
@@ -0,0 +1,451 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "encoding/json"
+ "io"
+ "time"
+
+ "go.uber.org/zap/buffer"
+)
+
+// DefaultLineEnding defines the default line ending when writing logs.
+// Alternate line endings specified in EncoderConfig can override this
+// behavior.
+const DefaultLineEnding = "\n"
+
+// OmitKey defines the key to use when callers want to remove a key from log output.
+const OmitKey = ""
+
+// A LevelEncoder serializes a Level to a primitive type.
+type LevelEncoder func(Level, PrimitiveArrayEncoder)
+
+// LowercaseLevelEncoder serializes a Level to a lowercase string. For example,
+// InfoLevel is serialized to "info".
+func LowercaseLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
+ enc.AppendString(l.String())
+}
+
+// LowercaseColorLevelEncoder serializes a Level to a lowercase string and adds coloring.
+// For example, InfoLevel is serialized to "info" and colored blue.
+func LowercaseColorLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
+ s, ok := _levelToLowercaseColorString[l]
+ if !ok {
+ s = _unknownLevelColor.Add(l.String())
+ }
+ enc.AppendString(s)
+}
+
+// CapitalLevelEncoder serializes a Level to an all-caps string. For example,
+// InfoLevel is serialized to "INFO".
+func CapitalLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
+ enc.AppendString(l.CapitalString())
+}
+
+// CapitalColorLevelEncoder serializes a Level to an all-caps string and adds color.
+// For example, InfoLevel is serialized to "INFO" and colored blue.
+func CapitalColorLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
+ s, ok := _levelToCapitalColorString[l]
+ if !ok {
+ s = _unknownLevelColor.Add(l.CapitalString())
+ }
+ enc.AppendString(s)
+}
+
+// UnmarshalText unmarshals text to a LevelEncoder. "capital" is unmarshaled to
+// CapitalLevelEncoder, "coloredCapital" is unmarshaled to CapitalColorLevelEncoder,
+// "colored" is unmarshaled to LowercaseColorLevelEncoder, and anything else
+// is unmarshaled to LowercaseLevelEncoder.
+func (e *LevelEncoder) UnmarshalText(text []byte) error {
+ switch string(text) {
+ case "capital":
+ *e = CapitalLevelEncoder
+ case "capitalColor":
+ *e = CapitalColorLevelEncoder
+ case "color":
+ *e = LowercaseColorLevelEncoder
+ default:
+ *e = LowercaseLevelEncoder
+ }
+ return nil
+}
+
+// A TimeEncoder serializes a time.Time to a primitive type.
+type TimeEncoder func(time.Time, PrimitiveArrayEncoder)
+
+// EpochTimeEncoder serializes a time.Time to a floating-point number of seconds
+// since the Unix epoch.
+func EpochTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
+ nanos := t.UnixNano()
+ sec := float64(nanos) / float64(time.Second)
+ enc.AppendFloat64(sec)
+}
+
+// EpochMillisTimeEncoder serializes a time.Time to a floating-point number of
+// milliseconds since the Unix epoch.
+func EpochMillisTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
+ nanos := t.UnixNano()
+ millis := float64(nanos) / float64(time.Millisecond)
+ enc.AppendFloat64(millis)
+}
+
+// EpochNanosTimeEncoder serializes a time.Time to an integer number of
+// nanoseconds since the Unix epoch.
+func EpochNanosTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
+ enc.AppendInt64(t.UnixNano())
+}
+
+func encodeTimeLayout(t time.Time, layout string, enc PrimitiveArrayEncoder) {
+ type appendTimeEncoder interface {
+ AppendTimeLayout(time.Time, string)
+ }
+
+ if enc, ok := enc.(appendTimeEncoder); ok {
+ enc.AppendTimeLayout(t, layout)
+ return
+ }
+
+ enc.AppendString(t.Format(layout))
+}
+
+// ISO8601TimeEncoder serializes a time.Time to an ISO8601-formatted string
+// with millisecond precision.
+//
+// If enc supports AppendTimeLayout(t time.Time,layout string), it's used
+// instead of appending a pre-formatted string value.
+func ISO8601TimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
+ encodeTimeLayout(t, "2006-01-02T15:04:05.000Z0700", enc)
+}
+
+// RFC3339TimeEncoder serializes a time.Time to an RFC3339-formatted string.
+//
+// If enc supports AppendTimeLayout(t time.Time,layout string), it's used
+// instead of appending a pre-formatted string value.
+func RFC3339TimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
+ encodeTimeLayout(t, time.RFC3339, enc)
+}
+
+// RFC3339NanoTimeEncoder serializes a time.Time to an RFC3339-formatted string
+// with nanosecond precision.
+//
+// If enc supports AppendTimeLayout(t time.Time,layout string), it's used
+// instead of appending a pre-formatted string value.
+func RFC3339NanoTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
+ encodeTimeLayout(t, time.RFC3339Nano, enc)
+}
+
+// TimeEncoderOfLayout returns TimeEncoder which serializes a time.Time using
+// given layout.
+func TimeEncoderOfLayout(layout string) TimeEncoder {
+ return func(t time.Time, enc PrimitiveArrayEncoder) {
+ encodeTimeLayout(t, layout, enc)
+ }
+}
+
+// UnmarshalText unmarshals text to a TimeEncoder.
+// "rfc3339nano" and "RFC3339Nano" are unmarshaled to RFC3339NanoTimeEncoder.
+// "rfc3339" and "RFC3339" are unmarshaled to RFC3339TimeEncoder.
+// "iso8601" and "ISO8601" are unmarshaled to ISO8601TimeEncoder.
+// "millis" is unmarshaled to EpochMillisTimeEncoder.
+// "nanos" is unmarshaled to EpochNanosEncoder.
+// Anything else is unmarshaled to EpochTimeEncoder.
+func (e *TimeEncoder) UnmarshalText(text []byte) error {
+ switch string(text) {
+ case "rfc3339nano", "RFC3339Nano":
+ *e = RFC3339NanoTimeEncoder
+ case "rfc3339", "RFC3339":
+ *e = RFC3339TimeEncoder
+ case "iso8601", "ISO8601":
+ *e = ISO8601TimeEncoder
+ case "millis":
+ *e = EpochMillisTimeEncoder
+ case "nanos":
+ *e = EpochNanosTimeEncoder
+ default:
+ *e = EpochTimeEncoder
+ }
+ return nil
+}
+
+// UnmarshalYAML unmarshals YAML to a TimeEncoder.
+// If value is an object with a "layout" field, it will be unmarshaled to TimeEncoder with given layout.
+//
+// timeEncoder:
+// layout: 06/01/02 03:04pm
+//
+// If value is string, it uses UnmarshalText.
+//
+// timeEncoder: iso8601
+func (e *TimeEncoder) UnmarshalYAML(unmarshal func(interface{}) error) error {
+ var o struct {
+ Layout string `json:"layout" yaml:"layout"`
+ }
+ if err := unmarshal(&o); err == nil {
+ *e = TimeEncoderOfLayout(o.Layout)
+ return nil
+ }
+
+ var s string
+ if err := unmarshal(&s); err != nil {
+ return err
+ }
+ return e.UnmarshalText([]byte(s))
+}
+
+// UnmarshalJSON unmarshals JSON to a TimeEncoder as same way UnmarshalYAML does.
+func (e *TimeEncoder) UnmarshalJSON(data []byte) error {
+ return e.UnmarshalYAML(func(v interface{}) error {
+ return json.Unmarshal(data, v)
+ })
+}
+
+// A DurationEncoder serializes a time.Duration to a primitive type.
+type DurationEncoder func(time.Duration, PrimitiveArrayEncoder)
+
+// SecondsDurationEncoder serializes a time.Duration to a floating-point number of seconds elapsed.
+func SecondsDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
+ enc.AppendFloat64(float64(d) / float64(time.Second))
+}
+
+// NanosDurationEncoder serializes a time.Duration to an integer number of
+// nanoseconds elapsed.
+func NanosDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
+ enc.AppendInt64(int64(d))
+}
+
+// MillisDurationEncoder serializes a time.Duration to an integer number of
+// milliseconds elapsed.
+func MillisDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
+ enc.AppendInt64(d.Nanoseconds() / 1e6)
+}
+
+// StringDurationEncoder serializes a time.Duration using its built-in String
+// method.
+func StringDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
+ enc.AppendString(d.String())
+}
+
+// UnmarshalText unmarshals text to a DurationEncoder. "string" is unmarshaled
+// to StringDurationEncoder, and anything else is unmarshaled to
+// NanosDurationEncoder.
+func (e *DurationEncoder) UnmarshalText(text []byte) error {
+ switch string(text) {
+ case "string":
+ *e = StringDurationEncoder
+ case "nanos":
+ *e = NanosDurationEncoder
+ case "ms":
+ *e = MillisDurationEncoder
+ default:
+ *e = SecondsDurationEncoder
+ }
+ return nil
+}
+
+// A CallerEncoder serializes an EntryCaller to a primitive type.
+type CallerEncoder func(EntryCaller, PrimitiveArrayEncoder)
+
+// FullCallerEncoder serializes a caller in /full/path/to/package/file:line
+// format.
+func FullCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) {
+ // TODO: consider using a byte-oriented API to save an allocation.
+ enc.AppendString(caller.String())
+}
+
+// ShortCallerEncoder serializes a caller in package/file:line format, trimming
+// all but the final directory from the full path.
+func ShortCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) {
+ // TODO: consider using a byte-oriented API to save an allocation.
+ enc.AppendString(caller.TrimmedPath())
+}
+
+// UnmarshalText unmarshals text to a CallerEncoder. "full" is unmarshaled to
+// FullCallerEncoder and anything else is unmarshaled to ShortCallerEncoder.
+func (e *CallerEncoder) UnmarshalText(text []byte) error {
+ switch string(text) {
+ case "full":
+ *e = FullCallerEncoder
+ default:
+ *e = ShortCallerEncoder
+ }
+ return nil
+}
+
+// A NameEncoder serializes a period-separated logger name to a primitive
+// type.
+type NameEncoder func(string, PrimitiveArrayEncoder)
+
+// FullNameEncoder serializes the logger name as-is.
+func FullNameEncoder(loggerName string, enc PrimitiveArrayEncoder) {
+ enc.AppendString(loggerName)
+}
+
+// UnmarshalText unmarshals text to a NameEncoder. Currently, everything is
+// unmarshaled to FullNameEncoder.
+func (e *NameEncoder) UnmarshalText(text []byte) error {
+ switch string(text) {
+ case "full":
+ *e = FullNameEncoder
+ default:
+ *e = FullNameEncoder
+ }
+ return nil
+}
+
+// An EncoderConfig allows users to configure the concrete encoders supplied by
+// zapcore.
+type EncoderConfig struct {
+ // Set the keys used for each log entry. If any key is empty, that portion
+ // of the entry is omitted.
+ MessageKey string `json:"messageKey" yaml:"messageKey"`
+ LevelKey string `json:"levelKey" yaml:"levelKey"`
+ TimeKey string `json:"timeKey" yaml:"timeKey"`
+ NameKey string `json:"nameKey" yaml:"nameKey"`
+ CallerKey string `json:"callerKey" yaml:"callerKey"`
+ FunctionKey string `json:"functionKey" yaml:"functionKey"`
+ StacktraceKey string `json:"stacktraceKey" yaml:"stacktraceKey"`
+ SkipLineEnding bool `json:"skipLineEnding" yaml:"skipLineEnding"`
+ LineEnding string `json:"lineEnding" yaml:"lineEnding"`
+ // Configure the primitive representations of common complex types. For
+ // example, some users may want all time.Times serialized as floating-point
+ // seconds since epoch, while others may prefer ISO8601 strings.
+ EncodeLevel LevelEncoder `json:"levelEncoder" yaml:"levelEncoder"`
+ EncodeTime TimeEncoder `json:"timeEncoder" yaml:"timeEncoder"`
+ EncodeDuration DurationEncoder `json:"durationEncoder" yaml:"durationEncoder"`
+ EncodeCaller CallerEncoder `json:"callerEncoder" yaml:"callerEncoder"`
+ // Unlike the other primitive type encoders, EncodeName is optional. The
+ // zero value falls back to FullNameEncoder.
+ EncodeName NameEncoder `json:"nameEncoder" yaml:"nameEncoder"`
+ // Configure the encoder for interface{} type objects.
+ // If not provided, objects are encoded using json.Encoder
+ NewReflectedEncoder func(io.Writer) ReflectedEncoder `json:"-" yaml:"-"`
+ // Configures the field separator used by the console encoder. Defaults
+ // to tab.
+ ConsoleSeparator string `json:"consoleSeparator" yaml:"consoleSeparator"`
+}
+
+// ObjectEncoder is a strongly-typed, encoding-agnostic interface for adding a
+// map- or struct-like object to the logging context. Like maps, ObjectEncoders
+// aren't safe for concurrent use (though typical use shouldn't require locks).
+type ObjectEncoder interface {
+ // Logging-specific marshalers.
+ AddArray(key string, marshaler ArrayMarshaler) error
+ AddObject(key string, marshaler ObjectMarshaler) error
+
+ // Built-in types.
+ AddBinary(key string, value []byte) // for arbitrary bytes
+ AddByteString(key string, value []byte) // for UTF-8 encoded bytes
+ AddBool(key string, value bool)
+ AddComplex128(key string, value complex128)
+ AddComplex64(key string, value complex64)
+ AddDuration(key string, value time.Duration)
+ AddFloat64(key string, value float64)
+ AddFloat32(key string, value float32)
+ AddInt(key string, value int)
+ AddInt64(key string, value int64)
+ AddInt32(key string, value int32)
+ AddInt16(key string, value int16)
+ AddInt8(key string, value int8)
+ AddString(key, value string)
+ AddTime(key string, value time.Time)
+ AddUint(key string, value uint)
+ AddUint64(key string, value uint64)
+ AddUint32(key string, value uint32)
+ AddUint16(key string, value uint16)
+ AddUint8(key string, value uint8)
+ AddUintptr(key string, value uintptr)
+
+ // AddReflected uses reflection to serialize arbitrary objects, so it can be
+ // slow and allocation-heavy.
+ AddReflected(key string, value interface{}) error
+ // OpenNamespace opens an isolated namespace where all subsequent fields will
+ // be added. Applications can use namespaces to prevent key collisions when
+ // injecting loggers into sub-components or third-party libraries.
+ OpenNamespace(key string)
+}
+
+// ArrayEncoder is a strongly-typed, encoding-agnostic interface for adding
+// array-like objects to the logging context. Of note, it supports mixed-type
+// arrays even though they aren't typical in Go. Like slices, ArrayEncoders
+// aren't safe for concurrent use (though typical use shouldn't require locks).
+type ArrayEncoder interface {
+ // Built-in types.
+ PrimitiveArrayEncoder
+
+ // Time-related types.
+ AppendDuration(time.Duration)
+ AppendTime(time.Time)
+
+ // Logging-specific marshalers.
+ AppendArray(ArrayMarshaler) error
+ AppendObject(ObjectMarshaler) error
+
+ // AppendReflected uses reflection to serialize arbitrary objects, so it's
+ // slow and allocation-heavy.
+ AppendReflected(value interface{}) error
+}
+
+// PrimitiveArrayEncoder is the subset of the ArrayEncoder interface that deals
+// only in Go's built-in types. It's included only so that Duration- and
+// TimeEncoders cannot trigger infinite recursion.
+type PrimitiveArrayEncoder interface {
+ // Built-in types.
+ AppendBool(bool)
+ AppendByteString([]byte) // for UTF-8 encoded bytes
+ AppendComplex128(complex128)
+ AppendComplex64(complex64)
+ AppendFloat64(float64)
+ AppendFloat32(float32)
+ AppendInt(int)
+ AppendInt64(int64)
+ AppendInt32(int32)
+ AppendInt16(int16)
+ AppendInt8(int8)
+ AppendString(string)
+ AppendUint(uint)
+ AppendUint64(uint64)
+ AppendUint32(uint32)
+ AppendUint16(uint16)
+ AppendUint8(uint8)
+ AppendUintptr(uintptr)
+}
+
+// Encoder is a format-agnostic interface for all log entry marshalers. Since
+// log encoders don't need to support the same wide range of use cases as
+// general-purpose marshalers, it's possible to make them faster and
+// lower-allocation.
+//
+// Implementations of the ObjectEncoder interface's methods can, of course,
+// freely modify the receiver. However, the Clone and EncodeEntry methods will
+// be called concurrently and shouldn't modify the receiver.
+type Encoder interface {
+ ObjectEncoder
+
+ // Clone copies the encoder, ensuring that adding fields to the copy doesn't
+ // affect the original.
+ Clone() Encoder
+
+ // EncodeEntry encodes an entry and fields, along with any accumulated
+ // context, into a byte buffer and returns it. Any fields that are empty,
+ // including fields on the `Entry` type, should be omitted.
+ EncodeEntry(Entry, []Field) (*buffer.Buffer, error)
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/encoder_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/encoder_test.go
new file mode 100644
index 0000000..c0dbc5b
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/encoder_test.go
@@ -0,0 +1,730 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "encoding/json"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "gopkg.in/yaml.v3"
+
+ . "go.uber.org/zap/zapcore"
+)
+
+var (
+ _epoch = time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC)
+ _testEntry = Entry{
+ LoggerName: "main",
+ Level: InfoLevel,
+ Message: `hello`,
+ Time: _epoch,
+ Stack: "fake-stack",
+ Caller: EntryCaller{Defined: true, File: "foo.go", Line: 42, Function: "foo.Foo"},
+ }
+)
+
+func testEncoderConfig() EncoderConfig {
+ return EncoderConfig{
+ MessageKey: "msg",
+ LevelKey: "level",
+ NameKey: "name",
+ TimeKey: "ts",
+ CallerKey: "caller",
+ FunctionKey: "func",
+ StacktraceKey: "stacktrace",
+ LineEnding: "\n",
+ EncodeTime: EpochTimeEncoder,
+ EncodeLevel: LowercaseLevelEncoder,
+ EncodeDuration: SecondsDurationEncoder,
+ EncodeCaller: ShortCallerEncoder,
+ }
+}
+
+func humanEncoderConfig() EncoderConfig {
+ cfg := testEncoderConfig()
+ cfg.EncodeTime = ISO8601TimeEncoder
+ cfg.EncodeLevel = CapitalLevelEncoder
+ cfg.EncodeDuration = StringDurationEncoder
+ return cfg
+}
+
+func capitalNameEncoder(loggerName string, enc PrimitiveArrayEncoder) {
+ enc.AppendString(strings.ToUpper(loggerName))
+}
+
+func TestEncoderConfiguration(t *testing.T) {
+ base := testEncoderConfig()
+
+ tests := []struct {
+ desc string
+ cfg EncoderConfig
+ amendEntry func(Entry) Entry
+ extra func(Encoder)
+ expectedJSON string
+ expectedConsole string
+ }{
+ {
+ desc: "messages to be escaped",
+ cfg: base,
+ amendEntry: func(ent Entry) Entry {
+ ent.Message = `hello\`
+ return ent
+ },
+ expectedJSON: `{"level":"info","ts":0,"name":"main","caller":"foo.go:42","func":"foo.Foo","msg":"hello\\","stacktrace":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\\\nfake-stack\n",
+ },
+ {
+ desc: "use custom entry keys in JSON output and ignore them in console output",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: "F",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\nfake-stack\n",
+ },
+ {
+ desc: "skip line ending if SkipLineEnding is 'true'",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: "F",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ SkipLineEnding: true,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}`,
+ expectedConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\nfake-stack",
+ },
+ {
+ desc: "skip level if LevelKey is omitted",
+ cfg: EncoderConfig{
+ LevelKey: OmitKey,
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: "F",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tmain\tfoo.go:42\tfoo.Foo\thello\nfake-stack\n",
+ },
+ {
+ desc: "skip timestamp if TimeKey is omitted",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: OmitKey,
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: "F",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"info","N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\n",
+ expectedConsole: "info\tmain\tfoo.go:42\tfoo.Foo\thello\nfake-stack\n",
+ },
+ {
+ desc: "skip message if MessageKey is omitted",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: OmitKey,
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: "F",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\nfake-stack\n",
+ },
+ {
+ desc: "skip name if NameKey is omitted",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: OmitKey,
+ CallerKey: "C",
+ FunctionKey: "F",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"info","T":0,"C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tfoo.go:42\tfoo.Foo\thello\nfake-stack\n",
+ },
+ {
+ desc: "skip caller if CallerKey is omitted",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: OmitKey,
+ FunctionKey: "F",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tmain\tfoo.Foo\thello\nfake-stack\n",
+ },
+ {
+ desc: "skip function if FunctionKey is omitted",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: OmitKey,
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\nfake-stack\n",
+ },
+ {
+ desc: "skip stacktrace if StacktraceKey is omitted",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: "F",
+ StacktraceKey: OmitKey,
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello"}` + "\n",
+ expectedConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\n",
+ },
+ {
+ desc: "use the supplied EncodeTime, for both the entry and any times added",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: "F",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: func(t time.Time, enc PrimitiveArrayEncoder) { enc.AppendString(t.String()) },
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ extra: func(enc Encoder) {
+ enc.AddTime("extra", _epoch)
+ enc.AddArray("extras", ArrayMarshalerFunc(func(enc ArrayEncoder) error {
+ enc.AppendTime(_epoch)
+ return nil
+ }))
+ },
+ expectedJSON: `{"L":"info","T":"1970-01-01 00:00:00 +0000 UTC","N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","extra":"1970-01-01 00:00:00 +0000 UTC","extras":["1970-01-01 00:00:00 +0000 UTC"],"S":"fake-stack"}` + "\n",
+ expectedConsole: "1970-01-01 00:00:00 +0000 UTC\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\t" + // plain-text preamble
+ `{"extra": "1970-01-01 00:00:00 +0000 UTC", "extras": ["1970-01-01 00:00:00 +0000 UTC"]}` + // JSON context
+ "\nfake-stack\n", // stacktrace after newline
+ },
+ {
+ desc: "use the supplied EncodeDuration for any durations added",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: "F",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: StringDurationEncoder,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ extra: func(enc Encoder) {
+ enc.AddDuration("extra", time.Second)
+ enc.AddArray("extras", ArrayMarshalerFunc(func(enc ArrayEncoder) error {
+ enc.AppendDuration(time.Minute)
+ return nil
+ }))
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","extra":"1s","extras":["1m0s"],"S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\t" + // preamble
+ `{"extra": "1s", "extras": ["1m0s"]}` + // context
+ "\nfake-stack\n", // stacktrace
+ },
+ {
+ desc: "use the supplied EncodeLevel",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: "F",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: CapitalLevelEncoder,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"INFO","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tINFO\tmain\tfoo.go:42\tfoo.Foo\thello\nfake-stack\n",
+ },
+ {
+ desc: "use the supplied EncodeName",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: "F",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ EncodeName: capitalNameEncoder,
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"MAIN","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tMAIN\tfoo.go:42\tfoo.Foo\thello\nfake-stack\n",
+ },
+ {
+ desc: "close all open namespaces",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: "F",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ extra: func(enc Encoder) {
+ enc.OpenNamespace("outer")
+ enc.OpenNamespace("inner")
+ enc.AddString("foo", "bar")
+ enc.OpenNamespace("innermost")
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","outer":{"inner":{"foo":"bar","innermost":{}}},"S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\t" +
+ `{"outer": {"inner": {"foo": "bar", "innermost": {}}}}` +
+ "\nfake-stack\n",
+ },
+ {
+ desc: "handle no-op EncodeTime",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: "F",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: func(time.Time, PrimitiveArrayEncoder) {},
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ extra: func(enc Encoder) { enc.AddTime("sometime", time.Unix(0, 100)) },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","sometime":100,"S":"fake-stack"}` + "\n",
+ expectedConsole: "info\tmain\tfoo.go:42\tfoo.Foo\thello\t" + `{"sometime": 100}` + "\nfake-stack\n",
+ },
+ {
+ desc: "handle no-op EncodeDuration",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: "F",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: func(time.Duration, PrimitiveArrayEncoder) {},
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ extra: func(enc Encoder) { enc.AddDuration("someduration", time.Microsecond) },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","someduration":1000,"S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\t" + `{"someduration": 1000}` + "\nfake-stack\n",
+ },
+ {
+ desc: "handle no-op EncodeLevel",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: "F",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: func(Level, PrimitiveArrayEncoder) {},
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tmain\tfoo.go:42\tfoo.Foo\thello\nfake-stack\n",
+ },
+ {
+ desc: "handle no-op EncodeCaller",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: "F",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: func(EntryCaller, PrimitiveArrayEncoder) {},
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tmain\tfoo.Foo\thello\nfake-stack\n",
+ },
+ {
+ desc: "handle no-op EncodeName",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: "F",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ EncodeName: func(string, PrimitiveArrayEncoder) {},
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tfoo.go:42\tfoo.Foo\thello\nfake-stack\n",
+ },
+ {
+ desc: "use custom line separator",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: "F",
+ StacktraceKey: "S",
+ LineEnding: "\r\n",
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + "\r\n",
+ expectedConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\nfake-stack\r\n",
+ },
+ {
+ desc: "omit line separator definition - fall back to default",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: "F",
+ StacktraceKey: "S",
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","F":"foo.Foo","M":"hello","S":"fake-stack"}` + DefaultLineEnding,
+ expectedConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\nfake-stack" + DefaultLineEnding,
+ },
+ }
+
+ for i, tt := range tests {
+ json := NewJSONEncoder(tt.cfg)
+ console := NewConsoleEncoder(tt.cfg)
+ if tt.extra != nil {
+ tt.extra(json)
+ tt.extra(console)
+ }
+ entry := _testEntry
+ if tt.amendEntry != nil {
+ entry = tt.amendEntry(_testEntry)
+ }
+ jsonOut, jsonErr := json.EncodeEntry(entry, nil)
+ if assert.NoError(t, jsonErr, "Unexpected error JSON-encoding entry in case #%d.", i) {
+ assert.Equal(
+ t,
+ tt.expectedJSON,
+ jsonOut.String(),
+ "Unexpected JSON output: expected to %v.", tt.desc,
+ )
+ }
+ consoleOut, consoleErr := console.EncodeEntry(entry, nil)
+ if assert.NoError(t, consoleErr, "Unexpected error console-encoding entry in case #%d.", i) {
+ assert.Equal(
+ t,
+ tt.expectedConsole,
+ consoleOut.String(),
+ "Unexpected console output: expected to %v.", tt.desc,
+ )
+ }
+ }
+}
+
+func TestLevelEncoders(t *testing.T) {
+ tests := []struct {
+ name string
+ expected interface{} // output of encoding InfoLevel
+ }{
+ {"capital", "INFO"},
+ {"lower", "info"},
+ {"", "info"},
+ {"something-random", "info"},
+ }
+
+ for _, tt := range tests {
+ var le LevelEncoder
+ require.NoError(t, le.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name)
+ assertAppended(
+ t,
+ tt.expected,
+ func(arr ArrayEncoder) { le(InfoLevel, arr) },
+ "Unexpected output serializing InfoLevel with %q.", tt.name,
+ )
+ }
+}
+
+func TestTimeEncoders(t *testing.T) {
+ moment := time.Unix(100, 50005000).UTC()
+ tests := []struct {
+ yamlDoc string
+ expected interface{} // output of serializing moment
+ }{
+ {"timeEncoder: iso8601", "1970-01-01T00:01:40.050Z"},
+ {"timeEncoder: ISO8601", "1970-01-01T00:01:40.050Z"},
+ {"timeEncoder: millis", 100050.005},
+ {"timeEncoder: nanos", int64(100050005000)},
+ {"timeEncoder: {layout: 06/01/02 03:04pm}", "70/01/01 12:01am"},
+ {"timeEncoder: ''", 100.050005},
+ {"timeEncoder: something-random", 100.050005},
+ {"timeEncoder: rfc3339", "1970-01-01T00:01:40Z"},
+ {"timeEncoder: RFC3339", "1970-01-01T00:01:40Z"},
+ {"timeEncoder: rfc3339nano", "1970-01-01T00:01:40.050005Z"},
+ {"timeEncoder: RFC3339Nano", "1970-01-01T00:01:40.050005Z"},
+ }
+
+ for _, tt := range tests {
+ cfg := EncoderConfig{}
+ require.NoError(t, yaml.Unmarshal([]byte(tt.yamlDoc), &cfg), "Unexpected error unmarshaling %q.", tt.yamlDoc)
+ require.NotNil(t, cfg.EncodeTime, "Unmashalled timeEncoder is nil for %q.", tt.yamlDoc)
+ assertAppended(
+ t,
+ tt.expected,
+ func(arr ArrayEncoder) { cfg.EncodeTime(moment, arr) },
+ "Unexpected output serializing %v with %q.", moment, tt.yamlDoc,
+ )
+ }
+}
+
+func TestTimeEncodersWrongYAML(t *testing.T) {
+ tests := []string{
+ "timeEncoder: [1, 2, 3]", // wrong type
+ "timeEncoder: {foo:bar", // broken yaml
+ }
+ for _, tt := range tests {
+ cfg := EncoderConfig{}
+ assert.Error(t, yaml.Unmarshal([]byte(tt), &cfg), "Expected unmarshaling %q to become error, but not.", tt)
+ }
+}
+
+func TestTimeEncodersParseFromJSON(t *testing.T) {
+ moment := time.Unix(100, 50005000).UTC()
+ tests := []struct {
+ jsonDoc string
+ expected interface{} // output of serializing moment
+ }{
+ {`{"timeEncoder": "iso8601"}`, "1970-01-01T00:01:40.050Z"},
+ {`{"timeEncoder": {"layout": "06/01/02 03:04pm"}}`, "70/01/01 12:01am"},
+ }
+
+ for _, tt := range tests {
+ cfg := EncoderConfig{}
+ require.NoError(t, json.Unmarshal([]byte(tt.jsonDoc), &cfg), "Unexpected error unmarshaling %q.", tt.jsonDoc)
+ require.NotNil(t, cfg.EncodeTime, "Unmashalled timeEncoder is nil for %q.", tt.jsonDoc)
+ assertAppended(
+ t,
+ tt.expected,
+ func(arr ArrayEncoder) { cfg.EncodeTime(moment, arr) },
+ "Unexpected output serializing %v with %q.", moment, tt.jsonDoc,
+ )
+ }
+}
+
+func TestDurationEncoders(t *testing.T) {
+ elapsed := time.Second + 500*time.Nanosecond
+ tests := []struct {
+ name string
+ expected interface{} // output of serializing elapsed
+ }{
+ {"string", "1.0000005s"},
+ {"nanos", int64(1000000500)},
+ {"ms", int64(1000)},
+ {"", 1.0000005},
+ {"something-random", 1.0000005},
+ }
+
+ for _, tt := range tests {
+ var de DurationEncoder
+ require.NoError(t, de.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name)
+ assertAppended(
+ t,
+ tt.expected,
+ func(arr ArrayEncoder) { de(elapsed, arr) },
+ "Unexpected output serializing %v with %q.", elapsed, tt.name,
+ )
+ }
+}
+
+func TestCallerEncoders(t *testing.T) {
+ caller := EntryCaller{Defined: true, File: "/home/jack/src/github.com/foo/foo.go", Line: 42}
+ tests := []struct {
+ name string
+ expected interface{} // output of serializing caller
+ }{
+ {"", "foo/foo.go:42"},
+ {"something-random", "foo/foo.go:42"},
+ {"short", "foo/foo.go:42"},
+ {"full", "/home/jack/src/github.com/foo/foo.go:42"},
+ }
+
+ for _, tt := range tests {
+ var ce CallerEncoder
+ require.NoError(t, ce.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name)
+ assertAppended(
+ t,
+ tt.expected,
+ func(arr ArrayEncoder) { ce(caller, arr) },
+ "Unexpected output serializing file name as %v with %q.", tt.expected, tt.name,
+ )
+ }
+}
+
+func TestNameEncoders(t *testing.T) {
+ tests := []struct {
+ name string
+ expected interface{} // output of encoding InfoLevel
+ }{
+ {"", "main"},
+ {"full", "main"},
+ {"something-random", "main"},
+ }
+
+ for _, tt := range tests {
+ var ne NameEncoder
+ require.NoError(t, ne.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name)
+ assertAppended(
+ t,
+ tt.expected,
+ func(arr ArrayEncoder) { ne("main", arr) },
+ "Unexpected output serializing logger name with %q.", tt.name,
+ )
+ }
+}
+
+func assertAppended(t testing.TB, expected interface{}, f func(ArrayEncoder), msgAndArgs ...interface{}) {
+ mem := NewMapObjectEncoder()
+ mem.AddArray("k", ArrayMarshalerFunc(func(arr ArrayEncoder) error {
+ f(arr)
+ return nil
+ }))
+ arr := mem.Fields["k"].([]interface{})
+ require.Equal(t, 1, len(arr), "Expected to append exactly one element to array.")
+ assert.Equal(t, expected, arr[0], msgAndArgs...)
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/entry.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/entry.go
new file mode 100644
index 0000000..ea0431e
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/entry.go
@@ -0,0 +1,299 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "fmt"
+ "runtime"
+ "strings"
+ "sync"
+ "time"
+
+ "go.uber.org/multierr"
+ "go.uber.org/zap/internal/bufferpool"
+ "go.uber.org/zap/internal/exit"
+)
+
+var (
+ _cePool = sync.Pool{New: func() interface{} {
+ // Pre-allocate some space for cores.
+ return &CheckedEntry{
+ cores: make([]Core, 4),
+ }
+ }}
+)
+
+func getCheckedEntry() *CheckedEntry {
+ ce := _cePool.Get().(*CheckedEntry)
+ ce.reset()
+ return ce
+}
+
+func putCheckedEntry(ce *CheckedEntry) {
+ if ce == nil {
+ return
+ }
+ _cePool.Put(ce)
+}
+
+// NewEntryCaller makes an EntryCaller from the return signature of
+// runtime.Caller.
+func NewEntryCaller(pc uintptr, file string, line int, ok bool) EntryCaller {
+ if !ok {
+ return EntryCaller{}
+ }
+ return EntryCaller{
+ PC: pc,
+ File: file,
+ Line: line,
+ Defined: true,
+ }
+}
+
+// EntryCaller represents the caller of a logging function.
+type EntryCaller struct {
+ Defined bool
+ PC uintptr
+ File string
+ Line int
+ Function string
+}
+
+// String returns the full path and line number of the caller.
+func (ec EntryCaller) String() string {
+ return ec.FullPath()
+}
+
+// FullPath returns a /full/path/to/package/file:line description of the
+// caller.
+func (ec EntryCaller) FullPath() string {
+ if !ec.Defined {
+ return "undefined"
+ }
+ buf := bufferpool.Get()
+ buf.AppendString(ec.File)
+ buf.AppendByte(':')
+ buf.AppendInt(int64(ec.Line))
+ caller := buf.String()
+ buf.Free()
+ return caller
+}
+
+// TrimmedPath returns a package/file:line description of the caller,
+// preserving only the leaf directory name and file name.
+func (ec EntryCaller) TrimmedPath() string {
+ if !ec.Defined {
+ return "undefined"
+ }
+ // nb. To make sure we trim the path correctly on Windows too, we
+ // counter-intuitively need to use '/' and *not* os.PathSeparator here,
+ // because the path given originates from Go stdlib, specifically
+ // runtime.Caller() which (as of Mar/17) returns forward slashes even on
+ // Windows.
+ //
+ // See https://github.com/golang/go/issues/3335
+ // and https://github.com/golang/go/issues/18151
+ //
+ // for discussion on the issue on Go side.
+ //
+ // Find the last separator.
+ //
+ idx := strings.LastIndexByte(ec.File, '/')
+ if idx == -1 {
+ return ec.FullPath()
+ }
+ // Find the penultimate separator.
+ idx = strings.LastIndexByte(ec.File[:idx], '/')
+ if idx == -1 {
+ return ec.FullPath()
+ }
+ buf := bufferpool.Get()
+ // Keep everything after the penultimate separator.
+ buf.AppendString(ec.File[idx+1:])
+ buf.AppendByte(':')
+ buf.AppendInt(int64(ec.Line))
+ caller := buf.String()
+ buf.Free()
+ return caller
+}
+
+// An Entry represents a complete log message. The entry's structured context
+// is already serialized, but the log level, time, message, and call site
+// information are available for inspection and modification. Any fields left
+// empty will be omitted when encoding.
+//
+// Entries are pooled, so any functions that accept them MUST be careful not to
+// retain references to them.
+type Entry struct {
+ Level Level
+ Time time.Time
+ LoggerName string
+ Message string
+ Caller EntryCaller
+ Stack string
+}
+
+// CheckWriteHook is a custom action that may be executed after an entry is
+// written.
+//
+// Register one on a CheckedEntry with the After method.
+//
+// if ce := logger.Check(...); ce != nil {
+// ce = ce.After(hook)
+// ce.Write(...)
+// }
+//
+// You can configure the hook for Fatal log statements at the logger level with
+// the zap.WithFatalHook option.
+type CheckWriteHook interface {
+ // OnWrite is invoked with the CheckedEntry that was written and a list
+ // of fields added with that entry.
+ //
+ // The list of fields DOES NOT include fields that were already added
+ // to the logger with the With method.
+ OnWrite(*CheckedEntry, []Field)
+}
+
+// CheckWriteAction indicates what action to take after a log entry is
+// processed. Actions are ordered in increasing severity.
+type CheckWriteAction uint8
+
+const (
+ // WriteThenNoop indicates that nothing special needs to be done. It's the
+ // default behavior.
+ WriteThenNoop CheckWriteAction = iota
+ // WriteThenGoexit runs runtime.Goexit after Write.
+ WriteThenGoexit
+ // WriteThenPanic causes a panic after Write.
+ WriteThenPanic
+ // WriteThenFatal causes an os.Exit(1) after Write.
+ WriteThenFatal
+)
+
+// OnWrite implements the OnWrite method to keep CheckWriteAction compatible
+// with the new CheckWriteHook interface which deprecates CheckWriteAction.
+func (a CheckWriteAction) OnWrite(ce *CheckedEntry, _ []Field) {
+ switch a {
+ case WriteThenGoexit:
+ runtime.Goexit()
+ case WriteThenPanic:
+ panic(ce.Message)
+ case WriteThenFatal:
+ exit.With(1)
+ }
+}
+
+var _ CheckWriteHook = CheckWriteAction(0)
+
+// CheckedEntry is an Entry together with a collection of Cores that have
+// already agreed to log it.
+//
+// CheckedEntry references should be created by calling AddCore or After on a
+// nil *CheckedEntry. References are returned to a pool after Write, and MUST
+// NOT be retained after calling their Write method.
+type CheckedEntry struct {
+ Entry
+ ErrorOutput WriteSyncer
+ dirty bool // best-effort detection of pool misuse
+ after CheckWriteHook
+ cores []Core
+}
+
+func (ce *CheckedEntry) reset() {
+ ce.Entry = Entry{}
+ ce.ErrorOutput = nil
+ ce.dirty = false
+ ce.after = nil
+ for i := range ce.cores {
+ // don't keep references to cores
+ ce.cores[i] = nil
+ }
+ ce.cores = ce.cores[:0]
+}
+
+// Write writes the entry to the stored Cores, returns any errors, and returns
+// the CheckedEntry reference to a pool for immediate re-use. Finally, it
+// executes any required CheckWriteAction.
+func (ce *CheckedEntry) Write(fields ...Field) {
+ if ce == nil {
+ return
+ }
+
+ if ce.dirty {
+ if ce.ErrorOutput != nil {
+ // Make a best effort to detect unsafe re-use of this CheckedEntry.
+ // If the entry is dirty, log an internal error; because the
+ // CheckedEntry is being used after it was returned to the pool,
+ // the message may be an amalgamation from multiple call sites.
+ fmt.Fprintf(ce.ErrorOutput, "%v Unsafe CheckedEntry re-use near Entry %+v.\n", ce.Time, ce.Entry)
+ ce.ErrorOutput.Sync()
+ }
+ return
+ }
+ ce.dirty = true
+
+ var err error
+ for i := range ce.cores {
+ err = multierr.Append(err, ce.cores[i].Write(ce.Entry, fields))
+ }
+ if err != nil && ce.ErrorOutput != nil {
+ fmt.Fprintf(ce.ErrorOutput, "%v write error: %v\n", ce.Time, err)
+ ce.ErrorOutput.Sync()
+ }
+
+ hook := ce.after
+ if hook != nil {
+ hook.OnWrite(ce, fields)
+ }
+ putCheckedEntry(ce)
+}
+
+// AddCore adds a Core that has agreed to log this CheckedEntry. It's intended to be
+// used by Core.Check implementations, and is safe to call on nil CheckedEntry
+// references.
+func (ce *CheckedEntry) AddCore(ent Entry, core Core) *CheckedEntry {
+ if ce == nil {
+ ce = getCheckedEntry()
+ ce.Entry = ent
+ }
+ ce.cores = append(ce.cores, core)
+ return ce
+}
+
+// Should sets this CheckedEntry's CheckWriteAction, which controls whether a
+// Core will panic or fatal after writing this log entry. Like AddCore, it's
+// safe to call on nil CheckedEntry references.
+// Deprecated: Use After(ent Entry, after CheckWriteHook) instead.
+func (ce *CheckedEntry) Should(ent Entry, should CheckWriteAction) *CheckedEntry {
+ return ce.After(ent, should)
+}
+
+// After sets this CheckEntry's CheckWriteHook, which will be called after this
+// log entry has been written. It's safe to call this on nil CheckedEntry
+// references.
+func (ce *CheckedEntry) After(ent Entry, hook CheckWriteHook) *CheckedEntry {
+ if ce == nil {
+ ce = getCheckedEntry()
+ ce.Entry = ent
+ }
+ ce.after = hook
+ return ce
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/entry_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/entry_test.go
new file mode 100644
index 0000000..6555ab6
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/entry_test.go
@@ -0,0 +1,149 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "sync"
+ "testing"
+
+ "go.uber.org/zap/internal/exit"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func assertGoexit(t *testing.T, f func()) {
+ var finished bool
+ recovered := make(chan interface{})
+ go func() {
+ defer func() {
+ recovered <- recover()
+ }()
+
+ f()
+ finished = true
+ }()
+
+ assert.Nil(t, <-recovered, "Goexit should cause recover to return nil")
+ assert.False(t, finished, "Goroutine should not finish after Goexit")
+}
+
+func TestPutNilEntry(t *testing.T) {
+ // Pooling nil entries defeats the purpose.
+ var wg sync.WaitGroup
+ wg.Add(2)
+
+ go func() {
+ defer wg.Done()
+ for i := 0; i < 1000; i++ {
+ putCheckedEntry(nil)
+ }
+ }()
+
+ go func() {
+ defer wg.Done()
+ for i := 0; i < 1000; i++ {
+ ce := getCheckedEntry()
+ assert.NotNil(t, ce, "Expected only non-nil CheckedEntries in pool.")
+ assert.False(t, ce.dirty, "Unexpected dirty bit set.")
+ assert.Nil(t, ce.ErrorOutput, "Non-nil ErrorOutput.")
+ assert.Nil(t, ce.after, "Unexpected terminal behavior.")
+ assert.Equal(t, 0, len(ce.cores), "Expected empty slice of cores.")
+ assert.True(t, cap(ce.cores) > 0, "Expected pooled CheckedEntries to pre-allocate slice of Cores.")
+ }
+ }()
+
+ wg.Wait()
+}
+
+func TestEntryCaller(t *testing.T) {
+ tests := []struct {
+ caller EntryCaller
+ full string
+ short string
+ }{
+ {
+ caller: NewEntryCaller(100, "/path/to/foo.go", 42, false),
+ full: "undefined",
+ short: "undefined",
+ },
+ {
+ caller: NewEntryCaller(100, "/path/to/foo.go", 42, true),
+ full: "/path/to/foo.go:42",
+ short: "to/foo.go:42",
+ },
+ {
+ caller: NewEntryCaller(100, "to/foo.go", 42, true),
+ full: "to/foo.go:42",
+ short: "to/foo.go:42",
+ },
+ }
+
+ for _, tt := range tests {
+ assert.Equal(t, tt.full, tt.caller.String(), "Unexpected string from EntryCaller.")
+ assert.Equal(t, tt.full, tt.caller.FullPath(), "Unexpected FullPath from EntryCaller.")
+ assert.Equal(t, tt.short, tt.caller.TrimmedPath(), "Unexpected TrimmedPath from EntryCaller.")
+ }
+}
+
+func TestCheckedEntryWrite(t *testing.T) {
+ t.Run("nil is safe", func(t *testing.T) {
+ var ce *CheckedEntry
+ assert.NotPanics(t, func() { ce.Write() }, "Unexpected panic writing nil CheckedEntry.")
+ })
+
+ t.Run("WriteThenPanic", func(t *testing.T) {
+ var ce *CheckedEntry
+ ce = ce.After(Entry{}, WriteThenPanic)
+ assert.Panics(t, func() { ce.Write() }, "Expected to panic when WriteThenPanic is set.")
+ })
+
+ t.Run("WriteThenGoexit", func(t *testing.T) {
+ var ce *CheckedEntry
+ ce = ce.After(Entry{}, WriteThenGoexit)
+ assertGoexit(t, func() { ce.Write() })
+ })
+
+ t.Run("WriteThenFatal", func(t *testing.T) {
+ var ce *CheckedEntry
+ ce = ce.After(Entry{}, WriteThenFatal)
+ stub := exit.WithStub(func() {
+ ce.Write()
+ })
+ assert.True(t, stub.Exited, "Expected to exit when WriteThenFatal is set.")
+ assert.Equal(t, 1, stub.Code, "Expected to exit when WriteThenFatal is set.")
+ })
+
+ t.Run("After", func(t *testing.T) {
+ var ce *CheckedEntry
+ hook := &customHook{}
+ ce = ce.After(Entry{}, hook)
+ ce.Write()
+ assert.True(t, hook.called, "Expected to call custom action after Write.")
+ })
+}
+
+type customHook struct {
+ called bool
+}
+
+func (c *customHook) OnWrite(_ *CheckedEntry, _ []Field) {
+ c.called = true
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/error.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/error.go
new file mode 100644
index 0000000..0635990
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/error.go
@@ -0,0 +1,132 @@
+// Copyright (c) 2017 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "fmt"
+ "reflect"
+ "sync"
+)
+
+// Encodes the given error into fields of an object. A field with the given
+// name is added for the error message.
+//
+// If the error implements fmt.Formatter, a field with the name ${key}Verbose
+// is also added with the full verbose error message.
+//
+// Finally, if the error implements errorGroup (from go.uber.org/multierr) or
+// causer (from github.com/pkg/errors), a ${key}Causes field is added with an
+// array of objects containing the errors this error was comprised of.
+//
+// {
+// "error": err.Error(),
+// "errorVerbose": fmt.Sprintf("%+v", err),
+// "errorCauses": [
+// ...
+// ],
+// }
+func encodeError(key string, err error, enc ObjectEncoder) (retErr error) {
+ // Try to capture panics (from nil references or otherwise) when calling
+ // the Error() method
+ defer func() {
+ if rerr := recover(); rerr != nil {
+ // If it's a nil pointer, just say "<nil>". The likeliest causes are a
+ // error that fails to guard against nil or a nil pointer for a
+ // value receiver, and in either case, "<nil>" is a nice result.
+ if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() {
+ enc.AddString(key, "<nil>")
+ return
+ }
+
+ retErr = fmt.Errorf("PANIC=%v", rerr)
+ }
+ }()
+
+ basic := err.Error()
+ enc.AddString(key, basic)
+
+ switch e := err.(type) {
+ case errorGroup:
+ return enc.AddArray(key+"Causes", errArray(e.Errors()))
+ case fmt.Formatter:
+ verbose := fmt.Sprintf("%+v", e)
+ if verbose != basic {
+ // This is a rich error type, like those produced by
+ // github.com/pkg/errors.
+ enc.AddString(key+"Verbose", verbose)
+ }
+ }
+ return nil
+}
+
+type errorGroup interface {
+ // Provides read-only access to the underlying list of errors, preferably
+ // without causing any allocs.
+ Errors() []error
+}
+
+// Note that errArray and errArrayElem are very similar to the version
+// implemented in the top-level error.go file. We can't re-use this because
+// that would require exporting errArray as part of the zapcore API.
+
+// Encodes a list of errors using the standard error encoding logic.
+type errArray []error
+
+func (errs errArray) MarshalLogArray(arr ArrayEncoder) error {
+ for i := range errs {
+ if errs[i] == nil {
+ continue
+ }
+
+ el := newErrArrayElem(errs[i])
+ arr.AppendObject(el)
+ el.Free()
+ }
+ return nil
+}
+
+var _errArrayElemPool = sync.Pool{New: func() interface{} {
+ return &errArrayElem{}
+}}
+
+// Encodes any error into a {"error": ...} re-using the same errors logic.
+//
+// May be passed in place of an array to build a single-element array.
+type errArrayElem struct{ err error }
+
+func newErrArrayElem(err error) *errArrayElem {
+ e := _errArrayElemPool.Get().(*errArrayElem)
+ e.err = err
+ return e
+}
+
+func (e *errArrayElem) MarshalLogArray(arr ArrayEncoder) error {
+ return arr.AppendObject(e)
+}
+
+func (e *errArrayElem) MarshalLogObject(enc ObjectEncoder) error {
+ return encodeError("error", e.err, enc)
+}
+
+func (e *errArrayElem) Free() {
+ e.err = nil
+ _errArrayElemPool.Put(e)
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/error_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/error_test.go
new file mode 100644
index 0000000..900f520
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/error_test.go
@@ -0,0 +1,177 @@
+// Copyright (c) 2017 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "testing"
+
+ richErrors "github.com/pkg/errors"
+ "github.com/stretchr/testify/assert"
+
+ "go.uber.org/multierr"
+ . "go.uber.org/zap/zapcore"
+)
+
+type errTooManyUsers int
+
+func (e errTooManyUsers) Error() string {
+ return fmt.Sprintf("%d too many users", int(e))
+}
+
+func (e errTooManyUsers) Format(s fmt.State, verb rune) {
+ // Implement fmt.Formatter, but don't add any information beyond the basic
+ // Error method.
+ if verb == 'v' && s.Flag('+') {
+ io.WriteString(s, e.Error())
+ }
+}
+
+type customMultierr struct{}
+
+func (e customMultierr) Error() string {
+ return "great sadness"
+}
+
+func (e customMultierr) Errors() []error {
+ return []error{
+ errors.New("foo"),
+ nil,
+ multierr.Append(
+ errors.New("bar"),
+ errors.New("baz"),
+ ),
+ }
+}
+
+func TestErrorEncoding(t *testing.T) {
+ tests := []struct {
+ k string
+ t FieldType // defaults to ErrorType
+ iface interface{}
+ want map[string]interface{}
+ }{
+ {
+ k: "k",
+ iface: errTooManyUsers(2),
+ want: map[string]interface{}{
+ "k": "2 too many users",
+ },
+ },
+ {
+ k: "err",
+ iface: multierr.Combine(
+ errors.New("foo"),
+ errors.New("bar"),
+ errors.New("baz"),
+ ),
+ want: map[string]interface{}{
+ "err": "foo; bar; baz",
+ "errCauses": []interface{}{
+ map[string]interface{}{"error": "foo"},
+ map[string]interface{}{"error": "bar"},
+ map[string]interface{}{"error": "baz"},
+ },
+ },
+ },
+ {
+ k: "e",
+ iface: customMultierr{},
+ want: map[string]interface{}{
+ "e": "great sadness",
+ "eCauses": []interface{}{
+ map[string]interface{}{"error": "foo"},
+ map[string]interface{}{
+ "error": "bar; baz",
+ "errorCauses": []interface{}{
+ map[string]interface{}{"error": "bar"},
+ map[string]interface{}{"error": "baz"},
+ },
+ },
+ },
+ },
+ },
+ {
+ k: "k",
+ iface: richErrors.WithMessage(errors.New("egad"), "failed"),
+ want: map[string]interface{}{
+ "k": "failed: egad",
+ "kVerbose": "egad\nfailed",
+ },
+ },
+ {
+ k: "error",
+ iface: multierr.Combine(
+ richErrors.WithMessage(
+ multierr.Combine(errors.New("foo"), errors.New("bar")),
+ "hello",
+ ),
+ errors.New("baz"),
+ richErrors.WithMessage(errors.New("qux"), "world"),
+ ),
+ want: map[string]interface{}{
+ "error": "hello: foo; bar; baz; world: qux",
+ "errorCauses": []interface{}{
+ map[string]interface{}{
+ "error": "hello: foo; bar",
+ "errorVerbose": "the following errors occurred:\n" +
+ " - foo\n" +
+ " - bar\n" +
+ "hello",
+ },
+ map[string]interface{}{"error": "baz"},
+ map[string]interface{}{"error": "world: qux", "errorVerbose": "qux\nworld"},
+ },
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ if tt.t == UnknownType {
+ tt.t = ErrorType
+ }
+
+ enc := NewMapObjectEncoder()
+ f := Field{Key: tt.k, Type: tt.t, Interface: tt.iface}
+ f.AddTo(enc)
+ assert.Equal(t, tt.want, enc.Fields, "Unexpected output from field %+v.", f)
+ }
+}
+
+func TestRichErrorSupport(t *testing.T) {
+ f := Field{
+ Type: ErrorType,
+ Interface: richErrors.WithMessage(richErrors.New("egad"), "failed"),
+ Key: "k",
+ }
+ enc := NewMapObjectEncoder()
+ f.AddTo(enc)
+ assert.Equal(t, "failed: egad", enc.Fields["k"], "Unexpected basic error message.")
+
+ serialized := enc.Fields["kVerbose"]
+ // Don't assert the exact format used by a third-party package, but ensure
+ // that some critical elements are present.
+ assert.Regexp(t, `egad`, serialized, "Expected original error message to be present.")
+ assert.Regexp(t, `failed`, serialized, "Expected error annotation to be present.")
+ assert.Regexp(t, `TestRichErrorSupport`, serialized, "Expected calling function to be present in stacktrace.")
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/field.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/field.go
new file mode 100644
index 0000000..95bdb0a
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/field.go
@@ -0,0 +1,233 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "bytes"
+ "fmt"
+ "math"
+ "reflect"
+ "time"
+)
+
+// A FieldType indicates which member of the Field union struct should be used
+// and how it should be serialized.
+type FieldType uint8
+
+const (
+ // UnknownType is the default field type. Attempting to add it to an encoder will panic.
+ UnknownType FieldType = iota
+ // ArrayMarshalerType indicates that the field carries an ArrayMarshaler.
+ ArrayMarshalerType
+ // ObjectMarshalerType indicates that the field carries an ObjectMarshaler.
+ ObjectMarshalerType
+ // BinaryType indicates that the field carries an opaque binary blob.
+ BinaryType
+ // BoolType indicates that the field carries a bool.
+ BoolType
+ // ByteStringType indicates that the field carries UTF-8 encoded bytes.
+ ByteStringType
+ // Complex128Type indicates that the field carries a complex128.
+ Complex128Type
+ // Complex64Type indicates that the field carries a complex128.
+ Complex64Type
+ // DurationType indicates that the field carries a time.Duration.
+ DurationType
+ // Float64Type indicates that the field carries a float64.
+ Float64Type
+ // Float32Type indicates that the field carries a float32.
+ Float32Type
+ // Int64Type indicates that the field carries an int64.
+ Int64Type
+ // Int32Type indicates that the field carries an int32.
+ Int32Type
+ // Int16Type indicates that the field carries an int16.
+ Int16Type
+ // Int8Type indicates that the field carries an int8.
+ Int8Type
+ // StringType indicates that the field carries a string.
+ StringType
+ // TimeType indicates that the field carries a time.Time that is
+ // representable by a UnixNano() stored as an int64.
+ TimeType
+ // TimeFullType indicates that the field carries a time.Time stored as-is.
+ TimeFullType
+ // Uint64Type indicates that the field carries a uint64.
+ Uint64Type
+ // Uint32Type indicates that the field carries a uint32.
+ Uint32Type
+ // Uint16Type indicates that the field carries a uint16.
+ Uint16Type
+ // Uint8Type indicates that the field carries a uint8.
+ Uint8Type
+ // UintptrType indicates that the field carries a uintptr.
+ UintptrType
+ // ReflectType indicates that the field carries an interface{}, which should
+ // be serialized using reflection.
+ ReflectType
+ // NamespaceType signals the beginning of an isolated namespace. All
+ // subsequent fields should be added to the new namespace.
+ NamespaceType
+ // StringerType indicates that the field carries a fmt.Stringer.
+ StringerType
+ // ErrorType indicates that the field carries an error.
+ ErrorType
+ // SkipType indicates that the field is a no-op.
+ SkipType
+
+ // InlineMarshalerType indicates that the field carries an ObjectMarshaler
+ // that should be inlined.
+ InlineMarshalerType
+)
+
+// A Field is a marshaling operation used to add a key-value pair to a logger's
+// context. Most fields are lazily marshaled, so it's inexpensive to add fields
+// to disabled debug-level log statements.
+type Field struct {
+ Key string
+ Type FieldType
+ Integer int64
+ String string
+ Interface interface{}
+}
+
+// AddTo exports a field through the ObjectEncoder interface. It's primarily
+// useful to library authors, and shouldn't be necessary in most applications.
+func (f Field) AddTo(enc ObjectEncoder) {
+ var err error
+
+ switch f.Type {
+ case ArrayMarshalerType:
+ err = enc.AddArray(f.Key, f.Interface.(ArrayMarshaler))
+ case ObjectMarshalerType:
+ err = enc.AddObject(f.Key, f.Interface.(ObjectMarshaler))
+ case InlineMarshalerType:
+ err = f.Interface.(ObjectMarshaler).MarshalLogObject(enc)
+ case BinaryType:
+ enc.AddBinary(f.Key, f.Interface.([]byte))
+ case BoolType:
+ enc.AddBool(f.Key, f.Integer == 1)
+ case ByteStringType:
+ enc.AddByteString(f.Key, f.Interface.([]byte))
+ case Complex128Type:
+ enc.AddComplex128(f.Key, f.Interface.(complex128))
+ case Complex64Type:
+ enc.AddComplex64(f.Key, f.Interface.(complex64))
+ case DurationType:
+ enc.AddDuration(f.Key, time.Duration(f.Integer))
+ case Float64Type:
+ enc.AddFloat64(f.Key, math.Float64frombits(uint64(f.Integer)))
+ case Float32Type:
+ enc.AddFloat32(f.Key, math.Float32frombits(uint32(f.Integer)))
+ case Int64Type:
+ enc.AddInt64(f.Key, f.Integer)
+ case Int32Type:
+ enc.AddInt32(f.Key, int32(f.Integer))
+ case Int16Type:
+ enc.AddInt16(f.Key, int16(f.Integer))
+ case Int8Type:
+ enc.AddInt8(f.Key, int8(f.Integer))
+ case StringType:
+ enc.AddString(f.Key, f.String)
+ case TimeType:
+ if f.Interface != nil {
+ enc.AddTime(f.Key, time.Unix(0, f.Integer).In(f.Interface.(*time.Location)))
+ } else {
+ // Fall back to UTC if location is nil.
+ enc.AddTime(f.Key, time.Unix(0, f.Integer))
+ }
+ case TimeFullType:
+ enc.AddTime(f.Key, f.Interface.(time.Time))
+ case Uint64Type:
+ enc.AddUint64(f.Key, uint64(f.Integer))
+ case Uint32Type:
+ enc.AddUint32(f.Key, uint32(f.Integer))
+ case Uint16Type:
+ enc.AddUint16(f.Key, uint16(f.Integer))
+ case Uint8Type:
+ enc.AddUint8(f.Key, uint8(f.Integer))
+ case UintptrType:
+ enc.AddUintptr(f.Key, uintptr(f.Integer))
+ case ReflectType:
+ err = enc.AddReflected(f.Key, f.Interface)
+ case NamespaceType:
+ enc.OpenNamespace(f.Key)
+ case StringerType:
+ err = encodeStringer(f.Key, f.Interface, enc)
+ case ErrorType:
+ err = encodeError(f.Key, f.Interface.(error), enc)
+ case SkipType:
+ break
+ default:
+ panic(fmt.Sprintf("unknown field type: %v", f))
+ }
+
+ if err != nil {
+ enc.AddString(fmt.Sprintf("%sError", f.Key), err.Error())
+ }
+}
+
+// Equals returns whether two fields are equal. For non-primitive types such as
+// errors, marshalers, or reflect types, it uses reflect.DeepEqual.
+func (f Field) Equals(other Field) bool {
+ if f.Type != other.Type {
+ return false
+ }
+ if f.Key != other.Key {
+ return false
+ }
+
+ switch f.Type {
+ case BinaryType, ByteStringType:
+ return bytes.Equal(f.Interface.([]byte), other.Interface.([]byte))
+ case ArrayMarshalerType, ObjectMarshalerType, ErrorType, ReflectType:
+ return reflect.DeepEqual(f.Interface, other.Interface)
+ default:
+ return f == other
+ }
+}
+
+func addFields(enc ObjectEncoder, fields []Field) {
+ for i := range fields {
+ fields[i].AddTo(enc)
+ }
+}
+
+func encodeStringer(key string, stringer interface{}, enc ObjectEncoder) (retErr error) {
+ // Try to capture panics (from nil references or otherwise) when calling
+ // the String() method, similar to https://golang.org/src/fmt/print.go#L540
+ defer func() {
+ if err := recover(); err != nil {
+ // If it's a nil pointer, just say "<nil>". The likeliest causes are a
+ // Stringer that fails to guard against nil or a nil pointer for a
+ // value receiver, and in either case, "<nil>" is a nice result.
+ if v := reflect.ValueOf(stringer); v.Kind() == reflect.Ptr && v.IsNil() {
+ enc.AddString(key, "<nil>")
+ return
+ }
+
+ retErr = fmt.Errorf("PANIC=%v", err)
+ }
+ }()
+
+ enc.AddString(key, stringer.(fmt.Stringer).String())
+ return nil
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/field_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/field_test.go
new file mode 100644
index 0000000..c436329
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/field_test.go
@@ -0,0 +1,318 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "errors"
+ "fmt"
+ "math"
+ "net/url"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "go.uber.org/zap"
+ . "go.uber.org/zap/zapcore"
+)
+
+type users int
+
+func (u users) String() string {
+ return fmt.Sprintf("%d users", int(u))
+}
+
+func (u users) MarshalLogObject(enc ObjectEncoder) error {
+ if int(u) < 0 {
+ return errors.New("too few users")
+ }
+ enc.AddInt("users", int(u))
+ return nil
+}
+
+func (u users) MarshalLogArray(enc ArrayEncoder) error {
+ if int(u) < 0 {
+ return errors.New("too few users")
+ }
+ for i := 0; i < int(u); i++ {
+ enc.AppendString("user")
+ }
+ return nil
+}
+
+type obj struct {
+ kind int
+}
+
+func (o *obj) String() string {
+ if o == nil {
+ return "nil obj"
+ }
+
+ if o.kind == 1 {
+ panic("panic with string")
+ } else if o.kind == 2 {
+ panic(errors.New("panic with error"))
+ } else if o.kind == 3 {
+ // panic with an arbitrary object that causes a panic itself
+ // when being converted to a string
+ panic((*url.URL)(nil))
+ }
+
+ return "obj"
+}
+
+type errObj struct {
+ kind int
+ errMsg string
+}
+
+func (eobj *errObj) Error() string {
+ if eobj.kind == 1 {
+ panic("panic in Error() method")
+ } else {
+ return eobj.errMsg
+ }
+}
+
+func TestUnknownFieldType(t *testing.T) {
+ unknown := Field{Key: "k", String: "foo"}
+ assert.Equal(t, UnknownType, unknown.Type, "Expected zero value of FieldType to be UnknownType.")
+ assert.Panics(t, func() {
+ unknown.AddTo(NewMapObjectEncoder())
+ }, "Expected using a field with unknown type to panic.")
+}
+
+func TestFieldAddingError(t *testing.T) {
+ var empty interface{}
+ tests := []struct {
+ t FieldType
+ iface interface{}
+ want interface{}
+ err string
+ }{
+ {t: ArrayMarshalerType, iface: users(-1), want: []interface{}{}, err: "too few users"},
+ {t: ObjectMarshalerType, iface: users(-1), want: map[string]interface{}{}, err: "too few users"},
+ {t: InlineMarshalerType, iface: users(-1), want: nil, err: "too few users"},
+ {t: StringerType, iface: obj{}, want: empty, err: "PANIC=interface conversion: zapcore_test.obj is not fmt.Stringer: missing method String"},
+ {t: StringerType, iface: &obj{1}, want: empty, err: "PANIC=panic with string"},
+ {t: StringerType, iface: &obj{2}, want: empty, err: "PANIC=panic with error"},
+ {t: StringerType, iface: &obj{3}, want: empty, err: "PANIC=<nil>"},
+ {t: ErrorType, iface: &errObj{kind: 1}, want: empty, err: "PANIC=panic in Error() method"},
+ }
+ for _, tt := range tests {
+ f := Field{Key: "k", Interface: tt.iface, Type: tt.t}
+ enc := NewMapObjectEncoder()
+ assert.NotPanics(t, func() { f.AddTo(enc) }, "Unexpected panic when adding fields returns an error.")
+ assert.Equal(t, tt.want, enc.Fields["k"], "On error, expected zero value in field.Key.")
+ assert.Equal(t, tt.err, enc.Fields["kError"], "Expected error message in log context.")
+ }
+}
+
+func TestFields(t *testing.T) {
+ tests := []struct {
+ t FieldType
+ i int64
+ s string
+ iface interface{}
+ want interface{}
+ }{
+ {t: ArrayMarshalerType, iface: users(2), want: []interface{}{"user", "user"}},
+ {t: ObjectMarshalerType, iface: users(2), want: map[string]interface{}{"users": 2}},
+ {t: BoolType, i: 0, want: false},
+ {t: ByteStringType, iface: []byte("foo"), want: "foo"},
+ {t: Complex128Type, iface: 1 + 2i, want: 1 + 2i},
+ {t: Complex64Type, iface: complex64(1 + 2i), want: complex64(1 + 2i)},
+ {t: DurationType, i: 1000, want: time.Microsecond},
+ {t: Float64Type, i: int64(math.Float64bits(3.14)), want: 3.14},
+ {t: Float32Type, i: int64(math.Float32bits(3.14)), want: float32(3.14)},
+ {t: Int64Type, i: 42, want: int64(42)},
+ {t: Int32Type, i: 42, want: int32(42)},
+ {t: Int16Type, i: 42, want: int16(42)},
+ {t: Int8Type, i: 42, want: int8(42)},
+ {t: StringType, s: "foo", want: "foo"},
+ {t: TimeType, i: 1000, iface: time.UTC, want: time.Unix(0, 1000).In(time.UTC)},
+ {t: TimeType, i: 1000, want: time.Unix(0, 1000)},
+ {t: Uint64Type, i: 42, want: uint64(42)},
+ {t: Uint32Type, i: 42, want: uint32(42)},
+ {t: Uint16Type, i: 42, want: uint16(42)},
+ {t: Uint8Type, i: 42, want: uint8(42)},
+ {t: UintptrType, i: 42, want: uintptr(42)},
+ {t: ReflectType, iface: users(2), want: users(2)},
+ {t: NamespaceType, want: map[string]interface{}{}},
+ {t: StringerType, iface: users(2), want: "2 users"},
+ {t: StringerType, iface: &obj{}, want: "obj"},
+ {t: StringerType, iface: (*obj)(nil), want: "nil obj"},
+ {t: SkipType, want: interface{}(nil)},
+ {t: StringerType, iface: (*url.URL)(nil), want: "<nil>"},
+ {t: StringerType, iface: (*users)(nil), want: "<nil>"},
+ {t: ErrorType, iface: (*errObj)(nil), want: "<nil>"},
+ }
+
+ for _, tt := range tests {
+ enc := NewMapObjectEncoder()
+ f := Field{Key: "k", Type: tt.t, Integer: tt.i, Interface: tt.iface, String: tt.s}
+ f.AddTo(enc)
+ assert.Equal(t, tt.want, enc.Fields["k"], "Unexpected output from field %+v.", f)
+
+ delete(enc.Fields, "k")
+ assert.Equal(t, 0, len(enc.Fields), "Unexpected extra fields present.")
+
+ assert.True(t, f.Equals(f), "Field does not equal itself")
+ }
+}
+
+func TestInlineMarshaler(t *testing.T) {
+ enc := NewMapObjectEncoder()
+
+ topLevelStr := Field{Key: "k", Type: StringType, String: "s"}
+ topLevelStr.AddTo(enc)
+
+ inlineObj := Field{Key: "ignored", Type: InlineMarshalerType, Interface: users(10)}
+ inlineObj.AddTo(enc)
+
+ nestedObj := Field{Key: "nested", Type: ObjectMarshalerType, Interface: users(11)}
+ nestedObj.AddTo(enc)
+
+ assert.Equal(t, map[string]interface{}{
+ "k": "s",
+ "users": 10,
+ "nested": map[string]interface{}{
+ "users": 11,
+ },
+ }, enc.Fields)
+}
+
+func TestEquals(t *testing.T) {
+ // Values outside the UnixNano range were encoded incorrectly (#737, #803).
+ timeOutOfRangeHigh := time.Unix(0, math.MaxInt64).Add(time.Nanosecond)
+ timeOutOfRangeLow := time.Unix(0, math.MinInt64).Add(-time.Nanosecond)
+ timeOutOfRangeHighNano := time.Unix(0, timeOutOfRangeHigh.UnixNano())
+ timeOutOfRangeLowNano := time.Unix(0, timeOutOfRangeLow.UnixNano())
+ require.False(t, timeOutOfRangeHigh.Equal(timeOutOfRangeHighNano), "should be different as value is > UnixNano range")
+ require.False(t, timeOutOfRangeHigh.Equal(timeOutOfRangeHighNano), "should be different as value is < UnixNano range")
+
+ tests := []struct {
+ a, b Field
+ want bool
+ }{
+ {
+ a: zap.Int16("a", 1),
+ b: zap.Int32("a", 1),
+ want: false,
+ },
+ {
+ a: zap.String("k", "a"),
+ b: zap.String("k", "a"),
+ want: true,
+ },
+ {
+ a: zap.String("k", "a"),
+ b: zap.String("k2", "a"),
+ want: false,
+ },
+ {
+ a: zap.String("k", "a"),
+ b: zap.String("k", "b"),
+ want: false,
+ },
+ {
+ a: zap.Time("k", time.Unix(1000, 1000)),
+ b: zap.Time("k", time.Unix(1000, 1000)),
+ want: true,
+ },
+ {
+ a: zap.Time("k", time.Unix(1000, 1000).In(time.UTC)),
+ b: zap.Time("k", time.Unix(1000, 1000).In(time.FixedZone("TEST", -8))),
+ want: false,
+ },
+ {
+ a: zap.Time("k", timeOutOfRangeLow),
+ b: zap.Time("k", timeOutOfRangeLowNano),
+ want: false,
+ },
+ {
+ a: zap.Time("k", timeOutOfRangeHigh),
+ b: zap.Time("k", timeOutOfRangeHighNano),
+ want: false,
+ },
+ {
+ a: zap.Time("k", time.Unix(1000, 1000)),
+ b: zap.Time("k", time.Unix(1000, 2000)),
+ want: false,
+ },
+ {
+ a: zap.Binary("k", []byte{1, 2}),
+ b: zap.Binary("k", []byte{1, 2}),
+ want: true,
+ },
+ {
+ a: zap.Binary("k", []byte{1, 2}),
+ b: zap.Binary("k", []byte{1, 3}),
+ want: false,
+ },
+ {
+ a: zap.ByteString("k", []byte("abc")),
+ b: zap.ByteString("k", []byte("abc")),
+ want: true,
+ },
+ {
+ a: zap.ByteString("k", []byte("abc")),
+ b: zap.ByteString("k", []byte("abd")),
+ want: false,
+ },
+ {
+ a: zap.Ints("k", []int{1, 2}),
+ b: zap.Ints("k", []int{1, 2}),
+ want: true,
+ },
+ {
+ a: zap.Ints("k", []int{1, 2}),
+ b: zap.Ints("k", []int{1, 3}),
+ want: false,
+ },
+ {
+ a: zap.Object("k", users(10)),
+ b: zap.Object("k", users(10)),
+ want: true,
+ },
+ {
+ a: zap.Object("k", users(10)),
+ b: zap.Object("k", users(20)),
+ want: false,
+ },
+ {
+ a: zap.Any("k", map[string]string{"a": "b"}),
+ b: zap.Any("k", map[string]string{"a": "b"}),
+ want: true,
+ },
+ {
+ a: zap.Any("k", map[string]string{"a": "b"}),
+ b: zap.Any("k", map[string]string{"a": "d"}),
+ want: false,
+ },
+ }
+
+ for _, tt := range tests {
+ assert.Equal(t, tt.want, tt.a.Equals(tt.b), "a.Equals(b) a: %#v b: %#v", tt.a, tt.b)
+ assert.Equal(t, tt.want, tt.b.Equals(tt.a), "b.Equals(a) a: %#v b: %#v", tt.a, tt.b)
+ }
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/hook.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/hook.go
new file mode 100644
index 0000000..198def9
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/hook.go
@@ -0,0 +1,77 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import "go.uber.org/multierr"
+
+type hooked struct {
+ Core
+ funcs []func(Entry) error
+}
+
+var (
+ _ Core = (*hooked)(nil)
+ _ leveledEnabler = (*hooked)(nil)
+)
+
+// RegisterHooks wraps a Core and runs a collection of user-defined callback
+// hooks each time a message is logged. Execution of the callbacks is blocking.
+//
+// This offers users an easy way to register simple callbacks (e.g., metrics
+// collection) without implementing the full Core interface.
+func RegisterHooks(core Core, hooks ...func(Entry) error) Core {
+ funcs := append([]func(Entry) error{}, hooks...)
+ return &hooked{
+ Core: core,
+ funcs: funcs,
+ }
+}
+
+func (h *hooked) Level() Level {
+ return LevelOf(h.Core)
+}
+
+func (h *hooked) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
+ // Let the wrapped Core decide whether to log this message or not. This
+ // also gives the downstream a chance to register itself directly with the
+ // CheckedEntry.
+ if downstream := h.Core.Check(ent, ce); downstream != nil {
+ return downstream.AddCore(ent, h)
+ }
+ return ce
+}
+
+func (h *hooked) With(fields []Field) Core {
+ return &hooked{
+ Core: h.Core.With(fields),
+ funcs: h.funcs,
+ }
+}
+
+func (h *hooked) Write(ent Entry, _ []Field) error {
+ // Since our downstream had a chance to register itself directly with the
+ // CheckedMessage, we don't need to call it here.
+ var err error
+ for i := range h.funcs {
+ err = multierr.Append(err, h.funcs[i](ent))
+ }
+ return err
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/hook_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/hook_test.go
new file mode 100644
index 0000000..46e3c35
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/hook_test.go
@@ -0,0 +1,82 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "testing"
+
+ . "go.uber.org/zap/zapcore"
+ "go.uber.org/zap/zaptest/observer"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestHooks(t *testing.T) {
+ tests := []struct {
+ entryLevel Level
+ coreLevel Level
+ expectCall bool
+ }{
+ {DebugLevel, InfoLevel, false},
+ {InfoLevel, InfoLevel, true},
+ {WarnLevel, InfoLevel, true},
+ }
+
+ for _, tt := range tests {
+ fac, logs := observer.New(tt.coreLevel)
+
+ // sanity check
+ require.Equal(t, tt.coreLevel, LevelOf(fac), "Original logger has the wrong level")
+
+ intField := makeInt64Field("foo", 42)
+ ent := Entry{Message: "bar", Level: tt.entryLevel}
+
+ var called int
+ f := func(e Entry) error {
+ called++
+ assert.Equal(t, ent, e, "Hook called with unexpected Entry.")
+ return nil
+ }
+
+ h := RegisterHooks(fac, f)
+ if ce := h.With([]Field{intField}).Check(ent, nil); ce != nil {
+ ce.Write()
+ }
+
+ t.Run("LevelOf", func(t *testing.T) {
+ assert.Equal(t, tt.coreLevel, LevelOf(h), "Wrapped logger has the wrong log level")
+ })
+
+ if tt.expectCall {
+ assert.Equal(t, 1, called, "Expected to call hook once.")
+ assert.Equal(
+ t,
+ []observer.LoggedEntry{{Entry: ent, Context: []Field{intField}}},
+ logs.AllUntimed(),
+ "Unexpected logs written out.",
+ )
+ } else {
+ assert.Equal(t, 0, called, "Didn't expect to call hook.")
+ assert.Equal(t, 0, logs.Len(), "Unexpected logs written out.")
+ }
+ }
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/increase_level.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/increase_level.go
new file mode 100644
index 0000000..7a11237
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/increase_level.go
@@ -0,0 +1,75 @@
+// Copyright (c) 2020 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import "fmt"
+
+type levelFilterCore struct {
+ core Core
+ level LevelEnabler
+}
+
+var (
+ _ Core = (*levelFilterCore)(nil)
+ _ leveledEnabler = (*levelFilterCore)(nil)
+)
+
+// NewIncreaseLevelCore creates a core that can be used to increase the level of
+// an existing Core. It cannot be used to decrease the logging level, as it acts
+// as a filter before calling the underlying core. If level decreases the log level,
+// an error is returned.
+func NewIncreaseLevelCore(core Core, level LevelEnabler) (Core, error) {
+ for l := _maxLevel; l >= _minLevel; l-- {
+ if !core.Enabled(l) && level.Enabled(l) {
+ return nil, fmt.Errorf("invalid increase level, as level %q is allowed by increased level, but not by existing core", l)
+ }
+ }
+
+ return &levelFilterCore{core, level}, nil
+}
+
+func (c *levelFilterCore) Enabled(lvl Level) bool {
+ return c.level.Enabled(lvl)
+}
+
+func (c *levelFilterCore) Level() Level {
+ return LevelOf(c.level)
+}
+
+func (c *levelFilterCore) With(fields []Field) Core {
+ return &levelFilterCore{c.core.With(fields), c.level}
+}
+
+func (c *levelFilterCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
+ if !c.Enabled(ent.Level) {
+ return ce
+ }
+
+ return c.core.Check(ent, ce)
+}
+
+func (c *levelFilterCore) Write(ent Entry, fields []Field) error {
+ return c.core.Write(ent, fields)
+}
+
+func (c *levelFilterCore) Sync() error {
+ return c.core.Sync()
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/increase_level_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/increase_level_test.go
new file mode 100644
index 0000000..547794f
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/increase_level_test.go
@@ -0,0 +1,129 @@
+// Copyright (c) 2020 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "go.uber.org/zap"
+ . "go.uber.org/zap/zapcore"
+ "go.uber.org/zap/zaptest/observer"
+)
+
+func TestIncreaseLevel(t *testing.T) {
+ tests := []struct {
+ coreLevel Level
+ increaseLevel Level
+ wantErr bool
+ with []Field
+ }{
+ {
+ coreLevel: InfoLevel,
+ increaseLevel: DebugLevel,
+ wantErr: true,
+ },
+ {
+ coreLevel: InfoLevel,
+ increaseLevel: InfoLevel,
+ },
+ {
+ coreLevel: InfoLevel,
+ increaseLevel: ErrorLevel,
+ },
+ {
+ coreLevel: InfoLevel,
+ increaseLevel: ErrorLevel,
+ with: []Field{zap.String("k", "v")},
+ },
+ {
+ coreLevel: ErrorLevel,
+ increaseLevel: DebugLevel,
+ wantErr: true,
+ },
+ {
+ coreLevel: ErrorLevel,
+ increaseLevel: InfoLevel,
+ wantErr: true,
+ },
+ {
+ coreLevel: ErrorLevel,
+ increaseLevel: WarnLevel,
+ wantErr: true,
+ },
+ {
+ coreLevel: ErrorLevel,
+ increaseLevel: PanicLevel,
+ },
+ }
+
+ for _, tt := range tests {
+ msg := fmt.Sprintf("increase %v to %v", tt.coreLevel, tt.increaseLevel)
+ t.Run(msg, func(t *testing.T) {
+ logger, logs := observer.New(tt.coreLevel)
+
+ // sanity check
+ require.Equal(t, tt.coreLevel, LevelOf(logger), "Original logger has the wrong level")
+
+ filteredLogger, err := NewIncreaseLevelCore(logger, tt.increaseLevel)
+ if tt.wantErr {
+ require.Error(t, err)
+ assert.Contains(t, err.Error(), "invalid increase level")
+ return
+ }
+
+ if len(tt.with) > 0 {
+ filteredLogger = filteredLogger.With(tt.with)
+ }
+
+ require.NoError(t, err)
+
+ t.Run("LevelOf", func(t *testing.T) {
+ assert.Equal(t, tt.increaseLevel, LevelOf(filteredLogger), "Filtered logger has the wrong level")
+ })
+
+ for l := DebugLevel; l <= FatalLevel; l++ {
+ enabled := filteredLogger.Enabled(l)
+ entry := Entry{Level: l}
+ ce := filteredLogger.Check(entry, nil)
+ ce.Write()
+ entries := logs.TakeAll()
+
+ if l >= tt.increaseLevel {
+ assert.True(t, enabled, "expect %v to be enabled", l)
+ assert.NotNil(t, ce, "expect non-nil Check")
+ assert.NotEmpty(t, entries, "Expect log to be written")
+ } else {
+ assert.False(t, enabled, "expect %v to be disabled", l)
+ assert.Nil(t, ce, "expect nil Check")
+ assert.Empty(t, entries, "No logs should have been written")
+ }
+
+ // Write should always log the entry as per the Core interface
+ require.NoError(t, filteredLogger.Write(entry, nil), "Write failed")
+ require.NoError(t, filteredLogger.Sync(), "Sync failed")
+ assert.NotEmpty(t, logs.TakeAll(), "Write should always log")
+ }
+ })
+ }
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/json_encoder.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/json_encoder.go
new file mode 100644
index 0000000..3921c5c
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/json_encoder.go
@@ -0,0 +1,562 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "encoding/base64"
+ "math"
+ "sync"
+ "time"
+ "unicode/utf8"
+
+ "go.uber.org/zap/buffer"
+ "go.uber.org/zap/internal/bufferpool"
+)
+
+// For JSON-escaping; see jsonEncoder.safeAddString below.
+const _hex = "0123456789abcdef"
+
+var _jsonPool = sync.Pool{New: func() interface{} {
+ return &jsonEncoder{}
+}}
+
+func getJSONEncoder() *jsonEncoder {
+ return _jsonPool.Get().(*jsonEncoder)
+}
+
+func putJSONEncoder(enc *jsonEncoder) {
+ if enc.reflectBuf != nil {
+ enc.reflectBuf.Free()
+ }
+ enc.EncoderConfig = nil
+ enc.buf = nil
+ enc.spaced = false
+ enc.openNamespaces = 0
+ enc.reflectBuf = nil
+ enc.reflectEnc = nil
+ _jsonPool.Put(enc)
+}
+
+type jsonEncoder struct {
+ *EncoderConfig
+ buf *buffer.Buffer
+ spaced bool // include spaces after colons and commas
+ openNamespaces int
+
+ // for encoding generic values by reflection
+ reflectBuf *buffer.Buffer
+ reflectEnc ReflectedEncoder
+}
+
+// NewJSONEncoder creates a fast, low-allocation JSON encoder. The encoder
+// appropriately escapes all field keys and values.
+//
+// Note that the encoder doesn't deduplicate keys, so it's possible to produce
+// a message like
+//
+// {"foo":"bar","foo":"baz"}
+//
+// This is permitted by the JSON specification, but not encouraged. Many
+// libraries will ignore duplicate key-value pairs (typically keeping the last
+// pair) when unmarshaling, but users should attempt to avoid adding duplicate
+// keys.
+func NewJSONEncoder(cfg EncoderConfig) Encoder {
+ return newJSONEncoder(cfg, false)
+}
+
+func newJSONEncoder(cfg EncoderConfig, spaced bool) *jsonEncoder {
+ if cfg.SkipLineEnding {
+ cfg.LineEnding = ""
+ } else if cfg.LineEnding == "" {
+ cfg.LineEnding = DefaultLineEnding
+ }
+
+ // If no EncoderConfig.NewReflectedEncoder is provided by the user, then use default
+ if cfg.NewReflectedEncoder == nil {
+ cfg.NewReflectedEncoder = defaultReflectedEncoder
+ }
+
+ return &jsonEncoder{
+ EncoderConfig: &cfg,
+ buf: bufferpool.Get(),
+ spaced: spaced,
+ }
+}
+
+func (enc *jsonEncoder) AddArray(key string, arr ArrayMarshaler) error {
+ enc.addKey(key)
+ return enc.AppendArray(arr)
+}
+
+func (enc *jsonEncoder) AddObject(key string, obj ObjectMarshaler) error {
+ enc.addKey(key)
+ return enc.AppendObject(obj)
+}
+
+func (enc *jsonEncoder) AddBinary(key string, val []byte) {
+ enc.AddString(key, base64.StdEncoding.EncodeToString(val))
+}
+
+func (enc *jsonEncoder) AddByteString(key string, val []byte) {
+ enc.addKey(key)
+ enc.AppendByteString(val)
+}
+
+func (enc *jsonEncoder) AddBool(key string, val bool) {
+ enc.addKey(key)
+ enc.AppendBool(val)
+}
+
+func (enc *jsonEncoder) AddComplex128(key string, val complex128) {
+ enc.addKey(key)
+ enc.AppendComplex128(val)
+}
+
+func (enc *jsonEncoder) AddComplex64(key string, val complex64) {
+ enc.addKey(key)
+ enc.AppendComplex64(val)
+}
+
+func (enc *jsonEncoder) AddDuration(key string, val time.Duration) {
+ enc.addKey(key)
+ enc.AppendDuration(val)
+}
+
+func (enc *jsonEncoder) AddFloat64(key string, val float64) {
+ enc.addKey(key)
+ enc.AppendFloat64(val)
+}
+
+func (enc *jsonEncoder) AddFloat32(key string, val float32) {
+ enc.addKey(key)
+ enc.AppendFloat32(val)
+}
+
+func (enc *jsonEncoder) AddInt64(key string, val int64) {
+ enc.addKey(key)
+ enc.AppendInt64(val)
+}
+
+func (enc *jsonEncoder) resetReflectBuf() {
+ if enc.reflectBuf == nil {
+ enc.reflectBuf = bufferpool.Get()
+ enc.reflectEnc = enc.NewReflectedEncoder(enc.reflectBuf)
+ } else {
+ enc.reflectBuf.Reset()
+ }
+}
+
+var nullLiteralBytes = []byte("null")
+
+// Only invoke the standard JSON encoder if there is actually something to
+// encode; otherwise write JSON null literal directly.
+func (enc *jsonEncoder) encodeReflected(obj interface{}) ([]byte, error) {
+ if obj == nil {
+ return nullLiteralBytes, nil
+ }
+ enc.resetReflectBuf()
+ if err := enc.reflectEnc.Encode(obj); err != nil {
+ return nil, err
+ }
+ enc.reflectBuf.TrimNewline()
+ return enc.reflectBuf.Bytes(), nil
+}
+
+func (enc *jsonEncoder) AddReflected(key string, obj interface{}) error {
+ valueBytes, err := enc.encodeReflected(obj)
+ if err != nil {
+ return err
+ }
+ enc.addKey(key)
+ _, err = enc.buf.Write(valueBytes)
+ return err
+}
+
+func (enc *jsonEncoder) OpenNamespace(key string) {
+ enc.addKey(key)
+ enc.buf.AppendByte('{')
+ enc.openNamespaces++
+}
+
+func (enc *jsonEncoder) AddString(key, val string) {
+ enc.addKey(key)
+ enc.AppendString(val)
+}
+
+func (enc *jsonEncoder) AddTime(key string, val time.Time) {
+ enc.addKey(key)
+ enc.AppendTime(val)
+}
+
+func (enc *jsonEncoder) AddUint64(key string, val uint64) {
+ enc.addKey(key)
+ enc.AppendUint64(val)
+}
+
+func (enc *jsonEncoder) AppendArray(arr ArrayMarshaler) error {
+ enc.addElementSeparator()
+ enc.buf.AppendByte('[')
+ err := arr.MarshalLogArray(enc)
+ enc.buf.AppendByte(']')
+ return err
+}
+
+func (enc *jsonEncoder) AppendObject(obj ObjectMarshaler) error {
+ // Close ONLY new openNamespaces that are created during
+ // AppendObject().
+ old := enc.openNamespaces
+ enc.openNamespaces = 0
+ enc.addElementSeparator()
+ enc.buf.AppendByte('{')
+ err := obj.MarshalLogObject(enc)
+ enc.buf.AppendByte('}')
+ enc.closeOpenNamespaces()
+ enc.openNamespaces = old
+ return err
+}
+
+func (enc *jsonEncoder) AppendBool(val bool) {
+ enc.addElementSeparator()
+ enc.buf.AppendBool(val)
+}
+
+func (enc *jsonEncoder) AppendByteString(val []byte) {
+ enc.addElementSeparator()
+ enc.buf.AppendByte('"')
+ enc.safeAddByteString(val)
+ enc.buf.AppendByte('"')
+}
+
+// appendComplex appends the encoded form of the provided complex128 value.
+// precision specifies the encoding precision for the real and imaginary
+// components of the complex number.
+func (enc *jsonEncoder) appendComplex(val complex128, precision int) {
+ enc.addElementSeparator()
+ // Cast to a platform-independent, fixed-size type.
+ r, i := float64(real(val)), float64(imag(val))
+ enc.buf.AppendByte('"')
+ // Because we're always in a quoted string, we can use strconv without
+ // special-casing NaN and +/-Inf.
+ enc.buf.AppendFloat(r, precision)
+ // If imaginary part is less than 0, minus (-) sign is added by default
+ // by AppendFloat.
+ if i >= 0 {
+ enc.buf.AppendByte('+')
+ }
+ enc.buf.AppendFloat(i, precision)
+ enc.buf.AppendByte('i')
+ enc.buf.AppendByte('"')
+}
+
+func (enc *jsonEncoder) AppendDuration(val time.Duration) {
+ cur := enc.buf.Len()
+ if e := enc.EncodeDuration; e != nil {
+ e(val, enc)
+ }
+ if cur == enc.buf.Len() {
+ // User-supplied EncodeDuration is a no-op. Fall back to nanoseconds to keep
+ // JSON valid.
+ enc.AppendInt64(int64(val))
+ }
+}
+
+func (enc *jsonEncoder) AppendInt64(val int64) {
+ enc.addElementSeparator()
+ enc.buf.AppendInt(val)
+}
+
+func (enc *jsonEncoder) AppendReflected(val interface{}) error {
+ valueBytes, err := enc.encodeReflected(val)
+ if err != nil {
+ return err
+ }
+ enc.addElementSeparator()
+ _, err = enc.buf.Write(valueBytes)
+ return err
+}
+
+func (enc *jsonEncoder) AppendString(val string) {
+ enc.addElementSeparator()
+ enc.buf.AppendByte('"')
+ enc.safeAddString(val)
+ enc.buf.AppendByte('"')
+}
+
+func (enc *jsonEncoder) AppendTimeLayout(time time.Time, layout string) {
+ enc.addElementSeparator()
+ enc.buf.AppendByte('"')
+ enc.buf.AppendTime(time, layout)
+ enc.buf.AppendByte('"')
+}
+
+func (enc *jsonEncoder) AppendTime(val time.Time) {
+ cur := enc.buf.Len()
+ if e := enc.EncodeTime; e != nil {
+ e(val, enc)
+ }
+ if cur == enc.buf.Len() {
+ // User-supplied EncodeTime is a no-op. Fall back to nanos since epoch to keep
+ // output JSON valid.
+ enc.AppendInt64(val.UnixNano())
+ }
+}
+
+func (enc *jsonEncoder) AppendUint64(val uint64) {
+ enc.addElementSeparator()
+ enc.buf.AppendUint(val)
+}
+
+func (enc *jsonEncoder) AddInt(k string, v int) { enc.AddInt64(k, int64(v)) }
+func (enc *jsonEncoder) AddInt32(k string, v int32) { enc.AddInt64(k, int64(v)) }
+func (enc *jsonEncoder) AddInt16(k string, v int16) { enc.AddInt64(k, int64(v)) }
+func (enc *jsonEncoder) AddInt8(k string, v int8) { enc.AddInt64(k, int64(v)) }
+func (enc *jsonEncoder) AddUint(k string, v uint) { enc.AddUint64(k, uint64(v)) }
+func (enc *jsonEncoder) AddUint32(k string, v uint32) { enc.AddUint64(k, uint64(v)) }
+func (enc *jsonEncoder) AddUint16(k string, v uint16) { enc.AddUint64(k, uint64(v)) }
+func (enc *jsonEncoder) AddUint8(k string, v uint8) { enc.AddUint64(k, uint64(v)) }
+func (enc *jsonEncoder) AddUintptr(k string, v uintptr) { enc.AddUint64(k, uint64(v)) }
+func (enc *jsonEncoder) AppendComplex64(v complex64) { enc.appendComplex(complex128(v), 32) }
+func (enc *jsonEncoder) AppendComplex128(v complex128) { enc.appendComplex(complex128(v), 64) }
+func (enc *jsonEncoder) AppendFloat64(v float64) { enc.appendFloat(v, 64) }
+func (enc *jsonEncoder) AppendFloat32(v float32) { enc.appendFloat(float64(v), 32) }
+func (enc *jsonEncoder) AppendInt(v int) { enc.AppendInt64(int64(v)) }
+func (enc *jsonEncoder) AppendInt32(v int32) { enc.AppendInt64(int64(v)) }
+func (enc *jsonEncoder) AppendInt16(v int16) { enc.AppendInt64(int64(v)) }
+func (enc *jsonEncoder) AppendInt8(v int8) { enc.AppendInt64(int64(v)) }
+func (enc *jsonEncoder) AppendUint(v uint) { enc.AppendUint64(uint64(v)) }
+func (enc *jsonEncoder) AppendUint32(v uint32) { enc.AppendUint64(uint64(v)) }
+func (enc *jsonEncoder) AppendUint16(v uint16) { enc.AppendUint64(uint64(v)) }
+func (enc *jsonEncoder) AppendUint8(v uint8) { enc.AppendUint64(uint64(v)) }
+func (enc *jsonEncoder) AppendUintptr(v uintptr) { enc.AppendUint64(uint64(v)) }
+
+func (enc *jsonEncoder) Clone() Encoder {
+ clone := enc.clone()
+ clone.buf.Write(enc.buf.Bytes())
+ return clone
+}
+
+func (enc *jsonEncoder) clone() *jsonEncoder {
+ clone := getJSONEncoder()
+ clone.EncoderConfig = enc.EncoderConfig
+ clone.spaced = enc.spaced
+ clone.openNamespaces = enc.openNamespaces
+ clone.buf = bufferpool.Get()
+ return clone
+}
+
+func (enc *jsonEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, error) {
+ final := enc.clone()
+ final.buf.AppendByte('{')
+
+ if final.LevelKey != "" && final.EncodeLevel != nil {
+ final.addKey(final.LevelKey)
+ cur := final.buf.Len()
+ final.EncodeLevel(ent.Level, final)
+ if cur == final.buf.Len() {
+ // User-supplied EncodeLevel was a no-op. Fall back to strings to keep
+ // output JSON valid.
+ final.AppendString(ent.Level.String())
+ }
+ }
+ if final.TimeKey != "" {
+ final.AddTime(final.TimeKey, ent.Time)
+ }
+ if ent.LoggerName != "" && final.NameKey != "" {
+ final.addKey(final.NameKey)
+ cur := final.buf.Len()
+ nameEncoder := final.EncodeName
+
+ // if no name encoder provided, fall back to FullNameEncoder for backwards
+ // compatibility
+ if nameEncoder == nil {
+ nameEncoder = FullNameEncoder
+ }
+
+ nameEncoder(ent.LoggerName, final)
+ if cur == final.buf.Len() {
+ // User-supplied EncodeName was a no-op. Fall back to strings to
+ // keep output JSON valid.
+ final.AppendString(ent.LoggerName)
+ }
+ }
+ if ent.Caller.Defined {
+ if final.CallerKey != "" {
+ final.addKey(final.CallerKey)
+ cur := final.buf.Len()
+ final.EncodeCaller(ent.Caller, final)
+ if cur == final.buf.Len() {
+ // User-supplied EncodeCaller was a no-op. Fall back to strings to
+ // keep output JSON valid.
+ final.AppendString(ent.Caller.String())
+ }
+ }
+ if final.FunctionKey != "" {
+ final.addKey(final.FunctionKey)
+ final.AppendString(ent.Caller.Function)
+ }
+ }
+ if final.MessageKey != "" {
+ final.addKey(enc.MessageKey)
+ final.AppendString(ent.Message)
+ }
+ if enc.buf.Len() > 0 {
+ final.addElementSeparator()
+ final.buf.Write(enc.buf.Bytes())
+ }
+ addFields(final, fields)
+ final.closeOpenNamespaces()
+ if ent.Stack != "" && final.StacktraceKey != "" {
+ final.AddString(final.StacktraceKey, ent.Stack)
+ }
+ final.buf.AppendByte('}')
+ final.buf.AppendString(final.LineEnding)
+
+ ret := final.buf
+ putJSONEncoder(final)
+ return ret, nil
+}
+
+func (enc *jsonEncoder) truncate() {
+ enc.buf.Reset()
+}
+
+func (enc *jsonEncoder) closeOpenNamespaces() {
+ for i := 0; i < enc.openNamespaces; i++ {
+ enc.buf.AppendByte('}')
+ }
+ enc.openNamespaces = 0
+}
+
+func (enc *jsonEncoder) addKey(key string) {
+ enc.addElementSeparator()
+ enc.buf.AppendByte('"')
+ enc.safeAddString(key)
+ enc.buf.AppendByte('"')
+ enc.buf.AppendByte(':')
+ if enc.spaced {
+ enc.buf.AppendByte(' ')
+ }
+}
+
+func (enc *jsonEncoder) addElementSeparator() {
+ last := enc.buf.Len() - 1
+ if last < 0 {
+ return
+ }
+ switch enc.buf.Bytes()[last] {
+ case '{', '[', ':', ',', ' ':
+ return
+ default:
+ enc.buf.AppendByte(',')
+ if enc.spaced {
+ enc.buf.AppendByte(' ')
+ }
+ }
+}
+
+func (enc *jsonEncoder) appendFloat(val float64, bitSize int) {
+ enc.addElementSeparator()
+ switch {
+ case math.IsNaN(val):
+ enc.buf.AppendString(`"NaN"`)
+ case math.IsInf(val, 1):
+ enc.buf.AppendString(`"+Inf"`)
+ case math.IsInf(val, -1):
+ enc.buf.AppendString(`"-Inf"`)
+ default:
+ enc.buf.AppendFloat(val, bitSize)
+ }
+}
+
+// safeAddString JSON-escapes a string and appends it to the internal buffer.
+// Unlike the standard library's encoder, it doesn't attempt to protect the
+// user from browser vulnerabilities or JSONP-related problems.
+func (enc *jsonEncoder) safeAddString(s string) {
+ for i := 0; i < len(s); {
+ if enc.tryAddRuneSelf(s[i]) {
+ i++
+ continue
+ }
+ r, size := utf8.DecodeRuneInString(s[i:])
+ if enc.tryAddRuneError(r, size) {
+ i++
+ continue
+ }
+ enc.buf.AppendString(s[i : i+size])
+ i += size
+ }
+}
+
+// safeAddByteString is no-alloc equivalent of safeAddString(string(s)) for s []byte.
+func (enc *jsonEncoder) safeAddByteString(s []byte) {
+ for i := 0; i < len(s); {
+ if enc.tryAddRuneSelf(s[i]) {
+ i++
+ continue
+ }
+ r, size := utf8.DecodeRune(s[i:])
+ if enc.tryAddRuneError(r, size) {
+ i++
+ continue
+ }
+ enc.buf.Write(s[i : i+size])
+ i += size
+ }
+}
+
+// tryAddRuneSelf appends b if it is valid UTF-8 character represented in a single byte.
+func (enc *jsonEncoder) tryAddRuneSelf(b byte) bool {
+ if b >= utf8.RuneSelf {
+ return false
+ }
+ if 0x20 <= b && b != '\\' && b != '"' {
+ enc.buf.AppendByte(b)
+ return true
+ }
+ switch b {
+ case '\\', '"':
+ enc.buf.AppendByte('\\')
+ enc.buf.AppendByte(b)
+ case '\n':
+ enc.buf.AppendByte('\\')
+ enc.buf.AppendByte('n')
+ case '\r':
+ enc.buf.AppendByte('\\')
+ enc.buf.AppendByte('r')
+ case '\t':
+ enc.buf.AppendByte('\\')
+ enc.buf.AppendByte('t')
+ default:
+ // Encode bytes < 0x20, except for the escape sequences above.
+ enc.buf.AppendString(`\u00`)
+ enc.buf.AppendByte(_hex[b>>4])
+ enc.buf.AppendByte(_hex[b&0xF])
+ }
+ return true
+}
+
+func (enc *jsonEncoder) tryAddRuneError(r rune, size int) bool {
+ if r == utf8.RuneError && size == 1 {
+ enc.buf.AppendString(`\ufffd`)
+ return true
+ }
+ return false
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/json_encoder_bench_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/json_encoder_bench_test.go
new file mode 100644
index 0000000..bcb5a01
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/json_encoder_bench_test.go
@@ -0,0 +1,101 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "encoding/json"
+ "testing"
+ "time"
+
+ . "go.uber.org/zap/zapcore"
+)
+
+func BenchmarkJSONLogMarshalerFunc(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ enc := NewJSONEncoder(testEncoderConfig())
+ enc.AddObject("nested", ObjectMarshalerFunc(func(enc ObjectEncoder) error {
+ enc.AddInt64("i", int64(i))
+ return nil
+ }))
+ }
+}
+
+func BenchmarkZapJSONFloat32AndComplex64(b *testing.B) {
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ enc := NewJSONEncoder(testEncoderConfig())
+ enc.AddFloat32("float32", 3.14)
+ enc.AddComplex64("complex64", 2.71+3.14i)
+ }
+ })
+}
+
+func BenchmarkZapJSON(b *testing.B) {
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ enc := NewJSONEncoder(testEncoderConfig())
+ enc.AddString("str", "foo")
+ enc.AddInt64("int64-1", 1)
+ enc.AddInt64("int64-2", 2)
+ enc.AddFloat64("float64", 1.0)
+ enc.AddString("string1", "\n")
+ enc.AddString("string2", "💩")
+ enc.AddString("string3", "🤔")
+ enc.AddString("string4", "🙊")
+ enc.AddBool("bool", true)
+ buf, _ := enc.EncodeEntry(Entry{
+ Message: "fake",
+ Level: DebugLevel,
+ }, nil)
+ buf.Free()
+ }
+ })
+}
+
+func BenchmarkStandardJSON(b *testing.B) {
+ record := struct {
+ Level string `json:"level"`
+ Message string `json:"msg"`
+ Time time.Time `json:"ts"`
+ Fields map[string]interface{} `json:"fields"`
+ }{
+ Level: "debug",
+ Message: "fake",
+ Time: time.Unix(0, 0),
+ Fields: map[string]interface{}{
+ "str": "foo",
+ "int64-1": int64(1),
+ "int64-2": int64(1),
+ "float64": float64(1.0),
+ "string1": "\n",
+ "string2": "💩",
+ "string3": "🤔",
+ "string4": "🙊",
+ "bool": true,
+ },
+ }
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ json.Marshal(record)
+ }
+ })
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/json_encoder_impl_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/json_encoder_impl_test.go
new file mode 100644
index 0000000..fde241f
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/json_encoder_impl_test.go
@@ -0,0 +1,659 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "encoding/json"
+ "errors"
+ "math"
+ "math/rand"
+ "reflect"
+ "testing"
+ "testing/quick"
+ "time"
+
+ "go.uber.org/zap/internal/bufferpool"
+
+ "github.com/stretchr/testify/assert"
+ "go.uber.org/multierr"
+)
+
+var _defaultEncoderConfig = EncoderConfig{
+ EncodeTime: EpochTimeEncoder,
+ EncodeDuration: SecondsDurationEncoder,
+}
+
+func TestJSONClone(t *testing.T) {
+ // The parent encoder is created with plenty of excess capacity.
+ parent := &jsonEncoder{buf: bufferpool.Get()}
+ clone := parent.Clone()
+
+ // Adding to the parent shouldn't affect the clone, and vice versa.
+ parent.AddString("foo", "bar")
+ clone.AddString("baz", "bing")
+
+ assertJSON(t, `"foo":"bar"`, parent)
+ assertJSON(t, `"baz":"bing"`, clone.(*jsonEncoder))
+}
+
+func TestJSONEscaping(t *testing.T) {
+ enc := &jsonEncoder{buf: bufferpool.Get()}
+ // Test all the edge cases of JSON escaping directly.
+ cases := map[string]string{
+ // ASCII.
+ `foo`: `foo`,
+ // Special-cased characters.
+ `"`: `\"`,
+ `\`: `\\`,
+ // Special-cased characters within everyday ASCII.
+ `foo"foo`: `foo\"foo`,
+ "foo\n": `foo\n`,
+ // Special-cased control characters.
+ "\n": `\n`,
+ "\r": `\r`,
+ "\t": `\t`,
+ // \b and \f are sometimes backslash-escaped, but this representation is also
+ // conformant.
+ "\b": `\u0008`,
+ "\f": `\u000c`,
+ // The standard lib special-cases angle brackets and ampersands by default,
+ // because it wants to protect users from browser exploits. In a logging
+ // context, we shouldn't special-case these characters.
+ "<": "<",
+ ">": ">",
+ "&": "&",
+ // ASCII bell - not special-cased.
+ string(byte(0x07)): `\u0007`,
+ // Astral-plane unicode.
+ `☃`: `☃`,
+ // Decodes to (RuneError, 1)
+ "\xed\xa0\x80": `\ufffd\ufffd\ufffd`,
+ "foo\xed\xa0\x80": `foo\ufffd\ufffd\ufffd`,
+ }
+
+ t.Run("String", func(t *testing.T) {
+ for input, output := range cases {
+ enc.truncate()
+ enc.safeAddString(input)
+ assertJSON(t, output, enc)
+ }
+ })
+
+ t.Run("ByteString", func(t *testing.T) {
+ for input, output := range cases {
+ enc.truncate()
+ enc.safeAddByteString([]byte(input))
+ assertJSON(t, output, enc)
+ }
+ })
+}
+
+func TestJSONEncoderObjectFields(t *testing.T) {
+ tests := []struct {
+ desc string
+ expected string
+ f func(Encoder)
+ }{
+ {"binary", `"k":"YWIxMg=="`, func(e Encoder) { e.AddBinary("k", []byte("ab12")) }},
+ {"bool", `"k\\":true`, func(e Encoder) { e.AddBool(`k\`, true) }}, // test key escaping once
+ {"bool", `"k":true`, func(e Encoder) { e.AddBool("k", true) }},
+ {"bool", `"k":false`, func(e Encoder) { e.AddBool("k", false) }},
+ {"byteString", `"k":"v\\"`, func(e Encoder) { e.AddByteString(`k`, []byte(`v\`)) }},
+ {"byteString", `"k":"v"`, func(e Encoder) { e.AddByteString("k", []byte("v")) }},
+ {"byteString", `"k":""`, func(e Encoder) { e.AddByteString("k", []byte{}) }},
+ {"byteString", `"k":""`, func(e Encoder) { e.AddByteString("k", nil) }},
+ {"complex128", `"k":"1+2i"`, func(e Encoder) { e.AddComplex128("k", 1+2i) }},
+ {"complex128/negative_i", `"k":"1-2i"`, func(e Encoder) { e.AddComplex128("k", 1-2i) }},
+ {"complex64", `"k":"1+2i"`, func(e Encoder) { e.AddComplex64("k", 1+2i) }},
+ {"complex64/negative_i", `"k":"1-2i"`, func(e Encoder) { e.AddComplex64("k", 1-2i) }},
+ {"complex64", `"k":"2.71+3.14i"`, func(e Encoder) { e.AddComplex64("k", 2.71+3.14i) }},
+ {"duration", `"k":0.000000001`, func(e Encoder) { e.AddDuration("k", 1) }},
+ {"duration/negative", `"k":-0.000000001`, func(e Encoder) { e.AddDuration("k", -1) }},
+ {"float64", `"k":1`, func(e Encoder) { e.AddFloat64("k", 1.0) }},
+ {"float64", `"k":10000000000`, func(e Encoder) { e.AddFloat64("k", 1e10) }},
+ {"float64", `"k":"NaN"`, func(e Encoder) { e.AddFloat64("k", math.NaN()) }},
+ {"float64", `"k":"+Inf"`, func(e Encoder) { e.AddFloat64("k", math.Inf(1)) }},
+ {"float64", `"k":"-Inf"`, func(e Encoder) { e.AddFloat64("k", math.Inf(-1)) }},
+ {"float64/pi", `"k":3.141592653589793`, func(e Encoder) { e.AddFloat64("k", math.Pi) }},
+ {"float32", `"k":1`, func(e Encoder) { e.AddFloat32("k", 1.0) }},
+ {"float32", `"k":2.71`, func(e Encoder) { e.AddFloat32("k", 2.71) }},
+ {"float32", `"k":0.1`, func(e Encoder) { e.AddFloat32("k", 0.1) }},
+ {"float32", `"k":10000000000`, func(e Encoder) { e.AddFloat32("k", 1e10) }},
+ {"float32", `"k":"NaN"`, func(e Encoder) { e.AddFloat32("k", float32(math.NaN())) }},
+ {"float32", `"k":"+Inf"`, func(e Encoder) { e.AddFloat32("k", float32(math.Inf(1))) }},
+ {"float32", `"k":"-Inf"`, func(e Encoder) { e.AddFloat32("k", float32(math.Inf(-1))) }},
+ {"float32/pi", `"k":3.1415927`, func(e Encoder) { e.AddFloat32("k", math.Pi) }},
+ {"int", `"k":42`, func(e Encoder) { e.AddInt("k", 42) }},
+ {"int64", `"k":42`, func(e Encoder) { e.AddInt64("k", 42) }},
+ {"int64/min", `"k":-9223372036854775808`, func(e Encoder) { e.AddInt64("k", math.MinInt64) }},
+ {"int64/max", `"k":9223372036854775807`, func(e Encoder) { e.AddInt64("k", math.MaxInt64) }},
+ {"int32", `"k":42`, func(e Encoder) { e.AddInt32("k", 42) }},
+ {"int32/min", `"k":-2147483648`, func(e Encoder) { e.AddInt32("k", math.MinInt32) }},
+ {"int32/max", `"k":2147483647`, func(e Encoder) { e.AddInt32("k", math.MaxInt32) }},
+ {"int16", `"k":42`, func(e Encoder) { e.AddInt16("k", 42) }},
+ {"int16/min", `"k":-32768`, func(e Encoder) { e.AddInt16("k", math.MinInt16) }},
+ {"int16/max", `"k":32767`, func(e Encoder) { e.AddInt16("k", math.MaxInt16) }},
+ {"int8", `"k":42`, func(e Encoder) { e.AddInt8("k", 42) }},
+ {"int8/min", `"k":-128`, func(e Encoder) { e.AddInt8("k", math.MinInt8) }},
+ {"int8/max", `"k":127`, func(e Encoder) { e.AddInt8("k", math.MaxInt8) }},
+ {"string", `"k":"v\\"`, func(e Encoder) { e.AddString(`k`, `v\`) }},
+ {"string", `"k":"v"`, func(e Encoder) { e.AddString("k", "v") }},
+ {"string", `"k":""`, func(e Encoder) { e.AddString("k", "") }},
+ {"time", `"k":1`, func(e Encoder) { e.AddTime("k", time.Unix(1, 0)) }},
+ {"uint", `"k":42`, func(e Encoder) { e.AddUint("k", 42) }},
+ {"uint64", `"k":42`, func(e Encoder) { e.AddUint64("k", 42) }},
+ {"uint64/max", `"k":18446744073709551615`, func(e Encoder) { e.AddUint64("k", math.MaxUint64) }},
+ {"uint32", `"k":42`, func(e Encoder) { e.AddUint32("k", 42) }},
+ {"uint32/max", `"k":4294967295`, func(e Encoder) { e.AddUint32("k", math.MaxUint32) }},
+ {"uint16", `"k":42`, func(e Encoder) { e.AddUint16("k", 42) }},
+ {"uint16/max", `"k":65535`, func(e Encoder) { e.AddUint16("k", math.MaxUint16) }},
+ {"uint8", `"k":42`, func(e Encoder) { e.AddUint8("k", 42) }},
+ {"uint8/max", `"k":255`, func(e Encoder) { e.AddUint8("k", math.MaxUint8) }},
+ {"uintptr", `"k":42`, func(e Encoder) { e.AddUintptr("k", 42) }},
+ {
+ desc: "object (success)",
+ expected: `"k":{"loggable":"yes"}`,
+ f: func(e Encoder) {
+ assert.NoError(t, e.AddObject("k", loggable{true}), "Unexpected error calling MarshalLogObject.")
+ },
+ },
+ {
+ desc: "object (error)",
+ expected: `"k":{}`,
+ f: func(e Encoder) {
+ assert.Error(t, e.AddObject("k", loggable{false}), "Expected an error calling MarshalLogObject.")
+ },
+ },
+ {
+ desc: "object (with nested array)",
+ expected: `"turducken":{"ducks":[{"in":"chicken"},{"in":"chicken"}]}`,
+ f: func(e Encoder) {
+ assert.NoError(
+ t,
+ e.AddObject("turducken", turducken{}),
+ "Unexpected error calling MarshalLogObject with nested ObjectMarshalers and ArrayMarshalers.",
+ )
+ },
+ },
+ {
+ desc: "array (with nested object)",
+ expected: `"turduckens":[{"ducks":[{"in":"chicken"},{"in":"chicken"}]},{"ducks":[{"in":"chicken"},{"in":"chicken"}]}]`,
+ f: func(e Encoder) {
+ assert.NoError(
+ t,
+ e.AddArray("turduckens", turduckens(2)),
+ "Unexpected error calling MarshalLogObject with nested ObjectMarshalers and ArrayMarshalers.",
+ )
+ },
+ },
+ {
+ desc: "array (success)",
+ expected: `"k":[true]`,
+ f: func(e Encoder) {
+ assert.NoError(t, e.AddArray(`k`, loggable{true}), "Unexpected error calling MarshalLogArray.")
+ },
+ },
+ {
+ desc: "array (error)",
+ expected: `"k":[]`,
+ f: func(e Encoder) {
+ assert.Error(t, e.AddArray("k", loggable{false}), "Expected an error calling MarshalLogArray.")
+ },
+ },
+ {
+ desc: "reflect (success)",
+ expected: `"k":{"escape":"<&>","loggable":"yes"}`,
+ f: func(e Encoder) {
+ assert.NoError(t, e.AddReflected("k", map[string]string{"escape": "<&>", "loggable": "yes"}), "Unexpected error JSON-serializing a map.")
+ },
+ },
+ {
+ desc: "reflect (failure)",
+ expected: "",
+ f: func(e Encoder) {
+ assert.Error(t, e.AddReflected("k", noJSON{}), "Unexpected success JSON-serializing a noJSON.")
+ },
+ },
+ {
+ desc: "namespace",
+ // EncodeEntry is responsible for closing all open namespaces.
+ expected: `"outermost":{"outer":{"foo":1,"inner":{"foo":2,"innermost":{`,
+ f: func(e Encoder) {
+ e.OpenNamespace("outermost")
+ e.OpenNamespace("outer")
+ e.AddInt("foo", 1)
+ e.OpenNamespace("inner")
+ e.AddInt("foo", 2)
+ e.OpenNamespace("innermost")
+ },
+ },
+ {
+ desc: "object (no nested namespace)",
+ expected: `"obj":{"obj-out":"obj-outside-namespace"},"not-obj":"should-be-outside-obj"`,
+ f: func(e Encoder) {
+ e.AddObject("obj", maybeNamespace{false})
+ e.AddString("not-obj", "should-be-outside-obj")
+ },
+ },
+ {
+ desc: "object (with nested namespace)",
+ expected: `"obj":{"obj-out":"obj-outside-namespace","obj-namespace":{"obj-in":"obj-inside-namespace"}},"not-obj":"should-be-outside-obj"`,
+ f: func(e Encoder) {
+ e.AddObject("obj", maybeNamespace{true})
+ e.AddString("not-obj", "should-be-outside-obj")
+ },
+ },
+ {
+ desc: "multiple open namespaces",
+ expected: `"k":{"foo":1,"middle":{"foo":2,"inner":{"foo":3}}}`,
+ f: func(e Encoder) {
+ e.AddObject("k", ObjectMarshalerFunc(func(enc ObjectEncoder) error {
+ e.AddInt("foo", 1)
+ e.OpenNamespace("middle")
+ e.AddInt("foo", 2)
+ e.OpenNamespace("inner")
+ e.AddInt("foo", 3)
+ return nil
+ }))
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ assertOutput(t, _defaultEncoderConfig, tt.expected, tt.f)
+ })
+ }
+}
+
+func TestJSONEncoderTimeFormats(t *testing.T) {
+ date := time.Date(2000, time.January, 2, 3, 4, 5, 6, time.UTC)
+
+ f := func(e Encoder) {
+ e.AddTime("k", date)
+ e.AddArray("a", ArrayMarshalerFunc(func(enc ArrayEncoder) error {
+ enc.AppendTime(date)
+ return nil
+ }))
+ }
+ tests := []struct {
+ desc string
+ cfg EncoderConfig
+ expected string
+ }{
+ {
+ desc: "time.Time ISO8601",
+ cfg: EncoderConfig{
+ EncodeDuration: NanosDurationEncoder,
+ EncodeTime: ISO8601TimeEncoder,
+ },
+ expected: `"k":"2000-01-02T03:04:05.000Z","a":["2000-01-02T03:04:05.000Z"]`,
+ },
+ {
+ desc: "time.Time RFC3339",
+ cfg: EncoderConfig{
+ EncodeDuration: NanosDurationEncoder,
+ EncodeTime: RFC3339TimeEncoder,
+ },
+ expected: `"k":"2000-01-02T03:04:05Z","a":["2000-01-02T03:04:05Z"]`,
+ },
+ {
+ desc: "time.Time RFC3339Nano",
+ cfg: EncoderConfig{
+ EncodeDuration: NanosDurationEncoder,
+ EncodeTime: RFC3339NanoTimeEncoder,
+ },
+ expected: `"k":"2000-01-02T03:04:05.000000006Z","a":["2000-01-02T03:04:05.000000006Z"]`,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ assertOutput(t, tt.cfg, tt.expected, f)
+ })
+ }
+}
+
+func TestJSONEncoderArrays(t *testing.T) {
+ tests := []struct {
+ desc string
+ expected string // expect f to be called twice
+ f func(ArrayEncoder)
+ }{
+ {"bool", `[true,true]`, func(e ArrayEncoder) { e.AppendBool(true) }},
+ {"byteString", `["k","k"]`, func(e ArrayEncoder) { e.AppendByteString([]byte("k")) }},
+ {"byteString", `["k\\","k\\"]`, func(e ArrayEncoder) { e.AppendByteString([]byte(`k\`)) }},
+ {"complex128", `["1+2i","1+2i"]`, func(e ArrayEncoder) { e.AppendComplex128(1 + 2i) }},
+ {"complex64", `["1+2i","1+2i"]`, func(e ArrayEncoder) { e.AppendComplex64(1 + 2i) }},
+ {"durations", `[0.000000002,0.000000002]`, func(e ArrayEncoder) { e.AppendDuration(2) }},
+ {"float64", `[3.14,3.14]`, func(e ArrayEncoder) { e.AppendFloat64(3.14) }},
+ {"float32", `[3.14,3.14]`, func(e ArrayEncoder) { e.AppendFloat32(3.14) }},
+ {"int", `[42,42]`, func(e ArrayEncoder) { e.AppendInt(42) }},
+ {"int64", `[42,42]`, func(e ArrayEncoder) { e.AppendInt64(42) }},
+ {"int32", `[42,42]`, func(e ArrayEncoder) { e.AppendInt32(42) }},
+ {"int16", `[42,42]`, func(e ArrayEncoder) { e.AppendInt16(42) }},
+ {"int8", `[42,42]`, func(e ArrayEncoder) { e.AppendInt8(42) }},
+ {"string", `["k","k"]`, func(e ArrayEncoder) { e.AppendString("k") }},
+ {"string", `["k\\","k\\"]`, func(e ArrayEncoder) { e.AppendString(`k\`) }},
+ {"times", `[1,1]`, func(e ArrayEncoder) { e.AppendTime(time.Unix(1, 0)) }},
+ {"uint", `[42,42]`, func(e ArrayEncoder) { e.AppendUint(42) }},
+ {"uint64", `[42,42]`, func(e ArrayEncoder) { e.AppendUint64(42) }},
+ {"uint32", `[42,42]`, func(e ArrayEncoder) { e.AppendUint32(42) }},
+ {"uint16", `[42,42]`, func(e ArrayEncoder) { e.AppendUint16(42) }},
+ {"uint8", `[42,42]`, func(e ArrayEncoder) { e.AppendUint8(42) }},
+ {"uintptr", `[42,42]`, func(e ArrayEncoder) { e.AppendUintptr(42) }},
+ {
+ desc: "arrays (success)",
+ expected: `[[true],[true]]`,
+ f: func(arr ArrayEncoder) {
+ assert.NoError(t, arr.AppendArray(ArrayMarshalerFunc(func(inner ArrayEncoder) error {
+ inner.AppendBool(true)
+ return nil
+ })), "Unexpected error appending an array.")
+ },
+ },
+ {
+ desc: "arrays (error)",
+ expected: `[[true],[true]]`,
+ f: func(arr ArrayEncoder) {
+ assert.Error(t, arr.AppendArray(ArrayMarshalerFunc(func(inner ArrayEncoder) error {
+ inner.AppendBool(true)
+ return errors.New("fail")
+ })), "Expected an error appending an array.")
+ },
+ },
+ {
+ desc: "objects (success)",
+ expected: `[{"loggable":"yes"},{"loggable":"yes"}]`,
+ f: func(arr ArrayEncoder) {
+ assert.NoError(t, arr.AppendObject(loggable{true}), "Unexpected error appending an object.")
+ },
+ },
+ {
+ desc: "objects (error)",
+ expected: `[{},{}]`,
+ f: func(arr ArrayEncoder) {
+ assert.Error(t, arr.AppendObject(loggable{false}), "Expected an error appending an object.")
+ },
+ },
+ {
+ desc: "reflect (success)",
+ expected: `[{"foo":5},{"foo":5}]`,
+ f: func(arr ArrayEncoder) {
+ assert.NoError(
+ t,
+ arr.AppendReflected(map[string]int{"foo": 5}),
+ "Unexpected an error appending an object with reflection.",
+ )
+ },
+ },
+ {
+ desc: "reflect (error)",
+ expected: `[]`,
+ f: func(arr ArrayEncoder) {
+ assert.Error(
+ t,
+ arr.AppendReflected(noJSON{}),
+ "Unexpected an error appending an object with reflection.",
+ )
+ },
+ },
+ {
+ desc: "object (no nested namespace) then string",
+ expected: `[{"obj-out":"obj-outside-namespace"},"should-be-outside-obj",{"obj-out":"obj-outside-namespace"},"should-be-outside-obj"]`,
+ f: func(arr ArrayEncoder) {
+ arr.AppendObject(maybeNamespace{false})
+ arr.AppendString("should-be-outside-obj")
+ },
+ },
+ {
+ desc: "object (with nested namespace) then string",
+ expected: `[{"obj-out":"obj-outside-namespace","obj-namespace":{"obj-in":"obj-inside-namespace"}},"should-be-outside-obj",{"obj-out":"obj-outside-namespace","obj-namespace":{"obj-in":"obj-inside-namespace"}},"should-be-outside-obj"]`,
+ f: func(arr ArrayEncoder) {
+ arr.AppendObject(maybeNamespace{true})
+ arr.AppendString("should-be-outside-obj")
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ f := func(enc Encoder) error {
+ return enc.AddArray("array", ArrayMarshalerFunc(func(arr ArrayEncoder) error {
+ tt.f(arr)
+ tt.f(arr)
+ return nil
+ }))
+ }
+ assertOutput(t, _defaultEncoderConfig, `"array":`+tt.expected, func(enc Encoder) {
+ err := f(enc)
+ assert.NoError(t, err, "Unexpected error adding array to JSON encoder.")
+ })
+ })
+ }
+}
+
+func TestJSONEncoderTimeArrays(t *testing.T) {
+ times := []time.Time{
+ time.Unix(1008720000, 0).UTC(), // 2001-12-19
+ time.Unix(1040169600, 0).UTC(), // 2002-12-18
+ time.Unix(1071619200, 0).UTC(), // 2003-12-17
+ }
+
+ tests := []struct {
+ desc string
+ encoder TimeEncoder
+ want string
+ }{
+ {
+ desc: "epoch",
+ encoder: EpochTimeEncoder,
+ want: `[1008720000,1040169600,1071619200]`,
+ },
+ {
+ desc: "epoch millis",
+ encoder: EpochMillisTimeEncoder,
+ want: `[1008720000000,1040169600000,1071619200000]`,
+ },
+ {
+ desc: "iso8601",
+ encoder: ISO8601TimeEncoder,
+ want: `["2001-12-19T00:00:00.000Z","2002-12-18T00:00:00.000Z","2003-12-17T00:00:00.000Z"]`,
+ },
+ {
+ desc: "rfc3339",
+ encoder: RFC3339TimeEncoder,
+ want: `["2001-12-19T00:00:00Z","2002-12-18T00:00:00Z","2003-12-17T00:00:00Z"]`,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ cfg := _defaultEncoderConfig
+ cfg.EncodeTime = tt.encoder
+
+ enc := &jsonEncoder{buf: bufferpool.Get(), EncoderConfig: &cfg}
+ err := enc.AddArray("array", ArrayMarshalerFunc(func(arr ArrayEncoder) error {
+ for _, time := range times {
+ arr.AppendTime(time)
+ }
+ return nil
+ }))
+ assert.NoError(t, err)
+ assert.Equal(t, `"array":`+tt.want, enc.buf.String())
+ })
+ }
+}
+
+func assertJSON(t *testing.T, expected string, enc *jsonEncoder) {
+ assert.Equal(t, expected, enc.buf.String(), "Encoded JSON didn't match expectations.")
+}
+
+func assertOutput(t testing.TB, cfg EncoderConfig, expected string, f func(Encoder)) {
+ enc := NewJSONEncoder(cfg).(*jsonEncoder)
+ f(enc)
+ assert.Equal(t, expected, enc.buf.String(), "Unexpected encoder output after adding.")
+
+ enc.truncate()
+ enc.AddString("foo", "bar")
+ f(enc)
+ expectedPrefix := `"foo":"bar"`
+ if expected != "" {
+ // If we expect output, it should be comma-separated from the previous
+ // field.
+ expectedPrefix += ","
+ }
+ assert.Equal(t, expectedPrefix+expected, enc.buf.String(), "Unexpected encoder output after adding as a second field.")
+}
+
+// Nested Array- and ObjectMarshalers.
+type turducken struct{}
+
+func (t turducken) MarshalLogObject(enc ObjectEncoder) error {
+ return enc.AddArray("ducks", ArrayMarshalerFunc(func(arr ArrayEncoder) error {
+ for i := 0; i < 2; i++ {
+ arr.AppendObject(ObjectMarshalerFunc(func(inner ObjectEncoder) error {
+ inner.AddString("in", "chicken")
+ return nil
+ }))
+ }
+ return nil
+ }))
+}
+
+type turduckens int
+
+func (t turduckens) MarshalLogArray(enc ArrayEncoder) error {
+ var err error
+ tur := turducken{}
+ for i := 0; i < int(t); i++ {
+ err = multierr.Append(err, enc.AppendObject(tur))
+ }
+ return err
+}
+
+type loggable struct{ bool }
+
+func (l loggable) MarshalLogObject(enc ObjectEncoder) error {
+ if !l.bool {
+ return errors.New("can't marshal")
+ }
+ enc.AddString("loggable", "yes")
+ return nil
+}
+
+func (l loggable) MarshalLogArray(enc ArrayEncoder) error {
+ if !l.bool {
+ return errors.New("can't marshal")
+ }
+ enc.AppendBool(true)
+ return nil
+}
+
+// maybeNamespace is an ObjectMarshaler that sometimes opens a namespace
+type maybeNamespace struct{ bool }
+
+func (m maybeNamespace) MarshalLogObject(enc ObjectEncoder) error {
+ enc.AddString("obj-out", "obj-outside-namespace")
+ if m.bool {
+ enc.OpenNamespace("obj-namespace")
+ enc.AddString("obj-in", "obj-inside-namespace")
+ }
+ return nil
+}
+
+type noJSON struct{}
+
+func (nj noJSON) MarshalJSON() ([]byte, error) {
+ return nil, errors.New("no")
+}
+
+func zapEncode(encode func(*jsonEncoder, string)) func(s string) []byte {
+ return func(s string) []byte {
+ enc := &jsonEncoder{buf: bufferpool.Get()}
+ // Escape and quote a string using our encoder.
+ var ret []byte
+ encode(enc, s)
+ ret = make([]byte, 0, enc.buf.Len()+2)
+ ret = append(ret, '"')
+ ret = append(ret, enc.buf.Bytes()...)
+ ret = append(ret, '"')
+ return ret
+ }
+}
+
+func roundTripsCorrectly(encode func(string) []byte, original string) bool {
+ // Encode using our encoder, decode using the standard library, and assert
+ // that we haven't lost any information.
+ encoded := encode(original)
+
+ var decoded string
+ err := json.Unmarshal(encoded, &decoded)
+ if err != nil {
+ return false
+ }
+ return original == decoded
+}
+
+func roundTripsCorrectlyString(original string) bool {
+ return roundTripsCorrectly(zapEncode((*jsonEncoder).safeAddString), original)
+}
+
+func roundTripsCorrectlyByteString(original string) bool {
+ return roundTripsCorrectly(
+ zapEncode(func(enc *jsonEncoder, s string) {
+ enc.safeAddByteString([]byte(s))
+ }),
+ original)
+}
+
+type ASCII string
+
+func (s ASCII) Generate(r *rand.Rand, size int) reflect.Value {
+ bs := make([]byte, size)
+ for i := range bs {
+ bs[i] = byte(r.Intn(128))
+ }
+ a := ASCII(bs)
+ return reflect.ValueOf(a)
+}
+
+func asciiRoundTripsCorrectlyString(s ASCII) bool {
+ return roundTripsCorrectlyString(string(s))
+}
+
+func asciiRoundTripsCorrectlyByteString(s ASCII) bool {
+ return roundTripsCorrectlyByteString(string(s))
+}
+
+func TestJSONQuick(t *testing.T) {
+ check := func(f interface{}) {
+ err := quick.Check(f, &quick.Config{MaxCountScale: 100.0})
+ assert.NoError(t, err)
+ }
+ // Test the full range of UTF-8 strings.
+ check(roundTripsCorrectlyString)
+ check(roundTripsCorrectlyByteString)
+
+ // Focus on ASCII strings.
+ check(asciiRoundTripsCorrectlyString)
+ check(asciiRoundTripsCorrectlyByteString)
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/json_encoder_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/json_encoder_test.go
new file mode 100644
index 0000000..4c651cf
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/json_encoder_test.go
@@ -0,0 +1,268 @@
+// Copyright (c) 2018 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "io"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+)
+
+// TestJSONEncodeEntry is an more "integrated" test that makes it easier to get
+// coverage on the json encoder (e.g. putJSONEncoder, let alone EncodeEntry
+// itself) than the tests in json_encoder_impl_test.go; it needs to be in the
+// zapcore_test package, so that it can import the toplevel zap package for
+// field constructor convenience.
+func TestJSONEncodeEntry(t *testing.T) {
+ type bar struct {
+ Key string `json:"key"`
+ Val float64 `json:"val"`
+ }
+
+ type foo struct {
+ A string `json:"aee"`
+ B int `json:"bee"`
+ C float64 `json:"cee"`
+ D []bar `json:"dee"`
+ }
+
+ tests := []struct {
+ desc string
+ expected string
+ ent zapcore.Entry
+ fields []zapcore.Field
+ }{
+ {
+ desc: "info entry with some fields",
+ expected: `{
+ "L": "info",
+ "T": "2018-06-19T16:33:42.000Z",
+ "N": "bob",
+ "M": "lob law",
+ "so": "passes",
+ "answer": 42,
+ "a_float32": 2.71,
+ "common_pie": 3.14,
+ "complex_value": "3.14-2.71i",
+ "null_value": null,
+ "array_with_null_elements": [{}, null, null, 2],
+ "such": {
+ "aee": "lol",
+ "bee": 123,
+ "cee": 0.9999,
+ "dee": [
+ {"key": "pi", "val": 3.141592653589793},
+ {"key": "tau", "val": 6.283185307179586}
+ ]
+ }
+ }`,
+ ent: zapcore.Entry{
+ Level: zapcore.InfoLevel,
+ Time: time.Date(2018, 6, 19, 16, 33, 42, 99, time.UTC),
+ LoggerName: "bob",
+ Message: "lob law",
+ },
+ fields: []zapcore.Field{
+ zap.String("so", "passes"),
+ zap.Int("answer", 42),
+ zap.Float64("common_pie", 3.14),
+ zap.Float32("a_float32", 2.71),
+ zap.Complex128("complex_value", 3.14-2.71i),
+ // Cover special-cased handling of nil in AddReflect() and
+ // AppendReflect(). Note that for the latter, we explicitly test
+ // correct results for both the nil static interface{} value
+ // (`nil`), as well as the non-nil interface value with a
+ // dynamic type and nil value (`(*struct{})(nil)`).
+ zap.Reflect("null_value", nil),
+ zap.Reflect("array_with_null_elements", []interface{}{&struct{}{}, nil, (*struct{})(nil), 2}),
+ zap.Reflect("such", foo{
+ A: "lol",
+ B: 123,
+ C: 0.9999,
+ D: []bar{
+ {"pi", 3.141592653589793},
+ {"tau", 6.283185307179586},
+ },
+ }),
+ },
+ },
+ }
+
+ enc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{
+ MessageKey: "M",
+ LevelKey: "L",
+ TimeKey: "T",
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: "F",
+ StacktraceKey: "S",
+ EncodeLevel: zapcore.LowercaseLevelEncoder,
+ EncodeTime: zapcore.ISO8601TimeEncoder,
+ EncodeDuration: zapcore.SecondsDurationEncoder,
+ EncodeCaller: zapcore.ShortCallerEncoder,
+ })
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ buf, err := enc.EncodeEntry(tt.ent, tt.fields)
+ if assert.NoError(t, err, "Unexpected JSON encoding error.") {
+ assert.JSONEq(t, tt.expected, buf.String(), "Incorrect encoded JSON entry.")
+ }
+ buf.Free()
+ })
+ }
+}
+
+func TestNoEncodeLevelSupplied(t *testing.T) {
+ enc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{
+ MessageKey: "M",
+ LevelKey: "L",
+ TimeKey: "T",
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: "F",
+ StacktraceKey: "S",
+ EncodeTime: zapcore.ISO8601TimeEncoder,
+ EncodeDuration: zapcore.SecondsDurationEncoder,
+ EncodeCaller: zapcore.ShortCallerEncoder,
+ })
+
+ ent := zapcore.Entry{
+ Level: zapcore.InfoLevel,
+ Time: time.Date(2018, 6, 19, 16, 33, 42, 99, time.UTC),
+ LoggerName: "bob",
+ Message: "lob law",
+ }
+
+ fields := []zapcore.Field{
+ zap.Int("answer", 42),
+ }
+
+ _, err := enc.EncodeEntry(ent, fields)
+ assert.NoError(t, err, "Unexpected JSON encoding error.")
+}
+
+func TestJSONEmptyConfig(t *testing.T) {
+ tests := []struct {
+ name string
+ field zapcore.Field
+ expected string
+ }{
+ {
+ name: "time",
+ field: zap.Time("foo", time.Unix(1591287718, 0)), // 2020-06-04 09:21:58 -0700 PDT
+ expected: `{"foo": 1591287718000000000}`,
+ },
+ {
+ name: "duration",
+ field: zap.Duration("bar", time.Microsecond),
+ expected: `{"bar": 1000}`,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ enc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{})
+
+ buf, err := enc.EncodeEntry(zapcore.Entry{
+ Level: zapcore.DebugLevel,
+ Time: time.Now(),
+ LoggerName: "mylogger",
+ Message: "things happened",
+ }, []zapcore.Field{tt.field})
+ if assert.NoError(t, err, "Unexpected JSON encoding error.") {
+ assert.JSONEq(t, tt.expected, buf.String(), "Incorrect encoded JSON entry.")
+ }
+
+ buf.Free()
+ })
+ }
+}
+
+// Encodes any object into empty json '{}'
+type emptyReflectedEncoder struct {
+ writer io.Writer
+}
+
+func (enc *emptyReflectedEncoder) Encode(obj interface{}) error {
+ _, err := enc.writer.Write([]byte("{}"))
+ return err
+}
+
+func TestJSONCustomReflectedEncoder(t *testing.T) {
+ tests := []struct {
+ name string
+ field zapcore.Field
+ expected string
+ }{
+ {
+ name: "encode custom map object",
+ field: zapcore.Field{
+ Key: "data",
+ Type: zapcore.ReflectType,
+ Interface: map[string]interface{}{
+ "foo": "hello",
+ "bar": 1111,
+ },
+ },
+ expected: `{"data":{}}`,
+ },
+ {
+ name: "encode nil object",
+ field: zapcore.Field{
+ Key: "data",
+ Type: zapcore.ReflectType,
+ },
+ expected: `{"data":null}`,
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ enc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{
+ NewReflectedEncoder: func(writer io.Writer) zapcore.ReflectedEncoder {
+ return &emptyReflectedEncoder{
+ writer: writer,
+ }
+ },
+ })
+
+ buf, err := enc.EncodeEntry(zapcore.Entry{
+ Level: zapcore.DebugLevel,
+ Time: time.Now(),
+ LoggerName: "logger",
+ Message: "things happened",
+ }, []zapcore.Field{tt.field})
+ if assert.NoError(t, err, "Unexpected JSON encoding error.") {
+ assert.JSONEq(t, tt.expected, buf.String(), "Incorrect encoded JSON entry.")
+ }
+ buf.Free()
+ })
+ }
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/leak_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/leak_test.go
new file mode 100644
index 0000000..4ef412e
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/leak_test.go
@@ -0,0 +1,31 @@
+// Copyright (c) 2021 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "testing"
+
+ "go.uber.org/goleak"
+)
+
+func TestMain(m *testing.M) {
+ goleak.VerifyTestMain(m)
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/level.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/level.go
new file mode 100644
index 0000000..e01a241
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/level.go
@@ -0,0 +1,229 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+)
+
+var errUnmarshalNilLevel = errors.New("can't unmarshal a nil *Level")
+
+// A Level is a logging priority. Higher levels are more important.
+type Level int8
+
+const (
+ // DebugLevel logs are typically voluminous, and are usually disabled in
+ // production.
+ DebugLevel Level = iota - 1
+ // InfoLevel is the default logging priority.
+ InfoLevel
+ // WarnLevel logs are more important than Info, but don't need individual
+ // human review.
+ WarnLevel
+ // ErrorLevel logs are high-priority. If an application is running smoothly,
+ // it shouldn't generate any error-level logs.
+ ErrorLevel
+ // DPanicLevel logs are particularly important errors. In development the
+ // logger panics after writing the message.
+ DPanicLevel
+ // PanicLevel logs a message, then panics.
+ PanicLevel
+ // FatalLevel logs a message, then calls os.Exit(1).
+ FatalLevel
+
+ _minLevel = DebugLevel
+ _maxLevel = FatalLevel
+
+ // InvalidLevel is an invalid value for Level.
+ //
+ // Core implementations may panic if they see messages of this level.
+ InvalidLevel = _maxLevel + 1
+)
+
+// ParseLevel parses a level based on the lower-case or all-caps ASCII
+// representation of the log level. If the provided ASCII representation is
+// invalid an error is returned.
+//
+// This is particularly useful when dealing with text input to configure log
+// levels.
+func ParseLevel(text string) (Level, error) {
+ var level Level
+ err := level.UnmarshalText([]byte(text))
+ return level, err
+}
+
+type leveledEnabler interface {
+ LevelEnabler
+
+ Level() Level
+}
+
+// LevelOf reports the minimum enabled log level for the given LevelEnabler
+// from Zap's supported log levels, or [InvalidLevel] if none of them are
+// enabled.
+//
+// A LevelEnabler may implement a 'Level() Level' method to override the
+// behavior of this function.
+//
+// func (c *core) Level() Level {
+// return c.currentLevel
+// }
+//
+// It is recommended that [Core] implementations that wrap other cores use
+// LevelOf to retrieve the level of the wrapped core. For example,
+//
+// func (c *coreWrapper) Level() Level {
+// return zapcore.LevelOf(c.wrappedCore)
+// }
+func LevelOf(enab LevelEnabler) Level {
+ if lvler, ok := enab.(leveledEnabler); ok {
+ return lvler.Level()
+ }
+
+ for lvl := _minLevel; lvl <= _maxLevel; lvl++ {
+ if enab.Enabled(lvl) {
+ return lvl
+ }
+ }
+
+ return InvalidLevel
+}
+
+// String returns a lower-case ASCII representation of the log level.
+func (l Level) String() string {
+ switch l {
+ case DebugLevel:
+ return "debug"
+ case InfoLevel:
+ return "info"
+ case WarnLevel:
+ return "warn"
+ case ErrorLevel:
+ return "error"
+ case DPanicLevel:
+ return "dpanic"
+ case PanicLevel:
+ return "panic"
+ case FatalLevel:
+ return "fatal"
+ default:
+ return fmt.Sprintf("Level(%d)", l)
+ }
+}
+
+// CapitalString returns an all-caps ASCII representation of the log level.
+func (l Level) CapitalString() string {
+ // Printing levels in all-caps is common enough that we should export this
+ // functionality.
+ switch l {
+ case DebugLevel:
+ return "DEBUG"
+ case InfoLevel:
+ return "INFO"
+ case WarnLevel:
+ return "WARN"
+ case ErrorLevel:
+ return "ERROR"
+ case DPanicLevel:
+ return "DPANIC"
+ case PanicLevel:
+ return "PANIC"
+ case FatalLevel:
+ return "FATAL"
+ default:
+ return fmt.Sprintf("LEVEL(%d)", l)
+ }
+}
+
+// MarshalText marshals the Level to text. Note that the text representation
+// drops the -Level suffix (see example).
+func (l Level) MarshalText() ([]byte, error) {
+ return []byte(l.String()), nil
+}
+
+// UnmarshalText unmarshals text to a level. Like MarshalText, UnmarshalText
+// expects the text representation of a Level to drop the -Level suffix (see
+// example).
+//
+// In particular, this makes it easy to configure logging levels using YAML,
+// TOML, or JSON files.
+func (l *Level) UnmarshalText(text []byte) error {
+ if l == nil {
+ return errUnmarshalNilLevel
+ }
+ if !l.unmarshalText(text) && !l.unmarshalText(bytes.ToLower(text)) {
+ return fmt.Errorf("unrecognized level: %q", text)
+ }
+ return nil
+}
+
+func (l *Level) unmarshalText(text []byte) bool {
+ switch string(text) {
+ case "debug", "DEBUG":
+ *l = DebugLevel
+ case "info", "INFO", "": // make the zero value useful
+ *l = InfoLevel
+ case "warn", "WARN":
+ *l = WarnLevel
+ case "error", "ERROR":
+ *l = ErrorLevel
+ case "dpanic", "DPANIC":
+ *l = DPanicLevel
+ case "panic", "PANIC":
+ *l = PanicLevel
+ case "fatal", "FATAL":
+ *l = FatalLevel
+ default:
+ return false
+ }
+ return true
+}
+
+// Set sets the level for the flag.Value interface.
+func (l *Level) Set(s string) error {
+ return l.UnmarshalText([]byte(s))
+}
+
+// Get gets the level for the flag.Getter interface.
+func (l *Level) Get() interface{} {
+ return *l
+}
+
+// Enabled returns true if the given level is at or above this level.
+func (l Level) Enabled(lvl Level) bool {
+ return lvl >= l
+}
+
+// LevelEnabler decides whether a given logging level is enabled when logging a
+// message.
+//
+// Enablers are intended to be used to implement deterministic filters;
+// concerns like sampling are better implemented as a Core.
+//
+// Each concrete Level value implements a static LevelEnabler which returns
+// true for itself and all higher logging levels. For example WarnLevel.Enabled()
+// will return true for WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, and
+// FatalLevel, but return false for InfoLevel and DebugLevel.
+type LevelEnabler interface {
+ Enabled(Level) bool
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/level_strings.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/level_strings.go
new file mode 100644
index 0000000..7af8dad
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/level_strings.go
@@ -0,0 +1,46 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import "go.uber.org/zap/internal/color"
+
+var (
+ _levelToColor = map[Level]color.Color{
+ DebugLevel: color.Magenta,
+ InfoLevel: color.Blue,
+ WarnLevel: color.Yellow,
+ ErrorLevel: color.Red,
+ DPanicLevel: color.Red,
+ PanicLevel: color.Red,
+ FatalLevel: color.Red,
+ }
+ _unknownLevelColor = color.Red
+
+ _levelToLowercaseColorString = make(map[Level]string, len(_levelToColor))
+ _levelToCapitalColorString = make(map[Level]string, len(_levelToColor))
+)
+
+func init() {
+ for level, color := range _levelToColor {
+ _levelToLowercaseColorString[level] = color.Add(level.String())
+ _levelToCapitalColorString[level] = color.Add(level.CapitalString())
+ }
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/level_strings_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/level_strings_test.go
new file mode 100644
index 0000000..14b0bac
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/level_strings_test.go
@@ -0,0 +1,38 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestAllLevelsCoveredByLevelString(t *testing.T) {
+ numLevels := int((_maxLevel - _minLevel) + 1)
+
+ isComplete := func(m map[Level]string) bool {
+ return len(m) == numLevels
+ }
+
+ assert.True(t, isComplete(_levelToLowercaseColorString), "Colored lowercase strings don't cover all levels.")
+ assert.True(t, isComplete(_levelToCapitalColorString), "Colored capital strings don't cover all levels.")
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/level_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/level_test.go
new file mode 100644
index 0000000..4543322
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/level_test.go
@@ -0,0 +1,249 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "bytes"
+ "flag"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestLevelString(t *testing.T) {
+ tests := map[Level]string{
+ DebugLevel: "debug",
+ InfoLevel: "info",
+ WarnLevel: "warn",
+ ErrorLevel: "error",
+ DPanicLevel: "dpanic",
+ PanicLevel: "panic",
+ FatalLevel: "fatal",
+ Level(-42): "Level(-42)",
+ InvalidLevel: "Level(6)", // InvalidLevel does not have a name
+ }
+
+ for lvl, stringLevel := range tests {
+ assert.Equal(t, stringLevel, lvl.String(), "Unexpected lowercase level string.")
+ assert.Equal(t, strings.ToUpper(stringLevel), lvl.CapitalString(), "Unexpected all-caps level string.")
+ }
+}
+
+func TestLevelText(t *testing.T) {
+ tests := []struct {
+ text string
+ level Level
+ }{
+ {"debug", DebugLevel},
+ {"info", InfoLevel},
+ {"", InfoLevel}, // make the zero value useful
+ {"warn", WarnLevel},
+ {"error", ErrorLevel},
+ {"dpanic", DPanicLevel},
+ {"panic", PanicLevel},
+ {"fatal", FatalLevel},
+ }
+ for _, tt := range tests {
+ if tt.text != "" {
+ lvl := tt.level
+ marshaled, err := lvl.MarshalText()
+ assert.NoError(t, err, "Unexpected error marshaling level %v to text.", &lvl)
+ assert.Equal(t, tt.text, string(marshaled), "Marshaling level %v to text yielded unexpected result.", &lvl)
+ }
+
+ var unmarshaled Level
+ err := unmarshaled.UnmarshalText([]byte(tt.text))
+ assert.NoError(t, err, `Unexpected error unmarshaling text %q to level.`, tt.text)
+ assert.Equal(t, tt.level, unmarshaled, `Text %q unmarshaled to an unexpected level.`, tt.text)
+ }
+}
+
+func TestParseLevel(t *testing.T) {
+ tests := []struct {
+ text string
+ level Level
+ err string
+ }{
+ {"info", InfoLevel, ""},
+ {"DEBUG", DebugLevel, ""},
+ {"FOO", 0, `unrecognized level: "FOO"`},
+ }
+ for _, tt := range tests {
+ parsedLevel, err := ParseLevel(tt.text)
+ if len(tt.err) == 0 {
+ assert.NoError(t, err)
+ assert.Equal(t, tt.level, parsedLevel)
+ } else {
+ assert.Error(t, err)
+ assert.Contains(t, err.Error(), tt.err)
+ }
+ }
+}
+
+func TestCapitalLevelsParse(t *testing.T) {
+ tests := []struct {
+ text string
+ level Level
+ }{
+ {"DEBUG", DebugLevel},
+ {"INFO", InfoLevel},
+ {"WARN", WarnLevel},
+ {"ERROR", ErrorLevel},
+ {"DPANIC", DPanicLevel},
+ {"PANIC", PanicLevel},
+ {"FATAL", FatalLevel},
+ }
+ for _, tt := range tests {
+ var unmarshaled Level
+ err := unmarshaled.UnmarshalText([]byte(tt.text))
+ assert.NoError(t, err, `Unexpected error unmarshaling text %q to level.`, tt.text)
+ assert.Equal(t, tt.level, unmarshaled, `Text %q unmarshaled to an unexpected level.`, tt.text)
+ }
+}
+
+func TestWeirdLevelsParse(t *testing.T) {
+ tests := []struct {
+ text string
+ level Level
+ }{
+ // I guess...
+ {"Debug", DebugLevel},
+ {"Info", InfoLevel},
+ {"Warn", WarnLevel},
+ {"Error", ErrorLevel},
+ {"Dpanic", DPanicLevel},
+ {"Panic", PanicLevel},
+ {"Fatal", FatalLevel},
+
+ // What even is...
+ {"DeBuG", DebugLevel},
+ {"InFo", InfoLevel},
+ {"WaRn", WarnLevel},
+ {"ErRor", ErrorLevel},
+ {"DpAnIc", DPanicLevel},
+ {"PaNiC", PanicLevel},
+ {"FaTaL", FatalLevel},
+ }
+ for _, tt := range tests {
+ var unmarshaled Level
+ err := unmarshaled.UnmarshalText([]byte(tt.text))
+ assert.NoError(t, err, `Unexpected error unmarshaling text %q to level.`, tt.text)
+ assert.Equal(t, tt.level, unmarshaled, `Text %q unmarshaled to an unexpected level.`, tt.text)
+ }
+}
+
+func TestLevelNils(t *testing.T) {
+ var l *Level
+
+ // The String() method will not handle nil level properly.
+ assert.Panics(t, func() {
+ assert.Equal(t, "Level(nil)", l.String(), "Unexpected result stringifying nil *Level.")
+ }, "Level(nil).String() should panic")
+
+ assert.Panics(t, func() {
+ l.MarshalText()
+ }, "Expected to panic when marshalling a nil level.")
+
+ err := l.UnmarshalText([]byte("debug"))
+ assert.Equal(t, errUnmarshalNilLevel, err, "Expected to error unmarshalling into a nil Level.")
+}
+
+func TestLevelUnmarshalUnknownText(t *testing.T) {
+ var l Level
+ err := l.UnmarshalText([]byte("foo"))
+ assert.Contains(t, err.Error(), "unrecognized level", "Expected unmarshaling arbitrary text to fail.")
+}
+
+func TestLevelAsFlagValue(t *testing.T) {
+ var (
+ buf bytes.Buffer
+ lvl Level
+ )
+ fs := flag.NewFlagSet("levelTest", flag.ContinueOnError)
+ fs.SetOutput(&buf)
+ fs.Var(&lvl, "level", "log level")
+
+ for _, expected := range []Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, FatalLevel} {
+ assert.NoError(t, fs.Parse([]string{"-level", expected.String()}))
+ assert.Equal(t, expected, lvl, "Unexpected level after parsing flag.")
+ assert.Equal(t, expected, lvl.Get(), "Unexpected output using flag.Getter API.")
+ assert.Empty(t, buf.String(), "Unexpected error output parsing level flag.")
+ buf.Reset()
+ }
+
+ assert.Error(t, fs.Parse([]string{"-level", "nope"}))
+ assert.Equal(
+ t,
+ `invalid value "nope" for flag -level: unrecognized level: "nope"`,
+ strings.Split(buf.String(), "\n")[0], // second line is help message
+ "Unexpected error output from invalid flag input.",
+ )
+}
+
+// enablerWithCustomLevel is a LevelEnabler that implements a custom Level
+// method.
+type enablerWithCustomLevel struct{ lvl Level }
+
+var _ leveledEnabler = (*enablerWithCustomLevel)(nil)
+
+func (l *enablerWithCustomLevel) Enabled(lvl Level) bool {
+ return l.lvl.Enabled(lvl)
+}
+
+func (l *enablerWithCustomLevel) Level() Level {
+ return l.lvl
+}
+
+func TestLevelOf(t *testing.T) {
+ tests := []struct {
+ desc string
+ give LevelEnabler
+ want Level
+ }{
+ {desc: "debug", give: DebugLevel, want: DebugLevel},
+ {desc: "info", give: InfoLevel, want: InfoLevel},
+ {desc: "warn", give: WarnLevel, want: WarnLevel},
+ {desc: "error", give: ErrorLevel, want: ErrorLevel},
+ {desc: "dpanic", give: DPanicLevel, want: DPanicLevel},
+ {desc: "panic", give: PanicLevel, want: PanicLevel},
+ {desc: "fatal", give: FatalLevel, want: FatalLevel},
+ {
+ desc: "leveledEnabler",
+ give: &enablerWithCustomLevel{lvl: InfoLevel},
+ want: InfoLevel,
+ },
+ {
+ desc: "noop",
+ give: NewNopCore(), // always disabled
+ want: InvalidLevel,
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.desc, func(t *testing.T) {
+ t.Parallel()
+
+ assert.Equal(t, tt.want, LevelOf(tt.give), "Reported level did not match.")
+ })
+ }
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/marshaler.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/marshaler.go
new file mode 100644
index 0000000..c3c55ba
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/marshaler.go
@@ -0,0 +1,61 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+// ObjectMarshaler allows user-defined types to efficiently add themselves to the
+// logging context, and to selectively omit information which shouldn't be
+// included in logs (e.g., passwords).
+//
+// Note: ObjectMarshaler is only used when zap.Object is used or when
+// passed directly to zap.Any. It is not used when reflection-based
+// encoding is used.
+type ObjectMarshaler interface {
+ MarshalLogObject(ObjectEncoder) error
+}
+
+// ObjectMarshalerFunc is a type adapter that turns a function into an
+// ObjectMarshaler.
+type ObjectMarshalerFunc func(ObjectEncoder) error
+
+// MarshalLogObject calls the underlying function.
+func (f ObjectMarshalerFunc) MarshalLogObject(enc ObjectEncoder) error {
+ return f(enc)
+}
+
+// ArrayMarshaler allows user-defined types to efficiently add themselves to the
+// logging context, and to selectively omit information which shouldn't be
+// included in logs (e.g., passwords).
+//
+// Note: ArrayMarshaler is only used when zap.Array is used or when
+// passed directly to zap.Any. It is not used when reflection-based
+// encoding is used.
+type ArrayMarshaler interface {
+ MarshalLogArray(ArrayEncoder) error
+}
+
+// ArrayMarshalerFunc is a type adapter that turns a function into an
+// ArrayMarshaler.
+type ArrayMarshalerFunc func(ArrayEncoder) error
+
+// MarshalLogArray calls the underlying function.
+func (f ArrayMarshalerFunc) MarshalLogArray(enc ArrayEncoder) error {
+ return f(enc)
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/memory_encoder.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/memory_encoder.go
new file mode 100644
index 0000000..dfead08
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/memory_encoder.go
@@ -0,0 +1,179 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import "time"
+
+// MapObjectEncoder is an ObjectEncoder backed by a simple
+// map[string]interface{}. It's not fast enough for production use, but it's
+// helpful in tests.
+type MapObjectEncoder struct {
+ // Fields contains the entire encoded log context.
+ Fields map[string]interface{}
+ // cur is a pointer to the namespace we're currently writing to.
+ cur map[string]interface{}
+}
+
+// NewMapObjectEncoder creates a new map-backed ObjectEncoder.
+func NewMapObjectEncoder() *MapObjectEncoder {
+ m := make(map[string]interface{})
+ return &MapObjectEncoder{
+ Fields: m,
+ cur: m,
+ }
+}
+
+// AddArray implements ObjectEncoder.
+func (m *MapObjectEncoder) AddArray(key string, v ArrayMarshaler) error {
+ arr := &sliceArrayEncoder{elems: make([]interface{}, 0)}
+ err := v.MarshalLogArray(arr)
+ m.cur[key] = arr.elems
+ return err
+}
+
+// AddObject implements ObjectEncoder.
+func (m *MapObjectEncoder) AddObject(k string, v ObjectMarshaler) error {
+ newMap := NewMapObjectEncoder()
+ m.cur[k] = newMap.Fields
+ return v.MarshalLogObject(newMap)
+}
+
+// AddBinary implements ObjectEncoder.
+func (m *MapObjectEncoder) AddBinary(k string, v []byte) { m.cur[k] = v }
+
+// AddByteString implements ObjectEncoder.
+func (m *MapObjectEncoder) AddByteString(k string, v []byte) { m.cur[k] = string(v) }
+
+// AddBool implements ObjectEncoder.
+func (m *MapObjectEncoder) AddBool(k string, v bool) { m.cur[k] = v }
+
+// AddDuration implements ObjectEncoder.
+func (m MapObjectEncoder) AddDuration(k string, v time.Duration) { m.cur[k] = v }
+
+// AddComplex128 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddComplex128(k string, v complex128) { m.cur[k] = v }
+
+// AddComplex64 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddComplex64(k string, v complex64) { m.cur[k] = v }
+
+// AddFloat64 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddFloat64(k string, v float64) { m.cur[k] = v }
+
+// AddFloat32 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddFloat32(k string, v float32) { m.cur[k] = v }
+
+// AddInt implements ObjectEncoder.
+func (m *MapObjectEncoder) AddInt(k string, v int) { m.cur[k] = v }
+
+// AddInt64 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddInt64(k string, v int64) { m.cur[k] = v }
+
+// AddInt32 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddInt32(k string, v int32) { m.cur[k] = v }
+
+// AddInt16 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddInt16(k string, v int16) { m.cur[k] = v }
+
+// AddInt8 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddInt8(k string, v int8) { m.cur[k] = v }
+
+// AddString implements ObjectEncoder.
+func (m *MapObjectEncoder) AddString(k string, v string) { m.cur[k] = v }
+
+// AddTime implements ObjectEncoder.
+func (m MapObjectEncoder) AddTime(k string, v time.Time) { m.cur[k] = v }
+
+// AddUint implements ObjectEncoder.
+func (m *MapObjectEncoder) AddUint(k string, v uint) { m.cur[k] = v }
+
+// AddUint64 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddUint64(k string, v uint64) { m.cur[k] = v }
+
+// AddUint32 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddUint32(k string, v uint32) { m.cur[k] = v }
+
+// AddUint16 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddUint16(k string, v uint16) { m.cur[k] = v }
+
+// AddUint8 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddUint8(k string, v uint8) { m.cur[k] = v }
+
+// AddUintptr implements ObjectEncoder.
+func (m *MapObjectEncoder) AddUintptr(k string, v uintptr) { m.cur[k] = v }
+
+// AddReflected implements ObjectEncoder.
+func (m *MapObjectEncoder) AddReflected(k string, v interface{}) error {
+ m.cur[k] = v
+ return nil
+}
+
+// OpenNamespace implements ObjectEncoder.
+func (m *MapObjectEncoder) OpenNamespace(k string) {
+ ns := make(map[string]interface{})
+ m.cur[k] = ns
+ m.cur = ns
+}
+
+// sliceArrayEncoder is an ArrayEncoder backed by a simple []interface{}. Like
+// the MapObjectEncoder, it's not designed for production use.
+type sliceArrayEncoder struct {
+ elems []interface{}
+}
+
+func (s *sliceArrayEncoder) AppendArray(v ArrayMarshaler) error {
+ enc := &sliceArrayEncoder{}
+ err := v.MarshalLogArray(enc)
+ s.elems = append(s.elems, enc.elems)
+ return err
+}
+
+func (s *sliceArrayEncoder) AppendObject(v ObjectMarshaler) error {
+ m := NewMapObjectEncoder()
+ err := v.MarshalLogObject(m)
+ s.elems = append(s.elems, m.Fields)
+ return err
+}
+
+func (s *sliceArrayEncoder) AppendReflected(v interface{}) error {
+ s.elems = append(s.elems, v)
+ return nil
+}
+
+func (s *sliceArrayEncoder) AppendBool(v bool) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendByteString(v []byte) { s.elems = append(s.elems, string(v)) }
+func (s *sliceArrayEncoder) AppendComplex128(v complex128) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendComplex64(v complex64) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendDuration(v time.Duration) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendFloat64(v float64) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendFloat32(v float32) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendInt(v int) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendInt64(v int64) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendInt32(v int32) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendInt16(v int16) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendInt8(v int8) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendString(v string) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendTime(v time.Time) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendUint(v uint) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendUint64(v uint64) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendUint32(v uint32) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendUint16(v uint16) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendUint8(v uint8) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendUintptr(v uintptr) { s.elems = append(s.elems, v) }
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/memory_encoder_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/memory_encoder_test.go
new file mode 100644
index 0000000..052bdb0
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/memory_encoder_test.go
@@ -0,0 +1,364 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestMapObjectEncoderAdd(t *testing.T) {
+ // Expected output of a turducken.
+ wantTurducken := map[string]interface{}{
+ "ducks": []interface{}{
+ map[string]interface{}{"in": "chicken"},
+ map[string]interface{}{"in": "chicken"},
+ },
+ }
+
+ tests := []struct {
+ desc string
+ f func(ObjectEncoder)
+ expected interface{}
+ }{
+ {
+ desc: "AddObject",
+ f: func(e ObjectEncoder) {
+ assert.NoError(t, e.AddObject("k", loggable{true}), "Expected AddObject to succeed.")
+ },
+ expected: map[string]interface{}{"loggable": "yes"},
+ },
+ {
+ desc: "AddObject (nested)",
+ f: func(e ObjectEncoder) {
+ assert.NoError(t, e.AddObject("k", turducken{}), "Expected AddObject to succeed.")
+ },
+ expected: wantTurducken,
+ },
+ {
+ desc: "AddArray",
+ f: func(e ObjectEncoder) {
+ assert.NoError(t, e.AddArray("k", ArrayMarshalerFunc(func(arr ArrayEncoder) error {
+ arr.AppendBool(true)
+ arr.AppendBool(false)
+ arr.AppendBool(true)
+ return nil
+ })), "Expected AddArray to succeed.")
+ },
+ expected: []interface{}{true, false, true},
+ },
+ {
+ desc: "AddArray (nested)",
+ f: func(e ObjectEncoder) {
+ assert.NoError(t, e.AddArray("k", turduckens(2)), "Expected AddArray to succeed.")
+ },
+ expected: []interface{}{wantTurducken, wantTurducken},
+ },
+ {
+ desc: "AddArray (empty)",
+ f: func(e ObjectEncoder) {
+ assert.NoError(t, e.AddArray("k", turduckens(0)), "Expected AddArray to succeed.")
+ },
+ expected: []interface{}{},
+ },
+ {
+ desc: "AddBinary",
+ f: func(e ObjectEncoder) { e.AddBinary("k", []byte("foo")) },
+ expected: []byte("foo"),
+ },
+ {
+ desc: "AddByteString",
+ f: func(e ObjectEncoder) { e.AddByteString("k", []byte("foo")) },
+ expected: "foo",
+ },
+ {
+ desc: "AddBool",
+ f: func(e ObjectEncoder) { e.AddBool("k", true) },
+ expected: true,
+ },
+ {
+ desc: "AddComplex128",
+ f: func(e ObjectEncoder) { e.AddComplex128("k", 1+2i) },
+ expected: 1 + 2i,
+ },
+ {
+ desc: "AddComplex64",
+ f: func(e ObjectEncoder) { e.AddComplex64("k", 1+2i) },
+ expected: complex64(1 + 2i),
+ },
+ {
+ desc: "AddDuration",
+ f: func(e ObjectEncoder) { e.AddDuration("k", time.Millisecond) },
+ expected: time.Millisecond,
+ },
+ {
+ desc: "AddFloat64",
+ f: func(e ObjectEncoder) { e.AddFloat64("k", 3.14) },
+ expected: 3.14,
+ },
+ {
+ desc: "AddFloat32",
+ f: func(e ObjectEncoder) { e.AddFloat32("k", 3.14) },
+ expected: float32(3.14),
+ },
+ {
+ desc: "AddInt",
+ f: func(e ObjectEncoder) { e.AddInt("k", 42) },
+ expected: 42,
+ },
+ {
+ desc: "AddInt64",
+ f: func(e ObjectEncoder) { e.AddInt64("k", 42) },
+ expected: int64(42),
+ },
+ {
+ desc: "AddInt32",
+ f: func(e ObjectEncoder) { e.AddInt32("k", 42) },
+ expected: int32(42),
+ },
+ {
+ desc: "AddInt16",
+ f: func(e ObjectEncoder) { e.AddInt16("k", 42) },
+ expected: int16(42),
+ },
+ {
+ desc: "AddInt8",
+ f: func(e ObjectEncoder) { e.AddInt8("k", 42) },
+ expected: int8(42),
+ },
+ {
+ desc: "AddString",
+ f: func(e ObjectEncoder) { e.AddString("k", "v") },
+ expected: "v",
+ },
+ {
+ desc: "AddTime",
+ f: func(e ObjectEncoder) { e.AddTime("k", time.Unix(0, 100)) },
+ expected: time.Unix(0, 100),
+ },
+ {
+ desc: "AddUint",
+ f: func(e ObjectEncoder) { e.AddUint("k", 42) },
+ expected: uint(42),
+ },
+ {
+ desc: "AddUint64",
+ f: func(e ObjectEncoder) { e.AddUint64("k", 42) },
+ expected: uint64(42),
+ },
+ {
+ desc: "AddUint32",
+ f: func(e ObjectEncoder) { e.AddUint32("k", 42) },
+ expected: uint32(42),
+ },
+ {
+ desc: "AddUint16",
+ f: func(e ObjectEncoder) { e.AddUint16("k", 42) },
+ expected: uint16(42),
+ },
+ {
+ desc: "AddUint8",
+ f: func(e ObjectEncoder) { e.AddUint8("k", 42) },
+ expected: uint8(42),
+ },
+ {
+ desc: "AddUintptr",
+ f: func(e ObjectEncoder) { e.AddUintptr("k", 42) },
+ expected: uintptr(42),
+ },
+ {
+ desc: "AddReflected",
+ f: func(e ObjectEncoder) {
+ assert.NoError(t, e.AddReflected("k", map[string]interface{}{"foo": 5}), "Expected AddReflected to succeed.")
+ },
+ expected: map[string]interface{}{"foo": 5},
+ },
+ {
+ desc: "OpenNamespace",
+ f: func(e ObjectEncoder) {
+ e.OpenNamespace("k")
+ e.AddInt("foo", 1)
+ e.OpenNamespace("middle")
+ e.AddInt("foo", 2)
+ e.OpenNamespace("inner")
+ e.AddInt("foo", 3)
+ },
+ expected: map[string]interface{}{
+ "foo": 1,
+ "middle": map[string]interface{}{
+ "foo": 2,
+ "inner": map[string]interface{}{
+ "foo": 3,
+ },
+ },
+ },
+ },
+ {
+ desc: "object (no nested namespace) then string",
+ f: func(e ObjectEncoder) {
+ e.OpenNamespace("k")
+ e.AddObject("obj", maybeNamespace{false})
+ e.AddString("not-obj", "should-be-outside-obj")
+ },
+ expected: map[string]interface{}{
+ "obj": map[string]interface{}{
+ "obj-out": "obj-outside-namespace",
+ },
+ "not-obj": "should-be-outside-obj",
+ },
+ },
+ {
+ desc: "object (with nested namespace) then string",
+ f: func(e ObjectEncoder) {
+ e.OpenNamespace("k")
+ e.AddObject("obj", maybeNamespace{true})
+ e.AddString("not-obj", "should-be-outside-obj")
+ },
+ expected: map[string]interface{}{
+ "obj": map[string]interface{}{
+ "obj-out": "obj-outside-namespace",
+ "obj-namespace": map[string]interface{}{
+ "obj-in": "obj-inside-namespace",
+ },
+ },
+ "not-obj": "should-be-outside-obj",
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ enc := NewMapObjectEncoder()
+ tt.f(enc)
+ assert.Equal(t, tt.expected, enc.Fields["k"], "Unexpected encoder output.")
+ })
+ }
+}
+func TestSliceArrayEncoderAppend(t *testing.T) {
+ tests := []struct {
+ desc string
+ f func(ArrayEncoder)
+ expected interface{}
+ }{
+ // AppendObject and AppendArray are covered by the AddObject (nested) and
+ // AddArray (nested) cases above.
+ {"AppendBool", func(e ArrayEncoder) { e.AppendBool(true) }, true},
+ {"AppendByteString", func(e ArrayEncoder) { e.AppendByteString([]byte("foo")) }, "foo"},
+ {"AppendComplex128", func(e ArrayEncoder) { e.AppendComplex128(1 + 2i) }, 1 + 2i},
+ {"AppendComplex64", func(e ArrayEncoder) { e.AppendComplex64(1 + 2i) }, complex64(1 + 2i)},
+ {"AppendDuration", func(e ArrayEncoder) { e.AppendDuration(time.Second) }, time.Second},
+ {"AppendFloat64", func(e ArrayEncoder) { e.AppendFloat64(3.14) }, 3.14},
+ {"AppendFloat32", func(e ArrayEncoder) { e.AppendFloat32(3.14) }, float32(3.14)},
+ {"AppendInt", func(e ArrayEncoder) { e.AppendInt(42) }, 42},
+ {"AppendInt64", func(e ArrayEncoder) { e.AppendInt64(42) }, int64(42)},
+ {"AppendInt32", func(e ArrayEncoder) { e.AppendInt32(42) }, int32(42)},
+ {"AppendInt16", func(e ArrayEncoder) { e.AppendInt16(42) }, int16(42)},
+ {"AppendInt8", func(e ArrayEncoder) { e.AppendInt8(42) }, int8(42)},
+ {"AppendString", func(e ArrayEncoder) { e.AppendString("foo") }, "foo"},
+ {"AppendTime", func(e ArrayEncoder) { e.AppendTime(time.Unix(0, 100)) }, time.Unix(0, 100)},
+ {"AppendUint", func(e ArrayEncoder) { e.AppendUint(42) }, uint(42)},
+ {"AppendUint64", func(e ArrayEncoder) { e.AppendUint64(42) }, uint64(42)},
+ {"AppendUint32", func(e ArrayEncoder) { e.AppendUint32(42) }, uint32(42)},
+ {"AppendUint16", func(e ArrayEncoder) { e.AppendUint16(42) }, uint16(42)},
+ {"AppendUint8", func(e ArrayEncoder) { e.AppendUint8(42) }, uint8(42)},
+ {"AppendUintptr", func(e ArrayEncoder) { e.AppendUintptr(42) }, uintptr(42)},
+ {
+ desc: "AppendReflected",
+ f: func(e ArrayEncoder) { e.AppendReflected(map[string]interface{}{"foo": 5}) },
+ expected: map[string]interface{}{"foo": 5},
+ },
+ {
+ desc: "AppendArray (arrays of arrays)",
+ f: func(e ArrayEncoder) {
+ e.AppendArray(ArrayMarshalerFunc(func(inner ArrayEncoder) error {
+ inner.AppendBool(true)
+ inner.AppendBool(false)
+ return nil
+ }))
+ },
+ expected: []interface{}{true, false},
+ },
+ {
+ desc: "object (no nested namespace) then string",
+ f: func(e ArrayEncoder) {
+ e.AppendArray(ArrayMarshalerFunc(func(inner ArrayEncoder) error {
+ inner.AppendObject(maybeNamespace{false})
+ inner.AppendString("should-be-outside-obj")
+ return nil
+ }))
+ },
+ expected: []interface{}{
+ map[string]interface{}{
+ "obj-out": "obj-outside-namespace",
+ },
+ "should-be-outside-obj",
+ },
+ },
+ {
+ desc: "object (with nested namespace) then string",
+ f: func(e ArrayEncoder) {
+ e.AppendArray(ArrayMarshalerFunc(func(inner ArrayEncoder) error {
+ inner.AppendObject(maybeNamespace{true})
+ inner.AppendString("should-be-outside-obj")
+ return nil
+ }))
+ },
+ expected: []interface{}{
+ map[string]interface{}{
+ "obj-out": "obj-outside-namespace",
+ "obj-namespace": map[string]interface{}{
+ "obj-in": "obj-inside-namespace",
+ },
+ },
+ "should-be-outside-obj",
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ enc := NewMapObjectEncoder()
+ assert.NoError(t, enc.AddArray("k", ArrayMarshalerFunc(func(arr ArrayEncoder) error {
+ tt.f(arr)
+ tt.f(arr)
+ return nil
+ })), "Expected AddArray to succeed.")
+
+ arr, ok := enc.Fields["k"].([]interface{})
+ require.True(t, ok, "Test case %s didn't encode an array.", tt.desc)
+ assert.Equal(t, []interface{}{tt.expected, tt.expected}, arr, "Unexpected encoder output.")
+ })
+ }
+}
+
+func TestMapObjectEncoderReflectionFailures(t *testing.T) {
+ enc := NewMapObjectEncoder()
+ assert.Error(t, enc.AddObject("object", loggable{false}), "Expected AddObject to fail.")
+ assert.Equal(
+ t,
+ map[string]interface{}{"object": map[string]interface{}{}},
+ enc.Fields,
+ "Expected encoder to use empty values on errors.",
+ )
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/reflected_encoder.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/reflected_encoder.go
new file mode 100644
index 0000000..8746360
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/reflected_encoder.go
@@ -0,0 +1,41 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "encoding/json"
+ "io"
+)
+
+// ReflectedEncoder serializes log fields that can't be serialized with Zap's
+// JSON encoder. These have the ReflectType field type.
+// Use EncoderConfig.NewReflectedEncoder to set this.
+type ReflectedEncoder interface {
+ // Encode encodes and writes to the underlying data stream.
+ Encode(interface{}) error
+}
+
+func defaultReflectedEncoder(w io.Writer) ReflectedEncoder {
+ enc := json.NewEncoder(w)
+ // For consistency with our custom JSON encoder.
+ enc.SetEscapeHTML(false)
+ return enc
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/sampler.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/sampler.go
new file mode 100644
index 0000000..dc51805
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/sampler.go
@@ -0,0 +1,230 @@
+// Copyright (c) 2016-2022 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "time"
+
+ "go.uber.org/atomic"
+)
+
+const (
+ _numLevels = _maxLevel - _minLevel + 1
+ _countersPerLevel = 4096
+)
+
+type counter struct {
+ resetAt atomic.Int64
+ counter atomic.Uint64
+}
+
+type counters [_numLevels][_countersPerLevel]counter
+
+func newCounters() *counters {
+ return &counters{}
+}
+
+func (cs *counters) get(lvl Level, key string) *counter {
+ i := lvl - _minLevel
+ j := fnv32a(key) % _countersPerLevel
+ return &cs[i][j]
+}
+
+// fnv32a, adapted from "hash/fnv", but without a []byte(string) alloc
+func fnv32a(s string) uint32 {
+ const (
+ offset32 = 2166136261
+ prime32 = 16777619
+ )
+ hash := uint32(offset32)
+ for i := 0; i < len(s); i++ {
+ hash ^= uint32(s[i])
+ hash *= prime32
+ }
+ return hash
+}
+
+func (c *counter) IncCheckReset(t time.Time, tick time.Duration) uint64 {
+ tn := t.UnixNano()
+ resetAfter := c.resetAt.Load()
+ if resetAfter > tn {
+ return c.counter.Inc()
+ }
+
+ c.counter.Store(1)
+
+ newResetAfter := tn + tick.Nanoseconds()
+ if !c.resetAt.CAS(resetAfter, newResetAfter) {
+ // We raced with another goroutine trying to reset, and it also reset
+ // the counter to 1, so we need to reincrement the counter.
+ return c.counter.Inc()
+ }
+
+ return 1
+}
+
+// SamplingDecision is a decision represented as a bit field made by sampler.
+// More decisions may be added in the future.
+type SamplingDecision uint32
+
+const (
+ // LogDropped indicates that the Sampler dropped a log entry.
+ LogDropped SamplingDecision = 1 << iota
+ // LogSampled indicates that the Sampler sampled a log entry.
+ LogSampled
+)
+
+// optionFunc wraps a func so it satisfies the SamplerOption interface.
+type optionFunc func(*sampler)
+
+func (f optionFunc) apply(s *sampler) {
+ f(s)
+}
+
+// SamplerOption configures a Sampler.
+type SamplerOption interface {
+ apply(*sampler)
+}
+
+// nopSamplingHook is the default hook used by sampler.
+func nopSamplingHook(Entry, SamplingDecision) {}
+
+// SamplerHook registers a function which will be called when Sampler makes a
+// decision.
+//
+// This hook may be used to get visibility into the performance of the sampler.
+// For example, use it to track metrics of dropped versus sampled logs.
+//
+// var dropped atomic.Int64
+// zapcore.SamplerHook(func(ent zapcore.Entry, dec zapcore.SamplingDecision) {
+// if dec&zapcore.LogDropped > 0 {
+// dropped.Inc()
+// }
+// })
+func SamplerHook(hook func(entry Entry, dec SamplingDecision)) SamplerOption {
+ return optionFunc(func(s *sampler) {
+ s.hook = hook
+ })
+}
+
+// NewSamplerWithOptions creates a Core that samples incoming entries, which
+// caps the CPU and I/O load of logging while attempting to preserve a
+// representative subset of your logs.
+//
+// Zap samples by logging the first N entries with a given level and message
+// each tick. If more Entries with the same level and message are seen during
+// the same interval, every Mth message is logged and the rest are dropped.
+//
+// For example,
+//
+// core = NewSamplerWithOptions(core, time.Second, 10, 5)
+//
+// This will log the first 10 log entries with the same level and message
+// in a one second interval as-is. Following that, it will allow through
+// every 5th log entry with the same level and message in that interval.
+//
+// If thereafter is zero, the Core will drop all log entries after the first N
+// in that interval.
+//
+// Sampler can be configured to report sampling decisions with the SamplerHook
+// option.
+//
+// Keep in mind that Zap's sampling implementation is optimized for speed over
+// absolute precision; under load, each tick may be slightly over- or
+// under-sampled.
+func NewSamplerWithOptions(core Core, tick time.Duration, first, thereafter int, opts ...SamplerOption) Core {
+ s := &sampler{
+ Core: core,
+ tick: tick,
+ counts: newCounters(),
+ first: uint64(first),
+ thereafter: uint64(thereafter),
+ hook: nopSamplingHook,
+ }
+ for _, opt := range opts {
+ opt.apply(s)
+ }
+
+ return s
+}
+
+type sampler struct {
+ Core
+
+ counts *counters
+ tick time.Duration
+ first, thereafter uint64
+ hook func(Entry, SamplingDecision)
+}
+
+var (
+ _ Core = (*sampler)(nil)
+ _ leveledEnabler = (*sampler)(nil)
+)
+
+// NewSampler creates a Core that samples incoming entries, which
+// caps the CPU and I/O load of logging while attempting to preserve a
+// representative subset of your logs.
+//
+// Zap samples by logging the first N entries with a given level and message
+// each tick. If more Entries with the same level and message are seen during
+// the same interval, every Mth message is logged and the rest are dropped.
+//
+// Keep in mind that zap's sampling implementation is optimized for speed over
+// absolute precision; under load, each tick may be slightly over- or
+// under-sampled.
+//
+// Deprecated: use NewSamplerWithOptions.
+func NewSampler(core Core, tick time.Duration, first, thereafter int) Core {
+ return NewSamplerWithOptions(core, tick, first, thereafter)
+}
+
+func (s *sampler) Level() Level {
+ return LevelOf(s.Core)
+}
+
+func (s *sampler) With(fields []Field) Core {
+ return &sampler{
+ Core: s.Core.With(fields),
+ tick: s.tick,
+ counts: s.counts,
+ first: s.first,
+ thereafter: s.thereafter,
+ hook: s.hook,
+ }
+}
+
+func (s *sampler) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
+ if !s.Enabled(ent.Level) {
+ return ce
+ }
+
+ if ent.Level >= _minLevel && ent.Level <= _maxLevel {
+ counter := s.counts.get(ent.Level, ent.Message)
+ n := counter.IncCheckReset(ent.Time, s.tick)
+ if n > s.first && (s.thereafter == 0 || (n-s.first)%s.thereafter != 0) {
+ s.hook(ent, LogDropped)
+ return ce
+ }
+ s.hook(ent, LogSampled)
+ }
+ return s.Core.Check(ent, ce)
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/sampler_bench_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/sampler_bench_test.go
new file mode 100644
index 0000000..26769a9
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/sampler_bench_test.go
@@ -0,0 +1,285 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "fmt"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "go.uber.org/atomic"
+ "go.uber.org/zap/internal/ztest"
+ . "go.uber.org/zap/zapcore"
+)
+
+var counterTestCases = [][]string{
+ // some stuff I made up
+ {
+ "foo",
+ "bar",
+ "baz",
+ "alpha",
+ "bravo",
+ "charlie",
+ "delta",
+ },
+
+ // shuf -n50 /usr/share/dict/words
+ {
+ "unbracing",
+ "stereotomy",
+ "supranervian",
+ "moaning",
+ "exchangeability",
+ "gunyang",
+ "sulcation",
+ "dariole",
+ "archheresy",
+ "synchronistically",
+ "clips",
+ "unsanctioned",
+ "Argoan",
+ "liparomphalus",
+ "layship",
+ "Fregatae",
+ "microzoology",
+ "glaciaria",
+ "Frugivora",
+ "patterist",
+ "Grossulariaceae",
+ "lithotint",
+ "bargander",
+ "opisthographical",
+ "cacography",
+ "chalkstone",
+ "nonsubstantialism",
+ "sardonicism",
+ "calamiform",
+ "lodginghouse",
+ "predisposedly",
+ "topotypic",
+ "broideress",
+ "outrange",
+ "gingivolabial",
+ "monoazo",
+ "sparlike",
+ "concameration",
+ "untoothed",
+ "Camorrism",
+ "reissuer",
+ "soap",
+ "palaiotype",
+ "countercharm",
+ "yellowbird",
+ "palterly",
+ "writinger",
+ "boatfalls",
+ "tuglike",
+ "underbitten",
+ },
+
+ // shuf -n100 /usr/share/dict/words
+ {
+ "rooty",
+ "malcultivation",
+ "degrade",
+ "pseudoindependent",
+ "stillatory",
+ "antiseptize",
+ "protoamphibian",
+ "antiar",
+ "Esther",
+ "pseudelminth",
+ "superfluitance",
+ "teallite",
+ "disunity",
+ "spirignathous",
+ "vergency",
+ "myliobatid",
+ "inosic",
+ "overabstemious",
+ "patriarchally",
+ "foreimagine",
+ "coetaneity",
+ "hemimellitene",
+ "hyperspatial",
+ "aulophyte",
+ "electropoion",
+ "antitrope",
+ "Amarantus",
+ "smaltine",
+ "lighthead",
+ "syntonically",
+ "incubous",
+ "versation",
+ "cirsophthalmia",
+ "Ulidian",
+ "homoeography",
+ "Velella",
+ "Hecatean",
+ "serfage",
+ "Spermaphyta",
+ "palatoplasty",
+ "electroextraction",
+ "aconite",
+ "avirulence",
+ "initiator",
+ "besmear",
+ "unrecognizably",
+ "euphoniousness",
+ "balbuties",
+ "pascuage",
+ "quebracho",
+ "Yakala",
+ "auriform",
+ "sevenbark",
+ "superorganism",
+ "telesterion",
+ "ensand",
+ "nagaika",
+ "anisuria",
+ "etching",
+ "soundingly",
+ "grumpish",
+ "drillmaster",
+ "perfumed",
+ "dealkylate",
+ "anthracitiferous",
+ "predefiance",
+ "sulphoxylate",
+ "freeness",
+ "untucking",
+ "misworshiper",
+ "Nestorianize",
+ "nonegoistical",
+ "construe",
+ "upstroke",
+ "teated",
+ "nasolachrymal",
+ "Mastodontidae",
+ "gallows",
+ "radioluminescent",
+ "uncourtierlike",
+ "phasmatrope",
+ "Clunisian",
+ "drainage",
+ "sootless",
+ "brachyfacial",
+ "antiheroism",
+ "irreligionize",
+ "ked",
+ "unfact",
+ "nonprofessed",
+ "milady",
+ "conjecture",
+ "Arctomys",
+ "guapilla",
+ "Sassenach",
+ "emmetrope",
+ "rosewort",
+ "raphidiferous",
+ "pooh",
+ "Tyndallize",
+ },
+}
+
+func BenchmarkSampler_Check(b *testing.B) {
+ for _, keys := range counterTestCases {
+ b.Run(fmt.Sprintf("%v keys", len(keys)), func(b *testing.B) {
+ fac := NewSamplerWithOptions(
+ NewCore(
+ NewJSONEncoder(testEncoderConfig()),
+ &ztest.Discarder{},
+ DebugLevel,
+ ),
+ time.Millisecond, 1, 1000)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ i := 0
+ for pb.Next() {
+ ent := Entry{
+ Level: DebugLevel + Level(i%4),
+ Message: keys[i],
+ }
+ _ = fac.Check(ent, nil)
+ i++
+ if n := len(keys); i >= n {
+ i -= n
+ }
+ }
+ })
+ })
+ }
+}
+
+func makeSamplerCountingHook() (func(_ Entry, dec SamplingDecision), *atomic.Int64, *atomic.Int64) {
+ droppedCount := new(atomic.Int64)
+ sampledCount := new(atomic.Int64)
+ h := func(_ Entry, dec SamplingDecision) {
+ if dec&LogDropped > 0 {
+ droppedCount.Inc()
+ } else if dec&LogSampled > 0 {
+ sampledCount.Inc()
+ }
+ }
+ return h, droppedCount, sampledCount
+}
+
+func BenchmarkSampler_CheckWithHook(b *testing.B) {
+ hook, dropped, sampled := makeSamplerCountingHook()
+ for _, keys := range counterTestCases {
+ b.Run(fmt.Sprintf("%v keys", len(keys)), func(b *testing.B) {
+ fac := NewSamplerWithOptions(
+ NewCore(
+ NewJSONEncoder(testEncoderConfig()),
+ &ztest.Discarder{},
+ DebugLevel,
+ ),
+ time.Millisecond,
+ 1,
+ 1000,
+ SamplerHook(hook),
+ )
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ i := 0
+ for pb.Next() {
+ ent := Entry{
+ Level: DebugLevel + Level(i%4),
+ Message: keys[i],
+ }
+ _ = fac.Check(ent, nil)
+ i++
+ if n := len(keys); i >= n {
+ i -= n
+ }
+ }
+ })
+ // We expect to see 1000 dropped messages for every sampled per settings,
+ // with a delta due to less 1000 messages getting dropped after initial one
+ // is sampled.
+ assert.Greater(b, dropped.Load()/1000, sampled.Load()-1000)
+ dropped.Store(0)
+ sampled.Store(0)
+ })
+ }
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/sampler_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/sampler_test.go
new file mode 100644
index 0000000..194427a
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/sampler_test.go
@@ -0,0 +1,328 @@
+// Copyright (c) 2016-2022 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "fmt"
+ "runtime"
+ "sync"
+ "testing"
+ "time"
+
+ "go.uber.org/atomic"
+ "go.uber.org/zap/internal/ztest"
+ . "go.uber.org/zap/zapcore"
+ "go.uber.org/zap/zaptest/observer"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func fakeSampler(lvl LevelEnabler, tick time.Duration, first, thereafter int) (Core, *observer.ObservedLogs) {
+ core, logs := observer.New(lvl)
+ // Keep using deprecated constructor for cc.
+ core = NewSampler(core, tick, first, thereafter)
+ return core, logs
+}
+
+func assertSequence(t testing.TB, logs []observer.LoggedEntry, lvl Level, seq ...int64) {
+ seen := make([]int64, len(logs))
+ for i, entry := range logs {
+ require.Equal(t, "", entry.Message, "Message wasn't created by writeSequence.")
+ require.Equal(t, 1, len(entry.Context), "Unexpected number of fields.")
+ require.Equal(t, lvl, entry.Level, "Unexpected level.")
+ f := entry.Context[0]
+ require.Equal(t, "iter", f.Key, "Unexpected field key.")
+ require.Equal(t, Int64Type, f.Type, "Unexpected field type")
+ seen[i] = f.Integer
+ }
+ assert.Equal(t, seq, seen, "Unexpected sequence logged at level %v.", lvl)
+}
+
+func writeSequence(core Core, n int, lvl Level) {
+ // All tests using writeSequence verify that counters are shared between
+ // parent and child cores.
+ core = core.With([]Field{makeInt64Field("iter", n)})
+ if ce := core.Check(Entry{Level: lvl, Time: time.Now()}, nil); ce != nil {
+ ce.Write()
+ }
+}
+
+func TestSampler(t *testing.T) {
+ for _, lvl := range []Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, FatalLevel} {
+ sampler, logs := fakeSampler(DebugLevel, time.Minute, 2, 3)
+
+ // Ensure that counts aren't shared between levels.
+ probeLevel := DebugLevel
+ if lvl == DebugLevel {
+ probeLevel = InfoLevel
+ }
+ for i := 0; i < 10; i++ {
+ writeSequence(sampler, 1, probeLevel)
+ }
+ // Clear any output.
+ logs.TakeAll()
+
+ for i := 1; i < 10; i++ {
+ writeSequence(sampler, i, lvl)
+ }
+ assertSequence(t, logs.TakeAll(), lvl, 1, 2, 5, 8)
+ }
+}
+
+func TestLevelOfSampler(t *testing.T) {
+ levels := []Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, FatalLevel}
+ for _, lvl := range levels {
+ lvl := lvl
+ t.Run(lvl.String(), func(t *testing.T) {
+ t.Parallel()
+
+ sampler, _ := fakeSampler(lvl, time.Minute, 2, 3)
+ assert.Equal(t, lvl, LevelOf(sampler), "Sampler level did not match.")
+ })
+ }
+}
+
+func TestSamplerDisabledLevels(t *testing.T) {
+ sampler, logs := fakeSampler(InfoLevel, time.Minute, 1, 100)
+
+ // Shouldn't be counted, because debug logging isn't enabled.
+ writeSequence(sampler, 1, DebugLevel)
+ writeSequence(sampler, 2, InfoLevel)
+ assertSequence(t, logs.TakeAll(), InfoLevel, 2)
+}
+
+func TestSamplerTicking(t *testing.T) {
+ // Ensure that we're resetting the sampler's counter every tick.
+ sampler, logs := fakeSampler(DebugLevel, 10*time.Millisecond, 5, 10)
+
+ // If we log five or fewer messages every tick, none of them should be
+ // dropped.
+ for tick := 0; tick < 2; tick++ {
+ for i := 1; i <= 5; i++ {
+ writeSequence(sampler, i, InfoLevel)
+ }
+ ztest.Sleep(15 * time.Millisecond)
+ }
+ assertSequence(
+ t,
+ logs.TakeAll(),
+ InfoLevel,
+ 1, 2, 3, 4, 5, // first tick
+ 1, 2, 3, 4, 5, // second tick
+ )
+
+ // If we log quickly, we should drop some logs. The first five statements
+ // each tick should be logged, then every tenth.
+ for tick := 0; tick < 3; tick++ {
+ for i := 1; i < 18; i++ {
+ writeSequence(sampler, i, InfoLevel)
+ }
+ ztest.Sleep(10 * time.Millisecond)
+ }
+
+ assertSequence(
+ t,
+ logs.TakeAll(),
+ InfoLevel,
+ 1, 2, 3, 4, 5, 15, // first tick
+ 1, 2, 3, 4, 5, 15, // second tick
+ 1, 2, 3, 4, 5, 15, // third tick
+ )
+}
+
+type countingCore struct {
+ logs atomic.Uint32
+}
+
+func (c *countingCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
+ return ce.AddCore(ent, c)
+}
+
+func (c *countingCore) Write(Entry, []Field) error {
+ c.logs.Inc()
+ return nil
+}
+
+func (c *countingCore) With([]Field) Core { return c }
+func (*countingCore) Enabled(Level) bool { return true }
+func (*countingCore) Sync() error { return nil }
+
+func TestSamplerConcurrent(t *testing.T) {
+ const (
+ logsPerTick = 10
+ numMessages = 5
+ numTicks = 25
+ numGoroutines = 10
+ tick = 10 * time.Millisecond
+
+ // We'll make a total of,
+ // (numGoroutines * numTicks * logsPerTick * 2) log attempts
+ // with numMessages unique messages.
+ numLogAttempts = numGoroutines * logsPerTick * numTicks * 2
+ // Of those, we'll accept (logsPerTick * numTicks) entries
+ // for each unique message.
+ expectedCount = numMessages * logsPerTick * numTicks
+ // The rest will be dropped.
+ expectedDropped = numLogAttempts - expectedCount
+ )
+
+ clock := ztest.NewMockClock()
+
+ cc := &countingCore{}
+
+ hook, dropped, sampled := makeSamplerCountingHook()
+ sampler := NewSamplerWithOptions(cc, tick, logsPerTick, 100000, SamplerHook(hook))
+
+ stop := make(chan struct{})
+ var wg sync.WaitGroup
+ for i := 0; i < numGoroutines; i++ {
+ wg.Add(1)
+ go func(i int, ticker *time.Ticker) {
+ defer wg.Done()
+ defer ticker.Stop()
+
+ for {
+ select {
+ case <-stop:
+ return
+
+ case <-ticker.C:
+ for j := 0; j < logsPerTick*2; j++ {
+ msg := fmt.Sprintf("msg%v", i%numMessages)
+ ent := Entry{
+ Level: DebugLevel,
+ Message: msg,
+ Time: clock.Now(),
+ }
+ if ce := sampler.Check(ent, nil); ce != nil {
+ ce.Write()
+ }
+
+ // Give a chance for other goroutines to run.
+ runtime.Gosched()
+ }
+ }
+ }
+ }(i, clock.NewTicker(tick))
+ }
+
+ clock.Add(tick * numTicks)
+ close(stop)
+ wg.Wait()
+
+ assert.Equal(
+ t,
+ expectedCount,
+ int(cc.logs.Load()),
+ "Unexpected number of logs",
+ )
+ assert.Equal(t,
+ expectedCount,
+ int(sampled.Load()),
+ "Unexpected number of logs sampled",
+ )
+ assert.Equal(t,
+ expectedDropped,
+ int(dropped.Load()),
+ "Unexpected number of logs dropped",
+ )
+}
+
+func TestSamplerRaces(t *testing.T) {
+ sampler, _ := fakeSampler(DebugLevel, time.Minute, 1, 1000)
+
+ var wg sync.WaitGroup
+ start := make(chan struct{})
+
+ for i := 0; i < 100; i++ {
+ wg.Add(1)
+ go func() {
+ <-start
+ for j := 0; j < 100; j++ {
+ writeSequence(sampler, j, InfoLevel)
+ }
+ wg.Done()
+ }()
+ }
+
+ close(start)
+ wg.Wait()
+}
+
+func TestSamplerUnknownLevels(t *testing.T) {
+ // Prove that out-of-bounds levels don't panic.
+ unknownLevels := []Level{
+ DebugLevel - 1,
+ FatalLevel + 1,
+ }
+
+ for _, lvl := range unknownLevels {
+ t.Run(lvl.String(), func(t *testing.T) {
+ sampler, logs := fakeSampler(lvl, time.Minute, 2, 3)
+ for i := 1; i < 10; i++ {
+ writeSequence(sampler, i, lvl)
+ }
+
+ // Expect no sampling for unknown levels.
+ assertSequence(t, logs.TakeAll(), lvl, 1, 2, 3, 4, 5, 6, 7, 8, 9)
+ })
+ }
+}
+
+func TestSamplerWithZeroThereafter(t *testing.T) {
+ var counter countingCore
+
+ // Logs two messages per second.
+ sampler := NewSamplerWithOptions(&counter, time.Second, 2, 0)
+
+ now := time.Now()
+
+ for i := 0; i < 1000; i++ {
+ ent := Entry{
+ Level: InfoLevel,
+ Message: "msg",
+ Time: now,
+ }
+ if ce := sampler.Check(ent, nil); ce != nil {
+ ce.Write()
+ }
+ }
+
+ assert.Equal(t, 2, int(counter.logs.Load()),
+ "Unexpected number of logs")
+
+ now = now.Add(time.Second)
+
+ for i := 0; i < 1000; i++ {
+ ent := Entry{
+ Level: InfoLevel,
+ Message: "msg",
+ Time: now,
+ }
+ if ce := sampler.Check(ent, nil); ce != nil {
+ ce.Write()
+ }
+ }
+
+ assert.Equal(t, 4, int(counter.logs.Load()),
+ "Unexpected number of logs")
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/tee.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/tee.go
new file mode 100644
index 0000000..9bb32f0
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/tee.go
@@ -0,0 +1,96 @@
+// Copyright (c) 2016-2022 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import "go.uber.org/multierr"
+
+type multiCore []Core
+
+var (
+ _ leveledEnabler = multiCore(nil)
+ _ Core = multiCore(nil)
+)
+
+// NewTee creates a Core that duplicates log entries into two or more
+// underlying Cores.
+//
+// Calling it with a single Core returns the input unchanged, and calling
+// it with no input returns a no-op Core.
+func NewTee(cores ...Core) Core {
+ switch len(cores) {
+ case 0:
+ return NewNopCore()
+ case 1:
+ return cores[0]
+ default:
+ return multiCore(cores)
+ }
+}
+
+func (mc multiCore) With(fields []Field) Core {
+ clone := make(multiCore, len(mc))
+ for i := range mc {
+ clone[i] = mc[i].With(fields)
+ }
+ return clone
+}
+
+func (mc multiCore) Level() Level {
+ minLvl := _maxLevel // mc is never empty
+ for i := range mc {
+ if lvl := LevelOf(mc[i]); lvl < minLvl {
+ minLvl = lvl
+ }
+ }
+ return minLvl
+}
+
+func (mc multiCore) Enabled(lvl Level) bool {
+ for i := range mc {
+ if mc[i].Enabled(lvl) {
+ return true
+ }
+ }
+ return false
+}
+
+func (mc multiCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
+ for i := range mc {
+ ce = mc[i].Check(ent, ce)
+ }
+ return ce
+}
+
+func (mc multiCore) Write(ent Entry, fields []Field) error {
+ var err error
+ for i := range mc {
+ err = multierr.Append(err, mc[i].Write(ent, fields))
+ }
+ return err
+}
+
+func (mc multiCore) Sync() error {
+ var err error
+ for i := range mc {
+ err = multierr.Append(err, mc[i].Sync())
+ }
+ return err
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/tee_logger_bench_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/tee_logger_bench_test.go
new file mode 100644
index 0000000..b30a173
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/tee_logger_bench_test.go
@@ -0,0 +1,62 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "testing"
+
+ "go.uber.org/zap/internal/ztest"
+ . "go.uber.org/zap/zapcore"
+)
+
+func withBenchedTee(b *testing.B, f func(Core)) {
+ fac := NewTee(
+ NewCore(NewJSONEncoder(testEncoderConfig()), &ztest.Discarder{}, DebugLevel),
+ NewCore(NewJSONEncoder(testEncoderConfig()), &ztest.Discarder{}, InfoLevel),
+ )
+ b.ResetTimer()
+ f(fac)
+}
+
+func BenchmarkTeeCheck(b *testing.B) {
+ cases := []struct {
+ lvl Level
+ msg string
+ }{
+ {DebugLevel, "foo"},
+ {InfoLevel, "bar"},
+ {WarnLevel, "baz"},
+ {ErrorLevel, "babble"},
+ }
+ withBenchedTee(b, func(core Core) {
+ b.RunParallel(func(pb *testing.PB) {
+ i := 0
+ for pb.Next() {
+ tt := cases[i]
+ entry := Entry{Level: tt.lvl, Message: tt.msg}
+ if cm := core.Check(entry, nil); cm != nil {
+ cm.Write(Field{Key: "i", Integer: int64(i), Type: Int64Type})
+ }
+ i = (i + 1) % len(cases)
+ }
+ })
+ })
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/tee_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/tee_test.go
new file mode 100644
index 0000000..b2b9c9d
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/tee_test.go
@@ -0,0 +1,191 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "errors"
+ "testing"
+
+ "go.uber.org/zap/internal/ztest"
+ . "go.uber.org/zap/zapcore"
+ "go.uber.org/zap/zaptest/observer"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func withTee(f func(core Core, debugLogs, warnLogs *observer.ObservedLogs)) {
+ debugLogger, debugLogs := observer.New(DebugLevel)
+ warnLogger, warnLogs := observer.New(WarnLevel)
+ tee := NewTee(debugLogger, warnLogger)
+ f(tee, debugLogs, warnLogs)
+}
+
+func TestTeeUnusualInput(t *testing.T) {
+ // Verify that Tee handles receiving one and no inputs correctly.
+ t.Run("one input", func(t *testing.T) {
+ obs, _ := observer.New(DebugLevel)
+ assert.Equal(t, obs, NewTee(obs), "Expected to return single inputs unchanged.")
+ })
+ t.Run("no input", func(t *testing.T) {
+ assert.Equal(t, NewNopCore(), NewTee(), "Expected to return NopCore.")
+ })
+}
+
+func TestLevelOfTee(t *testing.T) {
+ debugLogger, _ := observer.New(DebugLevel)
+ warnLogger, _ := observer.New(WarnLevel)
+
+ tests := []struct {
+ desc string
+ give []Core
+ want Level
+ }{
+ {desc: "empty", want: InvalidLevel},
+ {
+ desc: "debug",
+ give: []Core{debugLogger},
+ want: DebugLevel,
+ },
+ {
+ desc: "warn",
+ give: []Core{warnLogger},
+ want: WarnLevel,
+ },
+ {
+ desc: "debug and warn",
+ give: []Core{warnLogger, debugLogger},
+ want: DebugLevel,
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.desc, func(t *testing.T) {
+ t.Parallel()
+
+ core := NewTee(tt.give...)
+ assert.Equal(t, tt.want, LevelOf(core), "Level of Tee core did not match.")
+ })
+ }
+}
+
+func TestTeeCheck(t *testing.T) {
+ withTee(func(tee Core, debugLogs, warnLogs *observer.ObservedLogs) {
+ debugEntry := Entry{Level: DebugLevel, Message: "log-at-debug"}
+ infoEntry := Entry{Level: InfoLevel, Message: "log-at-info"}
+ warnEntry := Entry{Level: WarnLevel, Message: "log-at-warn"}
+ errorEntry := Entry{Level: ErrorLevel, Message: "log-at-error"}
+ for _, ent := range []Entry{debugEntry, infoEntry, warnEntry, errorEntry} {
+ if ce := tee.Check(ent, nil); ce != nil {
+ ce.Write()
+ }
+ }
+
+ assert.Equal(t, []observer.LoggedEntry{
+ {Entry: debugEntry, Context: []Field{}},
+ {Entry: infoEntry, Context: []Field{}},
+ {Entry: warnEntry, Context: []Field{}},
+ {Entry: errorEntry, Context: []Field{}},
+ }, debugLogs.All())
+
+ assert.Equal(t, []observer.LoggedEntry{
+ {Entry: warnEntry, Context: []Field{}},
+ {Entry: errorEntry, Context: []Field{}},
+ }, warnLogs.All())
+ })
+}
+
+func TestTeeWrite(t *testing.T) {
+ // Calling the tee's Write method directly should always log, regardless of
+ // the configured level.
+ withTee(func(tee Core, debugLogs, warnLogs *observer.ObservedLogs) {
+ debugEntry := Entry{Level: DebugLevel, Message: "log-at-debug"}
+ warnEntry := Entry{Level: WarnLevel, Message: "log-at-warn"}
+ for _, ent := range []Entry{debugEntry, warnEntry} {
+ tee.Write(ent, nil)
+ }
+
+ for _, logs := range []*observer.ObservedLogs{debugLogs, warnLogs} {
+ assert.Equal(t, []observer.LoggedEntry{
+ {Entry: debugEntry, Context: []Field{}},
+ {Entry: warnEntry, Context: []Field{}},
+ }, logs.All())
+ }
+ })
+}
+
+func TestTeeWith(t *testing.T) {
+ withTee(func(tee Core, debugLogs, warnLogs *observer.ObservedLogs) {
+ f := makeInt64Field("k", 42)
+ tee = tee.With([]Field{f})
+ ent := Entry{Level: WarnLevel, Message: "log-at-warn"}
+ if ce := tee.Check(ent, nil); ce != nil {
+ ce.Write()
+ }
+
+ for _, logs := range []*observer.ObservedLogs{debugLogs, warnLogs} {
+ assert.Equal(t, []observer.LoggedEntry{
+ {Entry: ent, Context: []Field{f}},
+ }, logs.All())
+ }
+ })
+}
+
+func TestTeeEnabled(t *testing.T) {
+ infoLogger, _ := observer.New(InfoLevel)
+ warnLogger, _ := observer.New(WarnLevel)
+ tee := NewTee(infoLogger, warnLogger)
+ tests := []struct {
+ lvl Level
+ enabled bool
+ }{
+ {DebugLevel, false},
+ {InfoLevel, true},
+ {WarnLevel, true},
+ {ErrorLevel, true},
+ {DPanicLevel, true},
+ {PanicLevel, true},
+ {FatalLevel, true},
+ }
+
+ for _, tt := range tests {
+ assert.Equal(t, tt.enabled, tee.Enabled(tt.lvl), "Unexpected Enabled result for level %s.", tt.lvl)
+ }
+}
+
+func TestTeeSync(t *testing.T) {
+ infoLogger, _ := observer.New(InfoLevel)
+ warnLogger, _ := observer.New(WarnLevel)
+ tee := NewTee(infoLogger, warnLogger)
+ assert.NoError(t, tee.Sync(), "Unexpected error from Syncing a tee.")
+
+ sink := &ztest.Discarder{}
+ err := errors.New("failed")
+ sink.SetError(err)
+
+ noSync := NewCore(
+ NewJSONEncoder(testEncoderConfig()),
+ sink,
+ DebugLevel,
+ )
+ tee = NewTee(tee, noSync)
+ assert.Equal(t, err, tee.Sync(), "Expected an error when part of tee can't Sync.")
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/write_syncer.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/write_syncer.go
new file mode 100644
index 0000000..d4a1af3
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/write_syncer.go
@@ -0,0 +1,122 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "io"
+ "sync"
+
+ "go.uber.org/multierr"
+)
+
+// A WriteSyncer is an io.Writer that can also flush any buffered data. Note
+// that *os.File (and thus, os.Stderr and os.Stdout) implement WriteSyncer.
+type WriteSyncer interface {
+ io.Writer
+ Sync() error
+}
+
+// AddSync converts an io.Writer to a WriteSyncer. It attempts to be
+// intelligent: if the concrete type of the io.Writer implements WriteSyncer,
+// we'll use the existing Sync method. If it doesn't, we'll add a no-op Sync.
+func AddSync(w io.Writer) WriteSyncer {
+ switch w := w.(type) {
+ case WriteSyncer:
+ return w
+ default:
+ return writerWrapper{w}
+ }
+}
+
+type lockedWriteSyncer struct {
+ sync.Mutex
+ ws WriteSyncer
+}
+
+// Lock wraps a WriteSyncer in a mutex to make it safe for concurrent use. In
+// particular, *os.Files must be locked before use.
+func Lock(ws WriteSyncer) WriteSyncer {
+ if _, ok := ws.(*lockedWriteSyncer); ok {
+ // no need to layer on another lock
+ return ws
+ }
+ return &lockedWriteSyncer{ws: ws}
+}
+
+func (s *lockedWriteSyncer) Write(bs []byte) (int, error) {
+ s.Lock()
+ n, err := s.ws.Write(bs)
+ s.Unlock()
+ return n, err
+}
+
+func (s *lockedWriteSyncer) Sync() error {
+ s.Lock()
+ err := s.ws.Sync()
+ s.Unlock()
+ return err
+}
+
+type writerWrapper struct {
+ io.Writer
+}
+
+func (w writerWrapper) Sync() error {
+ return nil
+}
+
+type multiWriteSyncer []WriteSyncer
+
+// NewMultiWriteSyncer creates a WriteSyncer that duplicates its writes
+// and sync calls, much like io.MultiWriter.
+func NewMultiWriteSyncer(ws ...WriteSyncer) WriteSyncer {
+ if len(ws) == 1 {
+ return ws[0]
+ }
+ return multiWriteSyncer(ws)
+}
+
+// See https://golang.org/src/io/multi.go
+// When not all underlying syncers write the same number of bytes,
+// the smallest number is returned even though Write() is called on
+// all of them.
+func (ws multiWriteSyncer) Write(p []byte) (int, error) {
+ var writeErr error
+ nWritten := 0
+ for _, w := range ws {
+ n, err := w.Write(p)
+ writeErr = multierr.Append(writeErr, err)
+ if nWritten == 0 && n != 0 {
+ nWritten = n
+ } else if n < nWritten {
+ nWritten = n
+ }
+ }
+ return nWritten, writeErr
+}
+
+func (ws multiWriteSyncer) Sync() error {
+ var err error
+ for _, w := range ws {
+ err = multierr.Append(err, w.Sync())
+ }
+ return err
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/write_syncer_bench_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/write_syncer_bench_test.go
new file mode 100644
index 0000000..db6ec45
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/write_syncer_bench_test.go
@@ -0,0 +1,90 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "os"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "go.uber.org/zap/internal/ztest"
+)
+
+func BenchmarkMultiWriteSyncer(b *testing.B) {
+ b.Run("2 discarder", func(b *testing.B) {
+ w := NewMultiWriteSyncer(
+ &ztest.Discarder{},
+ &ztest.Discarder{},
+ )
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ w.Write([]byte("foobarbazbabble"))
+ }
+ })
+ })
+ b.Run("4 discarder", func(b *testing.B) {
+ w := NewMultiWriteSyncer(
+ &ztest.Discarder{},
+ &ztest.Discarder{},
+ &ztest.Discarder{},
+ &ztest.Discarder{},
+ )
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ w.Write([]byte("foobarbazbabble"))
+ }
+ })
+ })
+ b.Run("4 discarder with buffer", func(b *testing.B) {
+ w := &BufferedWriteSyncer{
+ WS: NewMultiWriteSyncer(
+ &ztest.Discarder{},
+ &ztest.Discarder{},
+ &ztest.Discarder{},
+ &ztest.Discarder{},
+ ),
+ }
+ defer w.Stop()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ w.Write([]byte("foobarbazbabble"))
+ }
+ })
+ })
+}
+
+func BenchmarkWriteSyncer(b *testing.B) {
+ b.Run("write file with no buffer", func(b *testing.B) {
+ file, err := os.CreateTemp(b.TempDir(), "test.log")
+ require.NoError(b, err)
+
+ w := AddSync(file)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ w.Write([]byte("foobarbazbabble"))
+ }
+ })
+ })
+}
diff --git a/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/write_syncer_test.go b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/write_syncer_test.go
new file mode 100644
index 0000000..4748be7
--- /dev/null
+++ b/dependencies/pkg/mod/go.uber.org/zap@v1.23.0/zapcore/write_syncer_test.go
@@ -0,0 +1,136 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "go.uber.org/zap/internal/ztest"
+)
+
+type writeSyncSpy struct {
+ io.Writer
+ ztest.Syncer
+}
+
+func requireWriteWorks(t testing.TB, ws WriteSyncer) {
+ n, err := ws.Write([]byte("foo"))
+ require.NoError(t, err, "Unexpected error writing to WriteSyncer.")
+ require.Equal(t, 3, n, "Wrote an unexpected number of bytes.")
+}
+
+func TestAddSyncWriteSyncer(t *testing.T) {
+ buf := &bytes.Buffer{}
+ concrete := &writeSyncSpy{Writer: buf}
+ ws := AddSync(concrete)
+ requireWriteWorks(t, ws)
+
+ require.NoError(t, ws.Sync(), "Unexpected error syncing a WriteSyncer.")
+ require.True(t, concrete.Called(), "Expected to dispatch to concrete type's Sync method.")
+
+ concrete.SetError(errors.New("fail"))
+ assert.Error(t, ws.Sync(), "Expected to propagate errors from concrete type's Sync method.")
+}
+
+func TestAddSyncWriter(t *testing.T) {
+ // If we pass a plain io.Writer, make sure that we still get a WriteSyncer
+ // with a no-op Sync.
+ buf := &bytes.Buffer{}
+ ws := AddSync(buf)
+ requireWriteWorks(t, ws)
+ assert.NoError(t, ws.Sync(), "Unexpected error calling a no-op Sync method.")
+}
+
+func TestNewMultiWriteSyncerWorksForSingleWriter(t *testing.T) {
+ w := &ztest.Buffer{}
+
+ ws := NewMultiWriteSyncer(w)
+ assert.Equal(t, w, ws, "Expected NewMultiWriteSyncer to return the same WriteSyncer object for a single argument.")
+
+ ws.Sync()
+ assert.True(t, w.Called(), "Expected Sync to be called on the created WriteSyncer")
+}
+
+func TestMultiWriteSyncerWritesBoth(t *testing.T) {
+ first := &bytes.Buffer{}
+ second := &bytes.Buffer{}
+ ws := NewMultiWriteSyncer(AddSync(first), AddSync(second))
+
+ msg := []byte("dumbledore")
+ n, err := ws.Write(msg)
+ require.NoError(t, err, "Expected successful buffer write")
+ assert.Equal(t, len(msg), n)
+
+ assert.Equal(t, msg, first.Bytes())
+ assert.Equal(t, msg, second.Bytes())
+}
+
+func TestMultiWriteSyncerFailsWrite(t *testing.T) {
+ ws := NewMultiWriteSyncer(AddSync(&ztest.FailWriter{}))
+ _, err := ws.Write([]byte("test"))
+ assert.Error(t, err, "Write error should propagate")
+}
+
+func TestMultiWriteSyncerFailsShortWrite(t *testing.T) {
+ ws := NewMultiWriteSyncer(AddSync(&ztest.ShortWriter{}))
+ n, err := ws.Write([]byte("test"))
+ assert.NoError(t, err, "Expected fake-success from short write")
+ assert.Equal(t, 3, n, "Expected byte count to return from underlying writer")
+}
+
+func TestWritestoAllSyncs_EvenIfFirstErrors(t *testing.T) {
+ failer := &ztest.FailWriter{}
+ second := &bytes.Buffer{}
+ ws := NewMultiWriteSyncer(AddSync(failer), AddSync(second))
+
+ _, err := ws.Write([]byte("fail"))
+ assert.Error(t, err, "Expected error from call to a writer that failed")
+ assert.Equal(t, []byte("fail"), second.Bytes(), "Expected second sink to be written after first error")
+}
+
+func TestMultiWriteSyncerSync_PropagatesErrors(t *testing.T) {
+ badsink := &ztest.Buffer{}
+ badsink.SetError(errors.New("sink is full"))
+ ws := NewMultiWriteSyncer(&ztest.Discarder{}, badsink)
+
+ assert.Error(t, ws.Sync(), "Expected sync error to propagate")
+}
+
+func TestMultiWriteSyncerSync_NoErrorsOnDiscard(t *testing.T) {
+ ws := NewMultiWriteSyncer(&ztest.Discarder{})
+ assert.NoError(t, ws.Sync(), "Expected error-free sync to /dev/null")
+}
+
+func TestMultiWriteSyncerSync_AllCalled(t *testing.T) {
+ failed, second := &ztest.Buffer{}, &ztest.Buffer{}
+
+ failed.SetError(errors.New("disposal broken"))
+ ws := NewMultiWriteSyncer(failed, second)
+
+ assert.Error(t, ws.Sync(), "Expected first sink to fail")
+ assert.True(t, failed.Called(), "Expected first sink to have Sync method called.")
+ assert.True(t, second.Called(), "Expected call to Sync even with first failure.")
+}