01.设计高效日志框架库
目录介绍
- 01.整体概述
- 1.1 项目背景说明
- 1.2 考虑的事情
- 1.3 基础概念介绍
- 1.4 设计目标
- 1.5 产生收益分析
- 02.日志存储设计
- 2.1 日志存储总体设计
- 2.2 存储日志信息设计
- 2.3 日志保存策略设计
- 2.4 日志自动清理设计
- 2.5 存储数据队列设计
- 2.6 如何写数据的设计
- 03.高效日志库调研
- 3.1 高效日志方案
- 3.2 mmap日志分析
- 3.3 XLog介绍
- 3.4 Logan介绍
- 3.5 为何自研日志库
- 04.方案基础设计
- 4.1 整体架构图
- 4.2 UML设计图
- 4.3 关键流程图
- 4.4 接口设计图
- 4.5 模块间依赖关系
- 05.日志存储设实践
- 5.1 日志文件路径设置
- 5.2
- 5.3
- 5.4 组装log日志设计
- 5.5 日志文件创建机制
- 5.6 日志缓存的策略
- 5.8 日志存储GZip压缩
- 5.9 多进程存储问题
- 06.关键技术点说明
- 6.1 aidl通信步骤
- 6.2 跨进程启动服务
- 6.3 跨进程传输日志
- 6.4 日志写入操作
- 6.5 日志如何追加写入
- 6.6 日志切分存储
- 6.7 日志自动清除操作
- 6.9 遇到的一些问题
- 07.其他设计说明
- 7.1 性能设计
- 7.2 稳定性设计
- 7.3 灰度设计
- 7.4 降级设计
- 7.5 异常设计
01.整体概述
1.1 项目背景说明
1.2 考虑的事情
- 日志系统设计需要考虑的问题:
- 1.需要保证日志系统收集到的数据的有效性和完整性,在app崩溃时日志不丢失。
- 2.保证收集日志的过程不影响app的性能,不能占用过高cpu资源,或者频繁IO造成卡顿现象。
1.3 基础概念介绍
- 常见术语概念解析
- appender:主要控制日志输出到哪里,比如:文件、数据库、控制台打印等
- logger: 用来设置某一个包或者具体某一个类的日志打印级别、以及指定appender
1.4 设计目标
1.5 产生收益分析
02.日志存储设计
2.1 日志存储总体设计
2.2 存储日志信息设计
- 比如,打印日志如下所示:
- LogUtils.i("Log test info : click text view test")
- 日志组装后输出到控制台
- yc_log:: [(LogTestActivity.kt:110)#OnClick] Log test info : click text view test
- 携带方法和的类的源文件行号,支持点击跳转到源文件。
- 那么如何组装数据
- 获取当前堆栈信息,然后获取指定索引处的className和methodName。也就是:(LogTestActivity.kt:110)#OnClick
- 从中获取方法执行的线程相关的信息,以及执行的方法名称等。这些信息能帮助我们更好的查找问题之所在。
2.3 日志保存策略设计
- 日志保存策略设计大概如下
- 由于保存日志的过程是个耗时过程,我们需要开启线程去保存。但是日志产生的频率可能很高,又不能采用一般的线程去处理,太多的线程也会损耗性能。所以应该考虑队列的形式保存日志,然后一条一条的去保存。
2.4 独立进程写数据
- 使用独立的进程去写数据
- 第一步:在初始化的时候,bind绑定一个Service,主要是建立进程通信连接。然后获取aidl接口对象。
2.5 存储数据队列设计
- 采集一次日志就保存到file文件中吗
- 这个采集过程,如果是每产生一次日志数据,就写一次文件,势必造成系统IO调用的频繁操作,可能会造成app运行卡顿现象。因此采用队列保存日志,当达到某数值则开始写入到文件,简单来说是追加写入。
- 队列缓存大小,单位(条),也可以动态修改容积。
- 比如进入日志高频触发区,则将缓存容积扩大到100条,进入低频触发区就缩小到10条。容积缩小时,如果缓存内条目数已经大于目标条数,自动触发flush。
- 在初始化的时候,创建一个日志队列线程并开始
- 开启while循环,从日志队列取数据,如果有数据则doFlush()发送到LogService,通过Aidl进行数据发送。
2.6 如何写数据的设计
- 如何写数据的设计
- 创建Writer对象,调用writer.write(string)进行写入数据。
- flush写数据时机,粗略分为四种情况:
- 缓存池满、应用从前台进入后台、应用从后台恢复至前台、手动代码调用。手动代码调用一般发生在进入超高频触发区之前,或者是跨功能模块时,以及用户交互需求日志上传时。
03.高效日志库调研
3.1 高效日志方案
- 移动日志系统使用了Linux系统中提供的mmap作为日志文件的载体
- 目前业内流行的XLOG日志组件、MMKV、美团Logan均采用了此方案,其最大的优势就是高效I/O、低损耗、跨进程 等优势。
3.2 mmap日志分析
- 对于移动端日志采集SDK来说
- 主要进行的工作就是将用户写入的数据保存到文件中,在这个过程中涉及到在native层调用mmap函数实现在进程虚拟内存地址空间中分配地址空间,创建和物理内存的映射关系。
3.3 XLog介绍
3.4 Logan介绍
- 大概的原理介绍
- Logan通过Native方式来实现日志底层的核心逻辑,也就是C编写底层库。收集日志时,在C层实现流式的压缩和加密数据,可以减少CPU峰值,使程序运行更加顺滑。而且先压缩再加密的方式压缩率比较高,整体效率较高。加密方式为AES。
- Logan对日志协议数据进行格式化处理,针对大日志的分片处理,引入了MMAP机制解决了日志丢失问题,使用AES进行日志加密确保日志安全性。
- 使用mmap技术优化
- mmap: 是一种内存映射文件的方法,它将一个文件映射到进程的地址空间中,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系
- 实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上
3.5 为何自研日志库
04.方案基础设计
4.1 整体架构图
4.2 UML设计图
4.3 关键流程图
4.4 接口设计图
4.5 模块间依赖关系
05.日志存储设实践
5.1 日志文件路径设置
- Java创建file文件路径后,如何使用c++将日志写到对应的文件中。这个时候,就需要对文件路径设置
/** * 初始化 * * @param bufferPath 缓冲路径 * @param bufferSize 缓冲大小,目前是4096 * @param logPath 日志路径 * @param compress 是否压缩 * @return 返回 */ private native static long initNative(String bufferPath, int bufferSize, String logPath, boolean compress);
- 然后通过c++对该路径文件进行打开即可:
06.其他设计说明
6.1 性能设计
6.2 稳定性设计
6.3 灰度设计
6.4 降级设计
6.5 异常设计
07.其他说明介绍
7.1 参考链接
- 京东零售云mPaaS移动端日志回捞探索实践
- https://www.51cto.com/article/682747.html
- 美团开源移动端基础日志库
- https://tech.meituan.com/2018/10/11/logan-open-source.html
- Android跨进程启动Service流程及常见问题
- https://blog.csdn.net/One_Month/article/details/80255893
- Android进阶——AIDL详解 https://blog.csdn.net/ly0724ok/article/details/117450121/
- Android中AIDL使用案例
- https://www.jianshu.com/p/b174f14a2d2f
- 货拉拉客户端通用日志组件 - Glog
- https://juejin.cn/post/7168662263337861133
- 日志存储GZip压缩
- https://juejin.cn/post/7083634028640731143