前端实现监控埋点

news2024/11/26 11:10:40

前端实现监控埋点

前言

我们在应用开发完成本地测试跑通以后上线,线上可能会出现一些测试没有测出来的问题,那么这个时候我们如何定位到哪里会出现问题呢,因为在测试环境可能浏览器的不同,或是没有做兜底出现了一些线上的问题,我们通过监控的埋点便可以快速的定位到错误位置点。

监控点

错误统计

首先我们项目发布以后遇到一些报错问题,可能是接口问题,也可能是代码兜底的问题等五花八门的错误,可能随便一个错误都会直接影响到用户的使用体验。所以对于线上错误统计就显的尤为重要,能够让我们第一时间去响应错误并处理

行为日志

对于一些我们经常用到的应用 例如 抖音 淘宝 等 当你浏览了一些东西,那么下次打开就会给你推荐相似的东西,这是这些app都存在一套自己的用户行为分析系统,分析用户浏览时间比较页面 按钮点击的行为等,针对用户的行为来进行推荐推送不同的内容

pv/pu

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

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

在这里插入图片描述

  • 数据采集:数据采集做的就是采集我们系统的监控数据,包括 PV,UV 和用户行为及前端报错的数据。
  • 日志上报:上报做的就是将第一步采集到的数据发送到服务端。
  • 日志查询:这一步就是在后台查询我们采集并上报的数据,方便对系统进行分析。

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

错误监控

前端页面报错是很影响用户体验一件事,即使在测试充分后上线也会因为用户的操作行为以及操作的环境出现各种各样的错误。接下来我们看如何来进行监控这些错误并进行上报

  • 语法错误

语法错误一般可在开发阶段就发现,比如单词的拼写错误,符号错误等,且无法被try catch捕获,也一般不会被发布到线上环境

  • 同步错误

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

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

异步错误指的是在 setTimeout 等函数中发生的错误,是无法被 try catch 捕获到的,我们可以使用window.onerror来捕获

 window.onerror = (msg) => {
  }
  • promise 错误

在 promise 中使用 catch 可以捕获到异步的错误,但是如果没有写 catch 去捕获错误的话 window.onerror 也捕获不到的,这里我们可以使用unhandledrejection 的监听,用来监听没有被捕获的 promise 错误。

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

资源错误值得是文件获取错误,可能是服务器挂掉所造成的,这种情况一般都是比较严重的,需要及时进行处理的,我们可以使用window.addEventListener的error来进行错误的捕获

window.addEventListener(
  'error',
  (error) => {
    console.log(error)
  },
  true,
)

汇总代码

export function errorTrackerReport() {
 // --------  js error ---------
 const originOnError = window.onerror;
 window.onerror = function (msg, url, row, col, error) {
   // 处理原有的onerror
   if (originOnError) {
     originOnError.call(window, msg, url, row, col, error);
   }
   // 错误上报
   lazyReport('error', {
     message: msg,
     file: url,
     row,
     col,
     error,
     errorType: 'jsError'
   });
 }

 // ------  promise error  --------
 window.addEventListener('unhandledrejection', (error) => {
   lazyReport('error', {
     message: error.reason,
     error,
     errorType: 'promiseError'
   });
 });

 // ------- resource error --------
 window.addEventListener('error', (error) => {
   let target = error.target;
   let isElementTarget = target instanceof HTMLScriptElement || target instanceof HTMLLinkElement || target instanceof HTMLImageElement;
   if (!isElementTarget) {
     return; // js error不再处理
   }
   lazyReport('error', {
     message: "加载 " + target.tagName + " 资源错误",
     file: target.src,
     errorType: 'resourceError'
   });
 }, true)
}

用户埋点

埋点的话是监控用户的一些行为数据,比如按钮的点击,页面的浏览时长等数据,根据用户所做的行为进行上报

  • 手动埋点

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

/**
* 手动上报
*/
export function tracker(actionType, data) {
 lazyReport('action', {
   actionType,//类型
   data//上报数据
 });
}
   <button 
     style="marginRight: 20px"
     @click="tracker('click', '按钮1被点击了')"
   >按钮1</button>
  • 属性埋点 属性埋点和上方的手动埋点差不多,就是在元素上写一个自定义属性,自动上传自定义属性内的内容
js

 代码解读
