From 7f484e3be4ca6014f2ffd473694f621fffe0a2e6 Mon Sep 17 00:00:00 2001 From: Nathan Johnson Date: Wed, 1 Jun 2022 14:35:07 -0500 Subject: [PATCH] Removes hard dependency on logrus for logging Provides backwards compatibility with existing logrus via SetLogger() method Introduces a Logger interface that others can implement for other structured loggers such as zerolog. Add SetLoggerFromLogger method that allows caller to pass in an implementation of the new Logger interface. Bumps the golang.org/x/sys dependency since tests fail to run on go 1.18 with the old version. adding a test case for LogrusLogger adding benchmark, add WithFields method because it's a lot faster apparently --- go.mod | 1 + go.sum | 6 ++-- groupcache.go | 20 +++++++---- logger.go | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++ logger_test.go | 77 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 191 insertions(+), 8 deletions(-) create mode 100644 logger.go create mode 100644 logger_test.go 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..b2ab43f 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,13 @@ 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..533ad6d --- /dev/null +++ b/logger.go @@ -0,0 +1,95 @@ +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(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() + } +}