The mooC teaching resources are updated in the hands of this first-line developer buddy. Meanwhile, there are interview guidance technology services for the upcoming entrants.

This article will focus on the following points:

  1. Javascript handles exceptions
  2. Report the way
  3. Common faults of abnormal monitoring report

JS exception handling

For Javascript, we are only faced with exceptions. The occurrence of exceptions does not directly crash the JS engine, but at most terminates the currently executing task.

  1. The current code block will be pushed into the task queue as a task, and the JS thread will continue to extract tasks from the task queue for execution.

  2. When an exception occurs during the execution of a task, and the exception is not caught and handled, it will always be thrown out along the call stack layer by layer, and finally terminate the execution of the current task.

  3. The JS thread continues to extract the next task from the task queue.

Before a script error can be reported, we need to handle the exception, and the program needs to be aware that a script error has occurred before we can talk about exception reporting.

There are two types of script errors: syntax errors and runtime errors.

Here are some ways to handle exception monitoring:

Try-catch Exception handling

We often see a try-catch in our code. By wrapping a block of code with a try-catch, the catch will catch the error message when the block fails, and the page will continue executing.

However, the try-catch is limited in its ability to handle exceptions. It can only catch run-time non-asynchronous errors. It can’t catch syntax errors and asynchronous errors.

Example: Runtime error

