preface

As a front-end, in the development process, even if very careful, full self-test, in the complex operation of different users will inevitably appear unexpected problems of programmers, to the company or individuals to bring huge losses. At this time, a front-end error monitoring system that can timely report errors and help programmers to solve errors is essential. Let’s talk about common errors and handling.

This paper mainly focuses on the following points:

  1. Common JS error types
  2. Common JS handling errors
  3. Report the way, and report the content of several thoughts

Question:

  1. JS, CSS, IMG and other resources loading failure (CDN or map bed hung, accidentally deleted, file name changed) how to real-time information? Instead of the user telling you?
  2. How can useful error information be reported so that programmers can quickly locate the error and fix it? Instead of reporting some confusing information?
  3. How to make good use of SourceMap file to deal with error information in today’s engineering of compressed ugly code?
  4. How to go wrong without having to ask the user to help you reproduce? Want to model? How to do it?
  5. How to make better statistics on the distribution of problems (models, devices, browsers, geographical locations, bandwidth, etc.) and independently choose compatibility tendencies according to the data?
  6. .

Common mistakes

  1. Script errors
    • Grammar mistakes
    • Runtime error
      • Synchronization error
      • Asynchronous error
      • Promise error
  2. Network error
    • Resource loading error
    • Custom request error

You can read the monitoring library source code errorWatch for further understanding, or you can use it directly for projects.

Grammar mistakes

For example, English characters are written as Chinese characters. It is generally easy to discover during development.

Syntax errors cannot be handled by try catch

