aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/go.uber.org/zap
diff options
context:
space:
mode:
authoruzhas <uzhas@ydb.tech>2023-11-16 16:04:50 +0300
committeruzhas <uzhas@ydb.tech>2023-11-16 17:46:46 +0300
commit46f0c0079bb50609d2eeb6586642bcf114fc5239 (patch)
tree84e4e4978d57fe5de321ba69bf9d0c290de60a66 /vendor/go.uber.org/zap
parent73045e389397816cc2bdd6cd7818b4bce427b265 (diff)
downloadydb-46f0c0079bb50609d2eeb6586642bcf114fc5239.tar.gz
enable ya make for go projects
Diffstat (limited to 'vendor/go.uber.org/zap')
-rw-r--r--vendor/go.uber.org/zap/array.go447
-rw-r--r--vendor/go.uber.org/zap/array_test.go316
-rw-r--r--vendor/go.uber.org/zap/buffer/buffer.go146
-rw-r--r--vendor/go.uber.org/zap/buffer/buffer_test.go95
-rw-r--r--vendor/go.uber.org/zap/buffer/gotest/ya.make5
-rw-r--r--vendor/go.uber.org/zap/buffer/pool.go53
-rw-r--r--vendor/go.uber.org/zap/buffer/pool_test.go52
-rw-r--r--vendor/go.uber.org/zap/buffer/ya.make19
-rw-r--r--vendor/go.uber.org/zap/clock_test.go47
-rw-r--r--vendor/go.uber.org/zap/common_test.go57
-rw-r--r--vendor/go.uber.org/zap/config.go330
-rw-r--r--vendor/go.uber.org/zap/config_test.go203
-rw-r--r--vendor/go.uber.org/zap/doc.go117
-rw-r--r--vendor/go.uber.org/zap/encoder.go79
-rw-r--r--vendor/go.uber.org/zap/encoder_test.go88
-rw-r--r--vendor/go.uber.org/zap/error.go82
-rw-r--r--vendor/go.uber.org/zap/error_test.go133
-rw-r--r--vendor/go.uber.org/zap/example_test.go413
-rw-r--r--vendor/go.uber.org/zap/field.go613
-rw-r--r--vendor/go.uber.org/zap/field_test.go316
-rw-r--r--vendor/go.uber.org/zap/flag.go39
-rw-r--r--vendor/go.uber.org/zap/flag_test.go103
-rw-r--r--vendor/go.uber.org/zap/global.go169
-rw-r--r--vendor/go.uber.org/zap/global_test.go281
-rw-r--r--vendor/go.uber.org/zap/gotest/ya.make5
-rw-r--r--vendor/go.uber.org/zap/http_handler.go140
-rw-r--r--vendor/go.uber.org/zap/http_handler_test.go217
-rw-r--r--vendor/go.uber.org/zap/increase_level_test.go94
-rw-r--r--vendor/go.uber.org/zap/internal/bufferpool/bufferpool.go31
-rw-r--r--vendor/go.uber.org/zap/internal/bufferpool/ya.make9
-rw-r--r--vendor/go.uber.org/zap/internal/color/color.go44
-rw-r--r--vendor/go.uber.org/zap/internal/color/color_test.go36
-rw-r--r--vendor/go.uber.org/zap/internal/color/gotest/ya.make5
-rw-r--r--vendor/go.uber.org/zap/internal/color/ya.make15
-rw-r--r--vendor/go.uber.org/zap/internal/exit/exit.go66
-rw-r--r--vendor/go.uber.org/zap/internal/exit/exit_test.go48
-rw-r--r--vendor/go.uber.org/zap/internal/exit/gotest/ya.make5
-rw-r--r--vendor/go.uber.org/zap/internal/exit/ya.make15
-rw-r--r--vendor/go.uber.org/zap/internal/level_enabler.go37
-rw-r--r--vendor/go.uber.org/zap/internal/pool/gotest/ya.make5
-rw-r--r--vendor/go.uber.org/zap/internal/pool/pool.go58
-rw-r--r--vendor/go.uber.org/zap/internal/pool/pool_test.go106
-rw-r--r--vendor/go.uber.org/zap/internal/pool/ya.make15
-rw-r--r--vendor/go.uber.org/zap/internal/readme/readme.go244
-rw-r--r--vendor/go.uber.org/zap/internal/readme/ya.make9
-rw-r--r--vendor/go.uber.org/zap/internal/stacktrace/gotest/ya.make5
-rw-r--r--vendor/go.uber.org/zap/internal/stacktrace/stack.go181
-rw-r--r--vendor/go.uber.org/zap/internal/stacktrace/stack_test.go106
-rw-r--r--vendor/go.uber.org/zap/internal/stacktrace/ya.make15
-rw-r--r--vendor/go.uber.org/zap/internal/ya.make19
-rw-r--r--vendor/go.uber.org/zap/internal/ztest/clock.go153
-rw-r--r--vendor/go.uber.org/zap/internal/ztest/clock_test.go80
-rw-r--r--vendor/go.uber.org/zap/internal/ztest/doc.go24
-rw-r--r--vendor/go.uber.org/zap/internal/ztest/gotest/ya.make5
-rw-r--r--vendor/go.uber.org/zap/internal/ztest/timeout.go59
-rw-r--r--vendor/go.uber.org/zap/internal/ztest/writer.go96
-rw-r--r--vendor/go.uber.org/zap/internal/ztest/ya.make18
-rw-r--r--vendor/go.uber.org/zap/leak_test.go31
-rw-r--r--vendor/go.uber.org/zap/level.go153
-rw-r--r--vendor/go.uber.org/zap/level_test.go140
-rw-r--r--vendor/go.uber.org/zap/logger.go432
-rw-r--r--vendor/go.uber.org/zap/logger_bench_test.go361
-rw-r--r--vendor/go.uber.org/zap/logger_test.go938
-rw-r--r--vendor/go.uber.org/zap/options.go167
-rw-r--r--vendor/go.uber.org/zap/sink.go180
-rw-r--r--vendor/go.uber.org/zap/sink_test.go107
-rw-r--r--vendor/go.uber.org/zap/sink_windows_test.go71
-rw-r--r--vendor/go.uber.org/zap/sugar.go437
-rw-r--r--vendor/go.uber.org/zap/sugar_test.go515
-rw-r--r--vendor/go.uber.org/zap/time.go27
-rw-r--r--vendor/go.uber.org/zap/time_test.go42
-rw-r--r--vendor/go.uber.org/zap/writer.go98
-rw-r--r--vendor/go.uber.org/zap/writer_test.go266
-rw-r--r--vendor/go.uber.org/zap/ya.make66
-rw-r--r--vendor/go.uber.org/zap/zapcore/buffered_write_syncer.go219
-rw-r--r--vendor/go.uber.org/zap/zapcore/buffered_write_syncer_bench_test.go55
-rw-r--r--vendor/go.uber.org/zap/zapcore/buffered_write_syncer_test.go140
-rw-r--r--vendor/go.uber.org/zap/zapcore/clock.go48
-rw-r--r--vendor/go.uber.org/zap/zapcore/clock_test.go44
-rw-r--r--vendor/go.uber.org/zap/zapcore/console_encoder.go157
-rw-r--r--vendor/go.uber.org/zap/zapcore/console_encoder_bench_test.go49
-rw-r--r--vendor/go.uber.org/zap/zapcore/console_encoder_test.go91
-rw-r--r--vendor/go.uber.org/zap/zapcore/core.go122
-rw-r--r--vendor/go.uber.org/zap/zapcore/core_test.go165
-rw-r--r--vendor/go.uber.org/zap/zapcore/doc.go24
-rw-r--r--vendor/go.uber.org/zap/zapcore/encoder.go451
-rw-r--r--vendor/go.uber.org/zap/zapcore/encoder_test.go733
-rw-r--r--vendor/go.uber.org/zap/zapcore/entry.go298
-rw-r--r--vendor/go.uber.org/zap/zapcore/entry_test.go149
-rw-r--r--vendor/go.uber.org/zap/zapcore/error.go136
-rw-r--r--vendor/go.uber.org/zap/zapcore/error_test.go210
-rw-r--r--vendor/go.uber.org/zap/zapcore/field.go233
-rw-r--r--vendor/go.uber.org/zap/zapcore/gotest/ya.make5
-rw-r--r--vendor/go.uber.org/zap/zapcore/hook.go77
-rw-r--r--vendor/go.uber.org/zap/zapcore/increase_level.go75
-rw-r--r--vendor/go.uber.org/zap/zapcore/json_encoder.go583
-rw-r--r--vendor/go.uber.org/zap/zapcore/json_encoder_bench_test.go131
-rw-r--r--vendor/go.uber.org/zap/zapcore/json_encoder_impl_test.go736
-rw-r--r--vendor/go.uber.org/zap/zapcore/lazy_with.go54
-rw-r--r--vendor/go.uber.org/zap/zapcore/leak_test.go31
-rw-r--r--vendor/go.uber.org/zap/zapcore/level.go229
-rw-r--r--vendor/go.uber.org/zap/zapcore/level_strings.go46
-rw-r--r--vendor/go.uber.org/zap/zapcore/level_strings_test.go38
-rw-r--r--vendor/go.uber.org/zap/zapcore/level_test.go248
-rw-r--r--vendor/go.uber.org/zap/zapcore/marshaler.go61
-rw-r--r--vendor/go.uber.org/zap/zapcore/memory_encoder.go179
-rw-r--r--vendor/go.uber.org/zap/zapcore/memory_encoder_test.go370
-rw-r--r--vendor/go.uber.org/zap/zapcore/reflected_encoder.go41
-rw-r--r--vendor/go.uber.org/zap/zapcore/sampler.go229
-rw-r--r--vendor/go.uber.org/zap/zapcore/sampler_bench_test.go285
-rw-r--r--vendor/go.uber.org/zap/zapcore/tee.go96
-rw-r--r--vendor/go.uber.org/zap/zapcore/write_syncer.go122
-rw-r--r--vendor/go.uber.org/zap/zapcore/write_syncer_bench_test.go101
-rw-r--r--vendor/go.uber.org/zap/zapcore/write_syncer_test.go136
-rw-r--r--vendor/go.uber.org/zap/zapcore/ya.make69
-rw-r--r--vendor/go.uber.org/zap/zapgrpc/gotest/ya.make5
-rw-r--r--vendor/go.uber.org/zap/zapgrpc/ya.make15
-rw-r--r--vendor/go.uber.org/zap/zapgrpc/zapgrpc.go245
-rw-r--r--vendor/go.uber.org/zap/zapgrpc/zapgrpc_test.go263
-rw-r--r--vendor/go.uber.org/zap/zapio/example_test.go47
-rw-r--r--vendor/go.uber.org/zap/zapio/gotest/ya.make5
-rw-r--r--vendor/go.uber.org/zap/zapio/writer.go149
-rw-r--r--vendor/go.uber.org/zap/zapio/writer_test.go248
-rw-r--r--vendor/go.uber.org/zap/zapio/ya.make17
-rw-r--r--vendor/go.uber.org/zap/zaptest/doc.go22
-rw-r--r--vendor/go.uber.org/zap/zaptest/gotest/ya.make5
-rw-r--r--vendor/go.uber.org/zap/zaptest/logger.go140
-rw-r--r--vendor/go.uber.org/zap/zaptest/logger_test.go193
-rw-r--r--vendor/go.uber.org/zap/zaptest/observer/gotest/ya.make5
-rw-r--r--vendor/go.uber.org/zap/zaptest/observer/logged_entry.go39
-rw-r--r--vendor/go.uber.org/zap/zaptest/observer/logged_entry_test.go88
-rw-r--r--vendor/go.uber.org/zap/zaptest/observer/observer.go196
-rw-r--r--vendor/go.uber.org/zap/zaptest/observer/observer_test.go258
-rw-r--r--vendor/go.uber.org/zap/zaptest/observer/ya.make18
-rw-r--r--vendor/go.uber.org/zap/zaptest/testingt.go47
-rw-r--r--vendor/go.uber.org/zap/zaptest/testingt_test.go29
-rw-r--r--vendor/go.uber.org/zap/zaptest/timeout.go45
-rw-r--r--vendor/go.uber.org/zap/zaptest/timeout_test.go43
-rw-r--r--vendor/go.uber.org/zap/zaptest/writer.go44
-rw-r--r--vendor/go.uber.org/zap/zaptest/writer_test.go68
-rw-r--r--vendor/go.uber.org/zap/zaptest/ya.make25
141 files changed, 19724 insertions, 0 deletions
diff --git a/vendor/go.uber.org/zap/array.go b/vendor/go.uber.org/zap/array.go
new file mode 100644
index 0000000000..abfccb566d
--- /dev/null
+++ b/vendor/go.uber.org/zap/array.go
@@ -0,0 +1,447 @@
+// 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 zap
+
+import (
+ "fmt"
+ "time"
+
+ "go.uber.org/zap/zapcore"
+)
+
+// Array constructs a field with the given key and ArrayMarshaler. It provides
+// a flexible, but still type-safe and efficient, way to add array-like types
+// to the logging context. The struct's MarshalLogArray method is called lazily.
+func Array(key string, val zapcore.ArrayMarshaler) Field {
+ return Field{Key: key, Type: zapcore.ArrayMarshalerType, Interface: val}
+}
+
+// Bools constructs a field that carries a slice of bools.
+func Bools(key string, bs []bool) Field {
+ return Array(key, bools(bs))
+}
+
+// ByteStrings constructs a field that carries a slice of []byte, each of which
+// must be UTF-8 encoded text.
+func ByteStrings(key string, bss [][]byte) Field {
+ return Array(key, byteStringsArray(bss))
+}
+
+// Complex128s constructs a field that carries a slice of complex numbers.
+func Complex128s(key string, nums []complex128) Field {
+ return Array(key, complex128s(nums))
+}
+
+// Complex64s constructs a field that carries a slice of complex numbers.
+func Complex64s(key string, nums []complex64) Field {
+ return Array(key, complex64s(nums))
+}
+
+// Durations constructs a field that carries a slice of time.Durations.
+func Durations(key string, ds []time.Duration) Field {
+ return Array(key, durations(ds))
+}
+
+// Float64s constructs a field that carries a slice of floats.
+func Float64s(key string, nums []float64) Field {
+ return Array(key, float64s(nums))
+}
+
+// Float32s constructs a field that carries a slice of floats.
+func Float32s(key string, nums []float32) Field {
+ return Array(key, float32s(nums))
+}
+
+// Ints constructs a field that carries a slice of integers.
+func Ints(key string, nums []int) Field {
+ return Array(key, ints(nums))
+}
+
+// Int64s constructs a field that carries a slice of integers.
+func Int64s(key string, nums []int64) Field {
+ return Array(key, int64s(nums))
+}
+
+// Int32s constructs a field that carries a slice of integers.
+func Int32s(key string, nums []int32) Field {
+ return Array(key, int32s(nums))
+}
+
+// Int16s constructs a field that carries a slice of integers.
+func Int16s(key string, nums []int16) Field {
+ return Array(key, int16s(nums))
+}
+
+// Int8s constructs a field that carries a slice of integers.
+func Int8s(key string, nums []int8) Field {
+ return Array(key, int8s(nums))
+}
+
+// Objects constructs a field with the given key, holding a list of the
+// provided objects that can be marshaled by Zap.
+//
+// Note that these objects must implement zapcore.ObjectMarshaler directly.
+// That is, if you're trying to marshal a []Request, the MarshalLogObject
+// method must be declared on the Request type, not its pointer (*Request).
+// If it's on the pointer, use ObjectValues.
+//
+// Given an object that implements MarshalLogObject on the value receiver, you
+// can log a slice of those objects with Objects like so:
+//
+// type Author struct{ ... }
+// func (a Author) MarshalLogObject(enc zapcore.ObjectEncoder) error
+//
+// var authors []Author = ...
+// logger.Info("loading article", zap.Objects("authors", authors))
+//
+// Similarly, given a type that implements MarshalLogObject on its pointer
+// receiver, you can log a slice of pointers to that object with Objects like
+// so:
+//
+// type Request struct{ ... }
+// func (r *Request) MarshalLogObject(enc zapcore.ObjectEncoder) error
+//
+// var requests []*Request = ...
+// logger.Info("sending requests", zap.Objects("requests", requests))
+//
+// If instead, you have a slice of values of such an object, use the
+// ObjectValues constructor.
+//
+// var requests []Request = ...
+// logger.Info("sending requests", zap.ObjectValues("requests", requests))
+func Objects[T zapcore.ObjectMarshaler](key string, values []T) Field {
+ return Array(key, objects[T](values))
+}
+
+type objects[T zapcore.ObjectMarshaler] []T
+
+func (os objects[T]) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for _, o := range os {
+ if err := arr.AppendObject(o); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// ObjectMarshalerPtr is a constraint that specifies that the given type
+// implements zapcore.ObjectMarshaler on a pointer receiver.
+type ObjectMarshalerPtr[T any] interface {
+ *T
+ zapcore.ObjectMarshaler
+}
+
+// ObjectValues constructs a field with the given key, holding a list of the
+// provided objects, where pointers to these objects can be marshaled by Zap.
+//
+// Note that pointers to these objects must implement zapcore.ObjectMarshaler.
+// That is, if you're trying to marshal a []Request, the MarshalLogObject
+// method must be declared on the *Request type, not the value (Request).
+// If it's on the value, use Objects.
+//
+// Given an object that implements MarshalLogObject on the pointer receiver,
+// you can log a slice of those objects with ObjectValues like so:
+//
+// type Request struct{ ... }
+// func (r *Request) MarshalLogObject(enc zapcore.ObjectEncoder) error
+//
+// var requests []Request = ...
+// logger.Info("sending requests", zap.ObjectValues("requests", requests))
+//
+// If instead, you have a slice of pointers of such an object, use the Objects
+// field constructor.
+//
+// var requests []*Request = ...
+// logger.Info("sending requests", zap.Objects("requests", requests))
+func ObjectValues[T any, P ObjectMarshalerPtr[T]](key string, values []T) Field {
+ return Array(key, objectValues[T, P](values))
+}
+
+type objectValues[T any, P ObjectMarshalerPtr[T]] []T
+
+func (os objectValues[T, P]) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range os {
+ // It is necessary for us to explicitly reference the "P" type.
+ // We cannot simply pass "&os[i]" to AppendObject because its type
+ // is "*T", which the type system does not consider as
+ // implementing ObjectMarshaler.
+ // Only the type "P" satisfies ObjectMarshaler, which we have
+ // to convert "*T" to explicitly.
+ var p P = &os[i]
+ if err := arr.AppendObject(p); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Strings constructs a field that carries a slice of strings.
+func Strings(key string, ss []string) Field {
+ return Array(key, stringArray(ss))
+}
+
+// Stringers constructs a field with the given key, holding a list of the
+// output provided by the value's String method
+//
+// Given an object that implements String on the value receiver, you
+// can log a slice of those objects with Objects like so:
+//
+// type Request struct{ ... }
+// func (a Request) String() string
+//
+// var requests []Request = ...
+// logger.Info("sending requests", zap.Stringers("requests", requests))
+//
+// Note that these objects must implement fmt.Stringer directly.
+// That is, if you're trying to marshal a []Request, the String method
+// must be declared on the Request type, not its pointer (*Request).
+func Stringers[T fmt.Stringer](key string, values []T) Field {
+ return Array(key, stringers[T](values))
+}
+
+type stringers[T fmt.Stringer] []T
+
+func (os stringers[T]) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for _, o := range os {
+ arr.AppendString(o.String())
+ }
+ return nil
+}
+
+// Times constructs a field that carries a slice of time.Times.
+func Times(key string, ts []time.Time) Field {
+ return Array(key, times(ts))
+}
+
+// Uints constructs a field that carries a slice of unsigned integers.
+func Uints(key string, nums []uint) Field {
+ return Array(key, uints(nums))
+}
+
+// Uint64s constructs a field that carries a slice of unsigned integers.
+func Uint64s(key string, nums []uint64) Field {
+ return Array(key, uint64s(nums))
+}
+
+// Uint32s constructs a field that carries a slice of unsigned integers.
+func Uint32s(key string, nums []uint32) Field {
+ return Array(key, uint32s(nums))
+}
+
+// Uint16s constructs a field that carries a slice of unsigned integers.
+func Uint16s(key string, nums []uint16) Field {
+ return Array(key, uint16s(nums))
+}
+
+// Uint8s constructs a field that carries a slice of unsigned integers.
+func Uint8s(key string, nums []uint8) Field {
+ return Array(key, uint8s(nums))
+}
+
+// Uintptrs constructs a field that carries a slice of pointer addresses.
+func Uintptrs(key string, us []uintptr) Field {
+ return Array(key, uintptrs(us))
+}
+
+// Errors constructs a field that carries a slice of errors.
+func Errors(key string, errs []error) Field {
+ return Array(key, errArray(errs))
+}
+
+type bools []bool
+
+func (bs bools) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range bs {
+ arr.AppendBool(bs[i])
+ }
+ return nil
+}
+
+type byteStringsArray [][]byte
+
+func (bss byteStringsArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range bss {
+ arr.AppendByteString(bss[i])
+ }
+ return nil
+}
+
+type complex128s []complex128
+
+func (nums complex128s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendComplex128(nums[i])
+ }
+ return nil
+}
+
+type complex64s []complex64
+
+func (nums complex64s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendComplex64(nums[i])
+ }
+ return nil
+}
+
+type durations []time.Duration
+
+func (ds durations) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range ds {
+ arr.AppendDuration(ds[i])
+ }
+ return nil
+}
+
+type float64s []float64
+
+func (nums float64s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendFloat64(nums[i])
+ }
+ return nil
+}
+
+type float32s []float32
+
+func (nums float32s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendFloat32(nums[i])
+ }
+ return nil
+}
+
+type ints []int
+
+func (nums ints) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendInt(nums[i])
+ }
+ return nil
+}
+
+type int64s []int64
+
+func (nums int64s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendInt64(nums[i])
+ }
+ return nil
+}
+
+type int32s []int32
+
+func (nums int32s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendInt32(nums[i])
+ }
+ return nil
+}
+
+type int16s []int16
+
+func (nums int16s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendInt16(nums[i])
+ }
+ return nil
+}
+
+type int8s []int8
+
+func (nums int8s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendInt8(nums[i])
+ }
+ return nil
+}
+
+type stringArray []string
+
+func (ss stringArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range ss {
+ arr.AppendString(ss[i])
+ }
+ return nil
+}
+
+type times []time.Time
+
+func (ts times) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range ts {
+ arr.AppendTime(ts[i])
+ }
+ return nil
+}
+
+type uints []uint
+
+func (nums uints) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendUint(nums[i])
+ }
+ return nil
+}
+
+type uint64s []uint64
+
+func (nums uint64s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendUint64(nums[i])
+ }
+ return nil
+}
+
+type uint32s []uint32
+
+func (nums uint32s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendUint32(nums[i])
+ }
+ return nil
+}
+
+type uint16s []uint16
+
+func (nums uint16s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendUint16(nums[i])
+ }
+ return nil
+}
+
+type uint8s []uint8
+
+func (nums uint8s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendUint8(nums[i])
+ }
+ return nil
+}
+
+type uintptrs []uintptr
+
+func (nums uintptrs) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendUintptr(nums[i])
+ }
+ return nil
+}
diff --git a/vendor/go.uber.org/zap/array_test.go b/vendor/go.uber.org/zap/array_test.go
new file mode 100644
index 0000000000..97738c99ab
--- /dev/null
+++ b/vendor/go.uber.org/zap/array_test.go
@@ -0,0 +1,316 @@
+// 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 zap
+
+import (
+ "errors"
+ "fmt"
+ "testing"
+ "time"
+
+ "go.uber.org/zap/zapcore"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func BenchmarkBoolsArrayMarshaler(b *testing.B) {
+ // Keep this benchmark here to capture the overhead of the ArrayMarshaler
+ // wrapper.
+ bs := make([]bool, 50)
+ enc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{})
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ Bools("array", bs).AddTo(enc.Clone())
+ }
+}
+
+func BenchmarkBoolsReflect(b *testing.B) {
+ bs := make([]bool, 50)
+ enc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{})
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ Reflect("array", bs).AddTo(enc.Clone())
+ }
+}
+
+func TestArrayWrappers(t *testing.T) {
+ tests := []struct {
+ desc string
+ field Field
+ expected []interface{}
+ }{
+ {"empty bools", Bools("", []bool{}), []interface{}{}},
+ {"empty byte strings", ByteStrings("", [][]byte{}), []interface{}{}},
+ {"empty complex128s", Complex128s("", []complex128{}), []interface{}{}},
+ {"empty complex64s", Complex64s("", []complex64{}), []interface{}{}},
+ {"empty durations", Durations("", []time.Duration{}), []interface{}{}},
+ {"empty float64s", Float64s("", []float64{}), []interface{}{}},
+ {"empty float32s", Float32s("", []float32{}), []interface{}{}},
+ {"empty ints", Ints("", []int{}), []interface{}{}},
+ {"empty int64s", Int64s("", []int64{}), []interface{}{}},
+ {"empty int32s", Int32s("", []int32{}), []interface{}{}},
+ {"empty int16s", Int16s("", []int16{}), []interface{}{}},
+ {"empty int8s", Int8s("", []int8{}), []interface{}{}},
+ {"empty strings", Strings("", []string{}), []interface{}{}},
+ {"empty times", Times("", []time.Time{}), []interface{}{}},
+ {"empty uints", Uints("", []uint{}), []interface{}{}},
+ {"empty uint64s", Uint64s("", []uint64{}), []interface{}{}},
+ {"empty uint32s", Uint32s("", []uint32{}), []interface{}{}},
+ {"empty uint16s", Uint16s("", []uint16{}), []interface{}{}},
+ {"empty uint8s", Uint8s("", []uint8{}), []interface{}{}},
+ {"empty uintptrs", Uintptrs("", []uintptr{}), []interface{}{}},
+ {"bools", Bools("", []bool{true, false}), []interface{}{true, false}},
+ {"byte strings", ByteStrings("", [][]byte{{1, 2}, {3, 4}}), []interface{}{"\x01\x02", "\x03\x04"}},
+ {"complex128s", Complex128s("", []complex128{1 + 2i, 3 + 4i}), []interface{}{1 + 2i, 3 + 4i}},
+ {"complex64s", Complex64s("", []complex64{1 + 2i, 3 + 4i}), []interface{}{complex64(1 + 2i), complex64(3 + 4i)}},
+ {"durations", Durations("", []time.Duration{1, 2}), []interface{}{time.Nanosecond, 2 * time.Nanosecond}},
+ {"float64s", Float64s("", []float64{1.2, 3.4}), []interface{}{1.2, 3.4}},
+ {"float32s", Float32s("", []float32{1.2, 3.4}), []interface{}{float32(1.2), float32(3.4)}},
+ {"ints", Ints("", []int{1, 2}), []interface{}{1, 2}},
+ {"int64s", Int64s("", []int64{1, 2}), []interface{}{int64(1), int64(2)}},
+ {"int32s", Int32s("", []int32{1, 2}), []interface{}{int32(1), int32(2)}},
+ {"int16s", Int16s("", []int16{1, 2}), []interface{}{int16(1), int16(2)}},
+ {"int8s", Int8s("", []int8{1, 2}), []interface{}{int8(1), int8(2)}},
+ {"strings", Strings("", []string{"foo", "bar"}), []interface{}{"foo", "bar"}},
+ {"times", Times("", []time.Time{time.Unix(0, 0), time.Unix(0, 0)}), []interface{}{time.Unix(0, 0), time.Unix(0, 0)}},
+ {"uints", Uints("", []uint{1, 2}), []interface{}{uint(1), uint(2)}},
+ {"uint64s", Uint64s("", []uint64{1, 2}), []interface{}{uint64(1), uint64(2)}},
+ {"uint32s", Uint32s("", []uint32{1, 2}), []interface{}{uint32(1), uint32(2)}},
+ {"uint16s", Uint16s("", []uint16{1, 2}), []interface{}{uint16(1), uint16(2)}},
+ {"uint8s", Uint8s("", []uint8{1, 2}), []interface{}{uint8(1), uint8(2)}},
+ {"uintptrs", Uintptrs("", []uintptr{1, 2}), []interface{}{uintptr(1), uintptr(2)}},
+ }
+
+ for _, tt := range tests {
+ enc := zapcore.NewMapObjectEncoder()
+ tt.field.Key = "k"
+ tt.field.AddTo(enc)
+ assert.Equal(t, tt.expected, enc.Fields["k"], "%s: unexpected map contents.", tt.desc)
+ assert.Equal(t, 1, len(enc.Fields), "%s: found extra keys in map: %v", tt.desc, enc.Fields)
+ }
+}
+
+func TestObjectsAndObjectValues(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ desc string
+ give Field
+ want []any
+ }{
+ {
+ desc: "Objects/nil slice",
+ give: Objects[*emptyObject]("", nil),
+ want: []any{},
+ },
+ {
+ desc: "ObjectValues/nil slice",
+ give: ObjectValues[emptyObject]("", nil),
+ want: []any{},
+ },
+ {
+ desc: "ObjectValues/empty slice",
+ give: ObjectValues("", []emptyObject{}),
+ want: []any{},
+ },
+ {
+ desc: "ObjectValues/single item",
+ give: ObjectValues("", []emptyObject{
+ {},
+ }),
+ want: []any{
+ map[string]any{},
+ },
+ },
+ {
+ desc: "Objects/multiple different objects",
+ give: Objects("", []*fakeObject{
+ {value: "foo"},
+ {value: "bar"},
+ {value: "baz"},
+ }),
+ want: []any{
+ map[string]any{"value": "foo"},
+ map[string]any{"value": "bar"},
+ map[string]any{"value": "baz"},
+ },
+ },
+ {
+ desc: "ObjectValues/multiple different objects",
+ give: ObjectValues("", []fakeObject{
+ {value: "foo"},
+ {value: "bar"},
+ {value: "baz"},
+ }),
+ want: []any{
+ map[string]any{"value": "foo"},
+ map[string]any{"value": "bar"},
+ map[string]any{"value": "baz"},
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.desc, func(t *testing.T) {
+ t.Parallel()
+
+ tt.give.Key = "k"
+
+ enc := zapcore.NewMapObjectEncoder()
+ tt.give.AddTo(enc)
+ assert.Equal(t, tt.want, enc.Fields["k"])
+ })
+ }
+}
+
+type emptyObject struct{}
+
+func (*emptyObject) MarshalLogObject(zapcore.ObjectEncoder) error {
+ return nil
+}
+
+type fakeObject struct {
+ value string
+ err error // marshaling error, if any
+}
+
+func (o *fakeObject) MarshalLogObject(enc zapcore.ObjectEncoder) error {
+ enc.AddString("value", o.value)
+ return o.err
+}
+
+func TestObjectsAndObjectValues_marshalError(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ desc string
+ give Field
+ want []any
+ wantErr string
+ }{
+ {
+ desc: "Objects",
+ give: Objects("", []*fakeObject{
+ {value: "foo"},
+ {value: "bar", err: errors.New("great sadness")},
+ {value: "baz"}, // does not get marshaled
+ }),
+ want: []any{
+ map[string]any{"value": "foo"},
+ map[string]any{"value": "bar"},
+ },
+ wantErr: "great sadness",
+ },
+ {
+ desc: "ObjectValues",
+ give: ObjectValues("", []fakeObject{
+ {value: "foo"},
+ {value: "bar", err: errors.New("stuff failed")},
+ {value: "baz"}, // does not get marshaled
+ }),
+ want: []any{
+ map[string]any{"value": "foo"},
+ map[string]any{"value": "bar"},
+ },
+ wantErr: "stuff failed",
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.desc, func(t *testing.T) {
+ t.Parallel()
+
+ tt.give.Key = "k"
+
+ enc := zapcore.NewMapObjectEncoder()
+ tt.give.AddTo(enc)
+
+ require.Contains(t, enc.Fields, "k")
+ assert.Equal(t, tt.want, enc.Fields["k"])
+
+ // AddTo puts the error in a "%vError" field based on the name of the
+ // original field.
+ require.Contains(t, enc.Fields, "kError")
+ assert.Equal(t, tt.wantErr, enc.Fields["kError"])
+ })
+ }
+}
+
+type stringerObject struct {
+ value string
+}
+
+func (s stringerObject) String() string {
+ return s.value
+}
+
+func TestStringers(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ desc string
+ give Field
+ want []any
+ }{
+ {
+ desc: "Stringers",
+ give: Stringers("", []stringerObject{
+ {value: "foo"},
+ {value: "bar"},
+ {value: "baz"},
+ }),
+ want: []any{
+ "foo",
+ "bar",
+ "baz",
+ },
+ },
+ {
+ desc: "Stringers with []fmt.Stringer",
+ give: Stringers("", []fmt.Stringer{
+ stringerObject{value: "foo"},
+ stringerObject{value: "bar"},
+ stringerObject{value: "baz"},
+ }),
+ want: []any{
+ "foo",
+ "bar",
+ "baz",
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.desc, func(t *testing.T) {
+ t.Parallel()
+
+ tt.give.Key = "k"
+
+ enc := zapcore.NewMapObjectEncoder()
+ tt.give.AddTo(enc)
+ assert.Equal(t, tt.want, enc.Fields["k"])
+ })
+ }
+}
diff --git a/vendor/go.uber.org/zap/buffer/buffer.go b/vendor/go.uber.org/zap/buffer/buffer.go
new file mode 100644
index 0000000000..27fb5cd5da
--- /dev/null
+++ b/vendor/go.uber.org/zap/buffer/buffer.go
@@ -0,0 +1,146 @@
+// 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 buffer provides a thin wrapper around a byte slice. Unlike the
+// standard library's bytes.Buffer, it supports a portion of the strconv
+// package's zero-allocation formatters.
+package buffer // import "go.uber.org/zap/buffer"
+
+import (
+ "strconv"
+ "time"
+)
+
+const _size = 1024 // by default, create 1 KiB buffers
+
+// Buffer is a thin wrapper around a byte slice. It's intended to be pooled, so
+// the only way to construct one is via a Pool.
+type Buffer struct {
+ bs []byte
+ pool Pool
+}
+
+// AppendByte writes a single byte to the Buffer.
+func (b *Buffer) AppendByte(v byte) {
+ b.bs = append(b.bs, v)
+}
+
+// AppendBytes writes a single byte to the Buffer.
+func (b *Buffer) AppendBytes(v []byte) {
+ b.bs = append(b.bs, v...)
+}
+
+// AppendString writes a string to the Buffer.
+func (b *Buffer) AppendString(s string) {
+ b.bs = append(b.bs, s...)
+}
+
+// AppendInt appends an integer to the underlying buffer (assuming base 10).
+func (b *Buffer) AppendInt(i int64) {
+ b.bs = strconv.AppendInt(b.bs, i, 10)
+}
+
+// AppendTime appends the time formatted using the specified layout.
+func (b *Buffer) AppendTime(t time.Time, layout string) {
+ b.bs = t.AppendFormat(b.bs, layout)
+}
+
+// AppendUint appends an unsigned integer to the underlying buffer (assuming
+// base 10).
+func (b *Buffer) AppendUint(i uint64) {
+ b.bs = strconv.AppendUint(b.bs, i, 10)
+}
+
+// AppendBool appends a bool to the underlying buffer.
+func (b *Buffer) AppendBool(v bool) {
+ b.bs = strconv.AppendBool(b.bs, v)
+}
+
+// AppendFloat appends a float to the underlying buffer. It doesn't quote NaN
+// or +/- Inf.
+func (b *Buffer) AppendFloat(f float64, bitSize int) {
+ b.bs = strconv.AppendFloat(b.bs, f, 'f', -1, bitSize)
+}
+
+// Len returns the length of the underlying byte slice.
+func (b *Buffer) Len() int {
+ return len(b.bs)
+}
+
+// Cap returns the capacity of the underlying byte slice.
+func (b *Buffer) Cap() int {
+ return cap(b.bs)
+}
+
+// Bytes returns a mutable reference to the underlying byte slice.
+func (b *Buffer) Bytes() []byte {
+ return b.bs
+}
+
+// String returns a string copy of the underlying byte slice.
+func (b *Buffer) String() string {
+ return string(b.bs)
+}
+
+// Reset resets the underlying byte slice. Subsequent writes re-use the slice's
+// backing array.
+func (b *Buffer) Reset() {
+ b.bs = b.bs[:0]
+}
+
+// Write implements io.Writer.
+func (b *Buffer) Write(bs []byte) (int, error) {
+ b.bs = append(b.bs, bs...)
+ return len(bs), nil
+}
+
+// WriteByte writes a single byte to the Buffer.
+//
+// Error returned is always nil, function signature is compatible
+// with bytes.Buffer and bufio.Writer
+func (b *Buffer) WriteByte(v byte) error {
+ b.AppendByte(v)
+ return nil
+}
+
+// WriteString writes a string to the Buffer.
+//
+// Error returned is always nil, function signature is compatible
+// with bytes.Buffer and bufio.Writer
+func (b *Buffer) WriteString(s string) (int, error) {
+ b.AppendString(s)
+ return len(s), nil
+}
+
+// TrimNewline trims any final "\n" byte from the end of the buffer.
+func (b *Buffer) TrimNewline() {
+ if i := len(b.bs) - 1; i >= 0 {
+ if b.bs[i] == '\n' {
+ b.bs = b.bs[:i]
+ }
+ }
+}
+
+// Free returns the Buffer to its Pool.
+//
+// Callers must not retain references to the Buffer after calling Free.
+func (b *Buffer) Free() {
+ b.pool.put(b)
+}
diff --git a/vendor/go.uber.org/zap/buffer/buffer_test.go b/vendor/go.uber.org/zap/buffer/buffer_test.go
new file mode 100644
index 0000000000..71ffac1146
--- /dev/null
+++ b/vendor/go.uber.org/zap/buffer/buffer_test.go
@@ -0,0 +1,95 @@
+// 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 buffer
+
+import (
+ "bytes"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestBufferWrites(t *testing.T) {
+ buf := NewPool().Get()
+
+ tests := []struct {
+ desc string
+ f func()
+ want string
+ }{
+ {"AppendByte", func() { buf.AppendByte('v') }, "v"},
+ {"AppendString", func() { buf.AppendString("foo") }, "foo"},
+ {"AppendIntPositive", func() { buf.AppendInt(42) }, "42"},
+ {"AppendIntNegative", func() { buf.AppendInt(-42) }, "-42"},
+ {"AppendUint", func() { buf.AppendUint(42) }, "42"},
+ {"AppendBool", func() { buf.AppendBool(true) }, "true"},
+ {"AppendFloat64", func() { buf.AppendFloat(3.14, 64) }, "3.14"},
+ // Intentionally introduce some floating-point error.
+ {"AppendFloat32", func() { buf.AppendFloat(float64(float32(3.14)), 32) }, "3.14"},
+ {"AppendWrite", func() { buf.Write([]byte("foo")) }, "foo"},
+ {"AppendTime", func() { buf.AppendTime(time.Date(2000, 1, 2, 3, 4, 5, 6, time.UTC), time.RFC3339) }, "2000-01-02T03:04:05Z"},
+ {"WriteByte", func() { buf.WriteByte('v') }, "v"},
+ {"WriteString", func() { buf.WriteString("foo") }, "foo"},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ buf.Reset()
+ tt.f()
+ assert.Equal(t, tt.want, buf.String(), "Unexpected buffer.String().")
+ assert.Equal(t, tt.want, string(buf.Bytes()), "Unexpected string(buffer.Bytes()).")
+ assert.Equal(t, len(tt.want), buf.Len(), "Unexpected buffer length.")
+ // We're not writing more than a kibibyte in tests.
+ assert.Equal(t, _size, buf.Cap(), "Expected buffer capacity to remain constant.")
+ })
+ }
+}
+
+func BenchmarkBuffers(b *testing.B) {
+ // Because we use the strconv.AppendFoo functions so liberally, we can't
+ // use the standard library's bytes.Buffer anyways (without incurring a
+ // bunch of extra allocations). Nevertheless, let's make sure that we're
+ // not losing any precious nanoseconds.
+ str := strings.Repeat("a", 1024)
+ slice := make([]byte, 1024)
+ buf := bytes.NewBuffer(slice)
+ custom := NewPool().Get()
+ b.Run("ByteSlice", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ slice = append(slice, str...)
+ slice = slice[:0]
+ }
+ })
+ b.Run("BytesBuffer", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ buf.WriteString(str)
+ buf.Reset()
+ }
+ })
+ b.Run("CustomBuffer", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ custom.AppendString(str)
+ custom.Reset()
+ }
+ })
+}
diff --git a/vendor/go.uber.org/zap/buffer/gotest/ya.make b/vendor/go.uber.org/zap/buffer/gotest/ya.make
new file mode 100644
index 0000000000..974b099945
--- /dev/null
+++ b/vendor/go.uber.org/zap/buffer/gotest/ya.make
@@ -0,0 +1,5 @@
+GO_TEST_FOR(vendor/go.uber.org/zap/buffer)
+
+LICENSE(MIT)
+
+END()
diff --git a/vendor/go.uber.org/zap/buffer/pool.go b/vendor/go.uber.org/zap/buffer/pool.go
new file mode 100644
index 0000000000..846323360e
--- /dev/null
+++ b/vendor/go.uber.org/zap/buffer/pool.go
@@ -0,0 +1,53 @@
+// 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 buffer
+
+import (
+ "go.uber.org/zap/internal/pool"
+)
+
+// A Pool is a type-safe wrapper around a sync.Pool.
+type Pool struct {
+ p *pool.Pool[*Buffer]
+}
+
+// NewPool constructs a new Pool.
+func NewPool() Pool {
+ return Pool{
+ p: pool.New(func() *Buffer {
+ return &Buffer{
+ bs: make([]byte, 0, _size),
+ }
+ }),
+ }
+}
+
+// Get retrieves a Buffer from the pool, creating one if necessary.
+func (p Pool) Get() *Buffer {
+ buf := p.p.Get()
+ buf.Reset()
+ buf.pool = p
+ return buf
+}
+
+func (p Pool) put(buf *Buffer) {
+ p.p.Put(buf)
+}
diff --git a/vendor/go.uber.org/zap/buffer/pool_test.go b/vendor/go.uber.org/zap/buffer/pool_test.go
new file mode 100644
index 0000000000..a219815b55
--- /dev/null
+++ b/vendor/go.uber.org/zap/buffer/pool_test.go
@@ -0,0 +1,52 @@
+// 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 buffer
+
+import (
+ "sync"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestBuffers(t *testing.T) {
+ const dummyData = "dummy data"
+ p := NewPool()
+
+ var wg sync.WaitGroup
+ for g := 0; g < 10; g++ {
+ wg.Add(1)
+ go func() {
+ for i := 0; i < 100; i++ {
+ buf := p.Get()
+ assert.Zero(t, buf.Len(), "Expected truncated buffer")
+ assert.NotZero(t, buf.Cap(), "Expected non-zero capacity")
+
+ buf.AppendString(dummyData)
+ assert.Equal(t, buf.Len(), len(dummyData), "Expected buffer to contain dummy data")
+
+ buf.Free()
+ }
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+}
diff --git a/vendor/go.uber.org/zap/buffer/ya.make b/vendor/go.uber.org/zap/buffer/ya.make
new file mode 100644
index 0000000000..f4c893dc92
--- /dev/null
+++ b/vendor/go.uber.org/zap/buffer/ya.make
@@ -0,0 +1,19 @@
+GO_LIBRARY()
+
+LICENSE(MIT)
+
+SRCS(
+ buffer.go
+ pool.go
+)
+
+GO_TEST_SRCS(
+ buffer_test.go
+ pool_test.go
+)
+
+END()
+
+RECURSE(
+ gotest
+)
diff --git a/vendor/go.uber.org/zap/clock_test.go b/vendor/go.uber.org/zap/clock_test.go
new file mode 100644
index 0000000000..29825fc2fb
--- /dev/null
+++ b/vendor/go.uber.org/zap/clock_test.go
@@ -0,0 +1,47 @@
+// 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 zap
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "go.uber.org/zap/zaptest/observer"
+)
+
+type constantClock time.Time
+
+func (c constantClock) Now() time.Time { return time.Time(c) }
+func (c constantClock) NewTicker(d time.Duration) *time.Ticker {
+ return &time.Ticker{}
+}
+
+func TestWithClock(t *testing.T) {
+ date := time.Date(2077, 1, 23, 10, 15, 13, 441, time.UTC)
+ clock := constantClock(date)
+ withLogger(t, DebugLevel, []Option{WithClock(clock)}, func(log *Logger, logs *observer.ObservedLogs) {
+ log.Info("")
+ require.Equal(t, 1, logs.Len(), "Expected only one log entry to be written.")
+ assert.Equal(t, date, logs.All()[0].Time, "Unexpected entry time.")
+ })
+}
diff --git a/vendor/go.uber.org/zap/common_test.go b/vendor/go.uber.org/zap/common_test.go
new file mode 100644
index 0000000000..b0a4a2e520
--- /dev/null
+++ b/vendor/go.uber.org/zap/common_test.go
@@ -0,0 +1,57 @@
+// 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 zap
+
+import (
+ "sync"
+ "testing"
+
+ "go.uber.org/zap/zapcore"
+ "go.uber.org/zap/zaptest/observer"
+)
+
+func opts(opts ...Option) []Option {
+ return opts
+}
+
+// Here specifically to introduce an easily-identifiable filename for testing
+// stacktraces and caller skips.
+func withLogger(t testing.TB, e zapcore.LevelEnabler, opts []Option, f func(*Logger, *observer.ObservedLogs)) {
+ fac, logs := observer.New(e)
+ log := New(fac, opts...)
+ f(log, logs)
+}
+
+func withSugar(t testing.TB, e zapcore.LevelEnabler, opts []Option, f func(*SugaredLogger, *observer.ObservedLogs)) {
+ withLogger(t, e, opts, func(logger *Logger, logs *observer.ObservedLogs) { f(logger.Sugar(), logs) })
+}
+
+func runConcurrently(goroutines, iterations int, wg *sync.WaitGroup, f func()) {
+ wg.Add(goroutines)
+ for g := 0; g < goroutines; g++ {
+ go func() {
+ defer wg.Done()
+ for i := 0; i < iterations; i++ {
+ f()
+ }
+ }()
+ }
+}
diff --git a/vendor/go.uber.org/zap/config.go b/vendor/go.uber.org/zap/config.go
new file mode 100644
index 0000000000..e76e4e64fb
--- /dev/null
+++ b/vendor/go.uber.org/zap/config.go
@@ -0,0 +1,330 @@
+// 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 zap
+
+import (
+ "errors"
+ "sort"
+ "time"
+
+ "go.uber.org/zap/zapcore"
+)
+
+// SamplingConfig sets a sampling strategy for the logger. Sampling caps the
+// global CPU and I/O load that logging puts on your process while attempting
+// to preserve a representative subset of your logs.
+//
+// If specified, the Sampler will invoke the Hook after each decision.
+//
+// Values configured here are per-second. See zapcore.NewSamplerWithOptions for
+// details.
+type SamplingConfig struct {
+ Initial int `json:"initial" yaml:"initial"`
+ Thereafter int `json:"thereafter" yaml:"thereafter"`
+ Hook func(zapcore.Entry, zapcore.SamplingDecision) `json:"-" yaml:"-"`
+}
+
+// Config offers a declarative way to construct a logger. It doesn't do
+// anything that can't be done with New, Options, and the various
+// zapcore.WriteSyncer and zapcore.Core wrappers, but it's a simpler way to
+// toggle common options.
+//
+// Note that Config intentionally supports only the most common options. More
+// unusual logging setups (logging to network connections or message queues,
+// splitting output between multiple files, etc.) are possible, but require
+// direct use of the zapcore package. For sample code, see the package-level
+// BasicConfiguration and AdvancedConfiguration examples.
+//
+// For an example showing runtime log level changes, see the documentation for
+// AtomicLevel.
+type Config struct {
+ // Level is the minimum enabled logging level. Note that this is a dynamic
+ // level, so calling Config.Level.SetLevel will atomically change the log
+ // level of all loggers descended from this config.
+ Level AtomicLevel `json:"level" yaml:"level"`
+ // Development puts the logger in development mode, which changes the
+ // behavior of DPanicLevel and takes stacktraces more liberally.
+ Development bool `json:"development" yaml:"development"`
+ // DisableCaller stops annotating logs with the calling function's file
+ // name and line number. By default, all logs are annotated.
+ DisableCaller bool `json:"disableCaller" yaml:"disableCaller"`
+ // DisableStacktrace completely disables automatic stacktrace capturing. By
+ // default, stacktraces are captured for WarnLevel and above logs in
+ // development and ErrorLevel and above in production.
+ DisableStacktrace bool `json:"disableStacktrace" yaml:"disableStacktrace"`
+ // Sampling sets a sampling policy. A nil SamplingConfig disables sampling.
+ Sampling *SamplingConfig `json:"sampling" yaml:"sampling"`
+ // Encoding sets the logger's encoding. Valid values are "json" and
+ // "console", as well as any third-party encodings registered via
+ // RegisterEncoder.
+ Encoding string `json:"encoding" yaml:"encoding"`
+ // EncoderConfig sets options for the chosen encoder. See
+ // zapcore.EncoderConfig for details.
+ EncoderConfig zapcore.EncoderConfig `json:"encoderConfig" yaml:"encoderConfig"`
+ // OutputPaths is a list of URLs or file paths to write logging output to.
+ // See Open for details.
+ OutputPaths []string `json:"outputPaths" yaml:"outputPaths"`
+ // ErrorOutputPaths is a list of URLs to write internal logger errors to.
+ // The default is standard error.
+ //
+ // Note that this setting only affects internal errors; for sample code that
+ // sends error-level logs to a different location from info- and debug-level
+ // logs, see the package-level AdvancedConfiguration example.
+ ErrorOutputPaths []string `json:"errorOutputPaths" yaml:"errorOutputPaths"`
+ // InitialFields is a collection of fields to add to the root logger.
+ InitialFields map[string]interface{} `json:"initialFields" yaml:"initialFields"`
+}
+
+// NewProductionEncoderConfig returns an opinionated EncoderConfig for
+// production environments.
+//
+// Messages encoded with this configuration will be JSON-formatted
+// and will have the following keys by default:
+//
+// - "level": The logging level (e.g. "info", "error").
+// - "ts": The current time in number of seconds since the Unix epoch.
+// - "msg": The message passed to the log statement.
+// - "caller": If available, a short path to the file and line number
+// where the log statement was issued.
+// The logger configuration determines whether this field is captured.
+// - "stacktrace": If available, a stack trace from the line
+// where the log statement was issued.
+// The logger configuration determines whether this field is captured.
+//
+// By default, the following formats are used for different types:
+//
+// - Time is formatted as floating-point number of seconds since the Unix
+// epoch.
+// - Duration is formatted as floating-point number of seconds.
+//
+// You may change these by setting the appropriate fields in the returned
+// object.
+// For example, use the following to change the time encoding format:
+//
+// cfg := zap.NewProductionEncoderConfig()
+// cfg.EncodeTime = zapcore.ISO8601TimeEncoder
+func NewProductionEncoderConfig() zapcore.EncoderConfig {
+ return zapcore.EncoderConfig{
+ TimeKey: "ts",
+ LevelKey: "level",
+ NameKey: "logger",
+ CallerKey: "caller",
+ FunctionKey: zapcore.OmitKey,
+ MessageKey: "msg",
+ StacktraceKey: "stacktrace",
+ LineEnding: zapcore.DefaultLineEnding,
+ EncodeLevel: zapcore.LowercaseLevelEncoder,
+ EncodeTime: zapcore.EpochTimeEncoder,
+ EncodeDuration: zapcore.SecondsDurationEncoder,
+ EncodeCaller: zapcore.ShortCallerEncoder,
+ }
+}
+
+// NewProductionConfig builds a reasonable default production logging
+// configuration.
+// Logging is enabled at InfoLevel and above, and uses a JSON encoder.
+// Logs are written to standard error.
+// Stacktraces are included on logs of ErrorLevel and above.
+// DPanicLevel logs will not panic, but will write a stacktrace.
+//
+// Sampling is enabled at 100:100 by default,
+// meaning that after the first 100 log entries
+// with the same level and message in the same second,
+// it will log every 100th entry
+// with the same level and message in the same second.
+// You may disable this behavior by setting Sampling to nil.
+//
+// See [NewProductionEncoderConfig] for information
+// on the default encoder configuration.
+func NewProductionConfig() Config {
+ return Config{
+ Level: NewAtomicLevelAt(InfoLevel),
+ Development: false,
+ Sampling: &SamplingConfig{
+ Initial: 100,
+ Thereafter: 100,
+ },
+ Encoding: "json",
+ EncoderConfig: NewProductionEncoderConfig(),
+ OutputPaths: []string{"stderr"},
+ ErrorOutputPaths: []string{"stderr"},
+ }
+}
+
+// NewDevelopmentEncoderConfig returns an opinionated EncoderConfig for
+// development environments.
+//
+// Messages encoded with this configuration will use Zap's console encoder
+// intended to print human-readable output.
+// It will print log messages with the following information:
+//
+// - The log level (e.g. "INFO", "ERROR").
+// - The time in ISO8601 format (e.g. "2017-01-01T12:00:00Z").
+// - The message passed to the log statement.
+// - If available, a short path to the file and line number
+// where the log statement was issued.
+// The logger configuration determines whether this field is captured.
+// - If available, a stacktrace from the line
+// where the log statement was issued.
+// The logger configuration determines whether this field is captured.
+//
+// By default, the following formats are used for different types:
+//
+// - Time is formatted in ISO8601 format (e.g. "2017-01-01T12:00:00Z").
+// - Duration is formatted as a string (e.g. "1.234s").
+//
+// You may change these by setting the appropriate fields in the returned
+// object.
+// For example, use the following to change the time encoding format:
+//
+// cfg := zap.NewDevelopmentEncoderConfig()
+// cfg.EncodeTime = zapcore.ISO8601TimeEncoder
+func NewDevelopmentEncoderConfig() zapcore.EncoderConfig {
+ return zapcore.EncoderConfig{
+ // Keys can be anything except the empty string.
+ TimeKey: "T",
+ LevelKey: "L",
+ NameKey: "N",
+ CallerKey: "C",
+ FunctionKey: zapcore.OmitKey,
+ MessageKey: "M",
+ StacktraceKey: "S",
+ LineEnding: zapcore.DefaultLineEnding,
+ EncodeLevel: zapcore.CapitalLevelEncoder,
+ EncodeTime: zapcore.ISO8601TimeEncoder,
+ EncodeDuration: zapcore.StringDurationEncoder,
+ EncodeCaller: zapcore.ShortCallerEncoder,
+ }
+}
+
+// NewDevelopmentConfig builds a reasonable default development logging
+// configuration.
+// Logging is enabled at DebugLevel and above, and uses a console encoder.
+// Logs are written to standard error.
+// Stacktraces are included on logs of WarnLevel and above.
+// DPanicLevel logs will panic.
+//
+// See [NewDevelopmentEncoderConfig] for information
+// on the default encoder configuration.
+func NewDevelopmentConfig() Config {
+ return Config{
+ Level: NewAtomicLevelAt(DebugLevel),
+ Development: true,
+ Encoding: "console",
+ EncoderConfig: NewDevelopmentEncoderConfig(),
+ OutputPaths: []string{"stderr"},
+ ErrorOutputPaths: []string{"stderr"},
+ }
+}
+
+// Build constructs a logger from the Config and Options.
+func (cfg Config) Build(opts ...Option) (*Logger, error) {
+ enc, err := cfg.buildEncoder()
+ if err != nil {
+ return nil, err
+ }
+
+ sink, errSink, err := cfg.openSinks()
+ if err != nil {
+ return nil, err
+ }
+
+ if cfg.Level == (AtomicLevel{}) {
+ return nil, errors.New("missing Level")
+ }
+
+ log := New(
+ zapcore.NewCore(enc, sink, cfg.Level),
+ cfg.buildOptions(errSink)...,
+ )
+ if len(opts) > 0 {
+ log = log.WithOptions(opts...)
+ }
+ return log, nil
+}
+
+func (cfg Config) buildOptions(errSink zapcore.WriteSyncer) []Option {
+ opts := []Option{ErrorOutput(errSink)}
+
+ if cfg.Development {
+ opts = append(opts, Development())
+ }
+
+ if !cfg.DisableCaller {
+ opts = append(opts, AddCaller())
+ }
+
+ stackLevel := ErrorLevel
+ if cfg.Development {
+ stackLevel = WarnLevel
+ }
+ if !cfg.DisableStacktrace {
+ opts = append(opts, AddStacktrace(stackLevel))
+ }
+
+ if scfg := cfg.Sampling; scfg != nil {
+ opts = append(opts, WrapCore(func(core zapcore.Core) zapcore.Core {
+ var samplerOpts []zapcore.SamplerOption
+ if scfg.Hook != nil {
+ samplerOpts = append(samplerOpts, zapcore.SamplerHook(scfg.Hook))
+ }
+ return zapcore.NewSamplerWithOptions(
+ core,
+ time.Second,
+ cfg.Sampling.Initial,
+ cfg.Sampling.Thereafter,
+ samplerOpts...,
+ )
+ }))
+ }
+
+ if len(cfg.InitialFields) > 0 {
+ fs := make([]Field, 0, len(cfg.InitialFields))
+ keys := make([]string, 0, len(cfg.InitialFields))
+ for k := range cfg.InitialFields {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ for _, k := range keys {
+ fs = append(fs, Any(k, cfg.InitialFields[k]))
+ }
+ opts = append(opts, Fields(fs...))
+ }
+
+ return opts
+}
+
+func (cfg Config) openSinks() (zapcore.WriteSyncer, zapcore.WriteSyncer, error) {
+ sink, closeOut, err := Open(cfg.OutputPaths...)
+ if err != nil {
+ return nil, nil, err
+ }
+ errSink, _, err := Open(cfg.ErrorOutputPaths...)
+ if err != nil {
+ closeOut()
+ return nil, nil, err
+ }
+ return sink, errSink, nil
+}
+
+func (cfg Config) buildEncoder() (zapcore.Encoder, error) {
+ return newEncoder(cfg.Encoding, cfg.EncoderConfig)
+}
diff --git a/vendor/go.uber.org/zap/config_test.go b/vendor/go.uber.org/zap/config_test.go
new file mode 100644
index 0000000000..4badd1beec
--- /dev/null
+++ b/vendor/go.uber.org/zap/config_test.go
@@ -0,0 +1,203 @@
+// 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 zap
+
+import (
+ "os"
+ "path/filepath"
+ "sync/atomic"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "go.uber.org/zap/zapcore"
+)
+
+func TestConfig(t *testing.T) {
+ tests := []struct {
+ desc string
+ cfg Config
+ expectN int64
+ expectRe string
+ }{
+ {
+ desc: "production",
+ cfg: NewProductionConfig(),
+ expectN: 2 + 100 + 1, // 2 from initial logs, 100 initial sampled logs, 1 from off-by-one in sampler
+ expectRe: `{"level":"info","caller":"[a-z0-9_-]+/config_test.go:\d+","msg":"info","k":"v","z":"zz"}` + "\n" +
+ `{"level":"warn","caller":"[a-z0-9_-]+/config_test.go:\d+","msg":"warn","k":"v","z":"zz"}` + "\n",
+ },
+ {
+ desc: "development",
+ cfg: NewDevelopmentConfig(),
+ expectN: 3 + 200, // 3 initial logs, all 200 subsequent logs
+ expectRe: "DEBUG\t[a-z0-9_-]+/config_test.go:" + `\d+` + "\tdebug\t" + `{"k": "v", "z": "zz"}` + "\n" +
+ "INFO\t[a-z0-9_-]+/config_test.go:" + `\d+` + "\tinfo\t" + `{"k": "v", "z": "zz"}` + "\n" +
+ "WARN\t[a-z0-9_-]+/config_test.go:" + `\d+` + "\twarn\t" + `{"k": "v", "z": "zz"}` + "\n" +
+ `go.uber.org/zap.TestConfig.\w+`,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ logOut := filepath.Join(t.TempDir(), "test.log")
+
+ tt.cfg.OutputPaths = []string{logOut}
+ tt.cfg.EncoderConfig.TimeKey = "" // no timestamps in tests
+ tt.cfg.InitialFields = map[string]interface{}{"z": "zz", "k": "v"}
+
+ hook, count := makeCountingHook()
+ logger, err := tt.cfg.Build(Hooks(hook))
+ require.NoError(t, err, "Unexpected error constructing logger.")
+
+ logger.Debug("debug")
+ logger.Info("info")
+ logger.Warn("warn")
+
+ byteContents, err := os.ReadFile(logOut)
+ require.NoError(t, err, "Couldn't read log contents from temp file.")
+ logs := string(byteContents)
+ assert.Regexp(t, tt.expectRe, logs, "Unexpected log output.")
+
+ for i := 0; i < 200; i++ {
+ logger.Info("sampling")
+ }
+ assert.Equal(t, tt.expectN, count.Load(), "Hook called an unexpected number of times.")
+ })
+ }
+}
+
+func TestConfigWithInvalidPaths(t *testing.T) {
+ tests := []struct {
+ desc string
+ output string
+ errOutput string
+ }{
+ {"output directory doesn't exist", "/tmp/not-there/foo.log", "stderr"},
+ {"error output directory doesn't exist", "stdout", "/tmp/not-there/foo-errors.log"},
+ {"neither output directory exists", "/tmp/not-there/foo.log", "/tmp/not-there/foo-errors.log"},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ cfg := NewProductionConfig()
+ cfg.OutputPaths = []string{tt.output}
+ cfg.ErrorOutputPaths = []string{tt.errOutput}
+ _, err := cfg.Build()
+ assert.Error(t, err, "Expected an error opening a non-existent directory.")
+ })
+ }
+}
+
+func TestConfigWithMissingAttributes(t *testing.T) {
+ tests := []struct {
+ desc string
+ cfg Config
+ expectErr string
+ }{
+ {
+ desc: "missing level",
+ cfg: Config{
+ Encoding: "json",
+ },
+ expectErr: "missing Level",
+ },
+ {
+ desc: "missing encoder time in encoder config",
+ cfg: Config{
+ Level: NewAtomicLevelAt(zapcore.InfoLevel),
+ Encoding: "json",
+ EncoderConfig: zapcore.EncoderConfig{
+ MessageKey: "msg",
+ TimeKey: "ts",
+ },
+ },
+ expectErr: "missing EncodeTime in EncoderConfig",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ cfg := tt.cfg
+ _, err := cfg.Build()
+ assert.EqualError(t, err, tt.expectErr)
+ })
+ }
+}
+
+func makeSamplerCountingHook() (h func(zapcore.Entry, zapcore.SamplingDecision),
+ dropped, sampled *atomic.Int64,
+) {
+ dropped = new(atomic.Int64)
+ sampled = new(atomic.Int64)
+ h = func(_ zapcore.Entry, dec zapcore.SamplingDecision) {
+ if dec&zapcore.LogDropped > 0 {
+ dropped.Add(1)
+ } else if dec&zapcore.LogSampled > 0 {
+ sampled.Add(1)
+ }
+ }
+ return h, dropped, sampled
+}
+
+func TestConfigWithSamplingHook(t *testing.T) {
+ shook, dcount, scount := makeSamplerCountingHook()
+ cfg := Config{
+ Level: NewAtomicLevelAt(InfoLevel),
+ Development: false,
+ Sampling: &SamplingConfig{
+ Initial: 100,
+ Thereafter: 100,
+ Hook: shook,
+ },
+ Encoding: "json",
+ EncoderConfig: NewProductionEncoderConfig(),
+ OutputPaths: []string{"stderr"},
+ ErrorOutputPaths: []string{"stderr"},
+ }
+ expectRe := `{"level":"info","caller":"[a-z0-9_-]+/config_test.go:\d+","msg":"info","k":"v","z":"zz"}` + "\n" +
+ `{"level":"warn","caller":"[a-z0-9_-]+/config_test.go:\d+","msg":"warn","k":"v","z":"zz"}` + "\n"
+ expectDropped := 99 // 200 - 100 initial - 1 thereafter
+ expectSampled := 103 // 2 from initial + 100 + 1 thereafter
+
+ logOut := filepath.Join(t.TempDir(), "test.log")
+ cfg.OutputPaths = []string{logOut}
+ cfg.EncoderConfig.TimeKey = "" // no timestamps in tests
+ cfg.InitialFields = map[string]interface{}{"z": "zz", "k": "v"}
+
+ logger, err := cfg.Build()
+ require.NoError(t, err, "Unexpected error constructing logger.")
+
+ logger.Debug("debug")
+ logger.Info("info")
+ logger.Warn("warn")
+
+ byteContents, err := os.ReadFile(logOut)
+ require.NoError(t, err, "Couldn't read log contents from temp file.")
+ logs := string(byteContents)
+ assert.Regexp(t, expectRe, logs, "Unexpected log output.")
+
+ for i := 0; i < 200; i++ {
+ logger.Info("sampling")
+ }
+ assert.Equal(t, int64(expectDropped), dcount.Load())
+ assert.Equal(t, int64(expectSampled), scount.Load())
+}
diff --git a/vendor/go.uber.org/zap/doc.go b/vendor/go.uber.org/zap/doc.go
new file mode 100644
index 0000000000..3c50d7b4d3
--- /dev/null
+++ b/vendor/go.uber.org/zap/doc.go
@@ -0,0 +1,117 @@
+// 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 zap provides fast, structured, leveled logging.
+//
+// For applications that log in the hot path, reflection-based serialization
+// and string formatting are prohibitively expensive - they're CPU-intensive
+// and make many small allocations. Put differently, using json.Marshal and
+// fmt.Fprintf to log tons of interface{} makes your application slow.
+//
+// Zap takes a different approach. It includes a reflection-free,
+// zero-allocation JSON encoder, and the base Logger strives to avoid
+// serialization overhead and allocations wherever possible. By building the
+// high-level SugaredLogger on that foundation, zap lets users choose when
+// they need to count every allocation and when they'd prefer a more familiar,
+// loosely typed API.
+//
+// # Choosing a Logger
+//
+// In contexts where performance is nice, but not critical, use the
+// SugaredLogger. It's 4-10x faster than other structured logging packages and
+// supports both structured and printf-style logging. Like log15 and go-kit,
+// the SugaredLogger's structured logging APIs are loosely typed and accept a
+// variadic number of key-value pairs. (For more advanced use cases, they also
+// accept strongly typed fields - see the SugaredLogger.With documentation for
+// details.)
+//
+// sugar := zap.NewExample().Sugar()
+// defer sugar.Sync()
+// sugar.Infow("failed to fetch URL",
+// "url", "http://example.com",
+// "attempt", 3,
+// "backoff", time.Second,
+// )
+// sugar.Infof("failed to fetch URL: %s", "http://example.com")
+//
+// By default, loggers are unbuffered. However, since zap's low-level APIs
+// allow buffering, calling Sync before letting your process exit is a good
+// habit.
+//
+// In the rare contexts where every microsecond and every allocation matter,
+// use the Logger. It's even faster than the SugaredLogger and allocates far
+// less, but it only supports strongly-typed, structured logging.
+//
+// logger := zap.NewExample()
+// defer logger.Sync()
+// logger.Info("failed to fetch URL",
+// zap.String("url", "http://example.com"),
+// zap.Int("attempt", 3),
+// zap.Duration("backoff", time.Second),
+// )
+//
+// Choosing between the Logger and SugaredLogger doesn't need to be an
+// application-wide decision: converting between the two is simple and
+// inexpensive.
+//
+// logger := zap.NewExample()
+// defer logger.Sync()
+// sugar := logger.Sugar()
+// plain := sugar.Desugar()
+//
+// # Configuring Zap
+//
+// The simplest way to build a Logger is to use zap's opinionated presets:
+// NewExample, NewProduction, and NewDevelopment. These presets build a logger
+// with a single function call:
+//
+// logger, err := zap.NewProduction()
+// if err != nil {
+// log.Fatalf("can't initialize zap logger: %v", err)
+// }
+// defer logger.Sync()
+//
+// Presets are fine for small projects, but larger projects and organizations
+// naturally require a bit more customization. For most users, zap's Config
+// struct strikes the right balance between flexibility and convenience. See
+// the package-level BasicConfiguration example for sample code.
+//
+// More unusual configurations (splitting output between files, sending logs
+// to a message queue, etc.) are possible, but require direct use of
+// go.uber.org/zap/zapcore. See the package-level AdvancedConfiguration
+// example for sample code.
+//
+// # Extending Zap
+//
+// The zap package itself is a relatively thin wrapper around the interfaces
+// in go.uber.org/zap/zapcore. Extending zap to support a new encoding (e.g.,
+// BSON), a new log sink (e.g., Kafka), or something more exotic (perhaps an
+// exception aggregation service, like Sentry or Rollbar) typically requires
+// implementing the zapcore.Encoder, zapcore.WriteSyncer, or zapcore.Core
+// interfaces. See the zapcore documentation for details.
+//
+// Similarly, package authors can use the high-performance Encoder and Core
+// implementations in the zapcore package to build their own loggers.
+//
+// # Frequently Asked Questions
+//
+// An FAQ covering everything from installation errors to design decisions is
+// available at https://github.com/uber-go/zap/blob/master/FAQ.md.
+package zap // import "go.uber.org/zap"
diff --git a/vendor/go.uber.org/zap/encoder.go b/vendor/go.uber.org/zap/encoder.go
new file mode 100644
index 0000000000..caa04ceefd
--- /dev/null
+++ b/vendor/go.uber.org/zap/encoder.go
@@ -0,0 +1,79 @@
+// 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 zap
+
+import (
+ "errors"
+ "fmt"
+ "sync"
+
+ "go.uber.org/zap/zapcore"
+)
+
+var (
+ errNoEncoderNameSpecified = errors.New("no encoder name specified")
+
+ _encoderNameToConstructor = map[string]func(zapcore.EncoderConfig) (zapcore.Encoder, error){
+ "console": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) {
+ return zapcore.NewConsoleEncoder(encoderConfig), nil
+ },
+ "json": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) {
+ return zapcore.NewJSONEncoder(encoderConfig), nil
+ },
+ }
+ _encoderMutex sync.RWMutex
+)
+
+// RegisterEncoder registers an encoder constructor, which the Config struct
+// can then reference. By default, the "json" and "console" encoders are
+// registered.
+//
+// Attempting to register an encoder whose name is already taken returns an
+// error.
+func RegisterEncoder(name string, constructor func(zapcore.EncoderConfig) (zapcore.Encoder, error)) error {
+ _encoderMutex.Lock()
+ defer _encoderMutex.Unlock()
+ if name == "" {
+ return errNoEncoderNameSpecified
+ }
+ if _, ok := _encoderNameToConstructor[name]; ok {
+ return fmt.Errorf("encoder already registered for name %q", name)
+ }
+ _encoderNameToConstructor[name] = constructor
+ return nil
+}
+
+func newEncoder(name string, encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) {
+ if encoderConfig.TimeKey != "" && encoderConfig.EncodeTime == nil {
+ return nil, errors.New("missing EncodeTime in EncoderConfig")
+ }
+
+ _encoderMutex.RLock()
+ defer _encoderMutex.RUnlock()
+ if name == "" {
+ return nil, errNoEncoderNameSpecified
+ }
+ constructor, ok := _encoderNameToConstructor[name]
+ if !ok {
+ return nil, fmt.Errorf("no encoder registered for name %q", name)
+ }
+ return constructor(encoderConfig)
+}
diff --git a/vendor/go.uber.org/zap/encoder_test.go b/vendor/go.uber.org/zap/encoder_test.go
new file mode 100644
index 0000000000..b71eb654b7
--- /dev/null
+++ b/vendor/go.uber.org/zap/encoder_test.go
@@ -0,0 +1,88 @@
+// 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 zap
+
+import (
+ "testing"
+
+ "go.uber.org/zap/zapcore"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestRegisterDefaultEncoders(t *testing.T) {
+ testEncodersRegistered(t, "console", "json")
+}
+
+func TestRegisterEncoder(t *testing.T) {
+ testEncoders(func() {
+ assert.NoError(t, RegisterEncoder("foo", newNilEncoder), "expected to be able to register the encoder foo")
+ testEncodersRegistered(t, "foo")
+ })
+}
+
+func TestDuplicateRegisterEncoder(t *testing.T) {
+ testEncoders(func() {
+ assert.NoError(t, RegisterEncoder("foo", newNilEncoder), "expected to be able to register the encoder foo")
+ assert.Error(t, RegisterEncoder("foo", newNilEncoder), "expected an error when registering an encoder with the same name twice")
+ })
+}
+
+func TestRegisterEncoderNoName(t *testing.T) {
+ assert.Equal(t, errNoEncoderNameSpecified, RegisterEncoder("", newNilEncoder), "expected an error when registering an encoder with no name")
+}
+
+func TestNewEncoder(t *testing.T) {
+ testEncoders(func() {
+ assert.NoError(t, RegisterEncoder("foo", newNilEncoder), "expected to be able to register the encoder foo")
+ encoder, err := newEncoder("foo", zapcore.EncoderConfig{})
+ assert.NoError(t, err, "could not create an encoder for the registered name foo")
+ assert.Nil(t, encoder, "the encoder from newNilEncoder is not nil")
+ })
+}
+
+func TestNewEncoderNotRegistered(t *testing.T) {
+ _, err := newEncoder("foo", zapcore.EncoderConfig{})
+ assert.Error(t, err, "expected an error when trying to create an encoder of an unregistered name")
+}
+
+func TestNewEncoderNoName(t *testing.T) {
+ _, err := newEncoder("", zapcore.EncoderConfig{})
+ assert.Equal(t, errNoEncoderNameSpecified, err, "expected an error when creating an encoder with no name")
+}
+
+func testEncoders(f func()) {
+ existing := _encoderNameToConstructor
+ _encoderNameToConstructor = make(map[string]func(zapcore.EncoderConfig) (zapcore.Encoder, error))
+ defer func() { _encoderNameToConstructor = existing }()
+ f()
+}
+
+func testEncodersRegistered(t *testing.T, names ...string) {
+ assert.Len(t, _encoderNameToConstructor, len(names), "the expected number of registered encoders does not match the actual number")
+ for _, name := range names {
+ assert.NotNil(t, _encoderNameToConstructor[name], "no encoder is registered for name %s", name)
+ }
+}
+
+func newNilEncoder(_ zapcore.EncoderConfig) (zapcore.Encoder, error) {
+ return nil, nil
+}
diff --git a/vendor/go.uber.org/zap/error.go b/vendor/go.uber.org/zap/error.go
new file mode 100644
index 0000000000..45f7b838dc
--- /dev/null
+++ b/vendor/go.uber.org/zap/error.go
@@ -0,0 +1,82 @@
+// 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 zap
+
+import (
+ "go.uber.org/zap/internal/pool"
+ "go.uber.org/zap/zapcore"
+)
+
+var _errArrayElemPool = pool.New(func() *errArrayElem {
+ return &errArrayElem{}
+})
+
+// Error is shorthand for the common idiom NamedError("error", err).
+func Error(err error) Field {
+ return NamedError("error", err)
+}
+
+// NamedError constructs a field that lazily stores err.Error() under the
+// provided key. Errors which also implement fmt.Formatter (like those produced
+// by github.com/pkg/errors) will also have their verbose representation stored
+// under key+"Verbose". If passed a nil error, the field is a no-op.
+//
+// For the common case in which the key is simply "error", the Error function
+// is shorter and less repetitive.
+func NamedError(key string, err error) Field {
+ if err == nil {
+ return Skip()
+ }
+ return Field{Key: key, Type: zapcore.ErrorType, Interface: err}
+}
+
+type errArray []error
+
+func (errs errArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range errs {
+ if errs[i] == nil {
+ continue
+ }
+ // To represent each error as an object with an "error" attribute and
+ // potentially an "errorVerbose" attribute, we need to wrap it in a
+ // type that implements LogObjectMarshaler. To prevent this from
+ // allocating, pool the wrapper type.
+ elem := _errArrayElemPool.Get()
+ elem.error = errs[i]
+ err := arr.AppendObject(elem)
+ elem.error = nil
+ _errArrayElemPool.Put(elem)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+type errArrayElem struct {
+ error
+}
+
+func (e *errArrayElem) MarshalLogObject(enc zapcore.ObjectEncoder) error {
+ // Re-use the error field's logic, which supports non-standard error types.
+ Error(e.error).AddTo(enc)
+ return nil
+}
diff --git a/vendor/go.uber.org/zap/error_test.go b/vendor/go.uber.org/zap/error_test.go
new file mode 100644
index 0000000000..4bfa370d2a
--- /dev/null
+++ b/vendor/go.uber.org/zap/error_test.go
@@ -0,0 +1,133 @@
+// 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 zap
+
+import (
+ "errors"
+ "fmt"
+ "testing"
+
+ "go.uber.org/zap/zapcore"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestErrorConstructors(t *testing.T) {
+ fail := errors.New("fail")
+
+ tests := []struct {
+ name string
+ field Field
+ expect Field
+ }{
+ {"Error", Skip(), Error(nil)},
+ {"Error", Field{Key: "error", Type: zapcore.ErrorType, Interface: fail}, Error(fail)},
+ {"NamedError", Skip(), NamedError("foo", nil)},
+ {"NamedError", Field{Key: "foo", Type: zapcore.ErrorType, Interface: fail}, NamedError("foo", fail)},
+ {"Any:Error", Any("k", errors.New("v")), NamedError("k", errors.New("v"))},
+ {"Any:Errors", Any("k", []error{errors.New("v")}), Errors("k", []error{errors.New("v")})},
+ }
+
+ for _, tt := range tests {
+ if !assert.Equal(t, tt.expect, tt.field, "Unexpected output from convenience field constructor %s.", tt.name) {
+ t.Logf("type expected: %T\nGot: %T", tt.expect.Interface, tt.field.Interface)
+ }
+ assertCanBeReused(t, tt.field)
+ }
+}
+
+func TestErrorArrayConstructor(t *testing.T) {
+ tests := []struct {
+ desc string
+ field Field
+ expected []interface{}
+ }{
+ {"empty errors", Errors("", []error{}), []interface{}{}},
+ {
+ "errors",
+ Errors("", []error{nil, errors.New("foo"), nil, errors.New("bar")}),
+ []interface{}{map[string]interface{}{"error": "foo"}, map[string]interface{}{"error": "bar"}},
+ },
+ }
+
+ for _, tt := range tests {
+ enc := zapcore.NewMapObjectEncoder()
+ tt.field.Key = "k"
+ tt.field.AddTo(enc)
+ assert.Equal(t, tt.expected, enc.Fields["k"], "%s: unexpected map contents.", tt.desc)
+ assert.Equal(t, 1, len(enc.Fields), "%s: found extra keys in map: %v", tt.desc, enc.Fields)
+ }
+}
+
+func TestErrorsArraysHandleRichErrors(t *testing.T) {
+ errs := []error{fmt.Errorf("egad")}
+
+ enc := zapcore.NewMapObjectEncoder()
+ Errors("k", errs).AddTo(enc)
+ assert.Equal(t, 1, len(enc.Fields), "Expected only top-level field.")
+
+ val := enc.Fields["k"]
+ arr, ok := val.([]interface{})
+ require.True(t, ok, "Expected top-level field to be an array.")
+ require.Equal(t, 1, len(arr), "Expected only one error object in array.")
+
+ serialized := arr[0]
+ errMap, ok := serialized.(map[string]interface{})
+ require.True(t, ok, "Expected serialized error to be a map, got %T.", serialized)
+ assert.Equal(t, "egad", errMap["error"], "Unexpected standard error string.")
+}
+
+func TestErrArrayBrokenEncoder(t *testing.T) {
+ t.Parallel()
+
+ failWith := errors.New("great sadness")
+ err := (brokenArrayObjectEncoder{
+ Err: failWith,
+ ObjectEncoder: zapcore.NewMapObjectEncoder(),
+ }).AddArray("errors", errArray{
+ errors.New("foo"),
+ errors.New("bar"),
+ })
+ require.Error(t, err, "Expected error from broken encoder.")
+ assert.ErrorIs(t, err, failWith, "Unexpected error.")
+}
+
+// brokenArrayObjectEncoder is an ObjectEncoder
+// that builds a broken ArrayEncoder.
+type brokenArrayObjectEncoder struct {
+ zapcore.ObjectEncoder
+ zapcore.ArrayEncoder
+
+ Err error // error to return
+}
+
+func (enc brokenArrayObjectEncoder) AddArray(key string, marshaler zapcore.ArrayMarshaler) error {
+ return enc.ObjectEncoder.AddArray(key,
+ zapcore.ArrayMarshalerFunc(func(ae zapcore.ArrayEncoder) error {
+ enc.ArrayEncoder = ae
+ return marshaler.MarshalLogArray(enc)
+ }))
+}
+
+func (enc brokenArrayObjectEncoder) AppendObject(zapcore.ObjectMarshaler) error {
+ return enc.Err
+}
diff --git a/vendor/go.uber.org/zap/example_test.go b/vendor/go.uber.org/zap/example_test.go
new file mode 100644
index 0000000000..af7df0e25a
--- /dev/null
+++ b/vendor/go.uber.org/zap/example_test.go
@@ -0,0 +1,413 @@
+// 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 zap_test
+
+import (
+ "encoding/json"
+ "io"
+ "log"
+ "os"
+ "time"
+
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+)
+
+func Example_presets() {
+ // Using zap's preset constructors is the simplest way to get a feel for the
+ // package, but they don't allow much customization.
+ logger := zap.NewExample() // or NewProduction, or NewDevelopment
+ defer logger.Sync()
+
+ const url = "http://example.com"
+
+ // In most circumstances, use the SugaredLogger. It's 4-10x faster than most
+ // other structured logging packages and has a familiar, loosely-typed API.
+ sugar := logger.Sugar()
+ sugar.Infow("Failed to fetch URL.",
+ // Structured context as loosely typed key-value pairs.
+ "url", url,
+ "attempt", 3,
+ "backoff", time.Second,
+ )
+ sugar.Infof("Failed to fetch URL: %s", url)
+
+ // In the unusual situations where every microsecond matters, use the
+ // Logger. It's even faster than the SugaredLogger, but only supports
+ // structured logging.
+ logger.Info("Failed to fetch URL.",
+ // Structured context as strongly typed fields.
+ zap.String("url", url),
+ zap.Int("attempt", 3),
+ zap.Duration("backoff", time.Second),
+ )
+ // Output:
+ // {"level":"info","msg":"Failed to fetch URL.","url":"http://example.com","attempt":3,"backoff":"1s"}
+ // {"level":"info","msg":"Failed to fetch URL: http://example.com"}
+ // {"level":"info","msg":"Failed to fetch URL.","url":"http://example.com","attempt":3,"backoff":"1s"}
+}
+
+func Example_basicConfiguration() {
+ // For some users, the presets offered by the NewProduction, NewDevelopment,
+ // and NewExample constructors won't be appropriate. For most of those
+ // users, the bundled Config struct offers the right balance of flexibility
+ // and convenience. (For more complex needs, see the AdvancedConfiguration
+ // example.)
+ //
+ // See the documentation for Config and zapcore.EncoderConfig for all the
+ // available options.
+ rawJSON := []byte(`{
+ "level": "debug",
+ "encoding": "json",
+ "outputPaths": ["stdout", "/tmp/logs"],
+ "errorOutputPaths": ["stderr"],
+ "initialFields": {"foo": "bar"},
+ "encoderConfig": {
+ "messageKey": "message",
+ "levelKey": "level",
+ "levelEncoder": "lowercase"
+ }
+ }`)
+
+ var cfg zap.Config
+ if err := json.Unmarshal(rawJSON, &cfg); err != nil {
+ panic(err)
+ }
+ logger := zap.Must(cfg.Build())
+ defer logger.Sync()
+
+ logger.Info("logger construction succeeded")
+ // Output:
+ // {"level":"info","message":"logger construction succeeded","foo":"bar"}
+}
+
+func Example_advancedConfiguration() {
+ // The bundled Config struct only supports the most common configuration
+ // options. More complex needs, like splitting logs between multiple files
+ // or writing to non-file outputs, require use of the zapcore package.
+ //
+ // In this example, imagine we're both sending our logs to Kafka and writing
+ // them to the console. We'd like to encode the console output and the Kafka
+ // topics differently, and we'd also like special treatment for
+ // high-priority logs.
+
+ // First, define our level-handling logic.
+ highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
+ return lvl >= zapcore.ErrorLevel
+ })
+ lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
+ return lvl < zapcore.ErrorLevel
+ })
+
+ // Assume that we have clients for two Kafka topics. The clients implement
+ // zapcore.WriteSyncer and are safe for concurrent use. (If they only
+ // implement io.Writer, we can use zapcore.AddSync to add a no-op Sync
+ // method. If they're not safe for concurrent use, we can add a protecting
+ // mutex with zapcore.Lock.)
+ topicDebugging := zapcore.AddSync(io.Discard)
+ topicErrors := zapcore.AddSync(io.Discard)
+
+ // High-priority output should also go to standard error, and low-priority
+ // output should also go to standard out.
+ consoleDebugging := zapcore.Lock(os.Stdout)
+ consoleErrors := zapcore.Lock(os.Stderr)
+
+ // Optimize the Kafka output for machine consumption and the console output
+ // for human operators.
+ kafkaEncoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
+ consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())
+
+ // Join the outputs, encoders, and level-handling functions into
+ // zapcore.Cores, then tee the four cores together.
+ core := zapcore.NewTee(
+ zapcore.NewCore(kafkaEncoder, topicErrors, highPriority),
+ zapcore.NewCore(consoleEncoder, consoleErrors, highPriority),
+ zapcore.NewCore(kafkaEncoder, topicDebugging, lowPriority),
+ zapcore.NewCore(consoleEncoder, consoleDebugging, lowPriority),
+ )
+
+ // From a zapcore.Core, it's easy to construct a Logger.
+ logger := zap.New(core)
+ defer logger.Sync()
+ logger.Info("constructed a logger")
+}
+
+func ExampleNamespace() {
+ logger := zap.NewExample()
+ defer logger.Sync()
+
+ logger.With(
+ zap.Namespace("metrics"),
+ zap.Int("counter", 1),
+ ).Info("tracked some metrics")
+ // Output:
+ // {"level":"info","msg":"tracked some metrics","metrics":{"counter":1}}
+}
+
+type addr struct {
+ IP string
+ Port int
+}
+
+type request struct {
+ URL string
+ Listen addr
+ Remote addr
+}
+
+func (a addr) MarshalLogObject(enc zapcore.ObjectEncoder) error {
+ enc.AddString("ip", a.IP)
+ enc.AddInt("port", a.Port)
+ return nil
+}
+
+func (r *request) MarshalLogObject(enc zapcore.ObjectEncoder) error {
+ enc.AddString("url", r.URL)
+ zap.Inline(r.Listen).AddTo(enc)
+ return enc.AddObject("remote", r.Remote)
+}
+
+func ExampleObject() {
+ logger := zap.NewExample()
+ defer logger.Sync()
+
+ req := &request{
+ URL: "/test",
+ Listen: addr{"127.0.0.1", 8080},
+ Remote: addr{"127.0.0.1", 31200},
+ }
+ logger.Info("new request, in nested object", zap.Object("req", req))
+ logger.Info("new request, inline", zap.Inline(req))
+ // Output:
+ // {"level":"info","msg":"new request, in nested object","req":{"url":"/test","ip":"127.0.0.1","port":8080,"remote":{"ip":"127.0.0.1","port":31200}}}
+ // {"level":"info","msg":"new request, inline","url":"/test","ip":"127.0.0.1","port":8080,"remote":{"ip":"127.0.0.1","port":31200}}
+}
+
+func ExampleNewStdLog() {
+ logger := zap.NewExample()
+ defer logger.Sync()
+
+ std := zap.NewStdLog(logger)
+ std.Print("standard logger wrapper")
+ // Output:
+ // {"level":"info","msg":"standard logger wrapper"}
+}
+
+func ExampleRedirectStdLog() {
+ logger := zap.NewExample()
+ defer logger.Sync()
+
+ undo := zap.RedirectStdLog(logger)
+ defer undo()
+
+ log.Print("redirected standard library")
+ // Output:
+ // {"level":"info","msg":"redirected standard library"}
+}
+
+func ExampleReplaceGlobals() {
+ logger := zap.NewExample()
+ defer logger.Sync()
+
+ undo := zap.ReplaceGlobals(logger)
+ defer undo()
+
+ zap.L().Info("replaced zap's global loggers")
+ // Output:
+ // {"level":"info","msg":"replaced zap's global loggers"}
+}
+
+func ExampleAtomicLevel() {
+ atom := zap.NewAtomicLevel()
+
+ // To keep the example deterministic, disable timestamps in the output.
+ encoderCfg := zap.NewProductionEncoderConfig()
+ encoderCfg.TimeKey = ""
+
+ logger := zap.New(zapcore.NewCore(
+ zapcore.NewJSONEncoder(encoderCfg),
+ zapcore.Lock(os.Stdout),
+ atom,
+ ))
+ defer logger.Sync()
+
+ logger.Info("info logging enabled")
+
+ atom.SetLevel(zap.ErrorLevel)
+ logger.Info("info logging disabled")
+ // Output:
+ // {"level":"info","msg":"info logging enabled"}
+}
+
+func ExampleAtomicLevel_config() {
+ // The zap.Config struct includes an AtomicLevel. To use it, keep a
+ // reference to the Config.
+ rawJSON := []byte(`{
+ "level": "info",
+ "outputPaths": ["stdout"],
+ "errorOutputPaths": ["stderr"],
+ "encoding": "json",
+ "encoderConfig": {
+ "messageKey": "message",
+ "levelKey": "level",
+ "levelEncoder": "lowercase"
+ }
+ }`)
+ var cfg zap.Config
+ if err := json.Unmarshal(rawJSON, &cfg); err != nil {
+ panic(err)
+ }
+ logger := zap.Must(cfg.Build())
+ defer logger.Sync()
+
+ logger.Info("info logging enabled")
+
+ cfg.Level.SetLevel(zap.ErrorLevel)
+ logger.Info("info logging disabled")
+ // Output:
+ // {"level":"info","message":"info logging enabled"}
+}
+
+func ExampleLogger_Check() {
+ logger := zap.NewExample()
+ defer logger.Sync()
+
+ if ce := logger.Check(zap.DebugLevel, "debugging"); ce != nil {
+ // If debug-level log output isn't enabled or if zap's sampling would have
+ // dropped this log entry, we don't allocate the slice that holds these
+ // fields.
+ ce.Write(
+ zap.String("foo", "bar"),
+ zap.String("baz", "quux"),
+ )
+ }
+
+ // Output:
+ // {"level":"debug","msg":"debugging","foo":"bar","baz":"quux"}
+}
+
+func ExampleLogger_Named() {
+ logger := zap.NewExample()
+ defer logger.Sync()
+
+ // By default, Loggers are unnamed.
+ logger.Info("no name")
+
+ // The first call to Named sets the Logger name.
+ main := logger.Named("main")
+ main.Info("main logger")
+
+ // Additional calls to Named create a period-separated path.
+ main.Named("subpackage").Info("sub-logger")
+ // Output:
+ // {"level":"info","msg":"no name"}
+ // {"level":"info","logger":"main","msg":"main logger"}
+ // {"level":"info","logger":"main.subpackage","msg":"sub-logger"}
+}
+
+func ExampleWrapCore_replace() {
+ // Replacing a Logger's core can alter fundamental behaviors.
+ // For example, it can convert a Logger to a no-op.
+ nop := zap.WrapCore(func(zapcore.Core) zapcore.Core {
+ return zapcore.NewNopCore()
+ })
+
+ logger := zap.NewExample()
+ defer logger.Sync()
+
+ logger.Info("working")
+ logger.WithOptions(nop).Info("no-op")
+ logger.Info("original logger still works")
+ // Output:
+ // {"level":"info","msg":"working"}
+ // {"level":"info","msg":"original logger still works"}
+}
+
+func ExampleWrapCore_wrap() {
+ // Wrapping a Logger's core can extend its functionality. As a trivial
+ // example, it can double-write all logs.
+ doubled := zap.WrapCore(func(c zapcore.Core) zapcore.Core {
+ return zapcore.NewTee(c, c)
+ })
+
+ logger := zap.NewExample()
+ defer logger.Sync()
+
+ logger.Info("single")
+ logger.WithOptions(doubled).Info("doubled")
+ // Output:
+ // {"level":"info","msg":"single"}
+ // {"level":"info","msg":"doubled"}
+ // {"level":"info","msg":"doubled"}
+}
+
+func ExampleDict() {
+ logger := zap.NewExample()
+ defer logger.Sync()
+
+ logger.Info("login event",
+ zap.Dict("event",
+ zap.Int("id", 123),
+ zap.String("name", "jane"),
+ zap.String("status", "pending")))
+ // Output:
+ // {"level":"info","msg":"login event","event":{"id":123,"name":"jane","status":"pending"}}
+}
+
+func ExampleObjects() {
+ logger := zap.NewExample()
+ defer logger.Sync()
+
+ // Use the Objects field constructor when you have a list of objects,
+ // all of which implement zapcore.ObjectMarshaler.
+ logger.Debug("opening connections",
+ zap.Objects("addrs", []addr{
+ {IP: "123.45.67.89", Port: 4040},
+ {IP: "127.0.0.1", Port: 4041},
+ {IP: "192.168.0.1", Port: 4042},
+ }))
+ // Output:
+ // {"level":"debug","msg":"opening connections","addrs":[{"ip":"123.45.67.89","port":4040},{"ip":"127.0.0.1","port":4041},{"ip":"192.168.0.1","port":4042}]}
+}
+
+func ExampleObjectValues() {
+ logger := zap.NewExample()
+ defer logger.Sync()
+
+ // Use the ObjectValues field constructor when you have a list of
+ // objects that do not implement zapcore.ObjectMarshaler directly,
+ // but on their pointer receivers.
+ logger.Debug("starting tunnels",
+ zap.ObjectValues("addrs", []request{
+ {
+ URL: "/foo",
+ Listen: addr{"127.0.0.1", 8080},
+ Remote: addr{"123.45.67.89", 4040},
+ },
+ {
+ URL: "/bar",
+ Listen: addr{"127.0.0.1", 8080},
+ Remote: addr{"127.0.0.1", 31200},
+ },
+ }))
+ // Output:
+ // {"level":"debug","msg":"starting tunnels","addrs":[{"url":"/foo","ip":"127.0.0.1","port":8080,"remote":{"ip":"123.45.67.89","port":4040}},{"url":"/bar","ip":"127.0.0.1","port":8080,"remote":{"ip":"127.0.0.1","port":31200}}]}
+}
diff --git a/vendor/go.uber.org/zap/field.go b/vendor/go.uber.org/zap/field.go
new file mode 100644
index 0000000000..c8dd3358a9
--- /dev/null
+++ b/vendor/go.uber.org/zap/field.go
@@ -0,0 +1,613 @@
+// 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 zap
+
+import (
+ "fmt"
+ "math"
+ "time"
+
+ "go.uber.org/zap/internal/stacktrace"
+ "go.uber.org/zap/zapcore"
+)
+
+// Field is an alias for Field. Aliasing this type dramatically
+// improves the navigability of this package's API documentation.
+type Field = zapcore.Field
+
+var (
+ _minTimeInt64 = time.Unix(0, math.MinInt64)
+ _maxTimeInt64 = time.Unix(0, math.MaxInt64)
+)
+
+// Skip constructs a no-op field, which is often useful when handling invalid
+// inputs in other Field constructors.
+func Skip() Field {
+ return Field{Type: zapcore.SkipType}
+}
+
+// nilField returns a field which will marshal explicitly as nil. See motivation
+// in https://github.com/uber-go/zap/issues/753 . If we ever make breaking
+// changes and add zapcore.NilType and zapcore.ObjectEncoder.AddNil, the
+// implementation here should be changed to reflect that.
+func nilField(key string) Field { return Reflect(key, nil) }
+
+// Binary constructs a field that carries an opaque binary blob.
+//
+// Binary data is serialized in an encoding-appropriate format. For example,
+// zap's JSON encoder base64-encodes binary blobs. To log UTF-8 encoded text,
+// use ByteString.
+func Binary(key string, val []byte) Field {
+ return Field{Key: key, Type: zapcore.BinaryType, Interface: val}
+}
+
+// Bool constructs a field that carries a bool.
+func Bool(key string, val bool) Field {
+ var ival int64
+ if val {
+ ival = 1
+ }
+ return Field{Key: key, Type: zapcore.BoolType, Integer: ival}
+}
+
+// Boolp constructs a field that carries a *bool. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Boolp(key string, val *bool) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Bool(key, *val)
+}
+
+// ByteString constructs a field that carries UTF-8 encoded text as a []byte.
+// To log opaque binary blobs (which aren't necessarily valid UTF-8), use
+// Binary.
+func ByteString(key string, val []byte) Field {
+ return Field{Key: key, Type: zapcore.ByteStringType, Interface: val}
+}
+
+// Complex128 constructs a field that carries a complex number. Unlike most
+// numeric fields, this costs an allocation (to convert the complex128 to
+// interface{}).
+func Complex128(key string, val complex128) Field {
+ return Field{Key: key, Type: zapcore.Complex128Type, Interface: val}
+}
+
+// Complex128p constructs a field that carries a *complex128. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Complex128p(key string, val *complex128) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Complex128(key, *val)
+}
+
+// Complex64 constructs a field that carries a complex number. Unlike most
+// numeric fields, this costs an allocation (to convert the complex64 to
+// interface{}).
+func Complex64(key string, val complex64) Field {
+ return Field{Key: key, Type: zapcore.Complex64Type, Interface: val}
+}
+
+// Complex64p constructs a field that carries a *complex64. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Complex64p(key string, val *complex64) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Complex64(key, *val)
+}
+
+// Float64 constructs a field that carries a float64. The way the
+// floating-point value is represented is encoder-dependent, so marshaling is
+// necessarily lazy.
+func Float64(key string, val float64) Field {
+ return Field{Key: key, Type: zapcore.Float64Type, Integer: int64(math.Float64bits(val))}
+}
+
+// Float64p constructs a field that carries a *float64. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Float64p(key string, val *float64) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Float64(key, *val)
+}
+
+// Float32 constructs a field that carries a float32. The way the
+// floating-point value is represented is encoder-dependent, so marshaling is
+// necessarily lazy.
+func Float32(key string, val float32) Field {
+ return Field{Key: key, Type: zapcore.Float32Type, Integer: int64(math.Float32bits(val))}
+}
+
+// Float32p constructs a field that carries a *float32. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Float32p(key string, val *float32) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Float32(key, *val)
+}
+
+// Int constructs a field with the given key and value.
+func Int(key string, val int) Field {
+ return Int64(key, int64(val))
+}
+
+// Intp constructs a field that carries a *int. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Intp(key string, val *int) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Int(key, *val)
+}
+
+// Int64 constructs a field with the given key and value.
+func Int64(key string, val int64) Field {
+ return Field{Key: key, Type: zapcore.Int64Type, Integer: val}
+}
+
+// Int64p constructs a field that carries a *int64. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Int64p(key string, val *int64) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Int64(key, *val)
+}
+
+// Int32 constructs a field with the given key and value.
+func Int32(key string, val int32) Field {
+ return Field{Key: key, Type: zapcore.Int32Type, Integer: int64(val)}
+}
+
+// Int32p constructs a field that carries a *int32. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Int32p(key string, val *int32) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Int32(key, *val)
+}
+
+// Int16 constructs a field with the given key and value.
+func Int16(key string, val int16) Field {
+ return Field{Key: key, Type: zapcore.Int16Type, Integer: int64(val)}
+}
+
+// Int16p constructs a field that carries a *int16. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Int16p(key string, val *int16) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Int16(key, *val)
+}
+
+// Int8 constructs a field with the given key and value.
+func Int8(key string, val int8) Field {
+ return Field{Key: key, Type: zapcore.Int8Type, Integer: int64(val)}
+}
+
+// Int8p constructs a field that carries a *int8. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Int8p(key string, val *int8) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Int8(key, *val)
+}
+
+// String constructs a field with the given key and value.
+func String(key string, val string) Field {
+ return Field{Key: key, Type: zapcore.StringType, String: val}
+}
+
+// Stringp constructs a field that carries a *string. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Stringp(key string, val *string) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return String(key, *val)
+}
+
+// Uint constructs a field with the given key and value.
+func Uint(key string, val uint) Field {
+ return Uint64(key, uint64(val))
+}
+
+// Uintp constructs a field that carries a *uint. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Uintp(key string, val *uint) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Uint(key, *val)
+}
+
+// Uint64 constructs a field with the given key and value.
+func Uint64(key string, val uint64) Field {
+ return Field{Key: key, Type: zapcore.Uint64Type, Integer: int64(val)}
+}
+
+// Uint64p constructs a field that carries a *uint64. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Uint64p(key string, val *uint64) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Uint64(key, *val)
+}
+
+// Uint32 constructs a field with the given key and value.
+func Uint32(key string, val uint32) Field {
+ return Field{Key: key, Type: zapcore.Uint32Type, Integer: int64(val)}
+}
+
+// Uint32p constructs a field that carries a *uint32. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Uint32p(key string, val *uint32) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Uint32(key, *val)
+}
+
+// Uint16 constructs a field with the given key and value.
+func Uint16(key string, val uint16) Field {
+ return Field{Key: key, Type: zapcore.Uint16Type, Integer: int64(val)}
+}
+
+// Uint16p constructs a field that carries a *uint16. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Uint16p(key string, val *uint16) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Uint16(key, *val)
+}
+
+// Uint8 constructs a field with the given key and value.
+func Uint8(key string, val uint8) Field {
+ return Field{Key: key, Type: zapcore.Uint8Type, Integer: int64(val)}
+}
+
+// Uint8p constructs a field that carries a *uint8. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Uint8p(key string, val *uint8) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Uint8(key, *val)
+}
+
+// Uintptr constructs a field with the given key and value.
+func Uintptr(key string, val uintptr) Field {
+ return Field{Key: key, Type: zapcore.UintptrType, Integer: int64(val)}
+}
+
+// Uintptrp constructs a field that carries a *uintptr. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Uintptrp(key string, val *uintptr) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Uintptr(key, *val)
+}
+
+// Reflect constructs a field with the given key and an arbitrary object. It uses
+// an encoding-appropriate, reflection-based function to lazily serialize nearly
+// any object into the logging context, but it's relatively slow and
+// allocation-heavy. Outside tests, Any is always a better choice.
+//
+// If encoding fails (e.g., trying to serialize a map[int]string to JSON), Reflect
+// includes the error message in the final log output.
+func Reflect(key string, val interface{}) Field {
+ return Field{Key: key, Type: zapcore.ReflectType, Interface: val}
+}
+
+// Namespace creates a named, isolated scope within the logger's context. All
+// subsequent fields will be added to the new namespace.
+//
+// This helps prevent key collisions when injecting loggers into sub-components
+// or third-party libraries.
+func Namespace(key string) Field {
+ return Field{Key: key, Type: zapcore.NamespaceType}
+}
+
+// Stringer constructs a field with the given key and the output of the value's
+// String method. The Stringer's String method is called lazily.
+func Stringer(key string, val fmt.Stringer) Field {
+ return Field{Key: key, Type: zapcore.StringerType, Interface: val}
+}
+
+// Time constructs a Field with the given key and value. The encoder
+// controls how the time is serialized.
+func Time(key string, val time.Time) Field {
+ if val.Before(_minTimeInt64) || val.After(_maxTimeInt64) {
+ return Field{Key: key, Type: zapcore.TimeFullType, Interface: val}
+ }
+ return Field{Key: key, Type: zapcore.TimeType, Integer: val.UnixNano(), Interface: val.Location()}
+}
+
+// Timep constructs a field that carries a *time.Time. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Timep(key string, val *time.Time) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Time(key, *val)
+}
+
+// Stack constructs a field that stores a stacktrace of the current goroutine
+// under provided key. Keep in mind that taking a stacktrace is eager and
+// expensive (relatively speaking); this function both makes an allocation and
+// takes about two microseconds.
+func Stack(key string) Field {
+ return StackSkip(key, 1) // skip Stack
+}
+
+// StackSkip constructs a field similarly to Stack, but also skips the given
+// number of frames from the top of the stacktrace.
+func StackSkip(key string, skip int) Field {
+ // Returning the stacktrace as a string costs an allocation, but saves us
+ // from expanding the zapcore.Field union struct to include a byte slice. Since
+ // taking a stacktrace is already so expensive (~10us), the extra allocation
+ // is okay.
+ return String(key, stacktrace.Take(skip+1)) // skip StackSkip
+}
+
+// Duration constructs a field with the given key and value. The encoder
+// controls how the duration is serialized.
+func Duration(key string, val time.Duration) Field {
+ return Field{Key: key, Type: zapcore.DurationType, Integer: int64(val)}
+}
+
+// Durationp constructs a field that carries a *time.Duration. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Durationp(key string, val *time.Duration) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Duration(key, *val)
+}
+
+// Object constructs a field with the given key and ObjectMarshaler. It
+// provides a flexible, but still type-safe and efficient, way to add map- or
+// struct-like user-defined types to the logging context. The struct's
+// MarshalLogObject method is called lazily.
+func Object(key string, val zapcore.ObjectMarshaler) Field {
+ return Field{Key: key, Type: zapcore.ObjectMarshalerType, Interface: val}
+}
+
+// Inline constructs a Field that is similar to Object, but it
+// will add the elements of the provided ObjectMarshaler to the
+// current namespace.
+func Inline(val zapcore.ObjectMarshaler) Field {
+ return zapcore.Field{
+ Type: zapcore.InlineMarshalerType,
+ Interface: val,
+ }
+}
+
+// Dict constructs a field containing the provided key-value pairs.
+// It acts similar to [Object], but with the fields specified as arguments.
+func Dict(key string, val ...Field) Field {
+ return dictField(key, val)
+}
+
+// We need a function with the signature (string, T) for zap.Any.
+func dictField(key string, val []Field) Field {
+ return Object(key, dictObject(val))
+}
+
+type dictObject []Field
+
+func (d dictObject) MarshalLogObject(enc zapcore.ObjectEncoder) error {
+ for _, f := range d {
+ f.AddTo(enc)
+ }
+ return nil
+}
+
+// We discovered an issue where zap.Any can cause a performance degradation
+// when used in new goroutines.
+//
+// This happens because the compiler assigns 4.8kb (one zap.Field per arm of
+// switch statement) of stack space for zap.Any when it takes the form:
+//
+// switch v := v.(type) {
+// case string:
+// return String(key, v)
+// case int:
+// return Int(key, v)
+// // ...
+// default:
+// return Reflect(key, v)
+// }
+//
+// To avoid this, we use the type switch to assign a value to a single local variable
+// and then call a function on it.
+// The local variable is just a function reference so it doesn't allocate
+// when converted to an interface{}.
+//
+// A fair bit of experimentation went into this.
+// See also:
+//
+// - https://github.com/uber-go/zap/pull/1301
+// - https://github.com/uber-go/zap/pull/1303
+// - https://github.com/uber-go/zap/pull/1304
+// - https://github.com/uber-go/zap/pull/1305
+// - https://github.com/uber-go/zap/pull/1308
+type anyFieldC[T any] func(string, T) Field
+
+func (f anyFieldC[T]) Any(key string, val any) Field {
+ v, _ := val.(T)
+ // val is guaranteed to be a T, except when it's nil.
+ return f(key, v)
+}
+
+// Any takes a key and an arbitrary value and chooses the best way to represent
+// them as a field, falling back to a reflection-based approach only if
+// necessary.
+//
+// Since byte/uint8 and rune/int32 are aliases, Any can't differentiate between
+// them. To minimize surprises, []byte values are treated as binary blobs, byte
+// values are treated as uint8, and runes are always treated as integers.
+func Any(key string, value interface{}) Field {
+ var c interface{ Any(string, any) Field }
+
+ switch value.(type) {
+ case zapcore.ObjectMarshaler:
+ c = anyFieldC[zapcore.ObjectMarshaler](Object)
+ case zapcore.ArrayMarshaler:
+ c = anyFieldC[zapcore.ArrayMarshaler](Array)
+ case []Field:
+ c = anyFieldC[[]Field](dictField)
+ case bool:
+ c = anyFieldC[bool](Bool)
+ case *bool:
+ c = anyFieldC[*bool](Boolp)
+ case []bool:
+ c = anyFieldC[[]bool](Bools)
+ case complex128:
+ c = anyFieldC[complex128](Complex128)
+ case *complex128:
+ c = anyFieldC[*complex128](Complex128p)
+ case []complex128:
+ c = anyFieldC[[]complex128](Complex128s)
+ case complex64:
+ c = anyFieldC[complex64](Complex64)
+ case *complex64:
+ c = anyFieldC[*complex64](Complex64p)
+ case []complex64:
+ c = anyFieldC[[]complex64](Complex64s)
+ case float64:
+ c = anyFieldC[float64](Float64)
+ case *float64:
+ c = anyFieldC[*float64](Float64p)
+ case []float64:
+ c = anyFieldC[[]float64](Float64s)
+ case float32:
+ c = anyFieldC[float32](Float32)
+ case *float32:
+ c = anyFieldC[*float32](Float32p)
+ case []float32:
+ c = anyFieldC[[]float32](Float32s)
+ case int:
+ c = anyFieldC[int](Int)
+ case *int:
+ c = anyFieldC[*int](Intp)
+ case []int:
+ c = anyFieldC[[]int](Ints)
+ case int64:
+ c = anyFieldC[int64](Int64)
+ case *int64:
+ c = anyFieldC[*int64](Int64p)
+ case []int64:
+ c = anyFieldC[[]int64](Int64s)
+ case int32:
+ c = anyFieldC[int32](Int32)
+ case *int32:
+ c = anyFieldC[*int32](Int32p)
+ case []int32:
+ c = anyFieldC[[]int32](Int32s)
+ case int16:
+ c = anyFieldC[int16](Int16)
+ case *int16:
+ c = anyFieldC[*int16](Int16p)
+ case []int16:
+ c = anyFieldC[[]int16](Int16s)
+ case int8:
+ c = anyFieldC[int8](Int8)
+ case *int8:
+ c = anyFieldC[*int8](Int8p)
+ case []int8:
+ c = anyFieldC[[]int8](Int8s)
+ case string:
+ c = anyFieldC[string](String)
+ case *string:
+ c = anyFieldC[*string](Stringp)
+ case []string:
+ c = anyFieldC[[]string](Strings)
+ case uint:
+ c = anyFieldC[uint](Uint)
+ case *uint:
+ c = anyFieldC[*uint](Uintp)
+ case []uint:
+ c = anyFieldC[[]uint](Uints)
+ case uint64:
+ c = anyFieldC[uint64](Uint64)
+ case *uint64:
+ c = anyFieldC[*uint64](Uint64p)
+ case []uint64:
+ c = anyFieldC[[]uint64](Uint64s)
+ case uint32:
+ c = anyFieldC[uint32](Uint32)
+ case *uint32:
+ c = anyFieldC[*uint32](Uint32p)
+ case []uint32:
+ c = anyFieldC[[]uint32](Uint32s)
+ case uint16:
+ c = anyFieldC[uint16](Uint16)
+ case *uint16:
+ c = anyFieldC[*uint16](Uint16p)
+ case []uint16:
+ c = anyFieldC[[]uint16](Uint16s)
+ case uint8:
+ c = anyFieldC[uint8](Uint8)
+ case *uint8:
+ c = anyFieldC[*uint8](Uint8p)
+ case []byte:
+ c = anyFieldC[[]byte](Binary)
+ case uintptr:
+ c = anyFieldC[uintptr](Uintptr)
+ case *uintptr:
+ c = anyFieldC[*uintptr](Uintptrp)
+ case []uintptr:
+ c = anyFieldC[[]uintptr](Uintptrs)
+ case time.Time:
+ c = anyFieldC[time.Time](Time)
+ case *time.Time:
+ c = anyFieldC[*time.Time](Timep)
+ case []time.Time:
+ c = anyFieldC[[]time.Time](Times)
+ case time.Duration:
+ c = anyFieldC[time.Duration](Duration)
+ case *time.Duration:
+ c = anyFieldC[*time.Duration](Durationp)
+ case []time.Duration:
+ c = anyFieldC[[]time.Duration](Durations)
+ case error:
+ c = anyFieldC[error](NamedError)
+ case []error:
+ c = anyFieldC[[]error](Errors)
+ case fmt.Stringer:
+ c = anyFieldC[fmt.Stringer](Stringer)
+ default:
+ c = anyFieldC[any](Reflect)
+ }
+
+ return c.Any(key, value)
+}
diff --git a/vendor/go.uber.org/zap/field_test.go b/vendor/go.uber.org/zap/field_test.go
new file mode 100644
index 0000000000..f87f1592e4
--- /dev/null
+++ b/vendor/go.uber.org/zap/field_test.go
@@ -0,0 +1,316 @@
+// 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 zap
+
+import (
+ "math"
+ "net"
+ "regexp"
+ "sync"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "go.uber.org/zap/internal/stacktrace"
+ "go.uber.org/zap/zapcore"
+)
+
+type username string
+
+func (n username) MarshalLogObject(enc zapcore.ObjectEncoder) error {
+ enc.AddString("username", string(n))
+ return nil
+}
+
+func assertCanBeReused(t testing.TB, field Field) {
+ var wg sync.WaitGroup
+
+ for i := 0; i < 100; i++ {
+ enc := zapcore.NewMapObjectEncoder()
+
+ // Ensure using the field in multiple encoders in separate goroutines
+ // does not cause any races or panics.
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ assert.NotPanics(t, func() {
+ field.AddTo(enc)
+ }, "Reusing a field should not cause issues")
+ }()
+ }
+
+ wg.Wait()
+}
+
+func TestFieldConstructors(t *testing.T) {
+ // Interface types.
+ addr := net.ParseIP("1.2.3.4")
+ name := username("phil")
+ ints := []int{5, 6}
+
+ // Helpful values for use in constructing pointers to primitives below.
+ var (
+ boolVal = bool(true)
+ complex128Val = complex128(complex(0, 0))
+ complex64Val = complex64(complex(0, 0))
+ durationVal = time.Duration(time.Second)
+ float64Val = float64(1.0)
+ float32Val = float32(1.0)
+ intVal = int(1)
+ int64Val = int64(1)
+ int32Val = int32(1)
+ int16Val = int16(1)
+ int8Val = int8(1)
+ stringVal = string("hello")
+ timeVal = time.Unix(100000, 0)
+ uintVal = uint(1)
+ uint64Val = uint64(1)
+ uint32Val = uint32(1)
+ uint16Val = uint16(1)
+ uint8Val = uint8(1)
+ uintptrVal = uintptr(1)
+ nilErr error
+ )
+
+ tests := []struct {
+ name string
+ field Field
+ expect Field
+ }{
+ {"Skip", Field{Type: zapcore.SkipType}, Skip()},
+ {"Binary", Field{Key: "k", Type: zapcore.BinaryType, Interface: []byte("ab12")}, Binary("k", []byte("ab12"))},
+ {"Bool", Field{Key: "k", Type: zapcore.BoolType, Integer: 1}, Bool("k", true)},
+ {"Bool", Field{Key: "k", Type: zapcore.BoolType, Integer: 1}, Bool("k", true)},
+ {"ByteString", Field{Key: "k", Type: zapcore.ByteStringType, Interface: []byte("ab12")}, ByteString("k", []byte("ab12"))},
+ {"Complex128", Field{Key: "k", Type: zapcore.Complex128Type, Interface: 1 + 2i}, Complex128("k", 1+2i)},
+ {"Complex64", Field{Key: "k", Type: zapcore.Complex64Type, Interface: complex64(1 + 2i)}, Complex64("k", 1+2i)},
+ {"Duration", Field{Key: "k", Type: zapcore.DurationType, Integer: 1}, Duration("k", 1)},
+ {"Int", Field{Key: "k", Type: zapcore.Int64Type, Integer: 1}, Int("k", 1)},
+ {"Int64", Field{Key: "k", Type: zapcore.Int64Type, Integer: 1}, Int64("k", 1)},
+ {"Int32", Field{Key: "k", Type: zapcore.Int32Type, Integer: 1}, Int32("k", 1)},
+ {"Int16", Field{Key: "k", Type: zapcore.Int16Type, Integer: 1}, Int16("k", 1)},
+ {"Int8", Field{Key: "k", Type: zapcore.Int8Type, Integer: 1}, Int8("k", 1)},
+ {"String", Field{Key: "k", Type: zapcore.StringType, String: "foo"}, String("k", "foo")},
+ {"Time", Field{Key: "k", Type: zapcore.TimeType, Integer: 0, Interface: time.UTC}, Time("k", time.Unix(0, 0).In(time.UTC))},
+ {"Time", Field{Key: "k", Type: zapcore.TimeType, Integer: 1000, Interface: time.UTC}, Time("k", time.Unix(0, 1000).In(time.UTC))},
+ {"Time", Field{Key: "k", Type: zapcore.TimeType, Integer: math.MinInt64, Interface: time.UTC}, Time("k", time.Unix(0, math.MinInt64).In(time.UTC))},
+ {"Time", Field{Key: "k", Type: zapcore.TimeType, Integer: math.MaxInt64, Interface: time.UTC}, Time("k", time.Unix(0, math.MaxInt64).In(time.UTC))},
+ {"Time", Field{Key: "k", Type: zapcore.TimeFullType, Interface: time.Time{}}, Time("k", time.Time{})},
+ {"Time", Field{Key: "k", Type: zapcore.TimeFullType, Interface: time.Unix(math.MaxInt64, 0)}, Time("k", time.Unix(math.MaxInt64, 0))},
+ {"Uint", Field{Key: "k", Type: zapcore.Uint64Type, Integer: 1}, Uint("k", 1)},
+ {"Uint64", Field{Key: "k", Type: zapcore.Uint64Type, Integer: 1}, Uint64("k", 1)},
+ {"Uint32", Field{Key: "k", Type: zapcore.Uint32Type, Integer: 1}, Uint32("k", 1)},
+ {"Uint16", Field{Key: "k", Type: zapcore.Uint16Type, Integer: 1}, Uint16("k", 1)},
+ {"Uint8", Field{Key: "k", Type: zapcore.Uint8Type, Integer: 1}, Uint8("k", 1)},
+ {"Uintptr", Field{Key: "k", Type: zapcore.UintptrType, Integer: 10}, Uintptr("k", 0xa)},
+ {"Reflect", Field{Key: "k", Type: zapcore.ReflectType, Interface: ints}, Reflect("k", ints)},
+ {"Reflect", Field{Key: "k", Type: zapcore.ReflectType}, Reflect("k", nil)},
+ {"Stringer", Field{Key: "k", Type: zapcore.StringerType, Interface: addr}, Stringer("k", addr)},
+ {"Object", Field{Key: "k", Type: zapcore.ObjectMarshalerType, Interface: name}, Object("k", name)},
+ {"Inline", Field{Type: zapcore.InlineMarshalerType, Interface: name}, Inline(name)},
+ {"Any:ObjectMarshaler", Any("k", name), Object("k", name)},
+ {"Any:ArrayMarshaler", Any("k", bools([]bool{true})), Array("k", bools([]bool{true}))},
+ {"Any:Dict", Any("k", []Field{String("k", "v")}), Dict("k", String("k", "v"))},
+ {"Any:Stringer", Any("k", addr), Stringer("k", addr)},
+ {"Any:Bool", Any("k", true), Bool("k", true)},
+ {"Any:Bools", Any("k", []bool{true}), Bools("k", []bool{true})},
+ {"Any:Byte", Any("k", byte(1)), Uint8("k", 1)},
+ {"Any:Bytes", Any("k", []byte{1}), Binary("k", []byte{1})},
+ {"Any:Complex128", Any("k", 1+2i), Complex128("k", 1+2i)},
+ {"Any:Complex128s", Any("k", []complex128{1 + 2i}), Complex128s("k", []complex128{1 + 2i})},
+ {"Any:Complex64", Any("k", complex64(1+2i)), Complex64("k", 1+2i)},
+ {"Any:Complex64s", Any("k", []complex64{1 + 2i}), Complex64s("k", []complex64{1 + 2i})},
+ {"Any:Float64", Any("k", 3.14), Float64("k", 3.14)},
+ {"Any:Float64s", Any("k", []float64{3.14}), Float64s("k", []float64{3.14})},
+ {"Any:Float32", Any("k", float32(3.14)), Float32("k", 3.14)},
+ {"Any:Float32s", Any("k", []float32{3.14}), Float32s("k", []float32{3.14})},
+ {"Any:Int", Any("k", 1), Int("k", 1)},
+ {"Any:Ints", Any("k", []int{1}), Ints("k", []int{1})},
+ {"Any:Int64", Any("k", int64(1)), Int64("k", 1)},
+ {"Any:Int64s", Any("k", []int64{1}), Int64s("k", []int64{1})},
+ {"Any:Int32", Any("k", int32(1)), Int32("k", 1)},
+ {"Any:Int32s", Any("k", []int32{1}), Int32s("k", []int32{1})},
+ {"Any:Int16", Any("k", int16(1)), Int16("k", 1)},
+ {"Any:Int16s", Any("k", []int16{1}), Int16s("k", []int16{1})},
+ {"Any:Int8", Any("k", int8(1)), Int8("k", 1)},
+ {"Any:Int8s", Any("k", []int8{1}), Int8s("k", []int8{1})},
+ {"Any:Rune", Any("k", rune(1)), Int32("k", 1)},
+ {"Any:Runes", Any("k", []rune{1}), Int32s("k", []int32{1})},
+ {"Any:String", Any("k", "v"), String("k", "v")},
+ {"Any:Strings", Any("k", []string{"v"}), Strings("k", []string{"v"})},
+ {"Any:Uint", Any("k", uint(1)), Uint("k", 1)},
+ {"Any:Uints", Any("k", []uint{1}), Uints("k", []uint{1})},
+ {"Any:Uint64", Any("k", uint64(1)), Uint64("k", 1)},
+ {"Any:Uint64s", Any("k", []uint64{1}), Uint64s("k", []uint64{1})},
+ {"Any:Uint32", Any("k", uint32(1)), Uint32("k", 1)},
+ {"Any:Uint32s", Any("k", []uint32{1}), Uint32s("k", []uint32{1})},
+ {"Any:Uint16", Any("k", uint16(1)), Uint16("k", 1)},
+ {"Any:Uint16s", Any("k", []uint16{1}), Uint16s("k", []uint16{1})},
+ {"Any:Uint8", Any("k", uint8(1)), Uint8("k", 1)},
+ {"Any:Uint8s", Any("k", []uint8{1}), Binary("k", []uint8{1})},
+ {"Any:Uintptr", Any("k", uintptr(1)), Uintptr("k", 1)},
+ {"Any:Uintptrs", Any("k", []uintptr{1}), Uintptrs("k", []uintptr{1})},
+ {"Any:Time", Any("k", time.Unix(0, 0)), Time("k", time.Unix(0, 0))},
+ {"Any:TimeFullType", Any("k", time.Time{}), Time("k", time.Time{})},
+ {"Any:Times", Any("k", []time.Time{time.Unix(0, 0)}), Times("k", []time.Time{time.Unix(0, 0)})},
+ {"Any:Duration", Any("k", time.Second), Duration("k", time.Second)},
+ {"Any:Durations", Any("k", []time.Duration{time.Second}), Durations("k", []time.Duration{time.Second})},
+ {"Any:Fallback", Any("k", struct{}{}), Reflect("k", struct{}{})},
+ {"Ptr:Bool", Boolp("k", nil), nilField("k")},
+ {"Ptr:Bool", Boolp("k", &boolVal), Bool("k", boolVal)},
+ {"Any:PtrBool", Any("k", (*bool)(nil)), nilField("k")},
+ {"Any:PtrBool", Any("k", &boolVal), Bool("k", boolVal)},
+ {"Ptr:Complex128", Complex128p("k", nil), nilField("k")},
+ {"Ptr:Complex128", Complex128p("k", &complex128Val), Complex128("k", complex128Val)},
+ {"Any:PtrComplex128", Any("k", (*complex128)(nil)), nilField("k")},
+ {"Any:PtrComplex128", Any("k", &complex128Val), Complex128("k", complex128Val)},
+ {"Ptr:Complex64", Complex64p("k", nil), nilField("k")},
+ {"Ptr:Complex64", Complex64p("k", &complex64Val), Complex64("k", complex64Val)},
+ {"Any:PtrComplex64", Any("k", (*complex64)(nil)), nilField("k")},
+ {"Any:PtrComplex64", Any("k", &complex64Val), Complex64("k", complex64Val)},
+ {"Ptr:Duration", Durationp("k", nil), nilField("k")},
+ {"Ptr:Duration", Durationp("k", &durationVal), Duration("k", durationVal)},
+ {"Any:PtrDuration", Any("k", (*time.Duration)(nil)), nilField("k")},
+ {"Any:PtrDuration", Any("k", &durationVal), Duration("k", durationVal)},
+ {"Ptr:Float64", Float64p("k", nil), nilField("k")},
+ {"Ptr:Float64", Float64p("k", &float64Val), Float64("k", float64Val)},
+ {"Any:PtrFloat64", Any("k", (*float64)(nil)), nilField("k")},
+ {"Any:PtrFloat64", Any("k", &float64Val), Float64("k", float64Val)},
+ {"Ptr:Float32", Float32p("k", nil), nilField("k")},
+ {"Ptr:Float32", Float32p("k", &float32Val), Float32("k", float32Val)},
+ {"Any:PtrFloat32", Any("k", (*float32)(nil)), nilField("k")},
+ {"Any:PtrFloat32", Any("k", &float32Val), Float32("k", float32Val)},
+ {"Ptr:Int", Intp("k", nil), nilField("k")},
+ {"Ptr:Int", Intp("k", &intVal), Int("k", intVal)},
+ {"Any:PtrInt", Any("k", (*int)(nil)), nilField("k")},
+ {"Any:PtrInt", Any("k", &intVal), Int("k", intVal)},
+ {"Ptr:Int64", Int64p("k", nil), nilField("k")},
+ {"Ptr:Int64", Int64p("k", &int64Val), Int64("k", int64Val)},
+ {"Any:PtrInt64", Any("k", (*int64)(nil)), nilField("k")},
+ {"Any:PtrInt64", Any("k", &int64Val), Int64("k", int64Val)},
+ {"Ptr:Int32", Int32p("k", nil), nilField("k")},
+ {"Ptr:Int32", Int32p("k", &int32Val), Int32("k", int32Val)},
+ {"Any:PtrInt32", Any("k", (*int32)(nil)), nilField("k")},
+ {"Any:PtrInt32", Any("k", &int32Val), Int32("k", int32Val)},
+ {"Ptr:Int16", Int16p("k", nil), nilField("k")},
+ {"Ptr:Int16", Int16p("k", &int16Val), Int16("k", int16Val)},
+ {"Any:PtrInt16", Any("k", (*int16)(nil)), nilField("k")},
+ {"Any:PtrInt16", Any("k", &int16Val), Int16("k", int16Val)},
+ {"Ptr:Int8", Int8p("k", nil), nilField("k")},
+ {"Ptr:Int8", Int8p("k", &int8Val), Int8("k", int8Val)},
+ {"Any:PtrInt8", Any("k", (*int8)(nil)), nilField("k")},
+ {"Any:PtrInt8", Any("k", &int8Val), Int8("k", int8Val)},
+ {"Ptr:String", Stringp("k", nil), nilField("k")},
+ {"Ptr:String", Stringp("k", &stringVal), String("k", stringVal)},
+ {"Any:PtrString", Any("k", (*string)(nil)), nilField("k")},
+ {"Any:PtrString", Any("k", &stringVal), String("k", stringVal)},
+ {"Ptr:Time", Timep("k", nil), nilField("k")},
+ {"Ptr:Time", Timep("k", &timeVal), Time("k", timeVal)},
+ {"Any:PtrTime", Any("k", (*time.Time)(nil)), nilField("k")},
+ {"Any:PtrTime", Any("k", &timeVal), Time("k", timeVal)},
+ {"Any:PtrTimeFullType", Any("k", &time.Time{}), Time("k", time.Time{})},
+ {"Ptr:Uint", Uintp("k", nil), nilField("k")},
+ {"Ptr:Uint", Uintp("k", &uintVal), Uint("k", uintVal)},
+ {"Any:PtrUint", Any("k", (*uint)(nil)), nilField("k")},
+ {"Any:PtrUint", Any("k", &uintVal), Uint("k", uintVal)},
+ {"Ptr:Uint64", Uint64p("k", nil), nilField("k")},
+ {"Ptr:Uint64", Uint64p("k", &uint64Val), Uint64("k", uint64Val)},
+ {"Any:PtrUint64", Any("k", (*uint64)(nil)), nilField("k")},
+ {"Any:PtrUint64", Any("k", &uint64Val), Uint64("k", uint64Val)},
+ {"Ptr:Uint32", Uint32p("k", nil), nilField("k")},
+ {"Ptr:Uint32", Uint32p("k", &uint32Val), Uint32("k", uint32Val)},
+ {"Any:PtrUint32", Any("k", (*uint32)(nil)), nilField("k")},
+ {"Any:PtrUint32", Any("k", &uint32Val), Uint32("k", uint32Val)},
+ {"Ptr:Uint16", Uint16p("k", nil), nilField("k")},
+ {"Ptr:Uint16", Uint16p("k", &uint16Val), Uint16("k", uint16Val)},
+ {"Any:PtrUint16", Any("k", (*uint16)(nil)), nilField("k")},
+ {"Any:PtrUint16", Any("k", &uint16Val), Uint16("k", uint16Val)},
+ {"Ptr:Uint8", Uint8p("k", nil), nilField("k")},
+ {"Ptr:Uint8", Uint8p("k", &uint8Val), Uint8("k", uint8Val)},
+ {"Any:PtrUint8", Any("k", (*uint8)(nil)), nilField("k")},
+ {"Any:PtrUint8", Any("k", &uint8Val), Uint8("k", uint8Val)},
+ {"Ptr:Uintptr", Uintptrp("k", nil), nilField("k")},
+ {"Ptr:Uintptr", Uintptrp("k", &uintptrVal), Uintptr("k", uintptrVal)},
+ {"Any:PtrUintptr", Any("k", (*uintptr)(nil)), nilField("k")},
+ {"Any:PtrUintptr", Any("k", &uintptrVal), Uintptr("k", uintptrVal)},
+ {"Any:ErrorNil", Any("k", nilErr), nilField("k")},
+ {"Namespace", Namespace("k"), Field{Key: "k", Type: zapcore.NamespaceType}},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if !assert.Equal(t, tt.expect, tt.field, "Unexpected output from convenience field constructor") {
+ t.Logf("type expected: %T\nGot: %T", tt.expect.Interface, tt.field.Interface)
+ }
+ assertCanBeReused(t, tt.field)
+ })
+ }
+}
+
+func TestStackField(t *testing.T) {
+ f := Stack("stacktrace")
+ assert.Equal(t, "stacktrace", f.Key, "Unexpected field key.")
+ assert.Equal(t, zapcore.StringType, f.Type, "Unexpected field type.")
+ r := regexp.MustCompile(`field_test.go:(\d+)`)
+ assert.Equal(t, r.ReplaceAllString(stacktrace.Take(0), "field_test.go"), r.ReplaceAllString(f.String, "field_test.go"), "Unexpected stack trace")
+ assertCanBeReused(t, f)
+}
+
+func TestStackSkipField(t *testing.T) {
+ f := StackSkip("stacktrace", 0)
+ assert.Equal(t, "stacktrace", f.Key, "Unexpected field key.")
+ assert.Equal(t, zapcore.StringType, f.Type, "Unexpected field type.")
+ r := regexp.MustCompile(`field_test.go:(\d+)`)
+ assert.Equal(t, r.ReplaceAllString(stacktrace.Take(0), "field_test.go"), r.ReplaceAllString(f.String, "field_test.go"), f.String, "Unexpected stack trace")
+ assertCanBeReused(t, f)
+}
+
+func TestStackSkipFieldWithSkip(t *testing.T) {
+ f := StackSkip("stacktrace", 1)
+ assert.Equal(t, "stacktrace", f.Key, "Unexpected field key.")
+ assert.Equal(t, zapcore.StringType, f.Type, "Unexpected field type.")
+ assert.Equal(t, stacktrace.Take(1), f.String, "Unexpected stack trace")
+ assertCanBeReused(t, f)
+}
+
+func TestDict(t *testing.T) {
+ tests := []struct {
+ desc string
+ field Field
+ expected any
+ }{
+ {"empty", Dict(""), map[string]any{}},
+ {"single", Dict("", String("k", "v")), map[string]any{"k": "v"}},
+ {"multiple", Dict("", String("k", "v"), String("k2", "v2")), map[string]any{"k": "v", "k2": "v2"}},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ enc := zapcore.NewMapObjectEncoder()
+ tt.field.Key = "k"
+ tt.field.AddTo(enc)
+ assert.Equal(t, tt.expected, enc.Fields["k"], "unexpected map contents")
+ assert.Len(t, enc.Fields, 1, "found extra keys in map: %v", enc.Fields)
+
+ assertCanBeReused(t, tt.field)
+ })
+ }
+}
diff --git a/vendor/go.uber.org/zap/flag.go b/vendor/go.uber.org/zap/flag.go
new file mode 100644
index 0000000000..1312875072
--- /dev/null
+++ b/vendor/go.uber.org/zap/flag.go
@@ -0,0 +1,39 @@
+// 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 zap
+
+import (
+ "flag"
+
+ "go.uber.org/zap/zapcore"
+)
+
+// LevelFlag uses the standard library's flag.Var to declare a global flag
+// with the specified name, default, and usage guidance. The returned value is
+// a pointer to the value of the flag.
+//
+// If you don't want to use the flag package's global state, you can use any
+// non-nil *Level as a flag.Value with your own *flag.FlagSet.
+func LevelFlag(name string, defaultLevel zapcore.Level, usage string) *zapcore.Level {
+ lvl := defaultLevel
+ flag.Var(&lvl, name, usage)
+ return &lvl
+}
diff --git a/vendor/go.uber.org/zap/flag_test.go b/vendor/go.uber.org/zap/flag_test.go
new file mode 100644
index 0000000000..9ff5444d56
--- /dev/null
+++ b/vendor/go.uber.org/zap/flag_test.go
@@ -0,0 +1,103 @@
+// 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 zap
+
+import (
+ "flag"
+ "io"
+ "testing"
+
+ "go.uber.org/zap/zapcore"
+
+ "github.com/stretchr/testify/assert"
+)
+
+type flagTestCase struct {
+ args []string
+ wantLevel zapcore.Level
+ wantErr bool
+}
+
+func (tc flagTestCase) runImplicitSet(t testing.TB) {
+ origCommandLine := flag.CommandLine
+ flag.CommandLine = flag.NewFlagSet("test", flag.ContinueOnError)
+ flag.CommandLine.SetOutput(io.Discard)
+ defer func() { flag.CommandLine = origCommandLine }()
+
+ level := LevelFlag("level", InfoLevel, "")
+ tc.run(t, flag.CommandLine, level)
+}
+
+func (tc flagTestCase) runExplicitSet(t testing.TB) {
+ var lvl zapcore.Level
+ set := flag.NewFlagSet("test", flag.ContinueOnError)
+ set.SetOutput(io.Discard)
+ set.Var(&lvl, "level", "minimum enabled logging level")
+ tc.run(t, set, &lvl)
+}
+
+func (tc flagTestCase) run(t testing.TB, set *flag.FlagSet, actual *zapcore.Level) {
+ err := set.Parse(tc.args)
+ if tc.wantErr {
+ assert.Error(t, err, "Parse(%v) should fail.", tc.args)
+ return
+ }
+ if assert.NoError(t, err, "Parse(%v) should succeed.", tc.args) {
+ assert.Equal(t, tc.wantLevel, *actual, "Level mismatch.")
+ }
+}
+
+func TestLevelFlag(t *testing.T) {
+ tests := []flagTestCase{
+ {
+ args: nil,
+ wantLevel: zapcore.InfoLevel,
+ },
+ {
+ args: []string{"--level", "unknown"},
+ wantErr: true,
+ },
+ {
+ args: []string{"--level", "error"},
+ wantLevel: zapcore.ErrorLevel,
+ },
+ }
+
+ for _, tt := range tests {
+ tt.runExplicitSet(t)
+ tt.runImplicitSet(t)
+ }
+}
+
+func TestLevelFlagsAreIndependent(t *testing.T) {
+ origCommandLine := flag.CommandLine
+ flag.CommandLine = flag.NewFlagSet("test", flag.ContinueOnError)
+ flag.CommandLine.SetOutput(io.Discard)
+ defer func() { flag.CommandLine = origCommandLine }()
+
+ // Make sure that these two flags are independent.
+ fileLevel := LevelFlag("file-level", InfoLevel, "")
+ consoleLevel := LevelFlag("console-level", InfoLevel, "")
+
+ assert.NoError(t, flag.CommandLine.Parse([]string{"-file-level", "debug"}), "Unexpected flag-parsing error.")
+ assert.Equal(t, InfoLevel, *consoleLevel, "Expected file logging level to remain unchanged.")
+ assert.Equal(t, DebugLevel, *fileLevel, "Expected console logging level to have changed.")
+}
diff --git a/vendor/go.uber.org/zap/global.go b/vendor/go.uber.org/zap/global.go
new file mode 100644
index 0000000000..3cb46c9e0a
--- /dev/null
+++ b/vendor/go.uber.org/zap/global.go
@@ -0,0 +1,169 @@
+// 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 zap
+
+import (
+ "bytes"
+ "fmt"
+ "log"
+ "os"
+ "sync"
+
+ "go.uber.org/zap/zapcore"
+)
+
+const (
+ _stdLogDefaultDepth = 1
+ _loggerWriterDepth = 2
+ _programmerErrorTemplate = "You've found a bug in zap! Please file a bug at " +
+ "https://github.com/uber-go/zap/issues/new and reference this error: %v"
+)
+
+var (
+ _globalMu sync.RWMutex
+ _globalL = NewNop()
+ _globalS = _globalL.Sugar()
+)
+
+// L returns the global Logger, which can be reconfigured with ReplaceGlobals.
+// It's safe for concurrent use.
+func L() *Logger {
+ _globalMu.RLock()
+ l := _globalL
+ _globalMu.RUnlock()
+ return l
+}
+
+// S returns the global SugaredLogger, which can be reconfigured with
+// ReplaceGlobals. It's safe for concurrent use.
+func S() *SugaredLogger {
+ _globalMu.RLock()
+ s := _globalS
+ _globalMu.RUnlock()
+ return s
+}
+
+// ReplaceGlobals replaces the global Logger and SugaredLogger, and returns a
+// function to restore the original values. It's safe for concurrent use.
+func ReplaceGlobals(logger *Logger) func() {
+ _globalMu.Lock()
+ prev := _globalL
+ _globalL = logger
+ _globalS = logger.Sugar()
+ _globalMu.Unlock()
+ return func() { ReplaceGlobals(prev) }
+}
+
+// NewStdLog returns a *log.Logger which writes to the supplied zap Logger at
+// InfoLevel. To redirect the standard library's package-global logging
+// functions, use RedirectStdLog instead.
+func NewStdLog(l *Logger) *log.Logger {
+ logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth))
+ f := logger.Info
+ return log.New(&loggerWriter{f}, "" /* prefix */, 0 /* flags */)
+}
+
+// NewStdLogAt returns *log.Logger which writes to supplied zap logger at
+// required level.
+func NewStdLogAt(l *Logger, level zapcore.Level) (*log.Logger, error) {
+ logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth))
+ logFunc, err := levelToFunc(logger, level)
+ if err != nil {
+ return nil, err
+ }
+ return log.New(&loggerWriter{logFunc}, "" /* prefix */, 0 /* flags */), nil
+}
+
+// RedirectStdLog redirects output from the standard library's package-global
+// logger to the supplied logger at InfoLevel. Since zap already handles caller
+// annotations, timestamps, etc., it automatically disables the standard
+// library's annotations and prefixing.
+//
+// It returns a function to restore the original prefix and flags and reset the
+// standard library's output to os.Stderr.
+func RedirectStdLog(l *Logger) func() {
+ f, err := redirectStdLogAt(l, InfoLevel)
+ if err != nil {
+ // Can't get here, since passing InfoLevel to redirectStdLogAt always
+ // works.
+ panic(fmt.Sprintf(_programmerErrorTemplate, err))
+ }
+ return f
+}
+
+// RedirectStdLogAt redirects output from the standard library's package-global
+// logger to the supplied logger at the specified level. Since zap already
+// handles caller annotations, timestamps, etc., it automatically disables the
+// standard library's annotations and prefixing.
+//
+// It returns a function to restore the original prefix and flags and reset the
+// standard library's output to os.Stderr.
+func RedirectStdLogAt(l *Logger, level zapcore.Level) (func(), error) {
+ return redirectStdLogAt(l, level)
+}
+
+func redirectStdLogAt(l *Logger, level zapcore.Level) (func(), error) {
+ flags := log.Flags()
+ prefix := log.Prefix()
+ log.SetFlags(0)
+ log.SetPrefix("")
+ logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth))
+ logFunc, err := levelToFunc(logger, level)
+ if err != nil {
+ return nil, err
+ }
+ log.SetOutput(&loggerWriter{logFunc})
+ return func() {
+ log.SetFlags(flags)
+ log.SetPrefix(prefix)
+ log.SetOutput(os.Stderr)
+ }, nil
+}
+
+func levelToFunc(logger *Logger, lvl zapcore.Level) (func(string, ...Field), error) {
+ switch lvl {
+ case DebugLevel:
+ return logger.Debug, nil
+ case InfoLevel:
+ return logger.Info, nil
+ case WarnLevel:
+ return logger.Warn, nil
+ case ErrorLevel:
+ return logger.Error, nil
+ case DPanicLevel:
+ return logger.DPanic, nil
+ case PanicLevel:
+ return logger.Panic, nil
+ case FatalLevel:
+ return logger.Fatal, nil
+ }
+ return nil, fmt.Errorf("unrecognized level: %q", lvl)
+}
+
+type loggerWriter struct {
+ logFunc func(msg string, fields ...Field)
+}
+
+func (l *loggerWriter) Write(p []byte) (int, error) {
+ p = bytes.TrimSpace(p)
+ l.logFunc(string(p))
+ return len(p), nil
+}
diff --git a/vendor/go.uber.org/zap/global_test.go b/vendor/go.uber.org/zap/global_test.go
new file mode 100644
index 0000000000..17fa225e62
--- /dev/null
+++ b/vendor/go.uber.org/zap/global_test.go
@@ -0,0 +1,281 @@
+// 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 zap
+
+import (
+ "log"
+ "sync"
+ "sync/atomic"
+ "testing"
+ "time"
+
+ "go.uber.org/zap/internal/exit"
+ "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 TestReplaceGlobals(t *testing.T) {
+ initialL := *L()
+ initialS := *S()
+
+ withLogger(t, DebugLevel, nil, func(l *Logger, logs *observer.ObservedLogs) {
+ L().Info("no-op")
+ S().Info("no-op")
+ assert.Equal(t, 0, logs.Len(), "Expected initial logs to go to default no-op global.")
+
+ defer ReplaceGlobals(l)()
+
+ L().Info("captured")
+ S().Info("captured")
+ expected := observer.LoggedEntry{
+ Entry: zapcore.Entry{Message: "captured"},
+ Context: []Field{},
+ }
+ assert.Equal(
+ t,
+ []observer.LoggedEntry{expected, expected},
+ logs.AllUntimed(),
+ "Unexpected global log output.",
+ )
+ })
+
+ assert.Equal(t, initialL, *L(), "Expected func returned from ReplaceGlobals to restore initial L.")
+ assert.Equal(t, initialS, *S(), "Expected func returned from ReplaceGlobals to restore initial S.")
+}
+
+func TestGlobalsConcurrentUse(t *testing.T) {
+ var (
+ stop atomic.Bool
+ wg sync.WaitGroup
+ )
+
+ for i := 0; i < 100; i++ {
+ wg.Add(2)
+ go func() {
+ for !stop.Load() {
+ ReplaceGlobals(NewNop())
+ }
+ wg.Done()
+ }()
+ go func() {
+ for !stop.Load() {
+ L().With(Int("foo", 42)).Named("main").WithOptions(Development()).Info("")
+ S().Info("")
+ }
+ wg.Done()
+ }()
+ }
+
+ ztest.Sleep(100 * time.Millisecond)
+ // CAS loop to toggle the current value.
+ for old := stop.Load(); !stop.CompareAndSwap(old, !old); {
+ old = stop.Load()
+ }
+ wg.Wait()
+}
+
+func TestNewStdLog(t *testing.T) {
+ withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {
+ std := NewStdLog(l)
+ std.Print("redirected")
+ checkStdLogMessage(t, "redirected", logs)
+ })
+}
+
+func TestNewStdLogAt(t *testing.T) {
+ // include DPanicLevel here, but do not include Development in options
+ levels := []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel}
+ for _, level := range levels {
+ withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {
+ std, err := NewStdLogAt(l, level)
+ require.NoError(t, err, "Unexpected error.")
+ std.Print("redirected")
+ checkStdLogMessage(t, "redirected", logs)
+ })
+ }
+}
+
+func TestNewStdLogAtPanics(t *testing.T) {
+ // include DPanicLevel here and enable Development in options
+ levels := []zapcore.Level{DPanicLevel, PanicLevel}
+ for _, level := range levels {
+ withLogger(t, DebugLevel, []Option{AddCaller(), Development()}, func(l *Logger, logs *observer.ObservedLogs) {
+ std, err := NewStdLogAt(l, level)
+ require.NoError(t, err, "Unexpected error")
+ assert.Panics(t, func() { std.Print("redirected") }, "Expected log to panic.")
+ checkStdLogMessage(t, "redirected", logs)
+ })
+ }
+}
+
+func TestNewStdLogAtFatal(t *testing.T) {
+ withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {
+ stub := exit.WithStub(func() {
+ std, err := NewStdLogAt(l, FatalLevel)
+ require.NoError(t, err, "Unexpected error.")
+ std.Print("redirected")
+ checkStdLogMessage(t, "redirected", logs)
+ })
+ assert.True(t, true, stub.Exited, "Expected Fatal logger call to terminate process.")
+ stub.Unstub()
+ })
+}
+
+func TestNewStdLogAtInvalid(t *testing.T) {
+ _, err := NewStdLogAt(NewNop(), zapcore.Level(99))
+ assert.ErrorContains(t, err, "99", "Expected level code in error message")
+}
+
+func TestRedirectStdLog(t *testing.T) {
+ initialFlags := log.Flags()
+ initialPrefix := log.Prefix()
+
+ withLogger(t, DebugLevel, nil, func(l *Logger, logs *observer.ObservedLogs) {
+ defer RedirectStdLog(l)()
+ log.Print("redirected")
+
+ assert.Equal(t, []observer.LoggedEntry{{
+ Entry: zapcore.Entry{Message: "redirected"},
+ Context: []Field{},
+ }}, logs.AllUntimed(), "Unexpected global log output.")
+ })
+
+ assert.Equal(t, initialFlags, log.Flags(), "Expected to reset initial flags.")
+ assert.Equal(t, initialPrefix, log.Prefix(), "Expected to reset initial prefix.")
+}
+
+func TestRedirectStdLogCaller(t *testing.T) {
+ withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {
+ defer RedirectStdLog(l)()
+ log.Print("redirected")
+ entries := logs.All()
+ require.Len(t, entries, 1, "Unexpected number of logs.")
+ assert.Contains(t, entries[0].Caller.File, "global_test.go", "Unexpected caller annotation.")
+ })
+}
+
+func TestRedirectStdLogAt(t *testing.T) {
+ initialFlags := log.Flags()
+ initialPrefix := log.Prefix()
+
+ // include DPanicLevel here, but do not include Development in options
+ levels := []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel}
+ for _, level := range levels {
+ withLogger(t, DebugLevel, nil, func(l *Logger, logs *observer.ObservedLogs) {
+ restore, err := RedirectStdLogAt(l, level)
+ require.NoError(t, err, "Unexpected error.")
+ defer restore()
+ log.Print("redirected")
+
+ assert.Equal(t, []observer.LoggedEntry{{
+ Entry: zapcore.Entry{Level: level, Message: "redirected"},
+ Context: []Field{},
+ }}, logs.AllUntimed(), "Unexpected global log output.")
+ })
+ }
+
+ assert.Equal(t, initialFlags, log.Flags(), "Expected to reset initial flags.")
+ assert.Equal(t, initialPrefix, log.Prefix(), "Expected to reset initial prefix.")
+}
+
+func TestRedirectStdLogAtCaller(t *testing.T) {
+ // include DPanicLevel here, but do not include Development in options
+ levels := []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel}
+ for _, level := range levels {
+ withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {
+ restore, err := RedirectStdLogAt(l, level)
+ require.NoError(t, err, "Unexpected error.")
+ defer restore()
+ log.Print("redirected")
+ entries := logs.All()
+ require.Len(t, entries, 1, "Unexpected number of logs.")
+ assert.Contains(t, entries[0].Caller.File, "global_test.go", "Unexpected caller annotation.")
+ })
+ }
+}
+
+func TestRedirectStdLogAtPanics(t *testing.T) {
+ initialFlags := log.Flags()
+ initialPrefix := log.Prefix()
+
+ // include DPanicLevel here and enable Development in options
+ levels := []zapcore.Level{DPanicLevel, PanicLevel}
+ for _, level := range levels {
+ withLogger(t, DebugLevel, []Option{AddCaller(), Development()}, func(l *Logger, logs *observer.ObservedLogs) {
+ restore, err := RedirectStdLogAt(l, level)
+ require.NoError(t, err, "Unexpected error.")
+ defer restore()
+ assert.Panics(t, func() { log.Print("redirected") }, "Expected log to panic.")
+ checkStdLogMessage(t, "redirected", logs)
+ })
+ }
+
+ assert.Equal(t, initialFlags, log.Flags(), "Expected to reset initial flags.")
+ assert.Equal(t, initialPrefix, log.Prefix(), "Expected to reset initial prefix.")
+}
+
+func TestRedirectStdLogAtFatal(t *testing.T) {
+ initialFlags := log.Flags()
+ initialPrefix := log.Prefix()
+
+ withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {
+ stub := exit.WithStub(func() {
+ restore, err := RedirectStdLogAt(l, FatalLevel)
+ require.NoError(t, err, "Unexpected error.")
+ defer restore()
+ log.Print("redirected")
+ checkStdLogMessage(t, "redirected", logs)
+ })
+ assert.True(t, true, stub.Exited, "Expected Fatal logger call to terminate process.")
+ stub.Unstub()
+ })
+
+ assert.Equal(t, initialFlags, log.Flags(), "Expected to reset initial flags.")
+ assert.Equal(t, initialPrefix, log.Prefix(), "Expected to reset initial prefix.")
+}
+
+func TestRedirectStdLogAtInvalid(t *testing.T) {
+ restore, err := RedirectStdLogAt(NewNop(), zapcore.Level(99))
+ defer func() {
+ if restore != nil {
+ restore()
+ }
+ }()
+ assert.ErrorContains(t, err, "99", "Expected level code in error message")
+}
+
+func checkStdLogMessage(t *testing.T, msg string, logs *observer.ObservedLogs) {
+ require.Equal(t, 1, logs.Len(), "Expected exactly one entry to be logged")
+ entry := logs.AllUntimed()[0]
+ assert.Equal(t, []Field{}, entry.Context, "Unexpected entry context.")
+ assert.Equal(t, "redirected", entry.Message, "Unexpected entry message.")
+ assert.Regexp(
+ t,
+ `/global_test.go:\d+$`,
+ entry.Caller.String(),
+ "Unexpected caller annotation.",
+ )
+}
diff --git a/vendor/go.uber.org/zap/gotest/ya.make b/vendor/go.uber.org/zap/gotest/ya.make
new file mode 100644
index 0000000000..7e4cd8d0f4
--- /dev/null
+++ b/vendor/go.uber.org/zap/gotest/ya.make
@@ -0,0 +1,5 @@
+GO_TEST_FOR(vendor/go.uber.org/zap)
+
+LICENSE(MIT)
+
+END()
diff --git a/vendor/go.uber.org/zap/http_handler.go b/vendor/go.uber.org/zap/http_handler.go
new file mode 100644
index 0000000000..2be8f65150
--- /dev/null
+++ b/vendor/go.uber.org/zap/http_handler.go
@@ -0,0 +1,140 @@
+// 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 zap
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+
+ "go.uber.org/zap/zapcore"
+)
+
+// ServeHTTP is a simple JSON endpoint that can report on or change the current
+// logging level.
+//
+// # GET
+//
+// The GET request returns a JSON description of the current logging level like:
+//
+// {"level":"info"}
+//
+// # PUT
+//
+// The PUT request changes the logging level. It is perfectly safe to change the
+// logging level while a program is running. Two content types are supported:
+//
+// Content-Type: application/x-www-form-urlencoded
+//
+// With this content type, the level can be provided through the request body or
+// a query parameter. The log level is URL encoded like:
+//
+// level=debug
+//
+// The request body takes precedence over the query parameter, if both are
+// specified.
+//
+// This content type is the default for a curl PUT request. Following are two
+// example curl requests that both set the logging level to debug.
+//
+// curl -X PUT localhost:8080/log/level?level=debug
+// curl -X PUT localhost:8080/log/level -d level=debug
+//
+// For any other content type, the payload is expected to be JSON encoded and
+// look like:
+//
+// {"level":"info"}
+//
+// An example curl request could look like this:
+//
+// curl -X PUT localhost:8080/log/level -H "Content-Type: application/json" -d '{"level":"debug"}'
+func (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ if err := lvl.serveHTTP(w, r); err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ fmt.Fprintf(w, "internal error: %v", err)
+ }
+}
+
+func (lvl AtomicLevel) serveHTTP(w http.ResponseWriter, r *http.Request) error {
+ type errorResponse struct {
+ Error string `json:"error"`
+ }
+ type payload struct {
+ Level zapcore.Level `json:"level"`
+ }
+
+ enc := json.NewEncoder(w)
+
+ switch r.Method {
+ case http.MethodGet:
+ return enc.Encode(payload{Level: lvl.Level()})
+
+ case http.MethodPut:
+ requestedLvl, err := decodePutRequest(r.Header.Get("Content-Type"), r)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ return enc.Encode(errorResponse{Error: err.Error()})
+ }
+ lvl.SetLevel(requestedLvl)
+ return enc.Encode(payload{Level: lvl.Level()})
+
+ default:
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ return enc.Encode(errorResponse{
+ Error: "Only GET and PUT are supported.",
+ })
+ }
+}
+
+// Decodes incoming PUT requests and returns the requested logging level.
+func decodePutRequest(contentType string, r *http.Request) (zapcore.Level, error) {
+ if contentType == "application/x-www-form-urlencoded" {
+ return decodePutURL(r)
+ }
+ return decodePutJSON(r.Body)
+}
+
+func decodePutURL(r *http.Request) (zapcore.Level, error) {
+ lvl := r.FormValue("level")
+ if lvl == "" {
+ return 0, errors.New("must specify logging level")
+ }
+ var l zapcore.Level
+ if err := l.UnmarshalText([]byte(lvl)); err != nil {
+ return 0, err
+ }
+ return l, nil
+}
+
+func decodePutJSON(body io.Reader) (zapcore.Level, error) {
+ var pld struct {
+ Level *zapcore.Level `json:"level"`
+ }
+ if err := json.NewDecoder(body).Decode(&pld); err != nil {
+ return 0, fmt.Errorf("malformed request body: %v", err)
+ }
+ if pld.Level == nil {
+ return 0, errors.New("must specify logging level")
+ }
+ return *pld.Level, nil
+}
diff --git a/vendor/go.uber.org/zap/http_handler_test.go b/vendor/go.uber.org/zap/http_handler_test.go
new file mode 100644
index 0000000000..9da3dc7b52
--- /dev/null
+++ b/vendor/go.uber.org/zap/http_handler_test.go
@@ -0,0 +1,217 @@
+// 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 zap_test
+
+import (
+ "encoding/json"
+ "errors"
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestAtomicLevelServeHTTP(t *testing.T) {
+ tests := []struct {
+ desc string
+ method string
+ query string
+ contentType string
+ body string
+ expectedCode int
+ expectedLevel zapcore.Level
+ }{
+ {
+ desc: "GET",
+ method: http.MethodGet,
+ expectedCode: http.StatusOK,
+ expectedLevel: zap.InfoLevel,
+ },
+ {
+ desc: "PUT JSON",
+ method: http.MethodPut,
+ expectedCode: http.StatusOK,
+ expectedLevel: zap.WarnLevel,
+ body: `{"level":"warn"}`,
+ },
+ {
+ desc: "PUT URL encoded",
+ method: http.MethodPut,
+ expectedCode: http.StatusOK,
+ expectedLevel: zap.WarnLevel,
+ contentType: "application/x-www-form-urlencoded",
+ body: "level=warn",
+ },
+ {
+ desc: "PUT query parameters",
+ method: http.MethodPut,
+ query: "?level=warn",
+ expectedCode: http.StatusOK,
+ expectedLevel: zap.WarnLevel,
+ contentType: "application/x-www-form-urlencoded",
+ },
+ {
+ desc: "body takes precedence over query",
+ method: http.MethodPut,
+ query: "?level=info",
+ expectedCode: http.StatusOK,
+ expectedLevel: zap.WarnLevel,
+ contentType: "application/x-www-form-urlencoded",
+ body: "level=warn",
+ },
+ {
+ desc: "JSON ignores query",
+ method: http.MethodPut,
+ query: "?level=info",
+ expectedCode: http.StatusOK,
+ expectedLevel: zap.WarnLevel,
+ body: `{"level":"warn"}`,
+ },
+ {
+ desc: "PUT JSON unrecognized",
+ method: http.MethodPut,
+ expectedCode: http.StatusBadRequest,
+ body: `{"level":"unrecognized"}`,
+ },
+ {
+ desc: "PUT URL encoded unrecognized",
+ method: http.MethodPut,
+ expectedCode: http.StatusBadRequest,
+ contentType: "application/x-www-form-urlencoded",
+ body: "level=unrecognized",
+ },
+ {
+ desc: "PUT JSON malformed",
+ method: http.MethodPut,
+ expectedCode: http.StatusBadRequest,
+ body: `{"level":"warn`,
+ },
+ {
+ desc: "PUT URL encoded malformed",
+ method: http.MethodPut,
+ query: "?level=%",
+ expectedCode: http.StatusBadRequest,
+ contentType: "application/x-www-form-urlencoded",
+ },
+ {
+ desc: "PUT Query parameters malformed",
+ method: http.MethodPut,
+ expectedCode: http.StatusBadRequest,
+ contentType: "application/x-www-form-urlencoded",
+ body: "level=%",
+ },
+ {
+ desc: "PUT JSON unspecified",
+ method: http.MethodPut,
+ expectedCode: http.StatusBadRequest,
+ body: `{}`,
+ },
+ {
+ desc: "PUT URL encoded unspecified",
+ method: http.MethodPut,
+ expectedCode: http.StatusBadRequest,
+ contentType: "application/x-www-form-urlencoded",
+ body: "",
+ },
+ {
+ desc: "POST JSON",
+ method: http.MethodPost,
+ expectedCode: http.StatusMethodNotAllowed,
+ body: `{"level":"warn"}`,
+ },
+ {
+ desc: "POST URL",
+ method: http.MethodPost,
+ expectedCode: http.StatusMethodNotAllowed,
+ contentType: "application/x-www-form-urlencoded",
+ body: "level=warn",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ lvl := zap.NewAtomicLevel()
+ lvl.SetLevel(zapcore.InfoLevel)
+
+ server := httptest.NewServer(lvl)
+ defer server.Close()
+
+ req, err := http.NewRequest(tt.method, server.URL+tt.query, strings.NewReader(tt.body))
+ require.NoError(t, err, "Error constructing %s request.", req.Method)
+ if tt.contentType != "" {
+ req.Header.Set("Content-Type", tt.contentType)
+ }
+
+ res, err := http.DefaultClient.Do(req)
+ require.NoError(t, err, "Error making %s request.", req.Method)
+ defer func() {
+ assert.NoError(t, res.Body.Close(), "Error closing response body.")
+ }()
+
+ require.Equal(t, tt.expectedCode, res.StatusCode, "Unexpected status code.")
+ if tt.expectedCode != http.StatusOK {
+ // Don't need to test exact error message, but one should be present.
+ var pld struct {
+ Error string `json:"error"`
+ }
+ require.NoError(t, json.NewDecoder(res.Body).Decode(&pld), "Decoding response body")
+ assert.NotEmpty(t, pld.Error, "Expected an error message")
+ return
+ }
+
+ var pld struct {
+ Level zapcore.Level `json:"level"`
+ }
+ require.NoError(t, json.NewDecoder(res.Body).Decode(&pld), "Decoding response body")
+ assert.Equal(t, tt.expectedLevel, pld.Level, "Unexpected logging level returned")
+ })
+ }
+}
+
+func TestAtomicLevelServeHTTPBrokenWriter(t *testing.T) {
+ t.Parallel()
+
+ lvl := zap.NewAtomicLevel()
+
+ request, err := http.NewRequest(http.MethodGet, "http://localhost:1234/log/level", nil)
+ require.NoError(t, err, "Error constructing request.")
+
+ recorder := httptest.NewRecorder()
+ lvl.ServeHTTP(&brokenHTTPResponseWriter{
+ ResponseWriter: recorder,
+ }, request)
+
+ assert.Equal(t, http.StatusInternalServerError, recorder.Code, "Unexpected status code.")
+}
+
+type brokenHTTPResponseWriter struct {
+ http.ResponseWriter
+}
+
+func (w *brokenHTTPResponseWriter) Write([]byte) (int, error) {
+ return 0, errors.New("great sadness")
+}
diff --git a/vendor/go.uber.org/zap/increase_level_test.go b/vendor/go.uber.org/zap/increase_level_test.go
new file mode 100644
index 0000000000..2d883807e9
--- /dev/null
+++ b/vendor/go.uber.org/zap/increase_level_test.go
@@ -0,0 +1,94 @@
+// 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 zap
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "go.uber.org/zap/zapcore"
+ "go.uber.org/zap/zaptest/observer"
+)
+
+func newLoggedEntry(level zapcore.Level, msg string, fields ...zapcore.Field) observer.LoggedEntry {
+ if len(fields) == 0 {
+ fields = []zapcore.Field{}
+ }
+ return observer.LoggedEntry{
+ Entry: zapcore.Entry{Level: level, Message: msg},
+ Context: fields,
+ }
+}
+
+func TestIncreaseLevelTryDecrease(t *testing.T) {
+ errorOut := &bytes.Buffer{}
+ opts := []Option{
+ ErrorOutput(zapcore.AddSync(errorOut)),
+ }
+ withLogger(t, WarnLevel, opts, func(logger *Logger, logs *observer.ObservedLogs) {
+ logger.Warn("original warn log")
+
+ debugLogger := logger.WithOptions(IncreaseLevel(DebugLevel))
+ debugLogger.Debug("ignored debug log")
+ debugLogger.Warn("increase level warn log")
+ debugLogger.Error("increase level error log")
+
+ assert.Equal(t, []observer.LoggedEntry{
+ newLoggedEntry(WarnLevel, "original warn log"),
+ newLoggedEntry(WarnLevel, "increase level warn log"),
+ newLoggedEntry(ErrorLevel, "increase level error log"),
+ }, logs.AllUntimed(), "unexpected logs")
+ assert.Equal(t,
+ "failed to IncreaseLevel: invalid increase level, as level \"info\" is allowed by increased level, but not by existing core\n",
+ errorOut.String(),
+ "unexpected error output",
+ )
+ })
+}
+
+func TestIncreaseLevel(t *testing.T) {
+ errorOut := &bytes.Buffer{}
+ opts := []Option{
+ ErrorOutput(zapcore.AddSync(errorOut)),
+ }
+ withLogger(t, WarnLevel, opts, func(logger *Logger, logs *observer.ObservedLogs) {
+ logger.Warn("original warn log")
+
+ errorLogger := logger.WithOptions(IncreaseLevel(ErrorLevel))
+ errorLogger.Debug("ignored debug log")
+ errorLogger.Warn("ignored warn log")
+ errorLogger.Error("increase level error log")
+
+ withFields := errorLogger.With(String("k", "v"))
+ withFields.Debug("ignored debug log with fields")
+ withFields.Warn("ignored warn log with fields")
+ withFields.Error("increase level error log with fields")
+
+ assert.Equal(t, []observer.LoggedEntry{
+ newLoggedEntry(WarnLevel, "original warn log"),
+ newLoggedEntry(ErrorLevel, "increase level error log"),
+ newLoggedEntry(ErrorLevel, "increase level error log with fields", String("k", "v")),
+ }, logs.AllUntimed(), "unexpected logs")
+
+ assert.Empty(t, errorOut.String(), "expect no error output")
+ })
+}
diff --git a/vendor/go.uber.org/zap/internal/bufferpool/bufferpool.go b/vendor/go.uber.org/zap/internal/bufferpool/bufferpool.go
new file mode 100644
index 0000000000..dad583aaa5
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/bufferpool/bufferpool.go
@@ -0,0 +1,31 @@
+// 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 bufferpool houses zap's shared internal buffer pool. Third-party
+// packages can recreate the same functionality with buffers.NewPool.
+package bufferpool
+
+import "go.uber.org/zap/buffer"
+
+var (
+ _pool = buffer.NewPool()
+ // Get retrieves a buffer from the pool, creating one if necessary.
+ Get = _pool.Get
+)
diff --git a/vendor/go.uber.org/zap/internal/bufferpool/ya.make b/vendor/go.uber.org/zap/internal/bufferpool/ya.make
new file mode 100644
index 0000000000..1ab106c924
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/bufferpool/ya.make
@@ -0,0 +1,9 @@
+GO_LIBRARY()
+
+LICENSE(MIT)
+
+SRCS(
+ bufferpool.go
+)
+
+END()
diff --git a/vendor/go.uber.org/zap/internal/color/color.go b/vendor/go.uber.org/zap/internal/color/color.go
new file mode 100644
index 0000000000..c4d5d02abc
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/color/color.go
@@ -0,0 +1,44 @@
+// 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 color adds coloring functionality for TTY output.
+package color
+
+import "fmt"
+
+// Foreground colors.
+const (
+ Black Color = iota + 30
+ Red
+ Green
+ Yellow
+ Blue
+ Magenta
+ Cyan
+ White
+)
+
+// Color represents a text color.
+type Color uint8
+
+// Add adds the coloring to the given string.
+func (c Color) Add(s string) string {
+ return fmt.Sprintf("\x1b[%dm%s\x1b[0m", uint8(c), s)
+}
diff --git a/vendor/go.uber.org/zap/internal/color/color_test.go b/vendor/go.uber.org/zap/internal/color/color_test.go
new file mode 100644
index 0000000000..4982903aa1
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/color/color_test.go
@@ -0,0 +1,36 @@
+// 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 color
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestColorFormatting(t *testing.T) {
+ assert.Equal(
+ t,
+ "\x1b[31mfoo\x1b[0m",
+ Red.Add("foo"),
+ "Unexpected colored output.",
+ )
+}
diff --git a/vendor/go.uber.org/zap/internal/color/gotest/ya.make b/vendor/go.uber.org/zap/internal/color/gotest/ya.make
new file mode 100644
index 0000000000..32cf4e2849
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/color/gotest/ya.make
@@ -0,0 +1,5 @@
+GO_TEST_FOR(vendor/go.uber.org/zap/internal/color)
+
+LICENSE(MIT)
+
+END()
diff --git a/vendor/go.uber.org/zap/internal/color/ya.make b/vendor/go.uber.org/zap/internal/color/ya.make
new file mode 100644
index 0000000000..3e9fbfcd47
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/color/ya.make
@@ -0,0 +1,15 @@
+GO_LIBRARY()
+
+LICENSE(MIT)
+
+SRCS(
+ color.go
+)
+
+GO_TEST_SRCS(color_test.go)
+
+END()
+
+RECURSE(
+ gotest
+)
diff --git a/vendor/go.uber.org/zap/internal/exit/exit.go b/vendor/go.uber.org/zap/internal/exit/exit.go
new file mode 100644
index 0000000000..f673f9947b
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/exit/exit.go
@@ -0,0 +1,66 @@
+// 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 exit provides stubs so that unit tests can exercise code that calls
+// os.Exit(1).
+package exit
+
+import "os"
+
+var _exit = os.Exit
+
+// With terminates the process by calling os.Exit(code). If the package is
+// stubbed, it instead records a call in the testing spy.
+func With(code int) {
+ _exit(code)
+}
+
+// A StubbedExit is a testing fake for os.Exit.
+type StubbedExit struct {
+ Exited bool
+ Code int
+ prev func(code int)
+}
+
+// Stub substitutes a fake for the call to os.Exit(1).
+func Stub() *StubbedExit {
+ s := &StubbedExit{prev: _exit}
+ _exit = s.exit
+ return s
+}
+
+// WithStub runs the supplied function with Exit stubbed. It returns the stub
+// used, so that users can test whether the process would have crashed.
+func WithStub(f func()) *StubbedExit {
+ s := Stub()
+ defer s.Unstub()
+ f()
+ return s
+}
+
+// Unstub restores the previous exit function.
+func (se *StubbedExit) Unstub() {
+ _exit = se.prev
+}
+
+func (se *StubbedExit) exit(code int) {
+ se.Exited = true
+ se.Code = code
+}
diff --git a/vendor/go.uber.org/zap/internal/exit/exit_test.go b/vendor/go.uber.org/zap/internal/exit/exit_test.go
new file mode 100644
index 0000000000..2299584722
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/exit/exit_test.go
@@ -0,0 +1,48 @@
+// 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 exit_test
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "go.uber.org/zap/internal/exit"
+)
+
+func TestStub(t *testing.T) {
+ type want struct {
+ exit bool
+ code int
+ }
+ tests := []struct {
+ f func()
+ want want
+ }{
+ {func() { exit.With(42) }, want{exit: true, code: 42}},
+ {func() {}, want{}},
+ }
+
+ for _, tt := range tests {
+ s := exit.WithStub(tt.f)
+ assert.Equal(t, tt.want.exit, s.Exited, "Stub captured unexpected exit value.")
+ assert.Equal(t, tt.want.code, s.Code, "Stub captured unexpected exit value.")
+ }
+}
diff --git a/vendor/go.uber.org/zap/internal/exit/gotest/ya.make b/vendor/go.uber.org/zap/internal/exit/gotest/ya.make
new file mode 100644
index 0000000000..c5e8098769
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/exit/gotest/ya.make
@@ -0,0 +1,5 @@
+GO_TEST_FOR(vendor/go.uber.org/zap/internal/exit)
+
+LICENSE(MIT)
+
+END()
diff --git a/vendor/go.uber.org/zap/internal/exit/ya.make b/vendor/go.uber.org/zap/internal/exit/ya.make
new file mode 100644
index 0000000000..73d0dd8e21
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/exit/ya.make
@@ -0,0 +1,15 @@
+GO_LIBRARY()
+
+LICENSE(MIT)
+
+SRCS(
+ exit.go
+)
+
+GO_XTEST_SRCS(exit_test.go)
+
+END()
+
+RECURSE(
+ gotest
+)
diff --git a/vendor/go.uber.org/zap/internal/level_enabler.go b/vendor/go.uber.org/zap/internal/level_enabler.go
new file mode 100644
index 0000000000..40bfed81e6
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/level_enabler.go
@@ -0,0 +1,37 @@
+// Copyright (c) 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 internal and its subpackages hold types and functionality
+// that are not part of Zap's public API.
+package internal
+
+import "go.uber.org/zap/zapcore"
+
+// LeveledEnabler is an interface satisfied by LevelEnablers that are able to
+// report their own level.
+//
+// This interface is defined to use more conveniently in tests and non-zapcore
+// packages.
+// This cannot be imported from zapcore because of the cyclic dependency.
+type LeveledEnabler interface {
+ zapcore.LevelEnabler
+
+ Level() zapcore.Level
+}
diff --git a/vendor/go.uber.org/zap/internal/pool/gotest/ya.make b/vendor/go.uber.org/zap/internal/pool/gotest/ya.make
new file mode 100644
index 0000000000..8e1e7117ab
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/pool/gotest/ya.make
@@ -0,0 +1,5 @@
+GO_TEST_FOR(vendor/go.uber.org/zap/internal/pool)
+
+LICENSE(MIT)
+
+END()
diff --git a/vendor/go.uber.org/zap/internal/pool/pool.go b/vendor/go.uber.org/zap/internal/pool/pool.go
new file mode 100644
index 0000000000..60e9d2c432
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/pool/pool.go
@@ -0,0 +1,58 @@
+// Copyright (c) 2023 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 pool provides internal pool utilities.
+package pool
+
+import (
+ "sync"
+)
+
+// A Pool is a generic wrapper around [sync.Pool] to provide strongly-typed
+// object pooling.
+//
+// Note that SA6002 (ref: https://staticcheck.io/docs/checks/#SA6002) will
+// not be detected, so all internal pool use must take care to only store
+// pointer types.
+type Pool[T any] struct {
+ pool sync.Pool
+}
+
+// New returns a new [Pool] for T, and will use fn to construct new Ts when
+// the pool is empty.
+func New[T any](fn func() T) *Pool[T] {
+ return &Pool[T]{
+ pool: sync.Pool{
+ New: func() any {
+ return fn()
+ },
+ },
+ }
+}
+
+// Get gets a T from the pool, or creates a new one if the pool is empty.
+func (p *Pool[T]) Get() T {
+ return p.pool.Get().(T)
+}
+
+// Put returns x into the pool.
+func (p *Pool[T]) Put(x T) {
+ p.pool.Put(x)
+}
diff --git a/vendor/go.uber.org/zap/internal/pool/pool_test.go b/vendor/go.uber.org/zap/internal/pool/pool_test.go
new file mode 100644
index 0000000000..094edf9172
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/pool/pool_test.go
@@ -0,0 +1,106 @@
+// Copyright (c) 2023 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 pool_test
+
+import (
+ "runtime/debug"
+ "sync"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "go.uber.org/zap/internal/pool"
+)
+
+type pooledValue[T any] struct {
+ value T
+}
+
+func TestNew(t *testing.T) {
+ // Disable GC to avoid the victim cache during the test.
+ defer debug.SetGCPercent(debug.SetGCPercent(-1))
+
+ p := pool.New(func() *pooledValue[string] {
+ return &pooledValue[string]{
+ value: "new",
+ }
+ })
+
+ // Probabilistically, 75% of sync.Pool.Put calls will succeed when -race
+ // is enabled (see ref below); attempt to make this quasi-deterministic by
+ // brute force (i.e., put significantly more objects in the pool than we
+ // will need for the test) in order to avoid testing without race enabled.
+ //
+ // ref: https://cs.opensource.google/go/go/+/refs/tags/go1.20.2:src/sync/pool.go;l=100-103
+ for i := 0; i < 1_000; i++ {
+ p.Put(&pooledValue[string]{
+ value: t.Name(),
+ })
+ }
+
+ // Ensure that we always get the expected value. Note that this must only
+ // run a fraction of the number of times that Put is called above.
+ for i := 0; i < 10; i++ {
+ func() {
+ x := p.Get()
+ defer p.Put(x)
+ require.Equal(t, t.Name(), x.value)
+ }()
+ }
+
+ // Depool all objects that might be in the pool to ensure that it's empty.
+ for i := 0; i < 1_000; i++ {
+ p.Get()
+ }
+
+ // Now that the pool is empty, it should use the value specified in the
+ // underlying sync.Pool.New func.
+ require.Equal(t, "new", p.Get().value)
+}
+
+func TestNew_Race(t *testing.T) {
+ p := pool.New(func() *pooledValue[int] {
+ return &pooledValue[int]{
+ value: -1,
+ }
+ })
+
+ var wg sync.WaitGroup
+ defer wg.Wait()
+
+ // Run a number of goroutines that read and write pool object fields to
+ // tease out races.
+ for i := 0; i < 1_000; i++ {
+ i := i
+
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+
+ x := p.Get()
+ defer p.Put(x)
+
+ // Must both read and write the field.
+ if n := x.value; n >= -1 {
+ x.value = i
+ }
+ }()
+ }
+}
diff --git a/vendor/go.uber.org/zap/internal/pool/ya.make b/vendor/go.uber.org/zap/internal/pool/ya.make
new file mode 100644
index 0000000000..cd2e0b4ab2
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/pool/ya.make
@@ -0,0 +1,15 @@
+GO_LIBRARY()
+
+LICENSE(MIT)
+
+SRCS(
+ pool.go
+)
+
+GO_XTEST_SRCS(pool_test.go)
+
+END()
+
+RECURSE(
+ gotest
+)
diff --git a/vendor/go.uber.org/zap/internal/readme/readme.go b/vendor/go.uber.org/zap/internal/readme/readme.go
new file mode 100644
index 0000000000..148765908b
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/readme/readme.go
@@ -0,0 +1,244 @@
+// 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.
+
+// readme generates Zap's README from a template.
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "os/exec"
+ "sort"
+ "strconv"
+ "strings"
+ "text/template"
+ "time"
+)
+
+var (
+ libraryNameToMarkdownName = map[string]string{
+ "Zap": ":zap: zap",
+ "Zap.Sugar": ":zap: zap (sugared)",
+ "stdlib.Println": "standard library",
+ "sirupsen/logrus": "logrus",
+ "go-kit/kit/log": "go-kit",
+ "inconshreveable/log15": "log15",
+ "apex/log": "apex/log",
+ "rs/zerolog": "zerolog",
+ "slog": "slog",
+ }
+)
+
+func main() {
+ flag.Parse()
+ if err := do(); err != nil {
+ log.Fatal(err)
+ }
+}
+
+func do() error {
+ tmplData, err := getTmplData()
+ if err != nil {
+ return err
+ }
+ data, err := io.ReadAll(os.Stdin)
+ if err != nil {
+ return err
+ }
+ t, err := template.New("tmpl").Parse(string(data))
+ if err != nil {
+ return err
+ }
+ return t.Execute(os.Stdout, tmplData)
+}
+
+func getTmplData() (*tmplData, error) {
+ tmplData := &tmplData{}
+ rows, err := getBenchmarkRows("BenchmarkAddingFields")
+ if err != nil {
+ return nil, err
+ }
+ tmplData.BenchmarkAddingFields = rows
+ rows, err = getBenchmarkRows("BenchmarkAccumulatedContext")
+ if err != nil {
+ return nil, err
+ }
+ tmplData.BenchmarkAccumulatedContext = rows
+ rows, err = getBenchmarkRows("BenchmarkWithoutFields")
+ if err != nil {
+ return nil, err
+ }
+ tmplData.BenchmarkWithoutFields = rows
+ return tmplData, nil
+}
+
+func getBenchmarkRows(benchmarkName string) (string, error) {
+ benchmarkOutput, err := getBenchmarkOutput(benchmarkName)
+ if err != nil {
+ return "", err
+ }
+
+ // get the Zap time (unsugared) as baseline to compare with other loggers
+ baseline, err := getBenchmarkRow(benchmarkOutput, benchmarkName, "Zap", nil)
+ if err != nil {
+ return "", err
+ }
+
+ var benchmarkRows []*benchmarkRow
+ for libraryName := range libraryNameToMarkdownName {
+ benchmarkRow, err := getBenchmarkRow(
+ benchmarkOutput, benchmarkName, libraryName, baseline,
+ )
+ if err != nil {
+ return "", err
+ }
+ if benchmarkRow == nil {
+ continue
+ }
+ benchmarkRows = append(benchmarkRows, benchmarkRow)
+ }
+ sort.Sort(benchmarkRowsByTime(benchmarkRows))
+ rows := []string{
+ "| Package | Time | Time % to zap | Objects Allocated |",
+ "| :------ | :--: | :-----------: | :---------------: |",
+ }
+ for _, benchmarkRow := range benchmarkRows {
+ rows = append(rows, benchmarkRow.String())
+ }
+ return strings.Join(rows, "\n"), nil
+}
+
+func getBenchmarkRow(
+ input []string, benchmarkName string, libraryName string, baseline *benchmarkRow,
+) (*benchmarkRow, error) {
+ line, err := findUniqueSubstring(input, fmt.Sprintf("%s/%s-", benchmarkName, libraryName))
+ if err != nil {
+ return nil, err
+ }
+ if line == "" {
+ return nil, nil
+ }
+ split := strings.Split(line, "\t")
+ if len(split) < 5 {
+ return nil, fmt.Errorf("unknown benchmark line: %s", line)
+ }
+ duration, err := time.ParseDuration(strings.ReplaceAll(strings.TrimSuffix(strings.TrimSpace(split[2]), "/op"), " ", ""))
+ if err != nil {
+ return nil, err
+ }
+ allocatedBytes, err := strconv.Atoi(strings.TrimSuffix(strings.TrimSpace(split[3]), " B/op"))
+ if err != nil {
+ return nil, err
+ }
+ allocatedObjects, err := strconv.Atoi(strings.TrimSuffix(strings.TrimSpace(split[4]), " allocs/op"))
+ if err != nil {
+ return nil, err
+ }
+ r := &benchmarkRow{
+ Name: libraryNameToMarkdownName[libraryName],
+ Time: duration,
+ AllocatedBytes: allocatedBytes,
+ AllocatedObjects: allocatedObjects,
+ }
+
+ if baseline != nil {
+ r.ZapTime = baseline.Time
+ r.ZapAllocatedBytes = baseline.AllocatedBytes
+ r.ZapAllocatedObjects = baseline.AllocatedObjects
+ }
+
+ return r, nil
+}
+
+func findUniqueSubstring(input []string, substring string) (string, error) {
+ var output string
+ for _, line := range input {
+ if strings.Contains(line, substring) {
+ if output != "" {
+ return "", fmt.Errorf("input has duplicate substring %s", substring)
+ }
+ output = line
+ }
+ }
+ return output, nil
+}
+
+func getBenchmarkOutput(benchmarkName string) ([]string, error) {
+ cmd := exec.Command("go", "test", fmt.Sprintf("-bench=%s", benchmarkName), "-benchmem")
+ cmd.Dir = "benchmarks"
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ return nil, fmt.Errorf("error running 'go test -bench=%q': %v\n%s", benchmarkName, err, string(output))
+ }
+ return strings.Split(string(output), "\n"), nil
+}
+
+type tmplData struct {
+ BenchmarkAddingFields string
+ BenchmarkAccumulatedContext string
+ BenchmarkWithoutFields string
+}
+
+type benchmarkRow struct {
+ Name string
+
+ Time time.Duration
+ AllocatedBytes int
+ AllocatedObjects int
+
+ ZapTime time.Duration
+ ZapAllocatedBytes int
+ ZapAllocatedObjects int
+}
+
+func (b *benchmarkRow) String() string {
+ pct := func(val, baseline int64) string {
+ return fmt.Sprintf(
+ "%+0.f%%",
+ ((float64(val)/float64(baseline))*100)-100,
+ )
+ }
+ t := b.Time.Nanoseconds()
+ tp := pct(t, b.ZapTime.Nanoseconds())
+
+ return fmt.Sprintf(
+ "| %s | %d ns/op | %s | %d allocs/op", b.Name,
+ t, tp, b.AllocatedObjects,
+ )
+}
+
+type benchmarkRowsByTime []*benchmarkRow
+
+func (b benchmarkRowsByTime) Len() int { return len(b) }
+func (b benchmarkRowsByTime) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
+func (b benchmarkRowsByTime) Less(i, j int) bool {
+ left, right := b[i], b[j]
+ leftZap, rightZap := strings.Contains(left.Name, "zap"), strings.Contains(right.Name, "zap")
+
+ // If neither benchmark is for zap or both are, sort by time.
+ if leftZap == rightZap {
+ return left.Time.Nanoseconds() < right.Time.Nanoseconds()
+ }
+ // Sort zap benchmark first.
+ return leftZap
+}
diff --git a/vendor/go.uber.org/zap/internal/readme/ya.make b/vendor/go.uber.org/zap/internal/readme/ya.make
new file mode 100644
index 0000000000..69bb063cfb
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/readme/ya.make
@@ -0,0 +1,9 @@
+GO_PROGRAM()
+
+LICENSE(MIT)
+
+SRCS(
+ readme.go
+)
+
+END()
diff --git a/vendor/go.uber.org/zap/internal/stacktrace/gotest/ya.make b/vendor/go.uber.org/zap/internal/stacktrace/gotest/ya.make
new file mode 100644
index 0000000000..e5045319ca
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/stacktrace/gotest/ya.make
@@ -0,0 +1,5 @@
+GO_TEST_FOR(vendor/go.uber.org/zap/internal/stacktrace)
+
+LICENSE(MIT)
+
+END()
diff --git a/vendor/go.uber.org/zap/internal/stacktrace/stack.go b/vendor/go.uber.org/zap/internal/stacktrace/stack.go
new file mode 100644
index 0000000000..82af7551f9
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/stacktrace/stack.go
@@ -0,0 +1,181 @@
+// Copyright (c) 2023 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 stacktrace provides support for gathering stack traces
+// efficiently.
+package stacktrace
+
+import (
+ "runtime"
+
+ "go.uber.org/zap/buffer"
+ "go.uber.org/zap/internal/bufferpool"
+ "go.uber.org/zap/internal/pool"
+)
+
+var _stackPool = pool.New(func() *Stack {
+ return &Stack{
+ storage: make([]uintptr, 64),
+ }
+})
+
+// Stack is a captured stack trace.
+type Stack struct {
+ pcs []uintptr // program counters; always a subslice of storage
+ frames *runtime.Frames
+
+ // The size of pcs varies depending on requirements:
+ // it will be one if the only the first frame was requested,
+ // and otherwise it will reflect the depth of the call stack.
+ //
+ // storage decouples the slice we need (pcs) from the slice we pool.
+ // We will always allocate a reasonably large storage, but we'll use
+ // only as much of it as we need.
+ storage []uintptr
+}
+
+// Depth specifies how deep of a stack trace should be captured.
+type Depth int
+
+const (
+ // First captures only the first frame.
+ First Depth = iota
+
+ // Full captures the entire call stack, allocating more
+ // storage for it if needed.
+ Full
+)
+
+// Capture captures a stack trace of the specified depth, skipping
+// the provided number of frames. skip=0 identifies the caller of
+// Capture.
+//
+// The caller must call Free on the returned stacktrace after using it.
+func Capture(skip int, depth Depth) *Stack {
+ stack := _stackPool.Get()
+
+ switch depth {
+ case First:
+ stack.pcs = stack.storage[:1]
+ case Full:
+ stack.pcs = stack.storage
+ }
+
+ // Unlike other "skip"-based APIs, skip=0 identifies runtime.Callers
+ // itself. +2 to skip captureStacktrace and runtime.Callers.
+ numFrames := runtime.Callers(
+ skip+2,
+ stack.pcs,
+ )
+
+ // runtime.Callers truncates the recorded stacktrace if there is no
+ // room in the provided slice. For the full stack trace, keep expanding
+ // storage until there are fewer frames than there is room.
+ if depth == Full {
+ pcs := stack.pcs
+ for numFrames == len(pcs) {
+ pcs = make([]uintptr, len(pcs)*2)
+ numFrames = runtime.Callers(skip+2, pcs)
+ }
+
+ // Discard old storage instead of returning it to the pool.
+ // This will adjust the pool size over time if stack traces are
+ // consistently very deep.
+ stack.storage = pcs
+ stack.pcs = pcs[:numFrames]
+ } else {
+ stack.pcs = stack.pcs[:numFrames]
+ }
+
+ stack.frames = runtime.CallersFrames(stack.pcs)
+ return stack
+}
+
+// Free releases resources associated with this stacktrace
+// and returns it back to the pool.
+func (st *Stack) Free() {
+ st.frames = nil
+ st.pcs = nil
+ _stackPool.Put(st)
+}
+
+// Count reports the total number of frames in this stacktrace.
+// Count DOES NOT change as Next is called.
+func (st *Stack) Count() int {
+ return len(st.pcs)
+}
+
+// Next returns the next frame in the stack trace,
+// and a boolean indicating whether there are more after it.
+func (st *Stack) Next() (_ runtime.Frame, more bool) {
+ return st.frames.Next()
+}
+
+// Take returns a string representation of the current stacktrace.
+//
+// skip is the number of frames to skip before recording the stack trace.
+// skip=0 identifies the caller of Take.
+func Take(skip int) string {
+ stack := Capture(skip+1, Full)
+ defer stack.Free()
+
+ buffer := bufferpool.Get()
+ defer buffer.Free()
+
+ stackfmt := NewFormatter(buffer)
+ stackfmt.FormatStack(stack)
+ return buffer.String()
+}
+
+// Formatter formats a stack trace into a readable string representation.
+type Formatter struct {
+ b *buffer.Buffer
+ nonEmpty bool // whehther we've written at least one frame already
+}
+
+// NewFormatter builds a new Formatter.
+func NewFormatter(b *buffer.Buffer) Formatter {
+ return Formatter{b: b}
+}
+
+// FormatStack formats all remaining frames in the provided stacktrace -- minus
+// the final runtime.main/runtime.goexit frame.
+func (sf *Formatter) FormatStack(stack *Stack) {
+ // Note: On the last iteration, frames.Next() returns false, with a valid
+ // frame, but we ignore this frame. The last frame is a runtime frame which
+ // adds noise, since it's only either runtime.main or runtime.goexit.
+ for frame, more := stack.Next(); more; frame, more = stack.Next() {
+ sf.FormatFrame(frame)
+ }
+}
+
+// FormatFrame formats the given frame.
+func (sf *Formatter) FormatFrame(frame runtime.Frame) {
+ if sf.nonEmpty {
+ sf.b.AppendByte('\n')
+ }
+ sf.nonEmpty = true
+ sf.b.AppendString(frame.Function)
+ sf.b.AppendByte('\n')
+ sf.b.AppendByte('\t')
+ sf.b.AppendString(frame.File)
+ sf.b.AppendByte(':')
+ sf.b.AppendInt(int64(frame.Line))
+}
diff --git a/vendor/go.uber.org/zap/internal/stacktrace/stack_test.go b/vendor/go.uber.org/zap/internal/stacktrace/stack_test.go
new file mode 100644
index 0000000000..195eeaeaee
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/stacktrace/stack_test.go
@@ -0,0 +1,106 @@
+// Copyright (c) 2023 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 stacktrace
+
+import (
+ "bytes"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestTake(t *testing.T) {
+ trace := Take(0)
+ lines := strings.Split(trace, "\n")
+ require.NotEmpty(t, lines, "Expected stacktrace to have at least one frame.")
+ assert.Contains(
+ t,
+ lines[0],
+ "go.uber.org/zap/internal/stacktrace.TestTake",
+ "Expected stacktrace to start with the test.",
+ )
+}
+
+func TestTakeWithSkip(t *testing.T) {
+ trace := Take(1)
+ lines := strings.Split(trace, "\n")
+ require.NotEmpty(t, lines, "Expected stacktrace to have at least one frame.")
+ assert.Contains(
+ t,
+ lines[0],
+ "testing.",
+ "Expected stacktrace to start with the test runner (skipping our own frame).",
+ )
+}
+
+func TestTakeWithSkipInnerFunc(t *testing.T) {
+ var trace string
+ func() {
+ trace = Take(2)
+ }()
+ lines := strings.Split(trace, "\n")
+ require.NotEmpty(t, lines, "Expected stacktrace to have at least one frame.")
+ assert.Contains(
+ t,
+ lines[0],
+ "testing.",
+ "Expected stacktrace to start with the test function (skipping the test function).",
+ )
+}
+
+func TestTakeDeepStack(t *testing.T) {
+ const (
+ N = 500
+ withStackDepthName = "go.uber.org/zap/internal/stacktrace.withStackDepth"
+ )
+ withStackDepth(N, func() {
+ trace := Take(0)
+ for found := 0; found < N; found++ {
+ i := strings.Index(trace, withStackDepthName)
+ if i < 0 {
+ t.Fatalf(`expected %v occurrences of %q, found %d`,
+ N, withStackDepthName, found)
+ }
+ trace = trace[i+len(withStackDepthName):]
+ }
+ })
+}
+
+func BenchmarkTake(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Take(0)
+ }
+}
+
+func withStackDepth(depth int, f func()) {
+ var recurse func(rune) rune
+ recurse = func(r rune) rune {
+ if r > 0 {
+ bytes.Map(recurse, []byte(string([]rune{r - 1})))
+ } else {
+ f()
+ }
+ return 0
+ }
+ recurse(rune(depth))
+}
diff --git a/vendor/go.uber.org/zap/internal/stacktrace/ya.make b/vendor/go.uber.org/zap/internal/stacktrace/ya.make
new file mode 100644
index 0000000000..de4f0672f0
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/stacktrace/ya.make
@@ -0,0 +1,15 @@
+GO_LIBRARY()
+
+LICENSE(MIT)
+
+SRCS(
+ stack.go
+)
+
+GO_TEST_SRCS(stack_test.go)
+
+END()
+
+RECURSE(
+ gotest
+)
diff --git a/vendor/go.uber.org/zap/internal/ya.make b/vendor/go.uber.org/zap/internal/ya.make
new file mode 100644
index 0000000000..4515b67ad1
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/ya.make
@@ -0,0 +1,19 @@
+GO_LIBRARY()
+
+LICENSE(MIT)
+
+SRCS(
+ level_enabler.go
+)
+
+END()
+
+RECURSE(
+ bufferpool
+ color
+ exit
+ pool
+ readme
+ stacktrace
+ ztest
+)
diff --git a/vendor/go.uber.org/zap/internal/ztest/clock.go b/vendor/go.uber.org/zap/internal/ztest/clock.go
new file mode 100644
index 0000000000..47b0b7f965
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/ztest/clock.go
@@ -0,0 +1,153 @@
+// Copyright (c) 2023 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 ztest
+
+import (
+ "sort"
+ "sync"
+ "time"
+)
+
+// MockClock is a fake source of time.
+// It implements standard time operations,
+// but allows the user to control the passage of time.
+//
+// Use the [Add] method to progress time.
+type MockClock struct {
+ mu sync.RWMutex
+ now time.Time
+
+ // The MockClock works by maintaining a list of waiters.
+ // Each waiter knows the time at which it should be resolved.
+ // When the clock advances, all waiters that are in range are resolved
+ // in chronological order.
+ waiters []waiter
+}
+
+// NewMockClock builds a new mock clock
+// using the current actual time as the initial time.
+func NewMockClock() *MockClock {
+ return &MockClock{
+ now: time.Now(),
+ }
+}
+
+// Now reports the current time.
+func (c *MockClock) Now() time.Time {
+ c.mu.RLock()
+ defer c.mu.RUnlock()
+ return c.now
+}
+
+// NewTicker returns a time.Ticker that ticks at the specified frequency.
+//
+// As with [time.NewTicker],
+// the ticker will drop ticks if the receiver is slow,
+// and the channel is never closed.
+//
+// Calling Stop on the returned ticker is a no-op.
+// The ticker only runs when the clock is advanced.
+func (c *MockClock) NewTicker(d time.Duration) *time.Ticker {
+ ch := make(chan time.Time, 1)
+
+ var tick func(time.Time)
+ tick = func(now time.Time) {
+ next := now.Add(d)
+ c.runAt(next, func() {
+ defer tick(next)
+
+ select {
+ case ch <- next:
+ // ok
+ default:
+ // The receiver is slow.
+ // Drop the tick and continue.
+ }
+ })
+ }
+ tick(c.Now())
+
+ return &time.Ticker{C: ch}
+}
+
+// runAt schedules the given function to be run at the given time.
+// The function runs without a lock held, so it may schedule more work.
+func (c *MockClock) runAt(t time.Time, fn func()) {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ c.waiters = append(c.waiters, waiter{until: t, fn: fn})
+}
+
+type waiter struct {
+ until time.Time
+ fn func()
+}
+
+// Add progresses time by the given duration.
+// Other operations waiting for the time to advance
+// will be resolved if they are within range.
+//
+// Side effects of operations waiting for the time to advance
+// will take effect on a best-effort basis.
+// Avoid racing with operations that have side effects.
+//
+// Panics if the duration is negative.
+func (c *MockClock) Add(d time.Duration) {
+ if d < 0 {
+ panic("cannot add negative duration")
+ }
+
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ sort.Slice(c.waiters, func(i, j int) bool {
+ return c.waiters[i].until.Before(c.waiters[j].until)
+ })
+
+ newTime := c.now.Add(d)
+ // newTime won't be recorded until the end of this method.
+ // This ensures that any waiters that are resolved
+ // are resolved at the time they were expecting.
+
+ for len(c.waiters) > 0 {
+ w := c.waiters[0]
+ if w.until.After(newTime) {
+ break
+ }
+ c.waiters[0] = waiter{} // avoid memory leak
+ c.waiters = c.waiters[1:]
+
+ // The waiter is within range.
+ // Travel to the time of the waiter and resolve it.
+ c.now = w.until
+
+ // The waiter may schedule more work
+ // so we must release the lock.
+ c.mu.Unlock()
+ w.fn()
+ // Sleeping here is necessary to let the side effects of waiters
+ // take effect before we continue.
+ time.Sleep(1 * time.Millisecond)
+ c.mu.Lock()
+ }
+
+ c.now = newTime
+}
diff --git a/vendor/go.uber.org/zap/internal/ztest/clock_test.go b/vendor/go.uber.org/zap/internal/ztest/clock_test.go
new file mode 100644
index 0000000000..6db724bd97
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/ztest/clock_test.go
@@ -0,0 +1,80 @@
+// Copyright (c) 2023 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 ztest
+
+import (
+ "sync/atomic"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestMockClock_NewTicker(t *testing.T) {
+ var n atomic.Int32
+ clock := NewMockClock()
+
+ done := make(chan struct{})
+ defer func() { <-done }() // wait for end
+
+ quit := make(chan struct{})
+ // Create a channel to increment every microsecond.
+ go func(ticker *time.Ticker) {
+ defer close(done)
+ for {
+ select {
+ case <-quit:
+ ticker.Stop()
+ return
+ case <-ticker.C:
+ n.Add(1)
+ }
+ }
+ }(clock.NewTicker(time.Microsecond))
+
+ // Move clock forward.
+ clock.Add(2 * time.Microsecond)
+ assert.Equal(t, int32(2), n.Load())
+ close(quit)
+}
+
+func TestMockClock_NewTicker_slowConsumer(t *testing.T) {
+ clock := NewMockClock()
+
+ ticker := clock.NewTicker(time.Microsecond)
+ defer ticker.Stop()
+
+ // Two ticks, only one consumed.
+ clock.Add(2 * time.Microsecond)
+ <-ticker.C
+
+ select {
+ case <-ticker.C:
+ t.Fatal("unexpected tick")
+ default:
+ // ok
+ }
+}
+
+func TestMockClock_Add_negative(t *testing.T) {
+ clock := NewMockClock()
+ assert.Panics(t, func() { clock.Add(-1) })
+}
diff --git a/vendor/go.uber.org/zap/internal/ztest/doc.go b/vendor/go.uber.org/zap/internal/ztest/doc.go
new file mode 100644
index 0000000000..cd4b98cbcb
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/ztest/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 ztest provides low-level helpers for testing log output. These
+// utilities are helpful in zap's own unit tests, but any assertions using
+// them are strongly coupled to a single encoding.
+package ztest // import "go.uber.org/zap/internal/ztest"
diff --git a/vendor/go.uber.org/zap/internal/ztest/gotest/ya.make b/vendor/go.uber.org/zap/internal/ztest/gotest/ya.make
new file mode 100644
index 0000000000..708f864916
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/ztest/gotest/ya.make
@@ -0,0 +1,5 @@
+GO_TEST_FOR(vendor/go.uber.org/zap/internal/ztest)
+
+LICENSE(MIT)
+
+END()
diff --git a/vendor/go.uber.org/zap/internal/ztest/timeout.go b/vendor/go.uber.org/zap/internal/ztest/timeout.go
new file mode 100644
index 0000000000..e4222f9479
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/ztest/timeout.go
@@ -0,0 +1,59 @@
+// 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 ztest
+
+import (
+ "log"
+ "os"
+ "strconv"
+ "time"
+)
+
+var _timeoutScale = 1.0
+
+// Timeout scales the provided duration by $TEST_TIMEOUT_SCALE.
+func Timeout(base time.Duration) time.Duration {
+ return time.Duration(float64(base) * _timeoutScale)
+}
+
+// Sleep scales the sleep duration by $TEST_TIMEOUT_SCALE.
+func Sleep(base time.Duration) {
+ time.Sleep(Timeout(base))
+}
+
+// Initialize checks the environment and alters the timeout scale accordingly.
+// It returns a function to undo the scaling.
+func Initialize(factor string) func() {
+ fv, err := strconv.ParseFloat(factor, 64)
+ if err != nil {
+ panic(err)
+ }
+ original := _timeoutScale
+ _timeoutScale = fv
+ return func() { _timeoutScale = original }
+}
+
+func init() {
+ if v := os.Getenv("TEST_TIMEOUT_SCALE"); v != "" {
+ Initialize(v)
+ log.Printf("Scaling timeouts by %vx.\n", _timeoutScale)
+ }
+}
diff --git a/vendor/go.uber.org/zap/internal/ztest/writer.go b/vendor/go.uber.org/zap/internal/ztest/writer.go
new file mode 100644
index 0000000000..f54d8569ee
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/ztest/writer.go
@@ -0,0 +1,96 @@
+// 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 ztest
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "strings"
+)
+
+// A Syncer is a spy for the Sync portion of zapcore.WriteSyncer.
+type Syncer struct {
+ err error
+ called bool
+}
+
+// SetError sets the error that the Sync method will return.
+func (s *Syncer) SetError(err error) {
+ s.err = err
+}
+
+// Sync records that it was called, then returns the user-supplied error (if
+// any).
+func (s *Syncer) Sync() error {
+ s.called = true
+ return s.err
+}
+
+// Called reports whether the Sync method was called.
+func (s *Syncer) Called() bool {
+ return s.called
+}
+
+// A Discarder sends all writes to io.Discard.
+type Discarder struct{ Syncer }
+
+// Write implements io.Writer.
+func (d *Discarder) Write(b []byte) (int, error) {
+ return io.Discard.Write(b)
+}
+
+// FailWriter is a WriteSyncer that always returns an error on writes.
+type FailWriter struct{ Syncer }
+
+// Write implements io.Writer.
+func (w FailWriter) Write(b []byte) (int, error) {
+ return len(b), errors.New("failed")
+}
+
+// ShortWriter is a WriteSyncer whose write method never fails, but
+// nevertheless fails to the last byte of the input.
+type ShortWriter struct{ Syncer }
+
+// Write implements io.Writer.
+func (w ShortWriter) Write(b []byte) (int, error) {
+ return len(b) - 1, nil
+}
+
+// Buffer is an implementation of zapcore.WriteSyncer that sends all writes to
+// a bytes.Buffer. It has convenience methods to split the accumulated buffer
+// on newlines.
+type Buffer struct {
+ bytes.Buffer
+ Syncer
+}
+
+// Lines returns the current buffer contents, split on newlines.
+func (b *Buffer) Lines() []string {
+ output := strings.Split(b.String(), "\n")
+ return output[:len(output)-1]
+}
+
+// Stripped returns the current buffer contents with the last trailing newline
+// stripped.
+func (b *Buffer) Stripped() string {
+ return strings.TrimRight(b.String(), "\n")
+}
diff --git a/vendor/go.uber.org/zap/internal/ztest/ya.make b/vendor/go.uber.org/zap/internal/ztest/ya.make
new file mode 100644
index 0000000000..f66ad3b511
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/ztest/ya.make
@@ -0,0 +1,18 @@
+GO_LIBRARY()
+
+LICENSE(MIT)
+
+SRCS(
+ clock.go
+ doc.go
+ timeout.go
+ writer.go
+)
+
+GO_TEST_SRCS(clock_test.go)
+
+END()
+
+RECURSE(
+ gotest
+)
diff --git a/vendor/go.uber.org/zap/leak_test.go b/vendor/go.uber.org/zap/leak_test.go
new file mode 100644
index 0000000000..474ed2f2e3
--- /dev/null
+++ b/vendor/go.uber.org/zap/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 zap
+
+import (
+ "testing"
+
+ "go.uber.org/goleak"
+)
+
+func TestMain(m *testing.M) {
+ goleak.VerifyTestMain(m)
+}
diff --git a/vendor/go.uber.org/zap/level.go b/vendor/go.uber.org/zap/level.go
new file mode 100644
index 0000000000..155b208bd3
--- /dev/null
+++ b/vendor/go.uber.org/zap/level.go
@@ -0,0 +1,153 @@
+// 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 zap
+
+import (
+ "sync/atomic"
+
+ "go.uber.org/zap/internal"
+ "go.uber.org/zap/zapcore"
+)
+
+const (
+ // DebugLevel logs are typically voluminous, and are usually disabled in
+ // production.
+ DebugLevel = zapcore.DebugLevel
+ // InfoLevel is the default logging priority.
+ InfoLevel = zapcore.InfoLevel
+ // WarnLevel logs are more important than Info, but don't need individual
+ // human review.
+ WarnLevel = zapcore.WarnLevel
+ // ErrorLevel logs are high-priority. If an application is running smoothly,
+ // it shouldn't generate any error-level logs.
+ ErrorLevel = zapcore.ErrorLevel
+ // DPanicLevel logs are particularly important errors. In development the
+ // logger panics after writing the message.
+ DPanicLevel = zapcore.DPanicLevel
+ // PanicLevel logs a message, then panics.
+ PanicLevel = zapcore.PanicLevel
+ // FatalLevel logs a message, then calls os.Exit(1).
+ FatalLevel = zapcore.FatalLevel
+)
+
+// LevelEnablerFunc is a convenient way to implement zapcore.LevelEnabler with
+// an anonymous function.
+//
+// It's particularly useful when splitting log output between different
+// outputs (e.g., standard error and standard out). For sample code, see the
+// package-level AdvancedConfiguration example.
+type LevelEnablerFunc func(zapcore.Level) bool
+
+// Enabled calls the wrapped function.
+func (f LevelEnablerFunc) Enabled(lvl zapcore.Level) bool { return f(lvl) }
+
+// An AtomicLevel is an atomically changeable, dynamic logging level. It lets
+// you safely change the log level of a tree of loggers (the root logger and
+// any children created by adding context) at runtime.
+//
+// The AtomicLevel itself is an http.Handler that serves a JSON endpoint to
+// alter its level.
+//
+// AtomicLevels must be created with the NewAtomicLevel constructor to allocate
+// their internal atomic pointer.
+type AtomicLevel struct {
+ l *atomic.Int32
+}
+
+var _ internal.LeveledEnabler = AtomicLevel{}
+
+// NewAtomicLevel creates an AtomicLevel with InfoLevel and above logging
+// enabled.
+func NewAtomicLevel() AtomicLevel {
+ lvl := AtomicLevel{l: new(atomic.Int32)}
+ lvl.l.Store(int32(InfoLevel))
+ return lvl
+}
+
+// NewAtomicLevelAt is a convenience function that creates an AtomicLevel
+// and then calls SetLevel with the given level.
+func NewAtomicLevelAt(l zapcore.Level) AtomicLevel {
+ a := NewAtomicLevel()
+ a.SetLevel(l)
+ return a
+}
+
+// ParseAtomicLevel parses an AtomicLevel based on a lowercase 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 ParseAtomicLevel(text string) (AtomicLevel, error) {
+ a := NewAtomicLevel()
+ l, err := zapcore.ParseLevel(text)
+ if err != nil {
+ return a, err
+ }
+
+ a.SetLevel(l)
+ return a, nil
+}
+
+// Enabled implements the zapcore.LevelEnabler interface, which allows the
+// AtomicLevel to be used in place of traditional static levels.
+func (lvl AtomicLevel) Enabled(l zapcore.Level) bool {
+ return lvl.Level().Enabled(l)
+}
+
+// Level returns the minimum enabled log level.
+func (lvl AtomicLevel) Level() zapcore.Level {
+ return zapcore.Level(int8(lvl.l.Load()))
+}
+
+// SetLevel alters the logging level.
+func (lvl AtomicLevel) SetLevel(l zapcore.Level) {
+ lvl.l.Store(int32(l))
+}
+
+// String returns the string representation of the underlying Level.
+func (lvl AtomicLevel) String() string {
+ return lvl.Level().String()
+}
+
+// UnmarshalText unmarshals the text to an AtomicLevel. It uses the same text
+// representations as the static zapcore.Levels ("debug", "info", "warn",
+// "error", "dpanic", "panic", and "fatal").
+func (lvl *AtomicLevel) UnmarshalText(text []byte) error {
+ if lvl.l == nil {
+ lvl.l = &atomic.Int32{}
+ }
+
+ var l zapcore.Level
+ if err := l.UnmarshalText(text); err != nil {
+ return err
+ }
+
+ lvl.SetLevel(l)
+ return nil
+}
+
+// MarshalText marshals the AtomicLevel to a byte slice. It uses the same
+// text representation as the static zapcore.Levels ("debug", "info", "warn",
+// "error", "dpanic", "panic", and "fatal").
+func (lvl AtomicLevel) MarshalText() (text []byte, err error) {
+ return lvl.Level().MarshalText()
+}
diff --git a/vendor/go.uber.org/zap/level_test.go b/vendor/go.uber.org/zap/level_test.go
new file mode 100644
index 0000000000..db7391d9bb
--- /dev/null
+++ b/vendor/go.uber.org/zap/level_test.go
@@ -0,0 +1,140 @@
+// 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 zap
+
+import (
+ "sync"
+ "testing"
+
+ "go.uber.org/zap/zapcore"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestLevelEnablerFunc(t *testing.T) {
+ enab := LevelEnablerFunc(func(l zapcore.Level) bool { return l == zapcore.InfoLevel })
+ tests := []struct {
+ level zapcore.Level
+ enabled bool
+ }{
+ {DebugLevel, false},
+ {InfoLevel, true},
+ {WarnLevel, false},
+ {ErrorLevel, false},
+ {DPanicLevel, false},
+ {PanicLevel, false},
+ {FatalLevel, false},
+ }
+ for _, tt := range tests {
+ assert.Equal(t, tt.enabled, enab.Enabled(tt.level), "Unexpected result applying LevelEnablerFunc to %s", tt.level)
+ }
+}
+
+func TestNewAtomicLevel(t *testing.T) {
+ lvl := NewAtomicLevel()
+ assert.Equal(t, InfoLevel, lvl.Level(), "Unexpected initial level.")
+ lvl.SetLevel(ErrorLevel)
+ assert.Equal(t, ErrorLevel, lvl.Level(), "Unexpected level after SetLevel.")
+ lvl = NewAtomicLevelAt(WarnLevel)
+ assert.Equal(t, WarnLevel, lvl.Level(), "Unexpected level after SetLevel.")
+}
+
+func TestParseAtomicLevel(t *testing.T) {
+ tests := []struct {
+ text string
+ level AtomicLevel
+ err string
+ }{
+ {"info", NewAtomicLevel(), ""},
+ {"DEBUG", NewAtomicLevelAt(DebugLevel), ""},
+ {"FOO", NewAtomicLevel(), `unrecognized level: "FOO"`},
+ }
+
+ for _, tt := range tests {
+ parsedAtomicLevel, err := ParseAtomicLevel(tt.text)
+ if len(tt.err) == 0 {
+ require.NoError(t, err)
+ assert.Equal(t, tt.level, parsedAtomicLevel)
+ } else {
+ assert.ErrorContains(t, err, tt.err)
+ }
+ }
+}
+
+func TestAtomicLevelMutation(t *testing.T) {
+ lvl := NewAtomicLevel()
+ lvl.SetLevel(WarnLevel)
+ // Trigger races for non-atomic level mutations.
+ proceed := make(chan struct{})
+ wg := &sync.WaitGroup{}
+ runConcurrently(10, 100, wg, func() {
+ <-proceed
+ assert.Equal(t, WarnLevel, lvl.Level())
+ })
+ runConcurrently(10, 100, wg, func() {
+ <-proceed
+ lvl.SetLevel(WarnLevel)
+ })
+ close(proceed)
+ wg.Wait()
+}
+
+func TestAtomicLevelText(t *testing.T) {
+ tests := []struct {
+ text string
+ expect zapcore.Level
+ err bool
+ }{
+ {"debug", DebugLevel, false},
+ {"info", InfoLevel, false},
+ {"", InfoLevel, false},
+ {"warn", WarnLevel, false},
+ {"error", ErrorLevel, false},
+ {"dpanic", DPanicLevel, false},
+ {"panic", PanicLevel, false},
+ {"fatal", FatalLevel, false},
+ {"foobar", InfoLevel, true},
+ }
+
+ for _, tt := range tests {
+ var lvl AtomicLevel
+ // Test both initial unmarshaling and overwriting existing value.
+ for i := 0; i < 2; i++ {
+ if tt.err {
+ assert.Error(t, lvl.UnmarshalText([]byte(tt.text)), "Expected unmarshaling %q to fail.", tt.text)
+ } else {
+ assert.NoError(t, lvl.UnmarshalText([]byte(tt.text)), "Expected unmarshaling %q to succeed.", tt.text)
+ }
+ assert.Equal(t, tt.expect, lvl.Level(), "Unexpected level after unmarshaling.")
+ lvl.SetLevel(InfoLevel)
+ }
+
+ // Test marshalling
+ if tt.text != "" && !tt.err {
+ lvl.SetLevel(tt.expect)
+ marshaled, err := lvl.MarshalText()
+ assert.NoError(t, err, `Unexpected error marshalling level "%v" to text.`, tt.expect)
+ assert.Equal(t, tt.text, string(marshaled), "Expected marshaled text to match")
+ assert.Equal(t, tt.text, lvl.String(), "Expected Stringer call to match")
+ }
+ }
+}
diff --git a/vendor/go.uber.org/zap/logger.go b/vendor/go.uber.org/zap/logger.go
new file mode 100644
index 0000000000..6205fe48a6
--- /dev/null
+++ b/vendor/go.uber.org/zap/logger.go
@@ -0,0 +1,432 @@
+// 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 zap
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "strings"
+
+ "go.uber.org/zap/internal/bufferpool"
+ "go.uber.org/zap/internal/stacktrace"
+ "go.uber.org/zap/zapcore"
+)
+
+// A Logger provides fast, leveled, structured logging. All methods are safe
+// for concurrent use.
+//
+// The Logger is designed for contexts in which every microsecond and every
+// allocation matters, so its API intentionally favors performance and type
+// safety over brevity. For most applications, the SugaredLogger strikes a
+// better balance between performance and ergonomics.
+type Logger struct {
+ core zapcore.Core
+
+ development bool
+ addCaller bool
+ onFatal zapcore.CheckWriteHook // default is WriteThenFatal
+
+ name string
+ errorOutput zapcore.WriteSyncer
+
+ addStack zapcore.LevelEnabler
+
+ callerSkip int
+
+ clock zapcore.Clock
+}
+
+// New constructs a new Logger from the provided zapcore.Core and Options. If
+// the passed zapcore.Core is nil, it falls back to using a no-op
+// implementation.
+//
+// This is the most flexible way to construct a Logger, but also the most
+// verbose. For typical use cases, the highly-opinionated presets
+// (NewProduction, NewDevelopment, and NewExample) or the Config struct are
+// more convenient.
+//
+// For sample code, see the package-level AdvancedConfiguration example.
+func New(core zapcore.Core, options ...Option) *Logger {
+ if core == nil {
+ return NewNop()
+ }
+ log := &Logger{
+ core: core,
+ errorOutput: zapcore.Lock(os.Stderr),
+ addStack: zapcore.FatalLevel + 1,
+ clock: zapcore.DefaultClock,
+ }
+ return log.WithOptions(options...)
+}
+
+// NewNop returns a no-op Logger. It never writes out logs or internal errors,
+// and it never runs user-defined hooks.
+//
+// Using WithOptions to replace the Core or error output of a no-op Logger can
+// re-enable logging.
+func NewNop() *Logger {
+ return &Logger{
+ core: zapcore.NewNopCore(),
+ errorOutput: zapcore.AddSync(io.Discard),
+ addStack: zapcore.FatalLevel + 1,
+ clock: zapcore.DefaultClock,
+ }
+}
+
+// NewProduction builds a sensible production Logger that writes InfoLevel and
+// above logs to standard error as JSON.
+//
+// It's a shortcut for NewProductionConfig().Build(...Option).
+func NewProduction(options ...Option) (*Logger, error) {
+ return NewProductionConfig().Build(options...)
+}
+
+// NewDevelopment builds a development Logger that writes DebugLevel and above
+// logs to standard error in a human-friendly format.
+//
+// It's a shortcut for NewDevelopmentConfig().Build(...Option).
+func NewDevelopment(options ...Option) (*Logger, error) {
+ return NewDevelopmentConfig().Build(options...)
+}
+
+// Must is a helper that wraps a call to a function returning (*Logger, error)
+// and panics if the error is non-nil. It is intended for use in variable
+// initialization such as:
+//
+// var logger = zap.Must(zap.NewProduction())
+func Must(logger *Logger, err error) *Logger {
+ if err != nil {
+ panic(err)
+ }
+
+ return logger
+}
+
+// NewExample builds a Logger that's designed for use in zap's testable
+// examples. It writes DebugLevel and above logs to standard out as JSON, but
+// omits the timestamp and calling function to keep example output
+// short and deterministic.
+func NewExample(options ...Option) *Logger {
+ encoderCfg := zapcore.EncoderConfig{
+ MessageKey: "msg",
+ LevelKey: "level",
+ NameKey: "logger",
+ EncodeLevel: zapcore.LowercaseLevelEncoder,
+ EncodeTime: zapcore.ISO8601TimeEncoder,
+ EncodeDuration: zapcore.StringDurationEncoder,
+ }
+ core := zapcore.NewCore(zapcore.NewJSONEncoder(encoderCfg), os.Stdout, DebugLevel)
+ return New(core).WithOptions(options...)
+}
+
+// Sugar wraps the Logger to provide a more ergonomic, but slightly slower,
+// API. Sugaring a Logger is quite inexpensive, so it's reasonable for a
+// single application to use both Loggers and SugaredLoggers, converting
+// between them on the boundaries of performance-sensitive code.
+func (log *Logger) Sugar() *SugaredLogger {
+ core := log.clone()
+ core.callerSkip += 2
+ return &SugaredLogger{core}
+}
+
+// Named adds a new path segment to the logger's name. Segments are joined by
+// periods. By default, Loggers are unnamed.
+func (log *Logger) Named(s string) *Logger {
+ if s == "" {
+ return log
+ }
+ l := log.clone()
+ if log.name == "" {
+ l.name = s
+ } else {
+ l.name = strings.Join([]string{l.name, s}, ".")
+ }
+ return l
+}
+
+// WithOptions clones the current Logger, applies the supplied Options, and
+// returns the resulting Logger. It's safe to use concurrently.
+func (log *Logger) WithOptions(opts ...Option) *Logger {
+ c := log.clone()
+ for _, opt := range opts {
+ opt.apply(c)
+ }
+ return c
+}
+
+// With creates a child logger and adds structured context to it. Fields added
+// to the child don't affect the parent, and vice versa. Any fields that
+// require evaluation (such as Objects) are evaluated upon invocation of With.
+func (log *Logger) With(fields ...Field) *Logger {
+ if len(fields) == 0 {
+ return log
+ }
+ l := log.clone()
+ l.core = l.core.With(fields)
+ return l
+}
+
+// WithLazy creates a child logger and adds structured context to it lazily.
+//
+// The fields are evaluated only if the logger is further chained with [With]
+// or is written to with any of the log level methods.
+// Until that occurs, the logger may retain references to objects inside the fields,
+// and logging will reflect the state of an object at the time of logging,
+// not the time of WithLazy().
+//
+// WithLazy provides a worthwhile performance optimization for contextual loggers
+// when the likelihood of using the child logger is low,
+// such as error paths and rarely taken branches.
+//
+// Similar to [With], fields added to the child don't affect the parent, and vice versa.
+func (log *Logger) WithLazy(fields ...Field) *Logger {
+ if len(fields) == 0 {
+ return log
+ }
+ return log.WithOptions(WrapCore(func(core zapcore.Core) zapcore.Core {
+ return zapcore.NewLazyWith(core, fields)
+ }))
+}
+
+// Level reports the minimum enabled level for this logger.
+//
+// For NopLoggers, this is [zapcore.InvalidLevel].
+func (log *Logger) Level() zapcore.Level {
+ return zapcore.LevelOf(log.core)
+}
+
+// Check returns a CheckedEntry if logging a message at the specified level
+// is enabled. It's a completely optional optimization; in high-performance
+// applications, Check can help avoid allocating a slice to hold fields.
+func (log *Logger) Check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry {
+ return log.check(lvl, msg)
+}
+
+// Log logs a message at the specified level. The message includes any fields
+// passed at the log site, as well as any fields accumulated on the logger.
+// Any Fields that require evaluation (such as Objects) are evaluated upon
+// invocation of Log.
+func (log *Logger) Log(lvl zapcore.Level, msg string, fields ...Field) {
+ if ce := log.check(lvl, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// Debug logs a message at DebugLevel. The message includes any fields passed
+// at the log site, as well as any fields accumulated on the logger.
+func (log *Logger) Debug(msg string, fields ...Field) {
+ if ce := log.check(DebugLevel, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// Info logs a message at InfoLevel. The message includes any fields passed
+// at the log site, as well as any fields accumulated on the logger.
+func (log *Logger) Info(msg string, fields ...Field) {
+ if ce := log.check(InfoLevel, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// Warn logs a message at WarnLevel. The message includes any fields passed
+// at the log site, as well as any fields accumulated on the logger.
+func (log *Logger) Warn(msg string, fields ...Field) {
+ if ce := log.check(WarnLevel, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// Error logs a message at ErrorLevel. The message includes any fields passed
+// at the log site, as well as any fields accumulated on the logger.
+func (log *Logger) Error(msg string, fields ...Field) {
+ if ce := log.check(ErrorLevel, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// DPanic logs a message at DPanicLevel. The message includes any fields
+// passed at the log site, as well as any fields accumulated on the logger.
+//
+// If the logger is in development mode, it then panics (DPanic means
+// "development panic"). This is useful for catching errors that are
+// recoverable, but shouldn't ever happen.
+func (log *Logger) DPanic(msg string, fields ...Field) {
+ if ce := log.check(DPanicLevel, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// Panic logs a message at PanicLevel. The message includes any fields passed
+// at the log site, as well as any fields accumulated on the logger.
+//
+// The logger then panics, even if logging at PanicLevel is disabled.
+func (log *Logger) Panic(msg string, fields ...Field) {
+ if ce := log.check(PanicLevel, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// Fatal logs a message at FatalLevel. The message includes any fields passed
+// at the log site, as well as any fields accumulated on the logger.
+//
+// The logger then calls os.Exit(1), even if logging at FatalLevel is
+// disabled.
+func (log *Logger) Fatal(msg string, fields ...Field) {
+ if ce := log.check(FatalLevel, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// Sync calls the underlying Core's Sync method, flushing any buffered log
+// entries. Applications should take care to call Sync before exiting.
+func (log *Logger) Sync() error {
+ return log.core.Sync()
+}
+
+// Core returns the Logger's underlying zapcore.Core.
+func (log *Logger) Core() zapcore.Core {
+ return log.core
+}
+
+// Name returns the Logger's underlying name,
+// or an empty string if the logger is unnamed.
+func (log *Logger) Name() string {
+ return log.name
+}
+
+func (log *Logger) clone() *Logger {
+ clone := *log
+ return &clone
+}
+
+func (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry {
+ // Logger.check must always be called directly by a method in the
+ // Logger interface (e.g., Check, Info, Fatal).
+ // This skips Logger.check and the Info/Fatal/Check/etc. method that
+ // called it.
+ const callerSkipOffset = 2
+
+ // Check the level first to reduce the cost of disabled log calls.
+ // Since Panic and higher may exit, we skip the optimization for those levels.
+ if lvl < zapcore.DPanicLevel && !log.core.Enabled(lvl) {
+ return nil
+ }
+
+ // Create basic checked entry thru the core; this will be non-nil if the
+ // log message will actually be written somewhere.
+ ent := zapcore.Entry{
+ LoggerName: log.name,
+ Time: log.clock.Now(),
+ Level: lvl,
+ Message: msg,
+ }
+ ce := log.core.Check(ent, nil)
+ willWrite := ce != nil
+
+ // Set up any required terminal behavior.
+ switch ent.Level {
+ case zapcore.PanicLevel:
+ ce = ce.After(ent, zapcore.WriteThenPanic)
+ case zapcore.FatalLevel:
+ onFatal := log.onFatal
+ // nil or WriteThenNoop will lead to continued execution after
+ // a Fatal log entry, which is unexpected. For example,
+ //
+ // f, err := os.Open(..)
+ // if err != nil {
+ // log.Fatal("cannot open", zap.Error(err))
+ // }
+ // fmt.Println(f.Name())
+ //
+ // The f.Name() will panic if we continue execution after the
+ // log.Fatal.
+ if onFatal == nil || onFatal == zapcore.WriteThenNoop {
+ onFatal = zapcore.WriteThenFatal
+ }
+ ce = ce.After(ent, onFatal)
+ case zapcore.DPanicLevel:
+ if log.development {
+ ce = ce.After(ent, zapcore.WriteThenPanic)
+ }
+ }
+
+ // Only do further annotation if we're going to write this message; checked
+ // entries that exist only for terminal behavior don't benefit from
+ // annotation.
+ if !willWrite {
+ return ce
+ }
+
+ // Thread the error output through to the CheckedEntry.
+ ce.ErrorOutput = log.errorOutput
+
+ addStack := log.addStack.Enabled(ce.Level)
+ if !log.addCaller && !addStack {
+ return ce
+ }
+
+ // Adding the caller or stack trace requires capturing the callers of
+ // this function. We'll share information between these two.
+ stackDepth := stacktrace.First
+ if addStack {
+ stackDepth = stacktrace.Full
+ }
+ stack := stacktrace.Capture(log.callerSkip+callerSkipOffset, stackDepth)
+ defer stack.Free()
+
+ if stack.Count() == 0 {
+ if log.addCaller {
+ fmt.Fprintf(log.errorOutput, "%v Logger.check error: failed to get caller\n", ent.Time.UTC())
+ _ = log.errorOutput.Sync()
+ }
+ return ce
+ }
+
+ frame, more := stack.Next()
+
+ if log.addCaller {
+ ce.Caller = zapcore.EntryCaller{
+ Defined: frame.PC != 0,
+ PC: frame.PC,
+ File: frame.File,
+ Line: frame.Line,
+ Function: frame.Function,
+ }
+ }
+
+ if addStack {
+ buffer := bufferpool.Get()
+ defer buffer.Free()
+
+ stackfmt := stacktrace.NewFormatter(buffer)
+
+ // We've already extracted the first frame, so format that
+ // separately and defer to stackfmt for the rest.
+ stackfmt.FormatFrame(frame)
+ if more {
+ stackfmt.FormatStack(stack)
+ }
+ ce.Stack = buffer.String()
+ }
+
+ return ce
+}
diff --git a/vendor/go.uber.org/zap/logger_bench_test.go b/vendor/go.uber.org/zap/logger_bench_test.go
new file mode 100644
index 0000000000..9d4129826b
--- /dev/null
+++ b/vendor/go.uber.org/zap/logger_bench_test.go
@@ -0,0 +1,361 @@
+// 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 zap
+
+import (
+ "errors"
+ "runtime"
+ "strconv"
+ "sync"
+ "testing"
+ "time"
+
+ "go.uber.org/zap/internal/ztest"
+ "go.uber.org/zap/zapcore"
+)
+
+type user struct {
+ Name string
+ Email string
+ CreatedAt time.Time
+}
+
+func (u *user) MarshalLogObject(enc zapcore.ObjectEncoder) error {
+ enc.AddString("name", u.Name)
+ enc.AddString("email", u.Email)
+ enc.AddInt64("created_at", u.CreatedAt.UnixNano())
+ return nil
+}
+
+var _jane = &user{
+ Name: "Jane Doe",
+ Email: "jane@test.com",
+ CreatedAt: time.Date(1980, 1, 1, 12, 0, 0, 0, time.UTC),
+}
+
+func withBenchedLogger(b *testing.B, f func(*Logger)) {
+ logger := New(
+ zapcore.NewCore(
+ zapcore.NewJSONEncoder(NewProductionConfig().EncoderConfig),
+ &ztest.Discarder{},
+ DebugLevel,
+ ))
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ f(logger)
+ }
+ })
+}
+
+func BenchmarkNoContext(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("No context.")
+ })
+}
+
+func BenchmarkBoolField(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Boolean.", Bool("foo", true))
+ })
+}
+
+func BenchmarkByteStringField(b *testing.B) {
+ val := []byte("bar")
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("ByteString.", ByteString("foo", val))
+ })
+}
+
+func BenchmarkFloat64Field(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Floating point.", Float64("foo", 3.14))
+ })
+}
+
+func BenchmarkIntField(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Integer.", Int("foo", 42))
+ })
+}
+
+func BenchmarkInt64Field(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("64-bit integer.", Int64("foo", 42))
+ })
+}
+
+func BenchmarkStringField(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Strings.", String("foo", "bar"))
+ })
+}
+
+func BenchmarkStringerField(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Level.", Stringer("foo", InfoLevel))
+ })
+}
+
+func BenchmarkTimeField(b *testing.B) {
+ t := time.Unix(0, 0)
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Time.", Time("foo", t))
+ })
+}
+
+func BenchmarkDurationField(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Duration", Duration("foo", time.Second))
+ })
+}
+
+func BenchmarkErrorField(b *testing.B) {
+ err := errors.New("egad")
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Error.", Error(err))
+ })
+}
+
+func BenchmarkErrorsField(b *testing.B) {
+ errs := []error{
+ errors.New("egad"),
+ errors.New("oh no"),
+ errors.New("dear me"),
+ errors.New("such fail"),
+ }
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Errors.", Errors("errors", errs))
+ })
+}
+
+func BenchmarkStackField(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Error.", Stack("stacktrace"))
+ })
+}
+
+func BenchmarkObjectField(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Arbitrary ObjectMarshaler.", Object("user", _jane))
+ })
+}
+
+func BenchmarkReflectField(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Reflection-based serialization.", Reflect("user", _jane))
+ })
+}
+
+func BenchmarkAddCallerHook(b *testing.B) {
+ logger := New(
+ zapcore.NewCore(
+ zapcore.NewJSONEncoder(NewProductionConfig().EncoderConfig),
+ &ztest.Discarder{},
+ InfoLevel,
+ ),
+ AddCaller(),
+ )
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info("Caller.")
+ }
+ })
+}
+
+func BenchmarkAddCallerAndStacktrace(b *testing.B) {
+ logger := New(
+ zapcore.NewCore(
+ zapcore.NewJSONEncoder(NewProductionConfig().EncoderConfig),
+ &ztest.Discarder{},
+ InfoLevel,
+ ),
+ AddCaller(),
+ AddStacktrace(WarnLevel),
+ )
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Warn("Caller and stacktrace.")
+ }
+ })
+}
+
+func Benchmark5WithsUsed(b *testing.B) {
+ benchmarkWithUsed(b, (*Logger).With, 5, true)
+}
+
+// This benchmark will be used in future as a
+// baseline for improving
+func Benchmark5WithsNotUsed(b *testing.B) {
+ benchmarkWithUsed(b, (*Logger).With, 5, false)
+}
+
+func Benchmark5WithLazysUsed(b *testing.B) {
+ benchmarkWithUsed(b, (*Logger).WithLazy, 5, true)
+}
+
+// This benchmark will be used in future as a
+// baseline for improving
+func Benchmark5WithLazysNotUsed(b *testing.B) {
+ benchmarkWithUsed(b, (*Logger).WithLazy, 5, false)
+}
+
+func benchmarkWithUsed(b *testing.B, withMethod func(*Logger, ...zapcore.Field) *Logger, N int, use bool) {
+ keys := make([]string, N)
+ values := make([]string, N)
+ for i := 0; i < N; i++ {
+ keys[i] = "k" + strconv.Itoa(i)
+ values[i] = "v" + strconv.Itoa(i)
+ }
+
+ b.ResetTimer()
+
+ withBenchedLogger(b, func(log *Logger) {
+ for i := 0; i < N; i++ {
+ log = withMethod(log, String(keys[i], values[i]))
+ }
+ if use {
+ log.Info("used")
+ return
+ }
+ runtime.KeepAlive(log)
+ })
+}
+
+func Benchmark10Fields(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Ten fields, passed at the log site.",
+ Int("one", 1),
+ Int("two", 2),
+ Int("three", 3),
+ Int("four", 4),
+ Int("five", 5),
+ Int("six", 6),
+ Int("seven", 7),
+ Int("eight", 8),
+ Int("nine", 9),
+ Int("ten", 10),
+ )
+ })
+}
+
+func Benchmark100Fields(b *testing.B) {
+ const batchSize = 50
+ logger := New(zapcore.NewCore(
+ zapcore.NewJSONEncoder(NewProductionConfig().EncoderConfig),
+ &ztest.Discarder{},
+ DebugLevel,
+ ))
+
+ // Don't include allocating these helper slices in the benchmark. Since
+ // access to them isn't synchronized, we can't run the benchmark in
+ // parallel.
+ first := make([]Field, batchSize)
+ second := make([]Field, batchSize)
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ for i := 0; i < batchSize; i++ {
+ // We're duplicating keys, but that doesn't affect performance.
+ first[i] = Int("foo", i)
+ second[i] = Int("foo", i+batchSize)
+ }
+ logger.With(first...).Info("Child loggers with lots of context.", second...)
+ }
+}
+
+func BenchmarkAny(b *testing.B) {
+ key := "some-long-string-longer-than-16"
+
+ tests := []struct {
+ name string
+ typed func() Field
+ anyArg any
+ }{
+ {
+ name: "string",
+ typed: func() Field { return String(key, "yet-another-long-string") },
+ anyArg: "yet-another-long-string",
+ },
+ {
+ name: "stringer",
+ typed: func() Field { return Stringer(key, InfoLevel) },
+ anyArg: InfoLevel,
+ },
+ }
+
+ for _, tt := range tests {
+ b.Run(tt.name, func(b *testing.B) {
+ b.Run("field-only", func(b *testing.B) {
+ b.Run("typed", func(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ f := tt.typed()
+ runtime.KeepAlive(f)
+ })
+ })
+ b.Run("any", func(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ f := Any(key, tt.anyArg)
+ runtime.KeepAlive(f)
+ })
+ })
+ })
+ b.Run("log", func(b *testing.B) {
+ b.Run("typed", func(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("", tt.typed())
+ })
+ })
+ b.Run("any", func(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("", Any(key, tt.anyArg))
+ })
+ })
+ })
+ b.Run("log-go", func(b *testing.B) {
+ b.Run("typed", func(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go func() {
+ log.Info("", tt.typed())
+ wg.Done()
+ }()
+ wg.Wait()
+ })
+ })
+ b.Run("any", func(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go func() {
+ log.Info("", Any(key, tt.anyArg))
+ wg.Done()
+ }()
+ wg.Wait()
+ })
+ })
+ })
+ })
+ }
+}
diff --git a/vendor/go.uber.org/zap/logger_test.go b/vendor/go.uber.org/zap/logger_test.go
new file mode 100644
index 0000000000..c371b81228
--- /dev/null
+++ b/vendor/go.uber.org/zap/logger_test.go
@@ -0,0 +1,938 @@
+// 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 zap
+
+import (
+ "errors"
+ "fmt"
+ "strconv"
+ "sync"
+ "sync/atomic"
+ "testing"
+
+ "go.uber.org/zap/internal/exit"
+ "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 makeCountingHook() (func(zapcore.Entry) error, *atomic.Int64) {
+ count := &atomic.Int64{}
+ h := func(zapcore.Entry) error {
+ count.Add(1)
+ return nil
+ }
+ return h, count
+}
+
+func TestLoggerAtomicLevel(t *testing.T) {
+ // Test that the dynamic level applies to all ancestors and descendants.
+ dl := NewAtomicLevel()
+
+ withLogger(t, dl, nil, func(grandparent *Logger, _ *observer.ObservedLogs) {
+ parent := grandparent.With(Int("generation", 1))
+ child := parent.With(Int("generation", 2))
+
+ tests := []struct {
+ setLevel zapcore.Level
+ testLevel zapcore.Level
+ enabled bool
+ }{
+ {DebugLevel, DebugLevel, true},
+ {InfoLevel, DebugLevel, false},
+ {WarnLevel, PanicLevel, true},
+ }
+
+ for _, tt := range tests {
+ dl.SetLevel(tt.setLevel)
+ for _, logger := range []*Logger{grandparent, parent, child} {
+ if tt.enabled {
+ assert.NotNil(
+ t,
+ logger.Check(tt.testLevel, ""),
+ "Expected level %s to be enabled after setting level %s.", tt.testLevel, tt.setLevel,
+ )
+ } else {
+ assert.Nil(
+ t,
+ logger.Check(tt.testLevel, ""),
+ "Expected level %s to be enabled after setting level %s.", tt.testLevel, tt.setLevel,
+ )
+ }
+ }
+ }
+ })
+}
+
+func TestLoggerLevel(t *testing.T) {
+ levels := []zapcore.Level{
+ DebugLevel,
+ InfoLevel,
+ WarnLevel,
+ ErrorLevel,
+ DPanicLevel,
+ PanicLevel,
+ FatalLevel,
+ }
+
+ for _, lvl := range levels {
+ lvl := lvl
+ t.Run(lvl.String(), func(t *testing.T) {
+ t.Parallel()
+
+ core, _ := observer.New(lvl)
+ log := New(core)
+ assert.Equal(t, lvl, log.Level())
+ })
+ }
+
+ t.Run("Nop", func(t *testing.T) {
+ assert.Equal(t, zapcore.InvalidLevel, NewNop().Level())
+ })
+}
+
+func TestLoggerInitialFields(t *testing.T) {
+ fieldOpts := opts(Fields(Int("foo", 42), String("bar", "baz")))
+ withLogger(t, DebugLevel, fieldOpts, func(logger *Logger, logs *observer.ObservedLogs) {
+ logger.Info("")
+ assert.Equal(
+ t,
+ observer.LoggedEntry{Context: []Field{Int("foo", 42), String("bar", "baz")}},
+ logs.AllUntimed()[0],
+ "Unexpected output with initial fields set.",
+ )
+ })
+}
+
+func TestLoggerWith(t *testing.T) {
+
+ tests := []struct {
+ name string
+ initialFields []Field
+ withMethod func(*Logger, ...Field) *Logger
+ }{
+ {
+ "regular non lazy logger",
+ []Field{Int("foo", 42)},
+ (*Logger).With,
+ },
+ {
+ "regular non lazy logger no initial fields",
+ []Field{},
+ (*Logger).With,
+ },
+ {
+ "lazy with logger",
+ []Field{Int("foo", 42)},
+ (*Logger).WithLazy,
+ },
+ {
+ "lazy with logger no initial fields",
+ []Field{},
+ (*Logger).WithLazy,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ withLogger(t, DebugLevel, opts(Fields(tt.initialFields...)), func(logger *Logger, logs *observer.ObservedLogs) {
+ // Child loggers should have copy-on-write semantics, so two children
+ // shouldn't stomp on each other's fields or affect the parent's fields.
+ tt.withMethod(logger).Info("")
+ tt.withMethod(logger, String("one", "two")).Info("")
+ tt.withMethod(logger, String("three", "four")).Info("")
+ tt.withMethod(logger, String("five", "six")).With(String("seven", "eight")).Info("")
+ logger.Info("")
+
+ assert.Equal(t, []observer.LoggedEntry{
+ {Context: tt.initialFields},
+ {Context: append(tt.initialFields, String("one", "two"))},
+ {Context: append(tt.initialFields, String("three", "four"))},
+ {Context: append(tt.initialFields, String("five", "six"), String("seven", "eight"))},
+ {Context: tt.initialFields},
+ }, logs.AllUntimed(), "Unexpected cross-talk between child loggers.")
+ })
+ })
+ }
+}
+
+func TestLoggerWithCaptures(t *testing.T) {
+ type withF func(*Logger, ...Field) *Logger
+ tests := []struct {
+ name string
+ withMethods []withF
+ wantJSON []string
+ }{
+ {
+ name: "regular with captures arguments at time of With",
+ withMethods: []withF{(*Logger).With},
+ wantJSON: []string{
+ `{
+ "m": "hello 0",
+ "a0": [0],
+ "b0": [1]
+ }`,
+ `{
+ "m": "world 0",
+ "a0": [0],
+ "c0": [2]
+ }`,
+ },
+ },
+ {
+ name: "lazy with captures arguments at time of With or Logging",
+ withMethods: []withF{(*Logger).WithLazy},
+ wantJSON: []string{
+ `{
+ "m": "hello 0",
+ "a0": [1],
+ "b0": [1]
+ }`,
+ `{
+ "m": "world 0",
+ "a0": [1],
+ "c0": [2]
+ }`,
+ },
+ },
+ {
+ name: "2x With captures arguments at time of each With",
+ withMethods: []withF{(*Logger).With, (*Logger).With},
+ wantJSON: []string{
+ `{
+ "m": "hello 0",
+ "a0": [0],
+ "b0": [1]
+ }`,
+ `{
+ "m": "world 0",
+ "a0": [0],
+ "c0": [2]
+ }`,
+ `{
+ "m": "hello 1",
+ "a0": [0],
+ "c0": [2],
+ "a1": [10],
+ "b1": [11]
+ }`,
+ `{
+ "m": "world 1",
+ "a0": [0],
+ "c0": [2],
+ "a1": [10],
+ "c1": [12]
+ }`,
+ },
+ },
+ {
+ name: "2x WithLazy. Captures arguments only at logging time.",
+ withMethods: []withF{(*Logger).WithLazy, (*Logger).WithLazy},
+ wantJSON: []string{
+ `{
+ "m": "hello 0",
+ "a0": [1],
+ "b0": [1]
+ }`,
+ `{
+ "m": "world 0",
+ "a0": [1],
+ "c0": [2]
+ }`,
+ `{
+ "m": "hello 1",
+ "a0": [1],
+ "c0": [2],
+ "a1": [11],
+ "b1": [11]
+ }`,
+ `{
+ "m": "world 1",
+ "a0": [1],
+ "c0": [2],
+ "a1": [11],
+ "c1": [12]
+ }`,
+ },
+ },
+ {
+ name: "WithLazy then With",
+ withMethods: []withF{(*Logger).WithLazy, (*Logger).With},
+ wantJSON: []string{
+ `{
+ "m": "hello 0",
+ "a0": [1],
+ "b0": [1]
+ }`,
+ `{
+ "m": "world 0",
+ "a0": [1],
+ "c0": [2]
+ }`,
+ `{
+ "m": "hello 1",
+ "a0": [1],
+ "c0": [2],
+ "a1": [10],
+ "b1": [11]
+ }`,
+ `{
+ "m": "world 1",
+ "a0": [1],
+ "c0": [2],
+ "a1": [10],
+ "c1": [12]
+ }`,
+ },
+ },
+ {
+ name: "With then WithLazy",
+ withMethods: []withF{(*Logger).With, (*Logger).WithLazy},
+ wantJSON: []string{
+ `{
+ "m": "hello 0",
+ "a0": [0],
+ "b0": [1]
+ }`,
+ `{
+ "m": "world 0",
+ "a0": [0],
+ "c0": [2]
+ }`,
+ `{
+ "m": "hello 1",
+ "a0": [0],
+ "c0": [2],
+ "a1": [11],
+ "b1": [11]
+ }`,
+ `{
+ "m": "world 1",
+ "a0": [0],
+ "c0": [2],
+ "a1": [11],
+ "c1": [12]
+ }`,
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ enc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{
+ MessageKey: "m",
+ })
+
+ var bs ztest.Buffer
+ logger := New(zapcore.NewCore(enc, &bs, DebugLevel))
+
+ for i, withMethod := range tt.withMethods {
+
+ iStr := strconv.Itoa(i)
+ x := 10 * i
+ arr := zapcore.ArrayMarshalerFunc(func(enc zapcore.ArrayEncoder) error {
+ enc.AppendInt(x)
+ return nil
+ })
+
+ // Demonstrate the arguments are captured when With() and Info() are invoked.
+ logger = withMethod(logger, Array("a"+iStr, arr))
+ x++
+ logger.Info(fmt.Sprintf("hello %d", i), Array("b"+iStr, arr))
+ x++
+ logger = withMethod(logger, Array("c"+iStr, arr))
+ logger.Info(fmt.Sprintf("world %d", i))
+ }
+
+ if lines := bs.Lines(); assert.Len(t, lines, len(tt.wantJSON)) {
+ for i, want := range tt.wantJSON {
+ assert.JSONEq(t, want, lines[i], "Unexpected output from the %d'th log.", i)
+ }
+ }
+ })
+ }
+}
+
+func TestLoggerLogPanic(t *testing.T) {
+ for _, tt := range []struct {
+ do func(*Logger)
+ should bool
+ expected string
+ }{
+ {func(logger *Logger) { logger.Check(PanicLevel, "foo").Write() }, true, "foo"},
+ {func(logger *Logger) { logger.Log(PanicLevel, "bar") }, true, "bar"},
+ {func(logger *Logger) { logger.Panic("baz") }, true, "baz"},
+ } {
+ withLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {
+ if tt.should {
+ assert.Panics(t, func() { tt.do(logger) }, "Expected panic")
+ } else {
+ assert.NotPanics(t, func() { tt.do(logger) }, "Expected no panic")
+ }
+
+ output := logs.AllUntimed()
+ assert.Equal(t, 1, len(output), "Unexpected number of logs.")
+ assert.Equal(t, 0, len(output[0].Context), "Unexpected context on first log.")
+ assert.Equal(
+ t,
+ zapcore.Entry{Message: tt.expected, Level: PanicLevel},
+ output[0].Entry,
+ "Unexpected output from panic-level Log.",
+ )
+ })
+ }
+}
+
+func TestLoggerLogFatal(t *testing.T) {
+ for _, tt := range []struct {
+ do func(*Logger)
+ expected string
+ }{
+ {func(logger *Logger) { logger.Check(FatalLevel, "foo").Write() }, "foo"},
+ {func(logger *Logger) { logger.Log(FatalLevel, "bar") }, "bar"},
+ {func(logger *Logger) { logger.Fatal("baz") }, "baz"},
+ } {
+ withLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {
+ stub := exit.WithStub(func() {
+ tt.do(logger)
+ })
+ assert.True(t, stub.Exited, "Expected Fatal logger call to terminate process.")
+ output := logs.AllUntimed()
+ assert.Equal(t, 1, len(output), "Unexpected number of logs.")
+ assert.Equal(t, 0, len(output[0].Context), "Unexpected context on first log.")
+ assert.Equal(
+ t,
+ zapcore.Entry{Message: tt.expected, Level: FatalLevel},
+ output[0].Entry,
+ "Unexpected output from fatal-level Log.",
+ )
+ })
+ }
+}
+
+func TestLoggerLeveledMethods(t *testing.T) {
+ withLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {
+ tests := []struct {
+ method func(string, ...Field)
+ expectedLevel zapcore.Level
+ }{
+ {logger.Debug, DebugLevel},
+ {logger.Info, InfoLevel},
+ {logger.Warn, WarnLevel},
+ {logger.Error, ErrorLevel},
+ {logger.DPanic, DPanicLevel},
+ }
+ for i, tt := range tests {
+ tt.method("")
+ output := logs.AllUntimed()
+ assert.Equal(t, i+1, len(output), "Unexpected number of logs.")
+ assert.Equal(t, 0, len(output[i].Context), "Unexpected context on first log.")
+ assert.Equal(
+ t,
+ zapcore.Entry{Level: tt.expectedLevel},
+ output[i].Entry,
+ "Unexpected output from %s-level logger method.", tt.expectedLevel)
+ }
+ })
+}
+
+func TestLoggerLogLevels(t *testing.T) {
+ withLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {
+ levels := []zapcore.Level{
+ DebugLevel,
+ InfoLevel,
+ WarnLevel,
+ ErrorLevel,
+ DPanicLevel,
+ }
+ for i, level := range levels {
+ logger.Log(level, "")
+ output := logs.AllUntimed()
+ assert.Equal(t, i+1, len(output), "Unexpected number of logs.")
+ assert.Equal(t, 0, len(output[i].Context), "Unexpected context on first log.")
+ assert.Equal(
+ t,
+ zapcore.Entry{Level: level},
+ output[i].Entry,
+ "Unexpected output from %s-level logger method.", level)
+ }
+ })
+}
+
+func TestLoggerAlwaysPanics(t *testing.T) {
+ // Users can disable writing out panic-level logs, but calls to logger.Panic()
+ // should still call panic().
+ withLogger(t, FatalLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {
+ msg := "Even if output is disabled, logger.Panic should always panic."
+ assert.Panics(t, func() { logger.Panic("foo") }, msg)
+ assert.Panics(t, func() { logger.Log(PanicLevel, "foo") }, msg)
+ assert.Panics(t, func() {
+ if ce := logger.Check(PanicLevel, "foo"); ce != nil {
+ ce.Write()
+ }
+ }, msg)
+ assert.Equal(t, 0, logs.Len(), "Panics shouldn't be written out if PanicLevel is disabled.")
+ })
+}
+
+func TestLoggerAlwaysFatals(t *testing.T) {
+ // Users can disable writing out fatal-level logs, but calls to logger.Fatal()
+ // should still terminate the process.
+ withLogger(t, FatalLevel+1, nil, func(logger *Logger, logs *observer.ObservedLogs) {
+ stub := exit.WithStub(func() { logger.Fatal("") })
+ assert.True(t, stub.Exited, "Expected calls to logger.Fatal to terminate process.")
+
+ stub = exit.WithStub(func() { logger.Log(FatalLevel, "") })
+ assert.True(t, stub.Exited, "Expected calls to logger.Fatal to terminate process.")
+
+ stub = exit.WithStub(func() {
+ if ce := logger.Check(FatalLevel, ""); ce != nil {
+ ce.Write()
+ }
+ })
+ assert.True(t, stub.Exited, "Expected calls to logger.Check(FatalLevel, ...) to terminate process.")
+
+ assert.Equal(t, 0, logs.Len(), "Shouldn't write out logs when fatal-level logging is disabled.")
+ })
+}
+
+func TestLoggerDPanic(t *testing.T) {
+ withLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {
+ assert.NotPanics(t, func() { logger.DPanic("") })
+ assert.Equal(
+ t,
+ []observer.LoggedEntry{{Entry: zapcore.Entry{Level: DPanicLevel}, Context: []Field{}}},
+ logs.AllUntimed(),
+ "Unexpected log output from DPanic in production mode.",
+ )
+ })
+ withLogger(t, DebugLevel, opts(Development()), func(logger *Logger, logs *observer.ObservedLogs) {
+ assert.Panics(t, func() { logger.DPanic("") })
+ assert.Equal(
+ t,
+ []observer.LoggedEntry{{Entry: zapcore.Entry{Level: DPanicLevel}, Context: []Field{}}},
+ logs.AllUntimed(),
+ "Unexpected log output from DPanic in development mode.",
+ )
+ })
+}
+
+func TestLoggerNoOpsDisabledLevels(t *testing.T) {
+ withLogger(t, WarnLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {
+ logger.Info("silence!")
+ assert.Equal(
+ t,
+ []observer.LoggedEntry{},
+ logs.AllUntimed(),
+ "Expected logging at a disabled level to produce no output.",
+ )
+ })
+}
+
+func TestLoggerNames(t *testing.T) {
+ tests := []struct {
+ names []string
+ expected string
+ }{
+ {nil, ""},
+ {[]string{""}, ""},
+ {[]string{"foo"}, "foo"},
+ {[]string{"foo", ""}, "foo"},
+ {[]string{"foo", "bar"}, "foo.bar"},
+ {[]string{"foo.bar", "baz"}, "foo.bar.baz"},
+ // Garbage in, garbage out.
+ {[]string{"foo.", "bar"}, "foo..bar"},
+ {[]string{"foo", ".bar"}, "foo..bar"},
+ {[]string{"foo.", ".bar"}, "foo...bar"},
+ }
+
+ for _, tt := range tests {
+ withLogger(t, DebugLevel, nil, func(log *Logger, logs *observer.ObservedLogs) {
+ for _, n := range tt.names {
+ log = log.Named(n)
+ }
+ log.Info("")
+ require.Equal(t, 1, logs.Len(), "Expected only one log entry to be written.")
+ assert.Equal(t, tt.expected, logs.AllUntimed()[0].LoggerName, "Unexpected logger name from entry.")
+ assert.Equal(t, tt.expected, log.Name(), "Unexpected logger name.")
+ })
+ withSugar(t, DebugLevel, nil, func(log *SugaredLogger, logs *observer.ObservedLogs) {
+ for _, n := range tt.names {
+ log = log.Named(n)
+ }
+ log.Infow("")
+ require.Equal(t, 1, logs.Len(), "Expected only one log entry to be written.")
+ assert.Equal(t, tt.expected, logs.AllUntimed()[0].LoggerName, "Unexpected logger name from entry.")
+ assert.Equal(t, tt.expected, log.base.Name(), "Unexpected logger name.")
+ })
+ }
+}
+
+func TestLoggerWriteFailure(t *testing.T) {
+ errSink := &ztest.Buffer{}
+ logger := New(
+ zapcore.NewCore(
+ zapcore.NewJSONEncoder(NewProductionConfig().EncoderConfig),
+ zapcore.Lock(zapcore.AddSync(ztest.FailWriter{})),
+ DebugLevel,
+ ),
+ ErrorOutput(errSink),
+ )
+
+ logger.Info("foo")
+ // Should log the error.
+ assert.Regexp(t, `write error: failed`, errSink.Stripped(), "Expected to log the error to the error output.")
+ assert.True(t, errSink.Called(), "Expected logging an internal error to call Sync the error sink.")
+}
+
+func TestLoggerSync(t *testing.T) {
+ withLogger(t, DebugLevel, nil, func(logger *Logger, _ *observer.ObservedLogs) {
+ assert.NoError(t, logger.Sync(), "Expected syncing a test logger to succeed.")
+ assert.NoError(t, logger.Sugar().Sync(), "Expected syncing a sugared logger to succeed.")
+ })
+}
+
+func TestLoggerSyncFail(t *testing.T) {
+ noSync := &ztest.Buffer{}
+ err := errors.New("fail")
+ noSync.SetError(err)
+ logger := New(zapcore.NewCore(
+ zapcore.NewJSONEncoder(zapcore.EncoderConfig{}),
+ noSync,
+ DebugLevel,
+ ))
+ assert.Equal(t, err, logger.Sync(), "Expected Logger.Sync to propagate errors.")
+ assert.Equal(t, err, logger.Sugar().Sync(), "Expected SugaredLogger.Sync to propagate errors.")
+}
+
+func TestLoggerAddCaller(t *testing.T) {
+ tests := []struct {
+ options []Option
+ pat string
+ }{
+ {opts(), `^undefined$`},
+ {opts(WithCaller(false)), `^undefined$`},
+ {opts(AddCaller()), `.+/logger_test.go:[\d]+$`},
+ {opts(AddCaller(), WithCaller(false)), `^undefined$`},
+ {opts(WithCaller(true)), `.+/logger_test.go:[\d]+$`},
+ {opts(WithCaller(true), WithCaller(false)), `^undefined$`},
+ {opts(AddCaller(), AddCallerSkip(1), AddCallerSkip(-1)), `.+/logger_test.go:[\d]+$`},
+ {opts(AddCaller(), AddCallerSkip(1)), `.+/common_test.go:[\d]+$`},
+ {opts(AddCaller(), AddCallerSkip(1), AddCallerSkip(3)), `.+/src/runtime/.*:[\d]+$`},
+ }
+ for _, tt := range tests {
+ withLogger(t, DebugLevel, tt.options, func(logger *Logger, logs *observer.ObservedLogs) {
+ // Make sure that sugaring and desugaring resets caller skip properly.
+ logger = logger.Sugar().Desugar()
+ logger.Info("")
+ output := logs.AllUntimed()
+ assert.Equal(t, 1, len(output), "Unexpected number of logs written out.")
+ assert.Regexp(
+ t,
+ tt.pat,
+ output[0].Caller,
+ "Expected to find package name and file name in output.",
+ )
+ })
+ }
+}
+
+func TestLoggerAddCallerFunction(t *testing.T) {
+ tests := []struct {
+ options []Option
+ loggerFunction string
+ sugaredFunction string
+ }{
+ {
+ options: opts(),
+ loggerFunction: "",
+ sugaredFunction: "",
+ },
+ {
+ options: opts(WithCaller(false)),
+ loggerFunction: "",
+ sugaredFunction: "",
+ },
+ {
+ options: opts(AddCaller()),
+ loggerFunction: "go.uber.org/zap.infoLog",
+ sugaredFunction: "go.uber.org/zap.infoLogSugared",
+ },
+ {
+ options: opts(AddCaller(), WithCaller(false)),
+ loggerFunction: "",
+ sugaredFunction: "",
+ },
+ {
+ options: opts(WithCaller(true)),
+ loggerFunction: "go.uber.org/zap.infoLog",
+ sugaredFunction: "go.uber.org/zap.infoLogSugared",
+ },
+ {
+ options: opts(WithCaller(true), WithCaller(false)),
+ loggerFunction: "",
+ sugaredFunction: "",
+ },
+ {
+ options: opts(AddCaller(), AddCallerSkip(1), AddCallerSkip(-1)),
+ loggerFunction: "go.uber.org/zap.infoLog",
+ sugaredFunction: "go.uber.org/zap.infoLogSugared",
+ },
+ {
+ options: opts(AddCaller(), AddCallerSkip(2)),
+ loggerFunction: "go.uber.org/zap.withLogger",
+ sugaredFunction: "go.uber.org/zap.withLogger",
+ },
+ {
+ options: opts(AddCaller(), AddCallerSkip(2), AddCallerSkip(3)),
+ loggerFunction: "runtime.goexit",
+ sugaredFunction: "runtime.goexit",
+ },
+ }
+ for _, tt := range tests {
+ withLogger(t, DebugLevel, tt.options, func(logger *Logger, logs *observer.ObservedLogs) {
+ // Make sure that sugaring and desugaring resets caller skip properly.
+ logger = logger.Sugar().Desugar()
+ infoLog(logger, "")
+ infoLogSugared(logger.Sugar(), "")
+ infoLog(logger.Sugar().Desugar(), "")
+
+ entries := logs.AllUntimed()
+ assert.Equal(t, 3, len(entries), "Unexpected number of logs written out.")
+ for _, entry := range []observer.LoggedEntry{entries[0], entries[2]} {
+ assert.Regexp(
+ t,
+ tt.loggerFunction,
+ entry.Caller.Function,
+ "Expected to find function name in output.",
+ )
+ }
+ assert.Regexp(
+ t,
+ tt.sugaredFunction,
+ entries[1].Caller.Function,
+ "Expected to find function name in output.",
+ )
+ })
+ }
+}
+
+func TestLoggerAddCallerFail(t *testing.T) {
+ errBuf := &ztest.Buffer{}
+ withLogger(t, DebugLevel, opts(AddCaller(), AddCallerSkip(1e3), ErrorOutput(errBuf)), func(log *Logger, logs *observer.ObservedLogs) {
+ log.Info("Failure.")
+ assert.Regexp(
+ t,
+ `Logger.check error: failed to get caller`,
+ errBuf.String(),
+ "Didn't find expected failure message.",
+ )
+ assert.Equal(
+ t,
+ logs.AllUntimed()[0].Message,
+ "Failure.",
+ "Expected original message to survive failures in runtime.Caller.")
+ assert.Equal(
+ t,
+ logs.AllUntimed()[0].Caller.Function,
+ "",
+ "Expected function name to be empty string.")
+ })
+}
+
+func TestLoggerReplaceCore(t *testing.T) {
+ replace := WrapCore(func(zapcore.Core) zapcore.Core {
+ return zapcore.NewNopCore()
+ })
+ withLogger(t, DebugLevel, opts(replace), func(logger *Logger, logs *observer.ObservedLogs) {
+ logger.Debug("")
+ logger.Info("")
+ logger.Warn("")
+ assert.Equal(t, 0, logs.Len(), "Expected no-op core to write no logs.")
+ })
+}
+
+func TestLoggerIncreaseLevel(t *testing.T) {
+ withLogger(t, DebugLevel, opts(IncreaseLevel(WarnLevel)), func(logger *Logger, logs *observer.ObservedLogs) {
+ logger.Info("logger.Info")
+ logger.Warn("logger.Warn")
+ logger.Error("logger.Error")
+ require.Equal(t, 2, logs.Len(), "expected only warn + error logs due to IncreaseLevel.")
+ assert.Equal(
+ t,
+ logs.AllUntimed()[0].Message,
+ "logger.Warn",
+ "Expected first logged message to be warn level message",
+ )
+ })
+}
+
+func TestLoggerHooks(t *testing.T) {
+ hook, seen := makeCountingHook()
+ withLogger(t, DebugLevel, opts(Hooks(hook)), func(logger *Logger, logs *observer.ObservedLogs) {
+ logger.Debug("")
+ logger.Info("")
+ })
+ assert.Equal(t, int64(2), seen.Load(), "Hook saw an unexpected number of logs.")
+}
+
+func TestLoggerConcurrent(t *testing.T) {
+ withLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {
+ child := logger.With(String("foo", "bar"))
+
+ wg := &sync.WaitGroup{}
+ runConcurrently(5, 10, wg, func() {
+ logger.Info("", String("foo", "bar"))
+ })
+ runConcurrently(5, 10, wg, func() {
+ child.Info("")
+ })
+
+ wg.Wait()
+
+ // Make sure the output doesn't contain interspersed entries.
+ assert.Equal(t, 100, logs.Len(), "Unexpected number of logs written out.")
+ for _, obs := range logs.AllUntimed() {
+ assert.Equal(
+ t,
+ observer.LoggedEntry{
+ Entry: zapcore.Entry{Level: InfoLevel},
+ Context: []Field{String("foo", "bar")},
+ },
+ obs,
+ "Unexpected log output.",
+ )
+ }
+ })
+}
+
+func TestLoggerFatalOnNoop(t *testing.T) {
+ exitStub := exit.Stub()
+ defer exitStub.Unstub()
+ core, _ := observer.New(InfoLevel)
+
+ // We don't allow a no-op fatal hook.
+ New(core, WithFatalHook(zapcore.WriteThenNoop)).Fatal("great sadness")
+ assert.True(t, exitStub.Exited, "must exit for WriteThenNoop")
+ assert.Equal(t, 1, exitStub.Code, "must exit with status 1 for WriteThenNoop")
+}
+
+func TestLoggerCustomOnFatal(t *testing.T) {
+ tests := []struct {
+ msg string
+ onFatal zapcore.CheckWriteAction
+ recoverValue interface{}
+ }{
+ {
+ msg: "panic",
+ onFatal: zapcore.WriteThenPanic,
+ recoverValue: "fatal",
+ },
+ {
+ msg: "goexit",
+ onFatal: zapcore.WriteThenGoexit,
+ recoverValue: nil,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.msg, func(t *testing.T) {
+ withLogger(t, InfoLevel, opts(OnFatal(tt.onFatal)), func(logger *Logger, logs *observer.ObservedLogs) {
+ var finished bool
+ recovered := make(chan interface{})
+ go func() {
+ defer func() {
+ recovered <- recover()
+ }()
+
+ logger.Fatal("fatal")
+ finished = true
+ }()
+
+ assert.Equal(t, tt.recoverValue, <-recovered, "unexpected value from recover()")
+ assert.False(t, finished, "expect goroutine to not finish after Fatal")
+
+ assert.Equal(t, []observer.LoggedEntry{{
+ Entry: zapcore.Entry{Level: FatalLevel, Message: "fatal"},
+ Context: []Field{},
+ }}, logs.AllUntimed(), "unexpected logs")
+ })
+ })
+ }
+}
+
+type customWriteHook struct {
+ called bool
+}
+
+func (h *customWriteHook) OnWrite(_ *zapcore.CheckedEntry, _ []Field) {
+ h.called = true
+}
+
+func TestLoggerWithFatalHook(t *testing.T) {
+ var h customWriteHook
+ withLogger(t, InfoLevel, opts(WithFatalHook(&h)), func(logger *Logger, logs *observer.ObservedLogs) {
+ logger.Fatal("great sadness")
+ assert.True(t, h.called)
+ assert.Equal(t, 1, logs.FilterLevelExact(FatalLevel).Len())
+ })
+}
+
+func TestNopLogger(t *testing.T) {
+ logger := NewNop()
+
+ t.Run("basic levels", func(t *testing.T) {
+ logger.Debug("foo", String("k", "v"))
+ logger.Info("bar", Int("x", 42))
+ logger.Warn("baz", Strings("ks", []string{"a", "b"}))
+ logger.Error("qux", Error(errors.New("great sadness")))
+ })
+
+ t.Run("DPanic", func(t *testing.T) {
+ logger.With(String("component", "whatever")).DPanic("stuff")
+ })
+
+ t.Run("Panic", func(t *testing.T) {
+ assert.Panics(t, func() {
+ logger.Panic("great sadness")
+ }, "Nop logger should still cause panics.")
+ })
+}
+
+func TestMust(t *testing.T) {
+ t.Run("must without an error does not panic", func(t *testing.T) {
+ assert.NotPanics(t, func() { Must(NewNop(), nil) }, "must paniced with no error")
+ })
+
+ t.Run("must with an error panics", func(t *testing.T) {
+ assert.Panics(t, func() { Must(nil, errors.New("an error")) }, "must did not panic with an error")
+ })
+}
+
+func infoLog(logger *Logger, msg string, fields ...Field) {
+ logger.Info(msg, fields...)
+}
+
+func infoLogSugared(logger *SugaredLogger, args ...interface{}) {
+ logger.Info(args...)
+}
diff --git a/vendor/go.uber.org/zap/options.go b/vendor/go.uber.org/zap/options.go
new file mode 100644
index 0000000000..c4f3bca3d2
--- /dev/null
+++ b/vendor/go.uber.org/zap/options.go
@@ -0,0 +1,167 @@
+// 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 zap
+
+import (
+ "fmt"
+
+ "go.uber.org/zap/zapcore"
+)
+
+// An Option configures a Logger.
+type Option interface {
+ apply(*Logger)
+}
+
+// optionFunc wraps a func so it satisfies the Option interface.
+type optionFunc func(*Logger)
+
+func (f optionFunc) apply(log *Logger) {
+ f(log)
+}
+
+// WrapCore wraps or replaces the Logger's underlying zapcore.Core.
+func WrapCore(f func(zapcore.Core) zapcore.Core) Option {
+ return optionFunc(func(log *Logger) {
+ log.core = f(log.core)
+ })
+}
+
+// Hooks registers functions which will be called each time the Logger writes
+// out an Entry. Repeated use of Hooks is additive.
+//
+// Hooks are useful for simple side effects, like capturing metrics for the
+// number of emitted logs. More complex side effects, including anything that
+// requires access to the Entry's structured fields, should be implemented as
+// a zapcore.Core instead. See zapcore.RegisterHooks for details.
+func Hooks(hooks ...func(zapcore.Entry) error) Option {
+ return optionFunc(func(log *Logger) {
+ log.core = zapcore.RegisterHooks(log.core, hooks...)
+ })
+}
+
+// Fields adds fields to the Logger.
+func Fields(fs ...Field) Option {
+ return optionFunc(func(log *Logger) {
+ log.core = log.core.With(fs)
+ })
+}
+
+// ErrorOutput sets the destination for errors generated by the Logger. Note
+// that this option only affects internal errors; for sample code that sends
+// error-level logs to a different location from info- and debug-level logs,
+// see the package-level AdvancedConfiguration example.
+//
+// The supplied WriteSyncer must be safe for concurrent use. The Open and
+// zapcore.Lock functions are the simplest ways to protect files with a mutex.
+func ErrorOutput(w zapcore.WriteSyncer) Option {
+ return optionFunc(func(log *Logger) {
+ log.errorOutput = w
+ })
+}
+
+// Development puts the logger in development mode, which makes DPanic-level
+// logs panic instead of simply logging an error.
+func Development() Option {
+ return optionFunc(func(log *Logger) {
+ log.development = true
+ })
+}
+
+// AddCaller configures the Logger to annotate each message with the filename,
+// line number, and function name of zap's caller. See also WithCaller.
+func AddCaller() Option {
+ return WithCaller(true)
+}
+
+// WithCaller configures the Logger to annotate each message with the filename,
+// line number, and function name of zap's caller, or not, depending on the
+// value of enabled. This is a generalized form of AddCaller.
+func WithCaller(enabled bool) Option {
+ return optionFunc(func(log *Logger) {
+ log.addCaller = enabled
+ })
+}
+
+// AddCallerSkip increases the number of callers skipped by caller annotation
+// (as enabled by the AddCaller option). When building wrappers around the
+// Logger and SugaredLogger, supplying this Option prevents zap from always
+// reporting the wrapper code as the caller.
+func AddCallerSkip(skip int) Option {
+ return optionFunc(func(log *Logger) {
+ log.callerSkip += skip
+ })
+}
+
+// AddStacktrace configures the Logger to record a stack trace for all messages at
+// or above a given level.
+func AddStacktrace(lvl zapcore.LevelEnabler) Option {
+ return optionFunc(func(log *Logger) {
+ log.addStack = lvl
+ })
+}
+
+// IncreaseLevel increase the level of the logger. It has no effect if
+// the passed in level tries to decrease the level of the logger.
+func IncreaseLevel(lvl zapcore.LevelEnabler) Option {
+ return optionFunc(func(log *Logger) {
+ core, err := zapcore.NewIncreaseLevelCore(log.core, lvl)
+ if err != nil {
+ fmt.Fprintf(log.errorOutput, "failed to IncreaseLevel: %v\n", err)
+ } else {
+ log.core = core
+ }
+ })
+}
+
+// OnFatal sets the action to take on fatal logs.
+//
+// Deprecated: Use [WithFatalHook] instead.
+func OnFatal(action zapcore.CheckWriteAction) Option {
+ return WithFatalHook(action)
+}
+
+// WithFatalHook sets a CheckWriteHook to run on fatal logs.
+// Zap will call this hook after writing a log statement with a Fatal level.
+//
+// For example, the following builds a logger that will exit the current
+// goroutine after writing a fatal log message, but it will not exit the
+// program.
+//
+// zap.New(core, zap.WithFatalHook(zapcore.WriteThenGoexit))
+//
+// It is important that the provided CheckWriteHook stops the control flow at
+// the current statement to meet expectations of callers of the logger.
+// We recommend calling os.Exit or runtime.Goexit inside custom hooks at
+// minimum.
+func WithFatalHook(hook zapcore.CheckWriteHook) Option {
+ return optionFunc(func(log *Logger) {
+ log.onFatal = hook
+ })
+}
+
+// WithClock specifies the clock used by the logger to determine the current
+// time for logged entries. Defaults to the system clock with time.Now.
+func WithClock(clock zapcore.Clock) Option {
+ return optionFunc(func(log *Logger) {
+ log.clock = clock
+ })
+}
diff --git a/vendor/go.uber.org/zap/sink.go b/vendor/go.uber.org/zap/sink.go
new file mode 100644
index 0000000000..499772a00d
--- /dev/null
+++ b/vendor/go.uber.org/zap/sink.go
@@ -0,0 +1,180 @@
+// 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 zap
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "net/url"
+ "os"
+ "path/filepath"
+ "strings"
+ "sync"
+
+ "go.uber.org/zap/zapcore"
+)
+
+const schemeFile = "file"
+
+var _sinkRegistry = newSinkRegistry()
+
+// Sink defines the interface to write to and close logger destinations.
+type Sink interface {
+ zapcore.WriteSyncer
+ io.Closer
+}
+
+type errSinkNotFound struct {
+ scheme string
+}
+
+func (e *errSinkNotFound) Error() string {
+ return fmt.Sprintf("no sink found for scheme %q", e.scheme)
+}
+
+type nopCloserSink struct{ zapcore.WriteSyncer }
+
+func (nopCloserSink) Close() error { return nil }
+
+type sinkRegistry struct {
+ mu sync.Mutex
+ factories map[string]func(*url.URL) (Sink, error) // keyed by scheme
+ openFile func(string, int, os.FileMode) (*os.File, error) // type matches os.OpenFile
+}
+
+func newSinkRegistry() *sinkRegistry {
+ sr := &sinkRegistry{
+ factories: make(map[string]func(*url.URL) (Sink, error)),
+ openFile: os.OpenFile,
+ }
+ // Infallible operation: the registry is empty, so we can't have a conflict.
+ _ = sr.RegisterSink(schemeFile, sr.newFileSinkFromURL)
+ return sr
+}
+
+// RegisterScheme registers the given factory for the specific scheme.
+func (sr *sinkRegistry) RegisterSink(scheme string, factory func(*url.URL) (Sink, error)) error {
+ sr.mu.Lock()
+ defer sr.mu.Unlock()
+
+ if scheme == "" {
+ return errors.New("can't register a sink factory for empty string")
+ }
+ normalized, err := normalizeScheme(scheme)
+ if err != nil {
+ return fmt.Errorf("%q is not a valid scheme: %v", scheme, err)
+ }
+ if _, ok := sr.factories[normalized]; ok {
+ return fmt.Errorf("sink factory already registered for scheme %q", normalized)
+ }
+ sr.factories[normalized] = factory
+ return nil
+}
+
+func (sr *sinkRegistry) newSink(rawURL string) (Sink, error) {
+ // URL parsing doesn't work well for Windows paths such as `c:\log.txt`, as scheme is set to
+ // the drive, and path is unset unless `c:/log.txt` is used.
+ // To avoid Windows-specific URL handling, we instead check IsAbs to open as a file.
+ // filepath.IsAbs is OS-specific, so IsAbs('c:/log.txt') is false outside of Windows.
+ if filepath.IsAbs(rawURL) {
+ return sr.newFileSinkFromPath(rawURL)
+ }
+
+ u, err := url.Parse(rawURL)
+ if err != nil {
+ return nil, fmt.Errorf("can't parse %q as a URL: %v", rawURL, err)
+ }
+ if u.Scheme == "" {
+ u.Scheme = schemeFile
+ }
+
+ sr.mu.Lock()
+ factory, ok := sr.factories[u.Scheme]
+ sr.mu.Unlock()
+ if !ok {
+ return nil, &errSinkNotFound{u.Scheme}
+ }
+ return factory(u)
+}
+
+// RegisterSink registers a user-supplied factory for all sinks with a
+// particular scheme.
+//
+// All schemes must be ASCII, valid under section 0.1 of RFC 3986
+// (https://tools.ietf.org/html/rfc3983#section-3.1), and must not already
+// have a factory registered. Zap automatically registers a factory for the
+// "file" scheme.
+func RegisterSink(scheme string, factory func(*url.URL) (Sink, error)) error {
+ return _sinkRegistry.RegisterSink(scheme, factory)
+}
+
+func (sr *sinkRegistry) newFileSinkFromURL(u *url.URL) (Sink, error) {
+ if u.User != nil {
+ return nil, fmt.Errorf("user and password not allowed with file URLs: got %v", u)
+ }
+ if u.Fragment != "" {
+ return nil, fmt.Errorf("fragments not allowed with file URLs: got %v", u)
+ }
+ if u.RawQuery != "" {
+ return nil, fmt.Errorf("query parameters not allowed with file URLs: got %v", u)
+ }
+ // Error messages are better if we check hostname and port separately.
+ if u.Port() != "" {
+ return nil, fmt.Errorf("ports not allowed with file URLs: got %v", u)
+ }
+ if hn := u.Hostname(); hn != "" && hn != "localhost" {
+ return nil, fmt.Errorf("file URLs must leave host empty or use localhost: got %v", u)
+ }
+
+ return sr.newFileSinkFromPath(u.Path)
+}
+
+func (sr *sinkRegistry) newFileSinkFromPath(path string) (Sink, error) {
+ switch path {
+ case "stdout":
+ return nopCloserSink{os.Stdout}, nil
+ case "stderr":
+ return nopCloserSink{os.Stderr}, nil
+ }
+ return sr.openFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o666)
+}
+
+func normalizeScheme(s string) (string, error) {
+ // https://tools.ietf.org/html/rfc3986#section-3.1
+ s = strings.ToLower(s)
+ if first := s[0]; 'a' > first || 'z' < first {
+ return "", errors.New("must start with a letter")
+ }
+ for i := 1; i < len(s); i++ { // iterate over bytes, not runes
+ c := s[i]
+ switch {
+ case 'a' <= c && c <= 'z':
+ continue
+ case '0' <= c && c <= '9':
+ continue
+ case c == '.' || c == '+' || c == '-':
+ continue
+ }
+ return "", fmt.Errorf("may not contain %q", c)
+ }
+ return s, nil
+}
diff --git a/vendor/go.uber.org/zap/sink_test.go b/vendor/go.uber.org/zap/sink_test.go
new file mode 100644
index 0000000000..5fc37be918
--- /dev/null
+++ b/vendor/go.uber.org/zap/sink_test.go
@@ -0,0 +1,107 @@
+// 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 zap
+
+import (
+ "bytes"
+ "io"
+ "net/url"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ "go.uber.org/zap/zapcore"
+)
+
+func stubSinkRegistry(t testing.TB) *sinkRegistry {
+ origSinkRegistry := _sinkRegistry
+ t.Cleanup(func() {
+ _sinkRegistry = origSinkRegistry
+ })
+
+ r := newSinkRegistry()
+ _sinkRegistry = r
+ return r
+}
+
+func TestRegisterSink(t *testing.T) {
+ stubSinkRegistry(t)
+
+ const (
+ memScheme = "mem"
+ nopScheme = "no-op.1234"
+ )
+ var memCalls, nopCalls int
+
+ buf := bytes.NewBuffer(nil)
+ memFactory := func(u *url.URL) (Sink, error) {
+ assert.Equal(t, u.Scheme, memScheme, "Scheme didn't match registration.")
+ memCalls++
+ return nopCloserSink{zapcore.AddSync(buf)}, nil
+ }
+ nopFactory := func(u *url.URL) (Sink, error) {
+ assert.Equal(t, u.Scheme, nopScheme, "Scheme didn't match registration.")
+ nopCalls++
+ return nopCloserSink{zapcore.AddSync(io.Discard)}, nil
+ }
+
+ require.NoError(t, RegisterSink(strings.ToUpper(memScheme), memFactory), "Failed to register scheme %q.", memScheme)
+ require.NoError(t, RegisterSink(nopScheme, nopFactory), "Failed to register scheme %q.", nopScheme)
+
+ sink, closeSink, err := Open(
+ memScheme+"://somewhere",
+ nopScheme+"://somewhere-else",
+ )
+ require.NoError(t, err, "Unexpected error opening URLs with registered schemes.")
+ defer closeSink()
+
+ assert.Equal(t, 1, memCalls, "Unexpected number of calls to memory factory.")
+ assert.Equal(t, 1, nopCalls, "Unexpected number of calls to no-op factory.")
+
+ _, err = sink.Write([]byte("foo"))
+ assert.NoError(t, err, "Failed to write to combined WriteSyncer.")
+ assert.Equal(t, "foo", buf.String(), "Unexpected buffer contents.")
+}
+
+func TestRegisterSinkErrors(t *testing.T) {
+ nopFactory := func(_ *url.URL) (Sink, error) {
+ return nopCloserSink{zapcore.AddSync(io.Discard)}, nil
+ }
+ tests := []struct {
+ scheme string
+ err string
+ }{
+ {"", "empty string"},
+ {"FILE", "already registered"},
+ {"42", "not a valid scheme"},
+ {"http*", "not a valid scheme"},
+ }
+
+ for _, tt := range tests {
+ t.Run("scheme-"+tt.scheme, func(t *testing.T) {
+ r := newSinkRegistry()
+ err := r.RegisterSink(tt.scheme, nopFactory)
+ assert.ErrorContains(t, err, tt.err)
+ })
+ }
+}
diff --git a/vendor/go.uber.org/zap/sink_windows_test.go b/vendor/go.uber.org/zap/sink_windows_test.go
new file mode 100644
index 0000000000..fd6a475955
--- /dev/null
+++ b/vendor/go.uber.org/zap/sink_windows_test.go
@@ -0,0 +1,71 @@
+// Copyright (c) 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.
+
+//go:build windows
+
+package zap
+
+import (
+ "os"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestWindowsPaths(t *testing.T) {
+ // See https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats
+ tests := []struct {
+ msg string
+ path string
+ }{
+ {
+ msg: "local path with drive",
+ path: `c:\log.json`,
+ },
+ {
+ msg: "local path with drive using forward slash",
+ path: `c:/log.json`,
+ },
+ {
+ msg: "local path without drive",
+ path: `\Temp\log.json`,
+ },
+ {
+ msg: "unc path",
+ path: `\\Server2\Logs\log.json`,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.msg, func(t *testing.T) {
+ sr := newSinkRegistry()
+
+ openFilename := "<not called>"
+ sr.openFile = func(filename string, _ int, _ os.FileMode) (*os.File, error) {
+ openFilename = filename
+ return nil, assert.AnError
+ }
+
+ _, err := sr.newSink(tt.path)
+ assert.Equal(t, assert.AnError, err, "expect stub error from OpenFile")
+ assert.Equal(t, tt.path, openFilename, "unexpected path opened")
+ })
+ }
+}
diff --git a/vendor/go.uber.org/zap/sugar.go b/vendor/go.uber.org/zap/sugar.go
new file mode 100644
index 0000000000..00ac5fe3ac
--- /dev/null
+++ b/vendor/go.uber.org/zap/sugar.go
@@ -0,0 +1,437 @@
+// 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 zap
+
+import (
+ "fmt"
+
+ "go.uber.org/zap/zapcore"
+
+ "go.uber.org/multierr"
+)
+
+const (
+ _oddNumberErrMsg = "Ignored key without a value."
+ _nonStringKeyErrMsg = "Ignored key-value pairs with non-string keys."
+ _multipleErrMsg = "Multiple errors without a key."
+)
+
+// A SugaredLogger wraps the base Logger functionality in a slower, but less
+// verbose, API. Any Logger can be converted to a SugaredLogger with its Sugar
+// method.
+//
+// Unlike the Logger, the SugaredLogger doesn't insist on structured logging.
+// For each log level, it exposes four methods:
+//
+// - methods named after the log level for log.Print-style logging
+// - methods ending in "w" for loosely-typed structured logging
+// - methods ending in "f" for log.Printf-style logging
+// - methods ending in "ln" for log.Println-style logging
+//
+// For example, the methods for InfoLevel are:
+//
+// Info(...any) Print-style logging
+// Infow(...any) Structured logging (read as "info with")
+// Infof(string, ...any) Printf-style logging
+// Infoln(...any) Println-style logging
+type SugaredLogger struct {
+ base *Logger
+}
+
+// Desugar unwraps a SugaredLogger, exposing the original Logger. Desugaring
+// is quite inexpensive, so it's reasonable for a single application to use
+// both Loggers and SugaredLoggers, converting between them on the boundaries
+// of performance-sensitive code.
+func (s *SugaredLogger) Desugar() *Logger {
+ base := s.base.clone()
+ base.callerSkip -= 2
+ return base
+}
+
+// Named adds a sub-scope to the logger's name. See Logger.Named for details.
+func (s *SugaredLogger) Named(name string) *SugaredLogger {
+ return &SugaredLogger{base: s.base.Named(name)}
+}
+
+// WithOptions clones the current SugaredLogger, applies the supplied Options,
+// and returns the result. It's safe to use concurrently.
+func (s *SugaredLogger) WithOptions(opts ...Option) *SugaredLogger {
+ base := s.base.clone()
+ for _, opt := range opts {
+ opt.apply(base)
+ }
+ return &SugaredLogger{base: base}
+}
+
+// With adds a variadic number of fields to the logging context. It accepts a
+// mix of strongly-typed Field objects and loosely-typed key-value pairs. When
+// processing pairs, the first element of the pair is used as the field key
+// and the second as the field value.
+//
+// For example,
+//
+// sugaredLogger.With(
+// "hello", "world",
+// "failure", errors.New("oh no"),
+// Stack(),
+// "count", 42,
+// "user", User{Name: "alice"},
+// )
+//
+// is the equivalent of
+//
+// unsugared.With(
+// String("hello", "world"),
+// String("failure", "oh no"),
+// Stack(),
+// Int("count", 42),
+// Object("user", User{Name: "alice"}),
+// )
+//
+// Note that the keys in key-value pairs should be strings. In development,
+// passing a non-string key panics. In production, the logger is more
+// forgiving: a separate error is logged, but the key-value pair is skipped
+// and execution continues. Passing an orphaned key triggers similar behavior:
+// panics in development and errors in production.
+func (s *SugaredLogger) With(args ...interface{}) *SugaredLogger {
+ return &SugaredLogger{base: s.base.With(s.sweetenFields(args)...)}
+}
+
+// Level reports the minimum enabled level for this logger.
+//
+// For NopLoggers, this is [zapcore.InvalidLevel].
+func (s *SugaredLogger) Level() zapcore.Level {
+ return zapcore.LevelOf(s.base.core)
+}
+
+// Debug logs the provided arguments at [DebugLevel].
+// Spaces are added between arguments when neither is a string.
+func (s *SugaredLogger) Debug(args ...interface{}) {
+ s.log(DebugLevel, "", args, nil)
+}
+
+// Info logs the provided arguments at [InfoLevel].
+// Spaces are added between arguments when neither is a string.
+func (s *SugaredLogger) Info(args ...interface{}) {
+ s.log(InfoLevel, "", args, nil)
+}
+
+// Warn logs the provided arguments at [WarnLevel].
+// Spaces are added between arguments when neither is a string.
+func (s *SugaredLogger) Warn(args ...interface{}) {
+ s.log(WarnLevel, "", args, nil)
+}
+
+// Error logs the provided arguments at [ErrorLevel].
+// Spaces are added between arguments when neither is a string.
+func (s *SugaredLogger) Error(args ...interface{}) {
+ s.log(ErrorLevel, "", args, nil)
+}
+
+// DPanic logs the provided arguments at [DPanicLevel].
+// In development, the logger then panics. (See [DPanicLevel] for details.)
+// Spaces are added between arguments when neither is a string.
+func (s *SugaredLogger) DPanic(args ...interface{}) {
+ s.log(DPanicLevel, "", args, nil)
+}
+
+// Panic constructs a message with the provided arguments and panics.
+// Spaces are added between arguments when neither is a string.
+func (s *SugaredLogger) Panic(args ...interface{}) {
+ s.log(PanicLevel, "", args, nil)
+}
+
+// Fatal constructs a message with the provided arguments and calls os.Exit.
+// Spaces are added between arguments when neither is a string.
+func (s *SugaredLogger) Fatal(args ...interface{}) {
+ s.log(FatalLevel, "", args, nil)
+}
+
+// Debugf formats the message according to the format specifier
+// and logs it at [DebugLevel].
+func (s *SugaredLogger) Debugf(template string, args ...interface{}) {
+ s.log(DebugLevel, template, args, nil)
+}
+
+// Infof formats the message according to the format specifier
+// and logs it at [InfoLevel].
+func (s *SugaredLogger) Infof(template string, args ...interface{}) {
+ s.log(InfoLevel, template, args, nil)
+}
+
+// Warnf formats the message according to the format specifier
+// and logs it at [WarnLevel].
+func (s *SugaredLogger) Warnf(template string, args ...interface{}) {
+ s.log(WarnLevel, template, args, nil)
+}
+
+// Errorf formats the message according to the format specifier
+// and logs it at [ErrorLevel].
+func (s *SugaredLogger) Errorf(template string, args ...interface{}) {
+ s.log(ErrorLevel, template, args, nil)
+}
+
+// DPanicf formats the message according to the format specifier
+// and logs it at [DPanicLevel].
+// In development, the logger then panics. (See [DPanicLevel] for details.)
+func (s *SugaredLogger) DPanicf(template string, args ...interface{}) {
+ s.log(DPanicLevel, template, args, nil)
+}
+
+// Panicf formats the message according to the format specifier
+// and panics.
+func (s *SugaredLogger) Panicf(template string, args ...interface{}) {
+ s.log(PanicLevel, template, args, nil)
+}
+
+// Fatalf formats the message according to the format specifier
+// and calls os.Exit.
+func (s *SugaredLogger) Fatalf(template string, args ...interface{}) {
+ s.log(FatalLevel, template, args, nil)
+}
+
+// Debugw logs a message with some additional context. The variadic key-value
+// pairs are treated as they are in With.
+//
+// When debug-level logging is disabled, this is much faster than
+//
+// s.With(keysAndValues).Debug(msg)
+func (s *SugaredLogger) Debugw(msg string, keysAndValues ...interface{}) {
+ s.log(DebugLevel, msg, nil, keysAndValues)
+}
+
+// Infow logs a message with some additional context. The variadic key-value
+// pairs are treated as they are in With.
+func (s *SugaredLogger) Infow(msg string, keysAndValues ...interface{}) {
+ s.log(InfoLevel, msg, nil, keysAndValues)
+}
+
+// Warnw logs a message with some additional context. The variadic key-value
+// pairs are treated as they are in With.
+func (s *SugaredLogger) Warnw(msg string, keysAndValues ...interface{}) {
+ s.log(WarnLevel, msg, nil, keysAndValues)
+}
+
+// Errorw logs a message with some additional context. The variadic key-value
+// pairs are treated as they are in With.
+func (s *SugaredLogger) Errorw(msg string, keysAndValues ...interface{}) {
+ s.log(ErrorLevel, msg, nil, keysAndValues)
+}
+
+// DPanicw logs a message with some additional context. In development, the
+// logger then panics. (See DPanicLevel for details.) The variadic key-value
+// pairs are treated as they are in With.
+func (s *SugaredLogger) DPanicw(msg string, keysAndValues ...interface{}) {
+ s.log(DPanicLevel, msg, nil, keysAndValues)
+}
+
+// Panicw logs a message with some additional context, then panics. The
+// variadic key-value pairs are treated as they are in With.
+func (s *SugaredLogger) Panicw(msg string, keysAndValues ...interface{}) {
+ s.log(PanicLevel, msg, nil, keysAndValues)
+}
+
+// Fatalw logs a message with some additional context, then calls os.Exit. The
+// variadic key-value pairs are treated as they are in With.
+func (s *SugaredLogger) Fatalw(msg string, keysAndValues ...interface{}) {
+ s.log(FatalLevel, msg, nil, keysAndValues)
+}
+
+// Debugln logs a message at [DebugLevel].
+// Spaces are always added between arguments.
+func (s *SugaredLogger) Debugln(args ...interface{}) {
+ s.logln(DebugLevel, args, nil)
+}
+
+// Infoln logs a message at [InfoLevel].
+// Spaces are always added between arguments.
+func (s *SugaredLogger) Infoln(args ...interface{}) {
+ s.logln(InfoLevel, args, nil)
+}
+
+// Warnln logs a message at [WarnLevel].
+// Spaces are always added between arguments.
+func (s *SugaredLogger) Warnln(args ...interface{}) {
+ s.logln(WarnLevel, args, nil)
+}
+
+// Errorln logs a message at [ErrorLevel].
+// Spaces are always added between arguments.
+func (s *SugaredLogger) Errorln(args ...interface{}) {
+ s.logln(ErrorLevel, args, nil)
+}
+
+// DPanicln logs a message at [DPanicLevel].
+// In development, the logger then panics. (See [DPanicLevel] for details.)
+// Spaces are always added between arguments.
+func (s *SugaredLogger) DPanicln(args ...interface{}) {
+ s.logln(DPanicLevel, args, nil)
+}
+
+// Panicln logs a message at [PanicLevel] and panics.
+// Spaces are always added between arguments.
+func (s *SugaredLogger) Panicln(args ...interface{}) {
+ s.logln(PanicLevel, args, nil)
+}
+
+// Fatalln logs a message at [FatalLevel] and calls os.Exit.
+// Spaces are always added between arguments.
+func (s *SugaredLogger) Fatalln(args ...interface{}) {
+ s.logln(FatalLevel, args, nil)
+}
+
+// Sync flushes any buffered log entries.
+func (s *SugaredLogger) Sync() error {
+ return s.base.Sync()
+}
+
+// log message with Sprint, Sprintf, or neither.
+func (s *SugaredLogger) log(lvl zapcore.Level, template string, fmtArgs []interface{}, context []interface{}) {
+ // If logging at this level is completely disabled, skip the overhead of
+ // string formatting.
+ if lvl < DPanicLevel && !s.base.Core().Enabled(lvl) {
+ return
+ }
+
+ msg := getMessage(template, fmtArgs)
+ if ce := s.base.Check(lvl, msg); ce != nil {
+ ce.Write(s.sweetenFields(context)...)
+ }
+}
+
+// logln message with Sprintln
+func (s *SugaredLogger) logln(lvl zapcore.Level, fmtArgs []interface{}, context []interface{}) {
+ if lvl < DPanicLevel && !s.base.Core().Enabled(lvl) {
+ return
+ }
+
+ msg := getMessageln(fmtArgs)
+ if ce := s.base.Check(lvl, msg); ce != nil {
+ ce.Write(s.sweetenFields(context)...)
+ }
+}
+
+// getMessage format with Sprint, Sprintf, or neither.
+func getMessage(template string, fmtArgs []interface{}) string {
+ if len(fmtArgs) == 0 {
+ return template
+ }
+
+ if template != "" {
+ return fmt.Sprintf(template, fmtArgs...)
+ }
+
+ if len(fmtArgs) == 1 {
+ if str, ok := fmtArgs[0].(string); ok {
+ return str
+ }
+ }
+ return fmt.Sprint(fmtArgs...)
+}
+
+// getMessageln format with Sprintln.
+func getMessageln(fmtArgs []interface{}) string {
+ msg := fmt.Sprintln(fmtArgs...)
+ return msg[:len(msg)-1]
+}
+
+func (s *SugaredLogger) sweetenFields(args []interface{}) []Field {
+ if len(args) == 0 {
+ return nil
+ }
+
+ var (
+ // Allocate enough space for the worst case; if users pass only structured
+ // fields, we shouldn't penalize them with extra allocations.
+ fields = make([]Field, 0, len(args))
+ invalid invalidPairs
+ seenError bool
+ )
+
+ for i := 0; i < len(args); {
+ // This is a strongly-typed field. Consume it and move on.
+ if f, ok := args[i].(Field); ok {
+ fields = append(fields, f)
+ i++
+ continue
+ }
+
+ // If it is an error, consume it and move on.
+ if err, ok := args[i].(error); ok {
+ if !seenError {
+ seenError = true
+ fields = append(fields, Error(err))
+ } else {
+ s.base.Error(_multipleErrMsg, Error(err))
+ }
+ i++
+ continue
+ }
+
+ // Make sure this element isn't a dangling key.
+ if i == len(args)-1 {
+ s.base.Error(_oddNumberErrMsg, Any("ignored", args[i]))
+ break
+ }
+
+ // Consume this value and the next, treating them as a key-value pair. If the
+ // key isn't a string, add this pair to the slice of invalid pairs.
+ key, val := args[i], args[i+1]
+ if keyStr, ok := key.(string); !ok {
+ // Subsequent errors are likely, so allocate once up front.
+ if cap(invalid) == 0 {
+ invalid = make(invalidPairs, 0, len(args)/2)
+ }
+ invalid = append(invalid, invalidPair{i, key, val})
+ } else {
+ fields = append(fields, Any(keyStr, val))
+ }
+ i += 2
+ }
+
+ // If we encountered any invalid key-value pairs, log an error.
+ if len(invalid) > 0 {
+ s.base.Error(_nonStringKeyErrMsg, Array("invalid", invalid))
+ }
+ return fields
+}
+
+type invalidPair struct {
+ position int
+ key, value interface{}
+}
+
+func (p invalidPair) MarshalLogObject(enc zapcore.ObjectEncoder) error {
+ enc.AddInt64("position", int64(p.position))
+ Any("key", p.key).AddTo(enc)
+ Any("value", p.value).AddTo(enc)
+ return nil
+}
+
+type invalidPairs []invalidPair
+
+func (ps invalidPairs) MarshalLogArray(enc zapcore.ArrayEncoder) error {
+ var err error
+ for i := range ps {
+ err = multierr.Append(err, enc.AppendObject(ps[i]))
+ }
+ return err
+}
diff --git a/vendor/go.uber.org/zap/sugar_test.go b/vendor/go.uber.org/zap/sugar_test.go
new file mode 100644
index 0000000000..9e914ecf8a
--- /dev/null
+++ b/vendor/go.uber.org/zap/sugar_test.go
@@ -0,0 +1,515 @@
+// 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 zap
+
+import (
+ "errors"
+ "testing"
+
+ "go.uber.org/zap/internal/exit"
+ "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 TestSugarWith(t *testing.T) {
+ // Convenience functions to create expected error logs.
+ ignored := func(msg interface{}) observer.LoggedEntry {
+ return observer.LoggedEntry{
+ Entry: zapcore.Entry{Level: ErrorLevel, Message: _oddNumberErrMsg},
+ Context: []Field{Any("ignored", msg)},
+ }
+ }
+ nonString := func(pairs ...invalidPair) observer.LoggedEntry {
+ return observer.LoggedEntry{
+ Entry: zapcore.Entry{Level: ErrorLevel, Message: _nonStringKeyErrMsg},
+ Context: []Field{Array("invalid", invalidPairs(pairs))},
+ }
+ }
+ ignoredError := func(err error) observer.LoggedEntry {
+ return observer.LoggedEntry{
+ Entry: zapcore.Entry{Level: ErrorLevel, Message: _multipleErrMsg},
+ Context: []Field{Error(err)},
+ }
+ }
+
+ tests := []struct {
+ desc string
+ args []interface{}
+ expected []Field
+ errLogs []observer.LoggedEntry
+ }{
+ {
+ desc: "nil args",
+ args: nil,
+ expected: []Field{},
+ errLogs: nil,
+ },
+ {
+ desc: "empty slice of args",
+ args: []interface{}{},
+ expected: []Field{},
+ errLogs: nil,
+ },
+ {
+ desc: "just a dangling key",
+ args: []interface{}{"should ignore"},
+ expected: []Field{},
+ errLogs: []observer.LoggedEntry{ignored("should ignore")},
+ },
+ {
+ desc: "well-formed key-value pairs",
+ args: []interface{}{"foo", 42, "true", "bar"},
+ expected: []Field{Int("foo", 42), String("true", "bar")},
+ errLogs: nil,
+ },
+ {
+ desc: "just a structured field",
+ args: []interface{}{Int("foo", 42)},
+ expected: []Field{Int("foo", 42)},
+ errLogs: nil,
+ },
+ {
+ desc: "structured field and a dangling key",
+ args: []interface{}{Int("foo", 42), "dangling"},
+ expected: []Field{Int("foo", 42)},
+ errLogs: []observer.LoggedEntry{ignored("dangling")},
+ },
+ {
+ desc: "structured field and a dangling non-string key",
+ args: []interface{}{Int("foo", 42), 13},
+ expected: []Field{Int("foo", 42)},
+ errLogs: []observer.LoggedEntry{ignored(13)},
+ },
+ {
+ desc: "key-value pair and a dangling key",
+ args: []interface{}{"foo", 42, "dangling"},
+ expected: []Field{Int("foo", 42)},
+ errLogs: []observer.LoggedEntry{ignored("dangling")},
+ },
+ {
+ desc: "pairs, a structured field, and a dangling key",
+ args: []interface{}{"first", "field", Int("foo", 42), "baz", "quux", "dangling"},
+ expected: []Field{String("first", "field"), Int("foo", 42), String("baz", "quux")},
+ errLogs: []observer.LoggedEntry{ignored("dangling")},
+ },
+ {
+ desc: "one non-string key",
+ args: []interface{}{"foo", 42, true, "bar"},
+ expected: []Field{Int("foo", 42)},
+ errLogs: []observer.LoggedEntry{nonString(invalidPair{2, true, "bar"})},
+ },
+ {
+ desc: "pairs, structured fields, non-string keys, and a dangling key",
+ args: []interface{}{"foo", 42, true, "bar", Int("structure", 11), 42, "reversed", "baz", "quux", "dangling"},
+ expected: []Field{Int("foo", 42), Int("structure", 11), String("baz", "quux")},
+ errLogs: []observer.LoggedEntry{
+ ignored("dangling"),
+ nonString(invalidPair{2, true, "bar"}, invalidPair{5, 42, "reversed"}),
+ },
+ },
+ {
+ desc: "multiple errors",
+ args: []interface{}{errors.New("first"), errors.New("second"), errors.New("third")},
+ expected: []Field{Error(errors.New("first"))},
+ errLogs: []observer.LoggedEntry{
+ ignoredError(errors.New("second")),
+ ignoredError(errors.New("third")),
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {
+ logger.With(tt.args...).Info("")
+ output := logs.AllUntimed()
+ if len(tt.errLogs) > 0 {
+ for i := range tt.errLogs {
+ assert.Equal(t, tt.errLogs[i], output[i], "Unexpected error log at position %d for scenario %s.", i, tt.desc)
+ }
+ }
+ assert.Equal(t, len(tt.errLogs)+1, len(output), "Expected only one non-error message to be logged in scenario %s.", tt.desc)
+ assert.Equal(t, tt.expected, output[len(tt.errLogs)].Context, "Unexpected message context in scenario %s.", tt.desc)
+ })
+ }
+}
+
+func TestSugaredLoggerLevel(t *testing.T) {
+ levels := []zapcore.Level{
+ DebugLevel,
+ InfoLevel,
+ WarnLevel,
+ ErrorLevel,
+ DPanicLevel,
+ PanicLevel,
+ FatalLevel,
+ }
+
+ for _, lvl := range levels {
+ lvl := lvl
+ t.Run(lvl.String(), func(t *testing.T) {
+ t.Parallel()
+
+ core, _ := observer.New(lvl)
+ log := New(core).Sugar()
+ assert.Equal(t, lvl, log.Level())
+ })
+ }
+
+ t.Run("Nop", func(t *testing.T) {
+ t.Parallel()
+
+ assert.Equal(t, zapcore.InvalidLevel, NewNop().Sugar().Level())
+ })
+}
+
+func TestSugarFieldsInvalidPairs(t *testing.T) {
+ withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {
+ logger.With(42, "foo", []string{"bar"}, "baz").Info("")
+ output := logs.AllUntimed()
+
+ // Double-check that the actual message was logged.
+ require.Equal(t, 2, len(output), "Unexpected number of entries logged.")
+ require.Equal(t, observer.LoggedEntry{Context: []Field{}}, output[1], "Unexpected non-error log entry.")
+
+ // Assert that the error message's structured fields serialize properly.
+ require.Equal(t, 1, len(output[0].Context), "Expected one field in error entry context.")
+ enc := zapcore.NewMapObjectEncoder()
+ output[0].Context[0].AddTo(enc)
+ assert.Equal(t, []interface{}{
+ map[string]interface{}{"position": int64(0), "key": int64(42), "value": "foo"},
+ map[string]interface{}{"position": int64(2), "key": []interface{}{"bar"}, "value": "baz"},
+ }, enc.Fields["invalid"], "Unexpected output when logging invalid key-value pairs.")
+ })
+}
+
+func TestSugarStructuredLogging(t *testing.T) {
+ tests := []struct {
+ msg string
+ expectMsg string
+ }{
+ {"foo", "foo"},
+ {"", ""},
+ }
+
+ // Common to all test cases.
+ var (
+ err = errors.New("qux")
+ context = []interface{}{"foo", "bar"}
+ extra = []interface{}{err, "baz", false}
+ expectedFields = []Field{String("foo", "bar"), Error(err), Bool("baz", false)}
+ )
+
+ for _, tt := range tests {
+ withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {
+ logger.With(context...).Debugw(tt.msg, extra...)
+ logger.With(context...).Infow(tt.msg, extra...)
+ logger.With(context...).Warnw(tt.msg, extra...)
+ logger.With(context...).Errorw(tt.msg, extra...)
+ logger.With(context...).DPanicw(tt.msg, extra...)
+
+ expected := make([]observer.LoggedEntry, 5)
+ for i, lvl := range []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel} {
+ expected[i] = observer.LoggedEntry{
+ Entry: zapcore.Entry{Message: tt.expectMsg, Level: lvl},
+ Context: expectedFields,
+ }
+ }
+ assert.Equal(t, expected, logs.AllUntimed(), "Unexpected log output.")
+ })
+ }
+}
+
+func TestSugarConcatenatingLogging(t *testing.T) {
+ tests := []struct {
+ args []interface{}
+ expect string
+ }{
+ {[]interface{}{nil}, "<nil>"},
+ }
+
+ // Common to all test cases.
+ context := []interface{}{"foo", "bar"}
+ expectedFields := []Field{String("foo", "bar")}
+
+ for _, tt := range tests {
+ withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {
+ logger.With(context...).Debug(tt.args...)
+ logger.With(context...).Info(tt.args...)
+ logger.With(context...).Warn(tt.args...)
+ logger.With(context...).Error(tt.args...)
+ logger.With(context...).DPanic(tt.args...)
+
+ expected := make([]observer.LoggedEntry, 5)
+ for i, lvl := range []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel} {
+ expected[i] = observer.LoggedEntry{
+ Entry: zapcore.Entry{Message: tt.expect, Level: lvl},
+ Context: expectedFields,
+ }
+ }
+ assert.Equal(t, expected, logs.AllUntimed(), "Unexpected log output.")
+ })
+ }
+}
+
+func TestSugarTemplatedLogging(t *testing.T) {
+ tests := []struct {
+ format string
+ args []interface{}
+ expect string
+ }{
+ {"", nil, ""},
+ {"foo", nil, "foo"},
+ // If the user fails to pass a template, degrade to fmt.Sprint.
+ {"", []interface{}{"foo"}, "foo"},
+ }
+
+ // Common to all test cases.
+ context := []interface{}{"foo", "bar"}
+ expectedFields := []Field{String("foo", "bar")}
+
+ for _, tt := range tests {
+ withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {
+ logger.With(context...).Debugf(tt.format, tt.args...)
+ logger.With(context...).Infof(tt.format, tt.args...)
+ logger.With(context...).Warnf(tt.format, tt.args...)
+ logger.With(context...).Errorf(tt.format, tt.args...)
+ logger.With(context...).DPanicf(tt.format, tt.args...)
+
+ expected := make([]observer.LoggedEntry, 5)
+ for i, lvl := range []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel} {
+ expected[i] = observer.LoggedEntry{
+ Entry: zapcore.Entry{Message: tt.expect, Level: lvl},
+ Context: expectedFields,
+ }
+ }
+ assert.Equal(t, expected, logs.AllUntimed(), "Unexpected log output.")
+ })
+ }
+}
+
+func TestSugarLnLogging(t *testing.T) {
+ tests := []struct {
+ args []interface{}
+ expect string
+ }{
+ {nil, ""},
+ {[]interface{}{}, ""},
+ {[]interface{}{""}, ""},
+ {[]interface{}{"foo"}, "foo"},
+ {[]interface{}{"foo", "bar"}, "foo bar"},
+ }
+
+ // Common to all test cases.
+ context := []interface{}{"foo", "bar"}
+ expectedFields := []Field{String("foo", "bar")}
+
+ for _, tt := range tests {
+ withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {
+ logger.With(context...).Debugln(tt.args...)
+ logger.With(context...).Infoln(tt.args...)
+ logger.With(context...).Warnln(tt.args...)
+ logger.With(context...).Errorln(tt.args...)
+ logger.With(context...).DPanicln(tt.args...)
+
+ expected := make([]observer.LoggedEntry, 5)
+ for i, lvl := range []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel} {
+ expected[i] = observer.LoggedEntry{
+ Entry: zapcore.Entry{Message: tt.expect, Level: lvl},
+ Context: expectedFields,
+ }
+ }
+ assert.Equal(t, expected, logs.AllUntimed(), "Unexpected log output.")
+ })
+ }
+}
+
+func TestSugarLnLoggingIgnored(t *testing.T) {
+ withSugar(t, WarnLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {
+ logger.Infoln("hello")
+ assert.Zero(t, logs.Len(), "Expected zero log statements.")
+ })
+}
+
+func TestSugarPanicLogging(t *testing.T) {
+ tests := []struct {
+ loggerLevel zapcore.Level
+ f func(*SugaredLogger)
+ expectedMsg string
+ }{
+ {FatalLevel, func(s *SugaredLogger) { s.Panic("foo") }, ""},
+ {PanicLevel, func(s *SugaredLogger) { s.Panic("foo") }, "foo"},
+ {DebugLevel, func(s *SugaredLogger) { s.Panic("foo") }, "foo"},
+ {FatalLevel, func(s *SugaredLogger) { s.Panicf("%s", "foo") }, ""},
+ {PanicLevel, func(s *SugaredLogger) { s.Panicf("%s", "foo") }, "foo"},
+ {DebugLevel, func(s *SugaredLogger) { s.Panicf("%s", "foo") }, "foo"},
+ {FatalLevel, func(s *SugaredLogger) { s.Panicw("foo") }, ""},
+ {PanicLevel, func(s *SugaredLogger) { s.Panicw("foo") }, "foo"},
+ {DebugLevel, func(s *SugaredLogger) { s.Panicw("foo") }, "foo"},
+ {FatalLevel, func(s *SugaredLogger) { s.Panicln("foo") }, ""},
+ {PanicLevel, func(s *SugaredLogger) { s.Panicln("foo") }, "foo"},
+ {DebugLevel, func(s *SugaredLogger) { s.Panicln("foo") }, "foo"},
+ }
+
+ for _, tt := range tests {
+ withSugar(t, tt.loggerLevel, nil, func(sugar *SugaredLogger, logs *observer.ObservedLogs) {
+ assert.Panics(t, func() { tt.f(sugar) }, "Expected panic-level logger calls to panic.")
+ if tt.expectedMsg != "" {
+ assert.Equal(t, []observer.LoggedEntry{{
+ Context: []Field{},
+ Entry: zapcore.Entry{Message: tt.expectedMsg, Level: PanicLevel},
+ }}, logs.AllUntimed(), "Unexpected log output.")
+ } else {
+ assert.Equal(t, 0, logs.Len(), "Didn't expect any log output.")
+ }
+ })
+ }
+}
+
+func TestSugarFatalLogging(t *testing.T) {
+ tests := []struct {
+ loggerLevel zapcore.Level
+ f func(*SugaredLogger)
+ expectedMsg string
+ }{
+ {FatalLevel + 1, func(s *SugaredLogger) { s.Fatal("foo") }, ""},
+ {FatalLevel, func(s *SugaredLogger) { s.Fatal("foo") }, "foo"},
+ {DebugLevel, func(s *SugaredLogger) { s.Fatal("foo") }, "foo"},
+ {FatalLevel + 1, func(s *SugaredLogger) { s.Fatalf("%s", "foo") }, ""},
+ {FatalLevel, func(s *SugaredLogger) { s.Fatalf("%s", "foo") }, "foo"},
+ {DebugLevel, func(s *SugaredLogger) { s.Fatalf("%s", "foo") }, "foo"},
+ {FatalLevel + 1, func(s *SugaredLogger) { s.Fatalw("foo") }, ""},
+ {FatalLevel, func(s *SugaredLogger) { s.Fatalw("foo") }, "foo"},
+ {DebugLevel, func(s *SugaredLogger) { s.Fatalw("foo") }, "foo"},
+ {FatalLevel + 1, func(s *SugaredLogger) { s.Fatalln("foo") }, ""},
+ {FatalLevel, func(s *SugaredLogger) { s.Fatalln("foo") }, "foo"},
+ {DebugLevel, func(s *SugaredLogger) { s.Fatalln("foo") }, "foo"},
+ }
+
+ for _, tt := range tests {
+ withSugar(t, tt.loggerLevel, nil, func(sugar *SugaredLogger, logs *observer.ObservedLogs) {
+ stub := exit.WithStub(func() { tt.f(sugar) })
+ assert.True(t, stub.Exited, "Expected all calls to fatal logger methods to exit process.")
+ if tt.expectedMsg != "" {
+ assert.Equal(t, []observer.LoggedEntry{{
+ Context: []Field{},
+ Entry: zapcore.Entry{Message: tt.expectedMsg, Level: FatalLevel},
+ }}, logs.AllUntimed(), "Unexpected log output.")
+ } else {
+ assert.Equal(t, 0, logs.Len(), "Didn't expect any log output.")
+ }
+ })
+ }
+}
+
+func TestSugarAddCaller(t *testing.T) {
+ tests := []struct {
+ options []Option
+ pat string
+ }{
+ {opts(AddCaller()), `.+/sugar_test.go:[\d]+$`},
+ {opts(AddCaller(), AddCallerSkip(1), AddCallerSkip(-1)), `.+/sugar_test.go:[\d]+$`},
+ {opts(AddCaller(), AddCallerSkip(1)), `.+/common_test.go:[\d]+$`},
+ {opts(AddCaller(), AddCallerSkip(1), AddCallerSkip(5)), `.+/src/runtime/.*:[\d]+$`},
+ }
+ for _, tt := range tests {
+ withSugar(t, DebugLevel, tt.options, func(logger *SugaredLogger, logs *observer.ObservedLogs) {
+ logger.Info("")
+ output := logs.AllUntimed()
+ assert.Equal(t, 1, len(output), "Unexpected number of logs written out.")
+ assert.Regexp(
+ t,
+ tt.pat,
+ output[0].Caller,
+ "Expected to find package name and file name in output.",
+ )
+ })
+ }
+}
+
+func TestSugarAddCallerFail(t *testing.T) {
+ errBuf := &ztest.Buffer{}
+ withSugar(t, DebugLevel, opts(AddCaller(), AddCallerSkip(1e3), ErrorOutput(errBuf)), func(log *SugaredLogger, logs *observer.ObservedLogs) {
+ log.Info("Failure.")
+ assert.Regexp(
+ t,
+ `Logger.check error: failed to get caller`,
+ errBuf.String(),
+ "Didn't find expected failure message.",
+ )
+ assert.Equal(
+ t,
+ logs.AllUntimed()[0].Message,
+ "Failure.",
+ "Expected original message to survive failures in runtime.Caller.")
+ })
+}
+
+func TestSugarWithOptionsIncreaseLevel(t *testing.T) {
+ withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {
+ logger = logger.WithOptions(IncreaseLevel(WarnLevel))
+ logger.Info("logger.Info")
+ logger.Warn("logger.Warn")
+ logger.Error("logger.Error")
+ require.Equal(t, 2, logs.Len(), "expected only warn + error logs due to IncreaseLevel.")
+ assert.Equal(
+ t,
+ logs.AllUntimed()[0].Message,
+ "logger.Warn",
+ "Expected first logged message to be warn level message",
+ )
+ })
+}
+
+func TestSugarLnWithOptionsIncreaseLevel(t *testing.T) {
+ withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {
+ logger = logger.WithOptions(IncreaseLevel(WarnLevel))
+ logger.Infoln("logger.Infoln")
+ logger.Warnln("logger.Warnln")
+ logger.Errorln("logger.Errorln")
+ require.Equal(t, 2, logs.Len(), "expected only warn + error logs due to IncreaseLevel.")
+ assert.Equal(
+ t,
+ logs.AllUntimed()[0].Message,
+ "logger.Warnln",
+ "Expected first logged message to be warn level message",
+ )
+ })
+}
+
+func BenchmarkSugarSingleStrArg(b *testing.B) {
+ withSugar(b, InfoLevel, nil /* opts* */, func(log *SugaredLogger, logs *observer.ObservedLogs) {
+ for i := 0; i < b.N; i++ {
+ log.Info("hello world")
+ }
+ })
+}
+
+func BenchmarkLnSugarSingleStrArg(b *testing.B) {
+ withSugar(b, InfoLevel, nil /* opts* */, func(log *SugaredLogger, logs *observer.ObservedLogs) {
+ for i := 0; i < b.N; i++ {
+ log.Infoln("hello world")
+ }
+ })
+}
diff --git a/vendor/go.uber.org/zap/time.go b/vendor/go.uber.org/zap/time.go
new file mode 100644
index 0000000000..c5a1f16225
--- /dev/null
+++ b/vendor/go.uber.org/zap/time.go
@@ -0,0 +1,27 @@
+// 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 zap
+
+import "time"
+
+func timeToMillis(t time.Time) int64 {
+ return t.UnixNano() / int64(time.Millisecond)
+}
diff --git a/vendor/go.uber.org/zap/time_test.go b/vendor/go.uber.org/zap/time_test.go
new file mode 100644
index 0000000000..cb993ab1fb
--- /dev/null
+++ b/vendor/go.uber.org/zap/time_test.go
@@ -0,0 +1,42 @@
+// 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 zap
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestTimeToMillis(t *testing.T) {
+ tests := []struct {
+ t time.Time
+ stamp int64
+ }{
+ {t: time.Unix(0, 0), stamp: 0},
+ {t: time.Unix(1, 0), stamp: 1000},
+ {t: time.Unix(1, int64(500*time.Millisecond)), stamp: 1500},
+ }
+ for _, tt := range tests {
+ assert.Equal(t, tt.stamp, timeToMillis(tt.t), "Unexpected timestamp for time %v.", tt.t)
+ }
+}
diff --git a/vendor/go.uber.org/zap/writer.go b/vendor/go.uber.org/zap/writer.go
new file mode 100644
index 0000000000..06768c6791
--- /dev/null
+++ b/vendor/go.uber.org/zap/writer.go
@@ -0,0 +1,98 @@
+// 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 zap
+
+import (
+ "fmt"
+ "io"
+
+ "go.uber.org/zap/zapcore"
+
+ "go.uber.org/multierr"
+)
+
+// Open is a high-level wrapper that takes a variadic number of URLs, opens or
+// creates each of the specified resources, and combines them into a locked
+// WriteSyncer. It also returns any error encountered and a function to close
+// any opened files.
+//
+// Passing no URLs returns a no-op WriteSyncer. Zap handles URLs without a
+// scheme and URLs with the "file" scheme. Third-party code may register
+// factories for other schemes using RegisterSink.
+//
+// URLs with the "file" scheme must use absolute paths on the local
+// filesystem. No user, password, port, fragments, or query parameters are
+// allowed, and the hostname must be empty or "localhost".
+//
+// Since it's common to write logs to the local filesystem, URLs without a
+// scheme (e.g., "/var/log/foo.log") are treated as local file paths. Without
+// a scheme, the special paths "stdout" and "stderr" are interpreted as
+// os.Stdout and os.Stderr. When specified without a scheme, relative file
+// paths also work.
+func Open(paths ...string) (zapcore.WriteSyncer, func(), error) {
+ writers, closeAll, err := open(paths)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ writer := CombineWriteSyncers(writers...)
+ return writer, closeAll, nil
+}
+
+func open(paths []string) ([]zapcore.WriteSyncer, func(), error) {
+ writers := make([]zapcore.WriteSyncer, 0, len(paths))
+ closers := make([]io.Closer, 0, len(paths))
+ closeAll := func() {
+ for _, c := range closers {
+ _ = c.Close()
+ }
+ }
+
+ var openErr error
+ for _, path := range paths {
+ sink, err := _sinkRegistry.newSink(path)
+ if err != nil {
+ openErr = multierr.Append(openErr, fmt.Errorf("open sink %q: %w", path, err))
+ continue
+ }
+ writers = append(writers, sink)
+ closers = append(closers, sink)
+ }
+ if openErr != nil {
+ closeAll()
+ return nil, nil, openErr
+ }
+
+ return writers, closeAll, nil
+}
+
+// CombineWriteSyncers is a utility that combines multiple WriteSyncers into a
+// single, locked WriteSyncer. If no inputs are supplied, it returns a no-op
+// WriteSyncer.
+//
+// It's provided purely as a convenience; the result is no different from
+// using zapcore.NewMultiWriteSyncer and zapcore.Lock individually.
+func CombineWriteSyncers(writers ...zapcore.WriteSyncer) zapcore.WriteSyncer {
+ if len(writers) == 0 {
+ return zapcore.AddSync(io.Discard)
+ }
+ return zapcore.Lock(zapcore.NewMultiWriteSyncer(writers...))
+}
diff --git a/vendor/go.uber.org/zap/writer_test.go b/vendor/go.uber.org/zap/writer_test.go
new file mode 100644
index 0000000000..20e00b74bc
--- /dev/null
+++ b/vendor/go.uber.org/zap/writer_test.go
@@ -0,0 +1,266 @@
+// 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 zap
+
+import (
+ "errors"
+ "io"
+ "io/fs"
+ "net/url"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "go.uber.org/multierr"
+ "go.uber.org/zap/zapcore"
+)
+
+func TestOpenNoPaths(t *testing.T) {
+ ws, cleanup, err := Open()
+ defer cleanup()
+
+ assert.NoError(t, err, "Expected opening no paths to succeed.")
+ assert.Equal(
+ t,
+ zapcore.AddSync(io.Discard),
+ ws,
+ "Expected opening no paths to return a no-op WriteSyncer.",
+ )
+}
+
+func TestOpen(t *testing.T) {
+ tempName := filepath.Join(t.TempDir(), "test.log")
+ assert.False(t, fileExists(tempName))
+ require.True(t, filepath.IsAbs(tempName), "Expected absolute temp file path.")
+
+ tests := []struct {
+ msg string
+ paths []string
+ }{
+ {
+ msg: "stdout",
+ paths: []string{"stdout"},
+ },
+ {
+ msg: "stderr",
+ paths: []string{"stderr"},
+ },
+ {
+ msg: "temp file path only",
+ paths: []string{tempName},
+ },
+ {
+ msg: "temp file file scheme",
+ paths: []string{"file://" + tempName},
+ },
+ {
+ msg: "temp file with file scheme and host localhost",
+ paths: []string{"file://localhost" + tempName},
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.msg, func(t *testing.T) {
+ _, cleanup, err := Open(tt.paths...)
+ if err == nil {
+ defer cleanup()
+ }
+
+ assert.NoError(t, err, "Unexpected error opening paths %v.", tt.paths)
+ })
+ }
+
+ assert.True(t, fileExists(tempName))
+}
+
+func TestOpenPathsNotFound(t *testing.T) {
+ tempName := filepath.Join(t.TempDir(), "test.log")
+
+ tests := []struct {
+ msg string
+ paths []string
+ wantNotFoundPaths []string
+ }{
+ {
+ msg: "missing path",
+ paths: []string{"/foo/bar/baz"},
+ wantNotFoundPaths: []string{"/foo/bar/baz"},
+ },
+ {
+ msg: "missing file scheme url with host localhost",
+ paths: []string{"file://localhost/foo/bar/baz"},
+ wantNotFoundPaths: []string{"/foo/bar/baz"},
+ },
+ {
+ msg: "multiple paths",
+ paths: []string{"stdout", "/foo/bar/baz", tempName, "file:///baz/quux"},
+ wantNotFoundPaths: []string{
+ "/foo/bar/baz",
+ "/baz/quux",
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.msg, func(t *testing.T) {
+ _, cleanup, err := Open(tt.paths...)
+ if !assert.Error(t, err, "Open must fail.") {
+ cleanup()
+ return
+ }
+
+ errs := multierr.Errors(err)
+ require.Len(t, errs, len(tt.wantNotFoundPaths))
+ for i, err := range errs {
+ assert.ErrorIs(t, err, fs.ErrNotExist)
+ assert.ErrorContains(t, err, tt.wantNotFoundPaths[i], "missing path in error")
+ }
+ })
+ }
+}
+
+func TestOpenRelativePath(t *testing.T) {
+ const name = "test-relative-path.txt"
+
+ require.False(t, fileExists(name), "Test file already exists.")
+ s, cleanup, err := Open(name)
+ require.NoError(t, err, "Open failed.")
+ defer func() {
+ err := os.Remove(name)
+ if !t.Failed() {
+ // If the test has already failed, we probably didn't create this file.
+ require.NoError(t, err, "Deleting test file failed.")
+ }
+ }()
+ defer cleanup()
+
+ _, err = s.Write([]byte("test"))
+ assert.NoError(t, err, "Write failed.")
+ assert.True(t, fileExists(name), "Didn't create file for relative path.")
+}
+
+func TestOpenFails(t *testing.T) {
+ tests := []struct {
+ paths []string
+ }{
+ {paths: []string{"./non-existent-dir/file"}}, // directory doesn't exist
+ {paths: []string{"stdout", "./non-existent-dir/file"}}, // directory doesn't exist
+ {paths: []string{"://foo.log"}}, // invalid URL, scheme can't begin with colon
+ {paths: []string{"mem://somewhere"}}, // scheme not registered
+ }
+
+ for _, tt := range tests {
+ _, cleanup, err := Open(tt.paths...)
+ require.Nil(t, cleanup, "Cleanup function should never be nil")
+ assert.Error(t, err, "Open with invalid URL should fail.")
+ }
+}
+
+func TestOpenOtherErrors(t *testing.T) {
+ tempName := filepath.Join(t.TempDir(), "test.log")
+
+ tests := []struct {
+ msg string
+ paths []string
+ wantErr string
+ }{
+ {
+ msg: "file with unexpected host",
+ paths: []string{"file://host01.test.com" + tempName},
+ wantErr: "empty or use localhost",
+ },
+ {
+ msg: "file with user on localhost",
+ paths: []string{"file://rms@localhost" + tempName},
+ wantErr: "user and password not allowed",
+ },
+ {
+ msg: "file url with fragment",
+ paths: []string{"file://localhost" + tempName + "#foo"},
+ wantErr: "fragments not allowed",
+ },
+ {
+ msg: "file url with query",
+ paths: []string{"file://localhost" + tempName + "?foo=bar"},
+ wantErr: "query parameters not allowed",
+ },
+ {
+ msg: "file with port",
+ paths: []string{"file://localhost:8080" + tempName},
+ wantErr: "ports not allowed",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.msg, func(t *testing.T) {
+ _, cleanup, err := Open(tt.paths...)
+ if !assert.Error(t, err, "Open must fail.") {
+ cleanup()
+ return
+ }
+
+ assert.ErrorContains(t, err, tt.wantErr, "Unexpected error opening paths %v.", tt.paths)
+ })
+ }
+}
+
+type testWriter struct {
+ expected string
+ t testing.TB
+}
+
+func (w *testWriter) Write(actual []byte) (int, error) {
+ assert.Equal(w.t, []byte(w.expected), actual, "Unexpected write error.")
+ return len(actual), nil
+}
+
+func (w *testWriter) Sync() error {
+ return nil
+}
+
+func TestOpenWithErroringSinkFactory(t *testing.T) {
+ stubSinkRegistry(t)
+
+ msg := "expected factory error"
+ factory := func(_ *url.URL) (Sink, error) {
+ return nil, errors.New(msg)
+ }
+
+ assert.NoError(t, RegisterSink("test", factory), "Failed to register sink factory.")
+ _, _, err := Open("test://some/path")
+ assert.ErrorContains(t, err, msg)
+}
+
+func TestCombineWriteSyncers(t *testing.T) {
+ tw := &testWriter{"test", t}
+ w := CombineWriteSyncers(tw)
+ _, err := w.Write([]byte("test"))
+ assert.NoError(t, err, "Unexpected write error.")
+}
+
+func fileExists(name string) bool {
+ if _, err := os.Stat(name); os.IsNotExist(err) {
+ return false
+ }
+ return true
+}
diff --git a/vendor/go.uber.org/zap/ya.make b/vendor/go.uber.org/zap/ya.make
new file mode 100644
index 0000000000..7a40d4b2a9
--- /dev/null
+++ b/vendor/go.uber.org/zap/ya.make
@@ -0,0 +1,66 @@
+GO_LIBRARY()
+
+LICENSE(MIT)
+
+SRCS(
+ array.go
+ config.go
+ doc.go
+ encoder.go
+ error.go
+ field.go
+ flag.go
+ global.go
+ http_handler.go
+ level.go
+ logger.go
+ options.go
+ sink.go
+ sugar.go
+ time.go
+ writer.go
+)
+
+GO_TEST_SRCS(
+ array_test.go
+ clock_test.go
+ common_test.go
+ config_test.go
+ encoder_test.go
+ error_test.go
+ field_test.go
+ flag_test.go
+ global_test.go
+ increase_level_test.go
+ leak_test.go
+ level_test.go
+ logger_bench_test.go
+ logger_test.go
+ sink_test.go
+ sugar_test.go
+ time_test.go
+ writer_test.go
+)
+
+GO_XTEST_SRCS(
+ example_test.go
+ http_handler_test.go
+ # stacktrace_ext_test.go
+)
+
+IF (OS_WINDOWS)
+ GO_TEST_SRCS(sink_windows_test.go)
+ENDIF()
+
+END()
+
+RECURSE(
+ # benchmarks
+ buffer
+ gotest
+ internal
+ zapcore
+ zapgrpc
+ zapio
+ zaptest
+)
diff --git a/vendor/go.uber.org/zap/zapcore/buffered_write_syncer.go b/vendor/go.uber.org/zap/zapcore/buffered_write_syncer.go
new file mode 100644
index 0000000000..a40e93b3ec
--- /dev/null
+++ b/vendor/go.uber.org/zap/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/vendor/go.uber.org/zap/zapcore/buffered_write_syncer_bench_test.go b/vendor/go.uber.org/zap/zapcore/buffered_write_syncer_bench_test.go
new file mode 100644
index 0000000000..56ad5f2c66
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/buffered_write_syncer_bench_test.go
@@ -0,0 +1,55 @@
+// 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 func() {
+ assert.NoError(b, w.Stop(), "failed to stop buffered write syncer")
+ }()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ if _, err := w.Write([]byte("foobarbazbabble")); err != nil {
+ b.Fatal(err)
+ }
+ }
+ })
+ })
+}
diff --git a/vendor/go.uber.org/zap/zapcore/buffered_write_syncer_test.go b/vendor/go.uber.org/zap/zapcore/buffered_write_syncer_test.go
new file mode 100644
index 0000000000..d0f6037af0
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/buffered_write_syncer_test.go
@@ -0,0 +1,140 @@
+// 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.")
+ _, err = ws.Write([]byte("foo"))
+ assert.Error(t, err, "Expected error writing to WriteSyncer.")
+ 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/vendor/go.uber.org/zap/zapcore/clock.go b/vendor/go.uber.org/zap/zapcore/clock.go
new file mode 100644
index 0000000000..422fd82a6b
--- /dev/null
+++ b/vendor/go.uber.org/zap/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/vendor/go.uber.org/zap/zapcore/clock_test.go b/vendor/go.uber.org/zap/zapcore/clock_test.go
new file mode 100644
index 0000000000..0dff349914
--- /dev/null
+++ b/vendor/go.uber.org/zap/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/vendor/go.uber.org/zap/zapcore/console_encoder.go b/vendor/go.uber.org/zap/zapcore/console_encoder.go
new file mode 100644
index 0000000000..8ca0bfaf56
--- /dev/null
+++ b/vendor/go.uber.org/zap/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"
+
+ "go.uber.org/zap/buffer"
+ "go.uber.org/zap/internal/bufferpool"
+ "go.uber.org/zap/internal/pool"
+)
+
+var _sliceEncoderPool = pool.New(func() *sliceArrayEncoder {
+ return &sliceArrayEncoder{
+ elems: make([]interface{}, 0, 2),
+ }
+})
+
+func getSliceEncoder() *sliceArrayEncoder {
+ return _sliceEncoderPool.Get()
+}
+
+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/vendor/go.uber.org/zap/zapcore/console_encoder_bench_test.go b/vendor/go.uber.org/zap/zapcore/console_encoder_bench_test.go
new file mode 100644
index 0000000000..62feaea717
--- /dev/null
+++ b/vendor/go.uber.org/zap/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/vendor/go.uber.org/zap/zapcore/console_encoder_test.go b/vendor/go.uber.org/zap/zapcore/console_encoder_test.go
new file mode 100644
index 0000000000..b03f1a728a
--- /dev/null
+++ b/vendor/go.uber.org/zap/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/vendor/go.uber.org/zap/zapcore/core.go b/vendor/go.uber.org/zap/zapcore/core.go
new file mode 100644
index 0000000000..776e93f6f3
--- /dev/null
+++ b/vendor/go.uber.org/zap/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/vendor/go.uber.org/zap/zapcore/core_test.go b/vendor/go.uber.org/zap/zapcore/core_test.go
new file mode 100644
index 0000000000..e3186311ac
--- /dev/null
+++ b/vendor/go.uber.org/zap/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,
+ )
+
+ assert.NoError(t, core.Write(tt.entry, nil), "Unexpected error writing entry.")
+ 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/vendor/go.uber.org/zap/zapcore/doc.go b/vendor/go.uber.org/zap/zapcore/doc.go
new file mode 100644
index 0000000000..31000e91f7
--- /dev/null
+++ b/vendor/go.uber.org/zap/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/vendor/go.uber.org/zap/zapcore/encoder.go b/vendor/go.uber.org/zap/zapcore/encoder.go
new file mode 100644
index 0000000000..5769ff3e4e
--- /dev/null
+++ b/vendor/go.uber.org/zap/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/vendor/go.uber.org/zap/zapcore/encoder_test.go b/vendor/go.uber.org/zap/zapcore/encoder_test.go
new file mode 100644
index 0000000000..9b8142f5d4
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/encoder_test.go
@@ -0,0 +1,733 @@
+// 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)
+ err := enc.AddArray("extras", ArrayMarshalerFunc(func(enc ArrayEncoder) error {
+ enc.AppendTime(_epoch)
+ return nil
+ }))
+ assert.NoError(t, err)
+ },
+ 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)
+ err := enc.AddArray("extras", ArrayMarshalerFunc(func(enc ArrayEncoder) error {
+ enc.AppendDuration(time.Minute)
+ return nil
+ }))
+ assert.NoError(t, err)
+ },
+ 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()
+ err := mem.AddArray("k", ArrayMarshalerFunc(func(arr ArrayEncoder) error {
+ f(arr)
+ return nil
+ }))
+ assert.NoError(t, err, msgAndArgs...)
+ 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/vendor/go.uber.org/zap/zapcore/entry.go b/vendor/go.uber.org/zap/zapcore/entry.go
new file mode 100644
index 0000000000..459a5d7ce3
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/entry.go
@@ -0,0 +1,298 @@
+// 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"
+ "time"
+
+ "go.uber.org/multierr"
+ "go.uber.org/zap/internal/bufferpool"
+ "go.uber.org/zap/internal/exit"
+ "go.uber.org/zap/internal/pool"
+)
+
+var _cePool = pool.New(func() *CheckedEntry {
+ // Pre-allocate some space for cores.
+ return &CheckedEntry{
+ cores: make([]Core, 4),
+ }
+})
+
+func getCheckedEntry() *CheckedEntry {
+ ce := _cePool.Get()
+ 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() // ignore error
+ }
+ 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() // ignore error
+ }
+
+ 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 [CheckedEntry.After] 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/vendor/go.uber.org/zap/zapcore/entry_test.go b/vendor/go.uber.org/zap/zapcore/entry_test.go
new file mode 100644
index 0000000000..6555ab6d08
--- /dev/null
+++ b/vendor/go.uber.org/zap/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/vendor/go.uber.org/zap/zapcore/error.go b/vendor/go.uber.org/zap/zapcore/error.go
new file mode 100644
index 0000000000..c40df13269
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/error.go
@@ -0,0 +1,136 @@
+// 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"
+
+ "go.uber.org/zap/internal/pool"
+)
+
+// 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])
+ err := arr.AppendObject(el)
+ el.Free()
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+var _errArrayElemPool = pool.New(func() *errArrayElem {
+ 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()
+ 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/vendor/go.uber.org/zap/zapcore/error_test.go b/vendor/go.uber.org/zap/zapcore/error_test.go
new file mode 100644
index 0000000000..c5d61b0403
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/error_test.go
@@ -0,0 +1,210 @@
+// 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"
+
+ "github.com/stretchr/testify/assert"
+
+ "go.uber.org/multierr"
+ "go.uber.org/zap/zapcore"
+ . "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: fmt.Errorf("failed: %w", errors.New("egad")),
+ want: map[string]interface{}{
+ "k": "failed: egad",
+ },
+ },
+ {
+ k: "error",
+ iface: multierr.Combine(
+ fmt.Errorf("hello: %w",
+ multierr.Combine(errors.New("foo"), errors.New("bar")),
+ ),
+ errors.New("baz"),
+ fmt.Errorf("world: %w", errors.New("qux")),
+ ),
+ want: map[string]interface{}{
+ "error": "hello: foo; bar; baz; world: qux",
+ "errorCauses": []interface{}{
+ map[string]interface{}{
+ "error": "hello: foo; bar",
+ },
+ map[string]interface{}{"error": "baz"},
+ map[string]interface{}{"error": "world: qux"},
+ },
+ },
+ },
+ }
+
+ 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: fmt.Errorf("failed: %w", errors.New("egad")),
+ Key: "k",
+ }
+ enc := NewMapObjectEncoder()
+ f.AddTo(enc)
+ assert.Equal(t, "failed: egad", enc.Fields["k"], "Unexpected basic error message.")
+}
+
+func TestErrArrayBrokenEncoder(t *testing.T) {
+ t.Parallel()
+
+ f := Field{
+ Key: "foo",
+ Type: ErrorType,
+ Interface: multierr.Combine(
+ errors.New("foo"),
+ errors.New("bar"),
+ ),
+ }
+
+ failWith := errors.New("great sadness")
+ enc := NewMapObjectEncoder()
+ f.AddTo(brokenArrayObjectEncoder{
+ Err: failWith,
+ ObjectEncoder: enc,
+ })
+
+ // Failure to add the field to the encoder
+ // causes the error to be added as a string field.
+ assert.Equal(t, "great sadness", enc.Fields["fooError"],
+ "Unexpected error message.")
+}
+
+// brokenArrayObjectEncoder is an ObjectEncoder
+// that builds a broken ArrayEncoder.
+type brokenArrayObjectEncoder struct {
+ ObjectEncoder
+ ArrayEncoder
+
+ Err error // error to return
+}
+
+func (enc brokenArrayObjectEncoder) AddArray(key string, marshaler ArrayMarshaler) error {
+ return enc.ObjectEncoder.AddArray(key,
+ ArrayMarshalerFunc(func(ae ArrayEncoder) error {
+ enc.ArrayEncoder = ae
+ return marshaler.MarshalLogArray(enc)
+ }))
+}
+
+func (enc brokenArrayObjectEncoder) AppendObject(zapcore.ObjectMarshaler) error {
+ return enc.Err
+}
diff --git a/vendor/go.uber.org/zap/zapcore/field.go b/vendor/go.uber.org/zap/zapcore/field.go
new file mode 100644
index 0000000000..95bdb0a126
--- /dev/null
+++ b/vendor/go.uber.org/zap/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/vendor/go.uber.org/zap/zapcore/gotest/ya.make b/vendor/go.uber.org/zap/zapcore/gotest/ya.make
new file mode 100644
index 0000000000..3f45ec92e2
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/gotest/ya.make
@@ -0,0 +1,5 @@
+GO_TEST_FOR(vendor/go.uber.org/zap/zapcore)
+
+LICENSE(MIT)
+
+END()
diff --git a/vendor/go.uber.org/zap/zapcore/hook.go b/vendor/go.uber.org/zap/zapcore/hook.go
new file mode 100644
index 0000000000..198def9917
--- /dev/null
+++ b/vendor/go.uber.org/zap/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/vendor/go.uber.org/zap/zapcore/increase_level.go b/vendor/go.uber.org/zap/zapcore/increase_level.go
new file mode 100644
index 0000000000..7a11237ae9
--- /dev/null
+++ b/vendor/go.uber.org/zap/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/vendor/go.uber.org/zap/zapcore/json_encoder.go b/vendor/go.uber.org/zap/zapcore/json_encoder.go
new file mode 100644
index 0000000000..c8ab86979b
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/json_encoder.go
@@ -0,0 +1,583 @@
+// 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"
+ "time"
+ "unicode/utf8"
+
+ "go.uber.org/zap/buffer"
+ "go.uber.org/zap/internal/bufferpool"
+ "go.uber.org/zap/internal/pool"
+)
+
+// For JSON-escaping; see jsonEncoder.safeAddString below.
+const _hex = "0123456789abcdef"
+
+var _jsonPool = pool.New(func() *jsonEncoder {
+ return &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 := _jsonPool.Get()
+ 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) {
+ safeAppendStringLike(
+ (*buffer.Buffer).AppendString,
+ utf8.DecodeRuneInString,
+ enc.buf,
+ s,
+ )
+}
+
+// safeAddByteString is no-alloc equivalent of safeAddString(string(s)) for s []byte.
+func (enc *jsonEncoder) safeAddByteString(s []byte) {
+ safeAppendStringLike(
+ (*buffer.Buffer).AppendBytes,
+ utf8.DecodeRune,
+ enc.buf,
+ s,
+ )
+}
+
+// safeAppendStringLike is a generic implementation of safeAddString and safeAddByteString.
+// It appends a string or byte slice to the buffer, escaping all special characters.
+func safeAppendStringLike[S []byte | string](
+ // appendTo appends this string-like object to the buffer.
+ appendTo func(*buffer.Buffer, S),
+ // decodeRune decodes the next rune from the string-like object
+ // and returns its value and width in bytes.
+ decodeRune func(S) (rune, int),
+ buf *buffer.Buffer,
+ s S,
+) {
+ // The encoding logic below works by skipping over characters
+ // that can be safely copied as-is,
+ // until a character is found that needs special handling.
+ // At that point, we copy everything we've seen so far,
+ // and then handle that special character.
+ //
+ // last is the index of the last byte that was copied to the buffer.
+ last := 0
+ for i := 0; i < len(s); {
+ if s[i] >= utf8.RuneSelf {
+ // Character >= RuneSelf may be part of a multi-byte rune.
+ // They need to be decoded before we can decide how to handle them.
+ r, size := decodeRune(s[i:])
+ if r != utf8.RuneError || size != 1 {
+ // No special handling required.
+ // Skip over this rune and continue.
+ i += size
+ continue
+ }
+
+ // Invalid UTF-8 sequence.
+ // Replace it with the Unicode replacement character.
+ appendTo(buf, s[last:i])
+ buf.AppendString(`\ufffd`)
+
+ i++
+ last = i
+ } else {
+ // Character < RuneSelf is a single-byte UTF-8 rune.
+ if s[i] >= 0x20 && s[i] != '\\' && s[i] != '"' {
+ // No escaping necessary.
+ // Skip over this character and continue.
+ i++
+ continue
+ }
+
+ // This character needs to be escaped.
+ appendTo(buf, s[last:i])
+ switch s[i] {
+ case '\\', '"':
+ buf.AppendByte('\\')
+ buf.AppendByte(s[i])
+ case '\n':
+ buf.AppendByte('\\')
+ buf.AppendByte('n')
+ case '\r':
+ buf.AppendByte('\\')
+ buf.AppendByte('r')
+ case '\t':
+ buf.AppendByte('\\')
+ buf.AppendByte('t')
+ default:
+ // Encode bytes < 0x20, except for the escape sequences above.
+ buf.AppendString(`\u00`)
+ buf.AppendByte(_hex[s[i]>>4])
+ buf.AppendByte(_hex[s[i]&0xF])
+ }
+
+ i++
+ last = i
+ }
+ }
+
+ // add remaining
+ appendTo(buf, s[last:])
+}
diff --git a/vendor/go.uber.org/zap/zapcore/json_encoder_bench_test.go b/vendor/go.uber.org/zap/zapcore/json_encoder_bench_test.go
new file mode 100644
index 0000000000..9182b39518
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/json_encoder_bench_test.go
@@ -0,0 +1,131 @@
+// 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"
+ "fmt"
+ "testing"
+ "time"
+
+ . "go.uber.org/zap/zapcore"
+)
+
+func BenchmarkJSONLogMarshalerFunc(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ enc := NewJSONEncoder(testEncoderConfig())
+ err := enc.AddObject("nested", ObjectMarshalerFunc(func(enc ObjectEncoder) error {
+ enc.AddInt64("i", int64(i))
+ return nil
+ }))
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+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)
+ }
+ })
+}
+
+const _sliceSize = 5000
+
+type StringSlice []string
+
+func (s StringSlice) MarshalLogArray(encoder ArrayEncoder) error {
+ for _, str := range s {
+ encoder.AppendString(str)
+ }
+ return nil
+}
+
+func generateStringSlice(n int) StringSlice {
+ output := make(StringSlice, 0, n)
+ for i := 0; i < n; i++ {
+ output = append(output, fmt.Sprint("00000000-0000-0000-0000-0000000000", i))
+ }
+ return output
+}
+
+func BenchmarkZapJSON(b *testing.B) {
+ additional := generateStringSlice(_sliceSize)
+ b.ResetTimer()
+ 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)
+ _ = enc.AddArray("test", additional)
+ 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"`
+ Additional StringSlice
+ }{
+ 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,
+ },
+ Additional: generateStringSlice(_sliceSize),
+ }
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ if _, err := json.Marshal(record); err != nil {
+ b.Fatal(err)
+ }
+ }
+ })
+}
diff --git a/vendor/go.uber.org/zap/zapcore/json_encoder_impl_test.go b/vendor/go.uber.org/zap/zapcore/json_encoder_impl_test.go
new file mode 100644
index 0000000000..5f81262751
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/json_encoder_impl_test.go
@@ -0,0 +1,736 @@
+// 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"
+ "unicode/utf8"
+
+ "go.uber.org/zap/buffer"
+ "go.uber.org/zap/internal/bufferpool"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "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) {
+ assert.NoError(t, 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) {
+ assert.NoError(t, 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) {
+ err := 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
+ }))
+ assert.NoError(t, err)
+ },
+ },
+ }
+
+ 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)
+ err := e.AddArray("a", ArrayMarshalerFunc(func(enc ArrayEncoder) error {
+ enc.AppendTime(date)
+ return nil
+ }))
+ assert.NoError(t, err)
+ }
+ 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) {
+ assert.NoError(t, 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) {
+ assert.NoError(t, 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++ {
+ err := arr.AppendObject(ObjectMarshalerFunc(func(inner ObjectEncoder) error {
+ inner.AddString("in", "chicken")
+ return nil
+ }))
+ if err != nil {
+ return err
+ }
+ }
+ 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)
+}
+
+var _stringLikeCorpus = []string{
+ "",
+ "foo",
+ "bar",
+ "a\nb",
+ "a\tb",
+ "a\\b",
+ `a"b`,
+}
+
+func FuzzSafeAppendStringLike_bytes(f *testing.F) {
+ for _, s := range _stringLikeCorpus {
+ f.Add([]byte(s))
+ }
+ f.Fuzz(func(t *testing.T, b []byte) {
+ if !utf8.Valid(b) {
+ t.Skip()
+ }
+
+ fuzzSafeAppendStringLike(t, string(b), func(buf *buffer.Buffer) {
+ safeAppendStringLike(
+ (*buffer.Buffer).AppendBytes,
+ utf8.DecodeRune,
+ buf,
+ b,
+ )
+ })
+ })
+}
+
+func FuzzSafeAppendStringLike_string(f *testing.F) {
+ for _, s := range _stringLikeCorpus {
+ f.Add(s)
+ }
+ f.Fuzz(func(t *testing.T, s string) {
+ if !utf8.ValidString(s) {
+ t.Skip()
+ }
+
+ fuzzSafeAppendStringLike(t, s, func(buf *buffer.Buffer) {
+ safeAppendStringLike(
+ (*buffer.Buffer).AppendString,
+ utf8.DecodeRuneInString,
+ buf,
+ s,
+ )
+ })
+ })
+}
+
+func fuzzSafeAppendStringLike(
+ t *testing.T,
+ want string,
+ writeString func(*buffer.Buffer),
+) {
+ t.Helper()
+
+ buf := bufferpool.Get()
+ defer buf.Free()
+
+ buf.AppendByte('"')
+ writeString(buf)
+ buf.AppendByte('"')
+
+ var got string
+ require.NoError(t, json.Unmarshal(buf.Bytes(), &got))
+ assert.Equal(t, want, got)
+}
diff --git a/vendor/go.uber.org/zap/zapcore/lazy_with.go b/vendor/go.uber.org/zap/zapcore/lazy_with.go
new file mode 100644
index 0000000000..05288d6a88
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/lazy_with.go
@@ -0,0 +1,54 @@
+// Copyright (c) 2023 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"
+
+type lazyWithCore struct {
+ Core
+ sync.Once
+ fields []Field
+}
+
+// NewLazyWith wraps a Core with a "lazy" Core that will only encode fields if
+// the logger is written to (or is further chained in a lon-lazy manner).
+func NewLazyWith(core Core, fields []Field) Core {
+ return &lazyWithCore{
+ Core: core,
+ fields: fields,
+ }
+}
+
+func (d *lazyWithCore) initOnce() {
+ d.Once.Do(func() {
+ d.Core = d.Core.With(d.fields)
+ })
+}
+
+func (d *lazyWithCore) With(fields []Field) Core {
+ d.initOnce()
+ return d.Core.With(fields)
+}
+
+func (d *lazyWithCore) Check(e Entry, ce *CheckedEntry) *CheckedEntry {
+ d.initOnce()
+ return d.Core.Check(e, ce)
+}
diff --git a/vendor/go.uber.org/zap/zapcore/leak_test.go b/vendor/go.uber.org/zap/zapcore/leak_test.go
new file mode 100644
index 0000000000..4ef412e37c
--- /dev/null
+++ b/vendor/go.uber.org/zap/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/vendor/go.uber.org/zap/zapcore/level.go b/vendor/go.uber.org/zap/zapcore/level.go
new file mode 100644
index 0000000000..e01a241316
--- /dev/null
+++ b/vendor/go.uber.org/zap/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/vendor/go.uber.org/zap/zapcore/level_strings.go b/vendor/go.uber.org/zap/zapcore/level_strings.go
new file mode 100644
index 0000000000..7af8dadcb3
--- /dev/null
+++ b/vendor/go.uber.org/zap/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/vendor/go.uber.org/zap/zapcore/level_strings_test.go b/vendor/go.uber.org/zap/zapcore/level_strings_test.go
new file mode 100644
index 0000000000..14b0bac628
--- /dev/null
+++ b/vendor/go.uber.org/zap/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/vendor/go.uber.org/zap/zapcore/level_test.go b/vendor/go.uber.org/zap/zapcore/level_test.go
new file mode 100644
index 0000000000..d8eb962921
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/level_test.go
@@ -0,0 +1,248 @@
+// 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.ErrorContains(t, err, 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() // should panic
+ }, "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.ErrorContains(t, err, "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/vendor/go.uber.org/zap/zapcore/marshaler.go b/vendor/go.uber.org/zap/zapcore/marshaler.go
new file mode 100644
index 0000000000..c3c55ba0d9
--- /dev/null
+++ b/vendor/go.uber.org/zap/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/vendor/go.uber.org/zap/zapcore/memory_encoder.go b/vendor/go.uber.org/zap/zapcore/memory_encoder.go
new file mode 100644
index 0000000000..dfead0829d
--- /dev/null
+++ b/vendor/go.uber.org/zap/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/vendor/go.uber.org/zap/zapcore/memory_encoder_test.go b/vendor/go.uber.org/zap/zapcore/memory_encoder_test.go
new file mode 100644
index 0000000000..d5f215fb62
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/memory_encoder_test.go
@@ -0,0 +1,370 @@
+// 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")
+ assert.NoError(t, 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")
+ assert.NoError(t, 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) {
+ assert.NoError(t, e.AppendReflected(map[string]interface{}{"foo": 5}))
+ },
+ expected: map[string]interface{}{"foo": 5},
+ },
+ {
+ desc: "AppendArray (arrays of arrays)",
+ f: func(e ArrayEncoder) {
+ err := e.AppendArray(ArrayMarshalerFunc(func(inner ArrayEncoder) error {
+ inner.AppendBool(true)
+ inner.AppendBool(false)
+ return nil
+ }))
+ assert.NoError(t, err)
+ },
+ expected: []interface{}{true, false},
+ },
+ {
+ desc: "object (no nested namespace) then string",
+ f: func(e ArrayEncoder) {
+ err := e.AppendArray(ArrayMarshalerFunc(func(inner ArrayEncoder) error {
+ err := inner.AppendObject(maybeNamespace{false})
+ inner.AppendString("should-be-outside-obj")
+ return err
+ }))
+ assert.NoError(t, err)
+ },
+ 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) {
+ err := e.AppendArray(ArrayMarshalerFunc(func(inner ArrayEncoder) error {
+ err := inner.AppendObject(maybeNamespace{true})
+ inner.AppendString("should-be-outside-obj")
+ return err
+ }))
+ assert.NoError(t, err)
+ },
+ 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/vendor/go.uber.org/zap/zapcore/reflected_encoder.go b/vendor/go.uber.org/zap/zapcore/reflected_encoder.go
new file mode 100644
index 0000000000..8746360eca
--- /dev/null
+++ b/vendor/go.uber.org/zap/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/vendor/go.uber.org/zap/zapcore/sampler.go b/vendor/go.uber.org/zap/zapcore/sampler.go
new file mode 100644
index 0000000000..b7c093a4f2
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/sampler.go
@@ -0,0 +1,229 @@
+// 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 (
+ "sync/atomic"
+ "time"
+)
+
+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.Add(1)
+ }
+
+ c.counter.Store(1)
+
+ newResetAfter := tn + tick.Nanoseconds()
+ if !c.resetAt.CompareAndSwap(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.Add(1)
+ }
+
+ 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/vendor/go.uber.org/zap/zapcore/sampler_bench_test.go b/vendor/go.uber.org/zap/zapcore/sampler_bench_test.go
new file mode 100644
index 0000000000..100e2268e1
--- /dev/null
+++ b/vendor/go.uber.org/zap/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"
+ "sync/atomic"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "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.Add(1)
+ } else if dec&LogSampled > 0 {
+ sampledCount.Add(1)
+ }
+ }
+ 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/vendor/go.uber.org/zap/zapcore/tee.go b/vendor/go.uber.org/zap/zapcore/tee.go
new file mode 100644
index 0000000000..9bb32f0557
--- /dev/null
+++ b/vendor/go.uber.org/zap/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/vendor/go.uber.org/zap/zapcore/write_syncer.go b/vendor/go.uber.org/zap/zapcore/write_syncer.go
new file mode 100644
index 0000000000..d4a1af3d07
--- /dev/null
+++ b/vendor/go.uber.org/zap/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/vendor/go.uber.org/zap/zapcore/write_syncer_bench_test.go b/vendor/go.uber.org/zap/zapcore/write_syncer_bench_test.go
new file mode 100644
index 0000000000..90ae475581
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/write_syncer_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
+
+import (
+ "os"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "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() {
+ if _, err := w.Write([]byte("foobarbazbabble")); err != nil {
+ b.Fatal(err)
+ }
+ }
+ })
+ })
+ 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() {
+ if _, err := w.Write([]byte("foobarbazbabble")); err != nil {
+ b.Fatal(err)
+ }
+ }
+ })
+ })
+ b.Run("4 discarder with buffer", func(b *testing.B) {
+ w := &BufferedWriteSyncer{
+ WS: NewMultiWriteSyncer(
+ &ztest.Discarder{},
+ &ztest.Discarder{},
+ &ztest.Discarder{},
+ &ztest.Discarder{},
+ ),
+ }
+ defer func() {
+ assert.NoError(b, w.Stop(), "Unexpected error stopping buffered write syncer.")
+ }()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ if _, err := w.Write([]byte("foobarbazbabble")); err != nil {
+ b.Fatal(err)
+ }
+ }
+ })
+ })
+}
+
+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() {
+ if _, err := w.Write([]byte("foobarbazbabble")); err != nil {
+ b.Fatal(err)
+ }
+ }
+ })
+ })
+}
diff --git a/vendor/go.uber.org/zap/zapcore/write_syncer_test.go b/vendor/go.uber.org/zap/zapcore/write_syncer_test.go
new file mode 100644
index 0000000000..c0c2698e9e
--- /dev/null
+++ b/vendor/go.uber.org/zap/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.")
+
+ assert.NoError(t, ws.Sync(), "Expected Sync to succeed.")
+ 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.")
+}
diff --git a/vendor/go.uber.org/zap/zapcore/ya.make b/vendor/go.uber.org/zap/zapcore/ya.make
new file mode 100644
index 0000000000..6a3b005ee9
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/ya.make
@@ -0,0 +1,69 @@
+GO_LIBRARY()
+
+LICENSE(MIT)
+
+SRCS(
+ buffered_write_syncer.go
+ clock.go
+ console_encoder.go
+ core.go
+ doc.go
+ encoder.go
+ entry.go
+ error.go
+ field.go
+ hook.go
+ increase_level.go
+ json_encoder.go
+ lazy_with.go
+ level.go
+ level_strings.go
+ marshaler.go
+ memory_encoder.go
+ reflected_encoder.go
+ sampler.go
+ tee.go
+ write_syncer.go
+)
+
+GO_TEST_SRCS(
+ buffered_write_syncer_bench_test.go
+ buffered_write_syncer_test.go
+ clock_test.go
+ entry_test.go
+ json_encoder_impl_test.go
+ leak_test.go
+ level_strings_test.go
+ level_test.go
+ memory_encoder_test.go
+ write_syncer_bench_test.go
+ write_syncer_test.go
+)
+
+GO_XTEST_SRCS(
+ console_encoder_bench_test.go
+ console_encoder_test.go
+ core_test.go
+ encoder_test.go
+ error_test.go
+ # field_test.go
+ # hook_test.go
+ # increase_level_test.go
+ json_encoder_bench_test.go
+ # json_encoder_test.go
+ sampler_bench_test.go
+ # sampler_test.go
+ #tee_logger_bench_test.go
+ # tee_test.go
+)
+
+GO_SKIP_TESTS(
+ entry_ext_test.go
+ lazy_with_test.go
+)
+
+END()
+
+RECURSE(
+ gotest
+)
diff --git a/vendor/go.uber.org/zap/zapgrpc/gotest/ya.make b/vendor/go.uber.org/zap/zapgrpc/gotest/ya.make
new file mode 100644
index 0000000000..4921dd5ac1
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapgrpc/gotest/ya.make
@@ -0,0 +1,5 @@
+GO_TEST_FOR(vendor/go.uber.org/zap/zapgrpc)
+
+LICENSE(MIT)
+
+END()
diff --git a/vendor/go.uber.org/zap/zapgrpc/ya.make b/vendor/go.uber.org/zap/zapgrpc/ya.make
new file mode 100644
index 0000000000..cea7a6794c
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapgrpc/ya.make
@@ -0,0 +1,15 @@
+GO_LIBRARY()
+
+LICENSE(MIT)
+
+SRCS(
+ zapgrpc.go
+)
+
+GO_TEST_SRCS(zapgrpc_test.go)
+
+END()
+
+RECURSE(
+ gotest
+)
diff --git a/vendor/go.uber.org/zap/zapgrpc/zapgrpc.go b/vendor/go.uber.org/zap/zapgrpc/zapgrpc.go
new file mode 100644
index 0000000000..6823773b72
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapgrpc/zapgrpc.go
@@ -0,0 +1,245 @@
+// 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 zapgrpc provides a logger that is compatible with grpclog.
+package zapgrpc // import "go.uber.org/zap/zapgrpc"
+
+import (
+ "fmt"
+
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+)
+
+// See https://github.com/grpc/grpc-go/blob/v1.35.0/grpclog/loggerv2.go#L77-L86
+const (
+ grpcLvlInfo int = iota
+ grpcLvlWarn
+ grpcLvlError
+ grpcLvlFatal
+)
+
+var (
+ // _grpcToZapLevel maps gRPC log levels to zap log levels.
+ // See https://pkg.go.dev/go.uber.org/zap@v1.16.0/zapcore#Level
+ _grpcToZapLevel = map[int]zapcore.Level{
+ grpcLvlInfo: zapcore.InfoLevel,
+ grpcLvlWarn: zapcore.WarnLevel,
+ grpcLvlError: zapcore.ErrorLevel,
+ grpcLvlFatal: zapcore.FatalLevel,
+ }
+)
+
+// An Option overrides a Logger's default configuration.
+type Option interface {
+ apply(*Logger)
+}
+
+type optionFunc func(*Logger)
+
+func (f optionFunc) apply(log *Logger) {
+ f(log)
+}
+
+// WithDebug configures a Logger to print at zap's DebugLevel instead of
+// InfoLevel.
+// It only affects the Printf, Println and Print methods, which are only used in the gRPC v1 grpclog.Logger API.
+//
+// Deprecated: use grpclog.SetLoggerV2() for v2 API.
+func WithDebug() Option {
+ return optionFunc(func(logger *Logger) {
+ logger.print = &printer{
+ enab: logger.levelEnabler,
+ level: zapcore.DebugLevel,
+ print: logger.delegate.Debug,
+ printf: logger.delegate.Debugf,
+ }
+ })
+}
+
+// withWarn redirects the fatal level to the warn level, which makes testing
+// easier. This is intentionally unexported.
+func withWarn() Option {
+ return optionFunc(func(logger *Logger) {
+ logger.fatal = &printer{
+ enab: logger.levelEnabler,
+ level: zapcore.WarnLevel,
+ print: logger.delegate.Warn,
+ printf: logger.delegate.Warnf,
+ }
+ })
+}
+
+// NewLogger returns a new Logger.
+func NewLogger(l *zap.Logger, options ...Option) *Logger {
+ logger := &Logger{
+ delegate: l.Sugar(),
+ levelEnabler: l.Core(),
+ }
+ logger.print = &printer{
+ enab: logger.levelEnabler,
+ level: zapcore.InfoLevel,
+ print: logger.delegate.Info,
+ printf: logger.delegate.Infof,
+ }
+ logger.fatal = &printer{
+ enab: logger.levelEnabler,
+ level: zapcore.FatalLevel,
+ print: logger.delegate.Fatal,
+ printf: logger.delegate.Fatalf,
+ }
+ for _, option := range options {
+ option.apply(logger)
+ }
+ return logger
+}
+
+// printer implements Print, Printf, and Println operations for a Zap level.
+//
+// We use it to customize Debug vs Info, and Warn vs Fatal for Print and Fatal
+// respectively.
+type printer struct {
+ enab zapcore.LevelEnabler
+ level zapcore.Level
+ print func(...interface{})
+ printf func(string, ...interface{})
+}
+
+func (v *printer) Print(args ...interface{}) {
+ v.print(args...)
+}
+
+func (v *printer) Printf(format string, args ...interface{}) {
+ v.printf(format, args...)
+}
+
+func (v *printer) Println(args ...interface{}) {
+ if v.enab.Enabled(v.level) {
+ v.print(sprintln(args))
+ }
+}
+
+// Logger adapts zap's Logger to be compatible with grpclog.LoggerV2 and the deprecated grpclog.Logger.
+type Logger struct {
+ delegate *zap.SugaredLogger
+ levelEnabler zapcore.LevelEnabler
+ print *printer
+ fatal *printer
+ // printToDebug bool
+ // fatalToWarn bool
+}
+
+// Print implements grpclog.Logger.
+//
+// Deprecated: use [Logger.Info].
+func (l *Logger) Print(args ...interface{}) {
+ l.print.Print(args...)
+}
+
+// Printf implements grpclog.Logger.
+//
+// Deprecated: use [Logger.Infof].
+func (l *Logger) Printf(format string, args ...interface{}) {
+ l.print.Printf(format, args...)
+}
+
+// Println implements grpclog.Logger.
+//
+// Deprecated: use [Logger.Info].
+func (l *Logger) Println(args ...interface{}) {
+ l.print.Println(args...)
+}
+
+// Info implements grpclog.LoggerV2.
+func (l *Logger) Info(args ...interface{}) {
+ l.delegate.Info(args...)
+}
+
+// Infoln implements grpclog.LoggerV2.
+func (l *Logger) Infoln(args ...interface{}) {
+ if l.levelEnabler.Enabled(zapcore.InfoLevel) {
+ l.delegate.Info(sprintln(args))
+ }
+}
+
+// Infof implements grpclog.LoggerV2.
+func (l *Logger) Infof(format string, args ...interface{}) {
+ l.delegate.Infof(format, args...)
+}
+
+// Warning implements grpclog.LoggerV2.
+func (l *Logger) Warning(args ...interface{}) {
+ l.delegate.Warn(args...)
+}
+
+// Warningln implements grpclog.LoggerV2.
+func (l *Logger) Warningln(args ...interface{}) {
+ if l.levelEnabler.Enabled(zapcore.WarnLevel) {
+ l.delegate.Warn(sprintln(args))
+ }
+}
+
+// Warningf implements grpclog.LoggerV2.
+func (l *Logger) Warningf(format string, args ...interface{}) {
+ l.delegate.Warnf(format, args...)
+}
+
+// Error implements grpclog.LoggerV2.
+func (l *Logger) Error(args ...interface{}) {
+ l.delegate.Error(args...)
+}
+
+// Errorln implements grpclog.LoggerV2.
+func (l *Logger) Errorln(args ...interface{}) {
+ if l.levelEnabler.Enabled(zapcore.ErrorLevel) {
+ l.delegate.Error(sprintln(args))
+ }
+}
+
+// Errorf implements grpclog.LoggerV2.
+func (l *Logger) Errorf(format string, args ...interface{}) {
+ l.delegate.Errorf(format, args...)
+}
+
+// Fatal implements grpclog.LoggerV2.
+func (l *Logger) Fatal(args ...interface{}) {
+ l.fatal.Print(args...)
+}
+
+// Fatalln implements grpclog.LoggerV2.
+func (l *Logger) Fatalln(args ...interface{}) {
+ l.fatal.Println(args...)
+}
+
+// Fatalf implements grpclog.LoggerV2.
+func (l *Logger) Fatalf(format string, args ...interface{}) {
+ l.fatal.Printf(format, args...)
+}
+
+// V implements grpclog.LoggerV2.
+func (l *Logger) V(level int) bool {
+ return l.levelEnabler.Enabled(_grpcToZapLevel[level])
+}
+
+func sprintln(args []interface{}) string {
+ s := fmt.Sprintln(args...)
+ // Drop the new line character added by Sprintln
+ return s[:len(s)-1]
+}
diff --git a/vendor/go.uber.org/zap/zapgrpc/zapgrpc_test.go b/vendor/go.uber.org/zap/zapgrpc/zapgrpc_test.go
new file mode 100644
index 0000000000..a231d65ec8
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapgrpc/zapgrpc_test.go
@@ -0,0 +1,263 @@
+// 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 zapgrpc
+
+import (
+ "fmt"
+ "testing"
+
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+ "go.uber.org/zap/zaptest/observer"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestLoggerInfoExpected(t *testing.T) {
+ checkMessages(t, zapcore.DebugLevel, nil, zapcore.InfoLevel, []string{
+ "hello",
+ "s1s21 2 3s34s56",
+ "hello world",
+ "",
+ "foo",
+ "foo bar",
+ "s1 s2 1 2 3 s3 4 s5 6",
+ "hello",
+ "s1s21 2 3s34s56",
+ "hello world",
+ "",
+ "foo",
+ "foo bar",
+ "s1 s2 1 2 3 s3 4 s5 6",
+ }, func(logger *Logger) {
+ logger.Info("hello")
+ logger.Info("s1", "s2", 1, 2, 3, "s3", 4, "s5", 6)
+ logger.Infof("%s world", "hello")
+ logger.Infoln()
+ logger.Infoln("foo")
+ logger.Infoln("foo", "bar")
+ logger.Infoln("s1", "s2", 1, 2, 3, "s3", 4, "s5", 6)
+ logger.Print("hello")
+ logger.Print("s1", "s2", 1, 2, 3, "s3", 4, "s5", 6)
+ logger.Printf("%s world", "hello")
+ logger.Println()
+ logger.Println("foo")
+ logger.Println("foo", "bar")
+ logger.Println("s1", "s2", 1, 2, 3, "s3", 4, "s5", 6)
+ })
+}
+
+func TestLoggerDebugExpected(t *testing.T) {
+ checkMessages(t, zapcore.DebugLevel, []Option{WithDebug()}, zapcore.DebugLevel, []string{
+ "hello",
+ "s1s21 2 3s34s56",
+ "hello world",
+ "",
+ "foo",
+ "foo bar",
+ "s1 s2 1 2 3 s3 4 s5 6",
+ }, func(logger *Logger) {
+ logger.Print("hello")
+ logger.Print("s1", "s2", 1, 2, 3, "s3", 4, "s5", 6)
+ logger.Printf("%s world", "hello")
+ logger.Println()
+ logger.Println("foo")
+ logger.Println("foo", "bar")
+ logger.Println("s1", "s2", 1, 2, 3, "s3", 4, "s5", 6)
+ })
+}
+
+func TestLoggerDebugSuppressed(t *testing.T) {
+ checkMessages(t, zapcore.InfoLevel, []Option{WithDebug()}, zapcore.DebugLevel, nil, func(logger *Logger) {
+ logger.Print("hello")
+ logger.Printf("%s world", "hello")
+ logger.Println()
+ logger.Println("foo")
+ logger.Println("foo", "bar")
+ })
+}
+
+func TestLoggerWarningExpected(t *testing.T) {
+ checkMessages(t, zapcore.DebugLevel, nil, zapcore.WarnLevel, []string{
+ "hello",
+ "s1s21 2 3s34s56",
+ "hello world",
+ "",
+ "foo",
+ "foo bar",
+ "s1 s2 1 2 3 s3 4 s5 6",
+ }, func(logger *Logger) {
+ logger.Warning("hello")
+ logger.Warning("s1", "s2", 1, 2, 3, "s3", 4, "s5", 6)
+ logger.Warningf("%s world", "hello")
+ logger.Warningln()
+ logger.Warningln("foo")
+ logger.Warningln("foo", "bar")
+ logger.Warningln("s1", "s2", 1, 2, 3, "s3", 4, "s5", 6)
+ })
+}
+
+func TestLoggerErrorExpected(t *testing.T) {
+ checkMessages(t, zapcore.DebugLevel, nil, zapcore.ErrorLevel, []string{
+ "hello",
+ "s1s21 2 3s34s56",
+ "hello world",
+ "",
+ "foo",
+ "foo bar",
+ "s1 s2 1 2 3 s3 4 s5 6",
+ }, func(logger *Logger) {
+ logger.Error("hello")
+ logger.Error("s1", "s2", 1, 2, 3, "s3", 4, "s5", 6)
+ logger.Errorf("%s world", "hello")
+ logger.Errorln()
+ logger.Errorln("foo")
+ logger.Errorln("foo", "bar")
+ logger.Errorln("s1", "s2", 1, 2, 3, "s3", 4, "s5", 6)
+ })
+}
+
+func TestLoggerFatalExpected(t *testing.T) {
+ checkMessages(t, zapcore.DebugLevel, nil, zapcore.FatalLevel, []string{
+ "hello",
+ "s1s21 2 3s34s56",
+ "hello world",
+ "",
+ "foo",
+ "foo bar",
+ "s1 s2 1 2 3 s3 4 s5 6",
+ }, func(logger *Logger) {
+ logger.Fatal("hello")
+ logger.Fatal("s1", "s2", 1, 2, 3, "s3", 4, "s5", 6)
+ logger.Fatalf("%s world", "hello")
+ logger.Fatalln()
+ logger.Fatalln("foo")
+ logger.Fatalln("foo", "bar")
+ logger.Fatalln("s1", "s2", 1, 2, 3, "s3", 4, "s5", 6)
+ })
+}
+
+func TestLoggerV(t *testing.T) {
+ tests := []struct {
+ zapLevel zapcore.Level
+ grpcEnabled []int
+ grpcDisabled []int
+ }{
+ {
+ zapLevel: zapcore.DebugLevel,
+ grpcEnabled: []int{grpcLvlInfo, grpcLvlWarn, grpcLvlError, grpcLvlFatal},
+ grpcDisabled: []int{}, // everything is enabled, nothing is disabled
+ },
+ {
+ zapLevel: zapcore.InfoLevel,
+ grpcEnabled: []int{grpcLvlInfo, grpcLvlWarn, grpcLvlError, grpcLvlFatal},
+ grpcDisabled: []int{}, // everything is enabled, nothing is disabled
+ },
+ {
+ zapLevel: zapcore.WarnLevel,
+ grpcEnabled: []int{grpcLvlWarn, grpcLvlError, grpcLvlFatal},
+ grpcDisabled: []int{grpcLvlInfo},
+ },
+ {
+ zapLevel: zapcore.ErrorLevel,
+ grpcEnabled: []int{grpcLvlError, grpcLvlFatal},
+ grpcDisabled: []int{grpcLvlInfo, grpcLvlWarn},
+ },
+ {
+ zapLevel: zapcore.DPanicLevel,
+ grpcEnabled: []int{grpcLvlFatal},
+ grpcDisabled: []int{grpcLvlInfo, grpcLvlWarn, grpcLvlError},
+ },
+ {
+ zapLevel: zapcore.PanicLevel,
+ grpcEnabled: []int{grpcLvlFatal},
+ grpcDisabled: []int{grpcLvlInfo, grpcLvlWarn, grpcLvlError},
+ },
+ {
+ zapLevel: zapcore.FatalLevel,
+ grpcEnabled: []int{grpcLvlFatal},
+ grpcDisabled: []int{grpcLvlInfo, grpcLvlWarn, grpcLvlError},
+ },
+ }
+ for _, tst := range tests {
+ for _, grpcLvl := range tst.grpcEnabled {
+ t.Run(fmt.Sprintf("enabled %s %d", tst.zapLevel, grpcLvl), func(t *testing.T) {
+ checkLevel(t, tst.zapLevel, true, func(logger *Logger) bool {
+ return logger.V(grpcLvl)
+ })
+ })
+ }
+ for _, grpcLvl := range tst.grpcDisabled {
+ t.Run(fmt.Sprintf("disabled %s %d", tst.zapLevel, grpcLvl), func(t *testing.T) {
+ checkLevel(t, tst.zapLevel, false, func(logger *Logger) bool {
+ return logger.V(grpcLvl)
+ })
+ })
+ }
+ }
+}
+
+func checkLevel(
+ t testing.TB,
+ enab zapcore.LevelEnabler,
+ expectedBool bool,
+ f func(*Logger) bool,
+) {
+ withLogger(enab, nil, func(logger *Logger, observedLogs *observer.ObservedLogs) {
+ actualBool := f(logger)
+ if expectedBool {
+ require.True(t, actualBool)
+ } else {
+ require.False(t, actualBool)
+ }
+ })
+}
+
+func checkMessages(
+ t testing.TB,
+ enab zapcore.LevelEnabler,
+ opts []Option,
+ expectedLevel zapcore.Level,
+ expectedMessages []string,
+ f func(*Logger),
+) {
+ if expectedLevel == zapcore.FatalLevel {
+ expectedLevel = zapcore.WarnLevel
+ }
+ withLogger(enab, opts, func(logger *Logger, observedLogs *observer.ObservedLogs) {
+ f(logger)
+ logEntries := observedLogs.All()
+ require.Equal(t, len(expectedMessages), len(logEntries))
+ for i, logEntry := range logEntries {
+ require.Equal(t, expectedLevel, logEntry.Level)
+ require.Equal(t, expectedMessages[i], logEntry.Message)
+ }
+ })
+}
+
+func withLogger(
+ enab zapcore.LevelEnabler,
+ opts []Option,
+ f func(*Logger, *observer.ObservedLogs),
+) {
+ core, observedLogs := observer.New(enab)
+ f(NewLogger(zap.New(core), append(opts, withWarn())...), observedLogs)
+}
diff --git a/vendor/go.uber.org/zap/zapio/example_test.go b/vendor/go.uber.org/zap/zapio/example_test.go
new file mode 100644
index 0000000000..e9565dbd71
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapio/example_test.go
@@ -0,0 +1,47 @@
+// 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 zapio_test
+
+import (
+ "io"
+ "log"
+
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapio"
+)
+
+func ExampleWriter() {
+ logger := zap.NewExample()
+ w := &zapio.Writer{Log: logger}
+
+ io.WriteString(w, "starting up\n")
+ io.WriteString(w, "running\n")
+ io.WriteString(w, "shutting down\n")
+
+ if err := w.Close(); err != nil {
+ log.Fatal(err)
+ }
+
+ // Output:
+ // {"level":"info","msg":"starting up"}
+ // {"level":"info","msg":"running"}
+ // {"level":"info","msg":"shutting down"}
+}
diff --git a/vendor/go.uber.org/zap/zapio/gotest/ya.make b/vendor/go.uber.org/zap/zapio/gotest/ya.make
new file mode 100644
index 0000000000..0e433fd99b
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapio/gotest/ya.make
@@ -0,0 +1,5 @@
+GO_TEST_FOR(vendor/go.uber.org/zap/zapio)
+
+LICENSE(MIT)
+
+END()
diff --git a/vendor/go.uber.org/zap/zapio/writer.go b/vendor/go.uber.org/zap/zapio/writer.go
new file mode 100644
index 0000000000..a87d910fad
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapio/writer.go
@@ -0,0 +1,149 @@
+// 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 zapio provides tools for interacting with IO streams through Zap.
+package zapio
+
+import (
+ "bytes"
+ "io"
+
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+)
+
+// Writer is an io.Writer that writes to the provided Zap logger, splitting log
+// messages on line boundaries. The Writer will buffer writes in memory until
+// it encounters a newline, or the caller calls Sync or Close.
+//
+// Use the Writer with packages like os/exec where an io.Writer is required,
+// and you want to log the output using your existing logger configuration. For
+// example,
+//
+// writer := &zapio.Writer{Log: logger, Level: zap.DebugLevel}
+// defer writer.Close()
+//
+// cmd := exec.CommandContext(ctx, ...)
+// cmd.Stdout = writer
+// cmd.Stderr = writer
+// if err := cmd.Run(); err != nil {
+// return err
+// }
+//
+// Writer must be closed when finished to flush buffered data to the logger.
+type Writer struct {
+ // Log specifies the logger to which the Writer will write messages.
+ //
+ // The Writer will panic if Log is unspecified.
+ Log *zap.Logger
+
+ // Log level for the messages written to the provided logger.
+ //
+ // If unspecified, defaults to Info.
+ Level zapcore.Level
+
+ buff bytes.Buffer
+}
+
+var (
+ _ zapcore.WriteSyncer = (*Writer)(nil)
+ _ io.Closer = (*Writer)(nil)
+)
+
+// Write writes the provided bytes to the underlying logger at the configured
+// log level and returns the length of the bytes.
+//
+// Write will split the input on newlines and post each line as a new log entry
+// to the logger.
+func (w *Writer) Write(bs []byte) (n int, err error) {
+ // Skip all checks if the level isn't enabled.
+ if !w.Log.Core().Enabled(w.Level) {
+ return len(bs), nil
+ }
+
+ n = len(bs)
+ for len(bs) > 0 {
+ bs = w.writeLine(bs)
+ }
+
+ return n, nil
+}
+
+// writeLine writes a single line from the input, returning the remaining,
+// unconsumed bytes.
+func (w *Writer) writeLine(line []byte) (remaining []byte) {
+ idx := bytes.IndexByte(line, '\n')
+ if idx < 0 {
+ // If there are no newlines, buffer the entire string.
+ w.buff.Write(line)
+ return nil
+ }
+
+ // Split on the newline, buffer and flush the left.
+ line, remaining = line[:idx], line[idx+1:]
+
+ // Fast path: if we don't have a partial message from a previous write
+ // in the buffer, skip the buffer and log directly.
+ if w.buff.Len() == 0 {
+ w.log(line)
+ return
+ }
+
+ w.buff.Write(line)
+
+ // Log empty messages in the middle of the stream so that we don't lose
+ // information when the user writes "foo\n\nbar".
+ w.flush(true /* allowEmpty */)
+
+ return remaining
+}
+
+// Close closes the writer, flushing any buffered data in the process.
+//
+// Always call Close once you're done with the Writer to ensure that it flushes
+// all data.
+func (w *Writer) Close() error {
+ return w.Sync()
+}
+
+// Sync flushes buffered data to the logger as a new log entry even if it
+// doesn't contain a newline.
+func (w *Writer) Sync() error {
+ // Don't allow empty messages on explicit Sync calls or on Close
+ // because we don't want an extraneous empty message at the end of the
+ // stream -- it's common for files to end with a newline.
+ w.flush(false /* allowEmpty */)
+ return nil
+}
+
+// flush flushes the buffered data to the logger, allowing empty messages only
+// if the bool is set.
+func (w *Writer) flush(allowEmpty bool) {
+ if allowEmpty || w.buff.Len() > 0 {
+ w.log(w.buff.Bytes())
+ }
+ w.buff.Reset()
+}
+
+func (w *Writer) log(b []byte) {
+ if ce := w.Log.Check(w.Level, string(b)); ce != nil {
+ ce.Write()
+ }
+}
diff --git a/vendor/go.uber.org/zap/zapio/writer_test.go b/vendor/go.uber.org/zap/zapio/writer_test.go
new file mode 100644
index 0000000000..9bdf3488d4
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapio/writer_test.go
@@ -0,0 +1,248 @@
+// 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 zapio
+
+import (
+ "io"
+ "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 TestWriter(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ desc string
+ level zapcore.Level // defaults to info
+ writes []string
+ want []zapcore.Entry
+ }{
+ {
+ desc: "simple",
+ writes: []string{
+ "foo\n",
+ "bar\n",
+ "baz\n",
+ },
+ want: []zapcore.Entry{
+ {Level: zap.InfoLevel, Message: "foo"},
+ {Level: zap.InfoLevel, Message: "bar"},
+ {Level: zap.InfoLevel, Message: "baz"},
+ },
+ },
+ {
+ desc: "level too low",
+ level: zap.DebugLevel,
+ writes: []string{
+ "foo\n",
+ "bar\n",
+ },
+ want: []zapcore.Entry{},
+ },
+ {
+ desc: "multiple newlines in a message",
+ level: zap.WarnLevel,
+ writes: []string{
+ "foo\nbar\n",
+ "baz\n",
+ "qux\nquux\n",
+ },
+ want: []zapcore.Entry{
+ {Level: zap.WarnLevel, Message: "foo"},
+ {Level: zap.WarnLevel, Message: "bar"},
+ {Level: zap.WarnLevel, Message: "baz"},
+ {Level: zap.WarnLevel, Message: "qux"},
+ {Level: zap.WarnLevel, Message: "quux"},
+ },
+ },
+ {
+ desc: "message split across multiple writes",
+ level: zap.ErrorLevel,
+ writes: []string{
+ "foo",
+ "bar\nbaz",
+ "qux",
+ },
+ want: []zapcore.Entry{
+ {Level: zap.ErrorLevel, Message: "foobar"},
+ {Level: zap.ErrorLevel, Message: "bazqux"},
+ },
+ },
+ {
+ desc: "blank lines in the middle",
+ writes: []string{
+ "foo\n\nbar\nbaz",
+ },
+ want: []zapcore.Entry{
+ {Level: zap.InfoLevel, Message: "foo"},
+ {Level: zap.InfoLevel, Message: ""},
+ {Level: zap.InfoLevel, Message: "bar"},
+ {Level: zap.InfoLevel, Message: "baz"},
+ },
+ },
+ {
+ desc: "blank line at the end",
+ writes: []string{
+ "foo\nbar\nbaz\n",
+ },
+ want: []zapcore.Entry{
+ {Level: zap.InfoLevel, Message: "foo"},
+ {Level: zap.InfoLevel, Message: "bar"},
+ {Level: zap.InfoLevel, Message: "baz"},
+ },
+ },
+ {
+ desc: "multiple blank line at the end",
+ writes: []string{
+ "foo\nbar\nbaz\n\n",
+ },
+ want: []zapcore.Entry{
+ {Level: zap.InfoLevel, Message: "foo"},
+ {Level: zap.InfoLevel, Message: "bar"},
+ {Level: zap.InfoLevel, Message: "baz"},
+ {Level: zap.InfoLevel, Message: ""},
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt // for t.Parallel
+ t.Run(tt.desc, func(t *testing.T) {
+ t.Parallel()
+
+ core, observed := observer.New(zap.InfoLevel)
+
+ w := Writer{
+ Log: zap.New(core),
+ Level: tt.level,
+ }
+
+ for _, s := range tt.writes {
+ _, err := io.WriteString(&w, s)
+ require.NoError(t, err, "Writer.Write failed.")
+ }
+
+ assert.NoError(t, w.Close(), "Writer.Close failed.")
+
+ // Turn []observer.LoggedEntry => []zapcore.Entry
+ got := make([]zapcore.Entry, observed.Len())
+ for i, ent := range observed.AllUntimed() {
+ got[i] = ent.Entry
+ }
+ assert.Equal(t, tt.want, got, "Logged entries do not match.")
+ })
+ }
+}
+
+func TestWrite_Sync(t *testing.T) {
+ t.Parallel()
+
+ core, observed := observer.New(zap.InfoLevel)
+
+ w := Writer{
+ Log: zap.New(core),
+ Level: zap.InfoLevel,
+ }
+
+ io.WriteString(&w, "foo")
+ io.WriteString(&w, "bar")
+
+ t.Run("no sync", func(t *testing.T) {
+ assert.Zero(t, observed.Len(), "Expected no logs yet")
+ })
+
+ t.Run("sync", func(t *testing.T) {
+ defer observed.TakeAll()
+
+ require.NoError(t, w.Sync(), "Sync must not fail")
+
+ assert.Equal(t, []observer.LoggedEntry{
+ {Entry: zapcore.Entry{Message: "foobar"}, Context: []zapcore.Field{}},
+ }, observed.AllUntimed(), "Log messages did not match")
+ })
+
+ t.Run("sync on empty", func(t *testing.T) {
+ require.NoError(t, w.Sync(), "Sync must not fail")
+ assert.Zero(t, observed.Len(), "Expected no logs yet")
+ })
+}
+
+func BenchmarkWriter(b *testing.B) {
+ tests := []struct {
+ name string
+ writes [][]byte
+ }{
+ {
+ name: "single",
+ writes: [][]byte{
+ []byte("foobar\n"),
+ []byte("bazqux\n"),
+ },
+ },
+ {
+ name: "splits",
+ writes: [][]byte{
+ []byte("foo"),
+ []byte("bar\nbaz"),
+ []byte("qux\n"),
+ },
+ },
+ }
+
+ writer := Writer{
+ Log: zap.New(new(partiallyNopCore)),
+ Level: zapcore.DebugLevel,
+ }
+
+ for _, tt := range tests {
+ b.Run(tt.name, func(b *testing.B) {
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ for _, bs := range tt.writes {
+ writer.Write(bs)
+ }
+ }
+ })
+ }
+}
+
+// partiallyNopCore behaves exactly like NopCore except it always returns true
+// for whether the provided level is enabled, and accepts all Check requests.
+//
+// This lets us measure the overhead of the writer without measuring the cost
+// of logging.
+type partiallyNopCore struct{}
+
+func (*partiallyNopCore) Enabled(zapcore.Level) bool { return true }
+
+func (c *partiallyNopCore) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
+ return ce.AddCore(ent, c)
+}
+
+func (c *partiallyNopCore) With([]zapcore.Field) zapcore.Core { return c }
+func (*partiallyNopCore) Write(zapcore.Entry, []zapcore.Field) error { return nil }
+func (*partiallyNopCore) Sync() error { return nil }
diff --git a/vendor/go.uber.org/zap/zapio/ya.make b/vendor/go.uber.org/zap/zapio/ya.make
new file mode 100644
index 0000000000..02ee8449cf
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapio/ya.make
@@ -0,0 +1,17 @@
+GO_LIBRARY()
+
+LICENSE(MIT)
+
+SRCS(
+ writer.go
+)
+
+GO_TEST_SRCS(writer_test.go)
+
+GO_XTEST_SRCS(example_test.go)
+
+END()
+
+RECURSE(
+ gotest
+)
diff --git a/vendor/go.uber.org/zap/zaptest/doc.go b/vendor/go.uber.org/zap/zaptest/doc.go
new file mode 100644
index 0000000000..b377859c4a
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/doc.go
@@ -0,0 +1,22 @@
+// 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 zaptest provides a variety of helpers for testing log output.
+package zaptest // import "go.uber.org/zap/zaptest"
diff --git a/vendor/go.uber.org/zap/zaptest/gotest/ya.make b/vendor/go.uber.org/zap/zaptest/gotest/ya.make
new file mode 100644
index 0000000000..18e7edc92c
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/gotest/ya.make
@@ -0,0 +1,5 @@
+GO_TEST_FOR(vendor/go.uber.org/zap/zaptest)
+
+LICENSE(MIT)
+
+END()
diff --git a/vendor/go.uber.org/zap/zaptest/logger.go b/vendor/go.uber.org/zap/zaptest/logger.go
new file mode 100644
index 0000000000..6a4a35497a
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/logger.go
@@ -0,0 +1,140 @@
+// 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 zaptest
+
+import (
+ "bytes"
+
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+)
+
+// LoggerOption configures the test logger built by NewLogger.
+type LoggerOption interface {
+ applyLoggerOption(*loggerOptions)
+}
+
+type loggerOptions struct {
+ Level zapcore.LevelEnabler
+ zapOptions []zap.Option
+}
+
+type loggerOptionFunc func(*loggerOptions)
+
+func (f loggerOptionFunc) applyLoggerOption(opts *loggerOptions) {
+ f(opts)
+}
+
+// Level controls which messages are logged by a test Logger built by
+// NewLogger.
+func Level(enab zapcore.LevelEnabler) LoggerOption {
+ return loggerOptionFunc(func(opts *loggerOptions) {
+ opts.Level = enab
+ })
+}
+
+// WrapOptions adds zap.Option's to a test Logger built by NewLogger.
+func WrapOptions(zapOpts ...zap.Option) LoggerOption {
+ return loggerOptionFunc(func(opts *loggerOptions) {
+ opts.zapOptions = zapOpts
+ })
+}
+
+// NewLogger builds a new Logger that logs all messages to the given
+// testing.TB.
+//
+// logger := zaptest.NewLogger(t)
+//
+// Use this with a *testing.T or *testing.B to get logs which get printed only
+// if a test fails or if you ran go test -v.
+//
+// The returned logger defaults to logging debug level messages and above.
+// This may be changed by passing a zaptest.Level during construction.
+//
+// logger := zaptest.NewLogger(t, zaptest.Level(zap.WarnLevel))
+//
+// You may also pass zap.Option's to customize test logger.
+//
+// logger := zaptest.NewLogger(t, zaptest.WrapOptions(zap.AddCaller()))
+func NewLogger(t TestingT, opts ...LoggerOption) *zap.Logger {
+ cfg := loggerOptions{
+ Level: zapcore.DebugLevel,
+ }
+ for _, o := range opts {
+ o.applyLoggerOption(&cfg)
+ }
+
+ writer := newTestingWriter(t)
+ zapOptions := []zap.Option{
+ // Send zap errors to the same writer and mark the test as failed if
+ // that happens.
+ zap.ErrorOutput(writer.WithMarkFailed(true)),
+ }
+ zapOptions = append(zapOptions, cfg.zapOptions...)
+
+ return zap.New(
+ zapcore.NewCore(
+ zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()),
+ writer,
+ cfg.Level,
+ ),
+ zapOptions...,
+ )
+}
+
+// testingWriter is a WriteSyncer that writes to the given testing.TB.
+type testingWriter struct {
+ t TestingT
+
+ // If true, the test will be marked as failed if this testingWriter is
+ // ever used.
+ markFailed bool
+}
+
+func newTestingWriter(t TestingT) testingWriter {
+ return testingWriter{t: t}
+}
+
+// WithMarkFailed returns a copy of this testingWriter with markFailed set to
+// the provided value.
+func (w testingWriter) WithMarkFailed(v bool) testingWriter {
+ w.markFailed = v
+ return w
+}
+
+func (w testingWriter) Write(p []byte) (n int, err error) {
+ n = len(p)
+
+ // Strip trailing newline because t.Log always adds one.
+ p = bytes.TrimRight(p, "\n")
+
+ // Note: t.Log is safe for concurrent use.
+ w.t.Logf("%s", p)
+ if w.markFailed {
+ w.t.Fail()
+ }
+
+ return n, nil
+}
+
+func (w testingWriter) Sync() error {
+ return nil
+}
diff --git a/vendor/go.uber.org/zap/zaptest/logger_test.go b/vendor/go.uber.org/zap/zaptest/logger_test.go
new file mode 100644
index 0000000000..576f6828cd
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/logger_test.go
@@ -0,0 +1,193 @@
+// 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 zaptest
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "strings"
+ "testing"
+
+ "go.uber.org/zap"
+ "go.uber.org/zap/internal/ztest"
+ "go.uber.org/zap/zapcore"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestTestLogger(t *testing.T) {
+ ts := newTestLogSpy(t)
+ defer ts.AssertPassed()
+
+ log := NewLogger(ts)
+
+ log.Info("received work order")
+ log.Debug("starting work")
+ log.Warn("work may fail")
+ log.Error("work failed", zap.Error(errors.New("great sadness")))
+
+ assert.Panics(t, func() {
+ log.Panic("failed to do work")
+ }, "log.Panic should panic")
+
+ ts.AssertMessages(
+ "INFO received work order",
+ "DEBUG starting work",
+ "WARN work may fail",
+ `ERROR work failed {"error": "great sadness"}`,
+ "PANIC failed to do work",
+ )
+}
+
+func TestTestLoggerSupportsLevels(t *testing.T) {
+ ts := newTestLogSpy(t)
+ defer ts.AssertPassed()
+
+ log := NewLogger(ts, Level(zap.WarnLevel))
+
+ log.Info("received work order")
+ log.Debug("starting work")
+ log.Warn("work may fail")
+ log.Error("work failed", zap.Error(errors.New("great sadness")))
+
+ assert.Panics(t, func() {
+ log.Panic("failed to do work")
+ }, "log.Panic should panic")
+
+ ts.AssertMessages(
+ "WARN work may fail",
+ `ERROR work failed {"error": "great sadness"}`,
+ "PANIC failed to do work",
+ )
+}
+
+func TestTestLoggerSupportsWrappedZapOptions(t *testing.T) {
+ ts := newTestLogSpy(t)
+ defer ts.AssertPassed()
+
+ log := NewLogger(ts, WrapOptions(zap.AddCaller(), zap.Fields(zap.String("k1", "v1"))))
+
+ log.Info("received work order")
+ log.Debug("starting work")
+ log.Warn("work may fail")
+ log.Error("work failed", zap.Error(errors.New("great sadness")))
+
+ assert.Panics(t, func() {
+ log.Panic("failed to do work")
+ }, "log.Panic should panic")
+
+ ts.AssertMessages(
+ `INFO zaptest/logger_test.go:89 received work order {"k1": "v1"}`,
+ `DEBUG zaptest/logger_test.go:90 starting work {"k1": "v1"}`,
+ `WARN zaptest/logger_test.go:91 work may fail {"k1": "v1"}`,
+ `ERROR zaptest/logger_test.go:92 work failed {"k1": "v1", "error": "great sadness"}`,
+ `PANIC zaptest/logger_test.go:95 failed to do work {"k1": "v1"}`,
+ )
+}
+
+func TestTestingWriter(t *testing.T) {
+ ts := newTestLogSpy(t)
+ w := newTestingWriter(ts)
+
+ n, err := io.WriteString(w, "hello\n\n")
+ assert.NoError(t, err, "WriteString must not fail")
+ assert.Equal(t, 7, n)
+}
+
+func TestTestLoggerErrorOutput(t *testing.T) {
+ // This test verifies that the test logger logs internal messages to the
+ // testing.T and marks the test as failed.
+
+ ts := newTestLogSpy(t)
+ defer ts.AssertFailed()
+
+ log := NewLogger(ts)
+
+ // Replace with a core that fails.
+ log = log.WithOptions(zap.WrapCore(func(zapcore.Core) zapcore.Core {
+ return zapcore.NewCore(
+ zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()),
+ zapcore.Lock(zapcore.AddSync(ztest.FailWriter{})),
+ zapcore.DebugLevel,
+ )
+ }))
+
+ log.Info("foo") // this fails
+
+ if assert.Len(t, ts.Messages, 1, "expected a log message") {
+ assert.Regexp(t, `write error: failed`, ts.Messages[0])
+ }
+}
+
+// testLogSpy is a testing.TB that captures logged messages.
+type testLogSpy struct {
+ testing.TB
+
+ failed bool
+ Messages []string
+}
+
+func newTestLogSpy(t testing.TB) *testLogSpy {
+ return &testLogSpy{TB: t}
+}
+
+func (t *testLogSpy) Fail() {
+ t.failed = true
+}
+
+func (t *testLogSpy) Failed() bool {
+ return t.failed
+}
+
+func (t *testLogSpy) FailNow() {
+ t.Fail()
+ t.TB.FailNow()
+}
+
+func (t *testLogSpy) Logf(format string, args ...interface{}) {
+ // Log messages are in the format,
+ //
+ // 2017-10-27T13:03:01.000-0700 DEBUG your message here {data here}
+ //
+ // We strip the first part of these messages because we can't really test
+ // for the timestamp from these tests.
+ m := fmt.Sprintf(format, args...)
+ m = m[strings.IndexByte(m, '\t')+1:]
+ t.Messages = append(t.Messages, m)
+ t.TB.Log(m)
+}
+
+func (t *testLogSpy) AssertMessages(msgs ...string) {
+ assert.Equal(t.TB, msgs, t.Messages, "logged messages did not match")
+}
+
+func (t *testLogSpy) AssertPassed() {
+ t.assertFailed(false, "expected test to pass")
+}
+
+func (t *testLogSpy) AssertFailed() {
+ t.assertFailed(true, "expected test to fail")
+}
+
+func (t *testLogSpy) assertFailed(v bool, msg string) {
+ assert.Equal(t.TB, v, t.failed, msg)
+}
diff --git a/vendor/go.uber.org/zap/zaptest/observer/gotest/ya.make b/vendor/go.uber.org/zap/zaptest/observer/gotest/ya.make
new file mode 100644
index 0000000000..512a2db69d
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/observer/gotest/ya.make
@@ -0,0 +1,5 @@
+GO_TEST_FOR(vendor/go.uber.org/zap/zaptest/observer)
+
+LICENSE(MIT)
+
+END()
diff --git a/vendor/go.uber.org/zap/zaptest/observer/logged_entry.go b/vendor/go.uber.org/zap/zaptest/observer/logged_entry.go
new file mode 100644
index 0000000000..a4ea7ec36c
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/observer/logged_entry.go
@@ -0,0 +1,39 @@
+// 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 observer
+
+import "go.uber.org/zap/zapcore"
+
+// An LoggedEntry is an encoding-agnostic representation of a log message.
+// Field availability is context dependant.
+type LoggedEntry struct {
+ zapcore.Entry
+ Context []zapcore.Field
+}
+
+// ContextMap returns a map for all fields in Context.
+func (e LoggedEntry) ContextMap() map[string]interface{} {
+ encoder := zapcore.NewMapObjectEncoder()
+ for _, f := range e.Context {
+ f.AddTo(encoder)
+ }
+ return encoder.Fields
+}
diff --git a/vendor/go.uber.org/zap/zaptest/observer/logged_entry_test.go b/vendor/go.uber.org/zap/zaptest/observer/logged_entry_test.go
new file mode 100644
index 0000000000..50f6123bdc
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/observer/logged_entry_test.go
@@ -0,0 +1,88 @@
+// 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 observer
+
+import (
+ "testing"
+
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestLoggedEntryContextMap(t *testing.T) {
+ tests := []struct {
+ msg string
+ fields []zapcore.Field
+ want map[string]interface{}
+ }{
+ {
+ msg: "no fields",
+ fields: nil,
+ want: map[string]interface{}{},
+ },
+ {
+ msg: "simple",
+ fields: []zapcore.Field{
+ zap.String("k1", "v"),
+ zap.Int64("k2", 10),
+ },
+ want: map[string]interface{}{
+ "k1": "v",
+ "k2": int64(10),
+ },
+ },
+ {
+ msg: "overwrite",
+ fields: []zapcore.Field{
+ zap.String("k1", "v1"),
+ zap.String("k1", "v2"),
+ },
+ want: map[string]interface{}{
+ "k1": "v2",
+ },
+ },
+ {
+ msg: "nested",
+ fields: []zapcore.Field{
+ zap.String("k1", "v1"),
+ zap.Namespace("nested"),
+ zap.String("k2", "v2"),
+ },
+ want: map[string]interface{}{
+ "k1": "v1",
+ "nested": map[string]interface{}{
+ "k2": "v2",
+ },
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.msg, func(t *testing.T) {
+ entry := LoggedEntry{
+ Context: tt.fields,
+ }
+ assert.Equal(t, tt.want, entry.ContextMap())
+ })
+ }
+}
diff --git a/vendor/go.uber.org/zap/zaptest/observer/observer.go b/vendor/go.uber.org/zap/zaptest/observer/observer.go
new file mode 100644
index 0000000000..f77f1308ba
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/observer/observer.go
@@ -0,0 +1,196 @@
+// 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 observer provides a zapcore.Core that keeps an in-memory,
+// encoding-agnostic representation of log entries. It's useful for
+// applications that want to unit test their log output without tying their
+// tests to a particular output encoding.
+package observer // import "go.uber.org/zap/zaptest/observer"
+
+import (
+ "strings"
+ "sync"
+ "time"
+
+ "go.uber.org/zap/internal"
+ "go.uber.org/zap/zapcore"
+)
+
+// ObservedLogs is a concurrency-safe, ordered collection of observed logs.
+type ObservedLogs struct {
+ mu sync.RWMutex
+ logs []LoggedEntry
+}
+
+// Len returns the number of items in the collection.
+func (o *ObservedLogs) Len() int {
+ o.mu.RLock()
+ n := len(o.logs)
+ o.mu.RUnlock()
+ return n
+}
+
+// All returns a copy of all the observed logs.
+func (o *ObservedLogs) All() []LoggedEntry {
+ o.mu.RLock()
+ ret := make([]LoggedEntry, len(o.logs))
+ copy(ret, o.logs)
+ o.mu.RUnlock()
+ return ret
+}
+
+// TakeAll returns a copy of all the observed logs, and truncates the observed
+// slice.
+func (o *ObservedLogs) TakeAll() []LoggedEntry {
+ o.mu.Lock()
+ ret := o.logs
+ o.logs = nil
+ o.mu.Unlock()
+ return ret
+}
+
+// AllUntimed returns a copy of all the observed logs, but overwrites the
+// observed timestamps with time.Time's zero value. This is useful when making
+// assertions in tests.
+func (o *ObservedLogs) AllUntimed() []LoggedEntry {
+ ret := o.All()
+ for i := range ret {
+ ret[i].Time = time.Time{}
+ }
+ return ret
+}
+
+// FilterLevelExact filters entries to those logged at exactly the given level.
+func (o *ObservedLogs) FilterLevelExact(level zapcore.Level) *ObservedLogs {
+ return o.Filter(func(e LoggedEntry) bool {
+ return e.Level == level
+ })
+}
+
+// FilterMessage filters entries to those that have the specified message.
+func (o *ObservedLogs) FilterMessage(msg string) *ObservedLogs {
+ return o.Filter(func(e LoggedEntry) bool {
+ return e.Message == msg
+ })
+}
+
+// FilterMessageSnippet filters entries to those that have a message containing the specified snippet.
+func (o *ObservedLogs) FilterMessageSnippet(snippet string) *ObservedLogs {
+ return o.Filter(func(e LoggedEntry) bool {
+ return strings.Contains(e.Message, snippet)
+ })
+}
+
+// FilterField filters entries to those that have the specified field.
+func (o *ObservedLogs) FilterField(field zapcore.Field) *ObservedLogs {
+ return o.Filter(func(e LoggedEntry) bool {
+ for _, ctxField := range e.Context {
+ if ctxField.Equals(field) {
+ return true
+ }
+ }
+ return false
+ })
+}
+
+// FilterFieldKey filters entries to those that have the specified key.
+func (o *ObservedLogs) FilterFieldKey(key string) *ObservedLogs {
+ return o.Filter(func(e LoggedEntry) bool {
+ for _, ctxField := range e.Context {
+ if ctxField.Key == key {
+ return true
+ }
+ }
+ return false
+ })
+}
+
+// Filter returns a copy of this ObservedLogs containing only those entries
+// for which the provided function returns true.
+func (o *ObservedLogs) Filter(keep func(LoggedEntry) bool) *ObservedLogs {
+ o.mu.RLock()
+ defer o.mu.RUnlock()
+
+ var filtered []LoggedEntry
+ for _, entry := range o.logs {
+ if keep(entry) {
+ filtered = append(filtered, entry)
+ }
+ }
+ return &ObservedLogs{logs: filtered}
+}
+
+func (o *ObservedLogs) add(log LoggedEntry) {
+ o.mu.Lock()
+ o.logs = append(o.logs, log)
+ o.mu.Unlock()
+}
+
+// New creates a new Core that buffers logs in memory (without any encoding).
+// It's particularly useful in tests.
+func New(enab zapcore.LevelEnabler) (zapcore.Core, *ObservedLogs) {
+ ol := &ObservedLogs{}
+ return &contextObserver{
+ LevelEnabler: enab,
+ logs: ol,
+ }, ol
+}
+
+type contextObserver struct {
+ zapcore.LevelEnabler
+ logs *ObservedLogs
+ context []zapcore.Field
+}
+
+var (
+ _ zapcore.Core = (*contextObserver)(nil)
+ _ internal.LeveledEnabler = (*contextObserver)(nil)
+)
+
+func (co *contextObserver) Level() zapcore.Level {
+ return zapcore.LevelOf(co.LevelEnabler)
+}
+
+func (co *contextObserver) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
+ if co.Enabled(ent.Level) {
+ return ce.AddCore(ent, co)
+ }
+ return ce
+}
+
+func (co *contextObserver) With(fields []zapcore.Field) zapcore.Core {
+ return &contextObserver{
+ LevelEnabler: co.LevelEnabler,
+ logs: co.logs,
+ context: append(co.context[:len(co.context):len(co.context)], fields...),
+ }
+}
+
+func (co *contextObserver) Write(ent zapcore.Entry, fields []zapcore.Field) error {
+ all := make([]zapcore.Field, 0, len(fields)+len(co.context))
+ all = append(all, co.context...)
+ all = append(all, fields...)
+ co.logs.add(LoggedEntry{ent, all})
+ return nil
+}
+
+func (co *contextObserver) Sync() error {
+ return nil
+}
diff --git a/vendor/go.uber.org/zap/zaptest/observer/observer_test.go b/vendor/go.uber.org/zap/zaptest/observer/observer_test.go
new file mode 100644
index 0000000000..0a57a0f32a
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/observer/observer_test.go
@@ -0,0 +1,258 @@
+// 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 observer_test
+
+import (
+ "testing"
+ "time"
+
+ "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 assertEmpty(t testing.TB, logs *ObservedLogs) {
+ assert.Equal(t, 0, logs.Len(), "Expected empty ObservedLogs to have zero length.")
+ assert.Equal(t, []LoggedEntry{}, logs.All(), "Unexpected LoggedEntries in empty ObservedLogs.")
+}
+
+func TestObserver(t *testing.T) {
+ observer, logs := New(zap.InfoLevel)
+ assertEmpty(t, logs)
+
+ t.Run("LevelOf", func(t *testing.T) {
+ assert.Equal(t, zap.InfoLevel, zapcore.LevelOf(observer), "Observer reported the wrong log level.")
+ })
+
+ assert.NoError(t, observer.Sync(), "Unexpected failure in no-op Sync")
+
+ obs := zap.New(observer).With(zap.Int("i", 1))
+ obs.Info("foo")
+ obs.Debug("bar")
+ want := []LoggedEntry{{
+ Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "foo"},
+ Context: []zapcore.Field{zap.Int("i", 1)},
+ }}
+
+ assert.Equal(t, 1, logs.Len(), "Unexpected observed logs Len.")
+ assert.Equal(t, want, logs.AllUntimed(), "Unexpected contents from AllUntimed.")
+
+ all := logs.All()
+ require.Equal(t, 1, len(all), "Unexpected number of LoggedEntries returned from All.")
+ assert.NotEqual(t, time.Time{}, all[0].Time, "Expected non-zero time on LoggedEntry.")
+
+ // copy & zero time for stable assertions
+ untimed := append([]LoggedEntry{}, all...)
+ untimed[0].Time = time.Time{}
+ assert.Equal(t, want, untimed, "Unexpected LoggedEntries from All.")
+
+ assert.Equal(t, all, logs.TakeAll(), "Expected All and TakeAll to return identical results.")
+ assertEmpty(t, logs)
+}
+
+func TestObserverWith(t *testing.T) {
+ sf1, logs := New(zap.InfoLevel)
+
+ // need to pad out enough initial fields so that the underlying slice cap()
+ // gets ahead of its len() so that the sf3/4 With append's could choose
+ // not to copy (if the implementation doesn't force them)
+ sf1 = sf1.With([]zapcore.Field{zap.Int("a", 1), zap.Int("b", 2)})
+
+ sf2 := sf1.With([]zapcore.Field{zap.Int("c", 3)})
+ sf3 := sf2.With([]zapcore.Field{zap.Int("d", 4)})
+ sf4 := sf2.With([]zapcore.Field{zap.Int("e", 5)})
+ ent := zapcore.Entry{Level: zap.InfoLevel, Message: "hello"}
+
+ for i, core := range []zapcore.Core{sf2, sf3, sf4} {
+ if ce := core.Check(ent, nil); ce != nil {
+ ce.Write(zap.Int("i", i))
+ }
+ }
+
+ assert.Equal(t, []LoggedEntry{
+ {
+ Entry: ent,
+ Context: []zapcore.Field{
+ zap.Int("a", 1),
+ zap.Int("b", 2),
+ zap.Int("c", 3),
+ zap.Int("i", 0),
+ },
+ },
+ {
+ Entry: ent,
+ Context: []zapcore.Field{
+ zap.Int("a", 1),
+ zap.Int("b", 2),
+ zap.Int("c", 3),
+ zap.Int("d", 4),
+ zap.Int("i", 1),
+ },
+ },
+ {
+ Entry: ent,
+ Context: []zapcore.Field{
+ zap.Int("a", 1),
+ zap.Int("b", 2),
+ zap.Int("c", 3),
+ zap.Int("e", 5),
+ zap.Int("i", 2),
+ },
+ },
+ }, logs.All(), "expected no field sharing between With siblings")
+}
+
+func TestFilters(t *testing.T) {
+ logs := []LoggedEntry{
+ {
+ Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "log a"},
+ Context: []zapcore.Field{zap.String("fStr", "1"), zap.Int("a", 1)},
+ },
+ {
+ Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "log a"},
+ Context: []zapcore.Field{zap.String("fStr", "2"), zap.Int("b", 2)},
+ },
+ {
+ Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "log b"},
+ Context: []zapcore.Field{zap.Int("a", 1), zap.Int("b", 2)},
+ },
+ {
+ Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "log c"},
+ Context: []zapcore.Field{zap.Int("a", 1), zap.Namespace("ns"), zap.Int("a", 2)},
+ },
+ {
+ Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "msg 1"},
+ Context: []zapcore.Field{zap.Int("a", 1), zap.Namespace("ns")},
+ },
+ {
+ Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "any map"},
+ Context: []zapcore.Field{zap.Any("map", map[string]string{"a": "b"})},
+ },
+ {
+ Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "any slice"},
+ Context: []zapcore.Field{zap.Any("slice", []string{"a"})},
+ },
+ {
+ Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "msg 2"},
+ Context: []zapcore.Field{zap.Int("b", 2), zap.Namespace("filterMe")},
+ },
+ {
+ Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "any slice"},
+ Context: []zapcore.Field{zap.Any("filterMe", []string{"b"})},
+ },
+ {
+ Entry: zapcore.Entry{Level: zap.WarnLevel, Message: "danger will robinson"},
+ Context: []zapcore.Field{zap.Int("b", 42)},
+ },
+ {
+ Entry: zapcore.Entry{Level: zap.ErrorLevel, Message: "warp core breach"},
+ Context: []zapcore.Field{zap.Int("b", 42)},
+ },
+ }
+
+ logger, sink := New(zap.InfoLevel)
+ for _, log := range logs {
+ assert.NoError(t, logger.Write(log.Entry, log.Context), "Unexpected error writing log entry.")
+ }
+
+ tests := []struct {
+ msg string
+ filtered *ObservedLogs
+ want []LoggedEntry
+ }{
+ {
+ msg: "filter by message",
+ filtered: sink.FilterMessage("log a"),
+ want: logs[0:2],
+ },
+ {
+ msg: "filter by field",
+ filtered: sink.FilterField(zap.String("fStr", "1")),
+ want: logs[0:1],
+ },
+ {
+ msg: "filter by message and field",
+ filtered: sink.FilterMessage("log a").FilterField(zap.Int("b", 2)),
+ want: logs[1:2],
+ },
+ {
+ msg: "filter by field with duplicate fields",
+ filtered: sink.FilterField(zap.Int("a", 2)),
+ want: logs[3:4],
+ },
+ {
+ msg: "filter doesn't match any messages",
+ filtered: sink.FilterMessage("no match"),
+ want: []LoggedEntry{},
+ },
+ {
+ msg: "filter by snippet",
+ filtered: sink.FilterMessageSnippet("log"),
+ want: logs[0:4],
+ },
+ {
+ msg: "filter by snippet and field",
+ filtered: sink.FilterMessageSnippet("a").FilterField(zap.Int("b", 2)),
+ want: logs[1:2],
+ },
+ {
+ msg: "filter for map",
+ filtered: sink.FilterField(zap.Any("map", map[string]string{"a": "b"})),
+ want: logs[5:6],
+ },
+ {
+ msg: "filter for slice",
+ filtered: sink.FilterField(zap.Any("slice", []string{"a"})),
+ want: logs[6:7],
+ },
+ {
+ msg: "filter field key",
+ filtered: sink.FilterFieldKey("filterMe"),
+ want: logs[7:9],
+ },
+ {
+ msg: "filter by arbitrary function",
+ filtered: sink.Filter(func(e LoggedEntry) bool {
+ return len(e.Context) > 1
+ }),
+ want: func() []LoggedEntry {
+ // Do not modify logs slice.
+ w := make([]LoggedEntry, 0, len(logs))
+ w = append(w, logs[0:5]...)
+ w = append(w, logs[7])
+ return w
+ }(),
+ },
+ {
+ msg: "filter level",
+ filtered: sink.FilterLevelExact(zap.WarnLevel),
+ want: logs[9:10],
+ },
+ }
+
+ for _, tt := range tests {
+ got := tt.filtered.AllUntimed()
+ assert.Equal(t, tt.want, got, tt.msg)
+ }
+}
diff --git a/vendor/go.uber.org/zap/zaptest/observer/ya.make b/vendor/go.uber.org/zap/zaptest/observer/ya.make
new file mode 100644
index 0000000000..c3ae8b0a6b
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/observer/ya.make
@@ -0,0 +1,18 @@
+GO_LIBRARY()
+
+LICENSE(MIT)
+
+SRCS(
+ logged_entry.go
+ observer.go
+)
+
+GO_TEST_SRCS(logged_entry_test.go)
+
+GO_XTEST_SRCS(observer_test.go)
+
+END()
+
+RECURSE(
+ gotest
+)
diff --git a/vendor/go.uber.org/zap/zaptest/testingt.go b/vendor/go.uber.org/zap/zaptest/testingt.go
new file mode 100644
index 0000000000..792463be30
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/testingt.go
@@ -0,0 +1,47 @@
+// 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 zaptest
+
+// TestingT is a subset of the API provided by all *testing.T and *testing.B
+// objects.
+type TestingT interface {
+ // Logs the given message without failing the test.
+ Logf(string, ...interface{})
+
+ // Logs the given message and marks the test as failed.
+ Errorf(string, ...interface{})
+
+ // Marks the test as failed.
+ Fail()
+
+ // Returns true if the test has been marked as failed.
+ Failed() bool
+
+ // Returns the name of the test.
+ Name() string
+
+ // Marks the test as failed and stops execution of that test.
+ FailNow()
+}
+
+// Note: We currently only rely on Logf. We are including Errorf and FailNow
+// in the interface in anticipation of future need since we can't extend the
+// interface without a breaking change.
diff --git a/vendor/go.uber.org/zap/zaptest/testingt_test.go b/vendor/go.uber.org/zap/zaptest/testingt_test.go
new file mode 100644
index 0000000000..d8477964d0
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/testingt_test.go
@@ -0,0 +1,29 @@
+// 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 zaptest
+
+import "testing"
+
+// Just a compile-time test to ensure that TestingT matches the testing.TB
+// interface. We could do this in testingt.go but that would put a dependency
+// on the "testing" package from zaptest.
+
+var _ TestingT = (testing.TB)(nil)
diff --git a/vendor/go.uber.org/zap/zaptest/timeout.go b/vendor/go.uber.org/zap/zaptest/timeout.go
new file mode 100644
index 0000000000..f0be444165
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/timeout.go
@@ -0,0 +1,45 @@
+// 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 zaptest
+
+import (
+ "time"
+
+ "go.uber.org/zap/internal/ztest"
+)
+
+// Timeout scales the provided duration by $TEST_TIMEOUT_SCALE.
+//
+// Deprecated: This function is intended for internal testing and shouldn't be
+// used outside zap itself. It was introduced before Go supported internal
+// packages.
+func Timeout(base time.Duration) time.Duration {
+ return ztest.Timeout(base)
+}
+
+// Sleep scales the sleep duration by $TEST_TIMEOUT_SCALE.
+//
+// Deprecated: This function is intended for internal testing and shouldn't be
+// used outside zap itself. It was introduced before Go supported internal
+// packages.
+func Sleep(base time.Duration) {
+ ztest.Sleep(base)
+}
diff --git a/vendor/go.uber.org/zap/zaptest/timeout_test.go b/vendor/go.uber.org/zap/zaptest/timeout_test.go
new file mode 100644
index 0000000000..3962ecdaff
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/timeout_test.go
@@ -0,0 +1,43 @@
+// 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 zaptest
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "go.uber.org/zap/internal/ztest"
+)
+
+func TestTimeout(t *testing.T) {
+ defer ztest.Initialize("2")()
+ assert.Equal(t, time.Duration(100), Timeout(50), "Expected to scale up timeout.")
+}
+
+func TestSleep(t *testing.T) {
+ defer ztest.Initialize("2")()
+ const sleepFor = 50 * time.Millisecond
+ now := time.Now()
+ Sleep(sleepFor)
+ elapsed := time.Since(now)
+ assert.True(t, 2*sleepFor <= elapsed, "Expected to scale up timeout.")
+}
diff --git a/vendor/go.uber.org/zap/zaptest/writer.go b/vendor/go.uber.org/zap/zaptest/writer.go
new file mode 100644
index 0000000000..4b772f8c28
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/writer.go
@@ -0,0 +1,44 @@
+// 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 zaptest
+
+import "go.uber.org/zap/internal/ztest"
+
+type (
+ // A Syncer is a spy for the Sync portion of zapcore.WriteSyncer.
+ Syncer = ztest.Syncer
+
+ // A Discarder sends all writes to io.Discard.
+ Discarder = ztest.Discarder
+
+ // FailWriter is a WriteSyncer that always returns an error on writes.
+ FailWriter = ztest.FailWriter
+
+ // ShortWriter is a WriteSyncer whose write method never returns an error,
+ // but always reports that it wrote one byte less than the input slice's
+ // length (thus, a "short write").
+ ShortWriter = ztest.ShortWriter
+
+ // Buffer is an implementation of zapcore.WriteSyncer that sends all writes to
+ // a bytes.Buffer. It has convenience methods to split the accumulated buffer
+ // on newlines.
+ Buffer = ztest.Buffer
+)
diff --git a/vendor/go.uber.org/zap/zaptest/writer_test.go b/vendor/go.uber.org/zap/zaptest/writer_test.go
new file mode 100644
index 0000000000..c18f18a359
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/writer_test.go
@@ -0,0 +1,68 @@
+// 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 zaptest
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestSyncer(t *testing.T) {
+ err := errors.New("sentinel")
+ s := &Syncer{}
+ s.SetError(err)
+ assert.Equal(t, err, s.Sync(), "Expected Sync to fail with provided error.")
+ assert.True(t, s.Called(), "Expected to record that Sync was called.")
+}
+
+func TestDiscarder(t *testing.T) {
+ d := &Discarder{}
+ payload := []byte("foo")
+ n, err := d.Write(payload)
+ assert.NoError(t, err, "Unexpected error writing to Discarder.")
+ assert.Equal(t, len(payload), n, "Wrong number of bytes written.")
+}
+
+func TestFailWriter(t *testing.T) {
+ w := &FailWriter{}
+ payload := []byte("foo")
+ n, err := w.Write(payload)
+ assert.Error(t, err, "Expected an error writing to FailWriter.")
+ assert.Equal(t, len(payload), n, "Wrong number of bytes written.")
+}
+
+func TestShortWriter(t *testing.T) {
+ w := &ShortWriter{}
+ payload := []byte("foo")
+ n, err := w.Write(payload)
+ assert.NoError(t, err, "Unexpected error writing to ShortWriter.")
+ assert.Equal(t, len(payload)-1, n, "Wrong number of bytes written.")
+}
+
+func TestBuffer(t *testing.T) {
+ buf := &Buffer{}
+ buf.WriteString("foo\n")
+ buf.WriteString("bar\n")
+ assert.Equal(t, []string{"foo", "bar"}, buf.Lines(), "Unexpected output from Lines.")
+ assert.Equal(t, "foo\nbar", buf.Stripped(), "Unexpected output from Stripped.")
+}
diff --git a/vendor/go.uber.org/zap/zaptest/ya.make b/vendor/go.uber.org/zap/zaptest/ya.make
new file mode 100644
index 0000000000..4e9d3ad95f
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/ya.make
@@ -0,0 +1,25 @@
+GO_LIBRARY()
+
+LICENSE(MIT)
+
+SRCS(
+ doc.go
+ logger.go
+ testingt.go
+ timeout.go
+ writer.go
+)
+
+GO_TEST_SRCS(
+ logger_test.go
+ testingt_test.go
+ timeout_test.go
+ writer_test.go
+)
+
+END()
+
+RECURSE(
+ gotest
+ observer
+)