时隔半年可视化埋点迎来了一次大的版本迭代v2.0。本次迭代在v1.0低代码、可视化的基础上增加了可视化埋点报告的功能,增加了场景保存、Auth接口、关联已有点位的功能,优化了视觉交互,最终达成了埋点免测的目标,同时提高了产研的使用体验。平台整体效果和新增功能详情如下:
支持接口Mock数据及保存
支持多场景并发及切换删除
支持配置Auth接口并上报
支持关联已埋点位
支持显示/编辑已埋点位
支持已埋点位状态显示(异常/历史/新增 )
可视化埋点报告用于跟踪上报点位的状态,达成触发、上报、分析、报告四个环节的闭环,以完成埋点自动验证,实现埋点免测,进一步提高产研人效。目前可视化埋点报告已有功能如下:
分析点位是否已上报
分析点位是否有字段缺失
分析点位是否有多余埋点
分析点位数量是否异常
可视化埋点报告是可以脱离可视化埋点功能独立使用的,但需要手动从神策导入埋点数据来对比分析埋点结果。
通过可视化埋点配置进行分析
通过神策数据下载Excel进行分析
各行业数据分析对用户行为埋点依赖度高,用户行为数据对业务流程转化、用户体验、成交量提升等都能有很好的反馈和跟踪
硬编码埋点代码侵入深、点位多、代码冗余、验证难、交付流程长
需求迭代过程修改埋点难,研发需要mock具体业务场景,在一个页面中的众多埋点中修改几个参数并验证,风险高,成本大
特定情况下,为了一个埋点参数,需额外增加大量代码来实现埋点上报,变相加大了工程复杂度,随着版本迭代,后期的维护成本也会越来越高
埋点人工测试成本大,状态和数据正确性难保证
为了解决以上问题,可视化全自助无痕埋点平台通过无代码、全自助、可视化的方式来完成用户行为埋点的配置、发布、验证、管理、分析,让BA、PM、RD、QA都能轻松愉悦的完成埋点需求的交付和用户行为的追踪。
在1.0版本中,要接入可视化埋点还是需要依赖于widgettool.network。这对于接入者也是不小的门槛,对于新的项目还好,如果老项目则面临着改造。以至于2.0版本中着重对这一块进行了优化,减少可视化埋点的依赖性,让接入更加独立化,以减少框架的耦合性。接入网络库的最终目是为了hook接口数据进行关联埋点数据。市面上大概有以下三种方式hook网络数据。
这也是我们最初采用的方式,通过在自己的网络框架中进行拦截,然后分发到全局,已达到埋点的时候拿到接口数据,但是这就存在依赖性,接入成本变得复杂,以至于2.0直接删除了这样的代码。
似乎找到了另一种方式,通过XMLHttpRequest官方提供的onreadystatechange、abort、error、load、loadend、loadstart、progress、timeout几个事件建立监听,对每个接口状态进行管理,也是可以达成目的的。但由于事件管理流程繁琐,会出现边缘情况,导致未考虑到的case无法hook,最终导致埋点的不准确。
这个是最终采用的方式,即Ajax-hook的实现方式,通过全局代理XMLHttpRequest的方式进行hook所有的request/response
通过这种方式的hook即满足了我们的需求,又简化了代码。
interceptApiRequest () {
require('./hookapi')
ah.proxy({
'onRequest': (config, handler) => {
handler.next(config)
},
'onError': (err, handler) => {
handler.next(err)
},
'onResponse': (response, handler) => {
const url = `${response.config.url}`
.replace(`${location.protocol}//${location.host}`, '')
.replace('/api/', '')
const mockResponse =
JSON.parse(sessionStorage.getItem('MOCK_DATA')) &&
JSON.parse(sessionStorage.getItem('MOCK_DATA'))[url]
response.response =
(mockResponse && JSON.stringify(mockResponse)) || response.response
handler.next(response)
this.authApiImpl(url, response)
let { dataset } = window
if (!dataset) {
dataset = {}
dataset[`${url}`] = JSON.parse(response.response)
} else {
dataset[`${url}`] = JSON.parse(response.response)
}
window.dataset = dataset
this.broadcastMsg(window.dataset)
}
})
}
通过onResponse的响应进行数据的分发及mock。
在2.0版本可视化平台,对接口数据的展示和选取做了很大的调整,通过弹窗的展现形式进行埋点的交互,即增加了平台操作范围,又减少了其他无关功能对用户的误导。
优化点:
通过左侧菜单即可完成点位的填充,不仅减少了因复制粘贴而导致的错误,还降低了埋点操作所需要的时间
通过中间菜单即可完成具体点位的配置,自定义参数由原有的手动填写改成了预设列表的方式,防止上传的埋点无法落库
通过右侧菜单则可以完成接口数据关联,展示由原来的一铺到底的方式改成折叠面板,降低了无关操作面积,同时接口字段的展示也着重进行了优化
场景保存和还原对于埋点操作非常重要。生成一个场景传统的方式是通过修改后台数据或Mock接口来完成。一是无法二次利用,二要依赖后端修改来完成,维护成本较大。在2.0版本中通过平台不仅可以进行接口Mock,而且还可以保存业务场景重复使用,解决了埋点时在测试环境中反复Mock数据的问题。
在展示接口的交互上借鉴了Vue.JS DevTools,直接在线编辑接口数据,实时生效。同时支持多接口数据保存为场景的功能,实现场景复用。
saveValue(e) {
const {
item,
parentKey,
api
} = e
let appendKey = ''
for (let i = 0; i < parentKey.length; i++) {
const key = parentKey[i]
if (i !== 0) {
if (!/^[-]?\d+$/.test(key)) {
appendKey += '.'
}
}
if (/^[-]?\d+$/.test(key)) {
appendKey += `[${key}]`
} else {
appendKey += key
}
}
const tmpDatas = this.apiDatas
// 避免选中的接口值是第一层
const startData = appendKey ? 'tmpDatas[api].' + appendKey + '.' : 'tmpDatas[api].'
const reg = /^[0-9]+.?[0-9]*$/
if (reg.test(item.value)) {
if (reg.test(this.currentEditValue)) {
this.currentEditValue = parseInt(this.currentEditValue)
} else {
this.$message.error('字段只能为数字')
return
}
}
const curInterfacePath = `${startData}${item.key} = this.currentEditValue`
eval(curInterfacePath)
this.closeValue(item)
this.setObject({ key: 'apiDatas', val: tmpDatas })
},
这里涉及到一个难点是如何对修改后的字段进行反向保存。因为展示是基于JSON的,虽说通过正则拿到了需要修改的字段,但是对于把修改的值反向保存确实是个难事,因为当前的赋值操作都是string,而不是一个表达式。以上代码可以看到eval这个方法,这个方法其实很少被使用,除非非常必要的情况。
eval() 函数会将传入的字符串当做 JavaScript 代码进行执行。
eval(string)
每次埋点验收不论是在测试环境还是生产环境,都不可避免因人为的失误而导致线上数据的不准。推出埋点报告正是为了解决这一问题,让埋点验收更加自动化。通过自动化对比分析功能来减少埋点缺失、字段缺失、数量异常等问题。
人为的check和自动的check最大的不同就是细节,埋点报告是采用的全对比的方式。要实现全对比,第一步就是数据清洗。
上图简述了基本数据清洗的规则,但对于不同的场景也是需要调整的。当取PM埋点数据时,会发现接口的数据是有多层级的,这和将要对比的数据格式上就会有很大不同。
fullComparison(sensors, tracks) {
const md5 = require('md5')
const sjgArr = this._formateTracks(tracks)
const sensorsArr = JSON.parse(JSON.stringify(sensors))
// 所有的埋点情况
const totalTracks = []
// 正确+缺失参数的埋点的埋点总数,用于拿到额外埋点
let successAddfailure = []
// 多出来的埋点
let extraTracks = null
for (let i = 0; i < sjgArr.length; i++) {
const to = this._sortTo(sjgArr[i], requiredFields)
let res = this._diff(to, sensorsArr)
if (!res.length) {
// 埋点缺失
res = Object.assign(sjgArr[i], {
status: 'failure',
o: {
hadTrack: false,
lostFields: []
}
})
}
if (res.length) {
// 有埋点
const resT = JSON.parse(JSON.stringify(res))
successAddfailure = successAddfailure.concat(resT)
res = this._uniqueSuccessfulTracks(res, 'success')[0]
for (let i = 0; i < to.length; i++) {
if (requiredFields.includes(to[i][0])) {
if ((!res[to[i][0]] && !res[`${to[i][0]}_arr`]) && !res.o) {
res = Object.assign(res, {
status: 'failure',
o: {
hadTrack: true,
lostFields: [{ name: to[i][0], isLost: true }]
}
})
} else if ((!res[to[i][0]] && !res[`${to[i][0]}_arr`]) && res.o) {
res.o.lostFields.push({ name: to[i][0], isLost: true })
}
}
}
}
totalTracks.push(res)
}
报告让我们更加清晰的看到错误所在,找到异常点位,快速修复。
xiaocong,借款前端,负责老客活动、新客活动、平台活动、公益活动、游戏互动的开发,负责可视化埋点平台的研发
xiaopei,借款前端,负责用户权益,活动研发
CC,负责借款前端团队,负责大前端技术委员会