前端异常监控汇总
一.为什么需要做前端监控
通过对客户端环境信息以及用户行为信息收集,
在研发侧,能够对系统的运行情况做异常报警,快速定位异常问题,确保线上服务能够正常运行;
在用户端,能够通过对用户行为进行分析,结合定制的指标,去衡量产品功能的使用率、服务性能、用户行为偏好等,为用户提供更好的产品体验;
在产品侧,能够根据用户行为偏好,驱动产品迭代优化; 在运营侧,能够确认运营活动以及广告投放的效益
二.异常错误原因分类
错误类型
1.ECMAScript 2015中定义的错误构造函数
EvalError eval错误
RangeError 范围错误 当一个值不在其所允许的范围或者集合
ReferenceError 引用错误
TypeError 类型错误
URIError URI错误
SyntaxError 语法错误(这个错误webIDL中故意省略,保留给ES解析器使用)
Error 通用错误(这个错误WebIDL中故意省略,保留给开发者使用使用)
2.最新的DOM规范定义的错误类型集
IndexSizeError 索引不在允许范围内
HierarchyRequestError 节点树层次结构是不正确的
WrongDocumentError 对象是错误的
InvalidCharacterError 字符串包含无效字符
NoModificationAllowedError 对象不能被修改
NotFoundError 对象不能再这里被找到
NotSupportedError 不支持的操作
InvalidStateError 对象是一个无效的状态
SyntaxError 字符串不匹配预期模式
InvalidModificationError 对象不能以这种方式被修改
NamespaceError 操作在xml命名空间内是不被允许的
InvalidAccessError 对象不支持这种操作或参数
TypeMismatchError 对象的类型不匹配预期的类型
SecurityError 此操作是不安全的
NetworkError 发生网络错误
AbortError 操作被终止
URLMismatchError 给定的URL不匹配另一个URL
QuotaExceededError 已经超过给定配额
TimeoutError 操作超时
InvalidNodeTypeError 这个操作的 节点或节点祖先 是不正确的
DataCloneError 对象不能克隆
三.异常监控方式
== try…catch ==
在业务代码中对单个代码块进行包裹,或在逻辑流程中打点,实现有针对性的异常捕获:
专门写一个函数来收集异常信息,在异常发生时,调用该函数
处理异常的能力有限,只能捕获捉到运行时非异步错误,对于语法错误和异步错误就显得无能为力,捕捉不到
try {
error // 未定义变量
} catch(e) {
console.log('我知道错误了');
console.log(e);
}
== window.onerror ==
/**
- 捕获javascript异常
- @param {String} message 错误信息
- @param {String} source 出错文件
- @param {Number} lineno 行号
- @param {Number} colno 列号
- @param {Object} error Error对象(对象)
*/
window.onerror = function (msg, url, row, col, error) {
console.log('我知道错误了');
console.log({
msg, url, row, col, error
})
return true;
};
error;
// 异步错误
window.onerror = function (msg, url, row, col, error) {
console.log('我知道异步错误了');
console.log({
msg, url, row, col, error
})
return true;
};
setTimeout(() => {
error;
});
捕获异常能力比 try-catch 稍微强点,无论是异步还是非异步错误,onerror 都能捕获到运行时错误
one rror 函数只有在返回 true 的时候,异常才不会向上抛出,否则即使是知道异常的发生控制台还是会显示 Uncaught Error: xxxxx
对于 one rror 这种全局捕获,最好写在所有 JS 脚本的前面,因为你无法保证你写的代码是否出错,如果写在后面,一旦发生错误的话是不会被 one rror 捕获到的
对于语法错误无能为力 无法捕获到网络异常的错误
捕获资源加载异常
window.addEventListener('error', function (e) {
console.log(e)
const err = e.target.src || e.target.href
if (err) {
console.log('捕获到资源加载异常', err)
}
}, true)
<img src="./404.png" alt="">
参数为一个event对象
img
通过addEventListener(‘error’)监控静态资源加载,由于网络请求异常不会事件冒泡,因此必须在捕获阶段将其捕捉到才行
无法判断错误状态码,所以还需要配合服务端日志才能进行排查分析
vue项目全局异常捕获(2.2.0+)
Vue.config.errorHandler = function (err, vm, info) {
// `info` 是 Vue 特定的错误信息,比如错误所在的生命周期钩子
let msg = `错误发生在:${info}中,具体信息:${err.stack}`
console.log(msg)
}
react项目异常捕获(16.x)
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
this.setState({ hasError: true });
// 将异常信息上报给服务器
logErrorToMyService(error, info);
}
render() {
if (this.state.hasError) {
return '出错了';
}
return this.props.children;
}
}
然后:
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
捕获接口异常
框架级别的全局监听,例如aixos中使用interceptor进行拦截,vue、react都有自己的错误采集接口
// Promise 错误监听 可以在请求时抛出
new Promise((resolve, reject) => {
reject('error') // 报错
})
/**
* 监听promise未处理的reject错误, 跨域情况下监控不到
*/
window.addEventListener('unhandledrejection', event => {
console.log('捕获到未处理的promise异常',event.reason)
return true;
})
四.用户监控方式
获取用户行为以及跟踪产品在用户端的使用情况,并以监控数据为基础,指明产品优化的方向
用户行为分析包含页面点击量、用户点击流、用户访问路径、用户点击热力图、用户转化率、导流转化率、用户访问时长分析和用户访问内容分析等
上报的主要数据包含:appid、userAgent、timestamp(上报的时间戳)、currentUrl(用户当前的 url)、fromUrl(前一个页面的 url)、type(上报事件的类型)、element(触发上报事件的元素)、data(自定义数据)
手动埋点(代码埋点)
纯手动写代码,调用埋点SDK的函数,在需要埋点的业务逻辑功能位置调用接口上报埋点数据,友盟、百度统计、Google Analytics、腾讯分析等第三方数据统计服务
作用:为网站产品策划提供参考支持:流量分析、用户分析、提升转化率、改进页面布局
命令式
$(document).ready(()=>{
// ... 这里是你的业务逻辑代码
sendData(params); //这里是发送你的埋点数据,params是你封装的埋点数据
});
// 按钮点击时发送埋点请求
$('button').click(()=>{
// ... 业务逻辑
sendData(params); //同上
});
声明式
这里声明了自定义属性data-gtag,你可以在你的SDK中去扫描和识别这些自定义属性,并解析封装数据,在SDK中按照自定义规则去绑定事件并发送埋点数据
<button data-gtag="{key:'uber_comt_share_ck', act: 'click',msg:{}}">操作</button>
// 引入jsSDK
<script async src='https://www.google-analytics.com/analytics.js'></script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'UA-122876026-1');
css埋点
.link:active::after{
content: url("http://www.example.com?action=yourdata");
}
<a class="link">点击我,会发埋点数据</a>
框架方式
使用Vue或者React等前端框架,这些框架都有自己的各种生命周期,为了减少重复性的手动埋点次数,可以在各个生命周期位置,根据你的需求封装你所需的埋点。
react的react-tag-component
核心: 1.数据仓库:自定义埋点数据格式。2.埋点事件:定义数据仓库中数据的读取,打埋点事件的注入。3.埋点组件:嵌入Tag事件,用于挂载相应数据。
示例:
vue 利用导航守卫实现对不同页面的监控
router.beforeEach((to, from, next) => {
// to: Route: 即将要进入的目标 路由对象
// from: Route: 当前导航正要离开的路由
// next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)
})
beforeRouteEnter (to, from, next) {
},
beforeRouteUpdate (to, from, next) {
},
beforeRouteLeave (to, from, next) {
}
性能监控方式
window.performance 提供了在加载和使用当前页面期间发生的各种事件的性能计时信息(毫秒)
window.logInfo = {}; //统计页面加载时间
window.logInfo.openTime = performance.timing.navigationStart;
window.logInfo.whiteScreenTime = + new Date() - window.logInfo.openTime;
document.addEventListener('DOMContentLoaded', function (event) {
window.logInfo.readyTime = +new Date() - window.logInfo.openTime;
});
// 网页加载完毕执行
window.onload = function () {
window.logInfo.allloadTime = +new Date() - window.logInfo.openTime;
window.logInfo.nowTime = new Date().getTime();
var timname = {
whiteScreenTime: '白屏时间',
readyTime: '用户可操作时间',
allloadTime: '总下载时间',
mobile: '使用设备',
nowTime: '时间',
};
var logStr = '';
for (var i in timname) {
console.warn(timname[i] + ':' + window.logInfo[i] + 'ms');
if (i === 'mobile') {
logStr += '&' + i + '=' + window.logInfo[i];
} else {
logStr += '&' + i + '=' + window.logInfo[i];
}
}
//
(new Image()).src = '/action?' + logStr;
};
五.前端存储方式
用的持久化方案可选项也比较多了,主要有:Cookie、localStorage、sessionStorage、IndexedDB、webSQL 、FileSystem 等等
IndexedDB 是最好的选择,它具有容量大、异步的优势,异步的特性保证它不会对界面的渲染产生阻塞