玖叶教程网

前端编程开发入门

移动端日志回捞探索实践(移动端日志回捞探索实践研究)

背景

日志一般用于记录用户行为路径、网络诊断、设备状态等数据,以帮助研发同学解决用户反馈的问题,提升用户体验。移动端日志一般只存在于调试控制台或本地,在收到线上用户反馈问题时,有些很诡异而又不能复现的问题,研发同学无法通过远程拿到用户日志,在分析排查问题时,就缺少有效的日志数据支撑,排查问题效率比较低。为了帮助业务线更好的为用户提供服务,提高排查线上异常问题效率,因此我们研发移动端日志回捞基础组件。

需求分析

我们总结业务需求,主要有以下几点:

  • 安全稳定,必须保证写入的日志安全,以防敏感数据泄露,同时写入操作持续时间和应用的生命周期基本一致,不能因为日志组件影响应用程序性能,导致整个应用的崩溃、卡顿不流畅。
  • 完整不丢失,日志组件本身就是为排查异常问题而生,在应用前台、后台常规场景,被杀掉、崩溃等极端场景下,也要保证日志的完整性。
  • 精准回捞,能准确回捞多用户、多设备交叉情况下用户反馈的日志。
  • 高效存储,在需要满足回捞15~30天日志的条件下,尽量少占用用户设备的存储空间。


技术方案

整体设计

日志回捞,首先是收集移动端内部日志,把日志写入到本地,当用户反馈问题时,管理平台根据用户提供的手机号或用户标识进行标记,SDK根据探测按需上报移动端日志,研发同学根据日志数据,分析用户行为轨迹,排查移动端线上异常问题,其中,日志组件SDK目前支持iOS、Android平台。

日志回捞系统按功能可分为三部分:

日志写入

使用私有日志协议,引入了mmap机制,异步写入日志,保障日志的高效写入,同时采用流式压缩加密,保障日志安全,提升存储利用率。

日志回捞

提供设备、用户关联、探测上报、上传日志等能力,使用内部的WF开发Web服务器,WTable做逻辑存储,WOS做日志存储。

管理平台

主要提供了简易的操作页面,包括用户日志的标记、查询、下载、多设备关联等功能。

日志写入

日志写入是日志回捞组件的核心基石。写入模块采用C/C++实现跨平台写入,使用私有日志协议,支持异步多文件写入(业务标签),支持日志等级区分、支持日志自动填充时间、线程、方法等信息,支持安卓多进程。

日志写入流程时,首先通过 mmap 使用逻辑内存对硬盘文件进行映射,如果映射成功则使用 mmap 做为日志的缓存,否则启用内存 Buffer备选方案,然后进行流式压缩加密,以一个一个的压缩单元流进行,超过设置缓存阈值,写入缓存中,缓存打满阈值或者主动调用,将日志数据回写硬盘文件。

这里面涉及几个问题:

1、可不可以不用缓存,直接压缩加密写入文件 ?

首先,看下文件的写入流程,调用 fwrite() 把数据写到应用进程缓冲区,当缓冲区满了或者调用 fflush() 冲洗缓冲区, 系统会调用 write() 将数据写到内核页缓存,页被修改后成为脏页,由 pdflush 进程定时将脏页写回磁盘,定期的将内核缓冲区写入硬盘(也可以手工fsync控制回写)。

可以看出,在将数据写回到磁盘时,需要经历两次拷贝。一次是把数据从用户态拷贝到内核态,需要经历上下文切换;还有一次是内核缓冲区到硬盘。当日志写入次数过于频繁、数量过大,CPU峰值增加,整体性能也会下降,影响应用的流畅度。

由于稳定性、流畅性的要求,使用缓存策略加速提高性能,变成为了一个必选项。

2、为什么要引入 MMAP 机制?

使用内存缓存有一个致命的缺点,极容易造成日志丢失,日志在没有达满阈值时,进程被 kill 掉了,缓存的数据就丢了,引入 MMAP 机制,就是解决丢失问题,同时不需要用户态、内核态拷贝数据,切换上下文,具有内存级别的缓存速度。

使用 MMAP 还能保证日志的完整性,因为如下这些情况下会自动回写磁盘:

  • 内存不足
  • 进程 crash
  • 调用 msync 或者 munmap
  • 不设置 MAP_NOSYNC 情况下 30s-60s(仅限FreeBSD)

下面具体看下 MMAP 机制:

MMAP是一种内存映射文件的方法,将页缓存映射到用户进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对应关系,使得应用程序可以像使用内存指针一样访问文件,MMAP访问页缓存的方式在内核中是采用请求页面机制实现的。

首先,应用程序调用mmap(步骤1),陷入到内核中后调用do_mmap_pgoff(步骤2)。该函数从应用程序的地址空间中分配一段区域作为映射的内存地址,并使用一个VMA(vm_area_struct)结构代表该区域,之后就返回到应用程序(步骤3)。当应用程序访问mmap所返回的地址指针时(步骤4),由于虚实映射尚未建立,会触发缺页中断(步骤5)。之后系统会调用缺页中断处理函数(步骤6),在缺页中断处理函数中,内核通过相应区域的VMA结构判断出该区域属于文件映射,于是调用具体文件系统的接口读入相应的Page Cache项(步骤7、8、9),并填写相应的虚实映射表。经过这些步骤之后,应用程序就可以正常访问相应的内存区域了。

