前端监控与埋点 全总结

一、概念

前端埋点是指在网页或者应用程序中插入特定的代码,用于收集用户的行为数据并发送给服务器进行分析。这些数据可以包括用户的点击、浏览、输入等操作,帮助开发者了解用户的在其网站中的行为,从而进行针对性的优化和改进。

前端埋点通常包括以下几个步骤:

  1. 定义事件:定义需要收集的数据事件,如点击、浏览等。
  2. 添加代码:在网页或应用程序中添加特定的代码,用于收集事件数据。
  3. 发送数据:将收集到的数据发送给服务器进行分析。
  4. 分析数据:对收集到的数据进行分析和挖掘,找出用户行为规律和需求,为产品的改进和优化提供依据。
    在这里插入图片描述

二、前端监控

(一)常见的监控

  1. 数据监控 (主要关注用户在网站或应用中的行为和交互)
    • PV:即页面浏览量或点击量;
    • UV:指访问某个站点或点击某条新闻的不同 IP 地址的人数
    • 用户在每一个页面的停留时间
    • 用户通过什么入口来访问该网页
    • 用户在相应的页面中触发的行为
  2. 性能监控 (主要关注网站或应用的加载速度、响应时间和用户体验等方面)
    • 不同用户,不同机型和不同系统下的首屏加载时间
    • 白屏时间
    • http 等请求的响应时间
    • 静态资源整体下载时间
    • 页面渲染时间
    • 页面交互动画完成时间
  3. 异常监控 (主要关注网站或应用在运行过程中是否出现错误或异常)
    • Javascript 的异常监控
    • 样式丢失的异常监控
      在这里插入图片描述

(二)性能数据

在这里插入图片描述

字段描述计算方式意义
unload前一个页面卸载耗时unloadEventEnd - unloadEventStart-
redirect重定向耗时redirectEnd - redirectStart重定向时间
appCache缓存耗时domainLookupStart - fetchStart读取缓存的时间
dnsDNS 解析耗时domainLoopupEnd - domainLookupStart观察域名解析服务是否正常
tcpTCP 连接耗时connectEnd - connectStart建立连接的耗时
sslSSL 安全连接耗时connectEnd - secureConnectionStart反映数据安全连接建立耗时
response响应数据传输耗时responseEnd - responseStart观察网络是否正常
domDOM 解析耗时domInteractive - responseEnd观察 DOM 结构是否合理,是否有 JS 阻塞页面解析
dclDOMContentLoaded 事件耗时domContentLoadedEventEnd - domContentLoadedEventStart当 HTML 文档被完全加载和解析之后,DOMContentLoaded 事件被触发,无需等待样式表、图像的完成加载
resources资源加载耗时domComplete - domContentLoadedEventEnd可以观察文档流是否过大
domReadyDOM 阶段渲染耗时domContentLoadedEventEnd - fetchStartDOM 树和页面加载完成时间,会触发 domContentLoaded 事件
首次渲染耗时首次渲染耗时responsedEnd - fetchStart加载文档到看到第一帧非空图像的时间(白屏时间)
首次可交互时间首次可交互时间domInteractive - fetchStartDOM 树解析完成时间,此时 document.readyState 为 interactive
首包时间耗时首包时间耗时responseStart - domainLookupStartDNS 解析到响应返回给浏览器第一个字节的时间
页面完全加载时间页面完全加载时间loadEventStart - fetchStart-
onLoadonLoad 事件耗时loadEventEnd - loadEventStart-

在这里插入图片描述

性能采集: developer.mozilla.org/zh-CN/docs/…

  1. PerformanceTiming (精度不够,只能到毫秒,已废弃)
    在这里插入图片描述

  2. PerformanceNavigationTiming(精确到纳秒)
    在这里插入图片描述

  3. PerformanceObserver

    • observe():注册一个观察器,指定要观察的性能指标和回调函数。
    • disconnect():取消所有注册的观察器。
    • takeRecords():获取所有已有的性能测量结果。
    • onperformanceentry():处理性能观察事件的回调函数。
