基於微信XLog的日誌框架&&對於XLog的分析java
以前寫過一個 日誌框架LogHelper ,是基於 Logger 開源庫封裝的,當時的由於項目自己的日誌不是不少,徹底可使用,最近和其餘公司合做,在一個新的項目上反饋,說在 大量log 的狀況下會影響到手機主體功能的使用。從而讓我對以前的日誌行爲作了一個深入的檢討python
隨後在開發羣中諮詢了其餘開發的小夥伴,若是追求性能,能夠研究一下 微信的 xlog ,也是本篇博客的重點android
xlog 是什麼 這個問題 我這也是在【騰訊Bugly乾貨分享】微信mars 的高性能日誌模塊 xlog獲得了答案c++
簡單來講 ,就是騰訊團隊分享的基於 c/c++ 高可靠性高性能的運行期日誌組件git
知道了他是什麼,就要只要他是怎麼用的,打開github 找到官網Tencent/marsgithub
使用很是簡單緩存
dependencies {
compile 'com.tencent.mars:mars-xlog:1.2.3'
}
複製代碼
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());
複製代碼
OK 實現了他的功能bash
不要高興的太早,後續的問題都頭大微信
知道了最簡單的用法,就想看看他支持哪些功能app
按照官網的demo 首先分析一下appenderOpen
日誌級別 沒啥好說的 XLog 中已經寫得很清楚了
public static final int LEVEL_ALL = 0;
public static final int LEVEL_VERBOSE = 0;
public static final int LEVEL_DEBUG = 1;
public static final int LEVEL_INFO = 2;
public static final int LEVEL_WARNING = 3;
public static final int LEVEL_ERROR = 4;
public static final int LEVEL_FATAL = 5;
public static final int LEVEL_NONE = 6;
複製代碼
值得注意的地方 debug 版本下建議把控制檯日誌打開,日誌級別設爲 Verbose 或者 Debug, release 版本建議把控制檯日誌關閉,日誌級別使用 Info.
這個在官網的 接入指南
這裏也可使用
public static native void setLogLevel(int logLevel);
複製代碼
方法設置
寫入的模式
異步寫入
同步寫入
同步寫入,能夠理解爲實時的日誌,異步則不是
Release版本必定要用 AppednerModeAsync, Debug 版本兩個均可以,可是使用 AppednerModeSync 可能會有卡頓
這裏也可使用
public static native void setAppenderMode(int mode);
複製代碼
方法設置
緩存目錄,當 logDir 不可寫時候會寫進這個目錄,可選項,不選用請給 "", 如若要給,建議給應用的 /data/data/packname/files/log 目錄。
會在目錄下生成後綴爲 .mmap3 的緩存文件,
真正的日誌,後綴爲 .xlog
日誌寫入目錄,請給單獨的目錄,除了日誌文件不要把其餘文件放入該目錄,否則可能會被日誌的自動清理功能清理掉。
日誌文件名的前綴,例如該值爲TEST,生成的文件名爲:TEST_20170102.xlog。
通常狀況下填0便可。非0表示會在 _cachedir 目錄下存放幾天的日誌。
這裏的描述比較晦澀難懂,當我設置這個參數非0 的時候 會發現 本來設置在 logDir 目錄下的文件 出如今了 cacheDir
例如 正常應該是
文件結構
- cacheDir
- log.mmap3
- logDir
- log_20200710.xlog
- log_20200711.xlog
複製代碼
變成這樣
- cacheDir
- log.mmap3
- log_20200710.xlog
- log_20200711.xlog
- logDir
複製代碼
所有到了 cacheDir 下面
cacheDays 的意思是 在多少天之後 從緩存目錄移到日誌目錄
這裏涉及到了日誌的加密與解密,下面會專門介紹
在 Xlog 下有一個 native 方法
public static native void setMaxFileSize(long size);
複製代碼
他表示 最大文件大小,這裏須要說一下,本來的默認設置 是一天一個日誌文件在 appender.h 描述的很清楚
/* * By default, all logs will write to one file everyday. You can split logs to multi-file by changing max_file_size. * * @param _max_byte_size Max byte size of single log file, default is 0, meaning do not split. */
void appender_set_max_file_size(uint64_t _max_byte_size);
複製代碼
默認狀況下,全部日誌天天都寫入一個文件。能夠經過更改max_file_size將日誌分割爲多個文件。單個日誌文件的最大字節大小,默認爲0,表示不分割
當超過設置的文件大小之後。文件會變成以下目錄結構
- cacheDir
- log.mmap3
- logDir
- log_20200710.xlog
- log_20200710_1.xlog
- log_20200710_2.xlog
複製代碼
在 appender.cc 對應的有以下邏輯,
static long __get_next_fileindex(const std::string& _fileprefix, const std::string& _fileext) {
...
return (filesize > sg_max_file_size) ? index + 1 : index;
複製代碼
···java public static native void setConsoleLogOpen(boolean isOpen); ···
設置是否在控制檯答應日誌
這個方法是沒用的,一開始覺得哪裏繼承的有問題,在查看源碼的時候發現 他是一個空方法,沒有應用
使用的話會致使程序異常,在本身編譯的so 中我就把它給去掉了
public static native void setMaxAliveTime(long duration);
複製代碼
置單個文件最大保留時間 單位是秒,這個方法有3個須要注意的地方,
在 appender.cc 中能夠看到
static const long kMaxLogAliveTime = 10 * 24 * 60 * 60; // 10 days in second
static const long kMinLogAliveTime = 24 * 60 * 60; // 1 days in second
static long sg_max_alive_time = kMaxLogAliveTime;
....
void appender_set_max_alive_duration(long _max_time) {
if (_max_time >= kMinLogAliveTime) {
sg_max_alive_time = _max_time;
}
}
複製代碼
默認的時間是10天
在 文檔中介紹說是在 程序退出時關閉日誌 調用appenderClose的方法
然而在實際狀況中 Application 類的 onTerminate() 只有在模擬器中才會生效,在真機中無效的,
若是在程序退出的時候沒有觸發 appenderClose 那麼在下一次啓動的時候,xlog 也會把日誌寫入到文件中
因此如何觸發呢?
建議儘量的去觸發他 例如用戶雙擊back 退出的狀況下 你確定是知道的 若是放在後臺被殺死了,這個時候也真的沒辦法刷新,也不要緊,上面也說了,再次啓動的時候會刷新到日誌中,
當日志寫入模式爲異步時,調用該接口會把內存中的日誌寫入到文件。
isSync : true 爲同步 flush,flush 結束後纔會返回。 isSync : false 爲異步 flush,不等待 flush 結束就返回。
這一塊單獨拿出來講明,是由於以前使用上遇到了坑
首先是這個 入參 PUB_KEY,一臉懵,是個啥,
在 mars/blob/master/mars/log/crypt/gen_key.py 這個就是可以獲取到 PUB_KEY 的方法
運行以下
$ python gen_key.py
WARNING: Executing a script that is loading libcrypto in an unsafe way. This will fail in a future version of macOS. Set the LIBRESSL_REDIRECT_STUB_ABORT=1 in the environment to force this into an error.
save private key
471e607b1bb3760205f74a5e53d2764f795601e241ebc780c849e7fde1b4ce40
appender_open's parameter:
300330b09d9e771d6163bc53a4e23b188ac9b2f5c7150366835bce3a12b0c8d9c5ecb0b15274f12b2dffae7f4b11c3b3d340e0521e8690578f51813c93190e1e
複製代碼
上面的 private key 本身保存好
appender_open's parameter: 就是須要的 PUB_KEY
上面已經知道如何加密了,如今瞭解一下如何解密
在Xlog 加密使用指引中可以看到
須要下載 pyelliptic1.5.7 而後編譯 不然下面的命令會失敗
xlog 很貼心的給咱們提供了兩個腳本
使用 decode_mars_nocrypt_log_file.py 解壓沒有加密的
python decode_mars_nocrypt_log_file [path]
複製代碼
使用 decode_mars_crypt_log_file.py 加密的文件
在使用以前須要將 腳本中的
PRIV_KEY = "145aa7717bf9745b91e9569b80bbf1eedaa6cc6cd0e26317d810e35710f44cf8"
PUB_KEY = "572d1e2710ae5fbca54c76a382fdd44050b3a675cb2bf39feebe85ef63d947aff0fa4943f1112e8b6af34bebebbaefa1a0aae055d9259b89a1858f7cc9af9df1"
複製代碼
改爲上面本身獲取到的 key 不然是解壓不出來的
python decode_mars_crypt_log_file.py ~/Desktop/log/log_20200710.xlog
複製代碼
直接生成一個
- cacheDir
- log.mmap3
- logDir
- log_20200710.xlog
- log_20200710.xlog.log
複製代碼
也能夠自定義名字
python decode_mars_crypt_log_file.py ~/Desktop/log/log_20200710.xlog ~/Desktop/log/1.log
複製代碼
- cacheDir
- log.mmap3
- logDir
- log_20200710.xlog
- 1.log
複製代碼
打開咱們解壓好的日誌查看
^^^^^^^^^^Oct 14 2019^^^20:27:59^^^^^^^^^^[17223,17223][2020-07-24 +0800 09:49:19]
get mmap time: 3
MARS_URL:
MARS_PATH: master
MARS_REVISION: 85b19f92
MARS_BUILD_TIME: 2019-10-14 20:27:57
MARS_BUILD_JOB:
log appender mode:0, use mmap:1
cache dir space info, capacity:57926635520 free:52452691968 available:52452691968
log dir space info, capacity:57926635520 free:52452691968 available:52452691968
[I][2020-07-24 +8.0 09:49:21.179][17223, 17223][TAG][, , 0][======================> 1
[I][2020-07-24 +8.0 09:49:21.180][17223, 17223][TAG][, , 0][======================> 2
[I][2020-07-24 +8.0 09:49:21.180][17223, 17223][TAG][, , 0][======================> 3
[I][2020-07-24 +8.0 09:49:21.180][17223, 17223][TAG][, , 0][======================> 4
[I][2020-07-24 +8.0 09:49:21.181][17223, 17223][TAG][, , 0][======================> 5
[I][2020-07-24 +8.0 09:49:21.181][17223, 17223][TAG][, , 0][======================> 6
[I][2020-07-24 +8.0 09:49:21.182][17223, 17223][TAG][, , 0][======================> 7
[I][2020-07-24 +8.0 09:49:21.182][17223, 17223][TAG][, , 0][======================> 8
[I][2020-07-24 +8.0 09:49:21.182][17223, 17223][TAG][, , 0][======================> 9
[I][2020-07-24 +8.0 09:49:21.183][17223, 17223][TAG][, , 0][======================> 10
[I][2020-07-24 +8.0 09:49:21.183][17223, 17223][TAG][, , 0][======================> 11
[I][2020-07-24 +8.0 09:49:21.183][17223, 17223][TAG][, , 0][======================> 12
[I][2020-07-24 +8.0 09:49:21.184][17223, 17223][TAG][, , 0][======================> 13
[I][2020-07-24 +8.0 09:49:21.184][17223, 17223][TAG][, , 0][======================> 14
[I][2020-07-24 +8.0 09:49:21.185][17223, 17223][TAG][, , 0][======================> 15
[I][2020-07-24 +8.0 09:49:21.185][17223, 17223][TAG][, , 0][======================> 16
[I][2020-07-24 +8.0 09:49:21.185][17223, 17223][TAG][, , 0][======================> 17
複製代碼
我擦淚 除了咱們須要的信息之外,還有這麼多雜七雜八的信息,如何去掉,而且本身定義一下格式
這裏就須要本身去編譯 so 了,好在 xlog 已經給咱們提供了很好的編譯代碼
對應的文檔 本地編譯
對於編譯這塊按照文檔來就行了 須要注意的是
首先咱們去到這個頭文件,對於一個日誌框架來着,這個沒啥用
^^^^^^^^^^Oct 14 2019^^^20:27:59^^^^^^^^^^[17223,17223][2020-07-24 +0800 09:49:19]
get mmap time: 3
MARS_URL:
MARS_PATH: master
MARS_REVISION: 85b19f92
MARS_BUILD_TIME: 2019-10-14 20:27:57
MARS_BUILD_JOB:
log appender mode:0, use mmap:1
cache dir space info, capacity:57926635520 free:52452691968 available:52452691968
log dir space info, capacity:57926635520 free:52452691968 available:52452691968
複製代碼
在本機下載好的 mars 下,找到 appender.cc 將頭文件去掉
默認的格式很長
[I][2020-07-24 +8.0 09:49:21.179][17223, 17223][TAG][, , 0][======================> 1
複製代碼
[日誌級別][時間][pid,tid][tag][filename,strFuncName,line][日誌內容
是一個這樣結構
比較亂,咱們想要的日誌 就時間,級別,日誌內容 就好了
找到 formater.cc
將本來的
int ret = snprintf((char*)_log.PosPtr(), 1024, "[%s][%s][%" PRIdMAX ", %" PRIdMAX "%s][%s][%s, %s, %d][", // **CPPLINT SKIP**
_logbody ? levelStrings[_info->level] : levelStrings[kLevelFatal], temp_time,
_info->pid, _info->tid, _info->tid == _info->maintid ? "*" : "", _info->tag ? _info->tag : "",
filename, strFuncName, _info->line);
複製代碼
改爲
int ret = snprintf((char*)_log.PosPtr(), 1024, "[%s][%s]", // **CPPLINT SKIP**
temp_time, _logbody ? levelStrings[_info->level] : levelStrings[kLevelFatal] );
複製代碼
就好了
而後重新編譯,將so 翻入項目 在看一下如今的效果
[2020-07-24 +8.0 11:47:42.597][I]======================>9
複製代碼
ok 打完收工
基本上分析和實現了咱們須要的功能,那麼把這部分簡單的封裝一下
放上核心的 Builder 源碼可在下面自行查看
package com.allens.xlog
import android.content.Context
import com.tencent.mars.xlog.Log
import com.tencent.mars.xlog.Xlog class Builder(context: Context) {
companion object {
//日誌的tag
var tag = "log_tag"
}
//是不是debug 模式
private var debug = true
//是否打印控制檯日誌
private var consoleLogOpen = true
//是否天天一個日誌文件
private var oneFileEveryday = true
//默認的位置
private val defCachePath = context.getExternalFilesDir(null)?.path + "/mmap"
// mmap 位置 默認緩存的位置
private var cachePath = defCachePath
//實際保存的log 位置
private var logPath = context.getExternalFilesDir(null)?.path + "/logDir"
//文件名稱前綴 例如該值爲TEST,生成的文件名爲:TEST_20170102.xlog
private var namePreFix = "log"
//寫入文件的模式
private var model = LogModel.Async
//最大文件大小
//默認狀況下,全部日誌天天都寫入一個文件。能夠經過更改max_file_size將日誌分割爲多個文件。
//單個日誌文件的最大字節大小,默認爲0,表示不分割
// 最大 當文件不能超過 10M
private var maxFileSize = 0L
//日誌級別
//debug 版本下建議把控制檯日誌打開,日誌級別設爲 Verbose 或者 Debug, release 版本建議把控制檯日誌關閉,日誌級別使用 Info.
private var logLevel = LogLevel.LEVEL_INFO
//經過 python gen_key.py 獲取到的公鑰
private var pubKey = ""
//單個文件最大保留時間 最小 1天 默認時間 10天
private var maxAliveTime = 10
//緩存的天數 通常狀況下填0便可。非0表示會在 _cachedir 目錄下存放幾天的日誌。
//原來緩存日期的意思是幾天後從緩存目錄移到日誌目錄
private var cacheDays = 0
fun setCachePath(cachePath: String): Builder {
this.cachePath = cachePath
return this
}
fun setLogPath(logPath: String): Builder {
this.logPath = logPath
return this
}
fun setNamePreFix(namePreFix: String): Builder {
this.namePreFix = namePreFix
return this
}
fun setModel(model: LogModel): Builder {
this.model = model
return this
}
fun setPubKey(key: String): Builder {
this.pubKey = key
return this
}
//原來緩存日期的意思是幾天後從緩存目錄移到日誌目錄 默認 0 便可
//若是想讓文件保留多少天 用 [setMaxAliveTime] 方法便可
//大於 0 的時候 默認會放在緩存的位置上 [cachePath]
fun setCacheDays(days: Int): Builder {
if (days < 0) {
this.cacheDays = 0
} else {
this.cacheDays = days
}
return this
}
fun setDebug(debug: Boolean): Builder {
this.debug = debug
return this
}
fun setLogLevel(level: LogLevel): Builder {
this.logLevel = level
return this
}
fun setConsoleLogOpen(consoleLogOpen: Boolean): Builder {
this.consoleLogOpen = consoleLogOpen
return this
}
fun setTag(logTag: String): Builder {
tag = logTag
return this
}
/** * [isOpen] true 設置天天一個日誌文件 * false 那麼 [setMaxFileSize] 生效 */
fun setOneFileEveryday(isOpen: Boolean): Builder {
this.oneFileEveryday = isOpen
return this
}
fun setMaxFileSize(maxFileSize: Float): Builder {
when {
maxFileSize < 0 -> {
this.maxFileSize = 0L
}
maxFileSize > 10 -> {
this.maxFileSize = (10 * 1024 * 1024).toLong()
}
else -> {
this.maxFileSize = (maxFileSize * 1024 * 1024).toLong()
}
}
return this
}
/** * [day] 設置單個文件的過時時間 默認10天 在程序啓動30S 之後會檢查過時文件 * 過時時間依據 當前系統時間 - 文件最後修改時間計算 * 默認 單個文件保存 10天 */
fun setMaxAliveTime(day: Int): Builder {
when {
day < 0 -> {
this.maxAliveTime = 0
}
day > 10 -> {
this.maxAliveTime = 10
}
else -> {
this.maxAliveTime = day
}
}
return this
}
fun init() {
if (!debug) {
//判斷若是是release 就強制使用 異步
model = LogModel.Async
//日誌級別使用 Info
logLevel = LogLevel.LEVEL_INFO
}
if (cachePath.isEmpty()) {
//cachePath這個參數必傳,並且要data下的私有文件目錄,例如 /data/data/packagename/files/xlog, mmap文件會放在這個目錄,若是傳空串,可能會發生 SIGBUS 的crash。
cachePath = defCachePath
}
android.util.Log.i(tag, "Xlog=========================================>")
android.util.Log.i(
tag,
"info" + "\n"
+ "level:" + logLevel.level + "\n"
+ "model:" + model.model + "\n"
+ "cachePath:" + cachePath + "\n"
+ "logPath:" + logPath + "\n"
+ "namePreFix:" + namePreFix + "\n"
+ "cacheDays:" + cacheDays + "\n"
+ "pubKey:" + pubKey + "\n"
+ "consoleLogOpen:" + consoleLogOpen + "\n"
+ "maxFileSize:" + maxFileSize + "\n"
)
android.util.Log.i(tag, "Xlog=========================================<")
Xlog.setConsoleLogOpen(consoleLogOpen)
//天天一個日誌文件
if (oneFileEveryday) {
Xlog.setMaxFileSize(0)
} else {
Xlog.setMaxFileSize(maxFileSize)
}
Xlog.setMaxAliveTime((maxAliveTime * 24 * 60 * 60).toLong())
Xlog.appenderOpen(
logLevel.level,
model.model,
cachePath,
logPath,
namePreFix,
cacheDays,
pubKey
)
Log.setLogImp(Xlog())
}
}
複製代碼
Step 1. Add the JitPack repository to your build file Add it in your root build.gradle at the end of repositories:
allprojects {
repositories {
...
maven { url 'https://www.jitpack.io' }
}
}
複製代碼
Step 2. Add the dependency
dependencies {
implementation 'com.github.JiangHaiYang01:XLogHelper:Tag'
}
複製代碼
添加 abiFilter
android {
compileSdkVersion 30
buildToolsVersion "30.0.1"
defaultConfig {
...
ndk {
abiFilter "armeabi-v7a"
}
}
...
}
複製代碼
當前最新版本
XLogHelper.create(this)
.setModel(LogModel.Async)
.setTag("TAG")
.setConsoleLogOpen(true)
.setLogLevel(LogLevel.LEVEL_INFO)
.setNamePreFix("log")
.setPubKey("572d1e2710ae5fbca54c76a382fdd44050b3a675cb2bf39feebe85ef63d947aff0fa4943f1112e8b6af34bebebbaefa1a0aae055d9259b89a1858f7cc9af9df1")
.setMaxFileSize(1f)
.setOneFileEveryday(true)
.setCacheDays(0)
.setMaxAliveTime(2)
.init()
複製代碼
使用
XLogHelper.i("======================> %s", i)
XLogHelper.e("======================> %s", i)
複製代碼
寫了那麼多 雙手奉上代碼 XLogHelper
Tencent/mars Mars Android 接口詳細說明 【騰訊Bugly乾貨分享】微信mars 的高性能日誌模塊 xlog 本地編譯