Exception Monitoring: teach you how to deal with an elegant front-end anomaly

Front-end has been the closest one user, along with increasingly sophisticated products, we will pay more attention to user experience, and the tip was abnormal lump in the throat, is very annoying.

First, why should handle exceptions?

The exception is not controlled, it will affect the presentation of the final results, but we have every reason to do such a thing.

  1. Enhance the user experience;
  2. Remote location problem;
  3. Rainy day, early detection of problems;
  4. Not double-track problems, especially mobile terminal models, the system is the problem;
  5. Complete front-end solutions, front-end monitoring system;

For JS, just unusual, abnormal appearance we face does not directly lead to the collapse of JS engine, it will only make the task currently executing is terminated.

Second, the need to deal with which exceptions?

For the front, we can do exception caught quite a bit. To sum up, probably as follows:

  • JS syntax error codes Exception
  • AJAX request an exception
  • Abnormal Load static resources
  • Promise abnormal
  • Iframe abnormal
  • Cross-domain Script error
  • Collapse and Caton

Now I will for each particular case to illustrate how to handle these exceptions.

Three, Try-Catch Mistakes

try-catch only captures the synchronous run-time error, syntax and asynchronous errors can not do anything, not capture.

  1. Front-end run-time error:
the try { 
  the let name = 'jartto' ; 
  the console.log (Nam); 
} the catch (E) { 
  the console.log ( 'capture exception:' , E); 
}

Output:

Exception caught: a ReferenceError: Not defined Nam IS 
    AT <Anonymous>:. 3: 15

  2. not capture the specific syntax error, only a syntax error. We modify the code, delete a single quote:

the try { 
  the let name = ' jartto; 
  the console.log (Nam); 
} the catch (E) { 

  the console.log ( ' capture exception: ' , E); 
}

Output:

Uncaught SyntaxError: Invalid or unexpected token 
, however syntax errors can be seen at our stage of development, we should not be successful on the online environment.

  3. Asynchronous error:

the try { 
  the setTimeout (() => { 
    undefined.map (V => V); 
  }, 1000 ) 
} the catch (E) { 
  the console.log ( 'capture exception:' , E); 
}

We look at the log:

Uncaught TypeError: Cannot read property 'map' of undefined at setTimeout (<anonymous>:3:11)

Exception caught and no, this is where we need special attention.

Four, window.onerror is not a panacea

When an error occurs JS runtime, window will trigger an error event ErrorEvent interface, and perform window.onerror ().

/ * * 
* @Param {String} error Message 
* @param {String} source error file 
* @param {Number} lineno line number 
* @param {Number} colno column number 
* @param {Object} error Error Object (Object ) * 
/ 

window.onerror = function (Message, Source, lineno, colno, error) { 
   the console.log ( 'capture exception:' , {Message, Source, lineno, colno, error}); 
}

1. First, try to synchronize runtime error

= window.onerror function (Message, Source, lineno, colno, error) {
 // Message: Error message (character string). 
// Source: error occurred script URL (string) 
// lineno: error line number (numeric) 
// colno: column number where the error occurred (numeric) 
// error: Error objects (objects) 
console.log ( 'exception caught:' , {Message, Source, lineno, colno, error}); 
} 
Jartto;

You can see, we captured exception:

 

 2. Try again syntax error it:

window.onerror = function(message, source, lineno, colno, error) {
console.log('捕获到异常:',{message, source, lineno, colno, error});
}
let name = 'Jartto

Console print out this exception:

Uncaught SyntaxError: Invalid or unexpected token

What, not even capture the grammatical errors?

3. With disturbed mind, we finally try to asynchronous run-time error:

window.onerror = function(message, source, lineno, colno, error) {
    console.log('捕获到异常:',{message, source, lineno, colno, error});
}
setTimeout(() => {
    Jartto;
});

Console output:

