埋点方案

807 阅读9分钟

通常埋点的需求是为了监控系统一系列数据,包括数据监控、性能监控、异常监控。对这些数据进行监控是为了了解用户对产品的使用促进产品的迭代和明确迭代的方向,以及及时发现产品的存在的异常并打上补丁。进行数据采集的流程通常如下图所示:

1.1  数据监控(用户埋点)

这块主要是获取用户的一些行为数据,例如用户在页面停留的时间、跳出率、留存率、用户点击行为、页面跳转行为等。
目前主要的埋点技术:代码埋点、可视化埋点、无埋点

  • 代码埋点
    代码埋点是最经典埋点方式,实施埋点的研发将埋点代码结合到业务代码中,实现用户行为数据的采集。这种埋点方式能采集到非常复杂的行为,尤其是一些非点击的、不可视的行为,必须用代码埋点来实现。代码埋点按照位置的不同,可以分为前端埋点后端埋点。前端埋点用来记录用户在客户端的操作行为,后端埋点用来记录客户端进行服务器请求的日志。

    • 前端埋点
      前端埋点能够收集更全面、精细的用户数据,尤其是不需要请求服务器的行为数据,如:页面停留时长、页面浏览深度、视频播放时长、用户鼠标轨迹、表单项停留及终止等等,只能通过前端埋点实现。不过前端埋点的上报一般存在 15% 左右的延迟上报和漏报(客户端未联网、数据打包上报、用户删除行为数据等原因)

      在应用或界面初始化时,初始化埋点 SDK,在触发某个节点(如事件/页面)时调用  SDK  相应的方法,通过接口发送数据。 前端代码埋点在具体实施中可以分为命令式埋点声明式埋点

      (1)命令式埋点

      const handleClickBye = () => {
        //....业务代码 //采集埋点数据
        GrabAgent.click(...data);
      };
      //装载页面时
      useMount(() => {
        GrabAgent.onResume(...data);
      });
      //卸载页面时
      useUnmount(() => {
        GrabAgent.onPause(...data);
      });
      

      这种方式通常会对代码造成一定的侵入性。

      (2)声明式埋点
      通过在 dom 节点上声明data-{}方式,通过脱耦的方式遍历所有声明过得 dom 节点并绑定上事件,需要上报的数据都放在data-{}里,例如如果是在 Vue 项目中可以通过自定义指令的方式。

      <button v-focus="{type:"click",key:"shop"}" @click=”handleShop”>购买</button>
      
            directives:{
              focus:{
                bind:function(el,binding,vnode){
                  el.addEventListener('click',()=>{
                    GrabAgent.send(binding.value)
                  })
                }
              }
            }
      
    • 后端埋点
      相比于前端埋点,能实时采集数据,不存在延时上报,数据很准确;并且,服务端埋点支持与用户身份信息和行为附带属性信息整合;

    市场上的第三方数据采集均支持代码埋点, 诸葛  IOGA、 GrowingIO神策百度统计TalkingData  等。

    优点:个性化自定义,能够根据企业自身业务特性自定义属性、事件,定制化获取数据.
    缺点:时间、人力成本大。

  • 可视化埋点
    可视化全埋点,是指用户将设备连接到用户行为分析系统的可视化全埋点管理界面,对可交互的图片、按钮等页面元素,直接在界面上进行操作实现数据埋点。例如诸葛 io

    可视化全埋点也只是针对点击可见元素的,一些动态页面、不可见的行为是采集不到的;其次,可视化全埋点无法添加自定义属性,无法和业务关联;第三,可视化全埋点元素只能对可选元素逐个进行选择埋点,操作效率较低。

    👉 诸葛 io 可视化配置指南

  • 无埋点
    无埋点,也叫全埋点、无码埋点、自动埋点。无埋点是指预先收集用户的所有行为数据,然后再根据实际分析需求从中提取行为数据。一般用来做粗粒度的快速业务探索。

    无埋点只能覆盖基本的点击、展示等用户行为,其次,全埋点采集的数据量非常大,随着数据量上升,可能会导致客户端崩溃的概率也会上升。

    👉 诸葛 io 无埋点配置指南


