jhillyerd / enmime

MIME mail encoding and decoding package for Go

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Consider wrapping returned errors with stack

dcormier opened this issue · comments

When bugs are encountered, it's easier to track them down if there's a stack trace that goes with the error.

To that end, I know I would find it useful to wrap returned errors with errors.WithStack.

As you can see from the two examples there, normal usage of the wrapped error will change nothing. But using the %+v format specifier will produce a useful stack trace.

Already when I'm debugging this library I sprinkle errors.WithStack in place to help me see what's going on, but I haven't (yet) included those changes in a PR. I think it would be a useful change for contributors to be able to use these stack traces to help track down bugs.

Thanks for opening this Daniel, my thoughts on this:

  1. I'd like to be very careful about adding new imports to the library, as I tend to look at the imports of other libraries when evaluating them. I want to avoid the nightmare that is npm, and agree with the proverb "A little copying is better than a little dependency."
  2. If we do end up importing a library, I'd want to evaluate https://github.com/joomcode/errorx as it brings a lot more to the table. But I haven't used it, not sure if it imports a bunch of garbage or not. :)
  3. The Go error ecosystem is in flux right now, and the draft for error inspection looks quite nice to me: https://go.googlesource.com/proposal/+/master/design/go2draft-error-inspection.md
  4. We already have an error type built in to enmime, I'd like to re-use that if all possible. Especially until we have a better idea on the draft above.
  5. Edited to add: I'd also want to know how importing either errors or errorx would impact downstream clients.

Would implenting a Cause() function on our internal Error type yield benefits?

from pkg/errors:

// Cause returns the underlying cause of the error, if possible.
// An error value has a cause if it implements the following
// interface:
//
//     type causer interface {
//            Cause() error
//     }
  1. I'd like to be very careful about adding new imports to the library, as I tend to look at the imports of other libraries when evaluating them. I want to avoid the nightmare that is npm, and agree with the proverb "A little copying is better than a little dependency."

That's wise. Package errors fairly light weight and only imports from stdlib.

  1. If we do end up importing a library, I'd want to evaluate https://github.com/joomcode/errorx as it brings a lot more to the table. But I haven't used it, not sure if it imports a bunch of garbage or not. :)

I haven't used it, either. It also only imports from stdlib. I do see that it is (publicly) used far less than errors, though that's not a measure of quality. I'd have to take a look at what else it provides. Really a stack trace is all I'm after.

  1. The Go error ecosystem is in flux right now, and the draft for error inspection looks quite nice to me: https://go.googlesource.com/proposal/+/master/design/go2draft-error-inspection.md

Yes, I'm looking forward to that. Russ Cox has said that they want to include the error values proposal in go1.13, but that's still some 8+ months away.

  1. We already have an error type built in to enmime, I'd like to re-use that if all possible. Especially until we have a better idea on the draft above.

I was specifically referring to error instances that get returned to to callers of public functions/methods, rather than the *enmime.Error instances that are added to *enmime.Part and *enmime.Envelope. Do you want to start returning the *enmime.Error type from those functions/methods?

Related to that, I think your suggestion of implementing Cause() error (and/or Unwrap() error, as in the go2 proposal) on the *enmime.Error type (possibly in addition to adding Err error as a field on that type) would be quite useful. This might be a separate discussion.

  1. Edited to add: I'd also want to know how importing either errors or errorx would impact downstream clients.

Needs more investigation, but I think impact would be very minimal.

Any string comparison users may be doing on returned errors would not change (unless they are using the %+v format specifier on the error, but even then they might be unaffected).

As there are no public error variables, users of the package are not doing checks like err == enmime.ErrSomeKnownError.

Since no custom types implementing error are returned from public functions/methods (unless I missed something), and often errors.New() or fmt.Errorf() is used, users are unlikely to be checking the type of a returned error, and changing it to be a wrapped error would have minimal impact.


Edit: All that said, the last couple of bugs I've addressed have involved me wrapping these returned errors with errors.WithStack (locally) just to help me debug the problem, and understand the code path that resulted in the error. It has been quite helpful. It would be nice to have to do that each time I find it might be useful.

example output

Hah, I didn't realize that no public repos were using errorx. It's relatively new, but that's still not a great sign. Then again, after several years enmime only has a handful... 🤷‍♂️

Given that adding/formatting stack traces would be quite a bit of copying, I'm OK with adopting pkg/errors. With the understanding that it will be removed >=6 months after Go 2 error inspection make it into a release build.

At that point I'll probably look at refactoring our Error type as well.

Hah, I didn't realize that no public repos were using errorx. It's relatively new, but that's still not a great sign.

I was pretty surprised to find that, too.

Then again, after several years enmime only has a handful... 🤷‍♂️

Well, I'd bet receiving email with golang is a bit more of a niche thing. At least more so than sending.

Given that adding/formatting stack traces would be quite a bit of copying, I'm OK with adopting pkg/errors. With the understanding that it will be removed >=6 months after Go 2 error inspection make it into a release build.

Sounds good!

At that point I'll probably look at refactoring our Error type as well.

That sounds good, too. There might be a point when I can take a look at that. Probably not soon, though.