• 作者:老汪软件技巧
  • 发表时间:2024-09-22 04:01
  • 浏览量:

首先回答一下什么是埋点:

埋点是一种用于跟踪用户在网站或应用中行为的数据采集技术,通过记录点击、浏览等操作,帮助团队进行用户行为分析、AB 实验、错误监听,指导优化方向和资源分配

为了让读者能够从头到尾彻底学会埋点,我会分别从 埋点的概念与使用、如何实现一个埋点 sdk 入手

这篇文章核心围绕埋点的概念与使用展开,涉及到几个维度:

我们直接进入主题,相信能给大家带来不少的收获

一、埋点的基本概念

在业务开发中,埋点是一定要做的,那作为一名合格的研发,我们先要了解埋点都有什么类型,可以有效解决什么问题,这一部分的内容比较基础,网上也有很多精彩的文章,有一定基础的同学可以直接跳过~

我们会从几个维度进行基本概念的介绍:

监控类型上报方式埋点的生命周期(从创建到数据消费)1. 监控类型

基于我们要监控的内容,可以分为:数据监控、性能监控、异常监控等三个部分

我们先讲述一下几个类型的基本概念,后面会详细讲解如何实现的

1.1 数据监控特点:

数据监控主要指对用户行为、业务数据等进行埋点监控,采集并上报关键信息。他的目的是:

=> 通过收集用户在系统中的行为数据,帮助产品经理、运营人员更好地分析用户行为,优化产品决策

举例:

例如,在一个电商网站中,当用户点击了“购买”按钮,系统会埋点记录这次点击事件,包括用户 ID、商品 ID、点击时间等,都会被上报到服务器,用于分析转化率、购物路径优化等

优缺点:

缺点:

1.2 性能监控特点:

性能监控关注的是系统性能的表现,重点监控页面的加载时间、渲染时间、资源请求时间。他的目的是:

=> 帮助开发者了解项目的性能数据,为性能优化做导向

举例:

监控页面从开始加载到完全展示各阶段的时间,如 FP、FCP、LCP、JS 初始化、主接口加载等

优缺点:

缺点:

1.3 异常监控特点:

异常监控主要用于捕获系统中的异常情况,包括 JavaScript 错误、接口请求失败、未处理的 Promise 错误、资源加载失败等,它的目的是:

=> 通过及时捕获并上报异常信息,帮助开发者迅速定位问题、解决问题

举例:优缺点:

缺点:

2. 上报方式

在了解了埋点的监控类型后,我们再看看埋点的上报都有哪些方式

2.1 手动上报特点:

手动上报是指我们基于业务需求,在代码中显式地添加埋点逻辑

每当需要记录用户行为或系统事件时,手动调用上报函数,将数据发送至服务器,这种方式的可控性强,开发者可以精确地控制埋点位置和上报时机

举例:

    button.addEventListener('click', () => {
        sendEvent('click_button', { userId: '12345', time: Date.now() });
    });

这种方式能够精确记录用户点击的时间、操作对象等详细信息。

window.addEventListener('load', () => {
    sendEvent('page_view', { page: 'homepage', time: Date.now() });
});

const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting && entry.intersectionRatio > 0.5) {
            sendEvent('component_exposure', { component: 'banner', time: Date.now() });
        }
    });
}, {
    threshold: [0.5] // 超过50%可见时触发
});
const component = document.querySelector('#banner');
observer.observe(component);

IntersectionObserver - Web API | MDN ()

优缺点:

缺点:

2.2 可视化上报特点:

可视化上报是通过图形界面或后台工具来配置埋点,无需开发人员手动编写代码。在业务人员或测试人员通过可视化工具对页面上的元素(如按钮、链接等)进行选择和配置后,系统自动生成埋点逻辑并上报数据。这种方式降低了技术门槛,使非开发人员也能够参与埋点配置

大家可能好奇这种可视化的操作是如何实现的,这个会在后续的全流程实现模块中详细介绍~

举例:优缺点:

缺点:

2.3 自动上报(无埋点)特点:

自动上报(无埋点)是指系统通过框架或插件自动捕获用户行为或系统事件,无需手动埋点

举例:优缺点:

缺点:

当然,一个优秀且完整的企业级埋点系统,是灵活支持各类监控类型的,不同的类型专门服务不同的场景,实现对目标项目的全方位监控