window.addEventListener('load', e => {

    // PerformanceTiming
    const timing = window.performance.timing;
    const { domComplete, domLoading } = timing;
    const domTiming = domComplete - domLoading;
    console.log('domTiming: ', domTiming);
    

    // PerformanceNavigationTiming
    const perEntries = window.performance.getEntries();
    const { domainLookupStart, domainLookupEnd } = perEntries[0];
    const dnsTiming = domainLookupEnd - domainLookupStart;
    console.log('dnsTiming: ', dnsTiming);
    

    // PerformanceObserver
    // 创建一个新的PerformanceObserver实例
    const observer = new PerformanceObserver((list) => {
        const entries = list.getEntries();
        
        entries.forEach(entry => {
            if (entry.name === 'first-paint') {
                console.log('First Paint:', entry.startTime);
            } else if (entry.name === 'first-contentful-paint') {
                console.log('First Contentful Paint:', entry.startTime);
            }
        });
    });

    // 启动PerformanceObserver并指定观察的entryTypes为'paint'
    observer.observe({ entryTypes: ['paint'] });
})

三、埋点的分类

类型简述举例
展现埋点指的是在产品的特定位置(如网页,应用界面)设置的,用户记录用户是否看到展现了该位置的特定内容或元素的埋点。在一个网页中,我们希望知道用户是否看到了某个广告的特定的推广信息,这时候可以使用展现埋点来记录。展现埋点通过会记录展现的次数,以及展现的具体内容等信息。
曝光埋点曝光埋点和展现埋点类似,也是用于记录用户是否看到特定内容或元素的埋点。但曝光埋点更侧重于记录用户看到的内容或元素是否被充分地曝光,即用户是否有机会注意到该内容或元素。在一个广告投放中,我们可能希望知道广告是否被用户充分看到(曝光),这时就可以使用曝光埋点来记录。曝光埋点通常会记录曝光的次数,以及曝光的具体内容等信息。
交互埋点交互埋点是指在产品的特定位置设置的,用于记录用户与该位置的特定内容或元素进行交互(如点击、填写、分享等)的埋点。在一个网页中,我们可能希望知道用户是否点击了某个按钮或链接,这时就可以使用交互埋点来记录。交互埋点通常会记录交互的次数,以及交互的具体内容等信息。

四、前端常用的几种埋点方案

(一)常见埋点

  1. 代码埋点

    需要开发人员在网页中手动添加跟踪代码,通常是在事件触发的地方添加一段JavaScript代码,用于记录用户的行为数据,并发送给后端服务器进行分析。这种方式的优点是灵活性高,可以精确捕获到各种复杂的用户行为,但缺点是开发成本较高,且需要一定的技术门槛。

  2. 可视化埋点

    通常通过一个可视化的界面来完成,用户可以在界面上选择要跟踪的事件和页面,然后系统会自动生成相应的跟踪代码,用户只需要将其添加到网页中即可。这种方式的优点是操作简单,无需具备技术背景也可以进行数据跟踪,但缺点是灵活性较低,可能无法满足一些复杂的跟踪需求。

  3. 无痕埋点

    称为全埋点或自动埋点,它通过在网页中自动采集所有用户行为数据,然后发送给后端服务器进行分析。这种方式的优点是无需手动添加跟踪代码,可以大幅度降低开发成本,但缺点是可能会采集到大量的冗余数据,且难以精确捕获到一些复杂的用户行为。

分类代码埋点可视化埋点无痕埋点(全埋点/自动埋点)
原理按需埋点,跟迭代运行,定义好埋点事件后添加相应埋点代码将核心代码与埋点配置分开,在可视化界面中编辑埋点信息生成埋点配置,从服务端拉取配置,根据配置监听相关交互操作并采集上报通过SDK将程序中的数据尽可能多的采集、存储下来,以备后续使用
常见场景无痕埋点无法覆盖到,比如需要业务数据简单规范的页面场景简单规范的页面场景
优势可以在任意时刻,精确的发送或保存所需要的数据信息开发成本低,运营人员可直接进行相关埋点配置由于采集的是全量数据,所以产品迭代过程中是不需要关注埋点逻辑的,也不会出现漏埋、误埋等现象
不足工作量较大,每一个组件的埋点都需要添加相应的代码可视化埋点可以埋点的控件有限,不能手动定制无埋点采集全量数据,给数据传输和服务器增加压力无法灵活的定制各个事件所需要上传的数据
典例友盟、百度统计MixpanelGrowingIO

(二)埋点数据收集

类型页面浏览数据用户行为数据错误数据用户属性数据设备信息使用时长数据搜索关键词数据
包含页面的PV、UV、停留时间用户的点击、滚动、输入等操作行为代码中的错误信息、异常情况用户年龄、性别、地域等用户设备类型、操作系统、浏览器等信息用户使用产品的时长、频次等用户在搜索框中输入的关键词信息
作用反映页面受欢迎程度以及用户黏性反映用户的兴趣和偏好帮助开发者定位和解决问题定位用户的数据、营销优化产品跨平台体验反映产品的用户黏性和活跃度优化产品搜索功能

(三)如何开发埋点SDK

主要从三点数据监控、性能监控、异常监控出发;

细节大概分为 DOM 事件监听、JS 错误、PV来进行展开;

export default class Tracker {

    private data: Options

    public constructor(option: Options) { ... }

    // DOM 事件上报

    private domTracker() { ... }

    // JS 错误上报

    private jsError() { ... }

    // PV 上报

    private pv() { ... }

    // 数据上报到后端

    private sendData()<T>(data:T) { ... }

}
  1. DOM 监控上报

    private data: Options
    
    private eventList: string[] = ['click', 'dbclick', 'mousedown', 'mouseup', 'mouseenter', 'mouseout', 'mouseover']
    
    public constructor(option: Options) { ... }
    
    private domTracker () {
        this.eventList.forEach(item => {
            window.addEventListenter(item, e => {
                let element = e.target as HTMLElement;
                let isTarget = element.getAttribute('target-key');
                if(isTarget) {
                    this.sendData({type: 'DOM'});
                }
            })
        })
    }
    
    private sendData()<T>(data:T) { ... }
    
  2. JS 错误上报(逻辑错误、资源加载错误、promise 错误)

    • 逻辑错误

      JS 中逻辑表达式的错误可以通过 window.addEventListener(‘error’, function () { })

      window.addEventListener('error', e => {
          console.log('e: ', e);
      })
      
    • 资源加载错误

      常见的是页面的图标、图片等资源丢失;

      可以通过 window.addEventListener(‘error’, function () { })来捕获错误;

      区分于逻辑错误,可以通过 ErrorEvent 判断当前错误类型,逻辑错误事件的原型链存在 ErrorEvent;

      window.addEventListener('error', e => {
          e.preventDefault();
      
          // 判断错误类型
          const isErrorEvent: Boolean = e instanceof ErrorEvent;
      
          if (!isErrorEvent) { // 资源加载错误
              this.sendData(
                  {
                      type: 'resource',
                      msg: e.message,
                  }
              );
              return;
          }
      
          this.sendData( // js 错误
              {
                  type: 'js',
                  msg: e.message,
              }
          )
      
      }, true)
      
    • promise 错误(promise 内部产生的错误、 promise 的 reject 状态错误)

      通过 unhandledrejection 进行捕获

      window.addEventListener('unhandledrejection', (e: PromiseRejectionEvent) => {
          e.preventDefault();
      
          e.promise.catch((error) => {
              // 区分 promise 的两种错误消息
              let msg = error?.message || error;
              this.sendData({ type: 'promise', msg })
          })
      })
      
  3. PV 上报

    页面访问量监听可以通过 history 和 hash 两种路由来实现数据监听上报

    • hash 路由

      hash 路由的监听可以采用 hashChange 事件来进行监听

      window.addEventListener('hashchange', e => {
          this.sendData({ type: 'hash', msg: e })
      })
      
    • history 路由

      history 路由模式区别于 hash 不能使用 addEventListenter 来进行事件监听,只能通过自定义事件来监听 history 路由的改变。

      设计思路:由于 history 路由的跳转只能通过 pushState 和 replaceState 来操作,可以通过重写 pushState 以及 replaceState (保留原有方法的功能)并在完成路由跳转完成的同时出发自定义事件进行 pv 的统计。

      this.historyType.forEach((item: keyof History) => {
          let origin = history[item];
          let eventHistory = new Event(item);
      
          (window.history[item] as any) = function (this: any) {
              origin.apply(this, arguments);
              window.dispatchEvent(eventHistory)
          }
      
          window.addEventListener(item, () => {
              this.sendData({ type: 'history', msg: item })
          })
      })
      

      如何自定义事件?

      创建自定义对象->通过 addEventListener 监听自定义事件 -> 执行操作时派发自定义事件

      const e = new Event('customEvent');
      
      window.addEventListener('customEvent', e => {
          console.log('捕获自定义事件');
      })
      
      function btnClick() {
          window.dispatchEvent(e)
      }
      

(四)数据上报

  1. xhr 接口请求

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

  2. img 标签(使用GIF上报)

    img 是通过将埋点数据伪装为图片 URL 的请求方式避免跨域问题。但浏览器对于 URL 的长度会有限制,所以 img 上报不适合大数据量上报的场景。同时也会存在刷新或者打开页面的时候上报数据丢失。
    在这里插入图片描述

    a. 那为什么要使用请求 GIF 图片的方式上报数据呢?

    • 防止跨域

      一般来说,打点域名都不是当前的域名,所以几乎所有接口的请求都会构成跨域。而跨域请求很容易由于配置不当被浏览器拦截报错。但图片的 src 属性并不会跨域,并且同样跨域发起请求。

    • 防止阻塞页面加载,影响用户体验

      一般创建资源节点后只有将对象注入到浏览器 DOM 树后,浏览器才会实际发送资源请求。反复操作 DOM 容易引发性能问题,而且加载 JS/CSS 子资源还会阻塞页面渲染,影响用户体验。

      使用图片打点不用插入 DOM ,只要在 JS 中 new 出 Image 对象就能发送请求,而且还没有阻塞问题。在没有 JS 的浏览器环境中也可以通过 img 标签正常打点,这是其他类型的资源请求所做不到。

    • 相比PNG/JPG,GIF的体积最小

      BMP:74字节;PNG:67字节;GIF:43字节;

      据统计,同样的响应 GIF 可以比 BMP 节约41%的流量,比 PNG 节约35%的流量。

    b. 为什么大多数采用1*1像素的透明 GIF 来上报?

    • 1*1像素是最小的合法图片。通过图片打点,一般来说,图片最好是透明的,不影响页面本身的渲染效果,同时表示图片透明只要使用一个二进制位标记图片是透明色即可,不用存储色彩空间数据,节约体积。
  3. sendBeacon()

    sendBeacon() 方法用于将数据异步传输到服务器,通常用于收集用户行为数据或跟踪用户活动。该方法可以确保数据在页面关闭或刷新之前发送给服务器,从而避免数据丢失。

    sendBeacon() 方法接受两个参数:一个包含要发送的数据的字符串,以及一个可选的 URL,表示要将数据发送到哪个服务器。如果未指定 URL,数据将发送到当前页面的 URL。缺点就是在某些浏览器上存在兼容性问题。

    navigator.sendBeacon('http://127.0.0.1:5500/data', JSON.stringify({
        event: 'pageview',
        url: window.location.href,
        time: Date.now()
    }));
    

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python简单文件操作day9

