聊聊微信 Xlog

同步地址

本文介绍 MARS xlog 使用以及使用过程中踩过的坑

xlog 是什么

xlog 是微信开源框架 MARS 的一部分, 处理应用日志

微信的对 xlog 的介绍文档--「微信终端跨平台组件 mars 系列(一) - 高性能日志模块xlog)

总结出来就是

xlog 方案总结

使用流式方式对单行日志进行压缩,压缩加密后写进作为 log 中间 buffer的 mmap 中

虽然使用流式压缩并没有达到最理想的压缩率,但和 mmap 一起使用能兼顾流畅性 完整性 容错性 的前提下,83.7%的压缩率也是能接受的。使用这个方案,除非 IO 损坏或者磁盘没有可用空间,基本可以保证不会丢失任何一行日志。

一个优秀的日志模块必须做到:

  • 不能把用户的隐私信息打印到日志文件里,不能把日志明文打到日志文件里。
  • 不能影响程序的性能。最基本的保证是使用了日志不会导致程序卡顿。
  • 不能因为程序被系统杀掉,或者发生了 crash,crash 捕捉模块没有捕捉到导致部分时间点没有日志, 要保证程序整个生命周期内都有日志。
  • 不能因为部分数据损坏就影响了整个日志文件,应该最小化数据损坏对日志文件的影响。

上面这几点也即安全性 流畅性 完整性 容错性, 它们之间存在着矛盾关系:

  • 如果直接写文件会卡顿,但如果使用内存做中间 buffer 又可能丢日志
  • 如果不对日志内容进行压缩会导致 IO 卡顿影响性能,但如果压缩,部分损坏可能会影响整个压缩块,而且为了增大压缩率集中压缩又可能导致 CPU 短时间飙高。

mars 的日志模块 xlog 就是在兼顾这四点的前提下做到:高性能高压缩率、不丢失任何一行日志、避免系统卡顿和 CPU 波峰。

xlog 使用

MARS 的 GitHub 上介绍比较详细,

xlog 背景知识

先跑起来一个 Demo 之后, 需要深入了解一下

mmap

认真分析mmap:是什么 为什么 怎么用

mmap 是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。

mmap.png

正如微信的介绍文章中所说的:

mmap 是使用逻辑内存对磁盘文件进行映射,中间只是进行映射没有任何拷贝操作,避免了写文件的数据拷贝。操作内存就相当于在操作文件,避免了内核空间和用户空间的频繁切换。

mmap几乎和直接写内存一样的性能,而且 mmap 既不会丢日志,回写时机对我们来说又基本可控。

System.loadLibrary()

Java中System.loadLibrary() 的执行过程

上文中有关于该方法的源码分析, 总结来说

  1. 该方法用来加载 'xxx.so' 文件, 一些 native 方法的具体实现
  2. 该方法会从以下位置加载 so 文件: /vendor/lib, /system/lib, /data/app/com.xxxxx.xxx-1

so 文件

因为 Android 手机 CPU 架构的差异, 可能会有很多版本的 so 文件, 如果你是使用本地编译 xlog 的, 你应该注意对应不同 CPU 架构编译不同的 so 文件

本地编译的 so 文件放在 src/jniLibs 目录下, AS 可以自动编译到 apk 中

xlog 踩坑

我的坑主要是因为 xposed 的原因, 刚开始 Demo 很顺利, 接入到项目中问题就一个个的

couldn't find "libstlport_shared.so

java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/io.communet.ichater-2/base.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]] couldn't find "libc++_shared.so"
        at java.lang.Runtime.loadLibrary(Runtime.java:385)
        at java.lang.System.loadLibrary(System.java:993)
        at io.communet.ichater.main.util.LogUtils.initXLog(LogUtils.java:38)
        at io.communet.ichater.wx.hook.WxHook.dealWx(WxHook.java:163)
        at io.communet.ichater.wx.hook.WxHook.access$000(WxHook.java:48)
        at io.communet.ichater.wx.hook.WxHook$1.afterHookedMethod(WxHook.java:152)
        at de.robv.android.xposed.XposedBridge.handleHookedMethod(XposedBridge.java:374)
        at android.content.ContextWrapper.attachBaseContext(<Xposed>)
        at android.app.Service.attach(Service.java:702)
        at android.app.ActivityThread.handleCreateService(ActivityThread.java:2759)
        at android.app.ActivityThread.access$1800(ActivityThread.java:151)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1386)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:135)
        at android.app.ActivityThread.main(ActivityThread.java:5254)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:905)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:700)
        at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:107)

