Analysis of error monitoring principle

error monitoring

original address

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:

  1. Common JS Error Types
  2. Common JS error handling methods
  3. The way of reporting and some thoughts on the content of reporting

question:

  1. 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?
  2. How to report useful error information 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 files and handle error messages in today's engineering where there is no need to compress and uglify code?
  4. How can something go wrong without asking users to help you reproduce it? Want a model? Need steps?
  5. 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

  1. script error
    • Grammatical errors
    • runtime error
      • sync error
      • async error
      • Promise error
  2. 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.

syntaxError

Syntax errors cannot be try catchhandled

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 catchcaptured to ensure subsequent Synchronous code is executed.

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

async error

setTimeoutCommon methods such as will create a new event task and insert it into the event stack for subsequent execution.
So try catchthere 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.onerrorevents to monitor the occurrence of errors.
It try catchis 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 的时候,异常不会向上抛出,控制台不会输出错误
};

windowOnerror

window.onerror Notes

  1. window.onerrorCan catch common syntax, synchronous, asynchronous errors and other errors;
  2. window.onerrorUnable to catch Promiseerrors , network errors;
  3. window.onerrorShould be executed before all JS scripts to avoid missing;
  4. window.onerrorIt 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 CDNresources are down, it is extremely important to get feedback in time.

window.addEventListener('error', (error) => {
    
    
  console.log('404 错误');
  console.log(error);
  // return true; // 中断事件传播
}, true);

addEventListener

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.onerrorandwindow.addEventListener

window.addEventListenerThe 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.onerrorsuch . Generally only used window.addEventListenerto monitor resource loading errors.

  • For network request custom errors, it is best to report them manually.

Promise error

If you haven't unlocked it promisewhen catch, then onerrorthere 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.onunhandledrejectionor window.addEventListener("unhandledrejection")to monitor errors.
Receiving a PromiseError object, you can parse reasonthe property , which is somewhat similar stack.

The specific compatibility processing can be seen in TraceKit.js.

Reporting method

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

ajaxIt's just the class library that should be used, much the same.

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

Script error

Script errorIf 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 CDNof .
  • HTTP response headerSet in the script file CORS.
  1. Access-Control-Allow-Origin: You-allow-origin
  2. Add crossoriginthe attribute , for example<script src="http://www.xxx.com/index.js" crossorigin></script>

Response header and crossoriginvalue problem

  1. crossorigin="anonymous"(default), CORSnot equal to You-allow-origin, cannot takecookie
  2. crossorigin="use-credentials"And Access-Control-Allow-Credentials: true, CORSit cannot be set as *, energy band cookie.
    If CORSnot equal You-allow-origin, the browser does not load js.

corsWhen you have done a good job with the resources that you can control freely , Script erroryou 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 JavaScripterror usually consists of the following errors

  • error message
  • Tracing the stack (stack trace)

error

consoleError

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, SourceMapcan'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.onerrorthe fifth parameter error object was added to WHATWGthe 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

  1. window.onerrorIt 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.

  2. try catchEnhanced, the error information thrown is more complete, which can make up for the shortcomings window.onerrorof . But like said earlier,
    try catchasynchronous errors and promisebugs cannot be caught, and V8engine .

For example, Tencent's BadJS has packaged the following try catchrecommendations

  • 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 SourceMapfiles, the source code information can be obtained by parsing.

sourceMapDel

Analysis result

sourceMapDel

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 SourceMapthe 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. . .

reference site

  1. mozilla/source-map
  2. Front-end code exception monitoring in practice
  3. Front-end exception monitoring - BadJS
  4. The amount of script errors is extremely optimized-let script errors be clear at a glance

Guess you like

Origin blog.csdn.net/guduyibeizi/article/details/103745888