复制代码<button data-target="支付按钮" onClick={() => { // 业务代码 }} >手动上报</button>

这两种的优点呢就是可以上传具体的数据可控性比较强,缺点呢就是对代码入侵比较强

  • 无痕埋点

无痕埋点是为了解决手动埋点的缺点,实现一种不用侵入业务代码就能在应用中添加埋点监控的埋点方式。

js 代码解读复制代码/**
 * 自动上报
 */
export function autoTrackerReport() {
  // 自动上报
  document.body.addEventListener('click', function (e) {
    const clickedDom = e.target;

    // 获取标签上的data-target属性的值
    let target = clickedDom?.getAttribute('data-target');

    // 获取标签上的data-no属性的值(这里是为了避免重复上报,在需要手动上报的地方设置data-no)
    let no = clickedDom?.getAttribute('data-no');
    // 避免重复上报
    if (no) {
      return;
    }

    if (target) {
      lazyReport('action', {
        actionType: 'click',
        data: target
      });
    } else {
      // 获取被点击元素的dom路径
      const path = getPathTo(clickedDom);
      lazyReport('action', {
        actionType: 'click',
        data: path
      });
    }
  }, false);
}

这个的话可以实现全自动的上报,且不需要太强的代码入侵,但是无法自定义上报数据,且服务器压力比较大,因为只要又点击就会被上报

pv统计

PV 即页面浏览量,用来表示该页面的访问数量。 单页路由区分为 hash 路由和 history 路由,两种路由的原理又不一样,所以统计起来会有点复杂。 我们这里将分别针对两种路由来实现不同的采集数据的方式。

history 路由的实现主要依赖的就是 pushState 和 replaceState 来实现的,但是这两种方法不能被 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 的改变会出发 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 统计的是一天内访问该网站的用户数,只需要在初始化的时候上报一条消息就可以了

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

结尾

现在其实以及存在了比较成熟的监控库供我们使用,例如 sentry fundebug,在开发中还是用的的组件库比较多,这个呢我们只是了解一下这些内部库是如何进行实现的监控

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

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

相关文章

TLS协议基本原理与Wireshark分析

01背 景 随着车联网的迅猛发展&#xff0c;汽车已经不再是传统的机械交通工具&#xff0c;而是智能化、互联化的移动终端。然而&#xff0c;随之而来的是对车辆通信安全的日益严峻的威胁。在车联网生态系统中&#xff0c;车辆通过无线网络与其他车辆、基础设施以及云端服务进行…

取消element-ui中账号和密码登录功能浏览器默认的填充色,element-ui登录账号密码输入框禁用浏览器默认填充色问题

标题 问题展示 修改后 <div class="loginForm"><el-formref="formB":model="formDataB":rules="rulesB"class="login-form"label-position="left"><el-form-item prop="userNo" clas…

Spring Boot与Flyway实现自动化数据库版本控制

一、为什么使用Flyway 最简单的一个项目是一个软件连接到一个数据库&#xff0c;但是大多数项目中我们不仅要处理我们开发环境的副本&#xff0c;还需要处理其他很多副本。例如&#xff1a;开发环境、测试环境、生产环境。想到数据库管理&#xff0c;我们立刻就能想到一系列问…

Java最全面试题->Java基础面试题->JavaWeb面试题->Maven面试题

文章目录 Maven什么是maven&#xff1f;maven优缺点&#xff1f;maven常见的依赖范围有哪些?maven 坐标的含义?maven 常用命令?maven构建的过程&#xff1f;maven的生命周期&#xff1f;使用“mvn clean package”命令进行项目打包&#xff0c;该命令具体做了什么&#xff1…

Tcp_Sever(线程池版本的 TCP 服务器)

Tcp_Sever&#xff08;线程池版本的 TCP 服务器&#xff09; 前言1. 功能介绍及展示1.1 服务端连接1.2 客户端连接&#xff08;可多个用户同时在线连接服务端&#xff09;1.3 功能服务1.3.1 defaultService&#xff08;默认服务&#xff09;1.3.2 transform&#xff08;大小写转…

【STM32 ADC】

STM32 ADC功能简介 文章目录 前言一、ADC简介二、逐次逼近型ADC三、STM32的ADC内部框图四、STM32ADC输入通道五、规则组的四种转换模式六、规则组的触发源七、数据对齐八、转换时间九、校准部分十.ADC采样测量电压的程序设计十一、ADC的迟滞比较、滤波设计十二、四通道ADC采用问…

【github小问题】——push后报错error: src refspec master does not match any

温馨提示&#xff1a;这个问题可能有多种问题导致如未commit&#xff0c;本文在此讲述的是我遇到的这一种情况。 一、问题描述 从本地上传文件至github仓库时&#xff0c;add和commit都执行了且成功&#xff0c;但是执行git push -u origin master后出现了&#xff1a;error: …

深入理解JavaScript:两大编程思想和ES6类以及对象概念解析

文章目录 两大编程思想ES6中的类和对象 两大编程思想 面向过程 &#xff08;Procedural-Oriented Programming&#xff0c;POP&#xff09; 定义&#xff1a;面向过程的编程是一种基于过程调用的编程范式&#xff0c;它将程序看作是一系列函数或过程的集合。每个函数负责完成…

【K8S系列】Kubernetes pod节点Unknown 问题及解决方案详解【已解决】

在 Kubernetes 中&#xff0c;Pod 的状态为 Unknown 表示无法获取 Pod 的当前状态。这通常意味着 Kubernetes API 服务器无法与 Pod 所在的节点通信&#xff0c;或者 Kubelet 进程遇到问题。以下将详细介绍 Unknown 状态的原因、解决方案以及如何配置健康检查以提高系统的稳定性…

函数的实参和形参

什么是实参&#xff1f;什么是形参&#xff1f; 其实让我用语言来形容并不好描述&#xff0c;我们看例子&#xff1a; int add(int x , int y)//括号内就是形参 { int zxy; return z; } #include <stdio.h> int main() { int a8; int b9; int vadd(a,b);//括号内放置的参…

django-vue-admin测试环境搭建

django-vue-admin测试环境搭建 引言开发工具入门demo示例踩过的坑数据库字符集创建数据表前端路由 自定义app效果展示 引言 django-vue-admin框架&#xff0c;大幅度降低应用层代码难度,让每一个刚开始学习 django和vue的新手都能快速上手。这将会是你上手学习 djangovue的最佳…

PyQt 入门教程(3)基础知识 | 3.1、使用QtDesigner创建.ui文件

文章目录 一、使用QtDesigner创建.ui文件1、创建.ui文件2、生成.py文件3、使用新生成的.py文件4、编辑新生成的.py文件 一、使用QtDesigner创建.ui文件 1、创建.ui文件 打开PyCharm&#xff0c;使用自定义外部工具QtDesigner创建mydialog.ui文件&#xff0c;如下&#xff1a; …

pandas库——基础

1.概述 Pandas 是一个开源的第三方 Python 库&#xff0c;从 Numpy 和 Matplotlib 的基础上构建而来 Pandas 名字衍生自术语 "panel data"&#xff08;面板数据&#xff09;和 "Python data analysis"&#xff08;Python 数据分析&#xff09; Pandas 已…

Python酷库之旅-第三方库Pandas(166)

目录 一、用法精讲 761、pandas.Interval.closed_right属性 761-1、语法 761-2、参数 761-3、功能 761-4、返回值 761-5、说明 761-6、用法 761-6-1、数据准备 761-6-2、代码示例 761-6-3、结果输出 762、pandas.Interval.is_empty属性 762-1、语法 762-2、参数 …

【Mac 上将 MOV 格式转换为 MP4 格式的简易指南】

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【C++】哈希实现unordered_map/set

关于哈希模拟实现unordered_map/set&#xff0c;与红黑树模拟实现map/set的大体思路相似。 【C】红黑树模拟实现map和set-CSDN博客 HashTable的迭代器 operator template<class K,class T,class KeyOfT> struct __HashTableIterator {typedef __HashTableIterator<…

电梯导航 - 点击标题跳转对应区域

需求 点击标题&#xff0c;使用a标签的锚点自动跳到对应区域滚动区域&#xff0c;右边自动切换对应的标题 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"wid…

大话红黑树之(3)进阶解析

红黑树高阶知识讲解 红黑树作为一种自平衡的二叉查找树&#xff08;BST&#xff09;&#xff0c;在大多数语言和库中有着广泛应用。它能够在常规操作&#xff08;查找、插入、删除等&#xff09;中保持 O(log n) 的时间复杂度。这篇文章从红黑树的高级特性、性能优化、旋转机制…

U9的插件开发之BE插件(1)

U9插件可分为&#xff1a;BE插件、BP插件、UI插件&#xff1b; BE(Business Entity) 简单就是指实体&#xff0c;U9的元数据。 我的案例是设置BE默认值&#xff0c;即在单据新增时&#xff0c;设置单据某一个字段的默认值&#xff0c;具体如下&#xff1a; 1.插件开发工具&a…

使用virtualenv导入ssl模块找不到指定的模块

最近在学习tensorflow&#xff0c;由于教程里面使用的是virtualenv&#xff0c;所以就按照教程开始安装了虚拟环境。但是在使用的时候&#xff0c;卡在了import ssl这一步&#xff0c;提示如下错误 >>> import ssl Traceback (most recent call last):File "<…