前端埋点全解及埋点SDK实现方式

news2024/11/18 23:27:28

一、什么是埋点

        所谓“埋点”,是数据采集领域(尤其是用户行为数据采集领域)的术语,指的是针对特定用户行为或事件进行捕获、处理和发送的相关技术及其实施过程。比如用户某个icon点击次数、观看某个视频的时长等等。
        埋点的技术实质,是先监听软件应用运行过程中的事件,当需要关注的事件发生时进行判断和捕获。埋点需要在特定的时机来收集数据,然后上报给服务端进行分析

二、为什么需要埋点

        数据生产-》数据采集-》数据处理-》数据分析挖掘-》数据驱动/用户反馈-》产品优化/迭代
上述是整个数据从产生到最终作用于产品优化上的过程。埋点是整个流程的开始点,终端提供商在收集到埋点数据之后,通过大数据处理、数据统计、数据分析、数据挖掘等加工处理,可以得到衡量产品状态的一些基本指标,比如活跃、留存、新增等大盘数据,从而洞察产品的状态,随着数据挖掘等技术的兴起,埋点采集到的数据在以下方面的作用也越来越凸显。

三、如何去埋点

一般情况下,主要有3类埋点:展现埋点 + 曝光埋点 + 交互埋点。
1、展现埋点
定义展现其实是一个服务端的触发。服务端被触发后,用户侧将会展现什么内容,展现埋点需要记录的是页面展现的内容信息,即服务端下发的内容是什么(这些东西一定是当前页面主要内容,不包含一些交互信息)。
2、曝光埋点
哪些下发的内容被用户实际看到了。和展现埋点类似,由于屏幕有限,但内容可以无限。哪些内容被用户侧实际看到(曝光),需要记录的是单个“内容”被看到。一系列被下发的内容,可以触发多次曝光埋点。
3、交互埋点
交互埋点表明的是功能/内容被用户“点击”了。从埋点时机来说,这个是展现 & 曝光的下游。记录对于我们提供的“服务”的“消费”情况。比如,一个页面,用户可以点击,那么我们需要记录相应的交互动作埋点;比如,一个视频可以点赞,我们也可以记录交互埋点;比如,一个视频可以播放暂停,我们也可以记录消费埋点。总体来说,就是,我们要记录 被看到的可交互功能/信息的“消费”数据。

四、埋点记录

关于埋点记录需要明确记录两个信息:点位信息、点位映射。
点位信息:明确每个业务事件下的具体的参数信息,包含公共参数、业务参数。
点位映射:每个埋点对应的业务含义。

五、前端监控

        一般来讲一个成熟的产品,运营与产品团队需要关注用户在产品内的行为记录,通过用户的行为记录来优化产品,研发与测试团队则需要关注产品的性能以及异常,确保产品的性能体验以及安全迭代。

前端监控一般也分为三大类:

5.1 数据监控(监控用户行为)


●PV/UV: PV(page view):即页面浏览量或点击量;UV:指访问某个站点或点击某条新闻的不同 IP 地址的人数
●用户在每一个页面的停留时间
●用户通过什么入口来访问该网页
●用户在相应的页面中触发的行为,等...
统计这些数据是有意义的,比如我们知道了用户来源的渠道,可以促进产品的推广,知道用户在每一个页面停留的时间,可以针对停留较长的页面,增加广告推送等等。

5.2 性能监控(监控页面性能)


●不同用户,不同机型和不同系统下的首屏加载时间
●白屏时间
●http 等请求的响应时间
●静态资源整体下载时间
●页面渲染时间
●页面交互动画完成时间,等...
这些性能监控的结果,可以展示前端性能的好坏,根据性能监测的结果可以进一步的去优化前端性能,尽可能的提高用户体验。

5.3 异常监控(监控产品、系统异常)

及时的上报异常情况,可以避免线上故障的发上。虽然大部分异常可以通过 try catch 的方式捕获,但是比如内存泄漏以及其他偶现的异常难以捕获。常见的需要监控的异常包括:
●Javascript 的异常监控
●样式丢失的异常监控

六、埋点上报

实现前端监控,第一步肯定是将我们要监控的事项(数据)给收集起来,再提交给后台进行入库,最后再给数据分析组进行数据分析,最后处理好的数据再同步给运营或者是产品。数据收集的丰富性和准确性会直接影响到我们做前端监控的质量,因为我们会以此为基础,为产品的未来发展指引方向。
现在常见的埋点上报方法有三种:手动埋点、可视化埋点、无埋点


6.1 手动埋点