上文以及提到会在哪里加载 so 文件, 但是由于 xposed 的原因, Classloader 指向的文件为 /data/app/io.communet.ichater-2/base.apk, 不能找到指定的 so 文件, 所以需要指定绝对路径

解决:

String nativeLibraryDir = getNativeLibraryDir(context);

System.load(nativeLibraryDir + "/libc++_shared.so");
System.load(nativeLibraryDir + "/libmarsxlog.so");
/**
 * 获取本地支持库
 */
private static String getNativeLibraryDir(Context context) throws PackageManager.NameNotFoundException {
    ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo("io.communet.ichater", 0);
    return applicationInfo.nativeLibraryDir;
}

日志存储位置

微信有提到关于日志同步和异步两种写入方式以及日志文件的存储位置

mode : 文件写入模式,分异步和同步,变量定义见 Xlog.java 里 AppednerModeXX, Release版本一定要用 AppednerModeAsync, Debug 版本两个都可以,但是使用 AppednerModeSync 可能会有卡顿。

cacheDir : 缓存目录,当 logDir 不可写时候会写进这个目录,可选项,不选用请给 "", 如若要给,建议给应用的 /data/data/packname/files/log 目录。

logDir : 日志写入目录,请给单独的目录,除了日志文件不要把其他文件放入该目录,不然可能会被日志的自动清理功能清理掉。

实际运行中发现, 当同步写入时, 日志文件开始会被存放在 cacheDir, 一段时间后, 会被放到 logDir, 但是异步模式下, 文件一直放在 cacheDir, 即便调用 appenderFlush 方法, 日志会从 mmap 中写入文件, 但是文件的位置还是在 cacheDir, 当然, 应用有读写 SDCard 的权限

解决:

该问题还未查明原因, 目前的解决方法是不给 cacheDir, 文件会被直接放到 logDir, 但是, 官方说如果不给 cacheDir, 可能出现 SIGBUS, 参见 issue#249

2019/4/17更新: 解决了, 说起来都惭愧, 还有一个参数

cacheDays : 一般情况下填0即可。非0表示会在 _cachedir 目录下存放几天的日志。

将该值设置为 0 即可, 之前以为这个值表示的是缓存日志保存的天数, 设置了 7, 实际上保留缓存日志的天数默认 10 天, 清理逻辑如下

每次启动时会删除过期文件,只保留十天内的日志文件(该值定义在appender.cc中的 kMaxLogAliveTime ),所以给 Xlog 的目录请使用单独目录,防止误删其他文件。目前不会根据文件大小进行清理。如若想自定义清理逻辑请自行更改appender.cc中的 __del_timeout_file 函数。 #Android

couldn't find "xxx.so" is 32-bit instead of 64-bit

注意和上文中的那个 BUG 区分, 这里是因为用 32 位的 so 代替 64 位的 so 导致的

解决:

jniLibs 下面不要放 64 位的, 只放 32 的, 可以兼容

未完待续

还有坑的话继续更新

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容

  • title: App技术选型--日志框架 版 本 历 史 日志对于开发来说是非常重要的,不管是调试数据查看、bug...
    海南鸡阅读 1,459评论 0 0
  • 微信Mars——xlog使用全解析 很多人问我微信的Mars到底有什么用,今天就告诉你其中一个最简单的! 如约而至...
    eclipse_xu阅读 23,041评论 8 26
  • title: App技术选型--日志框架--- **版 本 历 史**| **版本** | **责任人** | *...
    海南鸡阅读 1,744评论 0 1
  • UNIX网络编程第二卷进程间通信对mmap函数进行了说明。该函数主要用途有三个:1、将一个普通文件映射到内存中,通...
    宇文黎琴阅读 3,491评论 0 4
  • 一、 设计理念 1.空间换时间 1)多级缓存,静态化 客户端页面缓存(http header中包含Expires/...
    帅T阅读 3,604评论 1 15