xlog學習記錄

基礎認識

mars 是微信官方的終端基礎組件

  • C++ 編寫(為了兼容多平臺)
  • 業(yè)務性無關,平臺性無關的基礎組件
  • 支持接入 Android 或者 iOS/OS X 或者 Windows
    mars架構
  • comm:可以獨立使用的公共庫,包括 socket、線程、消息隊列、協(xié)程等;
  • xlog:高可靠性高性能的運行期日志組件;
  • SDT: 網(wǎng)絡診斷組件;
  • STN: 信令分發(fā)網(wǎng)絡模塊,也是 Mars 最主要的部分。

優(yōu)勢

對于終端設備來說,打日志并不只是把日志信息寫到文件里這么簡單。除了流暢性,** 完整性, 容錯性,還有一個最重要的是安全性**?;诓慌卤黄平猓膊荒苋魏稳硕寄芷平獾脑瓌t, 對日志的規(guī)范比加密算法的選擇更為重要,所以這里并沒有討論這一點。

一個優(yōu)秀的終端日志模塊無論怎么設計都必須做到:

  1. 不能把用戶的隱私信息打印到日志文件里,不能把日志明文打到日志文件里。
  2. 不能影響程序的性能。最基本的保證是使用了日志不會導致程序卡頓。
  3. 不能因為程序被系統(tǒng)殺掉,或者發(fā)生了 crash,crash 捕捉模塊沒有捕捉到導致部分時間點沒有日志, 要保證程序整個生命周期內都有日志。
  4. 不能因為部分數(shù)據(jù)損壞就影響了整個日志文件,應該最小化數(shù)據(jù)損壞對日志文件的影響。

上面這幾點也即一直強調的 安全性 流暢性 完整性 容錯性, 它們之間存在著矛盾關系

  • 如果直接寫文件會卡頓,但如果使用內存做中間 buffer 又可能丟日志
  • 如果不對日志內容進行壓縮會導致 IO 卡頓影響性能,但如果壓縮,部分損壞可能會影響整個壓縮塊,而且為了增大壓縮率集中壓縮又可能導致 CPU 短時間飆高。

mars 的日志模塊 xlog 就是在兼顧這四點的前提下做到:高性能高壓縮率、不丟失任何一行日志、避免系統(tǒng)卡頓和 CPU 波峰。

使用(Android)

gradle 接入我們提供了兩種接入方式:mars-wrapper 或者 mars-core。如果你只是想做個 sample 推薦使用 mars-wrapper,可以快速開發(fā);但是如果你想把 mars 用到你的 app 中的話,推薦使用 mars-core,可定制性更高。

mars-wrapper

在 app/build.gradle 中添加 mars-wrapper 的依賴:

dependencies {
    compile 'com.tencent.mars:mars-wrapper:1.2.0'
}

或者

mars-core

在 app/build.gradle 中添加 mars-core 的依賴:

dependencies {
    compile 'com.tencent.mars:mars-core:1.2.2'
}

或者

mars-xlog

如果只想使用 xlog,可以只加 xlog 的依賴(mars-core,mars-wrapper 中都已經包括 xlog):

dependencies {
    compile 'com.tencent.mars:mars-xlog:1.0.7'
}

接著往下操作之前,請先確保你已經添加了 mars-wrapper 或者 mars-core 或者 mars-xlog 的依賴

Xlog Init

在程序啟動加載 Xlog 后緊接著初始化 Xlog。但要注意如果你的程序使用了多進程,不要把多個進程的日志輸出到同一個文件中,保證每個進程獨享一個日志文件。而且保存 log 的目錄請使用單獨的目錄,不要存放任何其他文件防止被 xlog 自動清理功能誤刪。

System.loadLibrary("c++_shared");
System.loadLibrary("marsxlog");

final String SDCARD = Environment.getExternalStorageDirectory().getAbsolutePath();
final String logPath = SDCARD + "/marssample/log";

// this is necessary, or may crash for SIGBUS
final String cachePath = this.getFilesDir() + "/xlog"

//init xlog
if (BuildConfig.DEBUG) {
    Xlog.appenderOpen(Xlog.LEVEL_DEBUG, Xlog.AppenderModeAsync, cachePath, logPath, "MarsSample", 0, "");
    Xlog.setConsoleLogOpen(true);

} else {
    Xlog.appenderOpen(Xlog.LEVEL_INFO, Xlog.AppenderModeAsync, cachePath, logPath, "MarsSample", 0, "");
    Xlog.setConsoleLogOpen(false);
}

Log.setLogImp(new Xlog());

程序退出時關閉日志:

Log.appenderClose();

如果你想修改 Xlog 的加密算法或者長短連的加解包部分甚至更改組件的其他部分,可以參考這里

Log生成完畢后,會在指定的路徑下生成相應的日志文件:

shell@R7:/sdcard/marssample/log $ ll
-rw-rw---- root     sdcard_r   153600 2016-12-30 17:06 MarsSample.mmap2
-rw-rw---- root     sdcard_r    29633 2016-12-30 17:06 MarsSample_20161230.xlog

其中MarsSample.mmap2是緩存文件,不用關心,我們需要的是.xlog文件,我們把這個文件pull出來,使用Mars提供的Python腳本進行解密。
找到Mars源碼log/crypt/decode_mars_log_file.py下的這個文件,執(zhí)行:

?  mars_xlog_sdk python decode_mars_log_file.py ~/Downloads/log/MarsSample_20161230.xlog

即可生成對應的log文件,用Sublime即可打開

常用 API 說明

Xlog.java

