-
Notifications
You must be signed in to change notification settings - Fork 27.4k
Add traceback to unhandled promise rejections, Fixes: #14631 #15527
Conversation
@gkalpak uhh, are the tests for the unhandled rejection even run? I added these to intentionally fail... |
Not sure what you mean. The tests should run (and I am quite sure they do). |
@gkalpak https://github.com/angular/angular.js/pull/15527/files#diff-0cfb390955e411a4a5437fa337d52481R2181 should fail on the fixture.type === 'exception' because it doesn't stringify to 'foo'. |
@gkalpak are you in IRC? |
@graingert, if you want it to fail, then you better not reject the promise with |
@gkalpak oh I am a silly... Fixing... |
BTW, it would made our life a tiny bit easier if you followed our commit message guidelines 😉 |
@gkalpak will do once I've got the tests working. Also your URL is out of date: you want https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#commit-message-format |
@@ -381,7 +381,11 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) { | |||
if (!toCheck.pur) { | |||
toCheck.pur = true; | |||
var errorMessage = 'Possibly unhandled rejection: ' + toDebugString(toCheck.value); |
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.
Here is another idea (either for toDebugString()
or just here):
If toCheck.value
has a toString()
method that is not the default Object.prototype.toString()
, then use that for serializing it. Something similar to stringify.
6e2531b
to
013779c
Compare
Thx! (My URL was fine btw - just intentionally pointing to the supersection of yours 😛) |
@gkalpak ah it didn't go to a section for me at all. (it works now) |
c014f10
to
1654b47
Compare
@gkalpak and we're green! |
@graingert, awesome! Now on to that commit message 😛 |
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 left some comments, but the general idea LGTM.
var exceptionStr = toDebugString(exceptionEg); | ||
var fixtures = [ | ||
{ | ||
type: 'exception', |
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.
Change "exception" to "Error object".
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.
Cool
var type = fixture.type; | ||
var value = fixture.value; | ||
var expected = fixture.expected; | ||
it('should log an unhandled' + type + ' rejected promise', function() { |
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.
Instead of repeating type
in all it
blocks, create a describe
block for each fixture and put type
in its description.
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.
Ah makes sense
forEach(fixtures, function(fixture) { | ||
var type = fixture.type; | ||
var value = fixture.value; | ||
var expected = fixture.expected; |
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.
Extra points for putting some space before it
blocks 😃
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.
Or describe
blocks...
expected: { | ||
reason: 'Possibly unhandled rejection: foo' | ||
} | ||
} |
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.
A couple more fixtures would be in order imo. E.g.:
- One with a non-
Error
object. - One with an object that "extends"
Error
.
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.
Yeah makes sense
c75796b
to
1dd6336
Compare
@gkalpak done |
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.
A couple of nitpicks, but otherwise LGTM.
} | ||
}, | ||
{ | ||
type: 'plain value', |
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.
Now everyone is capitalized except for plain value
. Just because it is nothing more than a plain value, doesn't mean you have to treat it like a 2nd-class citizen 😛
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 went with all lower case, and more accurate names
forEach(fixtures, function(fixture) { | ||
var type = fixture.type; | ||
var value = fixture.value; | ||
var expected = fixture.expected; |
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.
Or describe
blocks...
var type = fixture.type; | ||
var value = fixture.value; | ||
var expected = fixture.expected; | ||
describe(type, function() { |
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.
Nit: Using something like 'with ' + type
will make the description read better (in case of an error for example).
@gkalpak done |
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.
LGTM, but let's wait for some feedback from others as well.
(In the mean time, feel free to fix up that commit message 😁)
@gkalpak I did fix the commit message... What's wrong with it? |
Actual:
Expected something like:
(The change may seem trivial, but the format is important, because we auto-generate the changelog from these commit messages. So small deviations can make a lot of difference in the resulting changelog.) |
@graingert Thanks for the PR, this is awesome. 👏 I don't have any remarks apart from the incorrect commit message format. |
@gkalpak commit message fixed |
@@ -381,7 +381,11 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) { | |||
if (!toCheck.pur) { | |||
toCheck.pur = true; | |||
var errorMessage = 'Possibly unhandled rejection: ' + toDebugString(toCheck.value); | |||
exceptionHandler(errorMessage); | |||
if (toCheck.value instanceof Error) { | |||
exceptionHandler(toCheck.value, errorMessage); |
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.
Is this the correct order? It looks weird when the error message is after the stack trace.
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.
From https://docs.angularjs.org/api/ng/service/$exceptionHandler
$exceptionHandler(exception, [cause]);
Param | Type | Details |
---|---|---|
exception | Error | Exception associated with the error. |
cause (optional) | string | Optional information... |
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.
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.
There is nothing in the $exceptionHandler
code that would make the order important; that's all of it:
function $ExceptionHandlerProvider() {
this.$get = ['$log', function($log) {
return function(exception, cause) {
$log.error.apply($log, arguments);
};
}];
}
I wonder what's the reason for the current documentation. @gkalpak, @petebacondarwin does any one of you know?
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.
what about just doing
exceptionHandler(toCheck.value, 'Possibly unhandled rejection')
with no ifs or errorMessage generation.
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.
That sounds sensible but I'd still swap the order of operations. Note that currently we are trying to show the 'Possibly unhandled rejection'
message first and the error afterwards; we just put it all in one string argument so the stack trace gets lost. So I'd like:
exceptionHandler('Possibly unhandled rejection', toCheck.value);
but that would require updating the docs to no longer require the cause to be the second parameter (no code changes required as it already works as I mentioned before).
I'd just want to know if there's any reason for the current signature so let's see what others say. :)
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.
@mgol my custom $exceptionHandlers are configured to pass the first argument as the Error value, as per the documentation.
I presume others follow the same documentation also.
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.
@graingert Is the issue that some automated tools might only pick up the first argument and discard others, losing the error context? Because otherwise, in Angular source, the order influences only one thing - the order in which messages/errors are logged. And it makes more sense to log the summary first and the error second. In other words, the following:
exceptionHandler('Possibly unhandled rejection', toCheck.value);
is analogous to:
exceptionHandler('Possibly unhandled rejection: ' + toDebugString(toCheck.value));
just with more details printed to the console as the error stack is not lost.
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.
@mgol swapping the types of the parameters of $exceptionHandler is another issue for another PR, and would be a breaking change.
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.
OK, fair enough. In that case, maybe it's better to leave it as it is for now; your proposal to just write:
exceptionHandler(toCheck.value, 'Possibly unhandled rejection');
sounds nice but we pass other data types through toDebugString
and we'd lose it in that case. If we want to tweak that, it would be better to tackle that separately after this PR lands.
@@ -381,7 +381,11 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) { | |||
if (!toCheck.pur) { | |||
toCheck.pur = true; | |||
var errorMessage = 'Possibly unhandled rejection: ' + toDebugString(toCheck.value); | |||
exceptionHandler(errorMessage); | |||
if (toCheck.value instanceof Error) { |
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.
Though I don't think it necessarily a concern here, do we care that this will not identify errors that originate from an iframe? Don't know how common of a use case that is.
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 doubt it's common but if it turns out to be an issue we can revisit.
Ideally we would convert to a string (via toDebugString
) only certain types of values and log others unchanged. The problem is that then we'd have to pass Possibly unhandled rejection
and toCheck.value
as separate arguments to $exceptionHandler
and if we do it in a documented way (error first, (optional) reason second) the message would get swapped from e.g.:
'Possibly unhandled rejection: MY_THING'
to:
'MY_THING' 'Possibly unhandled rejection'
which is less pleasant.
We can discuss it but let's do it in a separate issue if desired.
Landed, thanks! |
@graingert One nit (I fixed it myself): there should be no colon after |
What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
Feature
What is the current behavior? (You can also link to an open issue here)
Console is filled with meaningless:
Possibly unhandled rejection: {}
errorsWhat is the new behavior (if this is a feature change)?
Console is filled with juicy traceback info.
Does this PR introduce a breaking change?
I don't think so
Please check if the PR fulfills these requirements
Other information: