proposal: change for Go beginners (stack trace) #140
Description
What I understand
WithMessage()adds a messageWithStack()adds the current stackWrap[f]()adds a message + stackNew|Errorf()creates an new error with message + stack
History
If I remember correctly:
Wrap[f]()was added firstWithMessage()+WithStack()were added later, becauseWrap[f]()was "too much" => we don't need a stack trace for all "wrapped error". It is only useful for the "deepest" error.
When should we use each function
- New root error =>
New|Errorf()(it will contain a stack trace) - Error returned by the std lib or a 3rd party lib =>
Wrap[f]()(we want to add a stack trace) - Error returned by our own code (in my app) =>
WithMessage()(we don't want to add more stack trace) - Error returned by a different goroutine =>
WithStack()(I don't use it very often)
Who am I working with
- ~10 Go developers
- All beginners in Go (they were working with other languages previously)
- Not always very strict/careful
The problem
- They don't/can't understand where to use
Wrap[f]()vsWithMessage() - We're losing a lot of time during code review
- Checking if
Wrap[f]()orWithMessage()should be called - Explaining in which case we must use which function
- Starting again for each newcomer
- Checking if
The risks
- If we call
WithMessage()instead ofWrap[f]()=> we're missing the stack trace - If we call
Wrap[f]()instead ofWithMessage()=> we "pollute" our error logs
What we need
A function that wraps an error with a message, and optionnaly with a stack trace if it doesn't have already one.
What I tried
My initial idea was to write a custom helper function that would do as described previously.
Sadly, this is not easily doable.
I can't call WithStack() or Wrap[f]() because it would include the helper function call in the stack.
The solutions
New function
Add a new function to github.com/pkg/errors, that is exactly like Wrap[f](), but doesn't add a stack trace if there is already one.
I don't really like this one, because it makes the package's interface more complex
Change behavior
Change Wrap[f](), and don't add a stack trace if there is already one.
This is my favorite solution.
I agree, it will change the existing behavior.
However, I think this is worth it.
I can't think of any use case that requires to call Wrap[f]() several times and have several redundant stack traces.
Just fork
I can perfectly understand if you disagree, and don't want to update the current code.
In this case, I will just copy the code to our internal repo, and adapt the code to match our needs.
I don't really like this, because I would stop using github.com/pkg/errors.
We're currently using it everywhere in our code.
I've been able to "extend" the behavior properly since the beginning.
The implementation
I've seen #122 and I really dislike the implementation.
It means that only internal github.com/pkg/errors types can be used.
We can't create our own types anymore.
What I would do instead:
- iterates over all causes
- check if at least 1 error implements
StackTrace()
Activity
davecheney commentedon Oct 29, 2017
Thank you for your detailed issue. I agree that #122 is problematic because I don't know how to express the business logic of "add a stack trace, unless there is one" in a comprehensive way and I'm not keen in trying for an 80% solution.
Re your proposed solutions
Causethey will interoperate fine.Your proposed solution sounds interesting; given that none of the tests passed with #122 (i haven't checked why, it may be unrelated), the field to attempt to implement this proposal is still open.
pierrre commentedon Oct 30, 2017
Hello,
For the technical implementation, my idea was:
StackTrace()So, I suggest to create a function:
Then call this function in
Wrap[f](), and don't addif there is a stack trace.
I can submit a PR if you agree.
About my motivation, I repeat:
Wrap[f]()orWithMessage()Wrap[f]()andWithMessage()I would really prefer to not fork
github.com/pkg/errors.I really like this package and I want to continue to use it.
Also, if I can make it easier to use for beginners, I'm happy to contribute :)
davecheney commentedon Oct 30, 2017
pierrre commentedon Oct 31, 2017
Do you mean that
Wrap[f]()shouldn't exist ?And you want to remove it, but you can't ?
So, my proposal to update
Wrap[f]()is obsolete, right ?davecheney commentedon Oct 31, 2017
pierrre commentedon Nov 1, 2017
So, if I understand correctly, you'd rather not add new function or change existing ones.
No problem, I understand your point of view :)
I've got a different (but related) question.
Currently if we want to get an error's stack trace we need to "assert" with the interface:
Do you think it's a good idea if we add a method
StackFrames() *runtime.Frames?If I remember correctly, this is important for "mid stack inlining" (but I'm not 100% sure).
I plan to add a similar method in my own/internal "errors" implementation.
davecheney commentedon Nov 1, 2017
pierrre commentedon Nov 1, 2017
Sorry, I expressed myself badly.
I meant: add a new method
StackFrames() *runtime.Frames(probably on thestacktype).It will not change the existing method/interface
StackTrace() errors.StackTrace.The goal is to allow us to get the
runtime.Framesfrom an error.