现成的SDK
- Sentry
- Fun Debug
需要监控什么?
-
错误统计
记录我们代码发布到线上各种奇奇怪怪的错误 -
行为日志埋点
记录用户行为,比如:分析用户浏览时间比较长的页面有哪些,常常点击的有哪些,可以做 相应的推荐 -
PV/UV统计
记录用户访问页面的次数
PV:访问的操作的次数,UV:访问页面的用户多少
前端监控的主要流程:
功能拆分
错误监控
错误监控,即当代码发生错误时,比如,同步错误,异步错误,promise错误,资源加载错误时,我们需要捕获到错误,然后上报给后端。
上报到后端简单,发送请求即可。那如何捕获到错误,我们下面进行讨论:
错误类型
-
语法错误
语法错误一般在可发阶段就可以发现,比如常见的单词拼写错误,中英文符号错误等。注意:语法错误是无法被try catch捕获的,因为在开发阶段就能发现,所以一般不会发布到线上环境。
try {let name = 'heima; // 少一个单引号console.log(name); } catch (error) {console.log('----捕获到了语法错误-----'); }
-
同步错误
同步错误指的是在js同步执行过程中的错误,比如变量未定义,是可以被try catch给捕获到的
try {const name = 'heima';console.log(nam); } catch (error) {console.log('------同步错误-------') }
-
异步错误
异步错误指的是在setTimeout等函数中发生的错误,是无法被try catch捕获到的
try {setTimeout(() => {undefined.map();}, 0); } catch (error) {console.log('-----异步错误-----') }
异步错误的话我们可以用window.onerror来进行处理,这个方法比try catch要强大很多
/*** @param {String} msg 错误描述* @param {String} url 报错文件* @param {Number} row 行号* @param {Number} col 列号* @param {Object} error 错误Error对象*/window.onerror = function (msg, url, row, col, error) {console.log('出错了!!!');console.log(msg);console.log(url);console.log(row);console.log(col);console.log(error); };
-
promise错误
在
promise
中使用catch
可以捕获到异步的错误,但是如果没有写catch
去捕获错误的话window.onerror
也捕获不到的,所以写promise
的时候最好要写上catch
,或者可以在全局加上unhandledrejection
的监听,用来监听没有被捕获的promise错误。window.addEventListener("unhandledrejection", function(error){console.log('捕获到异常:', error); }, true);
-
资源加载错误
资源加载错误指的是比如一些资源文件获取失败,可能是服务器挂掉了等原因造成的,出现这种情况就比较严重了,所以需要能够及时的处理,网路错误一般用
window.addEventListener
来捕获。window.addEventListener('error', (error) => {console.log(error); }, true);
用户埋点统计
埋点就是需要记录用户的某些行为,比如点击按钮,我们需要记录用户点击了哪个按钮,然后进行上报。这样做的作用是,我们得到的数据可以进行一些分析。PS:淘宝首页分类,需要知道哪个分类点击次数最多,即哪个分类最火。
手动埋点
手动埋点就是手动的去出发上报函数
// 方式1
<buttononClick={() => {// 业务代码tracker('click', '用户去支付');// tracker('visit', '访问新页面');// tracker('submit', '提交表单');}}
>手动埋点</button>
// 方式2
<button data-target="支付按钮"onClick={() => {// 业务代码}}
>手动上报</button>
- 优点:可控性强,可以自定义上报具体的数据。
- 缺点:对业务代码侵入性强,如果有很多地方需要埋点就得一个一个手动的去添加埋点代码。
无痕埋点
无痕埋点是为了解决手动埋点的缺点,实现一种不用侵入业务代码就能在应用中添加埋点监控的埋点方式。
<button onClick={() => {// 业务代码
}}>自动埋点</button>
// 自动埋点实现
function autoTracker () {// 添加全局click监听document.body.addEventListener('click', function (e) {const clickedDom = e.target;// 获取data-target属性值let target = clickedDom?.getAttribute('data-target');if (target) {// 如果设置data-target属性就上报对应的值--手动埋点tracker('click', target);} else {// 如果没有设置data-target属性就上报被点击元素的html路径const path = getPathTo(clickedDom);tracker('click', path);}}, false);
};
- 优点:不用侵入务代码就能实现全局的埋点。
- 缺点:只能上报基本的行为交互信息,无法上报自定义的数据;上报次数多,服务器性能压力大。
PV统计
- history路由
history路由是由
window.histroy
api来实现的:
- istory.back(); // 返回上一页,和浏览器回退功能一样
- history.forward(); // 前进一页,和浏览器前进功能一样
- history.go(); // 跳转到历史记录中的某一页,
- history.pushState(); // 添加新的历史记录
- history.replaceState(); // 修改当前的记录项
/*** 重写pushState和replaceState方法* @param {*} name * @returns */
const createHistoryEvent = function (name) {// 拿到原来的处理方法const origin = window.history[name];return function(event) {if (name === 'replaceState') {const { current } = event;const pathName = location.pathname;if (current === pathName) {let res = origin.apply(this, arguments);return res;}}let res = origin.apply(this, arguments);let e = new Event(name);e.arguments = arguments;window.dispatchEvent(e);return res;};
};window.history.pushState = createHistoryEvent('pushState');
window.history.replaceState = createHistoryEvent('replaceState');function listener() {const stayTime = getStayTime(); // 停留时间const currentPage = window.location.href; // 页面路径lazyReport('visit', {stayTime,page: beforePage,})beforePage = currentPage;
}// history.go()、history.back()、history.forward() 监听
window.addEventListener('popstate', function () {listener()
});// history.pushState
window.addEventListener('pushState', function () {listener()
});// history.replaceState
window.addEventListener('replaceState', function () {listener()
});
history
路由无法监听到pushState和replaceState
,所以我们冲洗了一个方法,并用windows.dispatch
创建了一个自定义监听事件。
- hash路由
url上hash的改变会出发hashchange
的监听,所以我们只需要在全局加上一个监听函数,在监听函数中实现采集并上报就可以了。但是在react和vue中,对于hash路由的跳转并不是通过hashchange
的监听实现的,而是通过pushState
实现,所以,还需要加上对pushState
的监听才可以。
export function hashPageTrackerReport() {let beforeTime = Date.now(); // 进入页面的时间let beforePage = ''; // 上一个页面// 上报function listener() {const stayTime = getStayTime();const currentPage = window.location.href;lazyReport('visit', {stayTime,page: beforePage,})beforePage = currentPage;}// hash路由监听window.addEventListener('hashchange', function () {listener()});
}
UV统计
在SDK初始化的时候,上报即可
UV统计的是一天内访问该网站的用户数
/*** 初始化配置* @param {*} options */
function init(options) {... // 加载配置report('user', '加载应用'); // uv统计
}
合并上报
对于无痕埋点来说,一次点击就进行一次上报,会对服务器造成很大的压力,所以我们需要合并一下请求
// cache.js
const cache = [];export function getCache() {return cache;
}export function addCache(data) {cache.push(data);
}// lazyReport.js
export function lazyReport(type, params) {// ....const data = getCache();if (delay === 0) { // delay=0相当于不做延迟上报report(data);return;}if (data.length > 10) { // 数据达到10条上报report(data);clearTimeout(timer);return;}clearTimeout(timer);timer = setTimeout(() => { // 合并上报report(data);}, delay);
}