App 日志系统方案选型分析

最近我们的 app 被反馈了几个不同的生产问题, 都属于偶现, 由于不能稳定复现, 因此排查起来比较困难, 只能猜测问题出现在哪里, 尝试进行修改, 改完之后也不知道有没有彻底解决, 很痛苦. 造成这种现象的原因是我们 app 目前缺乏一个日志系统, 结合前段时间的面试经历, 我意识到大型的 app 一般都会配备有日志分析系统, 而这正是我们 app 目前所欠缺的, 我觉得是时候考虑加入日志系统了

首先内部调研了一下, 同部门的安卓组倒是有日志系统, 他们采用的上报方案是通过 http 请求实时上报到后端, 后端将上报来的日志信息输出到 kibana 日志平台供安卓开发人员查询. 这种方案会频繁请求网络造成耗电, 且上报数据有限. 在我看来安卓的这种日志上报更像是埋点上报, 可能比较适合前端网页, 因为网页随时可能被用户关闭, 而且不能在磁盘进行存储, 只能进行实时上报了. 我认为应该充分利用 app 客户端的优势, 将日志写入到本地文件中, 在用户遇到问题时将日志进行上传供开发人员分析.

说干就干, 先把大致思路捋清楚吧

初步方案

  • app 开发一个本地日志模块, 作为基础库被各个业务组件依赖, 在重要业务代码处输出当前的状态到日志, 形成用户交互路径.
  • 客户端通过 文件 / 数据库 为载体对日志内容进行加密存储, 支持直接上传服务端或导出到第三方应用进行发送, 在开发人员分析时使用密钥解密查看日志内容
  • 添加单独日志上传页面, 用户可以主动上传并添加描述
  • 添加后端上报策略接口, 在 app 启动时请求策略接口判断是否要上传日志文件
  • 客户端日志数据支持按天上传, 支持最多保留最近 x 日的日志数据
  • 在用户遇到问题时可以在日志上报页面进行上报, 开发人员收到反馈后调出相应日志文件进行分析

完整的日志系统使用 采集部分 + 服务端存储部分 + console 部分 组成, 以下针对各个部分进行方案选型分析

采集部分

  • 自行实现: 按日期直接写文件方式
    • 上传时直接上传指定日期文件
    • 性能一般, 高频写会对性能及耗电有影响
    • 对每次写入内容进行加密的话性能较差
  • 自行实现: 写数据库方式
    • 上传时按需导出为文件并上传服务端
    • 性能高
    • 支持数据库加密, 可避免每次写入均需对数据内容加密
    • 数据库操作较繁琐
  • Logan iOS sdk 套件 / Logan Android sdk 套件, Logan

    • 写文件, 使用 mmap 机制对磁盘文件进行映射, 操作内存就相当于操作文件, 使用 C 底层库, 性能好
    • 可设置日志最长存储天数
    • 采用 AES 非对称加密对日志内容进行加密
    • 在上传时直接上传日志文件
    • Logan 编译为 framework 后二进制大小为 1MB

  • mars(微信客户端日志模块), Mars
    • 写文件, 使用 mmap 机制对磁盘文件进行映射, 性能好
    • 接口不友好
    • 不能源码引入, 引入项目时需要提前编译为二进制 (xlog 模块编译为 framework 后二进制大小为 9.2MB)

服务端存储部分

  • 自研 nodejs server: 可控性高, 开发量大
  • Logan server 套件

console 部分

  • 自研 h5: 可控性高, 开发量大
  • Logan site 套件

总结

综合以上考虑:

  1. 客户端使用 Logan sdk, 原因如下:
    • 开源时间较长, 2018 年开源, 已经有 5 年时间
    • 开源热度高, 有 5300 ⭐️
    • 客户端 sdk 部分使用 c 底层实现, 性能高;
    • 从编译产物体积, 代码量及接口简洁性上比 mars 有优势
    • 对应的后端部分与 console 部分也已经开源
    • 包含 1000 条加密日志的文件大小约为 13KB, 体积小
  2. server 端与 console 端也采用 Logan 提供的框架能力, 原因如下:
    • 可以充分利用 Logan 定义的协议
    • 通过实际部署发现 logan 开源的 server 端与 console 端是比较完整的, 检索 & 下载 & 在线查看等基本能力都涵盖, 基本能满足初期需求

方案概览

由于本方案是基于 Logan 的一整套开源框架, 因此详细内容可以直接查看 Logan, 这里对其进行简要概述

本方案核心体系由四大模块构成:

  • 日志输入
  • 日志存储
  • 后端系统
  • 前端系统

日志分析流程如下:

方案实施的不确定风险

  • 服务端维护: Logan 的服务端使用 java 代码 (约 2000 行) 编写, 可能需要有一定后端经验的人员负责维护跟进
  • Logan 客户端 sdk 使用了大量 c 代码, 团队自行维护有一定难度

方案实施阶段

由于开发资源有限, 计划将完整日志系统的建立分为两个阶段:

  • 第一阶段
    • iOS 加入日志模块, 拥有写日志文件功能
    • iOS 加入直接上报页面, 用户可直接上报日志文件
    • 服务端部署 Logan Server 的代码
    • Console 端部署 Logan Site 代码
  • 第二阶段
    • 服务端改为 nodejs 实现, 对于我们团队而言更加可控
    • 增加 app 启动上传策略接口, 实现后台静默上传日志文件

可以看到, 第一阶段我们将 Logan 框架的能力充分利用, 第二阶段进行改造处理以更加适应我们团队的业务

Logan 方案的缺点

Logan 这套开源框架很完善, 客户端, 服务端, console 端都开源了, 非常 nice. 不过在使用过程中, 我也发现了一些缺点:

  • 客户端在闪退情况下容易丢日志, 必须保证使用 flush 方法
  • 服务端解析日志错误时仍然有日志记录, 但是没有日志详情, 原因是后端将日志分为日志列表与日志详情两个数据库表存储, 在解析失败时仍然会对日志列表写入记录
  • java 代码中的一些 api 路径需要调整
  • 服务端与 console 端的部署放在了同一套 docker-compose 流程中, 如果要部署在不同服务器的话需要拆分 docker-compose.yaml 文件

当然, 瑕不掩瑜, 在权衡对比下, 这个已经是目前能找到的最优秀的 app 开源日志框架了 👍

参考

本博客文章采用 CC 4.0 协议,转载需注明出处和作者。

鼓励作者