前端异常监控(source-map代码反解)

964 阅读1分钟

背景

前端代码都是打包上线的,一旦出现错误很难判断具体发生在什么位置。但是source-map可以很好的帮助我们做代码反解。

实现思路

window.onerror = function (msg, url, row, col, error) {}

SDK监控到出现错误的地方进行上报。上报字段至少包含msg、url、row、col。 前端检测到异常告警时可以查到哪个资源的报错。当知道报错的资源以及row和col时,去服务端查询源码map文件,返回给客户端。

const sourceMap = require('source-map');
function lookupSourceMap(mapFile, line, column, callback) {
    // 首先得知道map源文件,读文件
    // 出错行  出错列  回调

    fs.readFile(mapFile, function(err, data) {
        if(err) {
            console.error(err);
            return;
        }
        // 源文件fileContent
        let fileContent = data.toString(), 
            fileObj = JSON.parse(fileContent),
            sources = fileObj.sources;

        sources.map(item => {
            sourcesPathMap[fixPath(item)] = item;
        });

        // console.log(sourcesPathMap, 'sourcesPathMap处理后');

        const consumer = new sourceMap.SourceMapConsumer(fileContent);
        console.log(consumer, 'consumer');
        const lookup = {
            line: parseInt(line),
            column: parseInt(column)
        };
        console.log(lookup, 'lookup');
        const result = consumer.originalPositionFor(lookup);
        console.log(result, '查找result');
        let originSource = sourcesPathMap[result.source],
            sourcesContent = fileObj.sourcesContent[sources.indexOf(originSource)];

        result.sourcesContent = sourcesContent;
        // console.log(result, 'result');
        callback && callback(result);
    });
}

前端拿到解析到的源码文件进行处理。

const $errCode = document.getElementById('errCode');
            $errCode.innerHTML = data.file;
            const lines = $errCode.innerText.split('\n');
            
            const row = data.row,
                len = lines.length - 1;

            const start = row - 3 >= 0? row - 3: 0,
                end = start + 5 >= len? len: start + 5; // 最多展示6行

            const newLines = [];
            for(let i = start; i <= end; i++) {
                newLines.push('<div class="code-line '+ (i + 1 == row? 'heightlight': '')+'" title="'+ (i + 1 === row ? encodeHTML(data.msg): '')+'">' + (i+1) + '.    ' + encodeHTML(lines[i]) + '</div>');
            }
            target.innerHTML += '<div class="errdetail"><div class="errheader">' + data.source + ' at line ' + data.row + ':' + data.column + '</div>' + 
                '<pre class="errCode">' + newLines.join("") + '</pre></div>';

image.png