背景
对于移动应用来说,日志库是必不可少的基础设施,美团点评集团旗下移动应用每天产生的众多种类的日志数据已经达到几十亿量级。为了解决日志模块普遍存在的效率、安全性、丢失日志等问题,Logan 基础日志库应运而生。
现存问题
目前,业内移动端日志库大多都存在以下几个问题:
卡顿,影响性能
日志丢失
安全性
日志分散
首先,日志模块作为底层的基础库,对上层的性能影响必须尽量小,但是日志的写操作是非常高频的,频繁在 Java 堆里操作数据容易导致 GC 的发生,从而引起应用卡顿,而频繁的 I/O 操作也很容易导致 CPU 占用过高,甚至出现 CPU 峰值,从而影响应用性能。
其次,日志丢失的场景也很常见,例如当用户的 App 发生了崩溃,崩溃日志还来不及写入文件,程序就退出了,但本次崩溃产生的日志就会丢失。对于开发者来说,这种情况是非常致命的,因为这类日志丢失,意味着无法复现用户的崩溃场景,很多问题依然得不到解决。
第三点,日志的安全性也是至关重要的,绝对不能随意被破解成明文,也要防止网络被劫持导致的日志泄漏。
最后一点,对于移动应用来说,日志肯定不止一种,一般会包含端到端日志、代码日志、崩溃日志、埋点日志这几种,甚至会更多。不同种类的日志都具有各自的特点,会导致日志比较分散,查一个问题需要在各个不同的日志平台查不同的日志,例如端到端日志还存在日志采样,这无疑增加了开发者定位问题的成本。
面对美团点评几十亿量级的移动端日志处理场景,这些问题会被无限放大,最终可能导致日志模块不稳定、不可用。然而,Logan 应运而生,漂亮地解决了上述问题。
简介
Logan,名称是 Log 和 An 的组合,代表个体日志服务的意思,同时也是金刚狼大叔的大名。通俗点说,Logan 是美团点评移动端底层的基础日志库,可以在本地存储各种类型的日志,在需要时可以对数据进行回捞和分析。
Logan 具备两个核心能力:本地存储和日志捞取。作为基础日志库,Logan 已经接入了集团众多日志系统,例如端到端日志、用户行为日志、代码级日志、崩溃日志等。作为移动应用的幕后英雄,Logan 每天都会处理几十亿量级的移动端日志。
设计
作为一款基础日志库,在设计之初就必须考虑如何解决日志系统现存的一些问题。
卡顿,影响性能
I/O 是比较耗性能的操作,写日志需要大量的 I/O 操作,为了提升性能,首先要减少 I/O 操作,最有效的措施就是加缓存。先把日志缓存到内存中,达到一定大小的时候再写入文件。为了减少写入本地的日志大小,需要对数据进行压缩,为了增强日志的安全性,需要对日志进行加密。然而这样做的弊端是:
对 Android 来说,对日志加密压缩等操作全部在 Java 堆里面。由于日志写入是一个高频的动作,频繁地堆内存操作,容易引发 Java 的 GC,导致应用卡顿;
集中压缩会导致 CPU 短时间飙高,出现峰值;
由于日志是内存缓存,在杀进程、Crash 的时候,容易丢失内存数据,从而导致日志丢失。
Logan 的解决方案是通过 Native 方式来实现日志底层的核心逻辑,也就是 C 编写底层库。这样做不光能解决 Java GC 问题,还做到了一份代码运行在 Android 和 iOS 两个平台上。同时在 C 层实现流式的压缩和加密数据,可以减少 CPU 峰值,使程序运行更加顺滑。而且先压缩再加密的方式压缩率比较高,整体效率较高,所以这个顺序不能变。
日志丢失
加缓存之后,异常退出丢失日志的问题就必须解决,Logan 为此引入了MMAP 机制。MMAP 是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对应关系。MMAP 机制的优势是:
MMAP 使用逻辑内存对磁盘文件进行映射,操作内存就相当于操作文件;
经过测试发现,操作 MMAP 的速度和操作内存的速度一样快,可以用 MMAP 来做数据缓存;
MMAP 将日志回写时机交给操作系统控制。如内存不足,进程退出的时候操作系统会自动回写文件;
MMAP 对文件的读写操作不需要页缓存,只需要从磁盘到用户主存的一次数据拷贝过程,减少了数据的拷贝次数,提高了文件读写效率。
引入 MMAP 机制之后,日志丢失问题得到了有效解决,同时也提升了性能。不过这种方式也不能百分百解决日志丢失的问题,MMAP 存在初始化失败的情况,这时候 Logan 会初始化堆内存来做日志缓存。根据我们统计的数据来看,MMAP 初始化失败的情况仅占 0.002%,已经是一个小概率事件了。
安全性
日志文件的安全性必须得到保障,不能随意被破解,更不能明文存储。Logan 采用了流式加密的方式,使用对称密钥加密日志数据,存储到本地。同时在日志上传时,使用非对称密钥对对称密钥 Key 做加密上传,防止密钥 Key 被破解,从而在网络层保证日志安全。
日志分散
针对日志分散的情况,为了保证日志全面,需要做本地聚合存储。Logan 采用了自研的日志协议,对于不同种类的日志都会按照 Logan 日志协议进行格式化处理,存储到本地。当需要上报的时候进行集中上报,通过 Logan 日志协议进行反解,还原出不同日志的原本面貌。同时 Logan 后台提供了聚合展示的能力,全面展示日志内容,根据协议综合各种日志进行分析,使用时间轴等方式展示不同种日志的重要信息,使得开发者只需要通过 Logan 平台就可以查询到某一段时间 App 到底产生了哪些日志,可以快速复现问题场景,定位问题并处理。
关于 Logan 平台是如何展示日志的,下文会再进行说明。
架构
首先,看一下 Logan 的整体架构图:
Logan 的整体架构图
Logan 自研的日志协议解决了日志本地聚合存储的问题,采用先压缩再加密的顺序,使用流式的加密和压缩,避免了CPU 峰值,同时减少了CPU 使用。跨平台 C 库提供了日志协议数据的格式化处理,针对大日志的分片处理,引入了MMAP 机制解决了日志丢失问题,使用 AES 进行日志加密确保日志安全性,并且提供了主动上报接口。Logan 核心逻辑都在 C 层完成,提供了跨平台支持的能力,在解决痛点问题的同时,也大大提升了性能。
日志分片
Logan 作为日志底层库,需要考虑上层传入日志过大的情况。针对这样的场景,Logan 会做日志分片处理。以 20k 大小做分片,每个切片按照 Logan 的协议进行存储,上报到 Logan 后台的时候再做反解合并,恢复日志本来的面貌。
那么 Logan 是如何进行日志写入的呢?下图为 Logan 写日志的流程:
Logan 写日志的流程
性能
为了检测 Logan 的性能优化效果,我们专门写了测试程序进行对比,读取 16000行的日志文本,间隔 3ms,依次调用写日志函数。
首先对比 Java 实现和 C 实现的内存状况:
Java:
C:
可以看出 Java 实现写日志,GC 频繁,而 C 实现并不会出现这种情况,因为它不会占用 Java 的堆内存。那么再对比一下 Java 实现和 C 实现的 CPU 使用情况:
C 实现没有频繁的 GC,同时采用流式的压缩和加密避免了集中压缩加密可能产生的 CPU 峰值,所以 CPU 平均使用率会降低,如上图所示。
特色功能
日志回捞
开发者可能都会遇到类似的场景:某个用户手机上装了App,出现了崩溃或者其它问题,日志还没上报或者上报过程中被网络劫持发生日志丢失,导致有些问题一直查不清原因,或者没法及时定位到问题,影响处理进程。依托集团 PushSDK 强大的推送能力,Logan 可以确保用户的本地日志在发出捞取指令后及时上传。通过网络类型和日志大小上限选择,可以为用户最大可能的节省移动流量。
回馈机制可以确保捞取日志任务的进度得到实时展现。
日志回捞平台有着严格的审核机制,确保开发者不会侵犯用户隐私,只关注问题场景。
主动上报
Logan 日志回捞,依赖于 Push 透传。客户端被唤醒接收 Push 消息,受到一些条件影响:
Android 想要后台唤醒 App,需要确保 Push 进程在后台存活;
iOS 想要后台唤醒 APP,需要确保用户开启后台刷新开关;
网络环境太差,Android 上 Push 长连建立不成功。
如果无法唤醒 App,只有在用户再次进入 App 时,Push 通道建立后才能收到推送消息,以上是导致 Logan 日志回捞会有延迟或收不到的根本原因,从分析可以看出,Logan 系统回捞的最大瓶颈在于 Push 系统。那么能否抛开 Push 系统得到 Logan 日志呢?先来看一下使用日志回捞方式的典型场景:
其中最大的障碍在于 Push 触达用户。那么主动上报的设计思路是怎样的呢?
通过在 App 中主动调用上报接口,用户直接上报日志的方式,称之为 Logan 的主动上报。主动上报的优势非常明显,跳过了Push 系统,让用户在需要的时候主动上报 Logan 日志,开发者再也不用为不能及时捞到日志而烦恼,在用户投诉之前就已经拿到日志,便于更高效地分析解决问题。
线上效果
Logan 基础日志库自 2017年9 月上线以来,运行非常稳定,大大提高了集团移动开发工程师分析日志、定位线上问题的效率。
Logan 平台时间轴日志展示:
Logan 日志聚合详情展示:
作为基础日志库,Logan 目前已经接入了集团众多日志系统:
CAT 端到端日志
埋点日志
用户行为日志
代码级日志
网络内部日志
Push 日志
Crash 崩溃日志
现在,Logan 已经接入美团、大众点评、美团外卖、猫眼等众多 App,日志种类也更加丰富。
展望未来
H5 SDK
目前,Logan 只有移动端版本,支持 Android/iOS 系统,暂不支持 H5 的日志上报。对于纯 JS 开发的页面来说,同样有日志分散、问题场景复现困难等痛点,也迫切需要类似的日志底层库。我们计划统一 H5 和 Native 的日志底层库,包括日志协议、聚合等,Logan 的 H5 SDK 也在筹备中。
日志分析
Logan 平台的日志展示方式,我们还在探索中。未来计划对日志做初步的机器分析与处理,能针对某些关键路径给出一些分析结果,让开发者更专注于业务问题的定位与分析,同时希望分析出用户的行为是否存在风险、恶意请求等。
思考题
本文给大家讲述了美团点评移动端底层基础日志库 Logan 的设计、架构与特色,Logan 在解决了许多问题的同时,也会带来新的问题。日志文件不能无限大,目前 Logan 日志文件最大限制为 10M,遇到大于 10M 的情况,应该如何处理最佳?是丢掉前面的日志,还是丢掉追加的日志,还是做分片处理呢?这是一个值得深思的问题。
作者简介
白帆,美团点评基础框架研发组 Android 技术专家。2015年加入原大众点评。先后负责客户端长连网络 SDK、Logan 日志 SDK 等项目。目前主导 Logan 日志 SDK 的开发和推广。
立成,美团点评基础框架研发组 Android 高级开发工程师。2016年加入美团点评,在 Android 开发、跨平台开发、移动端测试等领域有一定的实践经验,热爱新技术并愿意付诸实践,致力于产出高质量代码。
解释说明
端到端(end-to-end),端到端是网络连接。网络要通信,必须建立连接,不管有多远,中间有多少机器,都必须在两头(源和目的)间建立连接,一旦连接建立起来,就说已经是端到端连接了。下文图中的 CAT Log 即端到端日志。
评论