捕获到异常:{message: "Uncaught ReferenceError: Jartto is not defined", source: "http://127.0.0.1:8001/", lineno: 36, colno: 5, error: ReferenceError: Jartto is not defined
    at setTimeout (http://127.0.0.1:8001/:36:5)}

4. Then, we try unusual request the network:

<script>
window.onerror = function(message, source, lineno, colno, error) {
    console.log('捕获到异常:',{message, source, lineno, colno, error});
    return true;
}
</script>
<img src="./jartto.png">

我们发现,不论是静态资源异常,或者接口异常,错误都无法捕获到。Add that: window.onerror function returns true when only exception will not throw up, or even be aware of the occurrence of abnormal console will still show Uncaught Error: xxxxx

window.onerror = function(message, source, lineno, colno, error) {
    console.log('捕获到异常:',{message, source, lineno, colno, error});
    return true;
}
setTimeout(() => {
    Jartto;
});

The console will not have such a mistake:

Uncaught ReferenceError: Jartto is not defined at setTimeout ((index):36)

requires attention:

  • onerror best to write in front of all the JS script, or they may not catch errors;
  • onerror not catch grammatical errors;

Here basically clear: in the actual use of the process, mainly to capture the onerror unexpected error, try-catch and is used to monitor specific errors under foreseeable circumstances, more efficient use of a combination of both. Problem again, capturing less than the static resource loading abnormal how to do?

五、window.addEventListener

When a resource (such as images or scripts) fails to load, load the resource element triggers an error event Event interface, and perform onerror on the element () handler. These error events do not bubble up to the window, but (at least in Firefox) can be a single window.addEventListener capture.

<scritp>
window.addEventListener('error', (error) => {
    console.log('捕获到异常:', error);
}, true)
</script>
<img src="./jartto.png">

Console output

Since the network does not request an exception event bubbling, and therefore must be captured during the capture phase to the job, but in this way, although you can capture the abnormal network requests, but can not determine the status of HTTP is 404 or other such as 500 and so on, so also it needs to meet the server log analysis can only carry out the investigation. requires attention:

  • error object returned may be different in different browsers, need to pay attention compatible processing.
  • AddEventListener need to be taken to avoid duplicate monitoring.

六、Promise Catch

Use catch in the promise can be very convenient to capture asynchronous error, this is very simple. Promise not to write catch the thrown error can not be caught or onerror to try-catch, so we want to be sure not to forget to catch exceptions thrown in the treatment of Promise. Solution: In order to prevent abnormal missed Promise, a proposed increase in the global monitor of unhandledrejection, to globally monitor Uncaught Promise Error. Use:

window.addEventListener("unhandledrejection", function(e){
  console.log(e);
});

We continue to try:

window.addEventListener("unhandledrejection", function(e){
  e.preventDefault()
  console.log('捕获到异常:', e);
  return true;
});
Promise.reject('promise error');

We can see the following output:

Promise that if not catch it?

window.addEventListener("unhandledrejection", function(e){
  e.preventDefault()
  console.log('捕获到异常:', e);
  return true;
});
new Promise((resolve, reject) => {
  reject('jartto: promise error');
});

Ah, it turns out, it is also a normal captured. So, as we said above, in order to prevent abnormal missed Promise, a proposed increase in the global monitor of unhandledrejection, to globally monitor Uncaught Promise Error. Add this: If you remove the abnormal display console, you need to add:

event.preventDefault();

Seven, VUE errorHandler

= Vue.config.errorHandler (ERR, VM, info) => { 
  console.error ( 'Error captured by vue errorHandler' ); 
  console.error (ERR); 
  console.error (VM); 
  console.error (info) ; 
}

Eight, React exception caught

React 16 provides a built-in function componentDidCatch, using it can be very easy to get the wrong information under react

componentDidCatch(error, info) {
    console.log(error, info);
}

In addition, we can find out: JS error in a part of the cause of the error boundary UI should not undermine the entire program, in order to help users solve this problem React, React 16 introduces a mistake on the border (error boundary) of new ideas. Note that: error boundaries and below will not catch these errors.

  1. Event Processor
  2. Asynchronous code
  3. Server-side rendering code
  4. Error in the error boundaries regions

Let's take a small example, in the following componentDIdCatch (error, info) in the class will become a error boundary:

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }
 
  componentDidCatch(error, info) {
    // Display fallback UI
    this.setState({ hasError: true });
    // You can also log the error to an error reporting service
    logErrorToMyService(error, info);
  }
 
  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

Then we did use it like a normal components:

<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>

componentDidCatch () method works like the JS catch {} block, but for assembly, only the class types of components (Component class) may be an error boundaries. In fact, in most cases we can define an error boundary components throughout the program, then you can always use it!

Nine, iframe abnormal

For abnormal capture the iframe, we had to leveraging window.onerror:

window.onerror = function(message, source, lineno, colno, error) {
  console.log('捕获到异常:',{message, source, lineno, colno, error});
}

A simple example might be as follows:

<iframe src="./iframe.html" frameborder="0"></iframe>
<script>
  window.frames[0].onerror = function (message, source, lineno, colno, error) {
    console.log('捕获到 iframe 异常:',{message, source, lineno, colno, error});
    return true;
  };
</script>

十、Script error

Typically, if such an error Script error occurs, basically it can determine that there is a cross-domain problem. At this time, there will not be too many other auxiliary information, but the solution is nothing more than the idea as follows: Cross-Origin Resource Sharing Mechanism (CORS): We added crossOrigin script tag attribute.

<script src="http://jartto.wang/main.js" crossorigin></script>

Or to add dynamic js script:

const script = document.createElement('script');
script.crossOrigin = 'anonymous';
script.src = url;
document.body.appendChild(script);

Special attention, the server needs to be set: Access-Control-Allow-Origin In addition, we can also try this - Alternative ideas to solve the Script Error:

const originAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function (type, listener, options) {
  const wrappedListener = function (...args) {
    try {
      return listener.apply(this, args);
    }
    catch (err) {
      throw err;
    }
  }
  return originAddEventListener.call(this, type, wrappedListener, options);
}

Simple explanation: rewrite the addEventListener method of EventTarget; incoming listener packaging, returnable packaging over the listener, be try-catch its implementation; the browser does not intercept on try-catch up abnormal cross-domain, so to catch when there is a stack of information; re throw out an exception when execution is the same field code, so when window.onerror captured without losing stack information; use of packaging addEventListener, we can also reach the "extended stack" Effect:

(() => { 
   Const originAddEventListener = EventTarget.prototype.addEventListener; 
   EventTarget.prototype.addEventListener = function (of the type, listener, Options) {
 +     // when capturing add an event stack 
+ = const addStack new new Error ( `Event ($ type} {) `) .stack; 
     const wrappedListener = function (... args) {
        the try {
          return listener.apply ( the this , args); 
       } 
       the catch (ERR) {
 +         // when an exception occurs, the extended stack 
+ err. stack + = '\ n' + addStack;
         throw err;
       }
     }
     return originAddEventListener.call(this, type, wrappedListener, options);
   }
 })();

XI crashes and Caton

Caton is a temporary page response is slow, JS may not be performed in a timely manner. But the collapse is not the same, the page will collapse, JS do not run, what else can monitor the collapse of the page, and the page crash report it? Caton collapse and can not be ignored, may result in the loss of your users.

  1. Use the window object load and beforeunload events to achieve a web page crashes monitoring. Good article, Recommended reading: Logging Information on Browser Crashes.
window.addEventListener('load', function () {
    sessionStorage.setItem('good_exit', 'pending');
    setInterval(function () {
        sessionStorage.setItem('time_before_crash', new Date().toString());
    }, 1000);
  });

  window.addEventListener('beforeunload', function () {
    sessionStorage.setItem('good_exit', 'true');
  });

  if(sessionStorage.getItem('good_exit') &&
    sessionStorage.getItem('good_exit') !== 'true') {
    /*
        insert crash logging code here
    */
    alert('Hey, welcome back from your crash, looks like you crashed on: ' + sessionStorage.getItem('time_before_crash'));
  }
  1. Based on the following reasons, we can achieve the collapse of the web page to monitor the use of Service Worker:

Service Worker worker thread has its own independent, separate from the web area, Jim, will not collapse under the Service Worker normal circumstances; Service Worker life cycle is generally much longer than a page, can be used to monitor the state of the web page; pages can navigator.serviceWorker.controller.postMessage API sends a message to take charge of your SW.

Twelve, error reporting

1. Sending data via Ajax Ajax request itself because there may be abnormalities occur, and may lead to cross-domain issues, more generally recommended to use dynamically created img tag in the form of reporting.

2. Create a dynamic form of the img tag

function report(error) {
  let reportUrl = 'http://jartto.wang/report';
  new Image().src = `${reportUrl}?logs=${error}`;
}

Abnormal collect too much information, how to do? In practice, we have to consider such a situation: if your site becomes, the information sent by an inevitable mistake, there are many bars, this time, we need to set acquisition rates, thereby slowing the pressure on the server:

= Reporter.send function (Data) {
   // only collection% 30 
  IF (Math.random () <0.3 ) { 
    Send (Data)       // report error 
  } 
}

Acquisition rate should be set by the actual case, the random numbers, or some features of the user are a good choice.

XIII summary

Back to the question we raised at the beginning, how to handle exceptions elegant it?

  1. Suspect areas increased Try-Catch
  2. JS Global Monitoring abnormal window.onerror
  3. Global Monitoring static resource abnormal window.addEventListener
  4. Catch the capture no abnormal Promise: unhandledrejection
  5. VUE errorHandler 和 React componentDidCatch
  6. Monitoring web page crashes: load and beforeunload window object
  7. To solve cross-domain crossOrigin

Actually very simple, as stated above: using a combination of program, type of classification to catch exceptions, so 80% -90% of the basic problems of the invisible.

 

Description link

Guess you like

Origin www.cnblogs.com/momo798/p/12053587.html