错误数据采集
资源加载错误
使用 addEventListener() 监听 error 事件,可以捕获到资源加载失败错误。
// 捕获资源加载失败错误 js css img...
window.addEventListener('error', e => {
const target = e.target
if (!target) return
if (target.src || target.href) {
const url = target.src || target.href
lazyReportCache({
url,
type: 'error',
subType: 'resource',
startTime: e.timeStamp,
html: target.outerHTML,
resourceType: target.tagName,
paths: e.path.map(item => item.tagName).filter(Boolean),
pageURL: getPageURL(),
})
}
}, true)
js 错误
使用 window.onerror 可以监听 js 错误。
// 监听 js 错误
window.onerror = (msg, url, line, column, error) => {
lazyReportCache({
msg,
line,
column,
error: error.stack,
subType: 'js',
pageURL: url,
type: 'error',
startTime: performance.now(),
})
}
promise 错误
使用 addEventListener() 监听 unhandledrejection 事件,可以捕获到未处理的 promise 错误。
// 监听 promise 错误 缺点是获取不到列数据
window.addEventListener('unhandledrejection', e => {
lazyReportCache({
reason: e.reason?.stack,
subType: 'promise',
type: 'error',
startTime: e.timeStamp,
pageURL: getPageURL(),
})
})
sourcemap
一般生产环境的代码都是经过压缩的,并且生产环境不会把 sourcemap 文件上传。所以生产环境上的代码报错信息是很难读的。因此,我们可以利用 source-map 来对这些压缩过的代码报错信息进行还原。
当代码报错时,我们可以获取到对应的文件名、行数、列数:
{
line: 1,
column: 17,
file: 'https:/www.xxx.com/bundlejs',
}
然后调用下面的代码进行还原:
async function parse(error) {
const mapObj = JSON.parse(getMapFileContent(error.url))
const consumer = await new sourceMap.SourceMapConsumer(mapObj)
// 将 webpack://source-map-demo/./src/index.js 文件中的 ./ 去掉
const sources = mapObj.sources.map(item => format(item))
// 根据压缩后的报错信息得出未压缩前的报错行列数和源码文件
const originalInfo = consumer.originalPositionFor({ line: error.line, column: error.column })
// sourcesContent 中包含了各个文件的未压缩前的源码,根据文件名找出对应的源码
const originalFileContent = mapObj.sourcesContent[sources.indexOf(originalInfo.source)]
return {
file: originalInfo.source,
content: originalFileContent,
line: originalInfo.line,
column: originalInfo.column,
msg: error.msg,
error: error.error
}
}
function format(item) {
return item.replace(/(./)*/g, '')
}
function getMapFileContent(url) {
return fs.readFileSync(path.resolve(__dirname, `./maps/${url.split('/').pop()}.map`), 'utf-8')
}
每次项目打包时,如果开启了 sourcemap,那么每一个 js 文件都会有一个对应的 map 文件。
bundle.js
bundle.js.map
这时 js 文件放在静态服务器上供用户访问,map 文件存储在服务器,用于还原错误信息。source-map 库可以根据压缩过的代码报错信息还原出未压缩前的代码报错信息。例如压缩后报错位置为 1 行 47 列,还原后真正的位置可能为 4 行 10 列。除了位置信息,还可以获取到源码原文。