一、为什么需要前端埋点
前端数据埋点的目的是:
获取用户行为以及跟踪产品在用户端的使用情况,并以监控数据为基础,指明产品优化的方向。
前端监控可以分为三类:数据监控、性能监控和异常监控。
(1) 数据监控
数据监控,顾名思义就是监听用户的行为。常见的数据监控包括:
- PV/UV:PV(page view),即页面浏览量或点击量。UV:指访问某个站点或点击某条新闻的不同IP地址的人数
- 用户在每一个页面的停留时间
- 用户通过什么入口来访问该网页
- 用户在相应的页面中触发的行为
统计这些数据是有意义的,比如我们知道了用户来源的渠道,可以促进产品的推广,知道用户在每一个页面停留的时间,可以针对停留较长的页面,增加广告推送等等。
(2) 性能监控
性能监控指的是监听前端的性能,主要包括监听网页或者说产品在用户端的体验。常见的性能监控数据包括:
- 不同用户,不同机型和不同系统下的首屏加载时间
- 白屏时间
- http等请求的响应时间
- 静态资源整体下载时间
- 页面渲染时间
- 页面交互动画完成时间
这些性能监控的结果,可以展示前端性能的好坏,根据性能监测的结果可以进一步的去优化前端性能,比如兼容低版本浏览器的动画效果,加快首屏加载等等。
(3) 异常监控
此外,产品的前端代码在执行过程中也会发生异常,因此需要引入异常监控。及时的上报异常情况,可以避免线上故障的发上。虽然大部分异常可以通过try catch的方式捕获,但是比如内存泄漏以及其他偶现的异常难以捕获。常见的需要监控的异常包括:
- Javascript的异常监控
- 样式丢失的异常监控
二、埋点方案
(1) 代码埋点
代码埋点,就是以嵌入代码的形式进行埋点,比如需要监控用户的点击事件,会选择在用户点击时,插入一段代码,保存这个监听行为或者直接将监听行为以某一种数据格式直接传递给server端。此外比如需要统计产品的PV和UV的时候,需要在网页的初始化时,发送用户的访问信息等。
代码埋点的优点:
- 可以在任意时刻,精确的发送或保存所需要的数据信息。
缺点:
- 工作量较大,每一个组件的埋点都需要添加相应的代码
(2) 可视化埋点
通过可视化交互的手段,代替代码埋点。将业务代码和埋点代码分离,提供一个可视化交互的页面,输入为业务代码,通过这个可视化系统,可以在业务代码中自定义的增加埋点事件等等,最后输出的代码耦合了业务代码和埋点代码。
可视化埋点听起来比较高大上,实际上跟代码埋点还是区别不大。也就是用一个系统来实现手动插入代码埋点的过程。
缺点:
- 可视化埋点可以埋点的控件有限,不能手动定制。
(3) 无埋点
无埋点并不是说不需要埋点,而是全部埋点,前端的任意一个事件都被绑定一个标识,所有的事件都别记录下来。通过定期上传记录文件,配合文件解析,解析出来我们想要的数据,并生成可视化报告供专业人员分析因此实现“无埋点”统计。
从语言层面实现无埋点也很简单,比如从页面的js代码中,找出dom上被绑定的事件,然后进行全埋点。
无埋点的优点:
- 由于采集的是全量数据,所以产品迭代过程中是不需要关注埋点逻辑的,也不会出现漏埋、误埋等现象
缺点:
- 无埋点采集全量数据,给数据传输和服务器增加压力
- 无法灵活的定制各个事件所需要上传的数据
三、前端可利用的事件
(1) 浏览器窗口事件
| 事件名 | 何时触发 |
| load | 页面加载完成时触发 |
| beforeunload | 窗口关闭之前触发 |
| unload | 窗口关闭时触发 |
| focus | 窗口得到焦点时触发 |
| blur | 窗口失去焦点时触发 |
| error | 页面上有脚本报错时触发 |
| resize | 窗口大小改变时触发 |
(2) 鼠标事件
| 事件名 | 何时触发 |
| mousedown | 当在元素上按下鼠标按钮时触发 |
| mouseover | 当在元素上按下鼠标按钮时触发 |
| mouseout | 当鼠标指针移出元素时触发 |
| mouseup | 当在元素上释放鼠标按钮时触发 |
| mousewheel | 当在元素上滚动鼠标滚轮时触发 |
(3) 键盘事件
| 事件名 | 何时触发 |
| keydown | 用户按下按键触发 |
| keypress | 用户按下按键触发 晚于keydown |
| keyup | 用户释放按键时触发 |
(4) 表单事件
| 事件名 | 何时触发 |
| focus/focusin | 表单元素获取焦点时触发 |
| blur/focusout | 表单元素失去焦点时触发 |
| change | 表单元素值被改变时触发 |
| input | 表单元素获得用户输入时触发 |
| select | 表单元素内容被选中时触发 |
| submit | 提交表单时触发 |
(5) 拖放事件
| 事件名 | 何时触发 |
| drag | 元素被拖动时触发 |
| dragstart | 拖动操作开始时触发 |
| dragover | 当元素在有效拖放目标上正在被拖动时触发 |
| dragenter | 当元素已被拖动到目标区域时触发 |
| dragleave | 当元素离开有效目标时触发 |
| drop | 当被拖动元素放置在目标区域时触发Web浏览器常用事件 |
注:前端可以利用其中某些事件埋点分析用户行为并生成前端关于用户行为链路拓扑图,标红的error事件可以捕捉前端错误,可以让前端精准找到线上bug,可以提高bug定位率。
四、前端埋点方案选型和前端上报方案设计
(1) 监控数据
首先我们需要明确一个产品或者网页,普遍需要监控和上报的数据。监控的分为三个阶段:用户进入网页首页、用户在网页内部交互和交互中报错。每一个阶段需要监控和上报的数据如下图所示:
(2) 埋点方案
在实际项目初始阶段考虑到会有一部分系统改动比较大,为了尽量较少用户配置以及少修改代码的原则,所以选取无埋点方式。
(3) 上报周期和上报数据类型
如果埋点的事件不是很多,上报可以时时进行,比如监控用户的交互事件,可以在用户触发事件后,立刻上报用户所触发的事件类型。如果埋点的事件较多,或者说网页内部交互频繁,可以通过本地存储的方式先缓存上报信息,然后定期上报。
上报的数据类型:
{
"elementID": "elm_xxxxx",//触发元素的唯一ID
"useragent": "",//用户的系统
"networkstate": "",//网络等信息
"currenturl": "",//当前url
"fromurl":"",//从哪一个页面跳转到当前页面
"ip": "",//ip
"traceid": "",//链路标识
"fingerprint": "",// 指纹标识
"eventtype": "",//事件类型
"userid": "",//用户
"useriype": "",//用户类型
"parentid": "",// 记录前一个链路
"spanid": "",//可以使用 elementId替代
"timeStamp": "", // 时间戳
"widgettype": '',// 当前元素
}
(4) 埋点和上报举例
/**
* @author: visupervi
* @Date: 2021/3/5 1:21 下午
* @param:
* @return:
* @Description: 通过用户点击行为记录数据
*/
const postPointObj = (data) => $fetch(`http://localhost:8088/apis/setPointData`, data, "post");
document.querySelector("body").addEventListener("click", async (evt) => {
const selector = OptimalSelect.select(evt.target,{
ignore:{
id: true
}
})
const element = document.activeElement.tagName;
const eltType = document.activeElement.type;
const spanId = `spanId_${uuid()}`;
if (element === "BUTTON" || element === "A" || eltType === "button") {
delDomHTML();
let obj = {
userAgent: window.navigator.userAgent,
networkState: window.navigator.connection.effectiveType,
url: window.location.href,
ip: "",
fingerprint: fingerprint,
eventType: "click",
userId: "",
userType: "",
timeStamp: Date.now(),
widgetType: document.activeElement.tagName,
traceId: traceId,
"parentID": "",
spanId: spanId,
};
postPointObj(obj);
console.log("click event", obj);
}
});
(5) 前端埋点系统的前后端通信加密
在上报数据的前后端通信中,需要和server端协商加密机制,利用 OpenSSL库来实现的加密,OpenSSL已经是一个广泛被采用的加密算法。前端可以采用node的crypto模块。
首先来看hash算法,crypto.createHash() 来创建一个Hash实例,可利用的hash算法如下:
- md5
- sha1
- sha256
- sha512
- ripemd160
五、前端监控结果可视化展示系统的设计
当后端得到前端上报的信息之后,经过数据分析和处理,需要前端可视化的展示数据分析后的结果。
暂时先展示拓扑图。