Skip to content
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

feat: Sensible non-Error exception serializer #1253

Merged
merged 3 commits into from
Mar 13, 2018

Conversation

kamilogorek
Copy link
Contributor

@kamilogorek kamilogorek commented Mar 9, 2018

getsentry/raven-node#416 adapted to Raven.js needs.

Should help to fix most of the issues with logging a large amount of [object Object] events.

@kamilogorek kamilogorek requested a review from a team March 9, 2018 14:43
@kamilogorek kamilogorek force-pushed the sensible-error-serializer branch from 0ab8c75 to c10432c Compare March 9, 2018 15:25
@MaxBittker
Copy link
Contributor

this is a big change so I want to give it a proper review and test drive it
(although obviously there's a lot of confidence gained by its success in raven-node)

Copy link
Contributor

@MaxBittker MaxBittker left a comment

Choose a reason for hiding this comment

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

🏆

As far as I can tell, this looks great, and will be a big improvement.

People who upgrade will very likely get "new" errors as things has differently, so it may be worth noting that.

👍

};

// Unfortunately older browsers are not capable of extracting method names
// therefore we have to use `oneOf` here
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

assert.equal(actual, expected);
});

it('shouldnt append ellipsis if have enough space', function() {
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe a test case for when you use exactly the limit? (it shouldn't ellipsize, but this isn't a big deal)

Copy link
Contributor

Choose a reason for hiding this comment

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

it looks like you handle this right in the logic though

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added


it('should default to no-keys message if empty array provided', function() {
var actual = serializeKeysForMessage([]);
var expected = '[object has no keys]';
Copy link
Contributor

Choose a reason for hiding this comment

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

what do you think about "[Empty Object]" or "{}"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought about it, but I think it may be slightly confusing, as it looks like something coming from the browser itself and people may start to google for it :D

Copy link
Contributor

@MaxBittker MaxBittker Mar 13, 2018

Choose a reason for hiding this comment

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

[Object (no keys)]
[Object with no keys]

?

I don't know what kind of precedent we could follow here, but I think people might google for it either way (and ideally they'd find this repo?). I don't think this is that important since it shouldn't be super common, I'm just throwing out other options

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Let's leave it like this for now, as Raven-node is already using this string and change it if we'll get any reports of it being confusing :)

Copy link
Contributor

Choose a reason for hiding this comment

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

oh, i didn't realize raven-node uses it already, that's good enough for me

assert.equal(actual, expected);
});

it('should with with provided maxLength', function() {
Copy link
Contributor

Choose a reason for hiding this comment

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

should /work/ with?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed


it('should serialize arbitrary number of depths', function() {
var actual = serializeException(
{
Copy link
Contributor

Choose a reason for hiding this comment

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

how does this deal with cyclic objects? (if it just keeps following the links until it bottoms out, that's not that bad)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's exactly what it does. And MAX_SERIALIZE_EXCEPTION_DEPTH is set to 3, so the only way to "break" it is consciously calling serializeException(something, 10e100) or some other very large number that will take too much memory to calculate :p

And it's only in serializeObject, as for checking the size itself, we use safe-json-stringify which we use for Raven, which handles cyclic objects itself.

src/raven.js Outdated
// This will allow us to group events based on top-level keys
// which is much better than creating new group when any key/value change

var keys = Object.keys(ex).sort();
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe this logic should go in its own function? only because captureException is so central and might be the first thing someone reading the code would encounter

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agree

var MAX_SERIALIZE_KEYS_LENGTH = 40;

function utf8Length(value) {
return ~-encodeURI(value).split(/%..|./).length;
Copy link
Contributor

Choose a reason for hiding this comment

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

are you only using ~- this for subtracting 1? I trust it's right, just want to understand.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

why not use ).length - 1? (it's NBD, just curious)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because I trust in every line of code that Substack writes tbh, nothing more - https://github.com/substack/utf8-length/blob/master/index.js :P

} else if (
typeof value === 'number' ||
typeof value === 'boolean' ||
typeof value === 'undefined'
Copy link
Contributor

Choose a reason for hiding this comment

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

any other value types here we could want... dates?

Copy link
Contributor

Choose a reason for hiding this comment

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

or are those objects?

Copy link
Contributor Author

@kamilogorek kamilogorek Mar 13, 2018

Choose a reason for hiding this comment

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

Dates are treated as object with the representation of [object Date]. It's taken care of by JSON.stringify which extracts the date from it

JSON.stringify(new Date('02/03/2017'))
// ""2017-02-02T23:00:00.000Z""

There are definitely some other types that we can add in the future, but right now, they'll be gracefully handled by stringify.

var serialized = serializeObject(ex, depth);

if (jsonSize(stringify(serialized)) > maxSize) {
return serializeException(ex, depth - 1);
Copy link
Contributor

Choose a reason for hiding this comment

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

💯

keys = keys.filter(function(key) {
return typeof key === 'string';
});
if (keys.length === 0) return '[object has no keys]';
Copy link
Contributor

Choose a reason for hiding this comment

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

i mentioned this in the test but I think this string could be improved a bit to be more declarative/official looking

@kamilogorek
Copy link
Contributor Author

Thanks, that's a great review @MaxBittker! How about captureException structure now? :)

@MaxBittker
Copy link
Contributor

I think you improved captureException and it could be merged in its current form, but I took a crack at another layout for the function that reduces some of the logic and might be better:

https://github.com/getsentry/raven-js/pull/1259/files

@kamilogorek
Copy link
Contributor Author

Duuuuh! That's much more clever! Thank you :D

@kamilogorek kamilogorek merged commit add03a5 into master Mar 13, 2018
@kamilogorek
Copy link
Contributor Author

Thanks, @MaxBittker! I'll release it tomorrow though, as I don't want to put out an eventual fire at night :P

@kamilogorek kamilogorek deleted the sensible-error-serializer branch March 13, 2018 19:56
@MaxBittker
Copy link
Contributor

agreed 100% on not releasing it tonight

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

Successfully merging this pull request may close these issues.

2 participants