1、为什么要做前端监控

  • 更快的发现问题和解决问题
  • 做产品的决策依据
  • 提升工程师的技术深度和广度
  • 为业务扩展提供更多的可能性

2、前端监控目标

2.1 稳定性

  • js错误
  • 资源异常
  • 接口异常
  • 白屏

2.2 用户体验

  • 加载时间:各个阶段的加载时间
  • TTFB:是指浏览器发起的第一个请求到数据返回第一个字节所消耗的时间
  • FP:首次绘制包括了任何用户自定义的背景绘制,它是将第一个像素点绘制到屏幕的时刻
  • FCP:首次内容绘制是浏览器将第一个DOM渲染到屏幕的时间,可以是任何文本、图像、SVG等事件
  • FMP:首次有意义绘制是页面可用性的量度标准
  • FID:用户首次和页面交互到页面响应交互的时间
  • 卡顿:超过50ms的长任务

2.3 业务

  • PV page view页面浏览量或点击量
  • UV 指访问某个站点的不同IP地址的人数
  • 页面停留时间 用户在每一个页面的停留时间

3、前端监控流程

  • 前端埋点
  • 数据上报
  • 分析和计算,将采集到的数据进行加工汇总
  • 可视化展示 将数据按照各个维度进行展示
  • 监控报警 发现问题后按一定的条件出发报警

image.png

4、常见埋点方案

4.1 代码埋点

  • 手动埋点,就是向用户行为的代码添加埋点,例如点击事件
  • 优点可以在任意时刻,精准的发送或保存所需要的数据信息
  • 缺点工作量大

4.2 可视化埋点

  • 通过可视化交互手段,代替代码埋点
  • 将业务代码和埋点代码分离,提供一个可视化交互的页面,输入为业务代码
  • 可视化埋点其实是用系统来代替手工插入埋点代码

4.3 无痕埋点

image.png

5、实践

5.1 错误捕获

  1. window.addEventListener('error', function (event) {},true)
  2. // 脚本错误
  3. let lastEvent = getLastEvent();//最后一个交互事件
  4. //这是一个脚本加载错误
  5. if (event.target && (event.target.src || event.target.href)) {
  6. tracker.send({
  7. kind: 'stability',//监控指标的大类
  8. type: 'error',//小类型 这是一个错误
  9. errorType: 'resourceError',//js或css资源加载错误
  10. filename: event.target.src || event.target.href,//哪个文件报错了
  11. tagName: event.target.tagName,//SCRIPT
  12. //body div#container div.content input
  13. selector: getSelector(event.target) //代表最后一个操作的元素
  14. });
  15. }else {
  16. tracker.send({
  17. kind: 'stability',//监控指标的大类
  18. type: 'error',//小类型 这是一个错误
  19. errorType: 'jsError',//JS执行错误
  20. message: event.message,//报错信息
  21. filename: event.filename,//哪个文件报错了
  22. position: `${event.lineno}:${event.colno}`,
  23. stack: getLines(event.error.stack),
  24. //body div#container div.content input
  25. selector: lastEvent ? getSelector(lastEvent.path) : '' //代表最后一个操作的元素
  26. });
  27. }

重点:
捕获未处理的Promise错误

  1. window.addEventListener('unhandledrejection', (event) => {},true)
  2. tracker.send({
  3. kind: 'stability',//监控指标的大类
  4. type: 'error',//小类型 这是一个错误
  5. errorType: 'promiseError',//JS执行错误
  6. message,//报错信息
  7. filename,//哪个文件报错了
  8. position: `${line}:${column}`,
  9. stack, // 具体行
  10. //body div#container div.content input
  11. selector: lastEvent ? getSelector(lastEvent.path) : '' //代表最后一个操作的元素
  12. });