Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support alternative way of skipping frames #60

Merged
merged 2 commits into from
Aug 18, 2021

Conversation

pohly
Copy link
Contributor

@pohly pohly commented Aug 4, 2021

This is needed for log sinks where the underlying stack unwinding
doesn't support an explicit call depth, for example testing.T.Log.

One such log sink is currently pending in kubernetes/klog#240.
It currently incorrectly logs logr.go as call site.

@pohly
Copy link
Contributor Author

pohly commented Aug 4, 2021

I'm just posting this for discussion. I still need to check whether this works as intended...

@pohly
Copy link
Contributor Author

pohly commented Aug 4, 2021

No, it doesn't 😢

Implementing the proposed Helper() LogSink runs into the same limitation as before:

func (l *tlogger) Helper() logr.LogSink {
	l.l.Helper()
	return l
}

It marks itself as helper, not its caller.

Looks like enhancements in the standard library will be necessary to address this. Scary.

@pohly
Copy link
Contributor Author

pohly commented Aug 4, 2021

This approach can be made to work by returning the underlying function that then needs to be called by the helper functions. I've pushed another commit for that and tested that it works for the testing logger.

However, the resulting API is definitely not nice and naming probably should be improved (too many "helpers"...).

logr.go Show resolved Hide resolved
Copy link
Contributor

@thockin thockin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I patched this in and hacked on logr/testing and funcr and I can't make it work. The call to Helper() being in a func() means we're still off by one. Will look more later perhaps.

@pohly
Copy link
Contributor Author

pohly commented Aug 4, 2021

I've pushed further commits to make this work in the testing logger. With those changes I now get the expected result:

logr/testing $ go test -v .
=== RUN   TestTestLogger
    test_test.go:28: : "level"=0 "msg"="info"
    test_test.go:29: : "level"=0 "msg"="V(0).info"
    test_test.go:31: : "msg"="error" "error"="error"
    test_test.go:33: : "level"=0 "msg"="hello world"
--- PASS: TestTestLogger (0.00s)
PASS

funcr/funcr.go Outdated
prefix: "",
values: nil,
depth: 0,
write: fn,
logCaller: opts.LogCaller,
verbosity: opts.Verbosity,
helper: opts.Helper,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gofmt should have lined these up?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for that. My gofmt integration in emacs sometimes breaks down without me noticing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Copy link
Contributor

@thockin thockin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks OK overall.

  1. Please squash and make sure the commit and PR messages are clean and helpful.

  2. Can you please add the following file as funcr/example/main.go:

