web前端基础打点实现
前言:
在线上项目中,需要统计产品中用户行为和使用情况,从而可以从用户和产品的角度去了解用户群体,从而升级和迭代产品,使其更加贴近用户。 用户行为数据可以通过前端数据监控的方式获得,除此之外,前端代码的稳定性以及性能等也被开发运维人员所关注。 我们需要实现性能监控和异常监控以及时响应现网问题处理定位。其中性能监控包括首屏加载时间、白屏时间、http请求时间和http响应时间。异常监控包括前端脚本执行报错等。 本文针对整个前端监控,以一个具体的业务场景设计适用的方案。本文的主要内容分为:
一、数据监控类型
前端监控可以分为三类:数据监控、性能监控和异常监控。下面我们来一一的了解。
(1)数据监控,顾名思义就是监听用户的行为。常见的数据监控包括:
- PV/UV:PV(page view),即页面浏览量或点击量。UV:指访问某个站点或点击某条新闻的不同IP地址的人数
- 用户在每一个页面的停留时间
- 用户通过什么入口来访问该网页
- 用户在相应的页面中触发的行为
统计这些数据是有意义的,比如我们知道了用户来源的渠道,可以促进产品的推广,知道用户在每一个页面停留的时间,可以针对停留较长的页面,增加广告推送等等。
(2)性能监控
性能监控指的是监听前端的性能,主要包括监听网页或者说产品在用户端的体验。常见的性能监控数据包括:
- 不同用户,不同机型和不同系统下的首屏加载时间
- http等请求的响应时间
- 静态资源整体下载时间
- 页面渲染时间
- 页面交互动画完成时间
这些性能监控的结果,可以展示前端性能的好坏,根据性能监测的结果可以进一步的去优化前端性能,比如兼容低版本浏览器的动画效果,加快首屏加载等等。
(3)异常监控
此外,产品的前端代码在执行过程中也会发生异常,因此需要引入异常监控。及时的上报异常情况,可以避免线上故障的发上。虽然大部分异常可以通过try catch的方式捕获,但是比如内存泄漏以及其他偶现的异常难以捕获。常见的需要监控的异常包括:
- Javascript的异常监控
- 静态资源加载失败异常
二、常用前端埋点方案总结
在上一节中介绍了前端监控的作用,那么如何实现前端监控呢,实现前端监控的步骤为:前端埋点和上报、数据处理和数据分析。首要的步骤就是前端埋点和上报,也就是数据的收集阶段。数据收集的丰富性和准确性会影响对产品线上效果的判别结果。
目前常见的前端埋点方法分为三种:代码埋点、可视化埋点和无痕埋点。下面一一介绍每一种埋点的方法。
(1) 代码埋点
代码埋点,就是以嵌入代码的形式进行埋点,比如需要监控用户的点击事件,会选择在用户点击时,插入一段代码,保存这个监听行为或者直接将监听行为以某一种数据格式直接传递给server端。此外比如需要统计产品的PV和UV的时候,需要在网页的初始化时,发送用户的访问信息等。
代码埋点的优点:
可以在任意时刻,精确的发送或保存所需要的数据信息。
缺点:
工作量较大,每一个组件的埋点都需要添加相应的代码
(2) 可视化埋点
通过可视化交互的手段,代替代码埋点。将业务代码和埋点代码分离,提供一个可视化交互的页面,输入为业务代码,通过这个可视化系统,可以在业务代码中自定义的增加埋点事件等等,最后输出的代码耦合了业务代码和埋点代码。
可视化埋点听起来比较高大上,实际上跟代码埋点还是区别不大。也就是用一个系统来实现手动插入代码埋点的过程。
缺点:
可视化埋点可以埋点的控件有限,不能手动定制。
(3) 自动化埋点
通过监听页面事件,自动收集信息上报。以实现自动化打点上报。前端能感知的事件是巨量的。我们可以选择将所有能自动上报的数据和事件都记录上报,这属于全埋点;也可以通过配置声明的方式,过滤指定的事件元素,或指定上传数据达到针对性上报的效果,这属于声明式埋点。
对于全埋点方案,前端代码逻辑简化,但却会给数据传输和服务器增加压力,给数据清洗等能力提出较高要求;
对于声明式埋点,前端代码需要针对性的做事件元素的关联配置,逻辑都在前端。这仍然要求每新增一个交互都要适配。仍存在无法满足产品运营打点述求的未知场景。
可视化埋点往往应用在营销类的系统中,成本和约束较大。在实际业务项目中,我们一般同时采用代码埋点和自动化埋点协作的方式来实现。
打点类型:
基于埋点目的,这里主要分两类监控,分别用于运维监控定位和业务运营报表。 前端打点,基于打点能力的使用,可以分为自动捕获打点,手动自定义打点,及配置式打点。
自动打点包括“前端错误上报”、“前端性能上报”、“前端dom事件监听上报”;
手动自定义打点则是基于具体的业务场景流程,上报特定的字段信息;
配置式打点属于前端dom事件监听上报的变体。在前端框架上(如vue),添加自定义指令(directive),在钩子函数里将绑定点击事件,在用户点击时将组件上的配置信息上报。比较鸡肋,在自动打点和自定义打点之间。
Vue.directive('pin', {
bind: function (el, data) {
el.addEventListener("click", function() {
reportProcess(data);
}
})
三、 前端业务打点实现机制
该章节我们分别对自动化埋点和代码埋点中的具体技术实现和代码结构做分享。
(1) 自动监听js运行态报错 ———— 前端页面逻辑异常定位
window.onerror = (
message: string | Event,
errorUrl: string,
lineo: number,
coluo: number,
error: unknown
) => {
this.reportJsRunTimeError(message, errorUrl, lineo, coluo, error);
};
(2)自动监听收集资源类错误 ————业务变更及网络问题定位
window.document.addEventListener(
"error",
event => {
this.reportSourceError(event);
},
true
);
(3)页面进入时上报,记录页面性能相关数据——页面加载时间,开发运维使用,持续提升页面性能
window.onload = () => {
this.reportAppPerformance({
navigation: performance.navigation,
timing: performance.timing
});
};
监控访问方式:navigation.type=0&&navigation.redirectCount>0为提交跳转;navigation.type=1为页面刷新;navigation.type=2为页面前进后退。 监控页面性能:https://developer.mozilla.org/zh-CN/docs/Web/API/PerformanceTiming
(4) 页面离开时上报,记录总停留时间——用户停留时间,运营报表使用
window.onbeforeunload = () => {
this.reportAppCloseEvent();
};
(5)页面交互事件自动打点上报 —— 用户页面点击事件,用于运营人员统计关键事件,运维人员追溯用户操作旅程。
domClickListener() {
window.document.addEventListener(
"click",
event => {
this.reportDomInfoDebounce(event);
},
true
);
}
针对各类dom事件和event.target类型,注册不同的监听事件,可以一定程度上缩小监控范围,减少网络和服务器压力。
(6)Vue中通过自定义指令达到声明式埋点——相较原生的过滤,可以更精准,数据更可控。
Vue.directive('biReport', {
bind(el, data) {
const { triggerType } = data;
if (triggerType === 'show') {
this.report(data);
return;
}
el.addEventListener(
'click',
() => {
this.report(data);
},
true,
);
},
// unbind
});
声明式埋点使用示例。 // key表示埋点的唯一标识;act表示埋点方式
<button v-biReport="{triggerType:'click',evnetName: 'testEvent'}">埋点</button>
该方案作为一个公共能力对外提供能够满足绝大部分场景。
代码自定义埋点方案
自定义埋点方案和要监控的运营指标及数据强相关,具体的方案可参考下一节“上报数据”
四、打点上报数据结构及收集时机:
(1)设备信息
export interface BiEnvData {
device_id: string;
device_type: string;
os_type: string;
browser_type: string;
browser_version: string;
}
static genEnvData(): BiEnvData {
const envData = {} as BiEnvData;
envData.device_id = cacheUtil.getDeviceId();
envData.device_type = envUtil.device_type;
…………
return envData;
}
用户的设备信息在页面加载整个周期都是固定的,因此收集时机无影响。 其中device_id可通过浏览器cookie获取;其他设备信息可以通过userAgent获取。
(2)用户信息
信息字段:
export interface BiUserInfo {
openId: string;
protocol?: boolean;
}
信息缓存:
cacheUtil.setBiUserInfo({
protocol: !needSignFlag,
openId: res.openId
});
信息上报:
report(): void {
const user = cacheUtil.getBiUserInfo();
// 用户身份等信息,必须签署协议后上报
if (user?.protocol) {
this.setUserInfo(user);
Object.assign(this.reportData, this.envData);
}
用户关键信息,在全局登录之后,缓存在前端。在上报时如果用户签署过协议,才上报身份和设备信息。
(3)关键统计信息
信息字段:
eventType: string;
pageId: string;
eventId: string;
eventResult: boolean;
eventResultDesc: string;
// sceneId: string;
// senceResult: boolean;
通过eventType区分不同的打点类型,如js_error/dom_event/page_event。分别对应上一节中的埋点数据。 通过pageId区分单页面应用下的各个路由页面。在路由首尾里可以更新设置。
router.afterEach(to => {
operationBiInstance.setPageId(to.name);
});
针对业务运营打点,可以通过eventId/eventResult/eventResultDesc可以进一步细化统计。
(4)单页面应用全局信息
信息字段:
export interface OperationBiAutoData {
initTime: number;
eventTime: number;
orderNo: string;
mercNo: string;
version: string;
}
initTime在进入页面时自动生成;eventTime在数据上报时自动生成;其他字段在进入页面时做前端缓存,在数据上报时从缓存中获取。
(5)具体场景下的监控定位数据
信息字段:
durationTime?: number;
httpError?: HttpError;
jsError?: JSError;
sourceError?: SourceError;
commonPerformance?: CommonPerformance;
domInfo?: DomInfo;
customerInfo?: CustomerInfo;
// 创建订单后的详细订单信息
transOrder?: TransOrder;
这里包含了各个打点场景的详细信息,仅在特定场景下做收集。
(6)自定义埋点的使用。
通过前面的上报数据接口可以发现,绝大部分字段都可以自动收集上报的。仅在针对特定的业务场景,需要设置特定的统计数据和字段。设置打点上报数据最终使用如下:
biInstance
.setMercOrderData(res.transOrder)
.setEventId(bi_config.oper_eventId_config.event_cashier_show_success)
.setEventResult(true)
.report();
可以看出,自定义埋点比声明式埋点更灵活——可以更精确的控制上报时机。
五、打点日志使用
报表统计: 根据eventType/eventId/pageId等枚举值,梳理出数据字典,可以做到各个事件场景的成功率或时延统计; 根据mercNo或orderNo等业务组件,可以统计出相关业务报表。
日志定位: 根据openId/orderNo/deviceId等唯一标志,过滤出同一个用户或订单或设备的一系列打点日志,然后根据具体的监控定位字段,分析出用户的操作行为和异常触发点。以达到日志定位的目的。
其他思考:
页面的停留时间,用户的停留热力图等,理论上也能通过页面事件监听计算,达到自动上报打点的效果。 如何做一个完善通用的打点SDK,可以对外提供哪些埋点能力?