From d661831ff99451116db16906275902a8b2158639 Mon Sep 17 00:00:00 2001 From: Tim Hockin Date: Sun, 16 May 2021 16:26:45 -0700 Subject: [PATCH] Add LogSink.Init() mechanism This allows logr to pass params about internal stuff (such as the number of call frames it adds) to LogSink implementations. --- discard.go | 3 +++ examples/tab_logger.go | 3 +++ funcr/funcr.go | 42 +++++++++++++++++++++++++----------------- logr.go | 20 ++++++++++++++++++++ logr_test.go | 3 +++ 5 files changed, 54 insertions(+), 17 deletions(-) diff --git a/discard.go b/discard.go index 9a36257..97e64c2 100644 --- a/discard.go +++ b/discard.go @@ -31,6 +31,9 @@ type DiscardLogger struct{} // Verify that it actually implements the interface var _ LogSink = DiscardLogger{} +func (l DiscardLogger) Init(RuntimeInfo) { +} + func (l DiscardLogger) Enabled(int) bool { return false } diff --git a/examples/tab_logger.go b/examples/tab_logger.go index 46df2db..84892ae 100644 --- a/examples/tab_logger.go +++ b/examples/tab_logger.go @@ -34,6 +34,9 @@ type tabLogSink struct { var _ logr.LogSink = tabLogSink{} +func (_ tabLogSink) Init(info logr.RuntimeInfo) { +} + func (_ tabLogSink) Enabled(level int) bool { return true } diff --git a/funcr/funcr.go b/funcr/funcr.go index 34c19f4..4262969 100644 --- a/funcr/funcr.go +++ b/funcr/funcr.go @@ -33,7 +33,7 @@ import ( // New returns a logr.Logger which is implemented by a function. func New(fn func(prefix, args string), opts Options) logr.Logger { - fnl := fnlogger{ + fnl := &fnlogger{ prefix: "", values: nil, depth: 0, @@ -267,16 +267,18 @@ type callerID struct { } func (l fnlogger) caller() callerID { - // +1 for this frame, +1 for logr itself. - // FIXME: Maybe logr should offer a clue as to how many frames are - // needed here? Or is it part of the contract to LogSinks? - _, file, line, ok := runtime.Caller(framesToCaller() + l.depth + 2) + // +1 for this frame. + _, file, line, ok := runtime.Caller(framesToCaller() + l.depth + 1) if !ok { return callerID{"", 0} } return callerID{filepath.Base(file), line} } +func (l *fnlogger) Init(info logr.RuntimeInfo) { + l.depth += info.CallDepth +} + func (l fnlogger) Enabled(level int) bool { return level <= l.verbosity } @@ -313,25 +315,31 @@ func (l fnlogger) Error(err error, msg string, kvList ...interface{}) { // WithName returns a new Logger with the specified name appended. funcr // uses '/' characters to separate name elements. Callers should not pass '/' // in the provided name string, but this library does not actually enforce that. -func (l fnlogger) WithName(name string) logr.LogSink { - if len(l.prefix) > 0 { - l.prefix = l.prefix + "/" +func (l *fnlogger) WithName(name string) logr.LogSink { + l2 := &fnlogger{} + *l2 = *l + if len(l2.prefix) > 0 { + l.prefix = l2.prefix + "/" } - l.prefix += name + l2.prefix += name return l } -func (l fnlogger) WithValues(kvList ...interface{}) logr.LogSink { +func (l *fnlogger) WithValues(kvList ...interface{}) logr.LogSink { + l2 := &fnlogger{} + *l2 = *l // Three slice args forces a copy. n := len(l.values) - l.values = append(l.values[:n:n], kvList...) - return l + l2.values = append(l2.values[:n:n], kvList...) + return l2 } -func (l fnlogger) WithCallDepth(depth int) logr.LogSink { - l.depth += depth - return l +func (l *fnlogger) WithCallDepth(depth int) logr.LogSink { + l2 := &fnlogger{} + *l2 = *l + l2.depth += depth + return l2 } -var _ logr.LogSink = fnlogger{} -var _ logr.CallDepthLogSink = fnlogger{} +var _ logr.LogSink = &fnlogger{} +var _ logr.CallDepthLogSink = &fnlogger{} diff --git a/logr.go b/logr.go index 05e36e5..c0eb273 100644 --- a/logr.go +++ b/logr.go @@ -178,6 +178,7 @@ func New(sink LogSink) Logger { if withCallDepth, ok := sink.(CallDepthLogSink); ok { logger.withCallDepth = withCallDepth } + sink.Init(runtimeInfo) return logger } @@ -319,9 +320,28 @@ func NewContext(ctx context.Context, logger Logger) context.Context { return context.WithValue(ctx, contextKey{}, logger) } +// RuntimeInfo holds information that the logr "core" library knows which +// LogSinks might want to know. +type RuntimeInfo struct { + // CallDepth is the number of call frames the logr library adds between the + // end-user and the LogSink. LogSink implementations which choose to print + // the original logging site (e.g. file & line) should climb this many + // additional frames to find it. + CallDepth int +} + +// runtimeInfo is a static global. It must not be changed at run time. +var runtimeInfo = RuntimeInfo{ + CallDepth: 1, +} + // LogSink represents a logging implementation. End-users will generally not // interact with this type. type LogSink interface { + // Init receives optional information about the logr library for LogSink + // implementations that need it. + Init(info RuntimeInfo) + // Enabled tests whether this LogSink is enabled at the specified V-level. // For example, commandline flags might be used to set the logging // verbosity and disable some info logs. diff --git a/logr_test.go b/logr_test.go index 60a1c7b..0d2b0d2 100644 --- a/logr_test.go +++ b/logr_test.go @@ -24,6 +24,9 @@ import ( // testLogSink is a Logger just for testing that does nothing. type testLogSink struct{} +func (l *testLogSink) Init(RuntimeInfo) { +} + func (l *testLogSink) Enabled(int) bool { return false }