移动端日志收集方案对比

日志收集框架的作用

相信大家在开发应用的时候,总会遇到bug,这个时候,如果bug是在我们本地开发的过程中发现的,那么我们把手机插入android studio进行联调,就可以马上定位到出错的堆栈,报错的信息。但是,在我们的应用发布出去,给用户使用的时候,如果出现了bug,那么,我们就很难定位出问题的所在。一般来说,我们需要使用错误上报系统,把错误上报给我们,比如腾讯的bugly,阿里的友盟等第三方的错误收集插件。但是很多时候,bugly上报的错误并不能正确的把我们需要的错误信息反馈给我们,或者说,我们无法定位在出现了bug的时候,用户之前的操作是什么。在应用没有集成日志收集框架的时候,我们发现一个bug需要怎么做呢?首先,我们需要联系到这个用户,争取获得用户的配合。然后我们就发一个私包给用户,用户安装后,重现刚才的bug,然后我们把操作日志写入本地的错误日志中,让用户通过交流软件发给我们,或者我们在测试间,像个无头苍蝇一样,盲目的点击各个区域,尝试寻找bug重现的路线。基于日志收集框架,我们可以收集线上bug出现时的关联动作、关键内容,将bug与上下文信息上报到日志中控系统,以便分析解决问题。

日志收集方案

网上现在有的日志收集框架包括log4j、logback、CocoaLumberjack、Xlog、Logan、Glog(谷歌)等等。同时能适用于Android和iOS两个平台的有Xlog和Logan、Glog,Glog没有提供上层封装,而且没有使用mmap,在性能、开发成本和日志丢失处理方面要逊色与Xlog和Logan,所以这里不对它进行对比测试。表1是针对前两者方案进行对比。

图1

mmap:

mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。如下图所示:

图2

使用mmap的好处:

1、解决因为程序被系统杀掉,或者发生了 crash,crash捕捉模块没有捕捉到导致部分时间点没有日志,保证程序整个生命周期内都有日志。

2、解决因为部分数据损坏就影响了整个日志文件,最小化数据损坏对日志文件的影响。

3、可以直接通过操作内存来读写文件,性能上接近直接读写内存。针对一次写文件,节省了用户态到内核态切换的开销,也减少了数据拷贝的次数,提高了文件读写效率。

Xlog

大体流程:

图3

简单来说就是三步:

1. 将要打的日志附上各种信息(进程、线程、日期)并格式化。

2. 将日志写入高速缓冲区。这块高速缓冲区是使用mmap映射出来的内存区,被映射的磁盘文件是它新建的一个缓存文件,.mmap2后缀(若mmap失败,则用内存缓存代替)。每次打log时首先将它写入高速缓存,这样当使用mmap时可以保证这条log快速地被写入磁盘。

3. 当高速缓冲区内容写到一定阈值时(此处为1/3),通知后台线程将缓冲区的内容写入文件。

它使用mmap映射一块固定长度的文件,这样保证每条log第一时间都被写入磁盘,由于每次将log写入目标文件时都会清空高速缓冲区,所以高速缓冲区的内容可以认为没有被写入文件,每次启动时可以检查缓冲区,若有数据则将它先写入目标文件,达到**不丢日志**的效果。

源码分析:

xlog的代码主要分为两块,面向上层的使用封装xlogger,暴露了一系列的接口。以及核心的appender和log等。

log_buffer:

log_buffer其目的是封装了一个对mmap/传统内存操作的数据结构。其核心思想就是将上层的操作转换对实际开辟出来的日志缓存地址进行读写(也封装了加密压缩操作等等)。截图中的注释标注了具体实现步骤

图4
图5

整体上就是对写入的数据进行加密,如果有压缩的需求同时进行压缩。并将修改后的数据存入真正的mmap文件/内存缓存中。

图6

appender:

xlog方案真正的核心实际上只有一个appender文件,本质上的思路都比较清晰,将添加日志分为同步写和异步写。异步写的方式比较常用,同步写一般官方建议只用于Debug期。

图7

注意点1: 如果我们尝试打开mmap成功了,但是mmap对应的数据地址是NULL,那我们必须停止映射。因为NULL所代表的地址处于内核态,一旦映射了,势必造成Crash。

注意点2:使用mmap的情况下,如果上次应用断电了、Crash,日志的信息还是存在的,但是并不一定能及时的转换成我们想要的日志文件。因此我们下次使用前首先检查下mmap文件里面有没有数据,有的话先把这部分转换成日志。

而通过上层添加的日志,都会通过之前的xlogger_appender进行调用,进而往下层的__appender_async 记录日志。

__appender_async:

__appender_async 需要和其异步dump线程一起搭配看,是两段非常有意思的代码,它涉及了一个将mmap/内存数据写回到磁盘的策略。

图8

其次是异步线程Dump成日志

图9

Logan:

代码整体来说比较简单清晰,上层应用入口只是做一些简单的初始化工作。其中包括可以设置以下参数:(1)、mmap文件的缓存路径;(2)、日志切片大小,默认10M;(3)、日志缓存天数,默认7天;(4)、ASE加密的key;(5)、ASE加密的IV;(6)、SD卡最小额外存储,默认50M

图10

同时,框架封装好了日志上传的回调接口,只要实现SendLogRunnable中的sendLog回调方法,就可以实现将日志上传到对应的服务器地址。

图11

Logan默认的日志输出格式如下:

{"c":"clogan header","f":1,"l":1550048816507,"n":"clogan","i":1,"m":true}

图12

默认格式主要是由底层C的实现逻辑决定,也可以自己改写底层clogan_write方法及JNI接口,改变日志的数据存储结构。

图13

clogan_core.c:

clogan_core是Logan中的主要核心逻辑

图14

在写入之前,首先判断写入的一次写入的数据大小是否超过10M,如果超过10M,则拒绝写入。

图15

判断写入mmap的逻辑与Xlog大同小异,

1、判断mmap是否可用,不可用则启用内存

2、判断mmap是否有历史数据,有则将历史数据写出文件。

图16

最后磁盘的回写策略也是mmap超过三分之一时进行。

图17

总结:

Xlog和 Logan的性能和能力上相近,Xlog在细节上更优于Logan,例如对文件写入无限制,提供异步写入日志接口,包括文档的完整性及社区热度都更优越,所以推荐使用腾讯Xlog。

你可能感兴趣的:(移动端日志收集方案对比)