文末可获取进入小专栏读者群方式。

正文

小专栏的读者好,在《亿级Android架构》的前几篇文章里,我们围绕网络的连接优化、长连接、安全演进等多个方面讲解了国内大厂的网络架构体系,其中涉及了微信Mars、美团的Shark和阿里、蚂蚁的高可用网络体系等。

这一次,我们再来谈一谈在Android架构里平时默默无闻但关键时刻却至关重要的日志系统。

日志系统

日志系统对移动应用而言非常重要,它可以帮助我们定位崩溃、统计用户行为、发现代码运行问题等。这次我们结合美团的Logan、微信的xLog等著名开源项目,谈一谈如何搭建一套高性能的日志系统和其中核心的技术原理。

在文章开始前,读者可以先考虑一下几个问题:

很多开发者应该清楚我们只会在Debug环境下让Logcat去打印日志,一旦上线则会关闭这个开关,以防止过多IO操作影响到App正常运行。但这些日志在定位问题时却很重要,如何做到平衡呢?

日志在我们定位某些Crash时非常有用,但常常遇到的问题是在Crash发生时,我们的日志还没来得及写入文件,导致不完整,如何解决呢?

日志如何上报给后端呢?比如老板或者某位用户要你定位某个问题,而这个问题你却无法复现,如何快速获取这个用户手机上的日志呢?

c69a70dd6ad718396c8a0e9458d1c57a.png

I. 高性能背后的核心技术

对于xLog或Logan有一定了解的同学应该知道,这两个库的重点在于解决了移动端日志系统的流畅性、完整性、安全性、容错性。下面我们针对这些特性,阐述下xLog或Logan是如何解决的。

日志系统的基本流程就是采集代码里产生的各种日志数据,通过IO写入到文件。

首先我们假设,每次产生一条日志,我们就立即写入文件。这样子可以保证日志所有日志都能安全落地,不会发生丢失。但是,这里面存在频繁的IO操作,这会导致App自身业务逻辑运行变慢,出现卡顿等现象。

那么,我们考虑先将日志写入内存缓存,等达到一定量的时候,再一起压缩并写入文件。这样的话对于App流畅性就没有多大影响,但带来了新的问题:

如果在写入文件前App出现Crash,那么这部分日志就丢失了;

每次写入文件时,都需要做一次集中压缩和加密,这些是CPU密集型操作,容易出现CPU波峰和卡顿;

那么,如何既能保证App的流畅性,又能保证日志的完整性呢?可以想象下,我们需要的方案要符合下面几点特征:

不实时写入文件,防止频繁IO,但是又不存在丢失风险;

将压缩分摊成多次,而非一次集中压缩;

1. MMAP机制

针对第一点:不实时写入文件,防止频繁IO,但是又不存在丢失风险,xLog和Logan都采用了相同的系统机制:MMAP。MMAP是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对应关系。

6045e416ef073bc2747812411156a5c1.png

简单来说,MMAP实际是一段内存空间,我们可以实时将日志写入MMAP,因为它是内存,所以速度非常快;而另外,它也不存在丢失风险,因为在如内存不足,进程退出的时候操作系统会自动回写文件,因此也极大降低了丢失的风险。

如果对于MMAP的具体代码感兴趣,可以参考Logan里的mmap_util.c,底层使用了NDK里提供的mman.h内部的void* mmap(void* __addr, size_t __size, int __prot, int __flags, int __fd, off_t __offset);方法。

2. xLog的短语式压缩

在压缩方面,xLog采用的方案是每产生一条日志,都进行压缩且写入MMAP。这里采用的压缩算法是LZ77压缩。压缩率可以达到83.7%。

LZ77是一种基于字典的算法,它将长字符串(也称为短语)编码成短小的标记,用小标记代替字典中的短语,从而达到压缩的目的。感兴趣的同学可以在文末找到相关资料。

这种压缩算法的核心就是从以前压缩的内容里找到相同的短语,然后把当前的文本短语换成数字,参考下图:

de82f751cd8fb163219f3aca3f360cff.png

3. Logan的流式Gzip压缩

Logan里采用了流式Gzip压缩方式,通过阅读Logan压缩代码 zlib_util.c可以看出,每次以16KB为单位,通过z_stream将日志流式进行Gzip压缩。

通过流式可以避免CPU的峰值,而且压缩效率也不错。当然,它并没有很好的解决日志丢失的问题,如果系统Crash,那么这16KB的日志便有可能丢失,虽然相对而言丢失的单元已经减小很多。

4. GC优化

大量的日志的生成如果放在Java堆内存里,肯定会造成频繁的GC,从而导致卡顿。因此,xLog和Logan都是采用C层实现,不会造成Java GC问题,而且,也能够轻松实现跨平台,在Android、iOS等多种平台都能兼容运行。

通过上面的几种方式,可以实现日志系统的流畅性、完整性等高性能特点。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