搭建前端监控,如何采集异常数据?
作者:杨成功
简介:专注前端工程与架构产出
来源:SegmentFault 思否社区
大家好,我是杨成功。
前两篇,我们介绍了为什么前端应该有监控系统,以及搭建前端监控的总体步骤,前端监控的 Why 和 What 想必你已经明白了。接下来我们解决 How 如何实现的问题。
如果不了解前端监控,建议先看前两篇:
什么是异常数据?
接口异常
axios
.post('/test')
.then((res) => { console.log(res);
})
.catch((err) => { // err 就是捕获到的错误对象
handleError(err);
});
async () => { try { let res = await axios.post('/test'); console.log(res);
} catch (err) { // err 就是捕获到的错误对象
handleError(err);
}
};当捕获到异常之后,统一交给 handleError 函数处理,这个函数会将接收到的异 常进行处理,并调用 上报接口 将异常数据传到服务器,从而完成采集。
// 全局请求:src/request/axios.jsconst instance = axios.create({ baseURL: 'https://api.test.com'
timeout: 15000, headers: { 'Content-Type': 'application/json',
},
})export default instance
// a 页面:src/page/a.jsximport http from '@/src/request/axios.js';async () => { let res = await http.post('/test'); console.log(res);
};
拦截器中捕获异常
// 响应拦截器instance.interceptors.response.use( (response) => { return response.data;
}, (error) => { // 发生异常会走到这里
if (error.response) { let response = error.response; if (response.status >= 400) { handleError(response);
}
} else { handleError(null);
} return Promise.reject(error);
},
);
前端异常
try { // 任意同步代码} catch (err) { console.log(err);
}
try { Promise.reject(new Error('出错了')).catch((err) => console.log('1:', err));
} catch (err) { console.log('2:', err);
}
1:Error: 出错了
// js 错误捕获window.addEventListener('error', (error) => { // error 就是js的异常});
为啥不用 window.onerror ?
const loadCss = ()=> { let link = document.createElement('link')
link.type = 'text/css'
link.rel = 'stylesheet'
link.href = 'https://baidu.com/15.css'
document.getElementsByTagName('head')[10].append(link)
}render() { return <div>
<img src='./bbb.png'/>
<button onClick={loadCss}>加载样式<button/>
</div>}
// 捕获阶段全局监听window.addEventListene( 'error', (error) => { if (error.target != window) { console.log(error.target.tagName, error.target.src);
} handleError(error);
}, true,
);
// promise 错误捕获window.addEventListener('unhandledrejection', (error) => { // 打印异常原因
console.log(error.reason); handleError(error); // 阻止控制台打印
error.preventDefault();
});
异常处理函数
const handleError = (error: any, type: 1 | 2) { if(type == 1) { // 处理接口异常
} if(type == 2) { // 处理前端异常
}
}
处理前端异常:handleError(error, 1)
处理接口异常:handleError(error, 2)
处理接口异常
code:http 状态码
url:接口请求地址
method:接口请求方法
params:接口请求参数
error:接口报错信息
const handleError = (error: any, type: 1 | 2) { if(type == 1) { // 此时的 error 响应,它的 config 字段中包含请求信息
let { url, method, params, data } = error.config
let err_data = {
url, method, params: { query: params, body: data }, error: error.data?.message || JSON.stringify(error.data),
})
}
}
params: { query: params, body: data }
处理前端异常
ReferenceError:引用错误
RangeError:超出有效范围
TypeError:类型错误
URIError:URI 解析错误
const handleError = (error: any, type: 1 | 2) { if(type == 2) { let err_data = null
// 监测 error 是否是标准类型
if(error instanceof Error) { let { name, message } = error
err_data = { type: name, error: message
}
} else {
err_data = { type: 'other', error: JSON.strigify(error)
}
}
}
}
function test() { console.aaa('ccc');
}test();
const handleError = (error: any) => { if (error instanceof Error) { let { name, message } = error; console.log(name, message); // 打印结果:TypeError console.aaa is not a function
}
};
获取环境数据
app:应用的名称/标识
env:应用环境,一般是开发,测试,生产
version:应用的版本号
user_id:触发异常的用户 ID
user_name:触发异常的用户名
page_route:异常的页面路由
page_title:异常的页面名称
在 Vue 中
import store from '@/store'; // vuex 导出目录let user_info = store.state;let user_id = user_info.id;let user_name = user_info.name;
{ path: '/test', name: 'test', meta: { title: '测试页面'
}, component: () => import('@/views/test/Index.vue')
},这样配置之后,获取当前页面路由和页面名称就简单了:
window.vm = new Vue({...})let route = vm.$routelet page_route = route.pathlet page_title = route.meta.title
最后一步,我们再获取当前环境。当前环境用一个环境变量 VUE_APP_ENV 表示,有三个值:
dev:开发环境
test:测试环境
pro:生产环境
.env.development:VUE_APP_ENV=dev
.env.staging:VUE_APP_ENV=test
.env.production:VUE_APP_ENV=pro
{ env: process.env.VUE_APP_ENV;
}
# 测试环境打包$ num run build --mode staging# 生产环境打包$ num run build --mode production
在 React 中
import { TestStore } from '@/stores'; // mobx 导出目录let { user_info, cur_path, cur_page_title } = TestStore;// 用户信息:user_info// 页面信息:cur_path,cur_page_title
import { useLocation } from 'react-router';import { observer, useLocalObservable } from 'mobx-react';import { TestStore } from '@/stores';export default observer(() => { const { pathname, search } = useLocation(); const test_inst = useLocalObservable(() => TestStore); useEffect(() => {
test_inst.setCurPath(pathname, search);
}, [pathname]);
});
process.env.REACT_APP_ENV = 'dev';
if (argv.length >= 2 && argv[0] == '--mode') { switch (argv[1]) { case 'staging':
process.env.REACT_APP_ENV = 'test'; break; case 'production':
process.env.REACT_APP_ENV = 'pro'; break; default:
}
}
{ env: process.env.REACT_APP_ENV;
}
总结
评论