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: Wrap rrweb callbacks in wrapper to better handle errors #41

Merged
merged 1 commit into from
Feb 8, 2023

Conversation

mydea
Copy link
Member

@mydea mydea commented Feb 3, 2023

This adds a callbackWrapper method which we use to wrap any (async) callbacks in the record/observer codebase.
This wrapper will automatically try to add an __rrweb__ property to any error thrown in the given callback.

Later in sentry, we can use this to determine to ignore a given error.

Note: I had to rewrite the monkey-patched CSSStyleSheet stuff into proxies, as otherwise I couldn't get the this-binding correctly via the prototype stuff. However, I think this is a nicer implementation anyhow, so should be good IMHO.

I also added some tests (this took >50% of the implementation time 🙈 as you can't easily access error objects from puppeteer) to verify this works.

The tests cover:

  • CSSStyleSheet monkey patching
  • monkey patching of CSSGroupingRule
  • errors in mutation observer

This should give us a decent certainty that this generally works as expected. There may be a place or two that I missed, but I don't think so. I tried to put the wrappers in a way that it encapsulates any sync stuff going on.

I'd ask you @billyvg & @Lms24 to check this out and look at it in the editor, to really see the flow of functions & if I didn't miss any relevant function call that could slip through.

cc @mitsuhiko

@mydea mydea requested review from billyvg and Lms24 February 3, 2023 10:13
@mydea mydea self-assigned this Feb 3, 2023
@mydea mydea force-pushed the fn/wrap-callback-errors branch from 589151b to 6605ec2 Compare February 3, 2023 10:16
@@ -17,7 +17,7 @@ import * as fs from 'fs';

export async function launchPuppeteer() {
return await puppeteer.launch({
headless: process.env.PUPPETEER_HEADLESS ? true : false,
headless: process.env.PUPPETEER_DEBUG ? false : true,
Copy link
Member Author

Choose a reason for hiding this comment

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

I switched this to default to headless, as actually I got some flaky tests locally without this. IMHO headless makes sense as default, unless you really want to debug the chrome instance. If you think that's a bad change, I can revert this!

Copy link
Member

Choose a reason for hiding this comment

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

for the flaky local tests, I've had better success with runInBand

@mydea mydea force-pushed the fn/wrap-callback-errors branch from 6605ec2 to e9aac32 Compare February 3, 2023 10:18
@@ -27,6 +27,7 @@ import {
import { IframeManager } from './iframe-manager';
import { ShadowDomManager } from './shadow-dom-manager';
import { CanvasManager } from './observers/canvas/canvas-manager';
import { callbackWrapper } from '../sentry/callbackWrapper';
Copy link
Member

Choose a reason for hiding this comment

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

I think this should be generally upstreamable, so I would try to name this in a way that is not sentry specific.

Copy link
Member

Choose a reason for hiding this comment

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

Actually disregard this. I can see we need to separately upstream this with the amount of pending upstream changes already.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, I'm actually thinking to upstream this as a more generic configurable errorHandler kind of thing. IMHO that probably makes the most sense from a generic rrweb usage perspective! Then we can configure the error handler to add the __rrweb__ stuff in sentry-javascript for us (long term, when we eventually update our fork to v2)

@@ -27,6 +27,7 @@ import {
import { IframeManager } from './iframe-manager';
import { ShadowDomManager } from './shadow-dom-manager';
import { CanvasManager } from './observers/canvas/canvas-manager';
import { callbackWrapper } from '../sentry/callbackWrapper';
Copy link
Member

Choose a reason for hiding this comment

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

Actually disregard this. I can see we need to separately upstream this with the amount of pending upstream changes already.

@mydea
Copy link
Member Author

mydea commented Feb 3, 2023

ref rrweb-io#1107 - Upstream, I made a PR to add an errorHandler option, which we could use to handle this (if/when we update our fork to latest upstream)

Comment on lines +759 to +761
return callbackWrapper(() => {
handlers.forEach((h) => h());
};
});
Copy link
Member

Choose a reason for hiding this comment

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

In observer you also wrap the callbacks in each handler, do you need to do that here as well?

Copy link
Member Author

Choose a reason for hiding this comment

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

As the forEach is called sync inside of the callbackWrapper, I think it is not necessary there!

}
return insertRule.apply(this, arguments);
};
win.CSSStyleSheet.prototype.insertRule = new Proxy(insertRule, {
Copy link
Member

Choose a reason for hiding this comment

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

I'm wondering if we need callback wrappers for these methods... from what I've seen, exceptions haven't happened inside of these methods but rather when defining them as CSSStyleSheet.prototype isn't defined. -- e.g. here https://github.com/getsentry/rrweb/pull/41/files?diff=split&w=1#diff-b7a42c375ac5ab2de196b5e95e0b2f82b723969c5836fac0ff6f7ca6ad66ebd7R654-R655

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, I think this is more of an additional safeguard. IMHO it doesn't hurt to have them (and having them be proxies is probably safer as well).

@@ -17,7 +17,7 @@ import * as fs from 'fs';

export async function launchPuppeteer() {
return await puppeteer.launch({
headless: process.env.PUPPETEER_HEADLESS ? true : false,
headless: process.env.PUPPETEER_DEBUG ? false : true,
Copy link
Member

Choose a reason for hiding this comment

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

for the flaky local tests, I've had better success with runInBand

Copy link
Member

@Lms24 Lms24 left a comment

Choose a reason for hiding this comment

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

I went through the PR in VSCode and tried to wrap my head around how these callbacks are are interconnected/nested.
image

I'm afraid chances are pretty high that we missed some weird async callback/edge case but I think overall it looks good and we now have a simple way of quickly wrapping any CB we might have missed. I guess the biggest challenge is figuring out that the individual, currently unwrapped CB are okay not to be wrapped because they're executed in sync in other CBs that are wrapped.

Great that you upstreamed the error handler PR. If it's accepted I think this is a good reason to consider going to 2.x. Also noted this in getsentry/sentry-javascript#6655 so we don't lose track of it.

@mydea mydea merged commit df34a49 into sentry-v1 Feb 8, 2023
@mydea mydea deleted the fn/wrap-callback-errors branch February 8, 2023 09:28
mydea added a commit to getsentry/sentry-javascript that referenced this pull request Feb 8, 2023
Based on getsentry/rrweb#41, we can now ignore rrweb errors even when the code is minified.
mydea added a commit to getsentry/sentry-javascript that referenced this pull request Feb 8, 2023
Based on getsentry/rrweb#41, we can now ignore rrweb errors even when the code is minified.
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.

4 participants