源码定位
概览
通过相关 sourcemap 定位开发时书写的源代码,以帮助排查线上错误。本项目中,在检测到线上错误时,脚本不仅收集了错误信息,同时收集了控制台的 console 输出,这些输出对错误排查也至关重要。
为什么要源码定位
线上的生产环境的代码是经过打包构建工具进行变换之后得到体积更小相对于浏览器更友好但是开发者无法阅读的代码。
在开发时,项目报错浏览器会根据相关 sourcemap(开发时构建工具会自动提供 sourcemap)提示相关错误源码,此时可以很方便的进行 debug。但是如果在线上的生产环境发生错误,并不能及时的去排查线上应用。此时我们可以记录下来报错相关信息然后根据这些信息再去排查。
如何实现
可以通过报错信息以及相关 sourcemap 定位到相关源码,这样在后期排查时更加有依据。但是如果把 sourcemap 放到生产环境那么源码就能轻易的被他人拿到,可以将 sourcemap 文件上传到制定的地方。本项目将支持上传 sourcemap 文件(下一期功能迭代),目前仅支持读取生产环境下同目录下的.map 的 sourcemap 文件。
具体实现是监听到报错时,拿到报错的堆栈信息,堆栈信息会指明相关报错文件。然后获取到这个报错文件对应的.map 文件,再根据报错信息(行、列)以及 sourcemap 反推出源码。这里分享堆栈信息使用了TraceKit
这个库,它可以帮助我们解析报错输出的堆栈字符信息。sourcemap 是通过一定的计算从而得到的,可以使用source-map
这个库反推源码。
// 解析堆栈信息
const parsed = TraceKit.computeStackTrace(msg.error);
const data = {
error_type: msg?.type,
msg: msg?.message,
source: parsed?.stack[0]?.url,
lineno: parsed?.stack[0]?.line,
colno: parsed?.stack[0]?.column,
logs: logs.length > 20 ? logs.slice(logs.length - 20) : logs,
};
sendErrorRecord(data);
// 通过source map获取源码
const processSourceCode = (obj) => {
return new Promise((rs, rj) => {
const url = new URL(obj.source);
const map = url.origin + url.pathname + '.map';
// 获取对应的.map文件 目前仅支持从生产环境获取
axios
.get(map)
.then(async ({ data: sourceData }) => {
if (!sourceData) {
console.log('加载source map文件出错!');
rs(sourceDataTemplate);
return;
}
let consumer = await new sourceMap.SourceMapConsumer(sourceData);
let result = consumer.originalPositionFor({
line: Number(obj.lineno),
column: Number(obj.colno),
});
let code = consumer.sourceContentFor(result.source);
....
})
.catch((e) => {
console.log('没有map文件');
rs(sourceDataTemplate);
});
});
};
收集控制台 console 信息
这里简单重写了 console 对象的 log 方法,在调用 log 方法时,传入 log 得数据同时被记录下来。
const originLog = console.log;
console.log = function (...args) {
let str = null;
try {
str = JSON.stringify(args);
logs.push(
str.length >= 5000
? 'monitor SDK script(not from application): this console too long to be record'
: str
);
} catch (e) {
logs.push(
'monitor SDK script(not from application): can not record this console'
);
}
return originLog.call(this, ...args);
};
后期处理
对于收集到的错误信息,反推的错误源码、控制台信息都储存到数据库。然后统一展示到本平台。