preface

Before, I investigated the front-end code script errors of the company and tried to reduce the number of JS errors. Based on my previous experience, I practiced and summarized this aspect. Now I will talk about my opinions on the abnormal monitoring of front-end code.

This paper mainly discusses the following points:

  1. How JS handles exceptions
  2. Report the way
  3. Abnormal monitoring Reports common problems

JS exception handling

For Javascript, we are only faced with exceptions, the occurrence of exceptions will not directly cause the JS engine to crash, at most will cause the current execution of the task to terminate.

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

  2. When an exception occurs during the execution of a task and the exception is not caught and handled, it is continuously thrown out of the call stack until the execution of the current task is terminated.

  3. The JS thread will continue to fetch the next task from the task queue and continue to execute it.

Script errors generally fall into two categories: syntax errors and runtime errors.

Here are several ways to handle exception monitoring:

Try-catch exception processing

We often see try-catches in our code. By wrapping a block of code with a try-catch, when something goes wrong with the block, the catch catches the error message and the page continues to execute.

However, try-catch is limited in its ability to handle exceptions. It can only catch non-asynchronous errors at runtime. Syntax and asynchronous errors cannot be caught.

Example: runtime error

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

Example: syntax error

Try {varerror = 'error'; }catch(e){console.log(' I don't perceive errors '); console.log(e); }Copy the code

Example: Asynchronous error

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

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 I'm wrong '); console.log({msg,url,row,col,error})returntrue; }; error;Copy the code

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

In practical use, onError is mainly used to catch unexpected errors, while try-catch is used to monitor specific errors in predictable situations. The combination of onError and try-catch is more efficient.

Note that window.onerror does not raise an exception until it returns true, otherwise the console will display Uncaught Error: XXXXX even if it knows the exception occurred.

  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 are writing will not be wrong. If you write it later, it will not be caught by onError.
  2. In addition, onError is an error that cannot catch network exceptions.

When we meetOnerror does not help us catch a 404 network request exception.

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

<script>window.addEventListener('error',(MSG,url,row,col,error)=>{console.log(' I know 404 is wrong '); console.log(msg,url,row,col,error); returntrue; },true); </script><imgsrc="./404.png"alt="">Copy the code

Promise error

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

Window. addEventListener('error',(MSG,url,row,col,error)=>{console.log(' I don't perceive a promise error'); console.log(msg,url,row,col,error); },true); Promise.reject('promise error'); newPromise((resolve,reject)=>{reject('promise error'); }); newPromise((resolve)=>{resolve(); }).then(()=>{throw'promise error'});Copy the code

So if your application uses a lot of Promise instances, especially if you’re using asynchronous Promise based libraries like Axios, you have to be careful because you don’t know when those asynchronous requests will throw an exception and you’re not handling it, So you’d better add a Promise global exception capture event, unHandledrejection.

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

Abnormal Reporting Mode

After receiving the error information, the monitoring system needs to send the captured error information to the information collection platform. There are two main ways to send the error information:

  1. Send data through Ajax
  2. Dynamically create the form of an IMG tag

Instance – Dynamically create an IMG tag for reporting

functionreport(error){varreportUrl='http://xxxx/report'; newImage().src=reportUrl+'error='+error; }Copy the code

Monitoring Reports common problems

The following examples are all posted on my Github for readers to look up.

git clone https://github.com/happylindz/blog.git
cd blog/code/jserror/
npm install
Copy the code

What is a Script error

Because our online version is often CDN for static resources, this will cause the pages we visit frequently to come from different domain names of the file, which will easily produce Script errors if no additional configuration is made.

Script error is generated by the browser under the restriction of the same origin policy. The browser is concerned about security. If an exception is thrown when a page references a Script file outside a different domain name, the page has no right to know this error message.

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

This HTML is the login page of the bank. If you are already logged in, the login page automatically redirects to Welcome XXX… If you have not logged in, go to Please Login… , then the error will be Welcome XXX… Is not defined, Please Login… Is not defined, according to the information, you can determine whether a user logs in to his or her account, which provides a convenient channel for intruders to determine, which is quite insecure.

After the background, then should we address the problem?

The first solution that can come to mind must be the homology strategy, which can inline JS files to HTML or put them in the same domain. Although it can solve script error problem simply and effectively, it cannot take advantage of file cache and CDN, so it is not recommended. The correct approach is to solve script error errors at their root.

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. The onerror = function (MSG, url, the row, col, error) {the console. The log (' I know wrong, Also know the error message '); console.log({msg,url,row,col,error})returntrue; }; </script><scriptsrc="http://localhost:8081/test.js"crossorigin></script> // http://localhost:8081/test.js setTimeout(() => { console.log(error); });Copy the code

Access-control-allow-origin: localhost:8080 when you modify the front-end code, you also need to add access-Control-allow-Origin: localhost:8080 to the back end of the response header.

constKoa=require('koa'); constpath=require('path'); constcors=require('koa-cors'); constapp=newKoa(); 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

You think this is the end of it? No, here are some Script error points you don’t see very often:

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

/ / http://localhost:8080/index.htmlwindow.onerror=function (MSG, url, the row, col, error) {the console. The log (' I know the mistake, but I don't know the error message "); console.log({msg,url,row,col,error})returntrue; }; functionjsonpCallback(data){console.log(data); }consturl='http://localhost:8081/data? callback=jsonpCallback'; constscript=document.createElement('script'); script.src=url; document.body.appendChild(script);Copy the code

Because the returned message is executed as a script file, if the returned script content fails, the error message cannot be caught.As before, add crossOrigin to the dynamic add script and add the corresponding CORS field on the back end.

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

The reader can see the effect through NPM Run jsonpOnce you know how it works, you might think nothing of adding a crossOrigin field to every dynamically generated script, but in real life, you might be programming for many libraries, such as using jQuery, Seajs, or WebPack to load scripts asynchronously. Many libraries encapsulate the ability to load scripts asynchronously, and in the case of jQeury you might do this to trigger asynchronous scripts.

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

If one of these libraries doesn’t provide crossOrigin capability (jQuery jSONp probably does, pretend you don’t know), then you’ll have to modify the source code written by someone else, so here’s a way to hijack document.createElement. Add crossOrigin fields from the root for each dynamically generated script.

document.createElement=(function(){constfn=document.createElement.bind(document); returnfunction(type){constresult=fn(type); if(type==='script'){result.crossOrigin='anonymous'; }returnresult; }}) (); 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})returntrue; }; $.ajax({url:'http://localhost:8081/data',dataType:'jsonp',success:(data)=>{console.log(data); }})Copy the code

