埋点自动化 埋点回归 客户端提效-离线数据质量

1. 埋点相关文档:

埋点涉及到的各种平台

UT平台:抓取实时埋点日志

正则平台:埋点case正则表达式

FBI监控平台:关注埋点监控日报

BI埋点数据监控平台:大盘数据

OneData报警平台:但暂不支持正则报警,待RD完善

2. 搜索埋点测试方法

背景

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

全部埋点的测试之路

面临的困难

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

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

Ø❎埋点测试每个季度不能出一个bug,不允许出错

解决办法

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

Ø✅ 结合埋点治理平台,抓取埋点日志

Ø✅ 接入正则平台,正则表达式判断部分key value

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

Ø✅ 接入FBI监控平台,采样监控搜索埋点

Ø✅ 接入onedata报警平台

Ø✅ 接入dqc 设计监控SQL

Ø✅ 开发读取odps埋点日志的脚本,分场景判断

3. 埋点客户端服务端交互

4. 埋点测试历程

4.1 客户端

Ø❎2018年10月:单靠人肉diff工作量大且效率低

Ø❎2019年S1上季度:接入正则平台,正则case测试埋点,SQL批量创建共67条case + 开发埋点对比脚本(点击 曝光 新老版本 新老架构 track_info内外迁移)

好处-正则平台:新手上手快,不需要太多复杂的技术就可以测试

弊端-正则平台:先从UT平台以为spmCD作为埋点的唯一ID捞取日志,再去正则平台匹配规则,但由于搜索卡片的特殊性,很多卡片spmCD一致如K3-22节目卡片-外网如懿传,K8-9节目大词-外网,K6-1演员,查看详情-演员的spmcd为XXCast_\d*.portrait_\d* 也就是说如果UT 应该5个日志命中一条正则但只有4个命中了,整体为pass,无法精准定位是哪个卡片出错了。而这样的卡片占比(67-15)/67=77.6%,如果坚持用正则平台测试,也会大概率漏掉,造成埋点漏测;且UT平台iOS端经常断开连接

好处-脚本:日志一一对比省去了大量人肉测试的时间和精力,高效便捷,PM RD自测,提交bug时直接粘贴控制台日志,省时省力,无漏测。适用于新的埋点需求测试和老功能埋点回归

弊端-脚本:需要抓取双端日志,两端操作完全一致,读取本地文件。双端曝光日志方式不完全一致,导致内层jsonlist个数不一致,需要手动删除小部分日志

Ø❎ 2019年S1下季度:接入FBI监控平台

弊端:监控为T+1采样日志,发现bug时候已经晚了,比如在一灰后才有一部分用户量

Ø❎ 2019 S1下季度:接入onedata报警平台

弊端: 接完发现不知道正则case,搜索是第一个接进去的业务,也是第一个踩雷的

Ø❎ 2019 S2季度:线上全量搜索埋点监控:接入dqc,优化SQL

弊端-dqc:上手简单,多行SQL,大量复制粘贴工作。可用于大量线上日志监控,但如果此条日志漏报,如ogc ugc未上报埋点,则总体count数目为0,平台的监控判断是count!=0,此场景下会错误认为此条case是pass的,造成发现不了一级故障

好处-dqc:出结果快,适用于大量日志监控,尤其容易发现偶现的bug(A用户日志没问题,B没有,C有这种情况)。优化后的SQL比较能准确定位问题

Ø❎一灰前埋点回归测试:读取线上odps日志,按版本和utdid维护捞取日志并解析,提取公共的参数校验,报警信息接入钉钉机器人,基于Spring Boot开发接口,报警信息数据入库,与组内同学业务共建,开发前端,最终做成平台

4.2 服务端

Ø❎RD:2019年2月服务端将老接口XX改为XX,埋点字段spm scm trackInfo内的部分字段等由服务端下发,减少客户端冗余的代码,出了问题后端上线,比较灵活,对线上数据影响小

Ø❎QA:冒烟平台case设计,监控接口action.report下埋点字段的下发

4.3 历程总结

埋点回归:双端纯手工(4PD)-> 正则平台(2PD)-> 埋点对比脚本(1PD)-> 埋点回归自动化(0.5PD)

监控:孔明灯T+1 -> FBI T+1 -> qdc 

