-
Notifications
You must be signed in to change notification settings - Fork 43
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
Figure out integration story for logging errors, especially using zap #6
Comments
What about zap recognizing the
zap will simply define its own copy of the interface and attempt to type cast |
Yep that avoids the dependency, it is still implicit, and I think @akshayjshah had concerns about this implicit behaviour. I think the
That way, the special handling will only affect multierr errors. I really don't like the idea of exposing that interface though, I'd prefer to not expose it, or expose it without any private methods. |
In general, I'd prefer to respect the error interface. If we want our errors to have special serialization logic, let's implement {
"error": "oh no!; bad things happened; write failed",
"errorVerbose": " 3 errors:\\n* oh no!\\n* bad things happened\\n* write failed",
} If we really want to represent the multiple errors as a JSON array, though, I'd prefer to just export
and handle it explicitly in |
I don't think the I'm OK with exporting I'd like |
Does any of this add extra information? This seems largely presentation-related.
Yep, totally makes sense. If we don't call the interface
This makes using a search index challenging, since you can't tell what field I'd prefer this: "error": {
"error": "oh no!; bad things happened; write failed",
"causes": [
"oh no!",
"bad things happened",
"write failed"
]
} This also gives us a clear path to support |
So to summarize,
Open questions:
@prashantv @akshayjshah Thoughts? |
This changes how we log errors to break down multierr and pkg/errors errors if possible. If an error is a multierr error, its individual items will be listed under `${key}Causes` as objects. Roughly, "error": "foo; bar; baz", "errorVerbose": `the following errors occurred: - foo - bar - baz`, "errorCauses": [ {"error": "foo"}, {"error": "bar"}, {"error": "baz"}, ] Similarly, if an error is a pkg/errors causer, the cause will be listed under `${key}Causes` by itself. "error": "foo: bar", "errorVerbose": "foo: bar\n[stack trace]" "errorCauses": [ { "error": "bar", "errorVerbose": "bar\n[stack trace]", }, ] See: uber-go/multierr#6
This changes how we log errors to break down multierr and pkg/errors errors if possible. If an error is a multierr error, its individual items will be listed under `${key}Causes` as objects. Roughly, "error": "foo; bar; baz", "errorVerbose": `the following errors occurred: - foo - bar - baz`, "errorCauses": [ {"error": "foo"}, {"error": "bar"}, {"error": "baz"}, ] Similarly, if an error is a pkg/errors causer, the cause will be listed under `${key}Causes` by itself. "error": "foo: bar", "errorVerbose": "foo: bar\n[stack trace]" "errorCauses": [ { "error": "bar", "errorVerbose": "bar\n[stack trace]", }, ] See: uber-go/multierr#6
This changes how we log errors to break down multierr and pkg/errors errors if possible. If an error is a multierr error, its individual items will be listed under `${key}Causes` as objects. Roughly, "error": "foo; bar; baz", "errorVerbose": `the following errors occurred: - foo - bar - baz`, "errorCauses": [ {"error": "foo"}, {"error": "bar"}, {"error": "baz"}, ] Similarly, if an error is a pkg/errors causer, the cause will be listed under `${key}Causes` by itself. "error": "foo: bar", "errorVerbose": "foo: bar\n[stack trace]" "errorCauses": [ { "error": "bar", "errorVerbose": "bar\n[stack trace]", }, ] See: uber-go/multierr#6
Support landed in zap. Next release will include this. |
Following the discussions on #460, uber-go/multierr#6, and (most recently) uber-go/multierr#23, reduce log verbosity for `multierr`. This is fully backward-compatible with the last released version of zap. The small changes introduced here do two things: 1. First, we either report `errorCauses` or `errorVerbose`, but not both. 2. Second, we prefer `errorCauses` to `errorVerbose`. I think that this addresses our top-level wants without breaking any interfaces or removing behavior we've already shipped. If we ever decide to cut a new major release of zap, we should treat errors like durations and times - they're special types for which users choose a formatter. In a future release, we can add an `ErrorEncoder` interface that the JSON encoder and console encoder implement, and make the error field attempt an upcast into that type. That would let the user supply their own error encoder (much like they supply their own time and duration encoders now). Even if we do that, though, I suspect that we'll want to preserve the behavior here as the default.
Following the discussions on #460, uber-go/multierr#6, and (most recently) uber-go/multierr#23, reduce log verbosity for `multierr`. This is fully backward-compatible with the last released version of zap. The small changes introduced here do two things: 1. First, we either report `errorCauses` or `errorVerbose`, but not both. 2. Second, we prefer `errorCauses` to `errorVerbose`. I think that this addresses our top-level wants without breaking any interfaces or removing behavior we've already shipped. If we ever decide to cut a new major release of zap, we should treat errors like durations and times - they're special types for which users choose a formatter. In a future release, we can add an `ErrorEncoder` interface that the JSON encoder and console encoder implement, and make the error field attempt an upcast into that type. That would let the user supply their own error encoder (much like they supply their own time and duration encoders now). Even if we do that, though, I suspect that we'll want to preserve the behavior here as the default.
Right now, if we do
zap.Error(err)
and the error is amultierr
, then we end up with a single concatenated string that contains all the errors.Ideally we would have output more similar to
zap.Errors
when writing out a multiple errors.The
multierr
just returns anerror
, which may be:nil
if there were no errorserror
if there was only one error[]error
if there's multiple errorsI think the experience we want is a single function that takes the
error
, and returns either:zap.Skip()
if there was no errorzap.Error(..)
if there's a single errorzap.Errors(...)
if there are multiple errorscc @abhinav and @akshayjshah
I've documented some options for this below, I'm leaning towards the latter options. Would be great to document any other options.
Expose
Causes(error) []error
frommultierr
The user would be required to do:
The
Causes
function would always allocate a slice, and return a slice with either 0, 1, or N elements depending on the input. The final output would always contain "errors" even if there's 0 errors or just 1 error.Implement
zap.MarshalLogArray
in the internalmultierr
typeSince the underlying type is not exported, the user would probably do:
This would require a dependency on
zap
frommultierr
-- if there is a dependency, I think the other way makes more sense.The final output would always contain "errors" (similar to above).
Have zap implicitly recognize
multierr
zap
could recognize a multierr error, and automatically get all the errors usingCauses() []error
and usezap.Errors
instead.zap
would need to depend onmultierr
(which seems like a more reasonable dependency) but implicit handling may be a little unexpected.Add explicit
zap.MultiError(error)
field typeThis would still require a dependency, but would make the checks more explicit.
zap.MultiError
would return fields depending on the number of errors. This would be the ideal user experience, but extends thezap
interface to add anotherField
constructor which takeserror
. This may be confusing to users.Have a subpackage of
multierr
that provides the above field constructorWe could add a
multierr/zerr
package for users loggingmultierr
withzap
. This avoids cross dependencies between the core packages, but thezerr
package could depend on bothmultierr
andzap
, and provide a field constructorError(err error) zap.Field
that does the same logic as the previous option.The user experience is a little worse since users now need to import a second package. However, the cost may be worth avoiding cross dependencies or extending zap's interface.
The text was updated successfully, but these errors were encountered: