===============================================================================================

搜索埋点背景

由于搜索埋点数据过于老化,导致统计方面的一些工作的不方便。经过产品评估后决定需要重构部分埋点字段的参数,所以搜索的iOS端和Android端开始了一段全部埋点的测试之路。

0225版本新架构yksearch背景

❎ 流量分散、老sdk中包含大量且复杂的版本控制逻辑、埋点由客户端下发,容错率和迭代效率较低

✅ 埋点改为服务端下发。两端保持一致,新增统计字段时不需要重新发版

面临的困难

❎ 每次测试任务量巨大,要求QA覆盖全部46个类型卡片,100+种状态

❎ 单靠人肉diff工作量大且效率低

❎ 埋点日志字段较多。曝光埋点每个日志多达300+key value

❎ 埋点测试为S2季度客户端重点需求,不允许出错

解决办法

✅ Mock数据,构造全部产品形态,避免节目下线、节目类型难以查找等情况,辅助测试埋点

✅ 部分QA手工测试,文本在线diff

✅ 结合埋点治理平台UT(usertrack),抓取埋点日志

✅ 接入PLATO,正则表达式判断部分key value

✅ 设计埋点日志数据对比脚本,提升测试效率

android 埋点 能作为日志上传 日志埋点技术_新版本

 

===============================================================================================

android 埋点 能作为日志上传 日志埋点技术_json_02

 

===============================================================================================

设计思想:

1.定义数据data(单个json,校验track_info外的字段在track_info都要存在)、data1_old(老版本上报的埋点)、 
data2_new(新版本上报的埋点)

2.定义第一个类DataPathTag,老版本path1、新版本path2、同一个数据内path,以上埋点数据文件存放的绝对路径

3.定义第二个类DataUtils是工具类:将文件流放到内存中,BufferedReader按行读取, 
3.1 分离内外数据 3.1.1 内层与外层的区别标识为{},{为开始,}为结束,将内层与外层数据生成2个字符串track_info_outer、
track_info_inner, 找到track_info_inner第一个"="的下标,截取字符串,可强转为json

3.1.2 外层判断是否包含}

3.2 内层、外层数据转换 3.2.1 track_info_inner的格式本来就为json,所以直接处理json即可, 截取track_info_inner字符串,
json格式{}里所有内容,从{开始截取,直到}的位置,最后一位为"," { "searchtab":"0", "pageName":"page_searchresults", "group_num":38, }

3.2.2 track_info_outer的格式为A=1 外层数据处理,字符串转数组,数组先以","分隔,再以"="分隔,数组再转json,
如"A=1,B=2,C=3"-->[A=1,B=2,C=3]-->{"A":"1","B":"2","C":"1"}, object_id=dbb0ecb3786549098484, object_title=一出好戏, srid=1,

4. 第三个类JSONdifferent是新老数据的比较。调用DataUtils类,内层、外层分开判断,先判断key,再判断value 
老版本的所有key必须在新版本中存在,如果不存在打印key,value。如果存在判断value是否相同,不相同打印

5. 第四个类SingleCompare是单个版本上报的数据校验,判断track_info内是否包含track_info外所有的key 
如果不存在,打印key,value。如果存在判断value是否相同,不相同打印

6. 第五个类NewArchitecture是新老架构埋点数据的对比
内层判断:
校验老版本内层层必须不存在{"newArch"};
校验新版本内层层必须存在{"newArch"};

外层判断:
校验老版本外层必须存在{"spm_new","scm_new"};
新版本外层必须存在{"spm","scm"};
新版本外层必须不存在{"spm_new","scm_new"};
且新版本spm scm的value与老版本的spm_new scm_new的value必须一致



1.value中存在两个==,取第一个
2.value中存在:的情况,如曝光埋点_KG卡片(即UGC大词)"tagvalue":"2:0;1:0",json解析的时候出错

 

 

android 埋点 能作为日志上传 日志埋点技术_新版本_03

 

1. 工具类DataUtilsSingle:单个json格式日志解析

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;

public class DataUtilsSingle {
    public   JSONObject innerJSON;
    public   JSONObject outerJSON;

    //读取path路径的txt文件
    public void txt2String(String path){
        //StringBuilder为拼接字符串,减少对象的创建
        StringBuilder track_info_outer = new StringBuilder();
        StringBuilder track_info_inner = new StringBuilder();

        //文件读取必须得用try catch,文件按行读取
        try{
            File file = new File(path);
            BufferedReader br = new BufferedReader(new FileReader(file));//构造一个BufferedReader类来读取文件
            String s;
            boolean isInner = false;
            while((s = br.readLine())!=null){//使用readLine方法,一次读一行
                String result = s.trim();//去空白,末尾或者开头。去空格
                //内外层数据分离,用{}区分
                if (result.contains("{")){
                    isInner = true;
                }else if (result.contains("}")){
                    isInner = false;
                    //"A=1,B=2,C=3..."
                    track_info_inner.append(result);
                }
                boolean con = result.contains("}");
                if (!con){
                    if (isInner){
                        track_info_inner.append(result);
                    }else {
                        track_info_outer.append(result);
                    }

                }

            }
            br.close();
            //track_info_inner中等号(=)第一次出现的位置
            int firstIndex = track_info_inner.indexOf("=");
            //截取inner字符串,json格式{}里所有内容,从{开始截取,直到}的位置,最后一位为","
            String innerStr = track_info_inner.substring(firstIndex+1);
            if (innerStr.endsWith(",")){
                innerStr = innerStr.substring(0,innerStr.length()-1);
            }
            String[] outer = track_info_outer.toString().split(",");
            innerJSON = JSON.parseObject(innerStr);
            outerJSON = new JSONObject();
            for (int i=0;i<outer.length;i++){
                String[] array = outer[i].split("=");
                outerJSON.put(array[0].trim(),array[1].trim());
            }

        }catch(Exception e){
            e.printStackTrace();
        }
    }

}

 

2. 工具类DataUtilsMulti:多个json即jsonlist格式日志解析

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;

public class DataUtilsMulti {
    private JSONObject innerJSON;
    public List<JSONObject> innerList = new ArrayList<>();
    public JSONObject outerJSON;

    //读取文件data.txt内容
    public void txt2String(String path){
        File file = new File(path);

        StringBuilder track_info_outer = new StringBuilder();
        StringBuilder track_info_inner = new StringBuilder();

        try{
            BufferedReader br = new BufferedReader(new FileReader(file));//构造一个BufferedReader类来读取文件
            String s = null;
            boolean isInner = false;
            while((s = br.readLine())!=null){//使用readLine方法,一次读一行
                String result = s.trim();
                if (result.contains("{")){
                    isInner = true;
                }else if (result.contains("}") && !result.contains("{")){
                    isInner = false;
                    track_info_inner.append(result);
                    continue;
                }
                if (isInner){
                    track_info_inner.append(result);
                }else {
                    track_info_outer.append(result);
                }

            }
            br.close();

            //0311新增代码44 45 59,根据新架构pv日志格式调整,判断无track_info字段或者track_info为空的情况
            String info_inner = track_info_inner.toString();
                if (info_inner != null && info_inner.trim().length()!= 0) {
                //track_info_inner中等号(=)第一次出现的位置
                int firstIndex = track_info_inner.indexOf("=");
                //从=算index,index+1为{。取出的字符串为{};{};{};{},
                String inner = track_info_inner.substring(firstIndex + 1);
                String innerStr = inner.substring(0, inner.length() - 1);
                String[] innerArray = innerStr.split(";");
                for (int i = 0; i < innerArray.length; i++) {
                    if (innerArray[i].trim().length() != 0) {
                        //{}   {}   {}
                        innerJSON = JSON.parseObject(innerArray[i]);
                        innerList.add(innerJSON);
                    }
                }
            }

            String[] outer = track_info_outer.toString().split(",");
            outerJSON = new JSONObject();
            for (int i=0;i<outer.length;i++){
                int index = outer[i].indexOf("=");
                String[] array = outer[i].split("=",index+1);
                outerJSON.put(array[0].trim(),array[1].trim());
            }

        }catch(Exception e){
            e.printStackTrace();
        }
    }

}

 

3. 单个文件对比SingleCompare:对比单个json埋点日志track_info外层的key必须在内层存在,且值相等。判断内外层必须存在的MustExistKey的校验

android 埋点 能作为日志上传 日志埋点技术_新版本_04

 

 

新老版本对比:

android 埋点 能作为日志上传 日志埋点技术_android 埋点 能作为日志上传_05

 

新老架构对比:

android 埋点 能作为日志上传 日志埋点技术_java_06

 

点击埋点:

