說明: 本文只討論Log日志,而不是應用的埋點日志。
Android 日志架構
用一張圖來了解Android Log的架構:
具體的流程可以看下面的這張圖:
這里涉及到三個進程:
APP進程: 調用Log的接口打日志,最終通過soctket通信發送給Logd進程
Logd進程:有一個緩沖區用於存儲日志(環形緩沖區,當滿時會沖掉舊的日志)
Logcat進程:可以在adb shell中創建(可以創建多個),查看緩沖區中日志的進程(Android Studio的logcat也是一個Logcat進程)。
環形緩沖區是一個邏輯上的循環隊列,寫者可以往里面寫東西,而一旦有內容會通知等待隊列里的讀者們來讀取內容,否則等待隊列列處於阻塞狀態。 這就意味Logcat進程讀取也是阻塞形式的。
基於緩存的日志方案
普通應用
該方案會在系統的Log類上封裝一層,應用調用封裝層的日志接口。封裝層將業務記得log緩存到一個StringBuffer中,當量達到一定閾值后,再刷到文件中。到達一定條件上傳到服務端。
基於緩存的日志方案 對於普通應用:實現一套自己的Log,將每次打的日志緩存
系統應用
在應用中開啟一起Logcat進程,不斷的讀取Logd進程中的日志到緩存中,當日質量達到一定的閾值后再刷如文件。這種方案可以搜集到所有應用的日志。
缺點:
1. 寫文件 + 加密 會出現cpu峰值
2.崩潰或者進程意外退出 緩存中的日志可能無法刷到文件中,關鍵崩潰信息可能丟失。
3. 對於智能設備中專門做一個日志進程去搜集所有應用的日志,這種方案其實是簡單可行的。
基於內存映射mmp的方案
mmp 原理:
內存映射,簡而言之就是將用戶空間的一段內存區域映射到內核空間,映射成功后,用戶對這段內存區域的修改可以直接反映到內核空間,同樣,內核空間對這段區域的修改也直接反映用戶空間。那么對於內核空間<---->用戶空間兩者之間需要大量數據傳輸等操作的話效率是非常高的。
以下是一個把普遍文件映射到用戶空間的內存區域的示意圖。
系統層提供了具體的將用戶空間地址映射到內核空間的具體接口函數,這里未作學習。
通過mmp讀取磁盤文件:
如果在拷貝數據時,發現物理內存不夠用,則會通過虛擬內存機制(swap)將暫時不用的物理頁面交換到硬盤上,上圖步驟4所示。這個過程也與內存映射無關。
通過系統read/write文件原理:
它首先將文件內容從硬盤拷貝到內核空間的一個緩沖區,如圖過程1,然后再將這些數據拷貝到用戶空間,如圖過程2,在這個過程中,實際上完成了 兩次數據拷貝 ;
而mmap()也是系統調用,mmap()中沒有進行數據拷貝,真正的數據拷貝是在缺頁中斷處理時進行的,由於mmap()將文件直接映射到用戶空間,所以中斷處理函數根據這個映射關系,直接將文件從硬盤拷貝到用戶空間,只進行了 一次數據拷貝 。因此,內存映射的效率要比read/write效率高。
mmp作為日志方案優勢
1.讀寫文件比普通文件操作效率更高
2.不會丟日志(進程退出時能刷日志到映射的地址中) 會寫日志的時機: 內存不足 進程退出 調用 msync 或者 munmap 不設置 MAP_NOSYNC 情況下 30s-60s(僅限FreeBSD)
3. 對於CPU峰值問題,參考微信XLOG解決方案如下: 追加每行日志時,先壓縮后加密(避免了對整個文件的壓縮/加密)
思考:
大多數用戶的日志時無用的,日志上傳場景考慮,通過指令撈取
具體日志策略需要綜合多方面考慮: 流暢性/完整性/容錯性/安全性
MMP的具體使用還要結合具體場景,需要測試追加日志大小
參考:
https://blog.csdn.net/tencent_bugly/article/details/53157830
http://ju.outofmemory.cn/entry/224106
https://blog.csdn.net/coolwriter/article/details/80493166
https://blog.csdn.net/shift_wwx/article/details/89138117
log系統源碼下載地址:https://android.googlesource.com/platform/system/core/+/refs/tags/android-8.0.0_r37