1、文件操作的重要性和场景 重要性&#xff1a; 数据持久化、跨平台兼容性、数据备份与恢复、数据共享、配置管理、日志记录 应用场景&#xff1a; 数据分析、web开发、文本处理 2、文件的概念 文件是一个存储在某种持久性存储介质【硬盘、光盘、磁盘等】上的数据的结合。 …

指令存储和指令流水线

要求存储器的编址单位&#xff0c;首先观察到计算机采用的是32位定长指令字&#xff0c;因此一条指令就是32位&#xff0c;即4B&#xff0c;根据表中可知一条指令所占地址空间为08048104H-08048100H4H&#xff0c;因此所用的编制单位为字节&#xff08;B&#xff09; 将所有指令…

kafka管理工具

文章目录 前言一、Kafka Assistan1.1 描述1.2、配置安装 二、Conduktor2.1、描述2.2、配置安装 三、kafka-maneger3.1、描述3.2、配置安装3.3、命令启动3.4、[refer to](https://www.ctyun.cn/document/10000120/10033218#section-39755766f4910e4b) 前言 提示&#xff1a;这里…

JavaWeb常见注解

1.Controller 在 JavaWeb 开发中&#xff0c;Controller是 Spring 框架中的一个注解&#xff0c;主要用于定义控制器类&#xff08;Controller&#xff09;&#xff0c;是 Spring MVC 模式的核心组件之一。它表示该类是一个 Spring MVC 控制器&#xff0c;用来处理 HTTP 请求并…

axios平替!用浏览器自带的fetch处理AJAX(兼容表单/JSON/文件上传)

fetch 是啥&#xff1f; fetch 函数是 JavaScript 中用于发送网络请求的内置 API&#xff0c;可以替代传统的 XMLHttpRequest。它可以发送 HTTP 请求&#xff08;如 GET、POST 等&#xff09;&#xff0c;并返回一个 Promise&#xff0c;从而简化异步操作 基本用法 /* 下面是…

window任务计划记录中显示操作成功,但是代码只执行了第一句命令

一、创建定时任务 1. Windows键R 调出此窗口&#xff0c;输入compmgmt.msc &#xff08;调用的是计算机管理&#xff09; 2. 创建基本任务 在任务计划程序中右键 选择 创建基本任务。 输入任务名称及描述。 下一步中选择触发器的时间&#xff0c;这里选择每天。 选择开始时间&…

使用VSCode远程连接服务器并解决Neo4j无法登陆问题

摘要&#xff1a;本文介绍了如何通过VSCode连接内网部署的Neo4j服务器&#xff0c;并启动服务。在访问Neo4j登录界面时&#xff0c;遇到了端口映射问题导致无法登录。通过手动添加7687端口的映射后&#xff0c;成功登录Neo4j。 我在内网部署了一台服务器&#xff0c;并在其上运…

【异常解决】Linux shell报错:-bash: [: ==: 期待一元表达式 解决方法

博主介绍&#xff1a;✌全网粉丝21W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…

游戏引擎学习第四天

视频参考:https://www.bilibili.com/video/BV1aDmqYnEnc/ BitBlt 是 Windows GDI&#xff08;图形设备接口&#xff09;中的一个函数&#xff0c;用于在设备上下文&#xff08;device context, DC&#xff09;之间复制位图数据。BitBlt 的主要用途是将一个图像区域从一个地方复…

七牛云上传图片成功,但是无法访问显示{error : document not found}

上传图片成功&#xff0c;但是访问不了的问题&#xff0c;直接把地址放进浏览器显示{error : document not found}&#xff0c;直接访问 DCNF 404是符合预期的&#xff0c;因为还没有去空间复制外链&#xff0c;要访问实际存在的资源才可以的. 配置区域和访问域名 设置没问题了…

通过投毒Bingbot索引挖掘必应中的存储型XSS

简介 在本文中&#xff0c;我将讨论如何通过从外部网站对Bingbot进行投毒&#xff0c;来在Bing.com上实现持久性XSS攻击。 什么是存储型或持久性XSS&#xff1f;存储型攻击指的是将恶意脚本永久存储在目标服务器上&#xff0c;例如数据库、论坛、访问日志、评论栏等。受害者在…

84.7k Star!Excalidraw:开源的在线白板工具,具备手绘风格和实时协作功能

❤️ 如果你也关注大模型与 AI 的发展现状&#xff0c;且对大模型应用开发非常感兴趣&#xff0c;我会快速跟你分享最新的感兴趣的 AI 应用和热点信息&#xff0c;也会不定期分享自己的想法和开源实例&#xff0c;欢迎关注我哦&#xff01; &#x1f966; 微信公众号&#xff…

让Git走代理

有时候idea提交代码或者从github拉取代码&#xff0c;一直报错超时或者:Recv failure: Connection was reset,下面记录一下怎么让git走代理从而访问到github。 1.打开梯子 2.打开网络和Internet设置 3.设置代理 记住这个地址和端口 4.打开git bash终端 输入以下内容 git c…

CSS:导航栏三角箭头

用CSS实现导航流程图的样式。可根据自己的需求进行修改&#xff0c;代码精略的写了一下。 注&#xff1a;场景一和场景二在分辨率比较低的情况下会有一个1px的缝隙不太优雅&#xff0c;自行处理。有个方法是直接在每个外面包一个DIV&#xff0c;用动态样式设置底色。 场景一、…

第4章-计划 4.3 订计划、勤跟踪、要闭环

4.3 订计划、勤跟踪、要闭环 1.制订好的第一版计划先要基线化&#xff0c;确保有据可依2.计划要监督执行&#xff0c;发现延期时要“喊出来”3.计划要赶得上变化4.资源保障是计划能够执行的依赖 坚定执行制订好的计划&#xff0c;监督执行效果&#xff0c;计划产生偏差时及时制…

在 WPF 中,如何实现数据的双向绑定?

在 WPF 中&#xff0c;数据绑定是一个非常重要的特性&#xff0c;它允许 UI 与数据源之间自动同步。双向绑定是一种常见的绑定方式&#xff0c;当数据源更新时&#xff0c;UI 会自动更新&#xff1b;同样&#xff0c;当 UI 中的元素&#xff08;如文本框&#xff09;发生改变时…

Java面向对象编程进阶之包装类

Java面向对象编程进阶之包装类 一、为什么要使用包装类二、掌握基本数据类型与包装类之间的转换1、为什么需要转换&#xff1f;2、如何转换&#xff1f; 三、String与基本数据类型、包装类之间的转换1、案例2、特别注意 一、为什么要使用包装类 为了使得基本类型的数据变量具备…

基于Spring Boot与Redis的令牌主动失效机制实现

目录 前言1. 项目结构和依赖配置1.1 项目依赖配置1.2 Redis连接配置 2. 令牌主动失效机制的实现流程2.1 登录成功后将令牌存储到Redis中2.2 使用拦截器验证令牌2.3 用户修改密码后删除旧令牌 3. Redis的配置与测试4. 可能的扩展与优化结语 前言 在现代Web系统中&#xff0c;用…

yolov8-cls的onnx与tensorrt推理

本文不生产技术,只做技术的搬运工! 前言 最近需要使用yolov8-cls进行模型分类任务,但是使用ultralytics框架去部署非常不方便,因此打算进行onnx或者tensorrt去部署,查看了很多网上的帖子,并没有发现有完整复现yolov8-cls前处理(不需要后处理)的"轮子",通过自己debug…

Acrobat Pro DC 2023(pdf免费转化word)

所在位置 通过网盘分享的文件&#xff1a;Acrobat Pro DC 2023(64bit).tar 链接: https://pan.baidu.com/s/1_m8TT1rHTtp5YnU8F0QGXQ 提取码: 1234 --来自百度网盘超级会员v4的分享 安装流程 打开安装所在位置 进入安装程序 找到安装程序 进入后点击自定义安装&#xff0c;这里…