从自测到QA测试以及上线前的Code Review ,进过层层检查,还是会有疏忽。代码上线后,我们更多的是通过用户反馈或者后台的数据统计、异常数据来分析,找问题、定位问题。那么前端是否也能弄用一套比较完善的代码异步监控?
例子错误:
src = img.getAttribute("src");
src.indexOf("http://XXX.com/");
复制代码
在某些情况下,上诉的src可能是null,导致这里调用null的indexOf方法发生异常。导致代码不往下走了。
正常情况下,前端页面的语法错误以及运行时错误,都会在浏览器的console里边体现出来,包含错误的文件、行号、堆栈信息等。 那么有什么方法可以抓到错误日志?有两个方案:
- try...catch(e) 针对某个代码块使用try...catch(e) 包装,这样错误就能被捕获到。缺点:
- 捕获不了异步错误(所以很多node,走的是回调的形式)
- 捕获不了全局错误事件
// demo2 可以捕获
try{
var i = 0;
console.log(I)
var c = b + cc
}catch(e){
console.log(e)
}
// demo3 无法捕获
try{
setTimeout(()=>{
var i = 0;
console.log(I)
var c = b + cc
}, 0)
}catch(e){
console.log(e)
}
复制代码
- window.onerror
window.onerror 可以拿到出错信息的文件名、行号、列号,还可以在window.onerror最后return true让浏览器不输出错误信息到控制台。 注意:
- window.onerror 代码块需要独立出去,避免与业务代码块、语法错误的代码块一起,避免事件不生效。
- 对于跨域的JS 资源,window.onerror 也无法监听到。
- js 处加上 crossorigin
- 加上 Access-Control-Allow-Origin
因此通常路径是监听全局的window.onerror 事件捕获到运行时的错误,然后上报到采集端,最后再做一个页面展示数据。
利用new Image 的方式采集日志
window.onerror = function (msg, url, line, col, error) {
// 最后实时过滤出日志
(new Image).src = `/data?msg=${msg}&url=${url}&line=${line}&col=${col}&error=${error}`
console.log(msg)
console.log(url)
console.log(line)
console.log(col)
console.log(error)
// return true 实现错误日志不输出到console 处
return true
}
复制代码
最终形式:
window.onerror = function(msg,url,line,col,error){
//没有URL不上报!上报也不知道错误
if (msg != "Script error." && !url){
return true;
}
//采用异步的方式
//我遇到过在window.onunload进行ajax的堵塞上报
//由于客户端强制关闭webview导致这次堵塞上报有Network Error
//我猜测这里window.onerror的执行流在关闭前是必然执行的
//而离开文章之后的上报对于业务来说是可丢失的
//所以我把这里的执行流放到异步事件去执行
//脚本的异常数降低了10倍
setTimeout(function(){
var data = {};
//不一定所有浏览器都支持col参数
col = col || (window.event && window.event.errorCharacter) || 0;
data.url = url;
data.line = line;
data.col = col;
if (!!error && !!error.stack){
//如果浏览器有堆栈信息
//直接使用
data.msg = error.stack.toString();
}else if (!!arguments.callee){
//尝试通过callee拿堆栈信息
var ext = [];
var f = arguments.callee.caller, c = 3;
//这里只拿三层堆栈信息
while (f && (--c>0)) {
ext.push(f.toString());
if (f === f.caller) {
break;//如果有环
}
f = f.caller;
}
ext = ext.join(",");
data.msg = ext;
}
//把data上报到后台!
},0);
return true;
};
复制代码
前端日志存储,通常不会去实时上传日志,而是先存储,比如: Cookie, localStorage, sessionStorage, IndexDB, webSQL, FileSystem 几种毕竟方式
存储方式 | cookie | localStorage | sessionStorage | IndexDB | webSQL |
---|---|---|---|---|---|
容量 | 4K | 5M | 5M | 500M | 60M |
进程 | 同步 | 同步 | 同步 | 异步 | 异步 |
检索 | key | key | key | key, index | field |
IndexedDB是最好的选择,它具有容量大、异步的优势,异步的特性保证它不会对界面的渲染产生阻塞。而且IndexedDB是分库的,每个库又分store,还能按照索引进行查询,具有完整的数据库管理思维,比localStorage更适合做结构化数据管理。但是它有一个缺点,就是api非常复杂,不像localStorage那么简单直接。针对这一点,我们可以使用hello-indexeddb这个工具,它用Promise对复杂api进行来封装,简化操作,使IndexedDB的使用也能做到localStorage一样便捷
框架层异常处理
vue 中的Vue.config.errorHandler
, React 有ErrorBoundary 。
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 <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
// 在使用时,用 ErrorBoundary 包裹你的业务组件即可:
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
复制代码
window.performance 有浏览器相关时间的api
监听hash change 变化上报pv
第三方前端监控平台
阿里的前端监控平台,是按量收费的
3-5年内部岗位(平安、乐信、500万、vivo、oppo)推荐机会,欢迎发简历到: zgxie@126.com