Article directory
error monitoring
foreword
As a front-end, even if you are very careful in the development process and fully self-tested, under the complicated operations of different users, unexpected problems will inevitably occur, which will bring huge losses to the company or individual.
At this time, a front-end error monitoring system that can report errors in time and help programmers solve errors well is essential.
Next, let's talk about the occurrence and handling of common errors.
This article mainly focuses on the following points:
- Common JS Error Types
- Common JS error handling methods
- The way of reporting and some thoughts on the content of reporting
question:
- JS, CSS, img and other resources fail to load (CDN or image bed is down, accidentally deleted, file name changed) how to know in real time? Instead of the user telling you?
- How to report useful error information so that programmers can quickly locate the error and fix it? Instead of reporting some confusing information?
- How to make good use of SourceMap files and handle error messages in today's engineering where there is no need to compress and uglify code?
- How can something go wrong without asking users to help you reproduce it? Want a model? Need steps?
- How to better count the distribution of problems (model equipment, browser, geographical location, bandwidth, etc.), and independently choose compatibility tendencies based on data?
- …
common mistakes
- script error
- Grammatical errors
- runtime error
- sync error
- async error
- Promise error
- Network Error
- resource loading error
- custom request error
Grammatical errors
For example, English characters are written as Chinese characters. Generally easy to be found during development.
Syntax errors cannot be try
catch
handled
try {
const error = 'error'; // 圆角分号
} catch(e) {
console.log('我感知不到错误');
}
sync error
When the JS engine executes the script, it pushes the tasks into the event stack in blocks and polls them for execution. Each event task has its own context.
Errors in the code executed synchronously in the current context can be try
catch
captured to ensure subsequent Synchronous code is executed.
try {
error
} catch(e) {
console.log(e);
}
async error
setTimeout
Common methods such as will create a new event task and insert it into the event stack for subsequent execution.
So try
catch
there is no way to catch code errors in other contexts.
try {
setTimeout(() => {
error // 异步错误
})
} catch(e) {
console.log('我感知不到错误');
}
In order to facilitate the analysis of errors, window.onerror
events to monitor the occurrence of errors.
It try
catch
is more powerful than the ability to catch error messages.
/**
* @param {String} msg 错误描述
* @param {String} url 报错文件
* @param {Number} row 行号
* @param {Number} col 列号
* @param {Object} error 错误Error对象
*/
window.onerror = function (msg, url, row, col, error) {
console.log('我知道错误了');
// return true; // 返回 true 的时候,异常不会向上抛出,控制台不会输出错误
};
window.onerror Notes
window.onerror
Can catch common syntax, synchronous, asynchronous errors and other errors;window.onerror
Unable to catchPromise
errors , network errors;window.onerror
Should be executed before all JS scripts to avoid missing;window.onerror
It is easy to be overwritten, and should be considered when processing callbacks, and people are also using this event listener.
Network Error
Since network request exceptions do not bubble up, they should only be obtained during the event capture phase.
We can take advantage window.addEventListener
. For example, if important CDN
resources are down, it is extremely important to get feedback in time.
window.addEventListener('error', (error) => {
console.log('404 错误');
console.log(error);
// return true; // 中断事件传播
}, true);
For this type of resource loading error, enough information can be obtained in the event object, and the developer will be notified as soon as possible with SMS, DingTalk, etc.
window.addEventListener('error', (e) => {
if (e.target !== window) {
// 避免重复上报
console.log({
url: window.location.href, // 引用资源地址
srcUrl: e.target.src, // 资源加载出错地址
})
}
}, true);
window.onerror
andwindow.addEventListener
window.addEventListener
The advantage is that you are not afraid of the callback being overwritten, and you can monitor multiple callback functions, but remember to destroy them to avoid memory leaks and errors.
However, window.onerror
such . Generally only used window.addEventListener
to monitor resource loading errors.
- For network request custom errors, it is best to report them manually.
Promise error
If you haven't unlocked it promise
when catch
, then onerror
there nothing you can do about it.
Promise.reject('promise error');
new Promise((resolve, reject) => {
reject('promise error');
});
new Promise((resolve) => {
resolve();
}).then(() => {
throw 'promise error';
});
Also you can use window.onunhandledrejection
or window.addEventListener("unhandledrejection")
to monitor errors.
Receiving a PromiseError object, you can parse reason
the property , which is somewhat similar stack
.
The specific compatibility processing can be seen in TraceKit.js.
Reporting method
img
reportajax
report
function report(errInfo) {
new Image().src = 'http://your-api-website?data=' + errInfo;
}
ajax
It's just the class library that should be used, much the same.
- Note:
img
The request has a length limit, and it is best to use it if the data is too largeajax.post
.
Script error
Script error
If there is no special treatment for scripts that refer to different domain names, an error will be reported .
Generally, browsers do not display specific errors because of security considerations. He knows.
How to solve the problem of cross-domain error reporting of own scripts?
- All resources are switched to a unified domain name, but this loses the advantage
CDN
of . HTTP response header
Set in the script fileCORS
.
Access-Control-Allow-Origin: You-allow-origin
;- Add
crossorigin
the attribute , for example<script src="http://www.xxx.com/index.js" crossorigin></script>
Response header and crossorigin
value problem
crossorigin="anonymous"
(default),CORS
not equal toYou-allow-origin
, cannot takecookie
crossorigin="use-credentials"
AndAccess-Control-Allow-Credentials: true
,CORS
it cannot be set as*
, energy bandcookie
.
IfCORS
not equalYou-allow-origin
, the browser does not load js.
cors
When you have done a good job with the resources that you can control freely , Script error
you can basically filter them out and not report them.
Having said so much, there is another very important topic, how to analyze the error messages that I can catch?
Anatomy of a JavaScript Error
An JavaScript
error usually consists of the following errors
- error message
- Tracing the stack (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
It is recommended to use the second method. The third and fourth browsers cannot generate traceback stacks for the above two methods.
If the error information in each line of the traceback stack can be parsed, SourceMap
can't to locate the specific source code of each line?
The problem is that different browsers do not have a universal standard format for the above information. The difficulty lies in solving the compatibility problem.
For example, window.onerror
the fifth parameter error object was added to WHATWG
the specification in 2013.
In the early days, Safari and IE10 did not have it. Firefox added the Error object from version 14, and Chrome only added it in 2013.
recommended practice
-
window.onerror
It is the best way to catch JS errors, and only report when there is a legal Error object and traceback stack.
It can also avoid some uninterruptible errors, such as plug-in errors and cross-domain errors with incomplete information. -
try catch
Enhanced, the error information thrown is more complete, which can make up for the shortcomingswindow.onerror
of . But like said earlier,
try catch
asynchronous errors andpromise
bugs cannot be caught, andV8
engine .
For example, Tencent's BadJS has packaged the following try catch
recommendations
- setTimeout and setInterval
- event binding
- ajax callback
- define 和 require
- Business main entrance
Whether it is necessary to achieve such a fine-grained package depends on the situation.
SourceMap
For example, there is the following error traceback stack (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
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
}
]
With the ranks and corresponding SourceMap
files, the source code information can be obtained by parsing.
Analysis result
The processing code is as follows:
import {
SourceMapConsumer } from 'source-map';
// 必须初始化
SourceMapConsumer.initialize({
'lib/mappings.wasm': 'https://unpkg.com/[email protected]/lib/mappings.wasm',
});
/**
* 根据sourceMap文件解析源代码
* @param {String} rawSourceMap sourceMap文件
* @param {Number} line 压缩代码报错行
* @param {Number} column 压缩代码报错列
* @param {Number} offset 设置返回临近行数
* @returns {Promise<{context: string, originLine: number | null, source: string | null}>}
* context:源码错误行和上下附近的 offset 行,originLine:源码报错行,source:源码文件名
*/
export const sourceMapDeal = async (rawSourceMap, line, column, offset) => {
// 通过sourceMap库转换为sourceMapConsumer对象
const consumer = await new SourceMapConsumer(rawSourceMap);
// 传入要查找的行列数,查找到压缩前的源文件及行列数
const sm = consumer.originalPositionFor({
line, // 压缩后的行数
column, // 压缩后的列数
});
// 压缩前的所有源文件列表
const {
sources } = consumer;
// 根据查到的source,到源文件列表中查找索引位置
const smIndex = sources.indexOf(sm.source);
// 到源码列表中查到源代码
const smContent = consumer.sourcesContent[smIndex];
// 将源代码串按"行结束标记"拆分为数组形式
const rawLines = smContent.split(/\r?\n/g);
let begin = sm.line - offset;
const end = sm.line + offset;
begin = begin <= 0 ? 1 : begin;
// 输出源码行,因为数组索引从0开始,故行数需要-1
const context = rawLines.slice(begin - 1, end).join('\n');
// 记得销毁
consumer.destroy();
return {
context,
originLine: sm.line,
source: sm.source,
}
};
According to the format of SourceMap
the file can understand this code well.
At present, the monitoring system is being developed a little bit. If it is easy to use, it will be open sourced. . .