Skip to content

Add listeners for unhandled errors to web views#2139

Merged
robertbrignull merged 7 commits intomainfrom
robertbrignull/webview_error_telemetry
Mar 16, 2023
Merged

Add listeners for unhandled errors to web views#2139
robertbrignull merged 7 commits intomainfrom
robertbrignull/webview_error_telemetry

Conversation

@robertbrignull
Copy link
Copy Markdown
Contributor

Listens for unhandled errors and rejected promises in the webviews. We then send a message with info on the error so we can display the error and potentially log telemetry on it.

There's a few points worth noting here:

  • Each webview is isolated from others. If I register these listeners in one webview, they do not pick up errors from other views.
  • Including an Error object in a message is problematic. It just doesn't seem to work. And it's not like it just loses the prototype bits but keeps the message and stack fields. Everything gets a bit messed up and you end up with an empty object on the other end. To work around this I've made it send over an error-like object in the message, and I've made the logging and telemetry code handle this type just like it does Error objects.

Checklist

  • CHANGELOG.md has been updated to incorporate all user visible changes made by this pull request.
  • Issues have been created for any UI or other user-facing changes made by this pull request.
  • [Maintainers only] If this pull request makes user-facing changes that require documentation changes, open a corresponding docs pull request in the github/codeql repo and add the ready-for-doc-review label there.

@robertbrignull robertbrignull requested review from a team as code owners March 6, 2023 17:27
@robertbrignull
Copy link
Copy Markdown
Contributor Author

There's also a bug I'm still working out, but I decided to open the PR anyway while I look into it. We're getting duplicate window.addEventListener("error" events. It just seems to fire twice, and I'm not sure why. This doesn't happen with the unhandled rejection listener, and I don't believe it's because of different webviews interfering with each other.

@robertbrignull
Copy link
Copy Markdown
Contributor Author

Seems the error firing twice is due to react error boundaries. Possibly due to the default error boundary re-throwing. See facebook/react#11499

I'm going to try implementing an error boundary and see how it behaves. That will only affect errors thrown when rendering. From reading the issue it seems we could still end up with duplicate errors from event handlers. We can decide if that's acceptable, or whether to try fixing it by remembering which errors we've seen previously.

It doesn't appear to show a duplicate popup, so the user won't notice that there are two errors. It'll only show up in our telemetry and make the numbers a bit inflated.

Comment thread extensions/ql-vscode/src/pure/errors.ts Outdated
Comment thread extensions/ql-vscode/src/pure/errors.ts
import { vscode } from "../vscode-api";

const unhandledErrorListener = (event: ErrorEvent) => {
vscode.postMessage({
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Does the stack checking you implemented earlier to avoid printing stack traces for not-our-code also work for the webview? This is something to verify at least.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll try to do a test with another extension to make sure, but I'm currently pretty confident that webviews are isolated and so the errors from one are not reported in another. I say this because the various webviews in our application are not interfering with each other. We get errors from a webview if and only if a listener has been registered in that particular view, and having another view open with a listener registered will not make it report errors from other open views.

@robertbrignull
Copy link
Copy Markdown
Contributor Author

I've pushed some new changes which include code for remembering which errors we're seen previously and avoiding reporting telemetry for them again.

Please let me know what you think about the approach or remember previous errors. I haven't seen a better way of avoiding the duplicate reporting, but I'm very open to other options.

I've done some investigation of using an error boundary, and I found that it did indeed stop the duplicate error reporting for errors during rendering but not for errors from event handlers. This is as we thought it would be, but it's nice to confirm it. I think error boundaries can be considered separately from this PR so I've opened an issue to track them.

};

const unhandledRejectionListener = (event: PromiseRejectionEvent) => {
if (shouldReportError(event.reason)) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm not 100% sure but I think the problem of duplicate errors only happens for thrown errors and not rejected promises. So this check here may be unnecessary, but it also shouldn't hurt so I decided to keep it in. I'm not really sure about this though and I'm happy to remove it.

Copy link
Copy Markdown
Member

@koesie10 koesie10 left a comment

Choose a reason for hiding this comment

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

I think this approach makes sense, and if we can't filter out duplicate errors for event handlers anyway, then I think this behaviour is exactly what we need.

I have a few minor suggestions, but nothing blocking.

Comment thread extensions/ql-vscode/src/pure/errors.ts Outdated
Comment thread extensions/ql-vscode/src/pure/interface-types.ts Outdated
}
};

const unhandledRejectionListener = (event: PromiseRejectionEvent) => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we be making a distinction between the unhandled errors and unhandled rejections? As far as I am aware, unhandled rejections don't cause any user-visible behaviour (except perhaps some data not loading), but unhandled errors might cause the complete view to disappear. It would be useful to make that distinction in the telemetry.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

For now I'm going to leave them combined. I don't think it's quite as well defined as saying unhandled errors are always more important. Missing or incorrect data is also bad, and maybe worse if the user don't know the data they're looking at is incorrect. Unhandled errors can also happen outside of rendering.

Also, we can certainly split these up in the future, and doing it after this PR will be fine. The telemetry is quite schema-less and we only keep it for a short period, so changing the format is quite cheap to do.

@robertbrignull
Copy link
Copy Markdown
Contributor Author

I've done some more tests and this seems to be working correctly, so I think this is good to merge.

@robertbrignull robertbrignull merged commit 61cc919 into main Mar 16, 2023
@robertbrignull robertbrignull deleted the robertbrignull/webview_error_telemetry branch March 16, 2023 11:18
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.

3 participants