-
Notifications
You must be signed in to change notification settings - Fork 193
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
#391; implement error interface #399
#391; implement error interface #399
Conversation
Since this is a community submitted pull request, a Jenkins build has not been kicked off automatically. Can an Elastic organization member please verify the contents of this patch and then kick off a build manually? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR! A couple of queries, otherwise looking good.
} | ||
|
||
// Error returns string message for error. | ||
// if Error or Error.cause is nil, "[EMPTY]" will be used. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think for log-type errors, the log message should be returned by Error()
, while the associated error is returned by Cause()
(as you do already.)
Perhaps change NewError and NewErrorLog to set another error message field at construction time, alongside the cause?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, you mean I should add another error
field to both ErrorLogRecord
and ErrorData
structures?
Not sure when should I use them. Could you explain?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean add an error message field to Error
, so it looks something like:
type Error struct {
*ErrorData
cause error
err string
}
func (e *Error) Error() string {
return e.err
}
func (e *Error) Cause() error {
return e.cause
}
NewErrorLog
would set the ErrorLogRecord.Message value, and NewError
would just copy across the value from the provided error.
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea - I just wonder about scenario when NewErrorLog
is called with nil error.
This scenario exists in validation_test.go:236 TestValidateErrorLog
and not sure what to do with this.
At the moment I bypassed problem by settings Error.err
manually afterwards but I really don't like this solution. Do you think NewErrorLog
should be protected from calling with nil error as NewError is? I saw this is optional but seems like it should be non-nill in all runtime scenarios.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the moment I bypassed problem by settings Error.err manually afterwards but I really don't like this solution.
I would suggest either:
(1) revert newError
to having no parameters, and initialise both err
and cause
afterward
func (t *Tracer) NewError(err error) *Error {
...
e := t.newError()
...
e.cause = err
e.err = err.Error()
...
}
func (t *Tracer) NewErrorLog(r ErrorLogRecord) *Error {
...
e := t.newError()
...
e.cause = r.Error
e.err = e.log.Message
...
}
(2) change newError
to accept both the err
and cause
parameters, and compute the final "message" value before constructing the Error object
func (t *Tracer) NewError(err error) *Error {
...
e := t.newError(err, err.Error())
...
}
func (t *Tracer) NewErrorLog(r ErrorLogRecord) *Error {
if r.Message == "" {
r.Message = "[EMPTY]"
}
e := t.newError(r.Error, r.Message)
...
}
Do you think NewErrorLog should be protected from calling with nil error as NewError is? I saw this is optional but seems like it should be non-nill in all runtime scenarios.
No, the error is definitely optional. The use case is for logging frameworks, where you can optionally associate a log record with an error, e.g. in the apmlogrus module:
err, _ := entry.Data[logrus.ErrorKey].(error)
errlog := tracer.NewErrorLog(apm.ErrorLogRecord{
Message: entry.Message,
Level: entry.Level.String(),
Error: err,
})
(The logrus.ErrorKey
field is optional, hence the comma-ok type assertion.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for detailed explanation - I revert changes made to newError function and initialize fields later.
gocontext.go
Outdated
@@ -64,13 +64,15 @@ func StartSpanOptions(ctx context.Context, name, spanType string, opts SpanOptio | |||
// set either from err, or from the caller. | |||
// | |||
// If there is no span or transaction in the context, CaptureError returns | |||
// nil. As a convenience, if the provided error is nil, then CaptureError | |||
// will also return nil. | |||
// Error with nil ErrorData field. As a convenience, if the provided error is nil, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please explain the need for this change? It's not clear to me, and I'm reluctant to change this behaviour.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wanted to avoid situation when returned type is nil pointer to interface - it can be tricky sometimes https://stackoverflow.com/questions/13476349/check-for-nil-and-nil-interface-in-go
Second thing is to keep original error even if error was already sent.
If you like, I can revert that change - was made to provide stable interface, no matter error was sent previously or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I can see reason in that: we would be saying that if the result is nil if and only if the input error is nil. If the input error is non-nil, but there is no span or transaction in the context, then we'll get a non-nil Error that wraps the input error, only its (Send, SetTransaction, SetSpan) methods will be no-ops.
I'm fine with keeping this, but I'd like a change to the doc comment: it should be stated in terms of functionality, rather than whether the ErrorData field is nil or not. Maybe something like this?
If the provided error is nil, then CaptureError will also return nil; otherwise a non-nil Error will always be returned. If there is no transaction or span in the context, then the returned Error's Send method will have no effect.
Codecov Report
@@ Coverage Diff @@
## master #399 +/- ##
=========================================
- Coverage 81.15% 80.95% -0.2%
=========================================
Files 104 104
Lines 6452 6374 -78
=========================================
- Hits 5236 5160 -76
+ Misses 936 934 -2
Partials 280 280
Continue to review full report at Codecov.
|
@psrebniak do you expect to finish this off soon? I'd love to get it in for the next minor release, which I'm hoping to release in the next week or two. If you're unsure about the changes I requested, we can just land it as it is (after rebasing of course) and I can look into making minor adjustments later. |
@axw I'll finish changes on Monday, sorry for delay |
No worries, thank you! |
@psrebniak thank you, looks good. I think this is ready to merge, with a few minor things:
|
a802c75
to
30398a1
Compare
@axw done - thanks for your help and patience :) |
@psrebniak not at all, thank you very much for your contribution. |
Fixes #391
Had to change apm.captureError behavior a little bit - instead of returning nil, Error with nil ErrorData is returned. Comments are adjusted.
I think Error.sent() name may be adjusted as there are more cases when ErrorData is nil.
Tests for causer and error interfaces added.