有时 MMAP 映射会失败,把内存缓存也作为补充策略,这种情况设备有时会意外挂掉关机,页缓存中的数据就会丢失,适当时机需要调用fsync立即将页缓存中的数据回写到磁盘中。

3、为什么选择流式压缩?

日志的大小的问题,一方面涉及占用用户的设备存储空间,另一方面上传日志销毁流量,上传时长影响回捞成功率。日志加密后集中压缩上传,优点是压缩比较高,缺点是占用设备存储空间太大,还容易造成CPU峰值飙升,存在不足。流式压缩,随着日志写入同时进行压缩(5k阈值),优点占用存储空间少,把压缩时间分散在整个生命周期内,避免CPU峰值飙升问题,缺点压缩比相对低些,考虑到稳定性、流程性、存储空间占用,最终,采用流式压缩加密的方式。

在实践中,我们发现压缩块的数据,在未打满时,杀进程时这部分数据也容易丢失,而且压缩流状态不可恢复,就会造成即使写入到MMAP缓存中,日志解析也会失败,为了降低这种事情发生,我们捕获进程事件,在被杀掉、异常时等场景处理压缩块闭包问题。

4、多进程问题

Android 平台第一个想到的就是 ContentProvider:一个单独进程管理数据,数据同步不易出错,简单好用易上手。然而它的问题也很明显,就是一个字慢:启动慢,访问也慢,不符合我们性能的要求。基于我们日志的业务需求,目前主要采用进程分文件策略。

日志回捞

回捞方案回捞探测整体以短连接探测上报为主,长连接静默推(WPush)送为辅,其中短连接触发时机为APP启动、进入后台,选择短连接,一方面是可以快速开发上线,其次,是我们目前的业务场景对实时性需求不是很高。

在安全方面,短连接接口均使用非对称密钥对对称密钥Key做加密,防止密钥Key被破解,从而在网络层保证日志安全。

回捞中还有一个核心的问题,怎么找到反馈用户的设备?下面聊一聊 WLog 是如何选择的。

以用户为核心的回捞方案

日志回捞,我们首先想到的是现有的系统方案,以用户为核心的微聊日志回捞方案,用户反馈问题,标记用户,长连接向用户下发指令,客户端收到指令并上传日志。然而58集团的很多产品账号系统,允许同一账号在多台设备同时登录的情况,那么每台设备都登陆了同一个账号,该怎么精准回捞?多个账号再同一台设备登陆呢?如果我有多个账号,同时又多设备,又改怎么精准回捞?

存在的问题

  • 单用户对多设备,每台设备都是标记用户A, 当服务端标记了用户A,然后 设备1、2、3 有一台设备上报了日志,服务端都会消除用户A的标记,因为服务端不知道用户登陆了几台设备,这样就无法保证回捞日志的准确性。
  • 多用户对单设备,多个账号再同一台设备登陆,但是这几个用户都反馈问题,同时被标记上,会造成同一份日志多次重复上传。

讨论后,发现不符合我们的预期。

以设备为核心的回捞方案


设备回捞的核心是找到反馈用户与设备的关系,以及确定日志的唯一性,我们提出单纯的设备中的日志与用户无关,用户与设备之间是仅为一种动态映射关系,通过设备指纹确定一台设备,设备+BundleID/包名+时间 确定一条日志。

这样一来,用户反馈问题时,通过映射关系查询到用户关系的设备指纹,从而标记设备指纹,对应设备指纹日志上传标记消除。由于没有合适的时机解映射关系,因此我们以时间维度维护其生命周期,保证其是收敛的。

对于刚才提到问题解决办法

  • 单用户对多设备,根据映射关系会标记多个设备,多个设备会均会上传日志。
  • 多用户对单设备,根据映射关系会标记单个设备,也不存在重复上传问题。

同时,我们的管理平台也是根据回捞数据,在查询用户日志时,会显示多个设备上传日志的情况,已上传设备、未上传设备、设备最近状态,方案总体上满足预期需求。

管理平台

简易的管理平台支持能力

  • 支持多用户标识,包括手机号、用户ID、登录名等。
  • 支持用户日志的标记、查询、下载功能。
  • 支持单用户多设备映射关系展示及状态。
  • 支持多参数检索,包括包名、BundleID、平台类型、时间等


总结

本文主要从日志写入、日志回捞、管理平台三个方面介绍了58客户端日志回捞一些实践,解决目前内部客户端回捞日志远程回捞难的问题。其中,在有些方面还有些不足需要后续探索和优化,欢迎感兴趣的同学和我们一块交流。


欢迎大家关注“58架构师”微信公众号,定期分享云计算、AI、区块链、大数据、搜索、推荐、存储、中间件、移动、前端、运维等方面的前沿技术和实践经验。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言