前端监控学习笔记

现成的SDK

  • Sentry
  • Fun Debug

需要监控什么?

  • 错误统计
    记录我们代码发布到线上各种奇奇怪怪的错误

  • 行为日志埋点
    记录用户行为,比如:分析用户浏览时间比较长的页面有哪些,常常点击的有哪些,可以做 相应的推荐

  • PV/UV统计
    记录用户访问页面的次数
    PV:访问的操作的次数,UV:访问页面的用户多少

前端监控的主要流程:
在这里插入图片描述

功能拆分

在这里插入图片描述

错误监控

错误监控,即当代码发生错误时,比如,同步错误,异步错误,promise错误,资源加载错误时,我们需要捕获到错误,然后上报给后端。

上报到后端简单,发送请求即可。那如何捕获到错误,我们下面进行讨论:

错误类型

  1. 语法错误

    语法错误一般在可发阶段就可以发现,比如常见的单词拼写错误,中英文符号错误等。注意:语法错误是无法被try catch捕获的,因为在开发阶段就能发现,所以一般不会发布到线上环境。

    try {let name = 'heima; // 少一个单引号console.log(name);
    } catch (error) {console.log('----捕获到了语法错误-----');
    }
    
  2. 同步错误

    同步错误指的是在js同步执行过程中的错误,比如变量未定义,是可以被try catch给捕获到的

    try {const name = 'heima';console.log(nam);
    } catch (error) {console.log('------同步错误-------')
    }
    
  3. 异步错误

    异步错误指的是在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);
    };
    
  4. promise错误

    promise 中使用 catch 可以捕获到异步的错误,但是如果没有写 catch 去捕获错误的话 window.onerror 也捕获不到的,所以写 promise 的时候最好要写上 catch ,或者可以在全局加上 unhandledrejection 的监听,用来监听没有被捕获的promise错误。

    window.addEventListener("unhandledrejection", function(error){console.log('捕获到异常:', error);
    }, true);
    
  5. 资源加载错误

    资源加载错误指的是比如一些资源文件获取失败,可能是服务器挂掉了等原因造成的,出现这种情况就比较严重了,所以需要能够及时的处理,网路错误一般用 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);
}