try {
  const error = 'error';// semicolons with rounded corners
} catch(e) {
  console.log('I can't sense a mistake');
}
Copy the code

Synchronization error

When executing a script, the JS engine pushes tasks into the event stack and polling them out for execution. Each event task has its own context. Any error occurred in the synchronized code executed in the current context can be caught by try catch to ensure that the subsequent synchronized code is executed.

try {
  error
} catch(e) {
  console.log(e);
}
Copy the code

Asynchronous error

Common methods such as setTimeout create new event tasks to insert into the event stack for later execution. So try catch cannot catch code errors in other contexts.

try {
  setTimeout((a)= > {
    error        // Async error})}catch(e) {
  console.log('I can't sense a mistake');
}
Copy the code

In order to analyze errors, the window.onerror event is used to listen for errors. It is more powerful than try catch’s ability to catch error messages.

/** * @param {String} MSG Error description * @param {String} URL error file * @param {Number} row Number * @param {Number} col Number * @param {Object} error Error Object */
 window.onerror = function (msg, url, row, col, error) {
  console.log('I know my mistake.');
  // return true; // When true is returned, the exception will not be thrown up and the console will not print an error
};
Copy the code

  • Window. onerror Precautions
  1. window.onerrorCan catch common syntax, synchronous, asynchronous errors and other errors;
  2. window.onerrorUnable to capturePromiseError, network error;
  3. window.onerrorShould be executed before all JS scripts to avoid omission;
  4. window.onerrorEasily overridden and should be considered when handling callbacks, being listened on by someone who is also using the event.

Network error

Because network request exceptions do not bubble, they need to be retrieved during the event capture phase. We can use window.addeventListener. For example, when important CDN resources such as code and pictures fail, it is extremely important to get timely feedback.

window.addEventListener('error', (error) => {
  console.log('404 error');
  console.log(error);
  // return true; // Interrupts event propagation
}, true);
Copy the code

For such resource loading errors, sufficient information can be obtained in the event object, and the developer can be notified in the first time with SMS, nail, etc.

window.addEventListener('error', (e) => {
  if(e.target ! = =window) { // Avoid duplicate reporting
    console.log({
    	url: window.location.href, // Reference the resource address
    	srcUrl: e.target.src, // Resource loading error address})}},true);
Copy the code
  • window.onerrorwindow.addEventListener

The benefit of window.addeventListener is that you can listen for multiple callbacks without overwriting them, but remember to destroy them to avoid memory leaks and errors. But you don’t get as much information as window.onerror. Generally, only window.addeventListener is used to monitor resource loading errors.

  • You are advised to manually report customized network request errors.

Promise error

If you don’t catch a promise, then onError doesn’t do anything.

Promise.reject('promise error');
new Promise((resolve, reject) = > {
  reject('promise error');
});
new Promise((resolve) = > {
  resolve();
}).then((a)= > {
  throw 'promise error';
});
Copy the code

You can use the same window. Onunhandledrejection or Windows. The addEventListener (” unhandledrejection “) to monitor the error. Receive a PromiseError object that resolves the Reason property of the error object, sort of like a stack.

The compatibility process can be seen in traceKit.js.

Report the way

  1. imgreport
  2. ajaxreport
function report(errInfo) {
  new Image().src = 'http://your-api-website? data=' + errInfo;
}
Copy the code

Ajax should be using a library, much the same.

  • Note:imgThe request has a length limit. If the data is too large, it is best to use itajax.post.

supplement

Script error

If a Script referencing a different domain name is not processed in a special way, an error is reported. For security reasons, browsers do not display a specific error, but a Script error. For example, if someone is quoting your online non-open source business code for ulterior motives, you don’t want them to know about your script error message.

How to solve the cross-domain error problem of own scripts?

  • All resources are switched to the unified domain, but then lostCDNThe advantage of.
  • In the script fileHTTP response headerSet in theCORS.
  1. Access-Control-Allow-Origin: You-allow-origin;
  2. Script tagcrossoriginProperties, such as<script src="http://www.xxx.com/index.js" crossorigin></script>

Response header and Crossorigin values

  1. crossorigin="anonymous"(Default),CORSIs not equal toYou-allow-origin, can’tcookie
  2. crossorigin="use-credentials"andAccess-Control-Allow-Credentials: true ,CORSCannot be set to*Can,cookie. ifCORSIs not equal toYou-allow-originThe browser does not load JS.

Script errors can be filtered out and not reported when you cors free energy resources.

With all that said, there is a very important topic, how do I analyze error messages that I can catch?

JavaScript Error profiling

A JavaScript error usually consists of the following errors

  • Error message
  • Stack Trace

Developers can throw a JavaScript error in different ways:

  • throw new Error(‘Problem description.’)
  • throw Error(‘Problem description.’) <– equivalent to the first one
  • throw ‘Problem description.’ <– bad
  • throw null <– even worse

The second option is recommended; the third or fourth browsers cannot generate a trace stack for either of the two options.

If you can parse the error message in each row trace stack, the rows and columns in conjunction with SourceMap can locate each specific line of source code. The problem is that different browsers do not have a common format for this information. The difficulty lies in resolving compatibility issues.

For example, window.onerror the fifth parameter error object was added to the WHATWG specification in 2013. Safari and Internet Explorer 10 didn’t have them in the early days, Firefox added Error objects in version 14, and Chrome only added them in 2013.

The recommendation

  1. Window. onerror is the best way to catch JS errors and is reported only when there is a valid Error object and trace stack. You can also avoid non-intrusive errors such as plug-in errors and cross-domain errors with incomplete information.

  2. Try catch enhancements, which throw more complete error messages, can compensate for window.onerror. But as mentioned earlier, try catch does not catch asynchronous and promise errors, nor does it take advantage of V8 engine performance optimizations.

For example, Tencent’s BadJS has a try catch package for the following recommendations

  • SetTimeout and setInterval
  • event
  • ajax callback
  • Define and the require
  • Main business entrance

Whether such a fine-grained package is needed depends on the situation.

SourceMap

For example, there is the following stack trace

ReferenceError: thisIsAbug is not defined
    at Object.makeError (http://localhost:7001/public/js/traceKit.min.js:1:9435)
    at http://localhost:7001/public/demo.html:28:12
Copy the code

Can be parsed into the following format

[{"args": []."url" : "http://localhost:7001/public/js/traceKit.min.js"."func" : "Object.makeError"."line" : 1."column" : 9435."context" : null
	}, 
	{
	  "args": []."url" : "http://localhost:7001/public/demo.html"."func" : "?"."line" : 28."column" : 12."context" : null}]Copy the code

With rows and columns and the corresponding SourceMap file, source code information can be parsed.

Analytical results

The processing code is as follows:

import { SourceMapConsumer } from 'source-map';

// Must be initialized
SourceMapConsumer.initialize({
  'lib/mappings.wasm': 'https://unpkg.com/[email protected]/lib/mappings.wasm'});@param {String} rawSourceMap sourceMap * @param {Number} line error * @param {Number} Column compression code error column * @param {Number} offset set to return the Number of adjacent rows * @returns {Promise<{context: string, originLine: Number | null, source: string > | null}} * context: source lines error and fluctuation near offset, originLine: source line error, the source: the source file name * /
export const sourceMapDeal = async (rawSourceMap, line, column, offset) => {
  // Convert to the sourceMapConsumer object using the sourceMap library
  const consumer = await new SourceMapConsumer(rawSourceMap);
  // Pass in the number of rows to find, find the source file before compression and the number of rows
  const sm = consumer.originalPositionFor({
    line, // The number of compressed lines
    column, // The number of compressed columns
  });
  // A list of all source files before compression
  const { sources } = consumer;
  // Find the index position in the source file list
  const smIndex = sources.indexOf(sm.source);
  // Find the source code in the source list
  const smContent = consumer.sourcesContent[smIndex];
  // Split source code strings into arrays by "line end tag"
  const rawLines = smContent.split(/\r? \n/g);
  let begin = sm.line - offset;
  const end = sm.line + offset + 1;
  begin = begin < 0 ? 0 : begin;
  const context = rawLines.slice(begin, end).join('\n');
  // Remember to destroy
  consumer.destroy();
  return {
    context,
    originLine: sm.line + 1.// line starts at 0, so +1
    source: sm.source,
  }
};
Copy the code

You can easily understand this code based on the format of the SourceMap file.

Refer to the website

  1. mozilla/source-map
  2. Front-end code abnormal monitoring actual combat
  3. Front-end exception monitoring – BadJS
  4. Script error volume optimization – make the script error at a glance