当前流行的第三方数据产品

产品体验
Umeng阿里旗下的数据分析产品,通用性功能均有覆盖,在部分特定页面上有缺失,定制化弱,适合初创起步的企业应用
 Google Analytics体验较好,对个人网页、应用所需的数据埋点都能满足,对数据结果展示较为友好
神策数据可根据企业部署特定服务器,针对个性化定制,并且有对应业务员、开发工程师进行企业一对一对接,服务体验较为良好
诸葛 io国内领先、先行的数据分析公司,2013 年是国内首家最早推出无埋点方案

1.2 性能监控  &  异常监控

🚗 性能监控
主要获取系统的性能参数指标。目前性能指标数据大部分来源于  window.performance API 该 API 提供了如下接口定义的数据。

interface Performance {
  readonly attribute PerformanceTiming timing;
  readonly attribute PerformanceNavigation navigation;
};

//The PerformanceNavigation inteface
interface PerformanceNavigation {
  const unsigned short TYPE_NAVIGATE = 0; //导航开始于点击链接、或者在用户代理中输入 URL、或者表单提交
  const unsigned short TYPE_RELOAD = 1; //通过刷新操作或者 location.reload() 方法导航。
  const unsigned short TYPE_BACK_FORWARD = 2; //通过历史遍历操作导航。
  const unsigned short TYPE_RESERVED = 255; //其他非以上类型的导航。
  readonly attribute unsigned short type;
  readonly attribute unsigned short redirectCount;
};
//The PerformanceTiming interface
interface PerformanceTiming {
 readonly attribute unsigned long long navigationStart;
 readonly attribute unsigned long long unloadEventStart;
 readonly attribute unsigned long long unloadEventEnd;
 readonly attribute unsigned long long redirectStart;
 readonly attribute unsigned long long redirectEnd;
 readonly attribute unsigned long long fetchStart;
 readonly attribute unsigned long long domainLookupStart;
 readonly attribute unsigned long long domainLookupEnd;
 readonly attribute unsigned long long connectStart;
 readonly attribute unsigned long long connectEnd;
 readonly attribute unsigned long long secureConnectionStart;
 readonly attribute unsigned long long requestStart;
 readonly attribute unsigned long long responseStart;
 readonly attribute unsigned long long responseEnd;
 readonly attribute unsigned long long domLoading;
 readonly attribute unsigned long long domInteractive;
 readonly attribute unsigned long long domContentLoadedEventStart;
 readonly attribute unsigned long long domContentLoadedEventEnd;
 readonly attribute unsigned long long domComplete;
 readonly attribute unsigned long long loadEventStart;
 readonly attribute unsigned long long loadEventEnd;
};

注意:

  • 通过 window.performance.timing 所获的的页面渲染所相关的数据,在 SPA 应用中改变了 url 但不刷新页面的情况下是不会更新的。因此仅仅通过该 api 是无法获得每一个子路由所对应的页面渲染的时间。如果需要上报切换路由情况下每一个子页面重新 render 的时间,需要自定义上报。
  • 通过window.performance.getEntries()所获取的资源加载和异步请求所相关的数据,在页面切换路由的时候会重新的计算,可以实现自动的上报。

⚠️ 异常监控
前端异常是指在用户使用 Web 应用时无法快速得到符合预期结果的情况,不同的异常带来的后果程度不同,轻则引起用户使用不悦,重则导致产品无法使用,使用户丧失对产品的认可。 主要收集系统的出错的信息,方便开发快速定位进行  hotfix。如果采用自行开发埋点,则可以利用以下方法,对其做一次封装成库,应用进行捕获异常,并将其上报。

