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:
- Javascript handles exceptions
- Report the way
- 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.
-
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.
-
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.
-
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
- 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.
- 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:
- Sending data through Ajax
- 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.