/*
Copyright 2021 The logr Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"fmt"

	"github.com/go-logr/logr"
	"github.com/go-logr/logr/funcr"
)

type E struct {
	str string
}

func (e E) Error() string {
	return e.str
}

func Helper(log logr.Logger, msg string) {
	helper2(log, msg)
}

func helper2(log logr.Logger, msg string) {
	log.WithCallDepth(2).Info(msg)
}

func main() {
	log := funcr.New(
		func(pfx, args string) { fmt.Println(pfx, args) },
		funcr.Options{LogCaller: funcr.All})
	example(log.WithValues("module", "example"))
}

// If this were in another package, all it would depend on in logr, not glogr.
func example(log logr.Logger) {
	log.Info("hello", "val1", 1, "val2", map[string]int{"k": 1})
	log.V(1).Info("you should see this")
	log.V(1).V(1).Info("you should NOT see this")
	log.Error(nil, "uh oh", "trouble", true, "reasons", []float64{0.1, 0.11, 3.14})
	log.Error(E{"an error occurred"}, "goodbye", "code", -1)
	Helper(log, "thru a helper")
}

logr.go Outdated
@@ -188,6 +191,13 @@ type Logger struct {
level int
sink LogSink
withCallDepth CallDepthLogSink

// withHelper is set in Init if the log sink supports the interface.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to be super nit-picky to avoid followups :)

	// withHelper is set during init if the log sink supports the HelperLogSink
	// interface.
	//
	// We only need the function returned by withHelper.GetHelper(), but
	// cannot store it here because that makes Logger non-comparable,
	// which is something we want to guarantee.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

logr.go Show resolved Hide resolved
logr.go Outdated
@@ -261,6 +277,10 @@ func (l Logger) WithName(name string) Logger {
// If the underlying log implementation supports a WithCallDepth(int) method,
// it will be called and the result returned. If the implementation does not
// support CallDepthLogSink, the original Logger will be returned.
//
// Helper() should be used instead of WithCallDepth(1) because Helper()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super nit - the () should be removed unless you are describing an actual call (e.g. WithCallDepth(1) is ok)

logr.go Outdated
@@ -269,6 +289,32 @@ func (l Logger) WithCallDepth(depth int) Logger {
return l
}

// Helper returns a Logger instance that skips the direct caller when
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super nit: returns a new Logger instance

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

logr.go Show resolved Hide resolved
logr.go Show resolved Hide resolved
logr.go Outdated
// 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.
// WithCallDepth returns a log sink that will offset the call stack by the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/log sink/LogSink/

logr.go Outdated
// Successive calls to this are additive.
WithCallDepth(depth int) LogSink
}

// HelperLogSink represents a Logger that knows how to climb the call stack
// to identify the original call site and can skip intermediate helper functions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hard to tell but these lines look like thy might be > 80 columns? I have tried to keep comments wrapped cleanly

logr.go Outdated
// intermediate helper functions.
//
// This is an optional interface and implementations are not required to
// support it.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add "Implementations that choose to support this must not simply implement it as WithCallDepth(1), because Logger.Helper() will call both methods if they are present. This should only be implemented for LogSinks that actually need it, as with testing.T." or something like that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logger.Helper() or Logger.Helper? My chance for nit picking... 😀

I've added it with Logger.Helper.

funcr/funcr.go Outdated
@@ -55,6 +62,10 @@ type Options struct {
// Verbosity tells funcr which V logs to be write. Higher values enable
// more logs.
Verbosity int

// Helper is an optional function that funcr must call to mark its own
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/must call/will call/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.

@pohly
Copy link
Contributor Author

pohly commented Aug 5, 2021

Looks OK overall.

So we are really doing this, despite the awkward API? Okay.

Please squash and make sure the commit and PR messages are clean and helpful.

Will do.

Can you please add the following file as funcr/example/main.go:

Okay, just one question about this:

// If this were in another package, all it would depend on in logr, not glogr.
func example(log logr.Logger) {
log.Info("hello", "val1", 1, "val2", map[string]int{"k": 1})
log.V(1).Info("you should see this")
    ...

I don't understand the comment. There's no reference to glogr in the file. Looks like a cut-and-paste problem, I'll simply drop the comment unless I hear otherwise.

@thockin
Copy link
Contributor

thockin commented Aug 5, 2021 via email

@thockin
Copy link
Contributor

thockin commented Aug 5, 2021

So we are really doing this, despite the awkward API? Okay.

I don't love it, but given how testing.T,Helper() is implemented, I really don't see any other answer - the func HAS to be called from the helper-func's stack frame.

Before we merge this, I'll let it sit for a bit and see if anyone else has creative alternatives. I am doubtful, but maybe...

Copy link
Contributor Author

@pohly pohly left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Force-pushed.

funcr/funcr.go Outdated
prefix: "",
values: nil,
depth: 0,
write: fn,
logCaller: opts.LogCaller,
verbosity: opts.Verbosity,
helper: opts.Helper,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

funcr/funcr.go Outdated
@@ -55,6 +62,10 @@ type Options struct {
// Verbosity tells funcr which V logs to be write. Higher values enable
// more logs.
Verbosity int

// Helper is an optional function that funcr must call to mark its own
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.

logr.go Show resolved Hide resolved
logr.go Outdated
@@ -188,6 +191,13 @@ type Logger struct {
level int
sink LogSink
withCallDepth CallDepthLogSink

// withHelper is set in Init if the log sink supports the interface.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

logr.go Outdated
@@ -269,6 +289,32 @@ func (l Logger) WithCallDepth(depth int) Logger {
return l
}

// Helper returns a Logger instance that skips the direct caller when
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

logr.go Show resolved Hide resolved
logr.go Outdated
// intermediate helper functions.
//
// This is an optional interface and implementations are not required to
// support it.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logger.Helper() or Logger.Helper? My chance for nit picking... 😀

I've added it with Logger.Helper.

Copy link
Contributor

@thockin thockin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Great comments. Please squash to 1 commit and make sure commit and PR messages are complete.

We'll let this sit for a bit to see if anyone comes up with an obviously better answer.

@DirectXMan12 @wojas FYI

@thockin
Copy link
Contributor

thockin commented Aug 5, 2021

and if we merge this, I guess we'll want a 1.1.0 tag.

@pohly
Copy link
Contributor Author

pohly commented Aug 6, 2021

Please squash to 1 commit

I think that's too aggressive. 40bd6cd and 5bd8e58 are both stand-alone changes. We might even merge them already now, independently of the Helper API change. I'll put them into separate PRs so that we don't forget about them if we close this one here without merging.

Implementing the API and using it in testing can be squashed. I tend to prefer to keep that separate because it sometimes makes cherry-picking simpler, but that's subjective. Let's wait for more review feedback before I touch this PR again.

@thockin
Copy link
Contributor

thockin commented Aug 6, 2021

Fine, then :) Squash to the smallest set of whole commits that makes sense and make sure the comments are good ;)

@wojas
Copy link
Contributor

wojas commented Aug 9, 2021

func myWrapper(logger logr.Logger, msg string) {
    helper, logger := logger.Helper()
    helper()
    logger.Info(msg)
}

As an API this is horrible, but it does not look like we have much choice given how the Go testing package works.

Perhaps at least make the name on the logr side clearer. Given that this is a hack specifically added for the testing package, perhaps use something like WithTestingHelper instead? No need to pretend that this is some generic solution if this is really just a workaround for the testing package. Explicitly mentioning this in the comments would make it a less cryptic addition.

This would change the example as follows:

func myWrapper(logger logr.Logger, msg string) {
    helper, logger := logger.WithTestingHelper()
    helper()
    logger.Info(msg)
}

Would this be used in any other context?

Do I understand correctly that when not using the testing log sink, would you still need to use WithCallDepth? In that case you would need to use both this helper and WithCallDepth for a generic helper?

If this can be make more generic and completely replace the WithCallDepth call if no custom helper is provided, perhaps call it WithCallDepthHelper?

@pohly
Copy link
Contributor Author

pohly commented Aug 9, 2021

func myWrapper(logger logr.Logger, msg string) {
    helper, logger := logger.Helper()
    helper()
    logger.Info(msg)
}

As an API this is horrible, but it does not look like we have much choice given how the Go testing package works.

Perhaps at least make the name on the logr side clearer. Given that this is a hack specifically added for the testing package, perhaps use something like WithTestingHelper instead?

I agree that naming in the new API is sub-optimal (too much "helpers"). I just tried to get something working to get a feeling for how horrible it would be.

I like WithTestingHelper better (consistent with other Logger.With* functions, clearer reference to testing).

One concern I have is that users might get the impression that they should only use this when using testing. The function is also useful with other logging backends (see below).

No need to pretend that this is some generic solution if this is really just a workaround for the testing package. Explicitly mentioning this in the comments would make it a less cryptic addition.

Okay.

Would this be used in any other context?

I don't know of one. OTOH, that doesn't mean that there is no other logging backend that uses the same approach.

Do I understand correctly that when not using the testing log sink, would you still need to use WithCallDepth?
WithTestingHelper

No. A wrapper function must call either WithCallDepth(1) or WithTestingHelper(), but not both. Helper (= WithTestingHelper) is written so that it does the right thing for skipping the caller regardless of how the LogSink achieves that.

If this can be make more generic and completely replace the WithCallDepth call if no custom helper is provided, perhaps call it WithCallDepthHelper?

There's still a need for WithCallDepth when the parameter is greater than 1. Log wrappers which need that then don't work properly with testing.

@wojas
Copy link
Contributor

wojas commented Aug 9, 2021

OK, given that it calls WithCallDepth(1) if no helper is defined, it's basically a replacement for calling that directly and it should not have testing in the name.

I would then argue that calling something WithCallDepthHelper() would be the recommended way of implementing any helper function in your code. If you use this, it will work automatically for both testing loggers and for sinks that use the call depth approach. It also allows a log sink to implement to implement a similar mechanism as testing uses if it desires so.

We should then update the WithCallDepth documentation to recommend using the new helper approach instead.

Given the fragility of the API, perhaps add some extra checks there. It is too easy to forget to call the helper function and someone may find out that it 'still works' if you just ignore that return value with _. How about a descriptive panic() if the helper function was not called before using the new logger?

@pohly
Copy link
Contributor Author

pohly commented Aug 9, 2021

OK, given that it calls WithCallDepth(1) if no helper is defined, it's basically a replacement for calling that directly and it should not have testing in the name.

I would then argue that calling something WithCallDepthHelper() would be the recommended way of implementing any helper function in your code. If you use this, it will work automatically for both testing loggers and for sinks that use the call depth approach. It also allows a log sink to implement to implement a similar mechanism as testing uses if it desires so.

Correct. So replace Helper with WithCallDepthHelper?

We should then update the WithCallDepth documentation to recommend using the new helper approach instead.

That's already in the current PR 😄 ("Helper should be used instead of WithCallDepth(1) because ...")

Given the fragility of the API, perhaps add some extra checks there. It is too easy to forget to call the helper function and someone may find out that it 'still works' if you just ignore that return value with _. How about a descriptive panic() if the helper function was not called before using the new logger?

That would be nice, but cannot be implemented because the returned function intentionally doesn't go through the logger because testing.T.Helper cannot be wrapped, i.e. the logger cannot know whether it was called or not.

@wojas
Copy link
Contributor

wojas commented Aug 9, 2021

Correct. So replace Helper with WithCallDepthHelper?

That would be my preferred name choice. @thockin?

We should then update the WithCallDepth documentation to recommend using the new helper approach instead.

That's already in the current PR 😄 ("Helper should be used instead of WithCallDepth(1) because ...")

Ah, perfect, missed that. 😄

Given the fragility of the API, perhaps add some extra checks there. It is too easy to forget to call the helper function and someone may find out that it 'still works' if you just ignore that return value with _. How about a descriptive panic() if the helper function was not called before using the new logger?

That would be nice, but cannot be implemented because the returned function intentionally doesn't go through the logger because testing.T.Helper cannot be wrapped, i.e. the logger cannot know whether it was called or not.

Hmm, that's annoying. We could check it for situations where no helper was defined by the log sink and we return our own function, which could set some internal variable to false. But I don't like this inconsistency, because then you will get situations where code panics in production, even though it 'worked fine' in tests.

Perhaps just depend on Go's unused variable check then.

@pohly
Copy link
Contributor Author

pohly commented Aug 9, 2021

Perhaps just depend on Go's unused variable check then.

Yes. Hopefully programmers will think before discarding the helper function return value with _. What we would need to prevent that is golang/go#20803

@thockin
Copy link
Contributor

thockin commented Aug 9, 2021

I'm not 100% sure on names:

With*() implies some attribute of the Logger (WithName changes the Loggers name), which isn't really the case here. I'd expect WithCallDepthHelper to take an argument that was a "CallDepthHelper" and save that in the Logger.

Helper() has the advantage of following a convention (better than making something up). We don't really have examples of other implementations that are similar, so we don't know if there's actually a pattern to follow.

We're proposing that "best practice" be to call Helper() and run the returned function in (almost?) all cases of intermediate helper funcs. Given that, it seems to me that making it easier to use is important.

   log.Helper()()

vs

  log, helper := log.WithHelper()
  helper()

Which is more likely to be correct more often? The WithFoo() pattern is strongly associated with "catch the return value", where as t.Helper() is strongly associated with fire-and-forget. OTOH 1 line vs 2 lines and named variables.

Thoughts?

@pohly
Copy link
Contributor Author

pohly commented Aug 9, 2021

Helper() has the advantage of following a convention (better than making something up).

You mean testing.T.Helper? That's exactly the fire-and-forget semantic that we don't want to follow here because the return values must both be used. For me that's a strong argument to use With* because then using the return value is a familiar pattern.

I agree that WithCallDepthHelper sounds like it should have a parameter. How about WithOneMoreCallDepth? "One" implies that the parameter is not needed because it would be 1 and "More" implies that it is additive. It rolls of the tongue better than WithOneCallDepth, too.

@wojas
Copy link
Contributor

wojas commented Aug 10, 2021

The main issue with log.Helper()() is that this way we cannot increment the call depth for non-testing backends. It would the require every helper to both call log.Helper()() and WithCallDepth, the second one returning a new instance like the proposed WithCallDepthHelper or whatever the name ends up being:

Compare

func myWrapper(logger logr.Logger, msg string) {
    helper, logger := logger.WithCallDepthHelper()
    helper()
    logger.Info(msg)
}

with

func myWrapper(logger logr.Logger, msg string) {
    logger.Helper()()
    logger.WithCallDepth(1).Info(msg)
}

And as @pohly mentioned, the Go compiler will also not help you if you just call logger.Helper() by mistake without using the returned function, see https://play.golang.org/p/lfwo09sbGkh

@thockin
Copy link
Contributor

thockin commented Aug 10, 2021

OK. I had missed that, of course, we can't just call WithCallDepth() internally without returning the new logger.

So let's talk about 2 things:

  1. logger, helper := ... vs helper, logger := ...

  2. Names. Let's brainstorm and see if anything obvious emerges.

x, y := log.AsHelper()

x, y := log.AsHelperFunc()

x, y :=log.IsHelper()

x, y := log.ThisIsAHelperFunc()

x, y := log.GetHelperFunc()

more welcome...

@pohly
Copy link
Contributor Author

pohly commented Aug 10, 2021

logger, helper := ... vs helper, logger := ...

I don't have a strong opinion either way. At the end, both is arbitrary.

Regarding names:

My understand is that in testing, "the helper" is the log wrapper function. testing.T.Helper is just that API for marking the helper function. I wouldn't call that method "the helper", therefore a -1 from me for GetHelperFunc. I don't have a good suggestion for naming the variable, though. For consistency with t.Helper() in normal test code I probably would stick with helper().

All of other examples might look okay at first glance when called without using the return values although such usage is incorrect. Therefore I still prefer WithOneMoreCallDepth.

@thockin
Copy link
Contributor

thockin commented Aug 10, 2021

WithOneMoreCallDepth really doesn't "feel good" to me. I'm not really looking to rush this, so I'll let it percolate for a bit.

@wojas
Copy link
Contributor

wojas commented Aug 11, 2021

logger, helper := ... vs helper, logger := ...

Perhaps logger, helper := is better, because the context package also does this:

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

x, y := log.AsHelper()
x, y := log.AsHelperFunc()

To me As suggests a conversion, but we do not only return the cancel function and there is no actual conversion happening.

x, y :=log.IsHelper()

Feels like a check if this is a helper. I would expect a bool return value.

x, y := log.ThisIsAHelperFunc()

Suggests that this actual call marks it as a helper func. I would also not expect any return value here.

x, y := log.GetHelperFunc()

This would be fine is it would just return a helper func.

I think something starting with With would still be most clear. This is what we do with WithCallDepth and the pattern mirrors context.WithCancel. It suggests that you are adding something to the current object and receiving a new object.

@pohly is right that the calling function is the helper in the testing naming, but I think the function can still be seen as a helper to determine that call stack depth to subtract.

I still have a preference for WithCallDepthHelper, because like in WithCancel, it tells something about the extra return value.
WithOneMoreCallDepth does not tell you anything about the weird extra return value.

Come to think of it, this is a pretty common pattern:

ctx, cancel := context.WithCancel(ctx)
defer cancel()

This does not look too weird next to that:

logger, helper := logger.WithCallDepthHelper()
helper()

@thockin
Copy link
Contributor

thockin commented Aug 11, 2021

I guess I buy that reasoning for With*.

WithCallDepthHelper()

WithCallStackHelper()

WithCallerHelper()

WithHelper()

@pohly pohly mentioned this pull request Aug 11, 2021
@wojas
Copy link
Contributor

wojas commented Aug 12, 2021

One more:

WithCallDepthMarkerFunc()

@thockin
Copy link
Contributor

thockin commented Aug 13, 2021

rebase needed :)

@pohly
Copy link
Contributor Author

pohly commented Aug 17, 2021

rebase needed :)

Can you merge PRs #61 and #62 first? Then a rebase will make this PR smaller.

@thockin
Copy link
Contributor

thockin commented Aug 17, 2021

Done

@pohly
Copy link
Contributor Author

pohly commented Aug 18, 2021

@wojas: @thockin and I chatted about this a bit more and agreed on WithCallStackHelper because WithCallDepthHelper sounds like it should have a numeric depth argument, similar to WithCallDepth. I'll rename accordingly.

@pohly pohly changed the title RFC: support alternative way of skipping frames support alternative way of skipping frames Aug 18, 2021
logr.go Outdated
type CallStackHelperLogSink interface {
// GetHelper returns a function that must be called to mark the direct caller
// as helper function when logging call site information.
GetHelper() func()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be GetHelperFunc() or GetCallStackHelper() ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure. If we want a longer name, then I prefer GetCallStackHelper because it is more consistent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've pushed a version with GetCallStackHelper.

This is needed for log sinks where the underlying stack unwinding
doesn't support an explicit call depth, for example testing.T.Log.

Users should use the new logger.Helper method whenever possible by
calling it in each log helper function because it also works with
LogSinks that support either the CallDepthLogSink interface or the new
HelperLogSink interface, whereas Logger.WithCallDepth only works with
CallDepthLogSink.

A logger might implement both if it supports two different ways of
finding source code. For example, testing/test.go optionally unwinds
itself and then also testing.T adds source code information.
The funcr log sink must implement the new CallStackHelperLogSink
interface (otherwise the wrapper function wouldn't be handled
correctly) and also mark its own wrapper as a helper.
@thockin
Copy link
Contributor

thockin commented Aug 18, 2021

LGTM

@thockin thockin merged commit cf509d1 into go-logr:master Aug 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants