初识前端监控

news2024/11/25 17:04:57

以下笔记来源:黑马程序员

背景

思考一下,我们的项目代码在上线之后是不是就不用管了呢?

并不是,作为前端开发工程师,我们是直接跟用户打交道的,一个应用的用户留存率跟这个应用的稳定性有很大的关系,因为监控能让我们快速的发现并解决问题,所以一个系统的监控体系越完善这个系统就越稳定。所以不光后端要对接口做监控,前端的监控也显得很重要。

对于我们来说,学习如何做前端的监控一方面可以拓宽我们的知识面,另一方面可以在面试的时候加分,让面试官觉得我们不只是在公司整天的写业务代码,也有技术上的产出,不至于面试的时候问到这方面的问题完全摸不着头脑。

怎么做?

  • 接入现成的

    1. sentry https://docs.sentry.io/

    2. fundebug https://www.fundebug.com/

  • 自己封装

需要监控什么?

  • 错误统计

    首先,我们的代码发布到线上总是会发生奇奇怪怪的错误,错误原因也五花八门,可能是浏览器兼容问题,可能是代码里面没做兜底,也可能是后端接口挂掉了等等错误,可能随便一个错误都会影响用户的使用,所以对线上进行错误监控显的尤为重要,能够让我们第一时间去响应报错并解决。

  • 行为日志埋点

    对于一些常见的电商app,比如淘宝,都有一套自己的用户行为分析的系统,分析用户浏览时间比较长的页面有哪些,常点击的按钮有哪些等等行为,通过分析用户的这些行为去制定不同的策略引导你购物,这些都可以通过前端埋点去实现对用户行为的监控。

  • PV/UV统计

    我们上线那么多的前端页面,肯定特别想知道我们的用户对哪个页面的访问次数比较多,也想知道每天有多少的用户访问我们的系统,这就需要用到PV,UV的统计

所以我们系统的设计就主要围绕上面着三点进行设计,主要流程如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据采集:数据采集做的就是采集我们系统的监控数据,包括PV,UV和用户行为及前端报错的数据。

日志上报:上报做的就是将第一步采集到的数据发送到服务端。

日志查询:这一步就是在后台查询我们采集并上报的数据,方便对系统进行分析。

我们的SDK做的主要是对前两部分的实现。

功能拆分

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

环境

  • Node 16.14.2
  • npm 8.5.0

初始化

初始化其实很简单,就是获取用户传过来的参数,然后调用我们的初始化函数就可以了,在初始化函数中,我们可以注入一些监听事件来实现数据统计的功能。

// 1.加载配置 2.监控代码注入
function init(options) {
  loadConfig(options); // 加载配置
}

错误监控

前端是直接和用户打交道的,前端页面报错是很影响用户体验一件事,即使在测试充分后上线也会因为用户的操作行为以及操作的环境出现各种各样的错误,所以,不光是后端需要加报警监控,前端的错误监控也很重要。

错误类型

  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);
    

所以SDK错误监控的实现,就是围绕这几种错误实现的。 try-catch 用来在可预见情况下监控特定的错误,window.onerror 主要是来捕获预料之外的错误,比如异步错误。但是 window.onerror 也并不是万能的,它可以捕获语法,同步,异步的错误,但是对于promise错误以及网络错误还是无能为力,所以还需要 unhandledrejection 监听来捕获promise错误,最后,再加上 error 监听捕获资源加载的错误就能将各种类型的错误全覆盖了。

用户埋点统计

埋点是监控用户在我们应用上的一些动作表现,是不是经常感觉有些应用推荐的内容都是自己感兴趣的,这就是埋点这个“内鬼”在搞怪,比如你在淘宝上的某类型的鞋子的页面浏览了几分钟,那么就会有一个“张三在2022-7-16 15:30搜索了某款运动鞋并浏览了十分钟”的记录的上报,后台就可以根据这些上报的数据去分析用户的行为,并且制定之后推送或者产品的迭代优化等,对于产品后续的发展起着重要作用。埋点又分为手动埋点和无痕埋点。

