1. 背景
业务发展到一定阶段,我们需要一个埋点监控SDK去做数据的收集,后续再统计分析。有了分析数据,才能有针对性对网站进行优化。 对于前端来说,考虑到埋点逻辑和业务逻辑解耦,并且统一上报。
2. 监控场景
2.1 用户行为监控
负责统计PV(页面访问次数)、UV(页面访问人数)以及用户的点击操作等行为。
2.2 页面性能监控
用户的环境和我们不一样,也许是3G网,也许是很老的机型,我们需要知道在实际使用场景中的性能数据,比如页面加载时间、白屏时间等。
2.3 错误监控
对页面错误error,白屏等场景进行监控。
3. 埋点方式
3.1 手动埋点
手动埋点,顾名思义,在需要上报的地方主动编写相应的上报代码。
3.1.1 手动调用
3.1.2 自定义类指令式埋点实现
该方式适合简单的埋点上报,埋点逻辑与业务逻辑清晰分离,埋点 sdk 给 document 对象加上监听 click / hover 事件触发时,从当前触发事件的 target 逐级向上遍历,查看是否有对应此事件的指令。如果有,则上报此埋点事件,直至遇到一个没有事件指令的元素节点。这样也可以在指令中控制是否要继续向上遍历。
// 类指令式埋点实现逐级上报
<section data-tracking="100023">
<div data-tracking="100008">
<Button>点击</Button>
</div>
</section>
但是如果我们需要在上报事件前,对所上报的数据进行处理,那么这种方式就无法满足了。需要手动调用。
3.2 全埋点
全埋点,对应用中的行为进行无差别的记录上报。
4. 实现分析
4.1 实现流程
埋点主要就是两个步骤:
- 数据收集
- 数据上报
4.2 数据收集
4.3 数据上报
4.3.1 数据格式
export type ActionData = {
type: 'click' | 'pageload' | 'mouseover'; // 事件类型
page_url: string; // 页面地址
title: string; // 页面名称 例如:"首页 - 车队平台"
opts: {
[key: string]: any
}
event_id?: string; // 事件Id projectId+eventTypeId+pageId+locationId 例如'1010101'
event_desc?: string; // 事件描述 例如:"webConvoy-businessOrder-search-click"
btn_name?: string; // 按钮名称
};
export type ErrorData = {
type: 'jsError' | 'networkError'; // 错误类型
pageUrl: string; // 页面地址
message: string; // 错误描述
stack: any: // 错误堆栈
position: string;// 错误位置 ${e.lineno}:${e.colno}
opts: {
[key: string]: any
}
}
// 设备信息
export type DimData = {
os_name: string; //系统类型
os_version: string; //系统版本
browser_name: string; // 浏览器名称
browser_version: string; // 浏览器版本
memory: number; // 内存大小
}
type TrackData = {
app_id: string; // 项目唯一标识
version: string; // 版本
time: string; // 事件发生的时间 2023-09-26 15:25:23
tenant_id: number; // 租户id
use_name: string; // 用户
scene: 'action' | 'error' | 'performance';
dim: DimData;
data: ActionData | ErrorData
};
4.1.1.1 公共参数
| key | 类型 | 描述 |
|---|---|---|
| scene | 'Action' | 'Error' | 'Performance' | 业务场景 |
| app_id | string | 项目唯一标识,例如 车队: web-convoy |
| use_name | string | 用户名,登录之后从localStorage获取 |
| time | string | 事件发生时间, 2023-09-26 15:25:23 |
| tenant_id | number | 租户id, 登录之后从localStorage获取 |
| version | string | 字段更新版本 |
| data | any | 不同类型的自定义数据 |
| dim | DimData 见4.3.1图 | 设备信息 |
4.1.1.2 业务场景(data里的具体信息)
4.1.1.2.1 用户行为-action
用户行为这一类型,收集的主要是以下方面:
- who 什么地方
- time 什么时间
- where 什么地方
- what 做了什么、
| key | 类型 | 描述 |
|---|---|---|
| type | 'click' | 'pageload' | 'hover' | 'scroll'; | 事件类型 |
| event_id | string | 事件Id: |
| projectId(01开头) + pageId(二级路由01开头)+ pointId(元素id 00开头)+ typeId(type的枚举索引 01开头) | ||
| page_url | string | 页面地址 |
| event_desc | string | 描述被点击的元素(webConvoy-orderEdit-submitButton/searchButton) |
| btn_name | string | 按钮名称 |
| title | string | 页面标题 |
| opts | {[key: string]: any} | 扩展参数 |
4.3.2 上报方式
数据上报也就是将数据发送给服务端,考虑到以下两个问题:
- 跨域
- 页面关闭时会中断正在发送的数据
选择使用图片或者navigator.sendBeacon来发送数据**。考虑到navigator.sendBeacon的浏览器支持程度,使用两者结合的方式(如果浏览器不支持sendBeancon,就使用图片的方式)。** **伪代码: **
function send(data) {
const url = `xxxx`
if (navigator.sendBeacon) {
navigator.sendBeacon(url, JSON.stringify(data));
} else {
const imgReq = new Image();
imgReq.src = `${url}?params=${JSON.stringify(
data
)}&t=${new Date().getTime()}`;
}
}
综上,考虑到接入阿里云sls, 本期通过WebTracking的方式上传数据。 接入文档参考:help.aliyun.com/zh/sls/user…
import SlsTracker from '@aliyun-sls/web-track-browser'
const opts = {
host: 'cn-shenzhen.log.aliyuncs.com', // 所在地域的服务入口。例如cn-hangzhou.log.aliyuncs.com
project: 'falcon-bs-daily', // Project名称。
logstore: 'falcon-base-web-event', // Logstore名称。
time: 10, // 发送日志的时间间隔,默认是10秒。
count: 10, // 发送日志的数量大小,默认是10条。
topic: 'topic',// 自定义日志主题。
source: 'source',
tags: {
tags: 'tags',
},
}
const tracker = new SlsTracker(opts);
// 测试埋点
const sendLog = () => {
tracker.send({
customer: 'zhangsan',
product: 'iphone 12',
price: 7998,
})
}
5. 字段详解
5.1 eventId
事件id通过projectId + pageId + pointId + typeId获取
5.1.1 事件类型-typeId
事件类型id映射表:
| id | 含义 | 描述 |
|---|---|---|
| 01 | click | 点击 |
| 02 | pageload | 页面显示 |
| 03 | hover | 鼠标移入 |
5.1.2 项目 - projectId
项目id映射表:
| id | 含义 | 描述 |
|---|---|---|
| 01 | webConvoy | 车队web端 |
| 02 | webAgent | 货代web端 |
| 03 | freight-cp | 网络货运货主端 |
5.1.3 页面 - pageId
以车队为例:精确到二级页面
| id | 含义 | 描述 |
|---|---|---|
| 01 | businessOrder | 业务/业务订单 |
| 02 | financeOverview | 财务/收付总览 |
| ... | ... | ... |
5.1.4 点位 - pointId
具体页面的点位,以车队业务订单为例:
| id | 含义 | 描述 |
|---|---|---|
| 01 | editOrder | 编辑订单 |
| 02 | editFare | 编辑费用 |
| 03 | addOrder | 新增订单 |
| 04 | search | 查询 |
| 05 | reset | 重置 |
| ... | ... | ... |