Try {error // undefined variable} catch(e) {console.log(' I know there's an error '); console.log(e); }Copy the code

However, syntax errors and asynchronous errors are not caught.

Example: Syntax error

Try {var error = 'error'; } catch(e) {console.log(' I didn't notice the error '); console.log(e); }Copy the code

Common syntax errors will be reflected in the editor. The common error message will be:

Uncaught SyntaxError: Invalid or unexpected token xxx

Like this. However, this kind of error directly throws an exception, often causing the program to crash, and is generally easy to observe at coding time.

Example: Asynchronous error

Try {setTimeout(() => {error // asynchronous error})} catch(e) {console.log(' I didn't notice the error '); console.log(e); }Copy the code

Unless you put another layer of try-catch inside the setTimeout function, you won’t be able to detect the error, but that makes the code tedious to write.

Window. onError exception handling

Window. onError is slightly better at catching exceptions than try-catch. Onerror can catch run-time errors whether asynchronous or non-asynchronous.

Example: Runtime synchronization error

/** * @param {String} MSG error message * @param {String} URL error file * @param {Number} row Number * @param {Number} col column Number * @param */ window.onerror = function (MSG, url, row, col, error) {console.log(' I know there is an error '); console.log({ msg, url, row, col, error }) return true; }; error;Copy the code

Example: Asynchronous error

Window. onerror = function (MSG, url, row, col, error) {console.log(' I know asynchronous error '); console.log({ msg, url, row, col, error }) return true; }; setTimeout(() => { error; });Copy the code

However, window.onError does not help with syntax errors, so we should try to avoid syntax errors when writing code, but generally such errors will crash the entire page, is relatively easy to detect.

In practice, onError is used to catch unexpected errors, and try-catch is used to monitor specific errors in predictable situations. A combination of the two is more efficient.

It is important to note that the window. onError function only returns true when the exception is not raised, otherwise the console will display it even if it knows that the exception occurred

Uncaught Error: xxxxx

.

There are two other things to note about window.onError

  1. For global capture like onError, it is best to write it at the beginning of all JS scripts, because you cannot guarantee that the code you write will not make errors. If you write it later, the error will not be caught by onError.
  2. In addition, onError is an error that cannot catch a network exception.

When we meet

Onerror does not help us catch a 404 network request exception.

<script> window.onerror = function (MSG, url, row, col, error) {console.log(' I know asynchronous error '); console.log({ msg, url, row, col, error }) return true; }; </script> <img src="./404.png">Copy the code

Since network request exceptions do not bubble, they must be captured in the capture phase. Although this method can catch network request exceptions, it cannot determine whether the HTTP status is 404 or other, such as 500, etc. Therefore, it needs to coordinate with server logs to conduct investigation and analysis.

<script> window.addeventListener ('error', (MSG, URL, row, col, error) => {console.log(' I know 404 error'); console.log( msg, url, row, col, error ); return true; }, true); </script> <img src="./404.png" alt="">Copy the code

This knowledge still needs to know, otherwise the user visits the website, the picture CDN cannot serve, the picture does not come out and the developer is not aware of the embarrassment.

Promise error

Promise can help with the asynchronous callback hell, but once a Promise instance throws an exception that you don’t catch with a catch, there’s nothing onError or try-catch can do to catch the error.

Window.addeventlistener ('error', (MSG, URL, row, col, error) => {console.log(' I didn't realize the promise error'); console.log( msg, url, row, col, error ); }, true); Promise.reject('promise error'); new Promise((resolve, reject) => { reject('promise error'); }); new Promise((resolve) => { resolve(); }).then(() => { throw 'promise error' });Copy the code

While it’s a good habit to write a Promise instance with a catch function at the end, it’s easy to get confused and forget to write a catch.

So if your application uses a lot of Promise instances, especially if you’re using promise-based asynchronous libraries like Axios, you have to be careful because you don’t know when an asynchronous request is going to throw an exception and you’re not handling it. So you’d better add a Promise global exception catching event unhandledrejection.

Window.addeventlistener ("unhandledrejection", function(e){e.preventDefault() console.log(' I know the promise's error '); console.log(e.reason); return true; }); Promise.reject('promise error'); new Promise((resolve, reject) => { reject('promise error'); }); new Promise((resolve) => { resolve(); }).then(() => { throw 'promise error' });Copy the code

Of course, if your app doesn’t do Promise global exception handling, it’s likely to look something like this on the home page:

Abnormal Reporting Mode

After the monitor gets the error information, it needs to send the captured error information to the information collection platform. There are two commonly used sending forms:

  1. Sending data through Ajax
  2. Dynamically create img tag form

Instance – Dynamically create img tags for reporting

function report(error) {
  var reportUrl = 'http://xxxx/report';
  new Image().src = reportUrl + 'error=' + error;
}
Copy the code

Monitoring report FAQ

Script error What is a Script error

Because of our online version, we often do static resource CDN, which can cause pages we often visit to come from a different domain name than this file, which can easily generate Script errors without additional configuration.

through

npm run nocors

See the effect.

A Script error is generated by the browser under the same origin policy. The browser is concerned about security and does not have the right to know if an exception is thrown when a page references a Script file outside the same domain. Instead, the page will output a Script error.

The goal is to avoid data leaking into insecure domains. To take a simple example,

<script src="xxxx.com/login.html"></script>
Copy the code

We didn’t introduce a JS file, but an HTML, which is the bank’s login page. If you are already logged in, the login page will automatically jump to

Welcome XXX…

If you are not logged in, go to

Both Please Login…

, the error will also be

Welcome XXX… Is not defined, Please Login… is not defined

By using these information, we can determine whether a user has logged in to his account, which provides a very convenient judgment channel for intruders, which is quite insecure.

After that background, then should we go and solve the problem?

The first solution that can be thought of is surely the homology policy, which is to inline JS files to HTML or put them in the same domain. Although it can solve the script error problem simply and effectively, it cannot take advantage of the advantages of file caching and CDN, so it is not recommended. The correct approach should be to solve the root of script error errors.

Cross-source Resource Sharing Mechanism (CORS)

Start by adding the crossOrigin attribute to the script tag on the page

// http://localhost:8080/index.html <script> window.onerror = function (msg, url, row, col, Error) {console.log(' I know the error and know the error message '); console.log({ msg, url, row, col, error }) return true; }; </script> <script src="http://localhost:8081/test.js" crossorigin></script> // http://localhost:8081/test.js setTimeout(() => { console.log(error); });Copy the code

After you have changed the front end code, you need to add an extra response header for the back end

Access-Control-Allow-Origin: localhost:8080

I’ll use Koa as an example.

const Koa = require('koa');
const path = require('path');
const cors = require('koa-cors');
const app = new Koa();

app.use(cors());
app.use(require('koa-static')(path.resolve(__dirname, './public')));

app.listen(8081, () => {
  console.log('koa app listening at 8081')
});
Copy the code

Readers can read

npm run cors

I won’t go into the details of cross-domain knowledge, but check out my previous article: Cross-domain, all you need to know here

You think that’s the end of it? No, here are some points you don’t often encounter with Script error:

We all know that JSONP is used to get data across domains, and it is compatible and still used in some applications, so your project might use code like this:

/ / http://localhost:8080/index.html window. Onerror = function (MSG, url, the row, col, error) {the console. The log (' I know wrong, But don't know the error message '); console.log({ msg, url, row, col, error }) return true; }; function jsonpCallback(data) { console.log(data); } const url = 'http://localhost:8081/data? callback=jsonpCallback'; const script = document.createElement('script'); script.src = url; document.body.appendChild(script);Copy the code

Because the returned information is executed as a script file, if the returned content of the script fails, the error information cannot be caught.

The solution is as simple as adding crossOrigin to the dynamic add script and the corresponding CORS field on the back end.

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

Readers can read

npm run jsonp

See the effect

You might think it’s okay to add crossOrigin to every dynamically generated script, but in a real project, you might be programming to many libraries, like jQuery, Seajs, or WebPack to load scripts asynchronously. Many libraries encapsulate the ability to load scripts asynchronously, as in the case of jQeury you might do to trigger an asynchronous script.

$.ajax({ url: 'http://localhost:8081/data', dataType: 'jsonp', success: (data) => { console.log(data); }})Copy the code

If these libraries don’t provide crossOrigin capability (jQuery jSONp does, pretend you don’t know), then you’ll have to modify the source code that someone else wrote, so here’s an idea to hijack document.createElement. Add the crossOrigin field to each dynamically generated script from the root.

document.createElement = (function() { const fn = document.createElement.bind(document); return function(type) { const result = fn(type); if(type === 'script') { result.crossOrigin = 'anonymous'; } return result; }}) (); Window. onerror = function (MSG, url, row, col, error) {console.log(' I know the error, I know the error '); console.log({ msg, url, row, col, error }) return true; }; $.ajax({ url: 'http://localhost:8081/data', dataType: 'jsonp', success: (data) => { console.log(data); }})Copy the code

The effect is the same, the reader can pass

npm run jsonpjq

To see the effect:

This rewrite of createElement is not a problem in theory, but the original code is not guaranteed to be error-free, in engineering or need to try more to see before using, there may be compatibility issues, if you think there will be any problems are welcome to comment below.

So much for Script errors. If you understand the above, most Script errors should be solved.

Window. onError Specifies whether to catch iframe errors

When your page uses an iframe, you need to monitor the exception of the iframe you imported. Otherwise, if the page you imported has a problem, your main site will not see it and you will not know it.

If you want to catch an iframe exception, there are several ways to do so.

If your iframe page is on the same domain as your main site, simply add onError to the iframe.

<iframe src="./iframe.html" frameborder="0"></iframe> <script> window.frames[0].onerror = function (msg, url, row, col, Error) {console.log(' I know the error of the iframe and I know the error message '); console.log({ msg, url, row, col, error }) return true; }; </script>Copy the code

Readers can read

npm run iframe

Check the effect:

If your embedded iframe page is not in the same domain as your main site, but the content of the iframe is not owned by a third party and is within your control, you can communicate with the iframe to throw exception information to the main site for receiving. There are many ways to communicate with an iframe, such as cross-domain postMessage, hash, or name fields. If you are interested in cross-domain, see cross-domain

If the site is not in the same domain and not under their own control, in addition to see detailed error information through the console, there is no way to capture, this is for the sake of security, you introduced a Baidu home page, someone else’s page reported the error with what let you to monitor it, this will lead to a lot of security problems.

How does compression code locate script exceptions

Almost all the code on the line has been compressed, dozens of files packaged into one and ugly code, when we receive

a is not defined

We have no idea what this variable a means, and the error log is obviously invalid.

The first idea is to use Sourcemap to locate the exact location of the error code. For details, see Sourcemap to locate script errors

In addition, you can add a few lines of space between each merged file and add some comments when packaging, so that when locating the problem, it is easy to know which file reported the error, and then through some keyword search, you can quickly locate the location of the problem.

What should I do if I collect too much information about exceptions

If your website visits a lot, if the PV of the page has 1KW, then a necessary error to send information has 1KW, we can set a collection rate to the website:

Reporter. Send = function(data) {if(math.random () < 0.3) {send(data)}}Copy the code

The collection rate can be set according to the specific actual situation, and the methods can be diversified, such as using a random number or judging according to some characteristics of users.

The above is almost my understanding of front-end code monitoring, it is easy to say, but once used in engineering, it is inevitable to consider various problems such as compatibility, readers can adjust through their own specific circumstances, front-end code exception monitoring plays a vital role in the stability of our website. If there is anything wrong in the passage, please correct it.