diff --git a/go.mod b/go.mod index 1c6c15b..f35adcb 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ require ( github.com/golang/protobuf v1.3.1 github.com/segmentio/fasthash v1.0.3 github.com/sirupsen/logrus v1.6.0 + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect ) go 1.15 diff --git a/go.sum b/go.sum index 50c5960..e6f4bb4 100644 --- a/go.sum +++ b/go.sum @@ -6,11 +6,13 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM= +github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM= -github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/groupcache.go b/groupcache.go index 4815437..1eff499 100644 --- a/groupcache.go +++ b/groupcache.go @@ -38,9 +38,15 @@ import ( "github.com/sirupsen/logrus" ) -var logger *logrus.Entry +var logger Logger +// SetLogger - this is legacy to provide backwards compatibility with logrus. func SetLogger(log *logrus.Entry) { + logger = LogrusLogger{Entry: log} +} + +// SetLoggerFromLogger - set the logger to an implementation of the Logger interface +func SetLoggerFromLogger(log Logger) { logger = log } @@ -392,11 +398,12 @@ func (g *Group) load(ctx context.Context, key string, dest Sink) (value ByteView } if logger != nil { - logger.WithFields(logrus.Fields{ - "err": err, - "key": key, - "category": "groupcache", - }).Errorf("error retrieving key from peer '%s'", peer.GetURL()) + logger.Error(). + WithFields(map[string]interface{}{ + "err": err, + "key": key, + "category": "groupcache", + }).Printf("error retrieving key from peer '%s'", peer.GetURL()) } g.Stats.PeerErrors.Add(1) diff --git a/logger.go b/logger.go new file mode 100644 index 0000000..20501e2 --- /dev/null +++ b/logger.go @@ -0,0 +1,96 @@ +package groupcache + +import ( + "github.com/sirupsen/logrus" +) + +// Logger is a minimal interface that will allow us to use structured loggers, +// including (but not limited to) logrus. +type Logger interface { + // Error logging level + Error() Logger + + // Warn logging level + Warn() Logger + + // Info logging level + Info() Logger + + // Debug logging level + Debug() Logger + + // ErrorField is a field with an error value + ErrorField(label string, err error) Logger + + // StringField is a field with a string value + StringField(label string, val string) Logger + + // WithFields is willy-nilly key value pairs. + WithFields(fields map[string]interface{}) Logger + + // Printf is called last to emit the log at the given + // level. + Printf(format string, args ...interface{}) +} + +// LogrusLogger is an implementation of Logger that wraps logrus... who knew? +type LogrusLogger struct { + Entry *logrus.Entry + level logrus.Level +} + +func (l LogrusLogger) Info() Logger { + return LogrusLogger{ + Entry: l.Entry, + level: logrus.InfoLevel, + } +} + +func (l LogrusLogger) Debug() Logger { + return LogrusLogger{ + Entry: l.Entry, + level: logrus.DebugLevel, + } +} + +func (l LogrusLogger) Warn() Logger { + return LogrusLogger{ + Entry: l.Entry, + level: logrus.WarnLevel, + } +} + +func (l LogrusLogger) Error() Logger { + return LogrusLogger{ + Entry: l.Entry, + level: logrus.ErrorLevel, + } +} + +func (l LogrusLogger) WithFields(fields map[string]interface{}) Logger { + return LogrusLogger{ + Entry: l.Entry.WithFields(fields), + level: l.level, + } +} + +// ErrorField - create a field for an error +func (l LogrusLogger) ErrorField(label string, err error) Logger { + return LogrusLogger{ + Entry: l.Entry.WithField(label, err), + level: l.level, + } + +} + +// StringField - create a field for a string. +func (l LogrusLogger) StringField(label string, val string) Logger { + return LogrusLogger{ + Entry: l.Entry.WithField(label, val), + level: l.level, + } +} + +func (l LogrusLogger) Printf(format string, args ...interface{}) { + l.Entry.Logf(l.level, format, args...) +} diff --git a/logger_test.go b/logger_test.go new file mode 100644 index 0000000..164fa16 --- /dev/null +++ b/logger_test.go @@ -0,0 +1,77 @@ +package groupcache + +import ( + "bytes" + "errors" + "github.com/sirupsen/logrus" + "testing" +) + +// This tests the compatibility of the LogrusLogger with the previous behavior. +func TestLogrusLogger(t *testing.T) { + var buf bytes.Buffer + l := logrus.New() + l.SetFormatter(&logrus.TextFormatter{ + DisableTimestamp: true, + }) + l.Out = &buf + e := logrus.NewEntry(l) + e = e.WithField("ContextKey", "ContextVal") + SetLogger(e) + logger.Error(). + WithFields(map[string]interface{}{ + "err": errors.New("test error"), + "key": "keyValue", + "category": "groupcache", + }).Printf("error retrieving key from peer %s", "http://127.0.0.1:8080") + + interfaceOut := buf.String() + buf.Reset() + e.WithFields(logrus.Fields{ + "err": errors.New("test error"), + "key": "keyValue", + "category": "groupcache", + }).Errorf("error retrieving key from peer %s", "http://127.0.0.1:8080") + logrusOut := buf.String() + if interfaceOut != logrusOut { + t.Errorf("output is not the same.\ngot:\n%s\nwant:\n%s", interfaceOut, logrusOut) + } +} + +func BenchmarkLogrusLogger(b *testing.B) { + var buf bytes.Buffer + l := logrus.New() + l.SetFormatter(&logrus.TextFormatter{ + DisableTimestamp: true, + }) + l.Out = &buf + e := logrus.NewEntry(l) + SetLogger(e) + for i := 0; i < b.N; i++ { + logger.Error(). + WithFields(map[string]interface{}{ + "err": errors.New("test error"), + "key": "keyValue", + "category": "groupcache", + }).Printf("error retrieving key from peer %s", "http://127.0.0.1:8080") + buf.Reset() + } +} + +func BenchmarkLogrus(b *testing.B) { + var buf bytes.Buffer + l := logrus.New() + l.SetFormatter(&logrus.TextFormatter{ + DisableTimestamp: true, + }) + l.Out = &buf + e := logrus.NewEntry(l) + for i := 0; i < b.N; i++ { + e.WithFields(logrus.Fields{ + "err": errors.New("test error"), + "key": "keyValue", + "category": "groupcache", + }).Errorf("error retrieving key from peer %s", "http://127.0.0.1:8080") + buf.Reset() + } +}