手动埋点,也叫代码埋点,即纯手动写代码,调用埋点 SDK 的函数,在需要埋点的业务逻辑功能位置调用接口,上报埋点数据,像**[友盟][百度统计]**等第三方数据统计服务商大都采用这种方案。手动埋点让使用者可以方便地设置自定义属性、自定义事件;所以当你需要深入下钻,并精细化自定义分析时,比较适合使用手动埋点。
手动埋点的缺陷就是,项目工程量大,需要埋点的位置太多,而且需要产品开发运营之间相互反复沟通,容易出现手动差错,如果错误,重新埋点的成本也很高。


6.2 可视化埋点


通过可视化交互的手段,代替上述的代码埋点。将业务代码和埋点代码分离,提供一个可视化交互的页面,输入为业务代码,通过这个可视化系统,可以在业务代码中自定义的增加埋点事件等等,最后输出的代码耦合了业务代码和埋点代码。
可视化埋点的缺陷就是可以埋点的控件有限,不能手动定制。


6.3 无埋点


无埋点则是前端自动采集全部事件,上报埋点数据,由后端来过滤和计算出有用的数据。优点是前端只要一次加载埋点脚本,缺点是流量和采集的数据过于庞大,服务器性能压力山大。
佳佳
HTML
COBUB
PHP
无码埋点

七、SDK实现

数据收集以及数据上报的方式都有了,那么就可以开始实现SDK了,这里先以单文件(非工程化)进行实现

7.1 非工程化

// JS 完整代码部分
(function (e) {
  function wrap(event) {
    const fun = history[event];
    return function () {
      const res = fun.apply(this, arguments);

      const e = new Event(event);

      window.dispatchEvent(e);

      return res;
    };
  }

  class TrackingDemo {
    constructor(options = {}) {
      // 重写 pushState、replaceState
      window.history.pushState = wrap("pushState");
      window.history.replaceState = wrap("replaceState");

      // 上报地址
      this.reportUrl = options.reportUrl || "";
      this.sdkVersion = "1.0.0";

      this._eventList = ["click", "dblclick", "mouseout", "mouseover"];

      this._dulation = {
        startTime: 0,
        value: 0,
      };
      this._initJSError();

      // 初始化事件数据收集
      this._initEventHandler();

      // 初始化PV统计
      this._initPV();

      this._initPageDulation();
    }

    setUserId(uid) {
      this.uid = uid;
    }

    _initEventHandler() {
      this._eventList.forEach((event) => {
        window.addEventListener(event, (e) => {
          const target = e.target;
          const reportKey = target.getAttribute("report-key");
          if (reportKey) {
            this._report("event", {
              tagName: e.target.nodeName,
              tagText: e.target.innerText,
              event,
            });
          }
        });
      });
    }

    _initPV() {
      window.addEventListener("pushState", (e) => {
        this._report("pv", {
          type: "pushState",
          referrer: document.referrer,
        });
      });

      window.addEventListener("replaceState", (e) => {
        this._report("pv", {
          type: "replaceState",
          referrer: document.referrer,
        });
      });

      window.addEventListener("hashchange", () => {
        this._report("pv", {
          type: "hashchange",
          referrer: document.referrer,
        });
      });
    }

    _initPageDulation() {
      let self = this;

      function initDulation() {
        const time = new Date().getTime();
        self._dulation.value = time - self._dulation.startTime;

        self._report("dulation", {
          ...self._dulation,
        });

        self._dulation.startTime = time;
        self._dulation.value = 0;
      }

      // 首次进入页面
      window.addEventListener("load", () => {
        // 记录时间
        const time = new Date().getTime();
        this._dulation.startTime = time;
      });

      // 单页应用页面跳转(触发 replaceState)
      window.addEventListener("replaceState", () => {
        initDulation();
      });

      // 单页应用页面跳转(触发 pushState)
      window.addEventListener("pushState", () => {
        initDulation();
      });

      // 非单页应用跳转触发 popstate
      window.addEventListener("popstate", () => {
        initDulation();
      });

      // 页面没有任何跳转, 直接关闭页面的情况
      window.addEventListener("beforeunload", () => {
        initDulation();
      });
    }

    _initJSError() {
      window.addEventListener("error", (e) => {
        this._report("error", {
          message: e.message,
        });
      });

      window.addEventListener("unhandledrejection", (e) => {
        this._report("error", {
          message: e.reason,
        });
      });
    }

    // 用户可主动上报
    reportTracker(data) {
      this._report("custom", data);
    }

    _getPageInfo() {
      const { width, height } = window.screen;
      const { userAgent } = navigator;
      return {
        uid: this.uid,
        title: document.title,
        url: window.location.href,
        time: new Date().getTime(),
        ua: userAgent,
        screen: `${width}x${height}`,
      };
    }

    _report(type, data) {
      const reportData = {
        ...this._getPageInfo(),
        type,
        data,
        sdk: this.sdkVersion,
      };

      if (navigator.sendBeacon) {
        navigator.sendBeacon(this.reportUrl, JSON.stringify(reportData));
      } else {
        const imgReq = new Image();
        imgReq.src = `${this.reportUrl}?params=${JSON.stringify(
          reportData
        )}&t=${new Date().getTime()}`;
      }
    }
  }
  e.TrackingDemo = TrackingDemo;
})(window);
// HTML 代码部分
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button report-key="button">按钮</button>
</body>
<script src="./tackerDemo.js"></script>
<script>
    const trackingDemo = new TrackingDemo()