3. 埋点的生命周期

在了解了埋点类型和上报方式后,我们再来学习一下:一个需求中埋点的全流程

如果再精确到研发主要负责的范围,我们可以给出一个更精细化的图:

虽然该流程覆盖了埋点从需求确认到数据消费的各个步骤,但在需求确认、开发自测、数据监控等环节可能存在数据遗漏、埋点不准确、数据质量监控不足等问题:

二、埋点研发流程优化

我们可以通过自动化工具、加强测试覆盖等方式为了提高埋点质量问题,同时确保埋点对性能的影响最小化,至于具体怎么做,下面会给出一些实践经验:

1. 类型代码约束

我们可以通过 Typescript 为埋点的上报提供类型约束,它能带来的收益很明确:

尽可能避免埋点过程中出现数据格式、字段不一致等问题(你要是不管 error,咱也没办法)

了解了收益后,我们再来学习一下类型代码的消费方式,以及一个合理的类型代码结构

我们先提供一套最基本的埋点上报函数的封装(sendEvent):

type LogParams = Record<string, string | number | boolean | undefined | null>;
export const sendEvent = (event: string, params?: LogParams) => {
  // 埋点上传
};

然而这种类型提示非常的粗糙,对于上报时的检测效果很不理想,那如果要实现非常严格的代码检查,我们可以考虑用 ts 类型体操实现升级:

export const sendEvent = extends EventNames>(
  event: T,
  params?: TrackInterfaces[T]
) => {
  // 埋点上传
};
export interface TrackInterfaces {
  main_picture_click: MainPictureClick;
  page_view: PageView;
}
export type EventNames = "main_picture_click" | "page_view";
/**
 * 页面浏览
 */
export interface PageView {
  /**
   * 应用名称
   */
  appName: string;
  /**
   * 平台
   */
  device_platform: string;
  /**
   * 来源
   */
  enter_from: string;
  /**
   * 链接
   */
  page_url: string;
  /**
   * 页面名称
   */
  page_name: string;
}

埋点需求怎么写__学linux之前该学什么基础

可以看到我们通过一个枚举类型,维护了项目所有包含的类型名称,通过 T extends EventNames 获取了函数中具体的事件名,再通过 TrackInterfaces 映射到了具体的一个类型,比如:

// 事件名 page_view => 映射类型 PageView
sendEvent('page_view',{
    enter_form: 'douyin'
    page_name: 'home'
    // 参数……
})

话都说到这里了,我们可以再深入一下,对于通参(公共参数,比如操作系统、手机型号等),如果每次都要传递,是不是有点麻烦,那如果我们要抽离出来,该怎么做呢?代码如下:

export interface TrackInterfaces {
  main_picture_click: MainPictureClick;
  page_view: PageView;
}
export type EventNames = "main_picture_click" | "page_view";
// 公共属性
type CommonParams = {
  device_platform: string;
  // ……
};
type trackParamsextends EventNames> = Partial<CommonParams> & Omit<TrackInterfaces[T], keyof CommonParams>;
export const sendEvent = extends EventNames>(
  event: T,
  params?: trackParams[T]
) => {
  // 埋点上传
};

通过这种方式,我们把通参从具体的类型中剔除,可以放在一个独立的方法(比如叫 addCommonParams)中维护(一个变量,如 commonParams),在其他事件上报时插入即可

话说回来,维护一个类型代码结构也是相当困难啊,尤其是事件很多的时候,这种情况下,就必须要我们通过一些自动化工具来实现埋点数据转类型代码了

这个问题是我在团队中遇到的,基于这个问题,我开发了一套基于 VSCode 的埋点类型转换插件,核心借助 quick_type 实现,核心的实现逻辑如下:

从埋点管理平台获取事件数据结构 => 事件数据格式解析 => 投喂 quick_type 进行转换 => 进一步加工成合适的格式 => 插入项目代码

大家要是感兴趣也可以实现一下,后续我会考虑把这个插件重写一份并开源出来,也可以用 sdk 的方式提高适用面

2. 埋点测试用例

测试用例可以用很多种方式进行,比如单元测试、抓包测试等,其中单元测试可以通过如 Jest 或 Mocha 实现,存在一定 coding 成本,我们这边展开讲讲抓包测试的实现方式