scm_new=20140669.search.rnovel.novel_shuqiyk://openapp?params=%7B%22pageName%22%3A%22bookDetail%22%2C%22params%22%3A%7B%22bookId%22%3A%227507742%22%7D%7D&back=1&from=s_7507742_c_7507742,
object_num=1,
pid=64b6847e992c4c45,
cate_id=109,
search_from=1,
searchtab=0,
track_info={
  "source_from":"home",
  "cate_id":109,
  "group_num":1,
  "engine":"expid~req.ugc119.sort1004.rank119.qa1$eid~0b8b380b15474577070643133ed057$bts~soku_qp#1@soku_engine_master#119@soku_irank#204@soku_ogc#301@soku_ugc#403@soku_ai#B@show_filter#B@image_search#A@search_discover#B@soku_resultpage_lessshow#B@soku_resultpage_newcard_58#B$r_p_n~31",
  "aaid":"1e72ac097f6cbf359f7876122e999d0c",
  "k":"天坑鹰猎",
  "object_num":1,
  "searchtab":"0",
  "search_from":1,
  "click_id":"1e72ac097f6cbf359f7876122e999d0c1547458217234",
  "object_title":"天坑鹰猎(平装版)",
  "object_id":"shuqiyk:\/\/openapp?params=%7B%22pageName%22%3A%22bookDetail%22%2C%22params%22%3A%7B%22bookId%22%3A%227507742%22%7D%7D&back=1&from=s_7507742_c_7507742",
  "srid":1,
  "view_type":1032
},
object_id=shuqiyk://openapp?params=%7B%22pageName%22%3A%22bookDetail%22%2C%22params%22%3A%7B%22bookId%22%3A%227507742%22%7D%7D&back=1&from=s_7507742_c_7507742,
object_title=天坑鹰猎(平装版),
srid=1,
group_num=1,
spm=a2h0c.8166622.rnovel.screenshot,
k=天坑鹰猎,
spm_new=a2h0c.8166622.PhoneSokuPromote_1.screenshot,
source_from=home,
scm=20140669.search.rnovel.novel_shuqiyk://openapp?params=%7B%22pageName%22%3A%22bookDetail%22%2C%22params%22%3A%7B%22bookId%22%3A%227507742%22%7D%7D&back=1&from=s_7507742_c_7507742,
aaid=1e72ac097f6cbf359f7876122e999d0c,
engine=expid~req.ugc119.sort1004.rank119.qa1$eid~0b8b380b15474577070643133ed057$bts~soku_qp#1@soku_engine_master#119@soku_irank#204@soku_ogc#301@soku_ugc#403@soku_ai#B@show_filter#B@image_search#A@search_discover#B@soku_resultpage_lessshow#B@soku_resultpage_newcard_58#B$r_p_n~31,
view_type=1032

 

曝光埋点:

spm_new=a2h0c.8166622.PhoneSokuUgc_4.screenshot;a2h0c.8166622.PhoneSokuUgc_5.screenshot,
 scm=20140669.search.rugc.video_XMzgzODM5ODM2NA==;20140669.search.rugc.video_XMzg1OTM0MDEzNg==,
 source_from=home,
 spm=a2h0c.8166622.rugc.screenshot;a2h0c.8166622.rugc.screenshot,
 scm_new=20140669.search.rugc.video_XMzgzODM5ODM2NA==;20140669.search.rugc.video_XMzg1OTM0MDEzNg==,
 track_info={
   "srid":1,
   "item_log":"doc_source~2$eng_source~6$sp_id~3458764514780140519",
   "source_from":"home",
   "group_num":4,
   "engine":"expid~req.ugc119.sort1004.rank119.qa1$eid~0b8b380b15474587002515969ed05c$bts~soku_qp#1@soku_engine_master#119@soku_irank#204@soku_ogc#301@soku_ugc#403@soku_ai#B@show_filter#B@image_search#A@search_discover#B@soku_resultpage_lessshow#B@soku_resultpage_newcard_58#B$r_p_n~31",
   "aaid":"c869247b685afb310e227b16a7943a1e",
   "k":"天坑鹰猎",
   "object_num":1,
   "object_type":1,
   "group_id":"XMzgzODM5ODM2NA==",
   "isplay":11,
   "searchtab":"0",
   "object_id":"XMzgzODM5ODM2NA==",
   "object_title":"《天坑鹰猎》王俊凯误认为小姑娘喜欢她,
   呆萌的模样反被嘲笑",
   "search_from":3,
   "click_id":"c869247b685afb310e227b16a7943a1e1547458702726",
   "view_type":1005,
   "cate_id":-21
 };{
   "srid":1,
   "item_log":"doc_source~2$eng_source~6$sp_id~3458764514785375962",
   "source_from":"home",
   "group_num":5,
   "engine":"expid~req.ugc119.sort1004.rank119.qa1$eid~0b8b380b15474587002515969ed05c$bts~soku_qp#1@soku_engine_master#119@soku_irank#204@soku_ogc#301@soku_ugc#403@soku_ai#B@show_filter#B@image_search#A@search_discover#B@soku_resultpage_lessshow#B@soku_resultpage_newcard_58#B$r_p_n~31",
   "aaid":"c869247b685afb310e227b16a7943a1e",
   "k":"天坑鹰猎",
   "object_num":1,
   "object_type":1,
   "group_id":"XMzg1OTM0MDEzNg==",
   "isplay":11,
   "searchtab":"0",
   "object_id":"XMzg1OTM0MDEzNg==",
   "object_title":"天坑鹰猎:大结局终于看透张保庆的心思了,最不舍得的还是菜瓜!",
   "search_from":3,
   "click_id":"c869247b685afb310e227b16a7943a1e1547458702727",
   "view_type":1005,
   "cate_id":-21
 },
 search_from=3,
 engine=expid~req.ugc119.sort1004.rank119.qa1$eid~0b8b380b15474587002515969ed05c$bts~soku_qp#1@soku_engine_master#119@soku_irank#204@soku_ogc#301@soku_ugc#403@soku_ai#B@show_filter#B@image_search#A@search_discover#B@soku_resultpage_lessshow#B@soku_resultpage_newcard_58#B$r_p_n~31,
 pid=64b6847e992c4c45