前端异常监控该如何做?

454

前端异常捕获的几种方式

1 使用window.onError

全局的onError函数,可以搜集页面上的错误信息,但是有的浏览器不支持该方法

window.onerror = function(message, source, lineno, colno, error) { ... }

message: '异常基本信息'
source:  '发生异常Javascript文件url'
lineno:  '发生错误的行号'
colno:   '异常的列号'
error:   'Error对象'

2 try catch

try catch方法需要所有函数都加上try catch,该方法虽然能一步到位的捕获异常,但使用了try/catch影响性能。

可借助Uglify提供的操作AST(抽象语法树)的API,对源文件进行预处理,对每个函数自动的添加try catch

以下是网上的一段使用try catch处理异常的代码

var fs = require('fs');
var _ = require('lodash');
var UglifyJS = require('uglify-js');

var isASTFunctionNode = function (node) {
    return node instanceof UglifyJS.AST_Defun || node instanceof UglifyJS.AST_Function;
}

var globalFuncTryCatch = function (inputCode, errorHandler) {
    if(!_.isFunction(errorHandler)){
        throw 'errorHandler should be a valid function';
    }
    var errorHandlerSource = errorHandler.toString();
    var errorHandlerAST = UglifyJS.parse('(' + errorHandlerSource + ')(error);');
    var tryCatchAST = UglifyJS.parse('try{}catch(error){}');
    var inputAST = UglifyJS.parse(inputCode);
    var topFuncScope = [];

    //将错误处理函数包裹进入catch中
    tryCatchAST.body[0].bcatch.body[0] = errorHandlerAST;

    //搜集所有函数
    var walker = new UglifyJS.TreeWalker(function (node) {
        if (isASTFunctionNode(node)) {
            topFuncScope.push(node);
        }
    });
    inputAST.walk(walker);

    //对函数进行变换, 添加try catch语句
    var transfer = new UglifyJS.TreeTransformer(null,
        function (node) {
            if (isASTFunctionNode(node) && _.includes(topFuncScope, node)) {
                //函数内部代码搜集
                var stream = UglifyJS.OutputStream();
                for (var i = 0; i < node.body.length; i++) {
                    node.body[i].print(stream)
                }
                var innerFuncCode = stream.toString();

                //清除try catch中定义的多余语句
                tryCatchAST.body[0].body.splice(0, tryCatchAST.body[0].body.length);

                //用try catch包裹函数代码
                var innerTyrCatchNode = UglifyJS.parse(innerFuncCode, {toplevel: tryCatchAST.body[0]});

                //获取函数壳
                node.body.splice(0, node.body.length);

                //生成有try catch的函数
                return UglifyJS.parse(innerTyrCatchNode.print_to_string(), {toplevel: node});
            }
        });
    inputAST.transform(transfer);
    var outputCode = inputAST.print_to_string({beautify: true});
    return outputCode;
}

module.exports.globalFuncTryCatch = globalFuncTryCatch;

3 window.onunhandledrejection

Promise 的 reject 但是没有被 catch 捕捉时触发,可获取异常信息。

window.onunhandledrejection = event => { 
    console.log(event.colreason);
}

4 window.rejectionhandled

Promise 延迟调用 catch 方法时触发,可获取异常信息。

window.onrejectionhandled = event => {
    console.log(event.reason);
}

5 window.addEventListener

监听静态资源加载错误

window.addEventListener('error', function(event) {
    var errorTarget = event.target
    return errorTarget
}, true)

6 接口监听

监听页面所有AJAX请求

window.XMLHttpRequest = function () {

    let XML = new XMLHttpRequest

    XML.addEventListener("readystatechange", function (event) {
        console.log('请求状态码改变')
    })
    XML.addEventListener("error", function (event) {
        console.log('请求出错')
    })
    XML.addEventListener("timeout", function (event) {
        console.log('请求超时')
    })
    XML.addEventListener("loadstart", function (event) {
        console.log('请求开始')
    })
    XML.addEventListener("loadend", function (event) {
        console.log('请求结束')
    })
    XML.addEventListener("progress", function (event) {
        console.log('请求进度')
    })
    XML.addEventListener("load", function (event) {
        console.log('请求加载中')
    })
    XML.addEventListener("abort", function (event) {
        console.log('请求中止')
    })
    return XML
   
};

7 注意事项

1.错误异常处理脚本必须要放到所有代码的最前边。
2. Script error .  Access-Control-Allow-Origin
3. 编译过的代码需要sourcemap

neky-err

自己写的一个异常监控模块,在自己的项目上用了一下还可以,基本的需求都能够满足。

github 地址:

github.com/suguangwen/…

现在能做的事情:

JS异常监控
资源异常监控
XHR异常监控
轻度可配置化