整体的数据监控体系可以简化为下面这张:
这种方式既费时、费力又难以维护,因为如果在后续迭代中不需要统计某个函数的行为,就要找到这个函数的埋点代码手动删除。所以我们要来解决这样的问题,这里需要用到ES6的一些新特性:Proxy和Reflect。目前小程序运行时还不支持这些特性,你可以借助Babel将其转化为ES5语法。
减少用户打开小程序后的等待时间,这部分的性能称为启动性能;提高用户操作小程序的流畅度,这部分的性能称为运行时性能。
异常数据有三种类型:
数据监控体系是一套非常庞大的整体性方案,这节课我只讲述了一些具备通用性的理论和实践方案,在现实工作中这些是最基本、最通用的,你需要掌握。而到了业务层则需要更多定制化方案。这节课,我想强调这样几个重点:
数据分析需要非常深入的领域专属知识甚至特定的岗位,这指的是的对用户数据的分析,包括用户画像数据和行为数据,这些数据会影响产品的决策和迭代策略。而性能数据和异常数据可以认为是纯技术范畴内的概念。
经过以上改造,每当调用Page的API时都会上报数据。但是当调用Page的任何一个API都会上报数据,而大多数情况下只需要统计有限的几个API,所以要为report.js引入一种白名单机制:只有在名单之内的API上报数据。改造的方式也很简单。
export default function report(obj,apilist){
return new Proxy(obj, {
get(target,key,context){
const originHandler = Reflect.get(target,key,context);
// 只代理列表内的函数
if(typeof originHandler === "function"&&apiList.includes(key)){
return function(...args){
reportClientLog({
// ...上报数据
});
originHandler.call(context,...args);
}.bind(context);
}
return originHanlder;
}
});
}
确定了数据模型,接下来就是制定针对每种数据的采集方案。
*强:
到目前为止,我们完成了数据采集的实施方案,当然我们肯定会根据现实业务的需求做出调整和改造,比如制定上报数据的格式规范、上报时机、处理离线数据等细节。
性能数据
用户数据
使用截+片比对的方法应对所有类型的设备,获取相对粗粒度的性能数据;在Android设备上使用性能Trace工具获取更精细的数据。
异常数据和用户数据采集
这里要注意,粗粒度的性能数据并不是没有价值,正向我刚才说的“只有经过对比的性能数据才有意义”,所以数据的精细度对于性能数据真实性的影响并不大。所以结合两种采集方法的优缺点,我们可以得到一个综合方案:
具体到性能数据的采集方法上,主流的有两种:
截+片比对。使用官方提供的性能Trace工具导出数据。
04讲我们学习了如何通过微信开发者工具的体验评分功能,辅助我们优化小程序的性能,优化性能的目标主要有两个:
那么在小程序场景下,把这三种类型数据联系到一起的上层领域就是小程序的每个页面-Page。页面再上一层的领域就是小程序的运行环境。由此我们可以总结出小程序的数据统计所使用的的数据模型,如下所示:
使用Proxy和Reflect实现API代理那块,每次埋点上报的数据由于不用的业务场景也是不同的,这个要传进去??
采集到所需数据之后,然后就是根据这些数据****做分析、决策了。
端侧的代码异常,比如小程序JavaScript脚本的某段逻辑执行报错;服务异常,不过这类异常情况不仅仅是小程序服务端的问题,也可能是用户设备所在网络环境造成的HTTP请求失败;行为异常,最常见的一种就是爬虫脚本频繁地请求某个服务接口。
数据建模,明确需要统计的数据类型。采集方案,制定收集数据的具体措施。
今天的课后作业有一定难度,需要你去学习一些新知识,我们在异常数据和用户数据的采集方案中提到了使用Proxy和Reflect实现API代理,其实还有更优雅、可定制性更高的方式,就是使用TypeScript的装饰器Decorator。今天的课后作业就是:请你尝试使用Decorator实现API的代理。
然后在购物车页面中获取URL中携带的商品ID:
Page({
onLoad(query){
const { id } = query;
}
});
异常数据
性能数据采集
明确了需要采集用户行为路径,下一步就是我开篇提到的埋点,你可以使用一些新技术和特殊技巧摆脱“最脏最累”的代码埋点。
刚刚我提到,异常数据分为代码异常、服务异常和行为异常:
行为异常比如爬虫,在端侧是无法知悉的,防爬防刷是服务器安全保障的一部分,所以行为异常的监控一般都是由服务端承担,你可以把这项工作交给服务端的同事。服务异常的数据来源有两种,一种是用户网络原因导致的请求失败或超时,一种是服务器本身出了问题。第二种与行为异常同样是属于服务端的职责,而在小程序端侧只能够介入第一种异常数据的采集,在采集方案上与代码异常是一致的。
性能数据的采集通常会放在小程序发布前的研发或测试阶段,将其作为自动化测试的一部分。当然这并不是说采集小程序线上的性能数据没价值,而是必要性不足,因为影响线上性能数据的外界因素太多了,用户的网络情况、设备状态等都有可能造成某一时刻的性能数据波动,这种情况下统计的数据大多是没有实际价值的。而在研发或测试阶段往往是在固定的外界环境中进行性能数据的采集,多次抽样取期望值,然后与历史数据进行对比和评估。
你应该也注意到了,上面这段代码不仅加入了白名单机制,而且还把被代理的对象改成了动态的参数,这样便可以适用于任何对象,比如小程序的App和Page对象:
const report = require("./report.js");
// app.js
App = report(App, [
"onShow",
"onLoad",
"onLaunch"
]);
App({
// ...
});
// page.js
Page = report(Page, [
"onShow",
"onHide",
"onLoad",
"gotoCart"
]);
Page({
// ...
});
现实工作中需要根据具体的业务场景进行定制,这里的方案只是提供一种思路。
《精益创业:新创企业的成长思维》里提到的一个循环反馈的创业思想:构建-衡量-学习。通过“构建”把想法变成具体的产品能力,通过“衡量”把产品抽象的市场反馈具象为可量化的数据,再通过“学习”理解这些数据中用户和市场对于产品的反馈情况,及时改善想法后进入下一次循环:
老哥这个统计的方式又给我这个菜鸟长了见识,膜拜大佬
如果使用最原始的代码埋点,需要在两个页面的函数中手动填写埋点代码,如下:
// 商品页
Page({
gotoCart(){
reportClientLog({
// ...上报商品页数据
});
wx.navigateTo({
url: "pages/cart?id=xxx"
});
}
});
// 购物车页面
Page({
onLoad(query){
reportClientLog({
// ...上报购物车页面数据
});
const { id } = query;
}
});
还是以刚才的商品购买链路为例,点击商品页的“购买”按钮会触发跳转购物车,如下:
Page({
gotoCart(){
wx.navigateTo({
url: "pages/cart?id=xxx"
});
}
});
第二种方法能直接获取到各项性能指标的数值,包括启动耗时、下载耗时、渲染耗时……比第一种方法实施的成本低很多,而且数据精准度更高。但目前只能在Android手机上拿到Trace工具的数据,iPhone暂时不支持。
数据“分析”往往需要专业的岗位和人员负责,而对于研发,在数据生命周期中负责的是数据的“统计”,这也是数据监控体系要完成的工作。数据统计也可以细分为两部分:
性能数据能够帮助技术研发人员发现影响应用程序性能的不良因素,然后进行专项优化。异常数据主要的作用是监控线上环境存在的问题,然后根据问题影响面的大小制定告警策略,比如当监控到影响功能逻辑的严重脚本错误,后台监控服务会通过邮件、短信、电话的方式通知责任人督促尽快解决。
不同类型的数据在采集方案上也有一定的不同,其中性能数据、异常数据相对于用户数据来说与业务的关联度并不高,所以对应的采集方案也更具有通用性。而用户数据数据量更庞大、与业务场景的关联性比较高,所以我们尽量提取一些具备普适性的方案讲述。
将以上代码封装为一个独立的JavaScript文件,假设名称为report.js,然后在小程序中引入:
require("./report.js");
Page({
// ...
})
采集方案:自动化工具和API劫持
用户在使用小程序期间的一些交互操作数据,比如点击某个按钮,从页面A切换到页面B;用户的行为踪迹,比如先点击页面A的某个按钮然后点击另一个按钮最后切换到页面B;用户在某个页面的停留时长;用户的留存率;……
前几年,我看了《人人都是产品经理》这本畅销书,我觉得它给了我们一个很有意义的启示:技术之外,多思考产品。而数据对产品的意义很大,拿这节课来说,数据监控体系中,一个很重要的环节就是埋点。应用端侧的开发者,在工作中或多或少地都编写过埋点代码,这部分工作往往因为枯燥也没什么技术含量不被人欢迎,我刚开始工作时也有这种心理,觉得埋点不但累,还毫无意义。可当我深入了解数据带来的价值之后,改变了这种看法。
假如在这条链路中的“购物车”页面出现了异常,我们要采集的并不仅仅是当前页面脚本抛出的异常本身,而是要同时获取到引起异常的前序路径,即“商品页”信息。
精选评论
异常数据和用户数据的采集方案有很多共通之处,所以我把它们放到一起讲。
用户的数据可以分为两种类型:一是静态数据,包括用户的年龄、性别、地域等信息,这些数据叫“用户画像”;二是动态数据,或者称为用户行为数据,这是一个比较宽泛的概念,可以细分为很多子项,比如:
一个应用程序需要统计的数据从类型划为:性能数据、用户数据和异常数据,这三种数据类型同样适用于微信小程序。接下来我们便从这三种类型的数据入手,学习各自涵盖的细节以及三者之间的关联。
上面只是几种相对普遍的用户行为数据子项目,在现实场景中根据业务类型的不同还会演化出更多领域专属的行为数据类型:
明确了数据的意义之后,你还要对数据有充分的了解,这是你搭建针对小程序的数据监控体系的必要前提。
数据的生命周期可以简单地分为两部分:第一是统计,第二是分析。数据分析是一项需要深度专业领域知识的工作,包括数学、统计学以及应对特定业务领域的一些专属学科,比如电商领域的数据分析往往需要一定的经济学知识。很多大厂会专门设立数据科学家的岗位,市场上也有很多付费的数据分析平台,这些科学家或平台甚至可能用到了机器学习,比如Google的BigQueryML。
单独看性能数据没有任何价值,只有对比才能体现出应用程序的性能好坏。我们没有必要过分地追求性能,在保证功能的前提下,结合团队资源分配出合理的成本投入到性能优化上就可以了。
*琳:
用户行为数据的采集同样如此。我们要获取的并不仅仅是用户点击了哪个按钮,还需要采集到这个按钮所在的页面,如果此页面是由其他页面跳转而来还需要采集前序页面的路径信息。这就是为何我们把异常数据和用户数据的采集方案放到一起讲解的主要原因。
数据建模:性能、用户和异常
第一种方法跟06讲的自动化测试类似,在对小程序进行仿真操作的过程中按照一定的频率进行截,然后使用工具进行片比对,从而获取到一些性能数据,比如小程序启动耗时、首屏渲染耗时等等。通过这种方法获取到的性能数据有一个特点,数据的精细度与截的频率和片比对工具的准确性成正比,实施的成本相对比较高。
用Proxy和Reflect实现埋点的思路非常简单:代理小程序的API,在调用API的同时采集数据。以上述案例中用到的小程序Page对象为例,使用Proxy和Reflect实现API代理:
Page = new Proxy(Page, {
get(target,key,context){
const originHandler = Reflect.get(target,key,context);
// 只代理函数
if(typeof originHandler === "function"){
return function(...args){
reportClientLog({
// ...上报数据
});
originHandler.call(context,...args);
}.bind(context);
}
return originHanlder;
}
});
异常数据的采集也可以称为异常监控,采集到异常本身并不是主要目标,更重要的是能够采集到引起异常的用户行为路径。比如对于电商小程序典型的购买商品的链路:用户点击了商品详情页的“购买”按钮,首先跳转到“购物车”页面,然后继续点击“下单”跳转到订单页面,最后点击“支付”调起微信支付。这个过程用户一共需要四个步骤:
文章为作者独立观点,不代表股票交易接口观点