try{}catch(err){//上报错误}
window.onerror
window.addEventListener('error',() => {})
window.addEventListener('unhandledrejection'() => {})

目前主流的前端监控系统主要有  阿里  ARMS 、FUNdebugSentry  等,项目初期在异常监控这一块可以采用  Sentry  进行支持。

上报数据:

  1. sendBeacon方法
    sendBeacon方法在页面销毁期,可以异步的发送数据,因此不会造成类似同步ajax请求那样的阻塞问题,也不会影响下一个页面的渲染.

      navigator.sendBeacon(url [, data]);
    

    data可以为: ArrayBufferView, Blob, DOMString, 或者 FormData 为了发送参数,我们一般data制定为Blob的形式。此外还要注意的是,在sendBeacon的请求头header中,只支持application/x-www-form-urlencoded,multipart/form-data,text/plain 不支持Content-Type为“application/json; charset=utf-8”。一般制定为application/x-www-form-urlencoded

    function sendBeacon(url,data){
    //判断支不支持navigator.sendBeacon
    let headers = {
      type: 'application/x-www-form-urlencoded'
    };
    let blob = new Blob([JSON.stringify(data)], headers);
    navigator.sendBeacon(url,blob);
    }
    
  2. 动态创建img标签的形式
    通过动态创建img标签的形式,指定src属性所指定的url来发送请求,首先不受跨域的限制,其次img标签动态插入,会延迟页面的卸载保证图片的插入,因此可以保证在页面的销毁期,请求可以发生。

  3. 同步ajax post请求
    动态创建img标签的方法,拼接url的时候存在一定的问题,因为浏览器对url的长度是有限制的。而sendBeacon方法兼容性不是很好,最后兜底的处理方式就是发送同步的ajax请求,同步的ajax请求前面说过,会在页面销毁期之前执行,虽然会有一定程度的阻塞下一个页面的渲染。

      function xmlLoadData(url,data) {
      var client = new XMLHttpRequest();
      client.open("POST", url,false);
      client.setRequestHeader("Content-Type", "application/json; charset=utf-8");
      client.send(JSON.stringify(data));
    }
    

👉 采用 Sentry 进行性能监控和异常监控

Sentry  是一个开源的实时错误追踪系统,可以帮助开发者实时监控并修复异常问题。它主要专注于持续集成、提高效率并且提升用户体验。  Sentry  分为服务端和客户端  SDK,可以直接使用其提供的在线服务,也可以本地自行搭建;提供了对多种主流语言和框架的支持,包括  React、Angular、Node、Django、RoR、PHP、Laravel、Android、. NET、JAVA  等。同时它可提供了和其他流行服务集成的方案,例如  GitHub、GitLab、bitbuck、heroku、slack、Trello  等。 在  React  项目中配置  Sentry >

  • 安装  Sentry
yarn add @sentry/react
yarn add @sentry/react @sentry/tracing
  • 在项目入口文件初始化
import React from "react";
import ReactDOM from "react-dom";
import * as Sentry from "@sentry/react";
import App from "./App";
Sentry.init({
  dsn: "https://{key}@sentry.io/{project}",
  release: "my-project-name@" + process.env.npm_package_version,
  integrations: [new Integrations.BrowserTracing()], // We recommend adjusting this value in production, or using tracesSampler // for finer control
  tracesSampleRate: 1.0,
});
ReactDOM.render(<App />, document.getElementById("root"));

其他常用高级使用方法

  1. 主动捕获并上报错误
    有时我们可能需要自己去主动去触发一些错误上报,比如一些特定操作、某些已经被弃用的接口被调用了、捕获一些线上运行数据去排查问题。可以利用 Sentry 提供过的 captureException 或者 captureMessage 去上报错误或者文本信息。

  2. 丰富上报数据上下文
    除了 user 用户信息,还可以有 tags、level、fingerprint、extra data。 比如添加一些 tags,可以使用 scope.setTags(前端,不同语言语法不一样,如 Django 为 sentry_sdk.set_context) 可以给事件定义不同的键/值对。在后台查找的时候,筛选条件选项会多出来一些选项,就是通过 setTags 来设置的这些键值对。