最简单粗暴的,我们可以使用浏览器开发者工具、Charles 等工具,捕获网页上的网络请求,观察上报的参数是否符合埋点事件的要求

然而这样的方式比较笨,批量检查起来会比较麻烦,因此一般的埋点管理平台会提供一个界面,能够实时显示埋点上报的具体内容,类似于抓包工具,可以有效地提高测试阶段的效率与准确率

对于流程的优化,还有很多的思路,比如聚合上报埋点事件(单位时间内事件打包成一个请求进行上报),可以有效降低请求带来的性能影响,具体的实现就先不在这里详细讲述了

三、埋点数据的消费经验

高效消费埋点数据是数据驱动决策的重要环节,下面我将从多个角度来解释如何高效消费埋点数据,并进行有效的场景分析

等等,你说这都是产品或数据分析师的活?研发应该努力跳出自己的舒适圈,从各方面提高自己的业务能力、解决问题的能力,尤其在如今数据驱动业务的环境下,能做好这些的研发会更有核心竞争力

PV 和 UV

我们可以通过 PV 和 UV 评估不同页面的访问量。可以通过聚合查询每日/每周的 PV 和 UV 进行趋势分析:

SELECT page_name, COUNT(DISTINCT user_id) AS UV, COUNT(*) AS PV
FROM page_view_events
WHERE date BETWEEN '2024-09-01' AND '2024-09-08'
GROUP BY page_name;

当然 SQL 这一步,埋点平台都会做好了,我们不用关心具体的实现,这里展示只是为了更好说明逻辑

事件行为分析

事件行为分析通过埋点捕捉用户的操作习惯和行为路径,是我们最常用的分析模式,常用的操作如下:

1. 分析 click_button 事件中按钮名称为某个具体值的 PV

SELECT 
    button_name, 
    COUNT(*) AS PV
FROM events
WHERE event_name = 'click_button'
AND button_name = 'submit'  -- 按钮名称为 'submit'
GROUP BY button_name;

结果:

假设返回的 PV 为 500,表示 submit 按钮被点击了 500 次

2. 分析 purchase 事件的用户停留时长的平均数和 90 分位数

90 分位数(90th percentile)表示的是在一组数据中,90% 的数值小于等于这个数值,而另外 10% 的数值大于这个数值

SELECT 
    AVG(duration) AS avg_duration,  -- 计算平均持续时间
    PERCENTILE_CONT(0.9) WITHIN GROUP (ORDER BY duration) AS p90_duration  -- 计算 90 分位数
FROM purchase_events
WHERE event_name = 'purchase';

结果:路径转换分析

路径转换分析用于跟踪用户从进入系统到完成某个目标(如购买、注册、下单)的转化过程,可以查看用户经过的关键步骤,发现在哪一步可能有大量用户流失

转换分析应该按用户 ID 逐个跟踪,确保每个用户的行为链可以被完整记录下来

假设我们有一个电商平台,目标是分析从用户进入首页到完成购买的路径转化率

步骤 1: 定义事件表结构

假设数据库中存储了用户的事件数据,每个事件都有 user_id 和 event_name:

CREATE TABLE user_events (
    user_id VARCHAR(50),   -- 用户 ID
    event_name VARCHAR(50),  -- 事件名,如 "page_view"、"add_to_cart"
    event_time TIMESTAMP   -- 事件发生时间
);

步骤 2: 查询每个用户在各个步骤的转化情况

为了简单起见,假设路径有三个关键步骤:

下面的 SQL 查询可以计算每个用户是否完成了从 page_view 到 add_to_cart 再到 purchase 的全路径。

SELECT 
    user_id,
    MAX(CASE WHEN event_name = 'page_view' THEN 1 ELSE 0 END) AS viewed_product,
    MAX(CASE WHEN event_name = 'add_to_cart' THEN 1 ELSE 0 END) AS added_to_cart,
    MAX(CASE WHEN event_name = 'purchase' THEN 1 ELSE 0 END) AS purchased
FROM user_events
WHERE event_name IN ('page_view', 'add_to_cart', 'purchase')
GROUP BY user_id;

步骤 3: 计算路径中的转化率

上面的查询会生成一个结果表,显示每个用户是否完成了某个操作步骤。基于这些数据,可以进一步计算转化率。例如,下面的 SQL 可以计算从查看产品到加入购物车再到完成购买的转化率。