</script>
</html>

实现方式就是将此文中 数据收集 部分的代码整合成一个类。

7.2 工程化

如果项目是模块化开发的话,就需要打包工具进行打包,以便支持esm、cjs和umd三种方式,同时工程化还便于维护。

这里使用rollup进行打包,以及使用typescript进行类型约束。

GitHub - GaLiDunGuide/TrackerDemo

7.3 发布NPM包

"files": ["dist"],  // 发布 npm 时需要上传的目录
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
"browser": "dist/index.js",

八、为什么都用GIF来做埋点?

向服务器端上报数据,可以通过请求接口,请求普通文件,或者请求图片资源的方式进行。只要能上报数据,无论是请求GIF文件还是请求js文件或者是调用页面接口,服务器端其实并不关心具体的上报方式。那为什么所有系统都统一使用了请求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%的流量。
并且大多采用的是1*1像素的透明GIF来上报
1x1像素是最小的合法图片。而且,因为是通过图片打点,所以图片最好是透明的,这样一来不会影响页面本身展示效果,二者表示图片透明只要使用一个二进制位标记图片是透明色即可,不用存储色彩空间数据,可以节约体积。

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

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

相关文章

python-pytorch获取FashionMNIST实际图片标签数据集

在查看pytorch官方文档的时候&#xff0c;在这里链接中https://pytorch.org/tutorials/beginner/basics/data_tutorial.html的Creating a Custom Dataset for your files章节&#xff0c;有提到要自定义数据集&#xff0c;需要用到实际的图片和标签。 在网上找了半天没找到&a…

Jenkins用户角色权限管理

Jenkins作为一款强大的自动化构建与持续集成工具&#xff0c;用户角色权限管理是其功能体系中不可或缺的一环。有效的权限管理能确保项目的安全稳定&#xff0c;避免敏感信息泄露。 1、安装插件&#xff1a;Role-based Authorization Strategy 系统管理 > 插件管理 > 可…

java入门学习Day01

本篇文章主要是学会如何使用IDEA&#xff0c;和运行第一个java文件。 java环境安装&#xff1a;Windows下Java环境配置教程_windows java环境配置-CSDN博客 IDEA安装&#xff1a;IDEA 2023.2.5 最新激活码,注册码&#xff08;亲测好用&#xff09; - 异常教程 以上两个链接…

Docker中常见的命令行

1 docker的全部命令 docker attach #连接到正在运行中的容器 docker build #使用 Dockerfile 创建镜像 docker builder #管理builds docker builder prune #清除build缓存 docker checkpoint #管理checkpoints docker checkpoint create #从正在运行的容器创建检…

数据结构与算法 单链表的基本运算

一、实验内容 编写一个程序实现&#xff0c;实现单链表的各种基本运算&#xff08;假设单链表的元素类型为char&#xff09;&#xff0c;并以此为基础设计一个程序完成下列功能&#xff1a; 初始化单链表&#xff1b;采用尾插法依次插入元素a&#xff0c;b&#xff0c;c&…

小米HyperOS 澎湃os机型免答题 免社区等级 秒接bl锁操作步骤解析

小米机型解锁bl 绕过社区等级5才可以解锁的限制的教程_没有五级社区怎么解锁bl-CSDN博客 上次解析了小米有些出厂不是HyperOS系统的机型绕社区等级接bl锁的操作。目前有更新出厂为HyperOS系统的机型免社区登录等级限制 免答题解锁bl的操作。而且有网友在米14 平板6sp k70这些新…

SCI一区顶刊优化算法改进:基于强化学习的神经网络算法RLNNA,你绝对没见过,非常新颖!

声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类智能优化算法及其改进的朋友&#xff0c;可关注我的公众号&#xff1a;强盛机器学习&#xff0c;不定期会有很多免费代码分享~ 目录 神经网络优化算法NNA&#xff1a; 基于强化…

vue2项目设置浏览器标题title及图标logo

工作中肯定会遇到要修改网页的标题title及图标logo 一、固定设置标题方案 方法一&#xff1a;在vue.config.js文件&#xff0c;添加如下代码&#xff1a; chainWebpack: config > {// 配置网页标题config.plugin(html).tap((args) > {args[0].title 标题return args})…

LoadBalance 负载均衡服务调用

