前端异常监控
- 静态资源加载异常
静态资源加载失败,可以通过window的error事件进行捕获。核心代码
// 全局监控资源加载错误
window.addEventListener(
'error',
(event) => {
// 过滤 js error
const target = event.target || event.srcElement;
const isElementTarget =
target instanceof HTMLScriptElement ||
target instanceof HTMLLinkElement ||
target instanceof HTMLImageElement;
if (!isElementTarget) {
return false;
}
// 上报资源地址
const url =
(target as HTMLScriptElement | HTMLImageElement).src ||
(target as HTMLLinkElement).href;
this.log({
error: new Error(`ResourceLoadError: ${url}`),
type: 'resource load'
});
},
true
);
- 接口异常(后端和 native 的接口)
可以通过在封装的 http 模块中,全局集成上报错误函数(native 接口的错误上报类似,可在项目中查看)。核心代码如下:
function errorReport(
url: string,
error: string | Error,
requestOptions: AxiosRequestConfig,
response?: AnyObject
) {
if (window.$sentry) {
const errorInfo: RequestErrorInfo = {
error: typeof error === 'string' ? new Error(error) : error,
type: 'request',
requestUrl: url,
requestOptions: JSON.stringify(requestOptions)
};
if (response) {
errorInfo.response = JSON.stringify(response);
}
window.$sentry.log(errorInfo);
}
}
- js 报错
关于全局 js 报错,sentry 针对的前端的 sdk 已经通过 window.onerror 和 window.addEventListener('unhandledrejection', ..., false) 进行全局监听并上报。
需要注意的是其中 window.onerror = (message, source, lineno, colno, error) =>{} 不同于 window.addEventListener('error', ...),window.onerror 捕获的信息更丰富,包括了错误字符串信息、发生错误的 js 文件,错误所在的行数、列数、和 Error 对象(其中还会有调用堆栈信息等)。所以 sentry 会选择 window.onerror 进行 js 全局监控。
但有一种错误是 window.onerror 监听不到的,那就是 unhandledrejection 错误,这个错误是当 promise reject 后没有 catch 住所引起的。当然 sentry 的 sdk 也已经做了监听。
针对 vue 项目,也可对 errorHandler 钩子进行全局监听,react 的话可以通过 componentDidCatch 钩子,vue 相关代码如下:
// 全局监控 Vue errorHandler
Vue.config.errorHandler = (error, vm, info) => {
window.$sentry.log({
error,
type: 'vue errorHandler',
vm,
info
});
};
- 网页崩溃
为部署到线上的代码一般都是经过压缩混淆的,如果没有上传 sourcemap 的话,是无法定位到具体源码的,可以现在 项目中添加 .sentryclirc 文件,其中内容可参考本项目的 .sentryclirc,然后通过 sentry-cli (需要全局全装 sentry-cli 即npm install sentry-cli)命令行工具进行上传,命令如下:
sentry-cli releases -o 机构名 -p 项目名 files 版本 upload-sourcemaps sourcemap 文件相对位置 --url-prefix js 在线上相对根目录的位置 --rewrite
// 示例
sentry-cli releases -o mcukingdom -p hello-world files 0.2.1 upload-sourcemaps dist/js --url-prefix '~/js/' --rewrite
官方也提供了 webpack 插件 sentry-webpack-plugin,当打包时触发 webpack 的 after-emit 事件钩子(即生成资源到 output 目录之后),插件会自动上传打包目录中的 sourcemap 和关联的 js,vue.config.js相关配置:
const path = require('path');
const webpack = require('webpack');
const SentryPlugin = require('@sentry/webpack-plugin');
const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');
const IS_PRO = NODE_ENV === 'production';
const commonPlugins = [
new webpack.DefinePlugin({
__VERSION__: JSON.stringify(version)
})
];
module.exports = {
...
configureWebpack: () => {
if (IS_PRO) {
const productionPlugins = [new LodashModuleReplacementPlugin()];
productionPlugins.push(
new SentryPlugin({
release: version, //发布的版本
include: path.join(__dirname, './dist/js'), //需要上传到sentry服务器的资源目录,会自动匹配 js 以及 map 文件
urlPrefix: '~/mobile-web-best-practice/js', //线上对应的 url 资源的相对路径
ignore: ['node_modules'] //忽略文件目录, 当然我们在 inlcude 中制定了文件路径,这个忽略目录可以不加
})
);
return {
plugins: [...commonPlugins, ...productionPlugins],
externals: {
// key 是给 import 的时候用的,value 表示的是如何在 global 中访问到该对象
vue: 'Vue',
vuex: 'Vuex',
'vue-router': 'VueRouter'
}
};
} else {
return {
plugins: [...commonPlugins]
};
}
}
...
}
通常为了安全,是不允许在线上部署 sourcemap 文件的,所以上传 sourcemap 到 sentry 后,可手动删除线上 sourcemap 文件。