Logan:美团点评移动端基础日志库揭秘

背景

对于移动应用来说,日志库是必不可少的基础设施,美团点评集团旗下移动应用每天产生的众多种类的日志数据已经达到几十亿量级。为了解决日志模块普遍存在的效率、安全性、丢失日志等问题,Logan基础日志库应运而生。

现存问题

目前,业内移动端日志库大多都存在以下几个问题:

  • 卡顿,影响性能
  • 日志丢失
  • 安全性
  • 日志分散

首先,日志模块作为底层的基础库,对上层的性能影响必须尽量小,但是日志的写操作是非常高频的,频繁在Java堆里操作数据容易导致GC的发生,从而引起应用卡顿,而频繁的I/O操作也很容易导致CPU占用过高,甚至出现CPU峰值,从而影响应用性能。

其次,日志丢失的场景也很常见,例如当用户的App发生了崩溃,崩溃日志还来不及写入文件,程序就退出了,但本次崩溃产生的日志就会丢失。对于开发者来说,这种情况是非常致命的,因为这类日志丢失,意味着无法复现用户的崩溃场景,很多问题依然得不到解决。

第三点,日志的安全性也是至关重要的,绝对不能随意被破解成明文,也要防止网络被劫持导致的日志泄漏。

最后一点,对于移动应用来说,日志肯定不止一种,一般会包含端到端日志[1]、代码日志、崩溃日志、埋点日志这几种,甚至会更多。不同种类的日志都具有各自的特点,会导致日志比较分散,查一个问题需要在各个不同的日志平台查不同的日志,例如端到端日志还存在日志采样,这无疑增加了开发者定位问题的成本。

面对美团点评几十亿量级的移动端日志处理场景,这些问题会被无限放大,最终可能导致日志模块不稳定、不可用。然而,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自研的日志协议解决了日志本地聚合存储的问题,采用先压缩再加密的顺序,使用流式的加密和压缩,避免了CPU峰值,同时减少了CPU使用。跨平台C库提供了日志协议数据的格式化处理,针对大日志的分片处理,引入了MMAP机制解决了日志丢失问题,使用AES进行日志加密确保日志安全性,并且提供了主动上报接口。Logan核心逻辑都在C层完成,提供了跨平台支持的能力,在解决痛点问题的同时,也大大提升了性能。

日志分片

Logan作为日志底层库,需要考虑上层传入日志过大的情况。针对这样的场景,Logan会做日志分片处理。以20k大小做分片,每个切片按照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平台的日志展示方式,我们还在探索中。未来计划对日志做初步的机器分析与处理,能针对某些关键路径给出一些分析结果,让开发者更专注于业务问题的定位与分析,同时希望分析出用户的行为是否存在风险、恶意请求等。

原文发布于微信公众号 - 美团点评技术团队(meituantech)

原文发表时间:2018-02-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java进阶架构师

dubbo源码解析-服务暴露原理

之前讲完了dubbo集群容错系列,现在开始讲比较重要的环节,也就是dubbo面试中比较喜欢问的两个点:服务发布和服务引用.

812
来自专栏存储

Storm集群搭建的错误分析

第一时间关注程序猿(媛)身边的故事 首先 storm 集群的搭建不再赘述, 网上有很多, 在此推荐一个: http://blog.csdn.net/lzm134...

1665
来自专栏杨建荣的学习笔记

海量数据迁移之数据加载流程(r4笔记第88天)

在之前的博文中分享了关于数据抽取流程的一些思路,整体来说,数据的抽取是辅助,数据的加载是关键。加载的过程中每一步需要格外关注,稍有偏差就可能造成数据的损坏或者丢...

3213
来自专栏Java Edge

分布式MySQL集群方案

4746
来自专栏芋道源码1024

Dubbo源码解析 —— 服务暴露原理

前言 之前讲完了dubbo集群容错系列,现在开始讲比较重要的环节,也就是dubbo面试中比较喜欢问的两个点: 服务发布和 服务引用. 插播面试题 服务发布过程...

3025
来自专栏Java技术栈

收藏起来,史上最全的 MySQL 高性能优化实战总结!

MySQL 对于很多 Linux 从业者而言,是一个非常棘手的问题,多数情况都是因为对数据库出现问题的情况和处理思路不清晰。在进行 MySQL 的优化之前必须要...

1113
来自专栏韩东吉的Unity杂货铺

零基础入门 2: 平台切换

上一篇讲述了Unity不同平台的环境布置,以及破解教程。那这一篇就来说下Unity的不同平台切换。

692
来自专栏程序工场

100 个较全面的 IT 热门编程开发视频教程

1695
来自专栏ImportSource

必须要讨论的四种分布式事务方案

分布式事务伴随着微服务被人们一再提起。服务与服务之间的事务怎么处理比较好?到底使用哪种选择方案比较好。相信有人觉得分布式事务实现起来比较困难甚至不可能。也有人觉...

33811
来自专栏架构师之路

服务挂了,怎么自动恢复?

1544

扫码关注云+社区