前身:Ribbon LB负载均衡(Load Balance)是什么 简单的说就是将用户的请求平摊的分配到多个服务上&#xff0c;从而达到系统的HA&#xff08;高可用&#xff09;&#xff0c;常见的负载均衡有软件Nginx&#xff0c;LVS&#xff0c;硬件 F5等 spring-cloud-starter-loadbalancer组…

Bash and a Tough Math Puzzle 线段树维护区间gcd

还是一道很不错的题目&#xff0c;很容易想到用一棵线段树来维护区间gcd 注意用倍数来剪枝就好了&#xff0c;很是一到很好的题目的 #include<iostream> #include<vector> using namespace std; const int N 5e510; int n,q; struct Segment{int l,r;int d; }tr[…

AI智聊功能支持生成旅游攻略、作文、标题优化,方便视频剪辑

在快节奏的生活中&#xff0c;我们总是需要快速、准确地获取所需信息。无论是撰写旅游攻略、作文&#xff0c;还是准备演讲稿&#xff0c;AI智聊都能为您一键生成精彩文案&#xff0c;让您的创意无限发挥&#xff01; 媒体梦工厂的AI智聊功能&#xff0c;利用先进的自然语言处…

python Flask扩展:如何查找高效开发的第三方模块(库/插件)

如何找到扩展以及使用扩展的文档 一、背景二、如何寻找框架的扩展&#xff1f;三、找到想要的扩展四、找到使用扩展的文档五、项目中实战扩展 一、背景 刚入门python的flask的框架&#xff0c;跟着文档学习了一些以后&#xff0c;想着其实在项目开发中&#xff0c;经常会用到发…

神经网络:梯度下降法更新模型参数

作者&#xff1a;CSDN _养乐多_ 在神经网络领域&#xff0c;梯度下降是一种核心的优化算法&#xff0c;本文将介绍神经网络中梯度下降法更新参数的公式&#xff0c;并通过实例演示其在模型训练中的应用。通过本博客&#xff0c;读者将能够更好地理解深度学习中的优化算法和损…

Android-Handler详解_原理解析

为了方便阅读将文章分为使用篇和源码解析两篇&#xff0c;上一篇已经写了Handler是什么、有什么、怎们用&#xff0c;这一片从源码的角度分析完整流程&#xff0c;看看Handler消息机制到底是啥原理。才疏学浅&#xff0c;如有错误&#xff0c;欢迎指正&#xff0c;多谢。 完整…

XR虚拟直播间,引领创新风潮,打破直播局限!

随着互联网技术日新月异的发展&#xff0c;直播行业也迎来了蓬勃发展的春天。然而&#xff0c;大多数直播间在吸引观众眼球和延长用户观看时长方面&#xff0c;仍然面临着巨大的挑战。正是在这样的背景下&#xff0c;XR虚拟直播系统应运而生&#xff0c;以其多维度的直播场景、…

[flask]异常抛出和捕获异常

Python学习之Flask全局异常处理流程_flask 异常处理-CSDN博客 读取文件错误 OSError: [Errno 22] Invalid argument:_[errno 22] invalid argument: ..\\data\\snli_1.0\\-CSDN博客 异常触发 assert触发异常&#xff1a; 在Python中&#xff0c;使用assert语句可以检查某个条…

Altium Designer 22 DRC规则检查解析(转载)

原文链接&#xff1a;Altium Designer 22 DRC规则检查解析 - 哔哩哔哩 (bilibili.com) PCB Designer Rules 即用来约束PCB走线或布局的规则条例。通常有如&#xff0c;PCB板厂所提供给你的最小线宽线距参数、最小字符宽高度、最小VIA&PAD内外径参数等&#xff0c;需要设计…

解决kubesphere流水线docker登陆错误http: server gave HTTP response to HTTPS client

kubesphere DevOps流水线中&#xff0c;在登录私有的harbor仓库时&#xff0c;报以下错误 docker login 111.230.19.120:80 -u admin -p test123. WARNING! Using --password via the CLI is insecure. Use --password-stdin. Error response from daemon: Get "https://…

java调用jacob进行文件转换ppt转pdf或者png

java调用jacob进行文件转换ppt转pdf或者png 前情提要 最近项目上&#xff0c;遇到一个复杂的ppt&#xff0c;最终要求是要将ppt每一页转成图片原本这个是不难&#xff0c;网上一搜一大堆案例&#xff0c;外加我本身也比较精通aspose&#xff0c;那还不是分分钟搞定。结果就是…

外星人m18R2国行中文版原厂预装23H2原装Win11系统恢复带F12恢复重置

戴尔外星人m18R2国行中文版原厂预装23H2系统恢复安装 远程恢复安装&#xff1a;https://pan.baidu.com/s/166gtt2okmMmuPUL1Fo3Gpg?pwdm64f 提取码:m64f 1.自带原厂预装系统各驱动&#xff0c;主题&#xff0c;Logo,Office带所有Alienware主题壁纸、Alienware软件驱动 2.带…