5. 目标

业务方面:搜索核心指标和每个季度的重点工作,提前发现埋点类问题,保障线上不出问题

技术方面:沉淀出埋点自动化方法,从而达到提效,解放人力

5.1 提效之多端日志对比脚本

Ø 测试覆盖以下 3 个测试版本
ü 1119 版本治理一期:
新老版本埋点数据分别约 10045
埋点 BUG 总数 105
ü 1217 版本埋点双端统计
埋点 BUG 总数 40
ü 0107 版本:
手工测试平均 1h/2 个埋点,双端卡片 44 个,也就是 44h/88 个埋点,测试全部点击埋点耗时, 44h=5.5pd ,还不包括曝光埋点。。。
脚本测试,设计脚本 2pd ,测试 3pd=24h/88 个埋点,提效 (44-24)h/44 = 45.5%
埋点 BUG 总数 67
ü 后续每个版本 ……….
Ø 优点
ü 方便组内 QA 验证埋点,提高效率;使用简单,已为 PM 开通 git 权限
Ø 缺点: 没有实现完全自动化
 
 
 

3. QA贡献:

3.1 多端日志对比脚本

设计思想:

1. 整体设计:目前读取本地日志进行多端、多版本、单个json、jsonlist对比
src:entrance、utils、jsonDiff、localPathLog、logPath

2. entrance为读取UT平台接口返回的最新日志
2.1 utils为解析点击(单个json)、曝光埋点(jsonlist)日志格式
2.2 jsonDiff.SingleCompare为track_info内外key value的对比,必须存在的key 不需要校验的value如"aaid", "k", "engine", "item_log"的过滤
2.3 jsonDiff.DiffrentPlatForm为多端的日志对比,将两个数组放入集合,此集合包含这两个json的key。分内外层判断
内外层判断,校验所有集合是否在iOS Android中存在,不存在则日志打印


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

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

3. 定义第二个类DataUtilsSingle是单个json(点击日志)工具类:将文件流放到内存中,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. 定义第三个类DataUtilsMulti是多个json,即jsonlist(曝光埋点)工具类:把日志按track_info内外转化为json进行解析

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

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

6. 第六个类NewArchKV是新老架构埋点数据的对比
track_info内层判断:
校验老版本track_info内层层必须不存在{"newArch"};
校验新版本track_info内层层必须存在{"newArch"};
外层判断:
校验老版本track_info外层必须存在{"spm_new","scm_new"};
新版本track_info外层必须存在{"spm","scm"};
新版本track_info外层必须不存在{"spm_new","scm_new"};
且新版本spm scm的value与老版本的spm_new scm_new的value必须一致

7. 第七个类DiffrentPlatForm是判断不同平台 如 iOS Android iPad等多端或者多版本单个json的点击埋点日志,RD请参考自测

8. 第八个类ExposureDiff是判断不同平台 如 iOS Android iPad等多端或者多版本多个json(jsonlist)的曝光埋点日志,RD请参考自测

9. 增加BI要求必须存在的key的校验,共10个字段

待优化的地方:目前只支持从UT平台抓取日志手动拷贝到txt文件作对比。后续改为双端一致的操作,读取UT接口最新的日志多对比

1.value中存在两个==,取第一个
2.value中存在:的情况,如曝光埋点_KG卡片(即UGC大词)"tagvalue":"2:0;1:0",json解析的时候出错
3.Android最后的日志为track_info的json格式,需要在日志后面手动加英文逗号,再执行脚本

//内外层数据分离,用【{】表示track_info内层数据开始,用【},】表示track_info内层数据结束。有的埋点数据内层有"utparam":"{
"yk_abtest\":\"592:1381\"
}",这种数据,不能以}作为内层结束判断的标识

track_info={
    "k":"名侦探柯南",
    "object_num":3,
    "aaid":"074d90da3c8602ec629104ee7a57e796",
    "utparam":"{
    \"yk_abtest\":\"592:1381\"
    }",
    "newArch":"1",
    "object_title":"30-60分钟",
    "source_from":"home"
},
spm=a2h0c.8166622.PhoneSokuFilter.sfilter_3,
scm=20140669.search.filter.filter_30-60分钟,
pid=64b6847e992c4c45

流程图

脚本运行结果