WITH conversion_data AS (
    SELECT 
        user_id,
        MAX(CASE WHEN event_name = 'page_view' THEN 1 ELSE 0 END) AS viewed_product,
        MAX(CASE WHEN event_name = 'add_to_cart' THEN 1 ELSE 0 END) AS added_to_cart,
        MAX(CASE WHEN event_name = 'purchase' THEN 1 ELSE 0 END) AS purchased
    FROM user_events
    WHERE event_name IN ('page_view', 'add_to_cart', 'purchase')
    GROUP BY user_id
)
SELECT
    COUNT(*) AS total_users,
    SUM(CASE WHEN viewed_product = 1 THEN 1 ELSE 0 END) AS total_viewed_product,
    SUM(CASE WHEN viewed_product = 1 AND added_to_cart = 1 THEN 1 ELSE 0 END) AS total_added_to_cart,
    SUM(CASE WHEN viewed_product = 1 AND added_to_cart = 1 AND purchased = 1 THEN 1 ELSE 0 END) AS total_purchased,
    (SUM(CASE WHEN viewed_product = 1 AND added_to_cart = 1 THEN 1 ELSE 0 END) * 100.0) / SUM(CASE WHEN viewed_product = 1 THEN 1 ELSE 0 END) AS cart_conversion_rate,
    (SUM(CASE WHEN viewed_product = 1 AND added_to_cart = 1 AND purchased = 1 THEN 1 ELSE 0 END) * 100.0) / SUM(CASE WHEN viewed_product = 1 AND added_to_cart = 1 THEN 1 ELSE 0 END) AS purchase_conversion_rate
FROM conversion_data;

输出结果:

最终的结果一般会以漏斗图的形式展现,如:

通过这些结果,我们可以直观地看到每个关键节点的用户流失情况,并根据这些转化率数据进一步优化流程或产品设计 比如,add_to_cart 的转化率较高,但 purchase 的转化率较低,这可能意味着在支付环节出现了问题

异常值与极端行为分析

有时,我们需要分析极端的用户行为(如长时间不操作或过于频繁操作)。通过分析这些异常行为,研发可以定位到系统中的潜在问题或优化点

例如,某个用户反复多次点击某个按钮,这可能是系统卡顿或用户困惑导致的行为。

 SELECT user_id, COUNT(*) AS click_count
FROM event_log
WHERE event_name = 'click_button'
GROUP BY user_id
HAVING click_count > 10;

数据变更归因

在埋点数据分析过程中,数据变更归因也是非常重要的,归因分析的场景可以应用于多种情况,如性能优化效果、A/B 测试结果等

以下是几个常见的数据变更归因分析的应用场景:

1. 某次性能优化,1.5 秒开率增长远大于 2 秒开率增长

在一次性能优化后,发现产品的秒开率(即页面在 1.5 秒内完全加载的比例)有了显著提升,但相比之下,2 秒内的开率增长较为缓慢

其中,优化前 2s 开率 > 50%

用户的秒开率呈近似正态分布的形式,一次性能优化可以理解为分布左移,表现如下(图画的有点粗糙,请见谅):

可见有时候数据分析是需要经过严谨的推论才能给出置信的答案的

这时候又要问了:这不应该是数据分析师(DA)做的事吗?汇报的时候 DA 不一定会帮你分析,很多时候都得依赖自己,还是那个标准:跳出自己的舒适圈~

2. A/B 实验增长、下跌分析归因

A/B 实验是常用的用增手段,用来验证某个新功能或设计的效果(在 C 端业务比较常见)

通过对比 A 组(原始版本)和 B 组(新版本)的转化率,可以分析出某些指标的增长或下跌原因

我们可以分析两个版本在关键指标上的表现差异,找出新版本带来的改进点或不足之处

例如:

四、结语

相信这一套下来,大家对埋点的掌握也有一定深度了,考虑到篇幅,我们把埋点 sdk 的实现放在下一篇,同时也会涉及数据存取的优化方案等技术实现,敬请期待!

最后,如果你想加我们的技术社区,想要打破信息差、水群、提问、参与开源,可以加我:Tongxx_yj,欢迎大家的加入!


上一条查看详情 +定时更新UI的好伙伴-TimelineView
下一条 查看详情 +没有了