public static native void appenderOpen(int level, int mode, String cacheDir, String logDir, String nameprefix, int  cacheDays, String pubkey);

初始化日志需要調用的接口。

  • level: 日志級別,變量見 Xlog.java 里 LEVEL_XX, Debug版本推薦 LEVEL_DEBUG, Release 版本推薦 LEVEL_INFO。
  • mode : 文件寫入模式,分異步和同步,變量定義見 Xlog.java 里 AppednerModeXX, Release版本一定要用 AppednerModeAsync, Debug 版本兩個都可以,但是使用 AppednerModeSync 可能會有卡頓。
  • cacheDir : 緩存目錄,當 logDir 不可寫時候會寫進這個目錄,可選項,不選用請給 "", 如若要給,建議給應用的 /data/data/packname/files/log 目錄。
  • logDir : 日志寫入目錄,請給單獨的目錄,除了日志文件不要把其他文件放入該目錄,不然可能會被日志的自動清理功能清理掉。
  • nameprefix : 日志文件名前綴,例如該值為TEST,生成的文件名為:TEST_20170102.xlog。
  • cacheDays : 一般情況下填0即可。非0表示會在 _cachedir 目錄下存放幾天的日志。
  • pubkey : 加密所用的 pub_key,具體可參考 Xlog 加密指引

public static native void setConsoleLogOpen(boolean isOpen);
  • 是否會把日志打印到 logcat 中, 默認不打印。
  • isOpen : true 為打印,false為不打印。

Log.java

該文件包含打印日志最常調用的接口。

public static void setLogImp(LogImp imp)

設置 Log 的具體實現(xiàn),這里必須調用 Log.setLogImp(new Xlog()); 日志才會寫到 Xlog 中。

public static void appenderFlush(boolean isSync) 

當日志寫入模式為異步時,調用該接口會把內存中的日志寫入到文件。

  • isSync : true 為同步 flush,flush 結束后才會返回。 false 為異步 flush,不等待 flush 結束就返回。強制將緩存中的輸出流(字節(jié)流,字符流等)強制輸出.
public static void appenderClose()

關閉日志,在程序退出時調用。

public static void f/e/w/i/d(final String tag, final String msg)

寫日志時調用的接口,對應不同級別的日志。

問題

常見問題

重要技術點

技術實現(xiàn)思路:使用流式方式對單行日志進行壓縮,壓縮加密后寫進作為 log 中間 buffer的 mmap 中

技術水準有多牛逼,就看這個

mmap

如果大家對binder的實現(xiàn)有看過就知道,binder就是使用mmap來實現(xiàn)一次拷貝的跨進程的。


mmap 是使用邏輯內存對磁盤文件進行映射,中間只是進行映射沒有任何拷貝操作,避免了寫文件的數(shù)據(jù)拷貝。操作內存就相當于在操作文件,避免了內核空間和用戶空間的頻繁切換。

等于寫內存的速度:把512 Byte的數(shù)據(jù)分別寫入150 kb大小的內存和 mmap,以及磁盤文件100w次并統(tǒng)計耗時


xlog實現(xiàn)方案:先壓縮再加密效率比較高,這個順序不能改變。而且在寫入 mmap 之前先進行壓縮,也會減少所占用的 mmap 的大小,進而減少 mmap 所占用內存的大小。就是其他模塊每寫一行日志日志模塊就必須進行壓縮。

壓縮方案


第三種是把整個 app 生命周期作為一個壓縮單位進行壓縮,如果這個壓縮單位中有數(shù)據(jù)損壞,那么后面的日志也都解壓不出來。

但其實在短語式壓縮過程中,滑動窗口并不是無限大的,一般是 32kb ,所以只需要把一定大小作為一個壓縮單位就可以了。這也就是第四個方案, 這樣的話即使壓縮單位中有部分數(shù)據(jù)損壞,因為是流式壓縮,并不影響這個單位中損壞數(shù)據(jù)之前的日志的解壓,只會影響這個單位中這個損壞數(shù)據(jù)之后的日志

使用流式壓縮后,和之前使用通用壓縮的的日志方案進行了對比(耗時為單行日志的平均耗時):


多條日志同時壓縮會導致 CPU 曲線短時間內極速升高,進而可能會導致程序卡頓,而流式壓縮是把時間分散在整個生命周期內,CPU 的曲線更平滑,相當于把壓縮過程中使用的資源均分在整個 app 生命周期內。雖說時間上升了,但是分攤后CPU曲線更為平滑,實際上是性能的提升。

參考內容
github Mars 項目
mars Android 接入指南
聊聊微信 Xlog
【Dev Club 分享】微信mars 的高性能日志模塊 xlog

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • mean to add the formatted="false" attribute?.[ 46% 47325/...
    ProZoom閱讀 3,128評論 0 3
  • 同步地址 本文介紹 MARS xlog 使用以及使用過程中踩過的坑 xlog 是什么 xlog 是微信開源框架 M...
    Noah牛YY閱讀 13,001評論 2 0
  • ORA-00001: 違反唯一約束條件 (.) 錯誤說明:當在唯一索引所對應的列上鍵入重復值時,會觸發(fā)此異常。 O...
    我想起個好名字閱讀 5,918評論 0 9
  • 看完了微信團隊對Xlog的整體介紹,迫不及待開始了研究,理論部分我是完全參考微信終端跨平臺組件 mars 系列(一...
    星期五__閱讀 9,882評論 2 18
  • WinRAR - 最新版本的更新 版本 5.50 1. WinRAR 和命令行 RAR 默認使用 RAR ...
    王舒璇閱讀 2,503評論 0 2

友情鏈接更多精彩內容