Android动态日志系统Holmes

背景

美团是全球领先的一站式生活服务平台,为6亿多消费者和超过450万优质商户提供连接线上线下的电子商务网络。美团的业务覆盖了超过200个丰富品类和2800个城区县网络,在餐饮、外卖、酒店旅游、丽人、家庭、休闲娱乐等领域具有领先的市场地位。平台大,责任也大。在移动端,如何快速定位并解决线上问题提高用户体验给我们带来了极大挑战。线上偶尔会发生某一个页面打不开、新活动抢单按钮点击没响应、登录不了、不能下单等现象,由于Android碎片化、网络环境、机型ROM、操作系统版本、本地环境复杂多样,这些个性化的使用场景很难在本地复现,加上问题反馈的时候描述的往往都比较模糊,快速定位并解决问题难度不小。为此,我们开发了动态日志系统Holmes,希望它能像大侦探福尔摩斯那样帮我们顺着线上bug的蛛丝马迹,发现背后真相。

现有的解决办法

  • 发临时包用户安装
  • QA尝试去复现问题
  • 在线debug调试工具
  • 预先手动埋点回捞

现有办法的弊端

  • 临时发包:用户配合过程繁琐,而且解决问题时间很长
  • QA复现:尝试已有机型发现个性化场景很难复现
  • 在线debug:网络环境不稳定,代码混淆调试成本很高,占用用户过多时间用户难以接受
  • 手动埋点:覆盖范围有限,无法提前预知,而且由于业务量大、多地区协作开发、业务类型多等造成很难统一埋点方案,并且在排查问题时大量的手动埋点会产生很多冗余的上报数据,寻找一条有用的埋点信息犹如大海捞针

目标诉求

  • 快速拿到线上日志
  • 不需要大量埋点甚至不埋点
  • 精准的问题现场日志

实现

针对难定位的线上问题,动态日志提供了一套快速定位问题的方案。预先在用户手机自动产生方法执行的日志信息,当需要排查用户问题时,通过信令下发精准回捞用户日志,再现用户操作路径;动态日志系统也支持动态下发代码,从而实现动态分析运行时对象快照、动态增加埋点等功能,能够分析复杂使用场景下的用户问题。

自动埋点

自动埋点是线上App自动产生日志,怎么样自动产生日志呢?我们对方法进行了插桩来记录方法执行路径(调用堆栈),在方法的开头插入一段桩代码,当方法运行的时候就会记录方法签名、进程、线程、时间等形成一条完整的执行信息(这里我们叫TraceLog),将TraceLog存入DB等待信令下发回捞数据。

 public void onCreate(Bundle bundle) {//插桩代码if (Holmes.isEnable(....)) {Holmes.invoke(....);return;}super.onCreate(bundle);setContentView(R.layout.main);}       

历史数据

Tracelog形成的是代码的历史执行路径,一旦线上出现问题就可以回捞用户历史数据来排查问题,并且Tracelog有以下几个优点:

  1. Tracelog是自动产生的无需开发者手动埋点
  2. 插桩覆盖了所有的业务代码,而且这里Tracelog不受Proguard内联方法的限制,插桩在Proguard之前所以方法被内联之后桩代码也会被内联,这样就会记录下来对照原始代码的完整执行路径信息
  3. 回捞日志可以基于一个方法为中心点向前或者向后采集日志(例如:点击下单按钮无响应只需要回捞点击下单按钮事件之后的代码执行路径来分析问题),这样可以避免上报一堆无用日志,减少我们排查问题的时间和降低复杂度

Tracelog工作的流程

方法运行产生方法调用日志首先会经过checker进行检测,checker包含线程检测和方法检测(减少信息干扰),线程检测主要过滤类似于定时任务这种一直在不断的产生日志的线程,方法检测会在一定时间内检测方法调用的频率,过滤掉频繁调用的方法,方法如果不会被过滤就会进行异步处理,其次向对象池获取一个Tracelog对象,Tracelog对象进入生产队列组装时间、线程、序列号等信息,完成后进入消费队列,最后消费队列到达固定数量之后批量处理存入DB。

Tracelog数据展示

日志回捞到Trace平台上按时间顺序排列展示结果:

问题总结

我们的平台部署实施了几个版本,总结了很多的案例。经过实战的考验发现多数的场景下用户回捞Tracelog分析问题只能把问题的范围不断的缩小,但是很多的问题确定了是某一个方法的异常,这个时候是需要知道方法的执行信息比如:入参、当前对象字段、返回值等信息来确定代码的执行逻辑,只有Tracelog在这里的感觉就好比只差临门一脚了,怎么才能获取方法运行时产生的内存快照呢?这正是体现动态日志的动态性能力。

动态下发

对目标用户下发信令,动态执行一段代码并将结果上报,我们利用Lua脚本在方法运行的时候去获取对象的快照信息。为什么选择Lua?Lua运行时库非常小并且可以调用Java代码而且语言精简易懂。动态执行Lua有三个重要的时机:立即执行、方法前执行、方法后执行。

  • 立即执行:接受到信令之后就会立马去执行并上报结果
  • 方法前执行:在某一个方法执行之前执行Lua脚本,动态获取入参、对象字段等信息
  • 方法后执行:在某一个方法执行之后执行Lua脚本,动态获取返回值、入参变化、对象字段变化等信息

在方法后执行Lua脚本遇到了一些问题,我们只在方法前插桩,如果在方法后也插桩这样能解决在方法后执行的问题,但是这样增加代码体积和影响proguard内联方法数,如何解决这个问题如下:

我们利用反射执行当前方法,当进入方法前面的插桩代码不会直接执行本方法的方法体会在桩代码里通过反射调用自己,这样就做到了一个动态AOP的功能就可以在方法之后执行脚本,同样这种方法也存在一个问题,就是会出现死循环,解决这个问题的办法只需要在执行反射的时候标记是反射调用进来的就可以避免死循环的问题。

我们还可以让脚本做些什么呢?除了可以获取对象的快照信息外,还增加了DB查询、上报普通文本、ShardPreferences查询、获取Context对象、查询权限、追加埋点到本地、上传文件等综合能力,而且Lua脚本的功能远不仅如此,可以利用Lua脚本调用Java的方法来模拟代码逻辑,从而实现更深层次的动态能力。

动态下发数据展示

对象数据

权限信息

DB数据

技术挑战

动态日志在开发的过程当中遇到了很多的技术难点,我们在实施方案的时候遇到很多的问题,下面来回顾一下问题及解决方案。

数据量大的问题

  • 主线程卡顿

    • 1. 由于同时会有多个线程产生日志,所以要考虑到线程同步安全的问题。使用synchronized或者lock可以保证同步安全问题,但是同时也带来多线程之间锁互斥的问题,造成主线程等待并卡顿,这里使用CAS技术方案来实现自定义数据结构,保证线程同步安全的情况下并解决了多线程之间锁互斥的问题。
    • 2. 由于数据产生太多,所以在存储DB的时候就会产生大量的IO,导致CPU占用时间过长从而影响其他线程使用CPU的时间。针对这个问题,首先是采取线程过滤和方法过滤来减少产生无用的日志,并且降低处理线程的级别不与主线程争抢CPU时间,然后对数据进行批量处理来减少IO的频率,并在数据库操作上将原来的Delete+insert的操作改为update+insert。Tracelog固定存储30万条数据(大约美团App使用6次以上的记录),如果满30万就删除早期的一部分数据再写入新的数据。操作越久,delete操作越多,CPU资源占比越大。经过数据库操作的实际对比发现,直接改为满30万之后使用update来更新数据效率会更高一些(这里就不做太多的详细说明)。我们的优化成果从起初的CPU占比40%多降低到了20%左右再降到10%以内,这是在中低端的机器上测试的结果。
  • 创建对象过多导致频繁GC

    • 日志产生就会生成一个Tracelog对象,大量的日志会造成频繁的GC,针对这个问题我们使用了对象池来使对象复用,从而减少创建对象减低GC频率,对象池是类似于android.os.Message.obtain()的工作原理。
  • 干扰日志太多影响分析问题

    • 我们已经过滤掉了大部分的干扰日志,但还是会有一些代码执行比较频繁的方法会产生干扰日志。例如:自定义View库、日志类型的库、监控类型的库等,这些方法的日志会影响我们DB的存储空间,造成保留不了太多的正常方法执行路径,这种情况下很有可能会出现开发这关心的日志其实已经被冲掉了。怎么解决这个问题那?在插桩的时候可让开发者配置一些过滤或者识别的规则来认定是否要处理这个方法,在插桩的方法上增加一个二进制的参数,然后根据配置的规则会在相应的位上设置成0或者1,方法执行的时候只需要一个异或操作就能知道是否需要记录这个方法,这样增加的识别判断几乎对原来的方法执行耗时不会产生任何影响,使用这种方案产生的日志就是开发者所期望的日志,经过几番测试之后我们的日志也能保留住用户6次以上的完整行为,而且CPU的占用时间也降低到了5%以内。

性能影响

对每一个方法进行插桩记录日志,会对代码会造成方法耗时的影响吗?最终我们在中低端机型上分别测试了方法的耗时和CPU的使用占比。

  • 方法耗时影响的测试,100万次耗时平均值在55~65ms之间,方法执行一次的耗时就微乎其微了
  • CPU的耗时测试在5%以内,如下图所示:

  • 内存的使用测试在56kB左右,如下图:

对象快照

在方法运行时获取对象快照保留现场日志,提取对象快照就需要对一个对象进行深度clone(为了防止在还没有完整记录下来信息之前对象已经被改变,影响最终判断代码执行的结果),在Java中clone对象有以下几种方法:

  • 实现一个clone接口
  • 实现一个序列化接口
  • 使用Gson序列化

clone接口和序列化接口都有同样的一个问题,有可能这个对象没有实现相应的接口,这样是无法进行深度clone的,而且实现clone接口也做不到深度clone,Java序列化有IO问题执行效率很低。最后可能只有Gson序列化这个方法还可行,但是Gson也有很多的坑,如果一个对象中有和父类一样的字段,那么Gson在做序列的时候把父类的字段覆盖掉;如果两个对象有相互引用的场景,那么在Gson序列化的时候直接会死循环。

怎么解决以上的这些问题呢?最后我们参照一些开源库的方案和Java系统的一些API,开发出了一个深度clone的库,再加上自己定义数据对象和使用Gson来解决对象快照的问题。深度clone实现主要利用了Java系统API,先创建出来一个目标对象的空壳对象,然后利用反射将原对象上的所有字段都复制到这个空壳对象上,最后这个空壳对象会形成跟原有对象完全一样的东西,同时对Android增加的一些类型进行了特殊处理,在提高速度上对基本类型、集合、map等系统自带类型做了快速处理,clone完成的对象直接进行快照处理。

总结

动态日志对业务开发零成本,对用户使用无打扰。在排查线上问题时,方法执行路径可能直接就会反映出问题的原因,至少也能缩小问题代码的范围,最终锁定到某一个方法,这时再使用动态下发Lua脚本,最终确定问题代码的位置。动态日志的动态下发功能也可以做为一种基础的能力,提供给其他需要动态执行代码或动态获取数据的基础库,例如:遇到一些难解决的崩溃场景,除了正常的栈信息外,同时也可以根据不同的崩溃类型,动态采集一些其他的辅助信息来帮助排查问题。

作者介绍

  • 少飞,美团高级工程师,2015年加入美团,先后负责用户行为日志SDK、性能监控SDK、动态日志、业务异常监控
  • 陈潼,美团资深工程师,2015年加入美团,先后负责代码静态检查、网络层优化、动态日志等基础设施开发工作
  • 利峰,美团高级工程师,2017年加入美团,目前主要负责动态日志性能优化相关工作

招聘

美团平台客户端技术团队长期招聘技术专家,有兴趣的同学可以发送简历到:fangjintao#meituan.com。详情请点击:详细JD

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/478655.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

领域应用 | 知识图谱在小米的应用与探索

本文转载自公众号:DataFunTalk。分享嘉宾:彭力 小米编辑整理:马瑶出品平台:DataFunTalk导读:小米知识图谱于2017年创立,已支持公司了每天亿级的访问,已赋能小爱同学,小米有品、智能问…

前端应用开发架构图谱

个人整理的前端架构图谱,之后会根据这个图谱不断的完善内容。希望这个图谱可以对开发同学的知识脉络有个梳理的作用。 相关图谱文件已上传至Github:https://github.com/sahadev/front-end-architecture,后续将不定期更新。 2020年02月28日已…

丹琦女神新作:对比学习,简单到只需要Dropout两下

文 | 花小花Posy上周把 《对比学习有多火?文本聚类都被刷爆了...》分享到卖萌屋的群里后,遭到了群友们一波嫌弃安利。小伙伴们表示,插入替换的数据增强方式已经Out了,SimCSE才是现在的靓仔。snowfloating说:看完Danqi …

美团点评移动端基础日志库——Logan

背景 对于移动应用来说,日志库是必不可少的基础设施,美团点评集团旗下移动应用每天产生的众多种类的日志数据已经达到几十亿量级。为了解决日志模块普遍存在的效率、安全性、丢失日志等问题,Logan基础日志库应运而生。 现存问题 目前&#xf…

论文浅尝 - ACL2020 | Segmented Embedding of Knowledge Graphs

来源:ACL2020链接:https://arxiv.org/pdf/2005.00856.pdf摘要知识图谱的嵌入愈发变成AI的热点之一,对许多下游任务至关重要(如个性化推荐、问答等)同时,此模型强调两个关键特性:利用足够多的特征…

【论文翻译】HeteSim:异构网络中相关性度量的通用框架

原文链接:https://blog.csdn.net/Mrong1013967/article/details/115330139 HeteSim:异构网络中相关性度量的通用框架 摘要 相似性搜索是许多应用中的一个重要功能,它通常侧重于度量同一类型对象之间的相似性。然而,在许多场景中&a…

LeetCode 234. 回文链表(快慢指针+链表反转)

1. 题目 请判断一个链表是否为回文链表。 示例 1: 输入: 1->2 输出: false示例 2: 输入: 1->2->2->1 输出: true进阶: 你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?来源:力扣(LeetCode) 链接&a…

随机/线性颜色生成器(RandomColorGenerator)

最近在实现https://javascript30.com/的课程,其中有一门课程要求利用Canvas实现一个效果,我发现这个效果其中的颜色是线性生成的。结合我之前已经写过一个随机颜色生成器,就想将这个随机颜色生成器写出来,作为一个工具使用&#x…

美团点评运营数据产品化应用与实践

背景 美团点评作为全球最大的生活服务平台,承接超过千万的POI,服务于数量庞大的活跃用户。在海量数据的前提下,定位运营业务、准确找到需要数据的位置,并快速提供正确、一致、易读的数据就变得异常困难,这些困难主要体…

NAACL’21 | 来看如何让模型学会因为所以但是如果

文 | Eleanor 编 | 戏有一些标准考试那是真的难,难到能分分钟教你做人。对于留学党来说,申请法学博士需要 LSAT 考试成绩、申请商学院需要 GMAT 考试成绩。这些标准考试到底有多难,大概考过的都懂8(嘤嘤嘤_(:з」∠)_)…

领域应用 | 知识计算,华为云赋能企业知识化转型

本文转载自公众号:华为云AI。从计算智能,到感知智能,再到认知智能,是业界普遍认同的人工智能技术发展路径。随着技术的演进,人工智能已经在"听、说、看"等感知智能领域达到或超越了人类水准,但是…

我在小程序工程化方面的一些实践

我在小程序工程化方面的一些实践 早期做小程序时,还是原始时代,项目结构混乱,各种冗余代码,每次迭代时由于高昂的维护成本,极为头疼。遂在一次次的更迭中完成了基础组件的初版,极为酸爽。从此之后在当时的…

LeetCode 430. 扁平化多级双向链表(DFS)

1. 题目 您将获得一个双向链表,除了下一个和前一个指针之外,它还有一个子指针,可能指向单独的双向链表。这些子列表可能有一个或多个自己的子项,依此类推,生成多级数据结构,如下面的示例所示。 扁平化列表…

详解:多模态知识图谱种类及其应用

详解:多模态知识图谱种类及其应用 本文系统的讲述了一些关于多模态知识图谱的相关知识,作者介绍了一些重要的开源多模态知识图谱,基于百科多模态知识图谱Richpedia以及相关使用网站和多模态知识图谱的应用。>>加入极市CV技术交流群&am…

Android Hook技术防范漫谈

背景 当下,数据就像水、电、空气一样无处不在,说它是“21世纪的生产资料”一点都不夸张,由此带来的是,各行业对于数据的争夺热火朝天。随着互联网和数据的思维深入人心,一些灰色产业悄然兴起,数据贩子、爬虫…

论文浅尝 - ICML2020 | 跨域对齐的图最优运输算法

陈卓,浙江大学在读博士,主要研究方向为图神经网络和知识图谱表示学习。论文链接:https://arxiv.org/pdf/2006.14744代码:https://github.com/LiqunChen0606/Graph-Optimal-Transport发表会议:ICML 2020动机该论文的出发…

张俊林:对比学习研究进展精要

文 | 张俊林知乎对比学习(Contrastive Learning)最近一年比较火,各路大神比如Hinton、Yann LeCun、Kaiming He及一流研究机构比如Facebook、Google、DeepMind,都投入其中并快速提出各种改进模型:Moco系列、SimCLR系列、BYOL、SwAV…..&#x…

前端开发者的福音!通过拖拽就可生成Vue代码的平台来了!

Vue组件代码生成平台 Vue组件代码生成平台是一款面向Vue开发者的拖拽式组件代码生成工具。通过它可以快速搭建Vue组件的代码骨架结构。开发者可在此基础上进行二次开发。 目前该平台非常适合快速搭建一个常见的数据查询组件,仅需要拖三个组件进来即可完成&#xf…

LeetCode 523. 连续的子数组和(求余 哈希)

1. 题目 给定一个包含非负数的数组和一个目标整数 k,编写一个函数来判断该数组是否含有连续的子数组,其大小至少为 2,总和为 k 的倍数,即总和为 n*k,其中 n 也是一个整数。 示例 1: 输入: [23,2,4,6,7], k 6 输出: …

知识图谱简介

01 什么是知识图谱 我们可以从不同的视角去审视知识图谱的概念。 在Web视角下,知识图谱如同简单文本之间的超链接一样,通过建立数据之间的语义链接,支持语义搜索。 在自然语言处理视角下,知识图谱就是从文本中抽取语义和结构化的…