背景
持续快速交付线上版本并且保证质量、性能始终处于优良的状态,是技术研发、项目管理团队的永恒追求。随着移动开发技术的成熟,产业竞争的加剧,各大头部 App 逐步平台化、容器化,承担着各自公司业务主要入口的使命。这不可避免地导致其本身逐步巨型化,与早期移动应用相比,复杂度明显上升。
除此之外,快速的迭代开发节奏,复杂的用户环境(网络、设备、场景等)、多样的应用框架,这些都给移动端的开发和管理带来了巨大的挑战。事实上,传统的软件危机在移动开发上也有一定程度的体现。
在上述背景下,本文从移动端面临的质量和效率的挑战出发,结合爱奇艺 App 的具体情况,介绍了爱奇艺技术团队对这个问题的思考与实践。
遇到的挑战
从宏观上看,爱奇艺 App 这种巨型应用面临的复杂度来源于多个方面:
业务规模大:业务模块多达几十个,如何正确管理、隔离各模块之间的耦合、冲突,避免错误和故障的扩散蔓延,是一个不小的挑战;
开发人员规模大:上述模块很多分散归属于不同的开发团队,且开发集成周期并不完全同步,这给集成发布带来了很大的复杂性;
技术形态繁杂:由于业务的特点,各团队/业务所选择的技术栈不尽相同,Native,RN,H5,小程序,Flutter 等多种形态并存;不同的跨端语言/技术并行,如 JS、Lua、C/C++等,如何保证所有这些环境正确隔离,有序协作,需要考虑很多方面的问题;
迭代速度快:目前爱奇艺 App 保持着双周发版的节奏,后端前置开发,客户端一周开发+一周测试,在这种快速的迭代周期下,保持 App 整体可维护性、可测试性等质量标准良好,对研发架构、流程、工具都提出了新的要求。
从微观上看,各业务模块自身也不同程度地面临着技术复杂、需求变动频繁、历史沉淀追溯困难等挑战;在快速迭代过程中大多数时候属于行进过程中换轮子的状态,其本身代码质量、功能质量也是一个需要持续关注的主题。
解决方案
综合上述各方面的因素,多种复杂性和不确定性的叠加给持续高质量交付带来了巨大的挑战。在这种情况下,单纯简单依赖测试人力覆盖来进行质量保证,既不经济,也不现实,无法持久保证产品的整体质量。
在调研了业界实践情况并结合爱奇艺的实际情况,经过充分讨论之后,我们确定了:自动化、规范化、主动化、系统化的治理思路。具体来说,采取了下面一系列的措施:
首先,对于流程相对固定的 UI 业务场景,构造自动化测试;对于 UI 业务库接口,均要求配套相应的单元测试 case 并自动化运行,将测试人员从繁琐的重复工作中解放出来,聚焦于更复杂多变的场景;此外,在移动端引入了代码覆盖率自动统计机制(支持真机/模拟器 覆盖单次提交/整体 App)。单次提交质量和测试覆盖质量有了切实的数据评估,使得开发和测试对于改动的验证有的放矢。
其次,考虑到自动化和人力测试只能发现已经引入的缺陷,并且成本很高,并希望将缺陷尽量扼杀在编码和调试阶段,将成本降到最低。那么,如何达到这个目标呢?首先观念上大家要达成一致,经过讨论,大家都认同“最终产品的质量,需要测试保证,更需要开发来保证”,观念达成一致之后,还需要寻找有效的手段来达到最终目标。经过调研讨论,认为日志是一个很好的切入途径,理由在于:
覆盖面广:统一的日志系统能够覆盖所有模块,确保监控没有死角;
信息详实:日志由开发人员添加,输出该模块主要流程的关键节点,感兴趣的上下文和内部状态等内容,信息量远大于接口白盒测试,更不用说与黑盒测试比较了;
能持久化:日志信息随着模块代码一起迭代演化,沉淀下来。具体实施过程中,可以分期按优先级逐步累积,其成果能持久化地利用;
覆盖全流程:日志信息覆盖开发调试->测试->内测->外测->线上发布的全流程,能够在各阶段发挥其作用;
方便工具化、自动化:开发统一的日志 SDK,统一日志格式和规则,使日志方便使用、更加工具化。如果同时开发相应的配套工具,就能够在各阶段将日志的利用很大程度上自动化,提高效率,降低使用成本;
综合上述考量,团队以日志系统为核心构建了一套针对移动端的综合诊断调试系统。该系统通过对应用的开发、调试、内测、发布等各阶段提供全面的工具支持,将各类问题,以规范化、流程化的形式尽量暴露在前期,且保证问题的完整处理过程的可追溯;在发布后期,针对海量中特定用户出现的特殊场景,提供全面的诊断定位信息。此外,上传的日志、调试信息等入库之后,会对其根据规范化的格式进行统一的大数据分析处理,并与智能客服系统打通,形成上报、分析、监控、报表、智能客服、确认修复的完整闭环,在提高开发调试效率的同时,为整体质量保驾护航,整个系统可以用下面的示意图表示:
日志 SDK
日志 SDK 集成在主 App 和独立 App 中,为各模块中的关键节点提供日志监控与跟踪,目前已经覆盖了主要模块。除了日志分级、分片存储等基本功能之外,日志 SDK 还提供了不同的过滤策略、不同的存储介质、不同的上报策略以及云控远程配置功能。为了防止日志过度累积,支持存储容量限制、过期自动清理等功能,日志系统的结构如下图:
支持日志分级格式化后输出到不同的目标,除了常规的 console 输出、文件存储、服务端上传之外,还支持定向输出至日志调试器 LogDebugger、Web 查看器等,方便开发和测试人员使用。
配套工具集
日志 SDK 只是为监控和诊断等功能提供了必要的途径,为了将其充分利用起来,还需要开发配套的工具。这些工具按运行平台可以分为:客户端、桌面端、Web 端;按功能可以大体分为质量类和效率类两大类:
日志调试器
平时使用日志时遇到的一个常见的问题是,IDE 的 console 除了业务方的日志输出外,还混杂着大量的系统日志输出、第三方库的日志输出等,这些无关信息会将有效信息完全淹没。筛选出感兴趣的模块日志主要靠关键词搜索,在不知道或者没有统一关键词的情况下,开发和测试人员很难对该模块的日志输出有全局和直观的了解。为了解决该问题,方便和鼓励大家利用日志诊断定位问题,我们开发了日志调试器,通过 client-server 的结构互相连接,支持本地调试、交叉调试、内网远程调试,统一日志输出管道,这有效避免了日志信息淹没、无法有效提取的问题;同时还支持格式化显示、按 level、module、关键词监控等功能。
启动后,该工具会自动搜索发现局域网内的客户端,分别显示客户端 App 名称、版本、设备标识及连接状态等信息,并自动维护连接状态,如下图:
如果某客户端为在线状态,点击连接后即可连接至该客户端,建立连接后,该客户端通过 LogSDK 发送的日志,会实时显示在调试器窗口中,如下图所示:
日志调试器支持对日志分级别、分模块、分关键词等进行监控的功能,使用者可以对按照指定条件筛选出的日志进行导出发送。
Web 查看器
为了方便相关人员在开发、调试及内测阶段能随时有一种途径获取被调试客户端的日志及内部状态等信息,开发了 Web 查看器。Web 查看器支持查看实时/非实时日志,查看指定模块调试信息等,如下图是实时查看日志的场景:
主动诊断/调试
为了进一步提高调试效率以及应对复现机会稀少、甚至调试器本身可能会对被调试对象产生干扰(详见海森伯效应、Heisenbug)等场景,我们借鉴嵌入式开发的调试方法构造了一套主动诊断调试机制,其特点在于:
化被动为主动:单纯的日志输出与分析,还是相对被动,希望模块级别能支持一定的主动调试功能,特别是对于内部复杂的强状态模块/业务;
非接触式,甚至是在远程调试,覆盖开发、测试、发布阶段:具体来说,在开发和内测阶段,提供远程命令通道,开发人员可以通过 shell 对 App 进行交互式调试,如更精准地拉取日志、精准地获取该客户端 A/B 测试状态、相关配置信息等,同时还可以执行各模块自定义的调试指令,以获取其特定的调试信息;发布阶段,通过 push token 通道下发主动调试指令,获取客户端内部状态信息,方便更精准定位问题,为修复 bug 和改善体验提供资料。
框架可扩展:具体来说,框架实现最常用的基础调试指令,业务方可以根据其具体情况定义自己的扩展调试指令;
可调试的设计
上一节中提到了模块的主动调试机制,为什么要引入主动调试?其背景在于高复杂度的软件产品和快速的迭代节奏给可靠的测试和调试带来了巨大的困难,化解这些难题的一种做法是在设计和开发阶段,规划好调试功能,未雨绸缪,化被动为主动,这就是 Design for Debug 的思想,该思想最初来源于集成电路开发领域,后来被引入软件领域并逐步得到重视。
其实,宽泛一些来讲,在移动开发中,大到整个 App、小到某个模块、某个接口等都应该在一定程度上考虑到可调试设计。当然这是一个非常大的主题,我们目前实现的模块主动调试功能只是初步在模块层面实践了其思想,还需要进一步探索。
总结
日志系统贯通开发、调试、测试、发布的全流程,是提高效率和质量的一个很好的抓手,本文介绍了爱奇艺技术团队以其为核心构建的综合诊断调试系统及配套的相关工具。
此外还介绍了正在探索的主动诊断/调试功能的原理,更进一步地谈到了关于可调试设计的思想,这个最初源于集成电路领域的概念,在软件模块越来越复杂的今天,对我们有一定的启发。当然关于可调试设计,我们目前还处于早期探索阶段,欢迎与业界朋友们进一步交流讨论。
本文转载自公众号爱奇艺技术产品团队(ID:iQIYI-TP)。
原文链接:
评论