diff --git a/buffer.go b/buffer.go deleted file mode 100644 index e4de5eb..0000000 --- a/buffer.go +++ /dev/null @@ -1,108 +0,0 @@ -package sallust - -import ( - "bytes" - "io" - "sync" - - "go.uber.org/zap" -) - -// Buffer is a zap.Sink that captures all logging to an in-memory buffer. -// An optional max size can be placed on the buffer, at which time the buffer -// is cleared before writing more log information. All methods of a Buffer -// may be called concurrently. The zero value of a Buffer is valid, and is -// an unlimited size in-memory sink for logs. -// -// Deprecated: This will be removed in a future release. Use the zaptest package instead. -type Buffer struct { - lock sync.Mutex - buffer bytes.Buffer - limit int -} - -var _ zap.Sink = (*Buffer)(nil) - -// Len returns the current size of the internal buffer -func (b *Buffer) Len() (n int) { - b.lock.Lock() - n = b.buffer.Len() - b.lock.Unlock() - return -} - -// Limit returns the current size limit of the buffer, in bytes. -// A nonpositive value indicates no limit. -func (b *Buffer) Limit() (l int) { - b.lock.Lock() - l = b.limit - b.lock.Unlock() - return -} - -// SetLimit changes the size limit of this buffer. If the new limit -// is smaller than the current size of the buffer, the buffer is reset. -// A nonpositive value indicates no limit. -func (b *Buffer) SetLimit(l int) { - b.lock.Lock() - - if l < 1 { - b.limit = 0 - } else { - b.limit = l - if b.buffer.Len() > b.limit { - b.buffer.Reset() - } - } - - b.lock.Unlock() -} - -// Write appends log output to the buffer. If the output would grow the buffer -// beyond its limit, the buffer is cleared first. If the limit is smaller than -// the length of p, the write proceeds anyway because a client may still want -// to capture the log output. -func (b *Buffer) Write(p []byte) (n int, err error) { - b.lock.Lock() - if b.limit > 0 && b.buffer.Len()+len(p) > b.limit { - b.buffer.Reset() - } - - n, err = b.buffer.Write(p) - b.lock.Unlock() - return -} - -// WriteTo writes the current buffer's contents to the supplied writer. -// The buffer's contents are reset after writing. -func (b *Buffer) WriteTo(w io.Writer) (n int64, err error) { - b.lock.Lock() - n, err = b.buffer.WriteTo(w) - b.lock.Unlock() - return -} - -// Sync is a nop, as there is no underlying I/O done by a Buffer -func (b *Buffer) Sync() error { - return nil -} - -// Reset resets the internal buffer, retaining storage for future writes -func (b *Buffer) Reset() { - b.lock.Lock() - b.buffer.Reset() - b.lock.Unlock() -} - -// Close is a nop, as no underlying I/O is performed by a Buffer -func (b *Buffer) Close() error { - return nil -} - -// String returns the current buffer's log contents. -func (b *Buffer) String() (s string) { - b.lock.Lock() - s = b.buffer.String() - b.lock.Unlock() - return -} diff --git a/buffer_test.go b/buffer_test.go deleted file mode 100644 index 551cab4..0000000 --- a/buffer_test.go +++ /dev/null @@ -1,137 +0,0 @@ -package sallust - -import ( - "bytes" - "testing" - - "github.com/stretchr/testify/assert" -) - -const testBufferMessage = "this is a lovely test message" - -func testBufferInitial(t *testing.T) { - var ( - assert = assert.New(t) - writeTo = new(bytes.Buffer) - buffer Buffer - ) - - assert.Zero(buffer.Len()) - assert.Zero(buffer.Limit()) - assert.NoError(buffer.Sync()) - assert.NoError(buffer.Close()) - assert.Empty(buffer.String()) - - n64, err := buffer.WriteTo(writeTo) - assert.Zero(n64) - assert.NoError(err) - assert.Zero(writeTo.Len()) -} - -func testBufferReset(t *testing.T) { - var ( - assert = assert.New(t) - writeTo = new(bytes.Buffer) - buffer Buffer - ) - - buffer.Write([]byte(testBufferMessage)) - buffer.Reset() - assert.Empty(buffer.String()) - - n64, err := buffer.WriteTo(writeTo) - assert.Zero(n64) - assert.NoError(err) - assert.Zero(buffer.Len()) -} - -func testBufferSetLimit(t *testing.T) { - var ( - assert = assert.New(t) - buffer Buffer - ) - - buffer.Write([]byte(testBufferMessage)) - buffer.SetLimit(2 * len(testBufferMessage)) - assert.Equal(len(testBufferMessage), buffer.Len()) - assert.Equal(testBufferMessage, buffer.String()) - - buffer.SetLimit(len(testBufferMessage) - 1) - assert.Zero(buffer.Len()) - assert.Empty(buffer.String()) - - buffer.SetLimit(0) - buffer.Write([]byte(testBufferMessage)) - assert.Equal(len(testBufferMessage), buffer.Len()) - assert.Equal(testBufferMessage, buffer.String()) -} - -func testBufferWriteNoLimit(t *testing.T) { - var ( - assert = assert.New(t) - writeTo = new(bytes.Buffer) - buffer Buffer - ) - - n, err := buffer.Write([]byte(testBufferMessage)) - assert.Equal(len(testBufferMessage), n) - assert.NoError(err) - assert.Equal(len(testBufferMessage), buffer.Len()) - assert.Zero(buffer.Limit()) - assert.NoError(buffer.Sync()) - assert.NoError(buffer.Close()) - assert.Equal(testBufferMessage, buffer.String()) - - n64, err := buffer.WriteTo(writeTo) - assert.Equal(int64(len(testBufferMessage)), n64) - assert.NoError(err) - assert.Equal(len(testBufferMessage), writeTo.Len()) - writeTo.Reset() -} - -func testBufferWriteLimit(t *testing.T) { - var ( - assert = assert.New(t) - writeTo = new(bytes.Buffer) - buffer Buffer - ) - - buffer.SetLimit(len(testBufferMessage)) - assert.Equal(len(testBufferMessage), buffer.Limit()) - - n, err := buffer.Write([]byte(testBufferMessage)) - assert.Equal(len(testBufferMessage), n) - assert.NoError(err) - assert.Equal(len(testBufferMessage), buffer.Len()) - assert.Equal(len(testBufferMessage), buffer.Limit()) - assert.NoError(buffer.Sync()) - assert.NoError(buffer.Close()) - assert.Equal(testBufferMessage, buffer.String()) - - // write a second time, to violate the limit - n, err = buffer.Write([]byte(testBufferMessage)) - assert.Equal(len(testBufferMessage), n) - assert.NoError(err) - assert.Equal(len(testBufferMessage), buffer.Len()) - assert.Equal(len(testBufferMessage), buffer.Limit()) - assert.NoError(buffer.Sync()) - assert.NoError(buffer.Close()) - assert.Equal(testBufferMessage, buffer.String()) - - n64, err := buffer.WriteTo(writeTo) - assert.Equal(int64(len(testBufferMessage)), n64) - assert.NoError(err) - assert.Equal(len(testBufferMessage), writeTo.Len()) - assert.Equal(len(testBufferMessage), buffer.Limit()) - writeTo.Reset() -} - -func TestBuffer(t *testing.T) { - t.Run("Initial", testBufferInitial) - t.Run("Reset", testBufferReset) - t.Run("SetLimit", testBufferSetLimit) - t.Run("Write", func(t *testing.T) { - t.Run("NoLimit", testBufferWriteNoLimit) - t.Run("Limit", testBufferWriteLimit) - }) -} diff --git a/captureCore.go b/captureCore.go deleted file mode 100644 index 28f9cf5..0000000 --- a/captureCore.go +++ /dev/null @@ -1,137 +0,0 @@ -package sallust - -import ( - "sync" - - "go.uber.org/zap/zapcore" -) - -// message is a captured log message -type message struct { - entry zapcore.Entry - fields []zapcore.Field -} - -// CaptureCore is a zapcore.Core which captures each log entry and makes -// it available for inspection in it's structured form. This type allows -// tests to examine log output programmatically in a way that is much easier -// than simply parsing text. -// -// Deprecated: This will be removed in a future release. Use the zaptest package instead. -type CaptureCore struct { - // Core is the zap.Core to which output is delegated - zapcore.Core - - lock sync.RWMutex - messages []message - with []zapcore.Field -} - -// Check delegates to the embedded Core, then adds this CaptureCore -// if appropriate. -func (cc *CaptureCore) Check(e zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry { - // this basic approach is at: https://github.com/uber-go/zap/blob/v1.15.0/zapcore/core.go#L78 - if cc.Core.Enabled(e.Level) { - // add this decorated Core, rather than the delegate, - // so the decorated Write method gets called - return ce.AddCore(e, cc) - } - - return ce -} - -// With first delegates to the embedded Core, then returns a CaptureCore -// which decorates the new Core. The fields are captured, and are added -// to every captured log message. -func (cc *CaptureCore) With(f []zapcore.Field) zapcore.Core { - c := cc.Core.With(f) - - cc.lock.RLock() - decorated := &CaptureCore{ - Core: c, - messages: append([]message{}, cc.messages...), - with: append( - append([]zapcore.Field{}, cc.with...), - f..., - ), - } - - cc.lock.RUnlock() - return decorated -} - -// Write captures the log message, then delegates to its embedded Core -// for output -func (cc *CaptureCore) Write(e zapcore.Entry, f []zapcore.Field) error { - cc.lock.Lock() - cc.messages = append(cc.messages, - message{ - entry: e, - fields: append( - append([]zapcore.Field{}, f...), - cc.with..., - ), - }, - ) - cc.lock.Unlock() - - return cc.Core.Write(e, f) -} - -// ClearMessages wipes out the captured log messages -func (cc *CaptureCore) ClearMessages() { - cc.lock.Lock() - cc.messages = nil - cc.lock.Unlock() -} - -// EachMessage applies a visitor closure to each capture Message. -// This method returns the number of messages visited. Any fields -// added via With are included in the fields for each entry. -// -// If the supplied closure returns an error, visitation is halted -// and that error is returned. -func (cc *CaptureCore) EachMessage(f func(zapcore.Entry, []zapcore.Field) error) (int, error) { - defer cc.lock.RUnlock() - cc.lock.RLock() - - for i, m := range cc.messages { - if err := f(m.entry, m.fields); err != nil { - return i, err - } - } - - return len(cc.messages), nil -} - -// Len returns the current count of captured messages -func (cc *CaptureCore) Len() (n int) { - cc.lock.RLock() - n = len(cc.messages) - cc.lock.RUnlock() - return -} - -// Capture decorates a given core and captures messages written to it. -// This function may be used with zap.WrapCore. -// -// var l *zap.Logger = ... -// l = l.WithOptions(zap.WrapCore(sallust.Capture)) -// cc := l.Core().(*sallust.CaptureCore) -func Capture(c zapcore.Core) zapcore.Core { - return &CaptureCore{ - Core: c, - } -} - -// NewCaptureCore is an analog to zapcore.NewCore. It produces a CaptureCore -// that captures log messages and delegates to a Core with the given configuration. -// This can be used directly to create a logger: -// -// l := zap.New(sallust.NewCaptureCore(...)) -// cc := l.Core().(*sallust.CaptureCore) -func NewCaptureCore(enc zapcore.Encoder, ws zapcore.WriteSyncer, enab zapcore.LevelEnabler) *CaptureCore { - return &CaptureCore{ - Core: zapcore.NewCore(enc, ws, enab), - } -} diff --git a/captureCore_test.go b/captureCore_test.go deleted file mode 100644 index 9cc42bd..0000000 --- a/captureCore_test.go +++ /dev/null @@ -1,130 +0,0 @@ -package sallust - -import ( - "errors" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -func TestCaptureCore(t *testing.T) { - var ( - assert = assert.New(t) - require = require.New(t) - // nolint:staticcheck - enc = zapcore.NewJSONEncoder(zapcore.EncoderConfig{ - MessageKey: "msg", - EncodeLevel: zapcore.LowercaseLevelEncoder, - }) - - buffer Buffer - cc = NewCaptureCore(enc, &buffer, zapcore.InfoLevel) - l = zap.New(cc) - ) - - assert.Nil(cc.Check(zapcore.Entry{Level: zapcore.DebugLevel}, nil)) - assert.NotNil(cc.Check(zapcore.Entry{Level: zapcore.InfoLevel}, nil)) - - assert.Zero(cc.Len()) - n, err := cc.EachMessage(func(zapcore.Entry, []zapcore.Field) error { - return errors.New("EachMessage should not have been called") - }) - assert.Zero(n) - assert.NoError(err) - - l.Debug("this should not appear") - n, err = cc.EachMessage(func(zapcore.Entry, []zapcore.Field) error { - return errors.New("EachMessage should not have been called") - }) - assert.Zero(n) - assert.NoError(err) - - cc.ClearMessages() - n, err = cc.EachMessage(func(zapcore.Entry, []zapcore.Field) error { - return errors.New("EachMessage should not have been called") - }) - assert.Zero(n) - assert.NoError(err) - - l.Info("message", zap.Int("foo", 123)) - l.Sync() - assert.Equal(1, cc.Len()) - n, err = cc.EachMessage(func(e zapcore.Entry, f []zapcore.Field) error { - assert.Equal("message", e.Message) - require.Len(f, 1) - assert.Equal("foo", f[0].Key) - assert.Equal(int64(123), f[0].Integer) - return nil - }) - - assert.Equal(1, n) - assert.NoError(err) - - l = l.With(zap.String("bar", "asdf")) - l.Info("another message", zap.Int("foo", 456)) - l.Sync() - require.NotPanics(func() { - cc = l.Core().(*CaptureCore) - }) - - assert.Equal(2, cc.Len()) - n, err = cc.EachMessage(func(e zapcore.Entry, f []zapcore.Field) error { - if e.Message == "message" { - require.Len(f, 1) - assert.Equal("foo", f[0].Key) - assert.Equal(int64(123), f[0].Integer) - } else if e.Message == "another message" { - require.Len(f, 2) - assert.Equal("foo", f[0].Key) - assert.Equal(int64(456), f[0].Integer) - assert.Equal("bar", f[1].Key) - assert.Equal("asdf", f[1].String) - } else { - return errors.New("Unrecognized captured message") - } - - return nil - }) - - assert.Equal(2, n) - assert.NoError(err) - - n, err = cc.EachMessage(func(e zapcore.Entry, _ []zapcore.Field) error { - if e.Message != "another message" { - return nil - } - - return errors.New("expected") - }) - - assert.Equal(1, n) - assert.Error(err) -} - -func TestCapture(t *testing.T) { - var ( - assert = assert.New(t) - require = require.New(t) - enc = zapcore.NewJSONEncoder(zapcore.EncoderConfig{ - MessageKey: "msg", - EncodeLevel: zapcore.LowercaseLevelEncoder, - }) - - buffer Buffer - c = Capture( - zapcore.NewCore(enc, &buffer, zapcore.InfoLevel), - ) - ) - - require.NotNil(c) - - c.Write(zapcore.Entry{Level: zapcore.InfoLevel}, []zapcore.Field{}) - assert.True(buffer.Len() > 0) - - cc, ok := c.(*CaptureCore) - require.True(ok) - assert.Equal(1, cc.Len()) -} diff --git a/config_test.go b/config_test.go index 8a1db3c..9e3fae6 100644 --- a/config_test.go +++ b/config_test.go @@ -1,6 +1,7 @@ package sallust import ( + "bytes" "testing" "github.com/stretchr/testify/assert" @@ -231,7 +232,7 @@ func testConfigBuildSimple(t *testing.T) { assert = assert.New(t) require = require.New(t) - output Buffer + buffer bytes.Buffer c = Config{ Development: true, @@ -247,7 +248,7 @@ func testConfigBuildSimple(t *testing.T) { zap.WrapCore(func(zapcore.Core) zapcore.Core { return zapcore.NewCore( zapcore.NewJSONEncoder(zec), - &output, + zapcore.AddSync(&buffer), zapcore.DebugLevel, ) }), @@ -256,7 +257,7 @@ func testConfigBuildSimple(t *testing.T) { require.NoError(err) require.NotNil(l) l.Info("test message") - assert.Greater(output.Len(), 0) + assert.Greater(buffer.Len(), 0) } func testConfigBuild(t *testing.T) { diff --git a/context.go b/context.go index 2d789be..f8c2b4a 100644 --- a/context.go +++ b/context.go @@ -10,10 +10,6 @@ import ( // within a context.Context instance type contextKey struct{} -// GetLoggerFunc is the function used to get a request-specific logger from -// its context. -type GetLoggerFunc func(context.Context) *zap.Logger - // defaultLogger is used when no logger exists in the context var defaultLogger *zap.Logger = zap.NewNop() @@ -66,13 +62,3 @@ func GetDefault(ctx context.Context, def *zap.Logger) *zap.Logger { return Default() } - -// GetDefaultLogger returns the default logger, which doesn't do anything. -func GetDefaultLogger(_ context.Context) *zap.Logger { - return Default() -} - -// GetNilLogger returns nil. -func GetNilLogger(_ context.Context) *zap.Logger { - return nil -} diff --git a/printer.go b/printer.go deleted file mode 100644 index d8e36d4..0000000 --- a/printer.go +++ /dev/null @@ -1,71 +0,0 @@ -package sallust - -import ( - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -// Printer adapts the zap.SugaredLogger type onto a style of logging more like the fmt package. -// The stdlib's log package uses this approach. Also, go.uber.org/fx can use this type -// as a logger for DI container information: -// -// logger := zap.NewDevelopment() -// fx.New( -// fx.Logger(sallust.Printer{SugaredLogger: logger.Sugar()}), -// // etc -// ) -type Printer struct { - // SugaredLogger is the zap.SugaredLogger being adapted. A Printer still allows all the - // usual logging methods to be called. - *zap.SugaredLogger - - // Level is the log level used to emit print messages. - // If unset, zapcore.InfoLevel is used. - // - // The panic and fatal levels in the zapcore are not supported. - // If this field is set to one of those levels, InfoLevel is used instead. - Level zapcore.Level -} - -// Printf invokes the appropriate "*f" method on the SugaredLogger, based -// on the Level. The go.uber.org/fx package can use this method as an fx.Logger. -// The stdlib log package also exposes a method with this same signature. -func (p Printer) Printf(format string, args ...interface{}) { - switch p.Level { - case zapcore.DebugLevel: - p.SugaredLogger.Debugf(format, args...) - - case zapcore.WarnLevel: - p.SugaredLogger.Warnf(format, args...) - - case zapcore.ErrorLevel: - p.SugaredLogger.Errorf(format, args...) - - case zapcore.InfoLevel: - fallthrough - - default: - p.SugaredLogger.Infof(format, args...) - } -} - -// Print invokes the appropriate leveled fmt.Print-style method on the SugaredLogger, -// based on the Level. -func (p Printer) Print(args ...interface{}) { - switch p.Level { - case zapcore.DebugLevel: - p.SugaredLogger.Debug(args...) - - case zapcore.WarnLevel: - p.SugaredLogger.Warn(args...) - - case zapcore.ErrorLevel: - p.SugaredLogger.Error(args...) - - case zapcore.InfoLevel: - fallthrough - - default: - p.SugaredLogger.Info(args...) - } -} diff --git a/printer_test.go b/printer_test.go deleted file mode 100644 index bb0f7e8..0000000 --- a/printer_test.go +++ /dev/null @@ -1,133 +0,0 @@ -package sallust - -import ( - "strconv" - "testing" - - "github.com/stretchr/testify/assert" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -// exactLevelEnabler is used to allow only (1) particular level of logging. -// This can be used to assert that the appropriate log method got called. -type exactLevelEnabler struct { - Level zapcore.Level -} - -func (ele exactLevelEnabler) Enabled(l zapcore.Level) bool { - return ele.Level == l -} - -func testPrinterPrintf(t *testing.T) { - testData := []struct { - printerLevel zapcore.Level - levelEnabler zapcore.LevelEnabler - }{ - { - printerLevel: zapcore.DebugLevel, - levelEnabler: exactLevelEnabler{Level: zapcore.DebugLevel}, - }, - { - printerLevel: zapcore.InfoLevel, - levelEnabler: exactLevelEnabler{Level: zapcore.InfoLevel}, - }, - { - printerLevel: zapcore.WarnLevel, - levelEnabler: exactLevelEnabler{Level: zapcore.WarnLevel}, - }, - { - printerLevel: zapcore.ErrorLevel, - levelEnabler: exactLevelEnabler{Level: zapcore.ErrorLevel}, - }, - { - printerLevel: zapcore.Level(123), - levelEnabler: exactLevelEnabler{Level: zapcore.InfoLevel}, - }, - } - - for i, record := range testData { - t.Run(strconv.Itoa(i), func(t *testing.T) { - var ( - assert = assert.New(t) - buffer Buffer - core = zapcore.NewCore( - zapcore.NewJSONEncoder(zapcore.EncoderConfig{ - MessageKey: "msg", - LevelKey: "level", - EncodeLevel: zapcore.LowercaseLevelEncoder, - }), - &buffer, - record.levelEnabler, - ) - - logger = zap.New(core) - printer = Printer{SugaredLogger: logger.Sugar(), Level: record.printerLevel} - ) - - // just check to see if the output passed the levelEnabler - printer.Printf("test: %s %d", "string", 123) - logger.Sync() - assert.Greater(buffer.Len(), 0) - }) - } -} - -func testPrinterPrint(t *testing.T) { - testData := []struct { - printerLevel zapcore.Level - levelEnabler zapcore.LevelEnabler - }{ - { - printerLevel: zapcore.DebugLevel, - levelEnabler: exactLevelEnabler{Level: zapcore.DebugLevel}, - }, - { - printerLevel: zapcore.InfoLevel, - levelEnabler: exactLevelEnabler{Level: zapcore.InfoLevel}, - }, - { - printerLevel: zapcore.WarnLevel, - levelEnabler: exactLevelEnabler{Level: zapcore.WarnLevel}, - }, - { - printerLevel: zapcore.ErrorLevel, - levelEnabler: exactLevelEnabler{Level: zapcore.ErrorLevel}, - }, - { - printerLevel: zapcore.Level(123), - levelEnabler: exactLevelEnabler{Level: zapcore.InfoLevel}, - }, - } - - for i, record := range testData { - t.Run(strconv.Itoa(i), func(t *testing.T) { - var ( - assert = assert.New(t) - buffer Buffer - core = zapcore.NewCore( - zapcore.NewJSONEncoder(zapcore.EncoderConfig{ - MessageKey: "msg", - LevelKey: "level", - EncodeLevel: zapcore.LowercaseLevelEncoder, - }), - &buffer, - record.levelEnabler, - ) - - logger = zap.New(core) - printer = Printer{SugaredLogger: logger.Sugar(), Level: record.printerLevel} - ) - - // just check to see if the output passed the levelEnabler - printer.Print("string", 123, -67, 4) - logger.Sync() - assert.Greater(buffer.Len(), 0) - }) - } -} - -func TestPrinter(t *testing.T) { - t.Run("Printf", testPrinterPrintf) - t.Run("Print", testPrinterPrint) -} diff --git a/sallustkit/base_test.go b/sallustkit/base_test.go deleted file mode 100644 index 88945cf..0000000 --- a/sallustkit/base_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package sallustkit - -import ( - "io" - - "github.com/stretchr/testify/suite" - "github.com/xmidt-org/sallust" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -// GokitTestSuite is embedded by other suites to inherit -// the test setup logic. -type GokitTestSuite struct { - suite.Suite - // nolint:staticcheck - core *sallust.CaptureCore - zapLogger *zap.Logger -} - -var _ suite.SetupTestSuite = (*GokitTestSuite)(nil) - -func (suite *GokitTestSuite) SetupTest() { - encoderConfig := zapcore.EncoderConfig{ - MessageKey: "msg", - LevelKey: "level", - NameKey: "logger", - EncodeLevel: zapcore.LowercaseLevelEncoder, - EncodeTime: zapcore.ISO8601TimeEncoder, - EncodeDuration: zapcore.StringDurationEncoder, - } - - suite.core = sallust.NewCaptureCore( - zapcore.NewJSONEncoder(encoderConfig), - zapcore.AddSync(io.Discard), - zap.DebugLevel, // turn on everything - ) - - suite.zapLogger = zap.New(suite.core) -} - -func (suite *GokitTestSuite) assertOneMessage(f func(zapcore.Entry, []zap.Field) error) { - c, err := suite.core.EachMessage(f) - suite.NoError(err, "unexpected error from EachMessage") - suite.Equal(1, c, "wrong number of captured messages") -} diff --git a/sallustkit/doc.go b/sallustkit/doc.go deleted file mode 100644 index 20bfb34..0000000 --- a/sallustkit/doc.go +++ /dev/null @@ -1,5 +0,0 @@ -/* -Package sallustkit provides an adapter layer between go.uber.org/zap logging -and the variadic approach in go-kit. -*/ -package sallustkit diff --git a/sallustkit/logMethod.go b/sallustkit/logMethod.go deleted file mode 100644 index 983ec48..0000000 --- a/sallustkit/logMethod.go +++ /dev/null @@ -1,31 +0,0 @@ -package sallustkit - -import ( - "github.com/go-kit/log/level" - "go.uber.org/zap" -) - -// LogMethod refers to a method of a zap Logger. Used to define -// which method should received go-kit leveled logging, e.g. Error, Info, etc. -type LogMethod func(string, ...zap.Field) - -// LogMethodFor returns the method of a zap Logger that corresponds to -// a given go-kit level. If v is unrecognized, l.Error is returned. -// This function never returns nil. -func LogMethodFor(l *zap.Logger, v level.Value) (lm LogMethod) { - switch v { - case level.DebugValue(): - lm = l.Debug - - case level.InfoValue(): - lm = l.Info - - case level.WarnValue(): - lm = l.Warn - - default: - lm = l.Error - } - - return -} diff --git a/sallustkit/logMethod_test.go b/sallustkit/logMethod_test.go deleted file mode 100644 index 666aa36..0000000 --- a/sallustkit/logMethod_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package sallustkit - -import ( - "testing" - - "github.com/go-kit/log/level" - "github.com/stretchr/testify/suite" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -type LogMethodForTestSuite struct { - GokitTestSuite -} - -var _ suite.SetupTestSuite = (*LogMethodForTestSuite)(nil) - -func (suite *LogMethodForTestSuite) testLevel(expected zapcore.Level, actual level.Value) { - lm := LogMethodFor(suite.zapLogger, actual) - suite.Require().NotNil(lm) - - lm("test message", zap.Int("value", 123)) - suite.assertOneMessage( - func(e zapcore.Entry, fs []zapcore.Field) error { - suite.Equal("test message", e.Message) - suite.Equal(expected, e.Level) - - if suite.Len(fs, 1) { - suite.Equal(zap.Int("value", 123), fs[0]) - } - - return nil - }, - ) -} - -func (suite *LogMethodForTestSuite) TestDebugLevel() { - suite.testLevel(zapcore.DebugLevel, level.DebugValue()) -} - -func (suite *LogMethodForTestSuite) TestInfoLevel() { - suite.testLevel(zapcore.InfoLevel, level.InfoValue()) -} - -func (suite *LogMethodForTestSuite) TestWarnLevel() { - suite.testLevel(zapcore.WarnLevel, level.WarnValue()) -} - -func (suite *LogMethodForTestSuite) TestErrorLevel() { - suite.testLevel(zapcore.ErrorLevel, level.ErrorValue()) -} - -func TestLogMethodFor(t *testing.T) { - suite.Run(t, new(LogMethodForTestSuite)) -} diff --git a/sallustkit/logger.go b/sallustkit/logger.go deleted file mode 100644 index 91af401..0000000 --- a/sallustkit/logger.go +++ /dev/null @@ -1,149 +0,0 @@ -package sallustkit - -import ( - "github.com/go-kit/log" - "github.com/go-kit/log/level" - "go.uber.org/zap" -) - -const ( - // NotAString is used in log output when a key or value which should - // have been a string was not a string. - NotAString = "(NOT A STRING)" - - // NoLogMessage is used as the log message when no message could be - // found in the go-kit keyvals. - NoLogMessage = "No log message found" - - // DefaultMessageKey is the key assumed to hold the log message when no - // MessageKey is set. This is used when parsing go-kit Log keyvals. - // - // NOTE: This is not the same as the key used by zap to output the message - // in its log output. That is controlled by the zap configuration, which - // may be a sallust.Config. - DefaultMessageKey = "msg" -) - -// toString attempts to cast v to a string, returning NotAString -// if it isn't a string. -func toString(v interface{}) string { - s, ok := v.(string) - if ok { - return s - } - - return NotAString -} - -// Logger is a go-kit logger that adapts its output onto a zap logger. -type Logger struct { - // Zap is the zap Logger to which output is sent. This field is required, - // and Log will panic if it is not set. - Zap *zap.Logger - - // MessageKey is the go-kit logging key which holds the log message. - // If unset, DefaultMessageKey is used. - // - // This key is used to pull out the message so that it can be passed - // as the first argument to a zap logger's method, e.g. Error, Info, etc. - MessageKey string - - // DefaultLevel is the go-kit level to use when no level - // is supplied in the keyvals. If unset, Error is used. - DefaultLevel level.Value -} - -var _ log.Logger = Logger{} - -// Log accepts key/value pairs in the typical go-kit fashion and parses them -// to use with the configured zap logger. This method always returns nil. -// If keyvals is empty, then this method returns with no output. -// -// Each key/value pair is examined and used to build up a method call to -// the configured zap logger using the following basic steps: -// -// - Any key that is not a string results in a NotAString key in the zap output -// -// - If keyvals is of odd length, then the last key is emitted as a zap.NamedError -// with the value of go-kit's log.ErrMissingValue. -// -// - The value for any key that equals the configured MessageKey (or, DefaultMessageKey -// if that field is unset) is used as the first parameter to the zap logger method. -// -// - Any value that is a defined go-kit level.Value is used to determine which zap -// logger method is invoked, e.g. level.DebugValue() results in the Debug method, etc. -// In this case, the associated key is ignored. -// -// - Any key/value not matching the above steps is passed to the zap logger method -// as a zap.Any field. -// -// Examples: -// -// given: -// -// l, _ := zap.NewDevelopment() // or any *zap.Logger -// gokit := sallustkit.Logger{ -// Zap: l, -// // take the defaults for the other fields -// } -// -// then: -// -// this: gokit.Log("msg", "hi there", "value", 123) -// becomes: l.Error("hi there", zap.Any("value", 123)) // defaults to error, change this by setting go-kit.DefaultLevel -// -// this: gokit.Log("msg", "more values", "name1", "value1", "name2", 45.6) -// becomes: l.Error("more values", zap.Any("name1", "value1"), zap.Any("name2", 45.6)) -// -// this: gokit.Log(level.Key(), level.InfoValue(), "value", 123) -// becomes: l.Info("No log message found", zap.Any("value", 123)) -// -// this: gokit.Log("msg", "hi there", "this key doesn't matter", level.DebugValue()) -// becomes: l.Debug("hi there") // if a value is a go-kit level.Value, the key is ignored -// -func (l Logger) Log(keyvals ...interface{}) error { - if len(keyvals) > 0 { - var ( - message = NoLogMessage - messageKey = DefaultMessageKey - lvl = l.DefaultLevel - - // this is the maximum size we'll ever need for the fields, - // which means we only ever have (1) allocation - fields = make([]zap.Field, 0, 1+len(keyvals)/2) - ) - - if len(l.MessageKey) > 0 { - messageKey = l.MessageKey - } - - for i, j := 0, 1; j < len(keyvals); i, j = i+2, j+2 { - key := toString(keyvals[i]) - value := keyvals[j] - - if key == messageKey { - message = toString(value) - continue - } - - if fieldLevel, ok := value.(level.Value); ok { - lvl = fieldLevel - continue - } - - fields = append(fields, zap.Any(key, value)) - } - - if len(keyvals)%2 != 0 { - // odd number of keyvals ... - fields = append(fields, zap.NamedError( - toString(keyvals[len(keyvals)-1]), - log.ErrMissingValue, - )) - } - - LogMethodFor(l.Zap, lvl)(message, fields...) - } - - return nil -} diff --git a/sallustkit/logger_examples_test.go b/sallustkit/logger_examples_test.go deleted file mode 100644 index 86873f8..0000000 --- a/sallustkit/logger_examples_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package sallustkit - -import ( - "time" - - "github.com/go-kit/log/level" - "go.uber.org/zap" -) - -func ExampleLogger() { - l := Logger{ - Zap: zap.NewExample(), - } - - l.Log("msg", "hello, world!", "duration", 15*time.Second) - l.Log("msg", "this entry supplies a level", "value", 123, "this key doesn't matter", level.DebugValue()) - - // Output: - // {"level":"error","msg":"hello, world!","duration":"15s"} - // {"level":"debug","msg":"this entry supplies a level","value":123} -} - -func ExampleLogger_custom() { - l := Logger{ - Zap: zap.NewExample(), - MessageKey: "message", - DefaultLevel: level.InfoValue(), - } - - // the message will still be output to whatever zap is configured with - l.Log("message", "hello, world!", "duration", 15*time.Second) - - // override the DefaultLevel with what's passed to Log - l.Log("message", "this entry supplies a level", "value", 123, "this key doesn't matter", level.DebugValue()) - - // Output: - // {"level":"info","msg":"hello, world!","duration":"15s"} - // {"level":"debug","msg":"this entry supplies a level","value":123} -} diff --git a/sallustkit/logger_test.go b/sallustkit/logger_test.go deleted file mode 100644 index c318d75..0000000 --- a/sallustkit/logger_test.go +++ /dev/null @@ -1,195 +0,0 @@ -package sallustkit - -import ( - "testing" - - "github.com/go-kit/log" - "github.com/go-kit/log/level" - "github.com/stretchr/testify/suite" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -type LoggerTestSuite struct { - GokitTestSuite -} - -var _ suite.SetupTestSuite = (*LoggerTestSuite)(nil) - -func (suite *LoggerTestSuite) TestNoOutput() { - l := Logger{ - Zap: suite.zapLogger, - } - - suite.NoError( - l.Log(), - ) - - suite.Zero(suite.core.Len()) -} - -func (suite *LoggerTestSuite) TestNoZap() { - l := Logger{} - - suite.Panics(func() { - l.Log("msg", "this shouldn't exist!") - }) -} - -func (suite *LoggerTestSuite) TestNoSuppliedLevel() { - suite.Run("NoDefaultLevel", func() { - suite.core.ClearMessages() - l := Logger{ - Zap: suite.zapLogger, - } - - suite.NoError( - l.Log(DefaultMessageKey, "test message", "value", 123), - ) - - suite.assertOneMessage(func(e zapcore.Entry, fs []zap.Field) error { - suite.Equal("test message", e.Message) - suite.Equal(zap.ErrorLevel, e.Level) - suite.Equal( - []zap.Field{zap.Int("value", 123)}, - fs, - ) - - return nil - }) - }) - - suite.Run("WithDefaultLevel", func() { - suite.core.ClearMessages() - l := Logger{ - Zap: suite.zapLogger, - DefaultLevel: level.InfoValue(), - } - - suite.NoError( - l.Log(DefaultMessageKey, "test message", "value", 123), - ) - - suite.assertOneMessage(func(e zapcore.Entry, fs []zap.Field) error { - suite.Equal("test message", e.Message) - suite.Equal(zap.InfoLevel, e.Level) - suite.Equal( - []zap.Field{zap.Int("value", 123)}, - fs, - ) - - return nil - }) - }) -} - -func (suite *LoggerTestSuite) TestLevelSupplied() { - l := Logger{ - Zap: suite.zapLogger, - DefaultLevel: level.WarnValue(), // verify that this is overwritten - } - - suite.NoError( - l.Log("value", 123, DefaultMessageKey, "test message", "doesn't matter", level.InfoValue()), - ) - - suite.assertOneMessage(func(e zapcore.Entry, fs []zap.Field) error { - suite.Equal("test message", e.Message) - suite.Equal(zap.InfoLevel, e.Level) - suite.Equal( - []zap.Field{zap.Int("value", 123)}, - fs, - ) - - return nil - }) -} - -func (suite *LoggerTestSuite) TestNoMessageSupplied() { - l := Logger{ - Zap: suite.zapLogger, - } - - suite.NoError( - l.Log("value", 123), - ) - - suite.assertOneMessage(func(e zapcore.Entry, fs []zap.Field) error { - suite.Equal(NoLogMessage, e.Message) - suite.Equal(zap.ErrorLevel, e.Level) - suite.Equal( - []zap.Field{zap.Int("value", 123)}, - fs, - ) - - return nil - }) -} - -func (suite *LoggerTestSuite) TestCustomMessageKey() { - l := Logger{ - Zap: suite.zapLogger, - MessageKey: "custom", - } - - suite.NoError( - l.Log("value", 123, "custom", "test message"), - ) - - suite.assertOneMessage(func(e zapcore.Entry, fs []zap.Field) error { - suite.Equal("test message", e.Message) - suite.Equal(zap.ErrorLevel, e.Level) - suite.Equal( - []zap.Field{zap.Int("value", 123)}, - fs, - ) - - return nil - }) -} - -func (suite *LoggerTestSuite) TestOddKeyvals() { - l := Logger{ - Zap: suite.zapLogger, - } - - suite.NoError( - l.Log("value", 123, DefaultMessageKey, "test message", "dangling"), - ) - - suite.assertOneMessage(func(e zapcore.Entry, fs []zap.Field) error { - suite.Equal("test message", e.Message) - suite.Equal(zap.ErrorLevel, e.Level) - suite.ElementsMatch( - []zap.Field{zap.Int("value", 123), zap.NamedError("dangling", log.ErrMissingValue)}, - fs, - ) - - return nil - }) -} - -func (suite *LoggerTestSuite) TestNotAString() { - l := Logger{ - Zap: suite.zapLogger, - } - - suite.NoError( - l.Log(45.6, 123, DefaultMessageKey, "test message"), - ) - - suite.assertOneMessage(func(e zapcore.Entry, fs []zap.Field) error { - suite.Equal("test message", e.Message) - suite.Equal(zap.ErrorLevel, e.Level) - suite.ElementsMatch( - []zap.Field{zap.Any(NotAString, 123)}, - fs, - ) - - return nil - }) -} - -func TestLogger(t *testing.T) { - suite.Run(t, new(LoggerTestSuite)) -}