mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
*: upgrade zap logger to 1.15, replace global logger
Signed-off-by: Gyuho Lee <leegyuho@amazon.com>
This commit is contained in:
parent
ed27d9d2de
commit
7eac6bd497
@ -98,6 +98,7 @@ func (cfg *Config) setupLogging() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
zap.ReplaceGlobals(c.logger)
|
||||
c.loggerMu.Lock()
|
||||
defer c.loggerMu.Unlock()
|
||||
c.loggerConfig = &copied
|
||||
@ -145,6 +146,7 @@ func (cfg *Config) setupLogging() error {
|
||||
if cfg.ZapLoggerBuilder == nil {
|
||||
cfg.ZapLoggerBuilder = func(c *Config) error {
|
||||
c.logger = zap.New(cr, zap.AddCaller(), zap.ErrorOutput(syncer))
|
||||
zap.ReplaceGlobals(c.logger)
|
||||
c.loggerMu.Lock()
|
||||
defer c.loggerMu.Unlock()
|
||||
c.loggerConfig = nil
|
||||
|
@ -17,11 +17,12 @@ package membership
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/coreos/go-semver/semver"
|
||||
"path"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/go-semver/semver"
|
||||
|
||||
"go.etcd.io/etcd/v3/etcdserver/api/v2store"
|
||||
"go.etcd.io/etcd/v3/pkg/mock/mockstore"
|
||||
"go.etcd.io/etcd/v3/pkg/testutil"
|
||||
|
@ -17,15 +17,16 @@ package membership
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/coreos/go-semver/semver"
|
||||
"go.etcd.io/etcd/v3/version"
|
||||
"go.uber.org/zap"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/go-semver/semver"
|
||||
"go.etcd.io/etcd/v3/version"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func TestMustDetectDowngrade(t *testing.T) {
|
||||
|
@ -16,7 +16,6 @@ package snap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go.etcd.io/etcd/v3/pkg/fileutil"
|
||||
"hash/crc32"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@ -24,9 +23,9 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"go.etcd.io/etcd/v3/pkg/fileutil"
|
||||
"go.etcd.io/etcd/v3/raft/raftpb"
|
||||
"go.etcd.io/etcd/v3/wal/walpb"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
2
go.mod
2
go.mod
@ -39,7 +39,7 @@ require (
|
||||
github.com/urfave/cli v1.20.0
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2
|
||||
go.etcd.io/bbolt v1.3.5
|
||||
go.uber.org/zap v1.14.1
|
||||
go.uber.org/zap v1.15.0
|
||||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2
|
||||
|
6
go.sum
6
go.sum
@ -17,8 +17,6 @@ github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazu
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQamW5YV28=
|
||||
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
|
||||
github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@ -159,8 +157,8 @@ go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.14.1 h1:nYDKopTbvAPq/NrUVZwT15y2lpROBiLLyoRTbXOYWOo=
|
||||
go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
|
||||
go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
|
||||
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
|
@ -22,7 +22,6 @@ package embed_test
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"go.etcd.io/etcd/c/v3/pkg/transport"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -32,6 +31,7 @@ import (
|
||||
|
||||
"go.etcd.io/etcd/v3/clientv3"
|
||||
"go.etcd.io/etcd/v3/embed"
|
||||
"go.etcd.io/etcd/v3/pkg/transport"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -19,7 +19,7 @@ import (
|
||||
"time"
|
||||
|
||||
"go.etcd.io/etcd/v3/clientv3"
|
||||
"go.etcd.io/etcd/v3/contrib/recipes"
|
||||
recipe "go.etcd.io/etcd/v3/contrib/recipes"
|
||||
"go.etcd.io/etcd/v3/pkg/testutil"
|
||||
)
|
||||
|
||||
|
@ -19,7 +19,7 @@ import (
|
||||
"time"
|
||||
|
||||
"go.etcd.io/etcd/v3/clientv3/concurrency"
|
||||
"go.etcd.io/etcd/v3/contrib/recipes"
|
||||
recipe "go.etcd.io/etcd/v3/contrib/recipes"
|
||||
)
|
||||
|
||||
func TestDoubleBarrier(t *testing.T) {
|
||||
|
@ -20,7 +20,7 @@ import (
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"go.etcd.io/etcd/v3/contrib/recipes"
|
||||
recipe "go.etcd.io/etcd/v3/contrib/recipes"
|
||||
)
|
||||
|
||||
const (
|
||||
|
2
vendor/go.uber.org/zap/.travis.yml
generated
vendored
2
vendor/go.uber.org/zap/.travis.yml
generated
vendored
@ -9,8 +9,8 @@ env:
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- go: 1.12.x
|
||||
- go: 1.13.x
|
||||
- go: 1.14.x
|
||||
env: LINT=1
|
||||
|
||||
script:
|
||||
|
20
vendor/go.uber.org/zap/CHANGELOG.md
generated
vendored
20
vendor/go.uber.org/zap/CHANGELOG.md
generated
vendored
@ -1,5 +1,21 @@
|
||||
# Changelog
|
||||
|
||||
## 1.15.0 (23 Apr 2020)
|
||||
|
||||
Bugfixes:
|
||||
* [#804][]: Fix handling of `Time` values out of `UnixNano` range.
|
||||
* [#812][]: Fix `IncreaseLevel` being reset after a call to `With`.
|
||||
|
||||
Enhancements:
|
||||
* [#806][]: Add `WithCaller` option to supersede the `AddCaller` option. This
|
||||
allows disabling annotation of log entries with caller information if
|
||||
previously enabled with `AddCaller`.
|
||||
* [#813][]: Deprecate `NewSampler` constructor in favor of
|
||||
`NewSamplerWithOptions` which supports a `SamplerHook` option. This option
|
||||
adds support for monitoring sampling decisions through a hook.
|
||||
|
||||
Thanks to @danielbprice for their contributions to this release.
|
||||
|
||||
## 1.14.1 (14 Mar 2020)
|
||||
|
||||
Bugfixes:
|
||||
@ -379,3 +395,7 @@ upgrade to the upcoming stable release.
|
||||
[#791]: https://github.com/uber-go/zap/pull/791
|
||||
[#795]: https://github.com/uber-go/zap/pull/795
|
||||
[#799]: https://github.com/uber-go/zap/pull/799
|
||||
[#804]: https://github.com/uber-go/zap/pull/804
|
||||
[#812]: https://github.com/uber-go/zap/pull/812
|
||||
[#806]: https://github.com/uber-go/zap/pull/806
|
||||
[#813]: https://github.com/uber-go/zap/pull/813
|
||||
|
24
vendor/go.uber.org/zap/config.go
generated
vendored
24
vendor/go.uber.org/zap/config.go
generated
vendored
@ -32,10 +32,14 @@ import (
|
||||
// global CPU and I/O load that logging puts on your process while attempting
|
||||
// to preserve a representative subset of your logs.
|
||||
//
|
||||
// Values configured here are per-second. See zapcore.NewSampler for details.
|
||||
// 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"`
|
||||
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
|
||||
@ -208,9 +212,19 @@ func (cfg Config) buildOptions(errSink zapcore.WriteSyncer) []Option {
|
||||
opts = append(opts, AddStacktrace(stackLevel))
|
||||
}
|
||||
|
||||
if cfg.Sampling != nil {
|
||||
if scfg := cfg.Sampling; scfg != nil {
|
||||
opts = append(opts, WrapCore(func(core zapcore.Core) zapcore.Core {
|
||||
return zapcore.NewSampler(core, time.Second, int(cfg.Sampling.Initial), int(cfg.Sampling.Thereafter))
|
||||
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...,
|
||||
)
|
||||
}))
|
||||
}
|
||||
|
||||
|
8
vendor/go.uber.org/zap/field.go
generated
vendored
8
vendor/go.uber.org/zap/field.go
generated
vendored
@ -32,6 +32,11 @@ import (
|
||||
// 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 {
|
||||
@ -339,6 +344,9 @@ func Stringer(key string, val fmt.Stringer) Field {
|
||||
// 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()}
|
||||
}
|
||||
|
||||
|
11
vendor/go.uber.org/zap/options.go
generated
vendored
11
vendor/go.uber.org/zap/options.go
generated
vendored
@ -87,10 +87,17 @@ func Development() Option {
|
||||
}
|
||||
|
||||
// AddCaller configures the Logger to annotate each message with the filename
|
||||
// and line number of zap's caller.
|
||||
// and line number of zap's caller. See also WithCaller.
|
||||
func AddCaller() Option {
|
||||
return WithCaller(true)
|
||||
}
|
||||
|
||||
// WithCaller configures the Logger to annotate each message with the filename
|
||||
// and line number 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 = true
|
||||
log.addCaller = enabled
|
||||
})
|
||||
}
|
||||
|
||||
|
7
vendor/go.uber.org/zap/zapcore/field.go
generated
vendored
7
vendor/go.uber.org/zap/zapcore/field.go
generated
vendored
@ -65,8 +65,11 @@ const (
|
||||
Int8Type
|
||||
// StringType indicates that the field carries a string.
|
||||
StringType
|
||||
// TimeType indicates that the field carries a time.Time.
|
||||
// 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.
|
||||
@ -145,6 +148,8 @@ func (f Field) AddTo(enc ObjectEncoder) {
|
||||
// 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:
|
||||
|
17
vendor/go.uber.org/zap/zapcore/increase_level.go
generated
vendored
17
vendor/go.uber.org/zap/zapcore/increase_level.go
generated
vendored
@ -23,8 +23,7 @@ package zapcore
|
||||
import "fmt"
|
||||
|
||||
type levelFilterCore struct {
|
||||
Core
|
||||
|
||||
core Core
|
||||
level LevelEnabler
|
||||
}
|
||||
|
||||
@ -46,10 +45,22 @@ func (c *levelFilterCore) Enabled(lvl Level) bool {
|
||||
return c.level.Enabled(lvl)
|
||||
}
|
||||
|
||||
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)
|
||||
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()
|
||||
}
|
||||
|
94
vendor/go.uber.org/zap/zapcore/sampler.go
generated
vendored
94
vendor/go.uber.org/zap/zapcore/sampler.go
generated
vendored
@ -81,17 +81,92 @@ func (c *counter) IncCheckReset(t time.Time, tick time.Duration) uint64 {
|
||||
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.
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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
|
||||
@ -100,14 +175,10 @@ type sampler struct {
|
||||
// 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 &sampler{
|
||||
Core: core,
|
||||
tick: tick,
|
||||
counts: newCounters(),
|
||||
first: uint64(first),
|
||||
thereafter: uint64(thereafter),
|
||||
}
|
||||
return NewSamplerWithOptions(core, tick, first, thereafter)
|
||||
}
|
||||
|
||||
func (s *sampler) With(fields []Field) Core {
|
||||
@ -117,6 +188,7 @@ func (s *sampler) With(fields []Field) Core {
|
||||
counts: s.counts,
|
||||
first: s.first,
|
||||
thereafter: s.thereafter,
|
||||
hook: s.hook,
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,7 +200,9 @@ func (s *sampler) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
|
||||
counter := s.counts.get(ent.Level, ent.Message)
|
||||
n := counter.IncCheckReset(ent.Time, s.tick)
|
||||
if n > s.first && (n-s.first)%s.thereafter != 0 {
|
||||
s.hook(ent, LogDropped)
|
||||
return ce
|
||||
}
|
||||
s.hook(ent, LogSampled)
|
||||
return s.Core.Check(ent, ce)
|
||||
}
|
||||
|
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@ -140,7 +140,7 @@ go.etcd.io/bbolt
|
||||
go.uber.org/atomic
|
||||
# go.uber.org/multierr v1.5.0
|
||||
go.uber.org/multierr
|
||||
# go.uber.org/zap v1.14.1
|
||||
# go.uber.org/zap v1.15.0
|
||||
## explicit
|
||||
go.uber.org/zap
|
||||
go.uber.org/zap/buffer
|
||||
|
Loading…
x
Reference in New Issue
Block a user