提交bug到bug系统(为了数据脱敏,暂不附截图~~~总之很方便)

5.2 提效之线上埋点监控dpc

示例Demo

搜索结果页-二方卡spm遍历值条件下,10个核心埋点字段为空的占比统计,总和为0 ---pass

各项字段占比分别为0   ---pass

--odps sql

--********************************************************************--

--author:本人

--create time:2019-12-25 16:13:23

--********************************************************************--

--以下SQL的as也可以去掉

-- //通用需求正则平台必须存在的key的校验

-- public static String[] mustExistKeyOuter = {"spm", "scm"};

-- public static String[] mustExistKeyInner = {"soku_test_ab", "engine", "item_log", "aaid", "k", "source_from", "search_from"};

-- set odps.sql.type.system.odps2=true;

-- select date_format(CURRENT_TIMESTAMP(),"%Y%M%D") from dual;

select

-- 以下device,app_version,total_failrate在本地运行时为了查看详细数据,需要打开,在接入dqc时需要注释掉

-- device,app_version,

-- round(spm_rate +scm_rate +track_info_rate+ soku_test_ab_rate+ engine_rate+ item_log_rate+ aaid_rate+ k_rate+ source_from_rate+ search_from_rate ,4)

-- as total_failrate FROM (select

sum( case when spm is null or trim(spm)='' then 1 else 0 end) as spm_null, sum(1) as log_total,

round(sum(casewhen spm isnullortrim(spm)=''then1else0end)/sum(1),4)as spm_rate,

sum( case when scm is null or trim(scm)='' then 1 else 0 end) as scm_null,

round(sum(casewhen scm isnullortrim(scm)=''then1else0end)/sum(1),4)as scm_rate,

sum( case when track_info is null or trim(track_info)='' then 1 else 0 end) as track_info_null,

round(sum(casewhen track_info isnullortrim(track_info)=''then1else0end)/sum(1),4)as track_info_rate,

sum(casewhenget_json_object(track_info,'$.soku_test_ab')isnullortrim(get_json_object(track_info,'$.soku_test_ab'))=''then1else0end)as soku_test_ab_null,

round(sum(casewhenget_json_object(track_info,'$.soku_test_ab')isnullortrim(get_json_object(track_info,'$.soku_test_ab'))=''then1else0end)/sum(1),4)as soku_test_ab_rate,

sum( case when get_json_object(track_info,'$.engine') is null or trim(get_json_object(track_info,'$.engine'))='' then 1 else 0 end) as engine_null,

round(sum(casewhenget_json_object(track_info,'$.engine')isnullortrim(get_json_object(track_info,'$.engine'))=''then1else0end)/sum(1),4)as engine_rate,

sum( case when get_json_object(track_info,'$.item_log') is null or trim(get_json_object(track_info,'$.item_log'))='' then 1 else 0 end) as item_log_null,

round(sum(casewhenget_json_object(track_info,'$.item_log')isnullortrim(get_json_object(track_info,'$.item_log'))=''then1else0end)/sum(1),4) item_log_rate,

sum( case when get_json_object(track_info,'$.aaid') is null or trim(get_json_object(track_info,'$.aaid'))='' then 1 else 0 end) as aaid_null,

round(sum(casewhenget_json_object(track_info,'$.aaid')isnullortrim(get_json_object(track_info,'$.aaid'))=''then1else0end)/sum(1),4)as aaid_rate,

sum( case when get_json_object(track_info,'$.k') is null or trim(get_json_object(track_info,'$.k'))='' then 1 else 0 end) as k_null,

round(sum(casewhenget_json_object(track_info,'$.k')isnullortrim(get_json_object(track_info,'$.k'))=''then1else0end)/sum(1),4)as k_rate,

sum( case when get_json_object(track_info,'$.source_from') is null or trim(get_json_object(track_info,'$.source_from'))='' then 1 else 0 end) as source_from_null,

round(sum(casewhenget_json_object(track_info,'$.source_from')isnullortrim(get_json_object(track_info,'$.source_from'))=''then1else0end)/sum(1),4)as source_from_rate,

sum( case when get_json_object(track_info,'$.search_from') is null or trim(get_json_object(track_info,'$.search_from'))='' then 1 else 0 end) as search_from_null,

round(sum(casewhenget_json_object(track_info,'$.search_from')isnullortrim(get_json_object(track_info,'$.search_from'))=''then1else0end)/sum(1),4)as search_from_rate

-- 以下device,app_version,在本地运行时为了查看详细数据,需要打开,在接入dqc时需要注释掉

,device,app_version

from database.table_name

WHERE ds=to_char(dateadd(GETDATE(),-1,'dd'),"yyyymmdd")

-- 以下and hh in(18,19)在接入dqc时需要注释掉,监控线上一天24小时的数据。仅用于本地调试

and hh in(18,19)

-- WHERE ds=replace(split_part(dateadd(from_unixtime(unix_timestamp()),-1,'dd')," ",1),"-","")

and site='xx' and (device='android'or device='iphone')

and app_version >='8.3.0'

-- and (spm like '%a2h0c.8166622.rdirect%' OR spm like '%a2h0c.8166622.rmovie%')

-- and (original_spm like '%a2h0c.8166622.PhoneSokuTab%' OR original_spm like '%a2h0c.8166622.PhoneSokuCast%' OR original_spm like '%a2h0c.8166622.PhoneSokuPromote%')

and(original_spm like'%a2h0c.8166622.phonesokutab%'OR original_spm like'%a2h0c.8166622.phonesokucast%'OR original_spm like'%a2h0c.8166622.phonesokupromote%')

-- and (original_scm like '%20140669.search.filter%' OR original_scm like '%20140669.search.person%' OR original_scm like '%20140669.search.circle%')

-- and spm like '%a2h0c.8166622.rdirect%' and (original_spm REGEXP (.*a2h0c\.8166622\.(channeltab|portrait).*)

-- 以下GROUP BY device,app_version在本地运行时为了查看详细数据,需要打开,在接入dqc时需要注释掉

GROUPBY device,app_version

-- )

;

优化前的SQL:
设计上:
mysql也支持聚合的时候加一些条件,不过一般都是数据分析师才会这么搞,或者BI统计的时候用。日常这么写SQL,要被DBA干死的。因为这样很消耗Mysql的cpu,计算也一般都很慢,在线业务跑这种SQL,那接口几秒钟能返回也是够快了,随便几个并发起来了,库都要被拖挂了。
业务上:SQL查询的是A or B or C or D为空的总和计算/total_log,接入监控报警,一旦报警,无法准确定位是哪个字段出错了,可能是A可能是B可能是C,因为计算的是总和出错率。到时候还需要把SQL粘贴到odps,逐一修改判断哪个字段为空
优化后的SQL:
分别计算A为空B为空C为空D为空的出错率,因为最终的监控只能监控一个字段,所以需要sum()输出一个值接入监控系统
校验1个场景下的10个字段,统计,占比,如果哪个字段漏掉了,排查的时候,只需要把监控SQL粘贴到odps去掉sum(*),清晰地看到ABCD各自的失败率占比。哪个字段为空,准确定位
不需要分多个规则配置,不需要分端,减少冗余无效的复制粘贴以及一堆规则的填写,精简化,报错明显

5.3 提效之服务端冒烟case

5.4 提效之埋点回归自动化

## 脚本设计思路

脚本部分:

1.根据搜索的独特业务设计SQL(版本维度、utdid维度、ds维度等)读取线上离线表--点击埋点 曝光埋点15分日志钟延迟表,odps.xx.xx(点击)和xx(曝光)
获取每条SQL的查询字段内是否存在content字段(里面为所有日志,origialParam为客户端全部传参)

业务逻辑SQL设计如下:
--可用于预发包(后端连接线上)、灰度环境、线上环境回归测试,线上与预发的utdid不同,别写错了。日志15分钟延迟
SELECT app_version, os,utdid,user_nick,page,event_id,arg1,args,spm,ds,hh,mm FROM xx.xx 
where event_id = '2101' and spm like "%a2h0c%"  
-- and page in ("page_searchresults") 
and os in ("iPhone OS") and app_version='8.4.0' and
(user_nick ="越狱兔小兔兔" or utdid="XZ1OLGEEV5kDANSEeWq1HxAN") 
and site="youku" and ds="20191229" and hh in(16,17) LIMIT  500;

2. 参考timeassess中连接 读取odps的工具类和方法
和自己的搜索客户端传参监控代码设计

使用中间件开发环境-潘多拉(http://mw.alibaba-inc.com/bootstrap.html)
生成工程,配置maven,需要的依赖:com.aliyun.odps、fastjson、httpcore

3.按行读取SQL查询内容,单独处理解析args日志(搜索原始日志,track_info外层和内层),pageAndArg1类为按照页面维度和arg1页面控件维度进行日志判断,
具体日志解析为:找到{开始的位置,截取inner字符串,json格式{}里所有内容,从{开始截取,直到}的位置,以","拆分,获取key value
    try {
            int firstIndex = args.indexOf("{");
            // innerJson可能存在"object_title":"搜索","utparam":"{\"yk_abtest\":\"592:1381\"}"
            int lastIndexOf = args.lastIndexOf("}");
            //截取inner字符串,json格式{}里所有内容,从{开始截取,直到}的位置
            String innerStr = args.substring(firstIndex, lastIndexOf + 1);
            String outerStr = args.substring(lastIndexOf + 2);
            String[] outer = outerStr.toString().split(",");
            innerJSON = JSON.parseObject(innerStr);
            outerJSON = new JSONObject();
            for (int i = 0; i < outer.length; i++) {
                String[] array = outer[i].split("=");
                // 以=拆分之后,为key value
                outerJSON.put(array[0].trim(), array[1].trim());
            }

4.根据不同的搜索页面设计不同的类,将公共参数设计为单独的公类供。方便代码维护,一目了然
baseKeysCheck.existKeys(array1, record, count);

5.搜索结果页page_searchresults埋点校验,按照page && k的逻辑定位arg1,根据前3个条件判断spm scm具体的控件位置。如搜索的query变了,请更新此代码里的k
每个卡片在需要校验的算法字段前添加注释,注释分为2部分,前面part1==埋点文档卡片类型+点击区域,如搜索结果页1-26卡片一一对应,面part2=QA的case说明
part1+part2给人的感觉直观,清楚的知道哪部分埋点的测试,测试点,以免漏测



遇到的问题以及分析:
1. 类似page_searchresults_poster_of_PhoneSokuProgram page_searchresults_recommend_of_PhoneSokuProgram
的scm的B位带空格,UT日志是正确的,入库的时候错了,脚本用trim()方法去空白处理

2. 如果保证不同的卡片命中相同的正则表达式时,只有1条pass就pass的问题?
代码中用page.equals("page_searchresults") && k.equals("鹤唳华亭")筛选对应卡片的全部日志,
再根据arg1.equals("page_searchresults_poster_of_PhoneSokuProgram")定位卡片的详细区域
再根据if (!(spm.matches(spmAB + "PhoneSokuProgram_\\d*.poster") && scm.matches(scmAB + "(rtv|rvideoset-algo).\\S*"))) 判断具体的点击位置
最后对每条卡片进行baseKeysCheck.existKeys(array1, record, count);的校验
baseKeysCheck为公共方法判断,具体为BI要求的字段校验
/**
 * 通用需求必须存在的算法字段key以及value的判断
 * public static String[] mustExistKeyOuter = {"spm", "scm"};
 * public static String[] mustExistKeyInner = {"soku_test_ab", "engine", "item_log", "aaid", "k", "source_from", "search_from"};
 */

3. 何保证每条日志都校验,没有漏校验的日志,无logMiss?
在一堆if(){
    if (){
        }else if(){
    }
}else {
    System.err.println(arg1Miss() + logPrint(array1, record, count) + "缺失的arg1为:" + arg1);
             }
             
设计类logPrint,提供全局公共方法(count app_version event_id page arg1 os utdid user_nick args ds hh),大量减少sout 和 serr天长的日志文案打印
bugDesc() "埋点异常,BUG BUG BUG!!!";
passDesc() "埋点正确,PASS PASS PASS!!!"
arg1Miss() "arg1缺失,未上报,arg1Miss Miss Miss!!!";

4. 日志第4行解析有问题,解析报错?
经排查为此日志没有track_info,这种的日志(page_searchresults_detailsdetaildownloadbutton)占比极少数,则需要对解析方法单独处理
debug排查问题,在代码中打断点,debug模式运行主函数,在Debugger下,添加或者删除多余的代码

5. 如何保证日志中无漏测的埋点,该上报的日志都上报了?
5.1 在RunProcess中,
5.1.1 在开头
// 初始化page_searchresults的arg1
        page_searchresults.initList();
        
5.1.2 在结尾
// 判断最终的集合元素,正常为空,代表每条日志都各自命中了应该有的page&arg1,正确,无BUG!!!
           page_searchresults.result();
           
5.2 在page_searchresults中

5.2.1 在开头
// 定义集合,把搜索page下对应的arg1放到list
    public static List<String> list = new ArrayList<>();

    public static void initList() {
        // page_searchresults下所有的arg1,如有新增的在此处添加
        list.add("page_searchresults_poster_of_PhoneSokuProgram");
        list.add("page_searchresults_title_of_PhoneSokuProgram");
        list.add("page_searchresults_recommend_of_PhoneSokuProgram");
    }                    
           
    public static void result() {
        System.out.println("======debug【page_searchresults】======为:" + list.toString());
    }

5.2.2 在结尾
// 遍历每一条日志,把命中的arg1从集合移除,如果最终集合的元素为空,则正确,如果有arg1的值,则没有此日志,对应的arg1未上报,BUG!!!
              list.remove(arg1);         
           
     
          
该设计的好处:
完美避开UT日志平台上报延迟1min-30min的问题,做到一顿操作猛如虎,狂点一番后,直接捞取odps日志测试需要校验的埋点,重复正则的完美校验,无漏测~

--可用于预发包(后端连接线上)、灰度环境、线上环境回归测试,需要注意的是,线上与预发的utdid不同,别写错了。日志15分钟延迟

SELECT app_version, os,utdid,user_nick,page,event_id,arg1,args,spm,ds,hh,mm FROM database.table_name

where event_id = '2101' and spm like "%a2h0c%"

-- and page in ("page_searchresults")

and os in ("iPhone OS") and app_version='8.4.0' and

(user_nick ="XX" or utdid="XX")

and site="xx" and ds="20200107" and hh in(16,17) LIMIT 500;

客户端埋点日志如下,需要对BI要求的算法字段解析判断

运行结果, 场景:page_searchresults

提交bug到bug系统

1. 【iOS】搜索结果页,page_searchresults_recommend_of_PhoneSokuProgram场景下scm的B位多了空格,日志如下,测试代码如下

埋点异常,BUG BUG BUG!!!第2条埋点日志==app_version为:【8.4.2】,event_id为:【2101】,page为:【page_searchresults】,arg1为:【page_searchresults_recommend_of_PhoneSokuProgram】,os为:【iPhone OS】,utdid为:【XZ1OLGEEV5kDANSEeWq1HxAN】,user_nick为:【越狱兔小兔兔】,args为:【track_info={"alginfo":"-1rcTrig-2+1-1rcType-2Query2QUERY-1rcQVst-220191111 21:23:38-1abId-2126721-1rkBiz-2querying_esmm_gujin_v1-1pn-20-1sceneId-214177-1rkPos-26-1rcQSim-20.00700-1rcQCate-2Q2Q-1rkScore-20.01258-1rcQuery-2奉天往事-1rcShdCate-2QUERY","group_id":7916307,"object_type":110,"group_num":3,"search_q":"乡村爱情12上部","object_title":"鹤唳华亭·高甜番外","group_type":1,"source_from":"home","show_q":"乡村爱情12上部","aaid":"1b49dffdd574dcd175d714e8fea85b2f","searchtab":"0","soku_test_ab":"a","source_id":14,"k":"鹤唳华亭","item_log":"eps_t~1$site~14$show_id~dcfa5b70a2d643318dd3$doc_source~1$eng_source~6$sp_id~1154993465549914112$rec_scg_id~7916307$rec_alginfo~-1show_id-2482416-1sceneId-214945-1scg_id-27916307$rec_recext~reqid=0ec081a9-9132-406b-9450-3b4b33c4b076$scg_id~7916307","req_id":"reqid=dfcc84cd-2733-446e-a32a-dab8912f1684","search_from":"3","cn":"精选","engine":"expid~req.ugc118.sort1004.rank118.qa1$eid~0b1bcd7c15783873356484180e8bfc$bts~soku_qp#1@soku_engine_master#118@soku_irank#203@soku_ogc#303@soku_ugc#402@soku_ai#A@youku_user#B@show_scg#A@ott_sp#A@user_folder#B@ugc_black#C@big_store#B@sp_big_ha3#A@user_folder_ab#A@soku_ott#501@default_page_rank#A@week_ugc#A@default_recommend_like#B@show_ip_card#C@search_two_row#A@rank_scg_show#A@ysearch_sp_cache#D@app_page#B@big_store_ugc#B@default_page_scg#A@weex_eagle#A@taopiaopiao#A@damai#C@resultpage_tab_style#A$r_p_n~33","word_location":1,"object_url":"youku:\/\/page\/node?bizKey=NODE_PAGE&nodeKey=SOUSUO_PIANDAN&nav_style=1&bizContext=%7B%22scmB%22%3A%22apircmd%22%2C%22scmA%22%3A%2220140719%22%2C%22spmA%22%3A%22a2h8d%22%2C%22entityType%22%3A%22scg%22%2C%22spmB%22%3A%22sousuo_piandan%22%2C%22entityId%22%3A7916307%2C%22pagename%22%3A%22page_guidenode_sousuo_piandan%22%7D","object_num":1,"newArch":"1","recext":"reqid=dfcc84cd-2733-446e-a32a-dab8912f1684"},spm=a2h0c.8166622.PhoneSokuProgram_3.recommend_1,statisticsTag=e3e1fc18eb13ebdd,scm=20140669.search.rtv.set_7916307,utpvid=37,pid=69b81504767483cf】,ds为:【20200107】,hh为:【17】,搜索结果页点击【鹤唳华亭】的推荐语
 

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

// 1. 节目卡(正式/专题/花絮),点击ogc如鹤唳华亭的推荐语
else if (arg1.equals("page_searchresults_recommend_of_PhoneSokuProgram")) {
    String area = card1 + "推荐语";

    if (!(spm.equals(spmAB + "PhoneSokuProgram_\\d*.recommend_\\d*") &&
            scm.matches(scmAB + "rtv.(set|show|video|url|scg|playlist|live)_\\S*"))) {
        System.err.println(bugDesc() + logPrint(array1, record, count) + area);
    } else {
        System.out.println(passDesc() + logPrint(array1, record, count) + area);
    }

    baseKeysCheck.existKeys(array1, record, count);

}

排查原因如下:
类似page_searchresults_poster_of_PhoneSokuProgram page_searchresults_recommend_of_PhoneSokuProgram
的scm的B位带空格,UT日志是正确的,入库的时候错了,脚本用trim()方法去空白处理

完美避开UT日志平台上报延迟1min-30min的问题,做到一顿操作猛如虎,狂点一番后,直接捞取odps日志测试需要校验的埋点

1. debug排查问题,在代码中打断点,debug模式运行主函数,在Debugger下,添加或者删除多余的代码

经排查为日志第4行解析有问题,没有track_info,这种的日志占比极少数,则需要对解析方法单独处理

2. 正则

正则在线多个字符匹配可以添加(A|B|C),但代码如果写()则会报错

scm4.matches(scmAB + "(rvideoset-operation|PhoneSokuUgcSeries_\\d*).\\S*")

正确的为 scm4.matches(scmAB + "rvideoset-operation|PhoneSokuUgcSeries_\\d*.\\S*"))) {

感谢我栋哥给我推荐正则表达式的图形网站   https://regexper.com/

神奇 ,找了一下午原因,正则表达式看着没问题,为啥一直err??看了好久了 。。。。这个放在 TestDemo是pass的,但放在我的业务代码是fail的。因为自己傻缺的把test里的日志打印反了success 和fail反了。。。

如果track_info外层包含scm=20140669.search.rvideoset-algo.video_XNDQ5OTQ4NjgwNA==或者spm=a2h0c.8166622.page.downloadbutton,vid=XNDQyODk3OTMyOA==
则需要把==替换为空,不影响以,拆分的判断


未完待续。。。。readme  流程图产出,代码完善,接入钉钉报警,错误 正常信息入库,接口开发,前端开发等。。。

发布了45 篇原创文章 · 获赞 11 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_42498050/article/details/103755551