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

front_end_error_logging #237

Closed
wants to merge 7 commits into from
Closed

front_end_error_logging #237

wants to merge 7 commits into from

Conversation

salelkafrawy
Copy link
Contributor

Hello all,

I'm running into an issue, here is the detailed explanation:

For the front end error message:

  • I've added a ErrorBoundary component inside static_react_task/webapp/app.jsx temporarily (once I correct the behavior of the issue I have I'll move it to mephisto-task/utils
  • I was trying to render a nice looking message box or popup once an error occurs.
  • In the test case Pratik suggested, the error does show up in the console but the componentDidCatch inside ErrorBoundary component is not called, I've tried to console.log() some messages inside that method with nothing showing up in the console. Am I missing something or it's real that ErrorBoundary could be not triggered in that test case as stated here and I just need to try a different test case?

For propagating the error from the front to the backend:

  • I added handleFatalError same as handleSubmit in useMephistoTask with just calling handleSubmit with a different data dictionary.
  • Then I added a property to the ErrorBoundary called handleError which is triggered in componentDidCatch (which is not called properly as I explained in the first part)
  • was what I added functioning but not running due to the issue of componentDidCatch not being triggered or do I miss something important here?

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Sep 1, 2020
@JackUrb
Copy link
Contributor

JackUrb commented Sep 1, 2020

One issue is that react doesn't appear to be able to catch errors that happen in handlers, only in lifecycle functions: facebook/react#11409.

These kinds of errors will end up being type 2 and 3 instead.

For testing this, you should throw your error during the render method.

Copy link
Contributor

@JackUrb JackUrb left a comment

Choose a reason for hiding this comment

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

Error logging flow in general looks good. Nice job following the existing conventions for constants and post methods! Next up is going to be polishing and dealing with the global case in a way that allows users to decide if something major broke.

It might be worth some kind of heuristic on global errors, like setting a timeout when they occur, and then seeing if the user is still interacting with the app and making progress (which would imply that the error was actually minor). Any thoughts on what a good practice would be for this @pringshia?

examples/static_react_task/re-build.sh Outdated Show resolved Hide resolved
errorInfo: errorInfo
})
// alert Mephisto worker of a component error
alert("error from a component" + error)
Copy link
Contributor

Choose a reason for hiding this comment

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

We'll probably want to update this message once everything else is fully hashed out!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. It was an indication for me that the error was raised from ErrorBoundary

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 actually don't see a difference between type 2 and type 3 (global) errors. I had to place the window.eventListener('error'..) definition after the useMephistoTask as it uses handleFatalError. And even placing an error at the beginning of MainApp wouldn't fire the window.eventListener('error', ..) nor window.onerror callback as the error is place before defining any of them. So I don't know how to produce a global error that might happen right after defining the MainApp unless useMephistoTask should handle that type inside it, is that right?

Copy link
Contributor

@pringshia pringshia Sep 23, 2020

Choose a reason for hiding this comment

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

Hmm, I wonder why this is happening, as <script src="wrap_crowd_source.js"></script> is defined in the <head> tag. Let me experiment a bit with this and get back to you.

Copy link
Contributor

@pringshia pringshia Sep 23, 2020

Choose a reason for hiding this comment

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

seems like I was able to get type 3 errors to work:

console

Screen Shot 2020-09-23 at 12 53 21 PM

examples/parlai_chat_task_demo/custom_simple/main.js

Screen Shot 2020-09-23 at 12 59 54 PM

wrap_crowd_source.js

Screen Shot 2020-09-23 at 1 00 03 PM

Copy link
Contributor

@pringshia pringshia Sep 23, 2020

Choose a reason for hiding this comment

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

And this is from throwing the error from within a component (inside a React context).

Screen Shot 2020-09-23 at 1 04 38 PM

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes I am able to see them in the console but do you get them back in the backend? there should be a logging INFO for the error sent back to the backend

mephisto/server/architects/router/deploy/server.js Outdated Show resolved Hide resolved
(data) => {
console.log('inside handleFatalError ...')
postErrorLog(state.agentId, data)
// handleSubmit(data)
Copy link
Contributor

Choose a reason for hiding this comment

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

We may want to consider a distinction as to whether or not the error is actually fatal - in the cases where it is, that's when we should trigger a submit. This will allow us to surface something to the user when global errors happen, without necessarily forcing them to submit?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How can I categorize the fatal errors from non fatal ones?

Copy link
Contributor

@pringshia pringshia Sep 23, 2020

Choose a reason for hiding this comment

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

as a temporary step you can trigger a confirm() dialog and allow the user to choose? i don't like this because it can be a jarring experience for users, especially in the case of false positives, but it can be a good stopgap for now

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 already did this for type 2 errors. Thansk

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you mean do the confirm() inside componentDidCatch()? because I already do confirm() in the window.onerror event.

onClick={() => onSubmit({ rating: "good" })}
// Test for type 2 errors
onClick={()=> {throw new Error("test error event_handler");}}
// onClick={() => onSubmit({ rating: "good" })}
Copy link
Contributor

Choose a reason for hiding this comment

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

just a reminder to revert this when we're ready to merge

Copy link
Contributor

Choose a reason for hiding this comment

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

to echo Jack, these errors in the callback may actually be caught as type 3 errors, instead of 2

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the event handlers errors which can't be caught by React's ErrorBoundary are caught by the window.onerror event handler and those which can be caught by both the window.onerror event handler and ErrorBoundary are caught twice but this should be the case for mode=development and once the mode=production it should be caught only once in the ErrorBoundary [according to StackOverFlow]

console.log("You pressed Cancel!");
}
return true;
})
Copy link
Contributor

