Skip to content

Commit

Permalink
Merge pull request #31 from thockin/with-call-depth
Browse files Browse the repository at this point in the history
WIP: Add logr.WithCallDepth()
  • Loading branch information
thockin authored Jan 23, 2021
2 parents 3dceec2 + 52a0608 commit 9721152
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 1 deletion.
45 changes: 44 additions & 1 deletion logr.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ import (

// TODO: consider adding back in format strings if they're really needed
// TODO: consider other bits of zap/zapcore functionality like ObjectMarshaller (for arbitrary objects)
// TODO: consider other bits of glog functionality like Flush, InfoDepth, OutputStats
// TODO: consider other bits of glog functionality like Flush, OutputStats

// Logger represents the ability to log messages, both errors and not.
type Logger interface {
Expand Down Expand Up @@ -219,3 +219,46 @@ func FromContextOrDiscard(ctx context.Context) Logger {
func NewContext(ctx context.Context, l Logger) context.Context {
return context.WithValue(ctx, contextKey{}, l)
}

// CallDepthLogger represents a Logger that knows how to climb the call stack
// to identify the original call site and can offset the depth by a specified
// number of frames. This is useful for users who have helper functions
// between the "real" call site and the actual calls to Logger methods.
// Implementations that log information about the call site (such as file,
// function, or line) would otherwise log information about the intermediate
// helper functions.
//
// This is an optional interface and implementations are not required to
// support it.
type CallDepthLogger interface {
Logger

// WithCallDepth returns a Logger that will offset the call stack by the
// specified number of frames when logging call site information. If depth
// is 0 the attribution should be to the direct caller of this method. If
// depth is 1 the attribution should skip 1 call frame, and so on.
// Successive calls to this are additive.
WithCallDepth(depth int) Logger
}

// WithCallDepth returns a Logger that will offset the call stack by the
// specified number of frames when logging call site information, if possible.
// This is useful for users who have helper functions between the "real" call
// site and the actual calls to Logger methods. If depth is 0 the attribution
// should be to the direct caller of this function. If depth is 1 the
// attribution should skip 1 call frame, and so on. Successive calls to this
// are additive.
//
// If the underlying log implementation supports the CallDepthLogger interface,
// the WithCallDepth method will be called and the result returned. If the
// implementation does not support CallDepthLogger, the original Logger will be
// returned.
//
// Callers which care about whether this was supported or not should test for
// CallDepthLogger support themselves.
func WithCallDepth(logger Logger, depth int) Logger {
if decorator, ok := logger.(CallDepthLogger); ok {
return decorator.WithCallDepth(depth)
}
return logger
}
36 changes: 36 additions & 0 deletions logr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,39 @@ func TestContext(t *testing.T) {
t.Errorf("expected output to be the same as input: got in=%p, out=%p", logger, out)
}
}

// testCallDepthLogger is a Logger just for testing that does nothing.
type testCallDepthLogger struct {
*testLogger
depth int
}

func (l *testCallDepthLogger) WithCallDepth(depth int) Logger {
return &testCallDepthLogger{l.testLogger, l.depth + depth}
}

// Verify that it actually implements the interface
var _ CallDepthLogger = &testCallDepthLogger{}

func TestWithCallDepth(t *testing.T) {
// Test an impl that does not support it.
t.Run("not supported", func(t *testing.T) {
in := &testLogger{}
out := WithCallDepth(in, 42)
if out.(*testLogger) != in {
t.Errorf("expected output to be the same as input: got in=%p, out=%p", in, out)
}
})

// Test an impl that does support it.
t.Run("supported", func(t *testing.T) {
in := &testCallDepthLogger{&testLogger{}, 0}
out := WithCallDepth(in, 42)
if out.(*testCallDepthLogger) == in {
t.Errorf("expected output to be different than input: got in=out=%p", in)
}
if cdl := out.(*testCallDepthLogger); cdl.depth != 42 {
t.Errorf("expected depth=42, got %d", cdl.depth)
}
})
}

0 comments on commit 9721152

Please sign in to comment.