The effect is the same and can be seen by NPM run jsonpjq:Create createElement (createElement) {createElement (createElement) {createElement (createElement) {createElement (createElement) {createElement (createElement)}}

That’s it for Script errors, but if you understand the above, almost all Script errors can be solved.

Window. onError Whether to catch iframe errors

If your page uses an iframe, you need to monitor your imported iframe for exceptions. Otherwise, if your imported iframe page has a problem, your main site will not display it, and you will not know it.

First of all, it is important to note that the parent window cannot be caught directly using window.onError. If you want to catch an iframe exception, there are several cases.

If your iframe page is in the same domain as your host, simply add onError to the iframe.

<iframesrc="./iframe.html"frameborder="0"></iframe><script>window.frames[0].onerror=function(msg,url,row,col,error){cons Ole.log (' I know iframe is wrong and I know the error message '); console.log({msg,url,row,col,error})returntrue; }; </script>Copy the code

NPM run iframe to see the effect:If your embedded iframe page is not the same domain name as your host site, but the iframe content is not owned by a third party and is under your control, you can communicate with the iframe to send the exception message to the host site. There are many ways to communicate with an iframe, such as postMessage, hash, or name fields across fields.Cross domains. Everything you need to know is here

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

How does compressed code locate script exceptions

Almost all the codes on the line have been compressed, and dozens of files have been packaged into one ugly code. When we receive a is not defined, we have no idea what the variable A actually means, and the error log reported in this case is obviously invalid.

The first thought was to use Sourcemap to locate the error code. For details, see sourcemap to locate script errors

In addition, you can add several lines of space between each merged file and some corresponding comments during the packaging process. In this way, it is easy to know which file reports the error when locating the problem. Then, you can quickly locate the problem by searching some keywords.

How to collect too much exception information

If your website visits a lot, if the PV of the page has 1KW, then an inevitable error message will have 1kW, we can set a collection rate for the website:

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

This collection rate can be set by the actual situation, the method is diversified, can use a random number, can also be specific according to some characteristics of the user to determine.

The above is almost my understanding of front-end code monitoring, easy to say, but once in engineering application, it is inevitable to consider compatibility and other problems, readers can adjust through their own specific situation, front-end code anomaly monitoring plays a crucial role in the stability of our website. If the article all wrong place, also hope to correct.

Refer to the article

  • Script Error volume optimization – Monitors Script error reporting
  • Collect and monitor front-end code exception logs
  • Front-end Magic Hall – Exceptions are more than just try/catch