Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Add traceback to unhandled promise rejections, Fixes: #14631 #15527

Closed
wants to merge 1 commit into from

Conversation

graingert
Copy link
Contributor

@graingert graingert commented Dec 19, 2016

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: {} errors

What 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:

Sorry, something went wrong.

@graingert
Copy link
Contributor Author

@gkalpak uhh, are the tests for the unhandled rejection even run? I added these to intentionally fail...

@gkalpak
Copy link
Member

gkalpak commented Dec 20, 2016

Not sure what you mean. The tests should run (and I am quite sure they do).
(The tests need some fixes, because they are not currently testing what they should be testing, but that is unrelated.)

@graingert
Copy link
Contributor Author

@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'.

@graingert
Copy link
Contributor Author

@gkalpak are you in IRC?

@gkalpak
Copy link
Member

gkalpak commented Dec 20, 2016

@graingert, if you want it to fail, then you better not reject the promise with foo 😛
(I am rarely (read "never") on IRC these days, but you can find me on gitter.)

@graingert
Copy link
Contributor Author

@gkalpak oh I am a silly... Fixing...

@gkalpak
Copy link
Member

gkalpak commented Dec 20, 2016

BTW, it would made our life a tiny bit easier if you followed our commit message guidelines 😉

@graingert
Copy link
Contributor Author

@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);
Copy link
Member

@gkalpak gkalpak Dec 20, 2016

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.

@graingert graingert force-pushed the patch-3 branch 2 times, most recently from 6e2531b to 013779c Compare December 20, 2016 12:58
@gkalpak
Copy link
Member

gkalpak commented Dec 20, 2016

Thx! (My URL was fine btw - just intentionally pointing to the supersection of yours 😛)

@graingert
Copy link
Contributor Author

graingert commented Dec 20, 2016

@gkalpak ah it didn't go to a section for me at all. (it works now)

@graingert graingert force-pushed the patch-3 branch 5 times, most recently from c014f10 to 1654b47 Compare December 20, 2016 15:28
@graingert
Copy link
Contributor Author

@gkalpak and we're green!

@gkalpak
Copy link
Member

gkalpak commented Dec 20, 2016

@graingert, awesome! Now on to that commit message 😛

Copy link
Member

@gkalpak gkalpak left a 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',
Copy link
Member

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".

Copy link
Contributor Author

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() {
Copy link
Member

@gkalpak gkalpak Dec 20, 2016

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.

Copy link
Contributor Author

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;
Copy link
Member

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 😃

Copy link
Member

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'
}
}
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah makes sense

@graingert
Copy link
Contributor Author

@gkalpak done

Copy link
Member

@gkalpak gkalpak left a 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',
Copy link
Member

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 😛

Copy link
Contributor Author

@graingert graingert Dec 21, 2016

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;
Copy link
Member

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() {
Copy link
Member

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).

@graingert
Copy link
Contributor Author

@gkalpak done

Copy link
Member

@gkalpak gkalpak left a 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 😁)

@graingert
Copy link
Contributor Author

@gkalpak I did fix the commit message... What's wrong with it?

@gkalpak
Copy link
Member

gkalpak commented Dec 21, 2016

Actual:

fix($q): Add traceback to unhandled promise rejections, Fixes: #14631

Expected something like:

fix($q): add traceback to unhandled promise rejections

Fixes #14631

(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.)

@mgol
Copy link
Member

mgol commented Dec 21, 2016

@graingert Thanks for the PR, this is awesome. 👏

I don't have any remarks apart from the incorrect commit message format.

@graingert
Copy link
Contributor Author

@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);
Copy link
Member

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.

Copy link
Contributor Author

@graingert graingert Dec 21, 2016

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...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, OK. I mostly wondered because this:
screen shot 2016-12-21 at 15 48 43
looks a little weird.

Copy link
Member

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?

Copy link
Contributor Author

@graingert graingert Dec 21, 2016

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.

Copy link
Member

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. :)

Copy link
Contributor Author

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.

Copy link
Member

@mgol mgol Dec 21, 2016

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.

Copy link
Contributor Author

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.

Copy link
Member

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) {
Copy link
Contributor

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.

Copy link
Member

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.

@mgol mgol closed this in 316f60f Dec 21, 2016
mgol pushed a commit that referenced this pull request Dec 21, 2016
@mgol
Copy link
Member

mgol commented Dec 21, 2016

Landed, thanks!

@mgol
Copy link
Member

mgol commented Dec 21, 2016

@graingert One nit (I fixed it myself): there should be no colon after Fixes in the commit description.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants