教师端H5埋点的现状与改进

        数据很重要。用户的行为数据就好比给了我们一双天眼,清晰的观察用户的习惯和需求,同时也能从中看出我们自己产品设计和经营中的一些不合理存在。大数据时代背景下,海量数据喷涌而出,“数据收集——数据整理——数据分析——数据可视化”,势在必行。
        数据第一步:数据收集。实现方式: 埋点。
教师端埋点现状
        提前封装好js片段,当需要统计的行为发生时,触发脚本执行,发送统计信息。
教师端埋点–每个页面统一的配置信息 和 页面统一入口 http://note.youdao.com/noteshare?id=f65209f833f444fbeb06b8a872c56928&sub=9C87991238584D7FA1C54461952DB75F
        教师端埋点–某些页面需要特殊配置的地方,就行单独处理 http://note.youdao.com/noteshare?id=cf762255544e27dc23f5a0a3a6fd7225&sub=96734D456CEA4F208DBF095E7E001EA0
        教师端埋点–stat.js(发送统计信息的方法和一些逻辑处理)http://note.youdao.com/noteshare?id=ae7ae2a06d973e173a294eb38fa34153&sub=3F5F4ECCE54149CB84FF8E825C0D9A7A
        大部分信息在大部分场景是通过统一配置和相应的过滤、映射等方式获取,个别除外。信息部分:进入首页的方式(refer)必须从客户端插件获取,共有登陆,启屏,开机视频,修改密码4种,这个只有客户端自己清楚,而H5只有通过调用原生插件,才能获取到。场景部分:某个按钮的点击(去分享按钮),tab的切换(首页底边栏4个,班级详情顶部栏3个),这些目前是手动触发的,可以参考第二个链接中的代码。
教师端埋点改进
        H5在埋点方面已经实现一定程度上的自动化,统一的配置、统一的入口拦截和发送。又或者当我们又增加了新的页面时,只需在views里做统一配置,然后去stat.js里面作统一映射,就可以了。但是,有些事臣妾做不到。比如IOS的事件有统一管理栈,可以作统一拦截和处理,但H5并没有这样机制,H5只能自己去做标识,再去跟自己自己做的标识作相应的监听处理,它跟我们的需求数据(即收集的数据)有莫大的关联,只能根据BI小组的需求去做相应的特殊处理。这种方式已经在PC的埋点中有一定的实践和应用,确实在一定程度上减少了前端的工作量和以后扩展的成本,这个也可以应用到教师端,作为改进部分方案,可在一定程度上解决“大部分场景”的问题,实现可配置的自动化,当然,相应的js脚本也得跟着变化,需求去执行监听和处罚的操作。
        这些优化只能算是局部优化,离完全自动化的埋点还有很大差距,欢迎老司机们献计献策~
        随着公司的发展壮大,对数据的需求会变化,之前很多额埋点信息会作废,推倒重新来埋不是不可能,为了面对这种情况,埋点的可扩展,可更改,高效率的实现方式很重要,一劳永逸永远是代码的最高境界。但,我们在寻求这个方式的前提下,不得不去考虑
        1、数据的准确性;
        2、性能。
        数据的全面、及时很重要,但数据的准确、可靠才是其价值的根基。
        扯这段没别的意思,只是想提醒一下,所有的埋点的解决方案都必须以数据的准确可靠作为基础。

客户端埋点的一些分析和体会

最近了解很多的关于埋点的相关文章,感受颇多,在此给大家分享一下。有不正确的地方还望各位指正。这篇文章主要分为3部分:

一、埋点的重要性

追踪用户在平台每个界面上的系列行为,事件之间相互独立(如打开商品详情页——选择商品型号——加入购物车——下订单——购买完成);
联合公司工程、ETL采集分析用户全量行为,建立用户画像,还原用户行为模型,作为产品分析、优化的基础。
无疑,数据埋点是一种良好的私有化部署数据采集方式。数据采集准确,满足了企业去粗取精,实现产品、服务快速优化迭代的需求。
但是,因手动埋点工程量极大,且一不小心容易出错,成为很多工程师的痛。且其开发周期长,耗时费力,很多规模较小的公司并不具备自己埋点的能力。无埋点成为市场新宠。最后埋点、无埋点两种技术谁能成为最后赢家,我们拭目以待。

二、埋点的终极方案

埋点的最终理想状态是:不用修改客户端,通过网络进行配置相关数据,就可以达到修改客户端的埋点数据。
原理如下图:111
具体原理:
服务端通过网络配置json数据表(服务端进行数据表的版本控制)
客户端每次开启数据的时候,都会发请求BIVersion,将获取到的版本号和本地存储的版本号进行比较。如果版本号大于本地保存的版本号,根据网络数据,更新本地数据。
客户端每次发送请求的时候,发送的相关数据,和数据结构配置,都通过读取配置表里面的数据(服务端保存的JSon数据)。这样就可以做到只通过网络端进行配置,就可以实现客户端的埋点上传数据更改了。之后维护全部都是维护网络上的数据表就可以了。不用再修改客户端

三、目前项目现状

目前版本的教师端埋点实现方案:

Android客户端,已经实现抽出一个工具类。埋点的时候只需要调用一下工具类方法,并且将需要上传的参数传过去就可以实现埋点。
优点:抽出一个公共方法,可以避免代码的冗余,如果需要修改某一个公共字段,实现了一定的数据解耦和逻辑解耦。
缺点:手动埋点工程量极大,且一不小心容易出错,成为很多工程师的痛。且其开发周期长,耗时费力。

重构版本的教师端埋点实现方案:

在抽出工具类的基础上,对页面进行封装,抽出一个基类,在基类的生命周期中,调用工具类方法,同时最大的改进是所有的埋点需要上传数据和页面埋点发送数据结构,全部都通过本地保存的数据配置表来动态获取,做到了只需要修改数据表,就可以实现BI埋点的更改。
优点:避免代码荣誉,进一步对代码进行了优化。如果需要修改某一个字段,只需要修改配置表就可以,简化了数据操作,对数据和逻辑进行了进一步的解耦,实现了无埋点的理论
缺点:由于目前服务端不支持网络配置表,所以如果配置表修改了,就需要跟新客户端才能获取到最新的数据据,不能做到动态的数据变更。

理想版本的教师端埋点实现方案:

详情请见第二条
优点:所有埋点数据全部从网络获取,可以远程的进行动态配置,远程进行埋点数据更新,无论客户端是否进行更新,都可以做到全部的客户端同时更新。
缺点:再设计数据结构之前,需要做较多的调研,从而设计出一套比较完善合理的网络配置表。如果数据结构变动比较大的话,可能会出现不兼容问题。

关于新的系统架构的感想

通过学习,我了解到了公司目前的架构大致包括网关层,业务微服务层,中间件服务以及底层服务层。网关层主要用来流量控制、降级|容错、API监控。业务微服务层每个微服务均负责某一块垂直的业务,相关业务只会在对应的这一个微服务内部处理,不存在多个微服务处理同一个业务的情况。一些与业务无关的独立的服务,为所有微服务或系统任务提供功能的模块放在中间件服务层。相对旧的系统架构有了大幅度的提升。随着公司的不断壮大,业务的需要,目前的系统架构可以满足要求,不过还需继续优化,可以参照淘宝技术架构经历,打造云技术平台。作为一名菜鸟,我发现自己要学的很多,希望能与公司一起成长,越走越远。

Android教师端阿里埋点分析

一. 爱学习教师端需求分析

教师端当前埋点需求,简单描述就是: 基础数据 , “线” , “点” 三部分;

1.基础数据

BI需要记录用户的当前使用环境的基本数据,举个栗子如:设备IMEI , 经纬度, 客户端当前的毫秒值,设备型号,品牌 …

2.线

收集用户的使用行为,把页面的跳转都可以归纳为”线”的部分,目的了解用户使用app的行为线,在行为线的过程中还伴随着浏览资源数据的收集,例如:用户浏览课件视频的ID

3.点

收集特殊行为,例如:分享

二. 教师端当前埋点情况

根据BI提供的页面ID,自定义属性, 在页面抽取的基类中初始化埋点:

1.初始化

/**
 * 通过EndPoint、accessKeyID、accessKeySecret 构建日志服务客户端
 * @endPoint: 服务访问入口,参见 https://help.aliyun.com/document_detail/29008.html
 */
final LOGClient myClient = new LOGClient("cn-beijing.log.aliyuncs.com", "LTAICRtCfbl0u55m",
        "nrRJLautQTQzQopzePrP2NeKlEZAx9", "axx-logs");

/* 创建logGroup */
final LogGroup logGroup = new LogGroup();

2.抽取页面参数model(减少篇幅,简化代码)

public class AliLogBean {

    private String pad = "";
    private String da_src = "";
    private String rpad = "";
    private String pp2 = "";
}

3.定义数据收集,发射公共方法

/**
 * 阿里统计 2017.5
 * @param aliLogBean
 */
public void aliLog(AliLogBean aliLogBean){
   
    logGroup2.PutLog(log);
    //发送log ,在后台发送数据,以防止阻塞主线程
    Thread t = new Thread() {
        public void run() {
            try {
                myClient.PostLog(logGroup2, "user-log");
            } catch (LogException e) {
                e.printStackTrace();
            }
        }
    };
    t.start();

4.页面发送

QQ截图20170601180956

由于只记录跳转的轨迹,而不记录返回的轨迹,根据android中activity的生命周期,这部分是放在onCreate()中进行的

三. 关于埋点阅读的一些文章的总结

这是阿里云栖社区里  https://yq.aliyun.com/articles/8238   关于埋点的一篇文章内容比较概括, 但观点可以借鉴,主要说到:

1.代码埋点数据收集

我们现有的埋点方案应该就是这个;

优点:数据收集准确性高,可以完全契合需求(页面跳转线数据的收集)

缺点: 埋点代码分布到各个页面, 耦合性高, 代码不优雅

2.无埋点数据收集

无埋点暂且理解为一种集中数据收集,android这一块主要就是根据Page + ViewTree 的方式对页面的组件进行定位,viewtree概念图如下:

QQ截图20170601183647

注重于针对view的单点数据收集,并且在遍历vietree的同时非常的消耗性能,来看了两篇关于埋点的文章 <Android AOP之字节码插桩>  http://www.jianshu.com/p/c202853059b4   和 <Android 无埋点数据收集SDK关键技术> http://www.jianshu.com/p/b5ffe845fe2d?from=jiantop.com  ;

Android中AOP框架的局限性:

Dexposed,Xposed的缺陷很明显,xposed需要root权限,Dexposed只对部分系统版本有效。(这两个对于现在的Andorid 来说完全可以放弃)
与之相比AspactJ没有这些缺点,但是aspactJ作为一个AOP的框架来讲对于我们来讲太重了,不仅方法数大增,而且还有一堆AspactJ的依赖要引入项目中(这些代码定义了aspactJ框架诸如切点等概念)。更重要的是我们的目标仅仅是按照一些简单的切点(用户点击等)收集数据,而不是将整个项目开发从OOP过渡到AOP。
AspactJ对于我们想要实现的数据收集需求太重了,但是这种编译期操作class文件字节码实现AOP的方式对我们来说是合适的。

四.个人理解

关于当前Andorid 教师端的埋点,有需要优化的地方,例如:初始化不应在BaseActivity中进行,而应该放在Application中, 还有现在的点完全分布的, 应该对点进行集中整理,方便管理,  至于自动化埋点 和  上述的无埋点数据收集, 主要针对原生的页面, 着重于组件的点击事件收集(个人理解为着重于”点”数据收集),   并且在数据收集的准确性和性能消耗上不占优势,   我们BI业务需求着重于”线”数据收集, 对于教师端的APP 我们为混合开发,大部分的页面交互都是前端插件和原生页面之间的,且原生页面占比甚少, 也是我们业务的一个特殊性! 关于之前考虑到的点数据的变化, 想到走接口, 本地维护映射表的方式以解决, 就这个问题个人和吴世勇交流过, 约定的点数据是不会发生变化的,   也就不存在这个问题了!

个人观点,欢迎指正!

用户行为统计整体设计探讨 — iOS技术实现篇

在保证移动端流量不会受较大影响的前提下,PM 们总是希望埋点覆盖面越广越好与此同时,作为开发者的我们更希望以一种可复用、解耦、动态可配、易于维护的可执行方案。所以,本文旨在探讨一种可复、解耦、动态可配的、容易维护的用户行为统计 (User Behavior Statistics, UBS) 方案。我将尽量从整体设计的视野,以 iOS 技术实现为例,与大家一起探讨 UBS (俗称:埋点)。本文整体分为三部分:

常规做法的优缺点

目前常规的做法是将埋点代码封装成工具类,但凡工程中需要埋点(如点击事件、页面跳转)的地方都插入埋点代码:

  • 优点:哪里需要哪里注入代码,简单明了;不会出现莫名其妙的崩溃问题。 ps.相较于使用 Runtime 的实现而言
  • 缺点:随着项目越来越复杂,埋点的代码散落在程序的各个角落,不利于维护以及复用;统计操作完全依赖于移动端的版本升级,无法动态配置;工作量大

可复用、解耦方案初步落成

我们的项目中是怎么处理的呢?首先谈一下解耦,避免埋点代码散落在程序的各个角落与业务代码糅杂在一起。同时,不得不谈到 AOP 名词这里不做过多解释了,自己Google 吧。在 iOS 中实现 AOP 编程的技术就是基于 Objective-C Runtime 特性的 Method Swizzling。上干货:

+ (void)swizzlingInClass:(Class)cls originalSelector:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector
{
    Class class = cls;
    
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
    
    BOOL didAddMethod =
    class_addMethod(class,
                    originalSelector,
                    method_getImplementation(swizzledMethod),
                    method_getTypeEncoding(swizzledMethod));
    
    if (didAddMethod) {
        class_replaceMethod(class,
                            swizzledSelector,
                            method_getImplementation(originalMethod),
                            method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

接下来就是 hook 的方法:

  • 对于页面事件的收集,主要通过 hook 系统类 UIViewController 的生命周期方法来实现,比如:viewDidAppear
  • 所有的 UIControl 类型的控件、UITabBarButton 以及在导航栏上自定义添加的 UIBarButtonItem 的点击事件,都可以通过 hook 系统类UIApplication 的 sendAction:to:from:forEvent: 方法进行拦截。但是,这个方法并不能拦截到导航栏上系统自动添加的返回按钮的点击,因此又 hook 了 UINavigationController 的 navigationBar:shouldPopItem: 方法来实现对它的点击的拦截

这时候问题来了,项目中每个页面都会有自己的页面事件编号(pad),此处的埋点代码如何知道要发送什么 pad 给服务端呢?轻松祭出 KVC与Dictionary ,创建一个配置文件 GSUserStatisticsConfig.plist 代码示例:

{
    "GSLoginViewController": {
        "pad": {
            "enter":"at1"
        },
        "ped": {
            "onxxxBtnPressed":"xxx"
        }
    }
}

通过上述处理,基本上实现了埋点代码与业务代码的解耦,作为一个统计模块复用性也非常显著。接下来谈一下

探索动态可配与易维护

在探索动态可配与已维护的数据采集方面,业界有一种方案称之为无埋点也叫全埋点,即不需要用户主动埋点,可以收集用户所有的操作行为。
接下来贴一张图来的更为直观一点:
userStastics
从上图可以看出,在实现无埋点数据收集时,主要分为3步:上传统计配置文件、请求统计配置文件、业务数据的收集与上报。
配置文件的设计与自身业务息息相关不再详述,请求配置文件也不是要讨论的核心,核心在于业务数据根据配置文件的动态收集,业界开发者称之为无埋点去获取配置文件中想要的业务数据。
参考案例:

学习该方案与我们自身产品业务埋点的梳理:

优点非常突出:

  • 维护成本,主要管理配置文件即可
  • 弥补埋点时存在错埋、漏埋等情况,动态更新及添加
  • 埋点代码无需跟随APP版本一起发布,不耽误数据的收集与统计
  • 对于一些动态事件做到很好的支持,例如:同一位置显示不同的内容,同一内容显示在不同的位置
  • 可以统计同一个按钮事件,在产品上可能代表不同的状态,例如在播放状态下关闭按钮和回放状态下关闭按钮,按钮的事件是一样的,但是需求去判断是播放状态还是回放状态

缺点:

  • 我们的埋点注重的是用户的浏览行为是一条路径(亦可称之为一条线),对单点操作行为统计要求不强,有点“杀鸡焉用牛刀”的感觉;
  • 无埋点方案需要大量hook系统方法,该行为导致 crash 的几率升高;
  • 如果收集数据量比较庞大,遍历事件然后拿到配置文件匹配事件的能耗会显现出来导致 APP 卡顿;
  • 开发周期会相对长一些:需要前端要搭建配置文件的平台;需要 BI 配合下发和处理 采集的数据;需要 iOS 和 Android 两端攻克无埋点采集数据的技术难点。

补充:

weex 页面的业务埋点思路同上,但是需要再做额外的处理;

最后,探讨毕竟是探讨,欢迎留言讨论。

移动端埋点方式探讨 – iOS端

目标:灵活的埋点方式,移动端不需要大量修改代码,甚至不用重新发包即可按照PM要求发送需要的统计数据。

要求:高度解耦,复用性强,动态配置,易于维护。

知道了要努力的目标和要求,我们先来看一下现在老版教师端和重构版教师端的优缺点。

一.老版教师端采用的是一个工具类发送数据,需要的个性化数据由每个界面自行传值进入。

优点:

1.灵活性较高,能满足个性需求:按照现在的发送的数据映射表,个别界面需要发送一些特殊信息,此种实现方式可以较好的满足这种情况的出现。

举个栗子:播放教案时,需要发送教案本身的id以及教案所在讲次的id,这两个数据其余界面并不需要发送。

2.有一定的解耦:这里解耦的就是发送数据这块,做成了工具,每个界面调用工具的API然后传值进行发送,不需要每个界面都写一遍工具类中的实现代码。

缺点:

1.对界面侵入性强,后期维护困难:由于工具类只负责发送数据,每个界面都会写一遍调用API方法,这样就造成重复代码冗余,解决方案是所有的界面都继承一个公用基类,每个界面自己去实现基类的方法即可。

但是由于老版的界面没有一个基类来管理,所以导致现在如果添加基类需要修改大量代码,需要大量的测试工作以保证APP本身的质量和埋点的完善。

2.灵活性不强:这里所说的不灵活是指当统计需要的数据映射改变后,尤其是一些个性公用字段的修改(比如:界面id),就会导致APP涉及埋点界面全部修改一遍,非常不灵活。同时由于iOS本身的审核机制,导致了上架周期相对安卓变长,会造成数据的丢失和上架的不及时。

二.新版教师端的埋点,采用的是统一配置表,由基类抓取当前界面,然后根据配置表获取到界面,再根据界面来发送数据。

优点:

1.高度解耦:每个界面自身的id有一个配置表,通过运行时的一个抓取类来抓取当前界面,然后在配置表来获取界面id再发送数据。

2.侵入性低,维护简单:由于有基类的存在,每个界面都实现了基类的方法,就可以被钩子抓取到界面,统计的所有代码没有和业务代码在一起,后期维护时只需要修改配置表即可。

缺点:

1.灵活性不够,个性数据获取困难:钩子抓取到的界面,通过配置表获取需要数据发送,但是这些都是写死的,个别界面自身的特殊数据无法获取然后进行发送,可以看上面的例子,这种实现方式就无法获取,如果获取就需要侵入相应界面。

 

新老的埋点都是在原生写死的映射,无法实现灵活的修改更新。而且后期定下来的技术方案最好在新版本中进行实现,因为老版本的代码的问题可能不容易或无法满足一些条件。

 

下面是最近看的文章中关于统计方案的思路和实现方法。大部分的文章采用的都是和新版教师端一样的方法,利用OC的RunTime黑科技Method Swizzling来Hook界面注入代码实现。

先来这些文章的链接

http://blog.csdn.net/w10207010218/article/details/56274431

http://www.jianshu.com/p/0497afdad36d

http://blog.csdn.net/kaka_2928/article/details/61201320

http://www.cocoachina.com/ios/20170427/19108.html 主要查看了这篇文章,里面还有个链接可以查看基础篇

重点讲一下最后链接文章的内容,文章是网易乐得的技术人员写的,不过他们的SDK没有对外公开(好像安卓公开了),他们声称:SDK 已经具备不需要代码埋点就能 自动的、动态可配的、全面且正确 的收集用户在使用 App 时的所有事件数据。除此之外,还单独开发了与之配合的圈选SDK,能够在 App 端完成对界面元素的圈配以及 KVC 配置的上传。而界面元素圈配的工作完全可以交给用研与产品人员来做,减轻了开发人员的工作量。

完成了两大部分:基本事件数据的收集和业务层数据的收集

文章中的SDK的实现思路和大部分主流一样是利用了Runtime 特性的 Method Swizzling 黑魔法,但是SDK也可以实现对界面中的子控件的数据收集,通过viewPath 及 viewId ,内容很多建议通过链接查看

Snip20170602_5

要实现灵活的进行数据收集和发送,就需要使用KVC实现。

什么是KVC配置。其实 KVC配置 就是一些用来描述 App 应该在什么时机去收集什么数据的信息

  • 上传KVC配置
    • 利用 圈选SDK 上传 KVC配置 的操作对于用户是透明的,主要由开发人员进行上传与管理。此操作可以在任何时候进行,在想要收集某个或某些版本的 App 中的业务数据时,上传相应的KVC配置信息至后台即可,达到了根据需要动态可配的效果。
  • 请求KVC配置
    • SDK 在初始化时会触发 KVC配置 的请求操作,从后台拉取 App 当前版本对应的所有KVC配置,并将请求结果缓存起来,以提供给下一步使用。

上传的所有的 KVC配置 需要与 App 的版本相对应,因为 App 版本不同会直接导致keyPath可能不一样。所以与 KVC配置 相关的工作有如下2个:

  1. 针对当前 App 版本上传相应的 KVC配置,以获取想要的业务数据
  2. 当 App 新版本发布时,需要对之前版本上的 KVC配置 逐一验证,是否仍然适用于新版本。如果仍然适用,则直接在管理后台上把新的版本号添加到此 KVC配置;如果不再适用,则对新版本再上传一个新的KVC配置。

业务数据的收集与上报的流程

8db8721df6e82075t

重构版的埋点采用的是现在的主流方案,现在需要攻克的难点就是灵活性的问题,怎么样从服务器去获取配置表,然后本地映射获取需要的数据,然后发送给统计平台。