From b8e1fa2e1554e8536a42c911a2b3faed42b367f0 Mon Sep 17 00:00:00 2001 From: Stephen J Day Date: Wed, 9 Mar 2016 22:40:35 -0800 Subject: [PATCH 1/5] log: provide simple context logging We consolidate simple context logging for use across packages. For now, one still needs logrus, but we can take this further and avoid the import at a later time. Signed-off-by: Stephen J Day --- log/context.go | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 log/context.go diff --git a/log/context.go b/log/context.go new file mode 100644 index 0000000..b919e35 --- /dev/null +++ b/log/context.go @@ -0,0 +1,37 @@ +package log + +import ( + "github.com/Sirupsen/logrus" + "golang.org/x/net/context" +) + +var ( + // G is an alias for GetLogger. + // + // We may want to define this locally to a package to get package tagged log + // messages. + G = GetLogger + + // L is an alias for the the standard logger. + L = logrus.NewEntry(logrus.StandardLogger()) +) + +type loggerKey struct{} + +// WithLogger returns a new context with the provided logger. Use in +// combination with logger.WithField(s) for great effect. +func WithLogger(ctx context.Context, logger *logrus.Entry) context.Context { + return context.WithValue(ctx, loggerKey{}, logger) +} + +// GetLogger retrieves the current logger from the context. If no logger is +// available, the default logger is returned. +func GetLogger(ctx context.Context) *logrus.Entry { + logger := ctx.Value(loggerKey{}) + + if logger == nil { + return L + } + + return logger.(*logrus.Entry) +} From 41aadb3fb03f3a92d7f700abd703c97c5a7ded6d Mon Sep 17 00:00:00 2001 From: Stephen J Day Date: Thu, 12 May 2016 15:53:32 -0700 Subject: [PATCH 2/5] log: replace grpc logger with logrus Signed-off-by: Stephen J Day --- log/grpc.go | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 log/grpc.go diff --git a/log/grpc.go b/log/grpc.go new file mode 100644 index 0000000..df6e932 --- /dev/null +++ b/log/grpc.go @@ -0,0 +1,8 @@ +package log + +import "google.golang.org/grpc/grpclog" + +func init() { + // completely replace the grpc logger with the logrus logger. + grpclog.SetLogger(L) +} From 5288d8f9fe64984ded221bc088716263cbbdc6d3 Mon Sep 17 00:00:00 2001 From: Stephen J Day Date: Tue, 2 Aug 2016 16:41:49 -0700 Subject: [PATCH 3/5] log: improve module-oriented logging To improve the output of module logging, we now include a path that represents the modules that a context has passed through. This makes it easy to tell if a log line came from a particular module hierarchy. A helper function, `WithModule`, provides an easy way to mark a context with a module and inject that module into the logger. Signed-off-by: Stephen J Day --- log/context.go | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/log/context.go b/log/context.go index b919e35..4539e47 100644 --- a/log/context.go +++ b/log/context.go @@ -1,6 +1,8 @@ package log import ( + "path" + "github.com/Sirupsen/logrus" "golang.org/x/net/context" ) @@ -16,7 +18,10 @@ var ( L = logrus.NewEntry(logrus.StandardLogger()) ) -type loggerKey struct{} +type ( + loggerKey struct{} + moduleKey struct{} +) // WithLogger returns a new context with the provided logger. Use in // combination with logger.WithField(s) for great effect. @@ -35,3 +40,42 @@ func GetLogger(ctx context.Context) *logrus.Entry { return logger.(*logrus.Entry) } + +// WithModule adds the module to the context, appending it with a slash if a +// module already exists. A module is just an roughly correlated defined by the +// call tree for a given context. +// +// As an example, we might have a "node" module already part of a context. If +// this function is called with "tls", the new value of module will be +// "node/tls". +// +// Modules represent the call path. If the new module and last module are the +// same, a new module entry will not be created. If the new module and old +// older module are the same but separated by other modules, the cycle will be +// represented by the module path. +func WithModule(ctx context.Context, module string) context.Context { + parent := GetModulePath(ctx) + + if parent != "" { + // don't re-append module when module is the same. + if path.Base(parent) == module { + return ctx + } + + module = path.Join(parent, module) + } + + ctx = WithLogger(ctx, GetLogger(ctx).WithField("module", module)) + return context.WithValue(ctx, moduleKey{}, module) +} + +// GetModulePath returns the module path for the provided context. If no module +// is set, an empty string is returned. +func GetModulePath(ctx context.Context) string { + module := ctx.Value(moduleKey{}) + if module == nil { + return "" + } + + return module.(string) +} From deb62729bae72936ade3e7649deafffb7aff725b Mon Sep 17 00:00:00 2001 From: Stephen J Day Date: Tue, 2 Aug 2016 17:44:06 -0700 Subject: [PATCH 4/5] log: add testing for context logger Signed-off-by: Stephen J Day --- log/context_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 log/context_test.go diff --git a/log/context_test.go b/log/context_test.go new file mode 100644 index 0000000..ddff398 --- /dev/null +++ b/log/context_test.go @@ -0,0 +1,41 @@ +package log + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "golang.org/x/net/context" +) + +func TestLoggerContext(t *testing.T) { + ctx := context.Background() + assert.Equal(t, GetLogger(ctx), L) // should be same as L variable + assert.Equal(t, G(ctx), GetLogger(ctx)) // these should be the same. + + ctx = WithLogger(ctx, G(ctx).WithField("test", "one")) + assert.Equal(t, GetLogger(ctx).Data["test"], "one") + assert.Equal(t, G(ctx), GetLogger(ctx)) // these should be the same. +} + +func TestModuleContext(t *testing.T) { + ctx := context.Background() + assert.Equal(t, GetModulePath(ctx), "") + + ctx = WithModule(ctx, "a") // basic behavior + assert.Equal(t, GetModulePath(ctx), "a") + logger := GetLogger(ctx) + assert.Equal(t, logger.Data["module"], "a") + + parent, ctx := ctx, WithModule(ctx, "a") + assert.Equal(t, ctx, parent) // should be a no-op + assert.Equal(t, GetModulePath(ctx), "a") + assert.Equal(t, GetLogger(ctx).Data["module"], "a") + + ctx = WithModule(ctx, "b") // new module + assert.Equal(t, GetModulePath(ctx), "a/b") + assert.Equal(t, GetLogger(ctx).Data["module"], "a/b") + + ctx = WithModule(ctx, "c") // new module + assert.Equal(t, GetModulePath(ctx), "a/b/c") + assert.Equal(t, GetLogger(ctx).Data["module"], "a/b/c") +} From cd4f1f98d8258c0a520e2b67da63319aef687833 Mon Sep 17 00:00:00 2001 From: Stephen J Day Date: Wed, 30 Nov 2016 11:51:59 -0800 Subject: [PATCH 5/5] log: mark grpc messages with module Signed-off-by: Stephen J Day --- log/grpc.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/log/grpc.go b/log/grpc.go index df6e932..4978d49 100644 --- a/log/grpc.go +++ b/log/grpc.go @@ -1,8 +1,13 @@ package log -import "google.golang.org/grpc/grpclog" +import ( + "golang.org/x/net/context" + "google.golang.org/grpc/grpclog" +) func init() { + ctx := WithModule(context.Background(), "grpc") + // completely replace the grpc logger with the logrus logger. - grpclog.SetLogger(L) + grpclog.SetLogger(G(ctx)) }