手动埋点

手动埋点就是手动的在代码里面添加相关的埋点代码,比如用户点击某个按钮,就在这个按钮的点击事件中加入相关的埋点代码,或者提交了一个表单,就在这个提交事件中加入埋点代码。

// 方式1
<button
  onClick={() => {
    // 业务代码
  	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统计

PV即页面浏览量,用来表示该页面的访问数量

在SPA应用之前只需要监听 onload 事件即可统计页面的PV,在SPA应用中,页面路由的切换完全由前端实现,主流的react和vue框架都有自己的路由管理库,而单页路由又区分为 hash 路由和 history 路由,两种路由的原理又不一样,所以统计起来会有点复杂。我们这里将分别针对两种路由来实现不同的采集数据的方式。

history路由

history路由依赖全局对象 history 实现的

  • history.back(); // 返回上一页,和浏览器回退功能一样
  • history.forward(); // 前进一页,和浏览器前进功能一样
  • history.go(); // 跳转到历史记录中的某一页, eg: history.go(-1); history.go(1)
  • history.pushState(); // 添加新的历史记录
  • history.replaceState(); // 修改当前的记录项

history路由的实现主要依赖的就是 pushStatereplaceState 来实现的,但是这两种方法不能被 popstate 监听到,所以需要对这两种方法进行重写来实现数据的采集。

/**
 * 重写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()
});

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统计

UV统计的是一天内访问该网站的用户数

uv统计比较简单,就只需要在SDK初始化的时候上报一条消息就可以了

/**
 * 初始化配置
 * @param {*} options 
 */
function init(options) {
  ... // 加载配置
  report('user', '加载应用'); // uv统计
}

数据上报

  1. xhr接口请求

    采用接口请求的方式是最简单的,就跟请求其他业务接口一样,只不过上传的是埋点的数据。但是在通常的情况下,一般在公司里面处理埋点的服务器和处理业务逻辑的处理器不是同一台,所以还需要手动解决跨域的问题,另一方面,如果在上报的过程中刷新或者重新打开新页面,可能会造成埋点数据的缺失,所以传统的xhr接口请求的方式并不能很好的适应埋点的需求。

  2. img标签

    img标签的方式是通过将埋点数据伪装成图片URL的请求方式,这样就避免了跨域的问题,但是因为浏览器对url的长度会有限制,所以通过这种方式上报不适合大数据量上报的场景,而且也会存在刷新或者打开页面的时候上报的数据丢失的情况。

  3. sendBeacon

    鉴于以上两种方式的缺点,sendBeacon应运而生了,sendBeacon可以说是为埋点量身定做的,这种方式不会有跨域的限制,也不会存在因为刷新页面等情况造成数据丢失的情况,唯一的缺点就是在某些浏览器上存在兼容性的问题,所以在日常的开发场景中,通常采用sendBeacon上报和img标签上报结合的方式

 * 上报
 * @param {*} type 
 * @param {*} params 
 */
export function report(type, params) {
  const appId = window['_monitor_app_id_'];
  const userId = window['_monitor_user_id_'];
  const url = window['_monitor_report_url_'];

  const logParams = {
    appId, // 项目的appId
    userId,
    type, // error/action/visit/user
    data: params, // 上报的数据
    currentTime: new Date().getTime(), // 时间戳
    currentPage: window.location.href, // 当前页面
    ua: navigator.userAgent, // ua信息
  };

  let logParamsString = JSON.stringify(logParams);

  if (navigator.sendBeacon) { // 支持sendBeacon的浏览器
    navigator.sendBeacon(url, logParamsString);
  } else { // 不支持sendBeacon的浏览器
    let oImage = new Image();
    oImage.src = `${url}?logs=${logParamsString}`;
  }
}

合并上报

对于无痕埋点来说,一次点击就进行一次上报对服务器来说压力有点大,所以最好是能进行一个合并上报。

// 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);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2164705.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NXP i.MX8系列平台开发讲解 - 4.2.1 摄像头篇(一) - 认识摄像头模组

专栏文章目录传送门&#xff1a;返回专栏目录 Hi, 我是你们的老朋友&#xff0c;主要专注于嵌入式软件开发&#xff0c;有兴趣不要忘记点击关注【码思途远】 文章目录 目录 1. 引言 2. 嵌入式系统中的CCM应用 3. 摄像头模组的基本组成 4. 摄像头模组的封装工艺 5. 摄像头…

GORM入门

ORM框架 什么是ORM ORM优缺点 GORM介绍 Github GORM 中文官方网站 安装 go get -u github.com/jinzhu/gorm连接数据库 连接不同的数据库都需要导入对应数据的驱动程序&#xff0c;GORM已经包装了一些驱动程序&#xff0c;只需要按如下方式导入需要的数据库驱动即可&#…

Python 方法传参详解

参数 位置参数 和关键字参数 data{error: str(e)}&#xff1a;传递给了 __init__ 方法中的 data 参数&#xff0c;表示需要返回给客户端的 JSON 数据。status500&#xff1a;通过 **kwargs 传递给了父类 HttpResponse&#xff0c;并设置了响应状态码为 500。 位置参数 和关键字…

redis Redis-Cluster常用命令与Redis性能监控

起因&#xff1a;随着项目的进一步推广&#xff0c;数据量的增大&#xff0c;直接访问mysql数据库获取数据所使用的时间越来越长&#xff0c;为解决当前主要矛盾&#xff0c;决定引入redis非关系型数据库作为缓存层&#xff0c;使得数据并不能直接命中数据库&#xff0c;减少访…

重生之我在代码随想录刷算法第十三天 | 110.平衡二叉树、257. 二叉树的所有路径、404.左叶子之和、222.完全二叉树的节点个数

参考文献链接&#xff1a;代码随想录 本人代码是Java版本的&#xff0c;如有别的版本需要请上代码随想录网站查看。 110.平衡二叉树 力扣题目链接 解题思路 这道题目刚看到以为和二叉树的最大深度差不多&#xff0c;上来写了一堆迭代求深度的代码结果发现不对劲。 看了题…

非标精密五金加工的技术要求

非标精密五金加工在现代制造业中占据着重要地位&#xff0c;其对于产品的精度、质量和性能有着较高的要求。以下是时利和整理的其具体的技术要求&#xff1a; 一、高精度的加工设备 非标精密五金加工需要先进的加工设备来保证加工精度。例如&#xff0c;高精度的数控机床是必不…

【越学学糊涂的Linux系统】Linux指令篇(2)

一、echo指令&#xff1a; ✔️✔️在终端中显示文本内容或向文件中写入文本 Ⅰ.基本用法&#xff1a; 0x00打印字符串&#xff1a; 打印字符串/显示文本内容&#xff1b;可以用双引号作为文本内容⬇️⬇️更推荐用单引号 这里我将字符串打印出来了。和printf的功能一样&…

【Linux实践】实验五:用户和组群账户管理

【Linux实践】实验五&#xff1a;用户和组群账户管理 实验目的实验内容实验步骤及结果1. 创建用户2. 切换用户3. 修改用户4. 删除用户5. 创建组群6. 修改组群补充&#xff1a;删除组群 实验目的 1、掌握字符界面下用户账户的设置&#xff0c;包括命令useradd、usermod、userde…

深入解析 Apache Kylin

以下是关于 Kylin 概述 部分的内容&#xff0c;你可以在技术博客中使用&#xff1a; 1. Kylin 概述 什么是 Apache Kylin&#xff1f; Apache Kylin 是一个开源的分布式分析引擎&#xff0c;主要为大数据场景下的 OLAP&#xff08;Online Analytical Processing&#xff09;提…

利用echarts 显示图片信息

当前有个需求,需要对其进行相关统计,这里我们采用jquery3.6 与echarts.js 做相关图表,不解释,直接上代码吧 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"wid…

被动元数据的不足和主动元数据的先进性

元数据管理领域&#xff0c;被动元数据管理主要执行的是一种基础且相对直接的任务&#xff0c;即简单地对元数据进行编目或存储。这一过程通常不涉及复杂的逻辑处理或动态分析&#xff0c;而是侧重于数据的静态描述和记录&#xff0c;如数据类型、结构、位置、创建时间等。 这…

【PG备份恢复】基于时间点的恢复(踩坑指南)

1 设置基于时间点恢复所需的配置 要启用WAL归档&#xff0c; 需设置wal_level配置参数为replica或更高&#xff0c;设置archive_mode为on&#xff0c;并且使用archive_command配置参数指定一个shell命令 1.1 修改配置文件 postgresql.conf vim postgresql.conf archive_…

资深老师都在用的成绩发布小程序,月考发成绩就用它!

在教育的长河中&#xff0c;老师们一直扮演着知识传递者的角色。然而&#xff0c;随着时间的流逝&#xff0c;教育的方式也在不断地革新。过去&#xff0c;老师们发布成绩查询的方式既繁琐又耗时。 现在我们有了更高效、更便捷的成绩发布方式。在众多的成绩查询系统中&#xff…

2.数据结构研究

计算机解决问题的步骤 线性表 树 图&#xff08;点线&#xff09;

电脑录屏软件免费版,四款宝藏软件揭秘

在这个数字化时代&#xff0c;无论是教学演示、游戏直播还是软件教程分享&#xff0c;电脑录屏都成为了我们日常生活中不可或缺的一部分。然而&#xff0c;面对市面上琳琅满目的录屏软件&#xff0c;如何选择一款既免费又功能强大的工具&#xff0c;成为了不少小伙伴的难题。今…

桌面专业版【ssh配置】

UOS统信SSH协议的简单使用方法。包含软件安装,服务启动及常见问题等内容。 文章目录 功能概述一、SSH安装二、SSH使用方法三、SSH常见问题1. 无法使用root用户登陆。2. SSH登录提示”connect to hosts xxxx :Connection refused功能概述 SSH 为 Secure Shell 的缩写,由 IET…

手把手搞定VMware 的CentOS硬盘扩容

1.背景 用VMware虚拟机创建Centos系统时&#xff0c;选了40GB硬盘&#xff0c;用着用着发现硬盘不够用了。于是&#xff0c;我为了给硬盘扩容&#xff0c;实操了下centos的硬盘扩容。本文是记录下整个操作过程&#xff0c;方便后面查询和使用。 2.操作 2.1 VMware操作 2.2 Ce…

Goweb---Gorm操作数据库(二)

Gorm允许用户自己自定义钩子操作&#xff0c;使用这些钩子操作&#xff0c;可以在增删改查操作前进行相关的操作和检验&#xff0c;它会在创建、更新、查询、删除时自动被调用。如果任何回调返回错误&#xff0c;GORM 将停止后续的操作并回滚事务。 自定义钩子函数 package ma…

充电宝哪个牌子性价比高?2024年充电宝推荐!7款好用充电宝推荐

近年来&#xff0c;充电宝爆炸、自燃等安全事故屡见不鲜&#xff0c;给人们的生命财产安全带来了严重的威胁。比如 2023 年 1 月&#xff0c;广西桂平的一自建民房凌晨突发大火&#xff0c;经初步调查&#xff0c;起火原因就是充电宝发生短路。2024 年 2 月 19 日晚&#xff0c…

如何在项目申报中实施精细化管理?

项目申报作为企业获取资源、拓展市场、推动创新的关键环节&#xff0c;其成功与否往往直接关系到企业的战略发展。而要在众多申报者中脱颖而出&#xff0c;实现项目申报的成功&#xff0c;精细化管理成为关键因素之一。精细化管理强调的是对项目申报全过程的每一个细节进行精准…