Choose a reason for hiding this comment

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

once this seems to be working correctly, let's move it into wrap_crowd_source.js so all tasks automatically inherit this functionality

@@ -11,19 +11,76 @@ import ReactDOM from "react-dom";
import { BaseFrontend, LoadingScreen } from "./components/core_components.jsx";
import { useMephistoTask } from "mephisto-task";

class ErrorBoundary extends React.Component {
Copy link
Contributor

@pringshia pringshia Sep 18, 2020

Choose a reason for hiding this comment

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

great first step in implementing this. the next step would be to move this component into mephisto-task and import it into this project from there. this will allow us to have a reusable mephisto component we can use from other tasks

@pringshia
Copy link
Contributor

As Jack mentioned componentDidCatch is not being triggered because the error is being thrown from an onClick event handler, which gets run in a different execution context. What you'd want to do instead if throw an error from within the render method (that is not triggered via an event handler).

@salelkafrawy
Copy link
Contributor Author

I've polished the debugging logs and script, and re-located the error handling components (window.onerror event and ErrorBoundary react component) to be inside useMephistoTask and utils.js respectively.

I tried to add window.onerror to wrap_crowd_source as you said @pringshia but I need handleFatalError from useMephistoTask and also I didn't understand how this can be used for other providers since wrap_crowd_source is per provider. Did you mean to make it specific for every provider?

@salelkafrawy salelkafrawy changed the title [WIP] front_end_error_logging front_end_error_logging Sep 26, 2020
@pringshia
Copy link
Contributor

Are you still planning on adding a const to the top of wrap_crowd_source.js to flag between the two different behaviors we discussed? (i.e. auto-submit on error vs. prompt to submit on error).

@@ -15,6 +15,7 @@ for both to be able to register them with the backend database.
Returning None for the assignment_id means that the task is being
previewed by the given worker.
\------------------------------------------*/
auto_submit = false
Copy link
Contributor

Choose a reason for hiding this comment

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

looks good, I would make this ALL_CAPS as such and declare it as a const

}
}
else {
console.log("sending to email address: ####")
Copy link
Contributor

Choose a reason for hiding this comment

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

we can wire up this portion to the /log_error endpoint you created?

Comment on lines +67 to +68
prompt('send the following error to the email address: '+
'[email address]', JSON.stringify(event.error.message))
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry a bit out of context here - why are we asking for an email address?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Pratik told me there should be a choice whether for the user to auto submit the error (wiring up /log_error) or show them an email and a copy-enabled version of the error message to send that error by themselves to an email address.

Copy link
Contributor

@JackUrb JackUrb Oct 8, 2020

Choose a reason for hiding this comment

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

Understood - generally we may have a case where we want the submit to be optional but not explicitly state the contact info (for a case where people may not want to expose their actual email, but instead get contacted through the site). So really there should be three options:

  1. Auto-submit. Display the message "An error has occurred. This has been logged to the requester automatically."
  2. Don't auto-submit. Display the message. "We think an error has occurred. If the task is broken, please send this context to the requester.
  3. Don't auto-submit. Same message as the above, but with an email instead of "to the requester"

Edit: I'm realizing this is attached to a specific wrap_crowd_source.js file, meaning the scenario here is fixed.

@@ -53,3 +54,22 @@ function handleSubmitToProvider(task_data) {
alert("The task has been submitted! Data: " + JSON.stringify(task_data))
return true;
}

// Adding event listener instead of using window.onerror prevents the error to be caught twice
window.addEventListener('error', function (event) {
Copy link
Contributor

Choose a reason for hiding this comment

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

@pringshia is there a reason this listener is defined in wrap_crowd_source.js and not as a part of mephisto-task?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the first place I put it inside mephisto-task but Pratik said it's better to be inside each provider separately but I didn't find it convenience because we will need to put it in each Provider's wrap_crowd_source.js, @pringshia could you elaborate why is this better?

Copy link
Contributor

@pringshia pringshia Oct 12, 2020

Choose a reason for hiding this comment

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

@JackUrb - The reason for putting this in wrap_crowd_source.js as opposed to mephisto-task is so that this global catch-all error handling (list as #3 here: #231) can also apply to:

  • Errors in static HTML tasks
  • Errors that may occur at the React library level, before the application code has had a chance to run (maybe related due to a build linking / webpack error?)

@pringshia pringshia linked an issue Oct 12, 2020 that may be closed by this pull request
@facebook-github-bot
Copy link
Contributor

Hi @saraebrahim!

Thank you for your pull request. We require contributors to sign our Contributor License Agreement, and yours needs attention.

You currently have a record in our system, but we do not have a signature on file.

In order for us to review and merge your code, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

If you have received this in error or have any questions, please contact us at [email protected]. Thanks!

@JackUrb JackUrb reopened this Nov 11, 2020
@pringshia pringshia mentioned this pull request Dec 17, 2020
@JackUrb
Copy link
Contributor

JackUrb commented Dec 30, 2020

Changes in here are all included in #343, so I'm closing this version.

@JackUrb JackUrb closed this Dec 30, 2020
@JackUrb JackUrb deleted the sara_front_end_error branch January 6, 2021 21:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add front-end error logging support to the Web UI
4 participants