Github: https://github.com/elvishew/xLog
輕量、美觀強(qiáng)大、可擴(kuò)展的 Android 和 Java 日志庫,可同時(shí)將日志打印在如 Logcat、Console 和文件中。如果你愿意,你可以將日志打印到任何地方。
Logcat 輸出

快速開始
依賴
implementation 'com.elvishew:xlog:1.10.0'
初始化
XLog.init(LogLevel.ALL);
打印日志
XLog.d("你好 xlog");
打印日志
打印簡單消息。
XLog.d(message);
打印帶 throwable 的消息,通常用于有異常被拋出時(shí)。
XLog.e(message, throwable);
支持格式化字符串,這樣你就不需要去使用 + 拼接一大串的字符串和變量。
XLog.d("你好%s,我今年 %d 歲", "Elvis", 20);
未格式化的 JSON 和 XML 字符串會被自動格式化。
XLog.json(JSON_CONTENT);
XLog.xml(XML_CONTENT);
支持所有的 Collection 和 Map 類型的數(shù)據(jù)。
XLog.d(array);
XLog.d(list);
XLog.d(map);
如需要,你也可以直接打印 Intent 和 Bundle 對象。
XLog.d(intent);
XLog.d(bundle);
事實(shí)上,你可以打印任何類型的對象。你甚至可以為不同類型指定不同的 ObjectFormatter,如不指定,在對象轉(zhuǎn)換為字符串時(shí),會直接調(diào)用對象類型的 toString()。
XLog.d(object);
注意:以上內(nèi)容中的 v/d/i/w/e 是可以相互替換的,v 表示 VERBOSE,d 表示 DEBUG,i for INFO,w 表示 WARNING,e 表示 ERROR。
配置
xLog 具有高度可擴(kuò)展性,幾乎任何一個(gè)組件都是可配置的。
當(dāng)初始化時(shí),可以用最簡單的方式,
XLog.init(LogLevel.ALL);
也可以用高級的方式。
LogConfiguration config = new LogConfiguration.Builder()
.logLevel(BuildConfig.DEBUG ? LogLevel.ALL // 指定日志級別,低于該級別的日志將不會被打印,默認(rèn)為 LogLevel.ALL
: LogLevel.NONE)
.tag("MY_TAG") // 指定 TAG,默認(rèn)為 "X-LOG"
.enableThreadInfo() // 允許打印線程信息,默認(rèn)禁止
.enableStackTrace(2) // 允許打印深度為 2 的調(diào)用棧信息,默認(rèn)禁止
.enableBorder() // 允許打印日志邊框,默認(rèn)禁止
.jsonFormatter(new MyJsonFormatter()) // 指定 JSON 格式化器,默認(rèn)為 DefaultJsonFormatter
.xmlFormatter(new MyXmlFormatter()) // 指定 XML 格式化器,默認(rèn)為 DefaultXmlFormatter
.throwableFormatter(new MyThrowableFormatter()) // 指定可拋出異常格式化器,默認(rèn)為 DefaultThrowableFormatter
.threadFormatter(new MyThreadFormatter()) // 指定線程信息格式化器,默認(rèn)為 DefaultThreadFormatter
.stackTraceFormatter(new MyStackTraceFormatter()) // 指定調(diào)用棧信息格式化器,默認(rèn)為 DefaultStackTraceFormatter
.borderFormatter(new MyBoardFormatter()) // 指定邊框格式化器,默認(rèn)為 DefaultBorderFormatter
.addObjectFormatter(AnyClass.class, // 為指定類型添加對象格式化器
new AnyClassObjectFormatter()) // 默認(rèn)使用 Object.toString()
.addInterceptor(new BlacklistTagsFilterInterceptor( // 添加黑名單 TAG 過濾器
"blacklist1", "blacklist2", "blacklist3"))
.addInterceptor(new MyInterceptor()) // 添加一個(gè)日志攔截器
.build();
Printer androidPrinter = new AndroidPrinter(true); // 通過 android.util.Log 打印日志的打印器
Printer consolePrinter = new ConsolePrinter(); // 通過 System.out 打印日志到控制臺的打印器
Printer filePrinter = new FilePrinter // 打印日志到文件的打印器
.Builder("<日志目錄全路徑>") // 指定保存日志文件的路徑
.fileNameGenerator(new DateFileNameGenerator()) // 指定日志文件名生成器,默認(rèn)為 ChangelessFileNameGenerator("log")
.backupStrategy(new NeverBackupStrategy()) // 指定日志文件備份策略,默認(rèn)為 FileSizeBackupStrategy(1024 * 1024)
.cleanStrategy(new FileLastModifiedCleanStrategy(MAX_TIME)) // 指定日志文件清除策略,默認(rèn)為 NeverCleanStrategy()
.flattener(new MyFlattener()) // 指定日志平鋪器,默認(rèn)為 DefaultFlattener
.build();
XLog.init( // 初始化 XLog
config, // 指定日志配置,如果不指定,會默認(rèn)使用 new LogConfiguration.Builder().build()
androidPrinter, // 添加任意多的打印器。如果沒有添加任何打印器,會默認(rèn)使用 AndroidPrinter(Android)/ConsolePrinter(java)
consolePrinter,
filePrinter);
初始化后,一個(gè)擁有全局配置的全局 Logger 將被創(chuàng)建,所有對 XLog 的打印函數(shù)的調(diào)用都會被傳遞到這個(gè)全局 Logger 來進(jìn)行打印。
另外,你可以創(chuàng)建不限個(gè)數(shù)的、不同配置的 Logger:
- 基于全局
Logger將 TAG 改為"TAG-A"。
Logger logger = XLog.tag("TAG-A")
... // 其他配置的覆蓋
.build();
logger.d("定制了 TAG 的消息");
- 基于全局
Logger,允許打印日志邊框和線程信息。
Logger logger = XLog.enableBorder()
.enableThread()
... // 其他配置的覆蓋
.build();
logger.d("帶有線程信息和日志邊框的消息");
你還可以使用一次性配置來打印日志。
XLog.tag("TAG-A").d("定制了 TAG 的消息");
XLog.enableBorder().enableThread().d("帶有線程信息和日志邊框的消息");
打印到任何地方
只需一句調(diào)用
XLog.d("你好 xlog");
你就可以將 "你好 xlog" 打印到
Logcat(使用
AndroidPrinter)文件(使用
FilePrinter)
以及任何你想打印到的其他地方。
打印到其他地方,你只需自己實(shí)現(xiàn)個(gè) Printer 接口,并在初始化過程中指定它
XLog.init(config, printer1, printer2...printerN);
或者在創(chuàng)建非全局 Logger 時(shí)指定它
Logger logger = XLog.printer(printer1, printer2...printerN)
.build();
或者在一次性打印時(shí)指定它
XLog.printer(printer1, printer2...printerN).d("用一次性配置打印的消息");
保存日志到文件
要保存日志到文件,你需要?jiǎng)?chuàng)建一個(gè) FilePrinter
Printer filePrinter = new FilePrinter // 打印日志到文件的打印器
.Builder("<日志目錄全路徑>") // 指定保存日志文件的路徑
.fileNameGenerator(new DateFileNameGenerator()) // 指定日志文件名生成器,默認(rèn)為 ChangelessFileNameGenerator("log")
.backupStrategy(new NeverBackupStrategy()) // 指定日志文件備份策略,默認(rèn)為 FileSizeBackupStrategy(1024 * 1024)
.cleanStrategy(new FileLastModifiedCleanStrategy(MAX_TIME)) // 指定日志文件清除策略,默認(rèn)為 NeverCleanStrategy()
.flattener(new MyFlattener()) // 指定日志平鋪器,默認(rèn)為 DefaultFlattener
.build();
并在初始化時(shí)添加它
XLog.init(config, filePrinter);
或者在創(chuàng)建非全局 Logger 時(shí)添加它
Logger logger = XLog.printer(filePrinter)
... // other overrides
.build();
或者在一次性打印時(shí)添加它
XLog.printer(filePrinter).d("用一次性配置打印的消息");
保存第三方庫打印的日志到文件
你可以在初始化 XLog 后配置 LibCat。
LibCat.config(true, filePrinter);
然后,由第三方庫/模塊(在同一個(gè) app 里)打印的日志也將會被保存到文件中。
點(diǎn)擊 LibCat 了解更多細(xì)節(jié)。
自定義日志文件名
你可以直接指定一個(gè)文件名,也可以根據(jù)一些規(guī)則將日志保存到不同文件中。
- 使用
ChangelessFileNameGenerator,你可以指定一個(gè)不變的文件名。
日志目錄
└──log
- 使用
LevelFileNameGenerator,根據(jù)級別將日志保存到不同文件中。
日志目錄
├──VERBOSE
├──DEBUG
├──INFO
├──WARN
└──ERROR
- 使用
DateFileNameGenerator,根據(jù)日期將日志保存到不同文件中。
日志目錄
├──2020-01-01
├──2020-01-02
├──2020-01-03
└──2020-01-04
- 直接實(shí)現(xiàn)一個(gè)
FileNameGenerator,根據(jù)自定義的文件名生成規(guī)則來保存日志。
日志目錄
├──2020-01-01-<hash1>.log
├──2020-01-01-<hash2>.log
├──2020-01-03-<hash>.log
└──2020-01-05-<hash>.log
默認(rèn)情況下,會使用 ChangelessFileNameGenerator 將日志保存到一個(gè)名叫 log 的文件中。
自定義日志格式
各日志元素(日期,時(shí)間,日志級別和消息) 在被保存到日志文件前,需要被“平鋪”成一個(gè)單獨(dú)的字符串,你可以使用 Flattener 來做這件事。
我們已經(jīng)定義了一個(gè) PatternFlattener,足以滿足你的大部分需求。你所需要做的只是,傳入一個(gè)帶參的 pattern。
支持的參數(shù):
| 參數(shù) | 含義 |
|---|---|
| u0z1t8os | 日期時(shí)間。使用默認(rèn)的日期時(shí)間格式 "yyyy-MM-dd HH:mm:ss.SSS" |
| {d format} | 日期時(shí)間。使用自定義的日期時(shí)間格式 |
| {l} | 日志級別的縮寫。例如:V/D/I |
| {L} | 日志級別的全稱。例如:VERBOSE/DEBUG/INFO |
| {t} | 日志的 TAG |
| {m} | 日志的消息 |
想象有這么一個(gè)日志,級別為 DEBUG,TAG 為 "my_tag",消息為 "簡單消息",使用不同的 pattern,平鋪后的日志為:
| Pattern | 平鋪后的日志 |
|---|---|
| u0z1t8os {l}/{t}: {m} | 2016-11-30 13:00:00.000 D/my_tag: 簡單消息 |
| {d yyyy-MM-dd HH:mm:ss.SSS} {l}/{t}: {m} | 2016-11-30 13:00:00.000 D/my_tag: 簡單消息 |
| {d yyyy/MM/dd HH:mm:ss} {l}|{t}: {m} | 2016/11/30 13:00:00 D|my_tag: 簡單消息 |
| {d yy/MM/dd HH:mm:ss} {l}|{t}: {m} | 16/11/30 13:00:00 D|my_tag: 簡單消息 |
| {d MM/dd HH:mm} {l}-{t}-{m} | 11/30 13:00 D-my_tag-簡單消息 |
如果你不想自己指定所謂的 pattern,可以使用 ClassicFlattener。它實(shí)際上是一個(gè)使用 u0z1t8os {l}/{t}: {m} pattern 的 PatternFlattener。
默認(rèn)情況下,FilePrinter 會使用 DefaultFlattener,這個(gè)平鋪器只會簡單地將時(shí)間戳和消息連接起來,你應(yīng)該不會喜歡它,所以你得記得自己指定 Flattener,推薦使用 ClassicFlattener。
自動備份
隨著時(shí)間推移,日志文件可能會變得很大,大到我們不希望的程度。使用 AbstractBackupStrategy2 可以幫助我們在特定條件下創(chuàng)建一個(gè)全新的同名日志文件,并繼續(xù)寫入,而舊日志文件會被加上 .bak.n(n 是備份序號)的文件名后綴。以上過程即為“日志備份”
日志目錄
├──log
├──log.bak.1
├──log.bak.2
├──log.bak.3
├──...
└──log.bak.n
如果你不喜歡 .bak.n 后綴,你可以直接使用 BackupStrategy2 指定備份文件名。
大部分時(shí)候,你只是想在日志文件達(dá)到一定大小時(shí),觸發(fā)備份。 FileSizeBackupStrategy2 剛好可以滿足這個(gè)要求。
默認(rèn)情況下,xLog 會使用 FileSizeBackupStrategy(1024*1024),在日志文件大小達(dá)到 1M 時(shí)觸發(fā)備份,且同時(shí)最多只會有一個(gè)正在寫入的文件,以及一個(gè)備份文件,這意味著你最多只能保存 2M 的日志。
所以,如果你想要保存更多的日志,以及允許更多的備份數(shù)量(而不僅僅是默認(rèn)的一個(gè)),請使用 FileSizeBackupStrategy2,它允許多個(gè)備份文件同時(shí)存在。
自動清理
如果你使用會生成可變名字的 FileNameGenerator,那日志文件夾里就很可能會有不止一個(gè)日志,并且可能會越來越多。此外,如果你還使用了不限數(shù)量的備份策略,那也可能會讓日志數(shù)量失控。為了防止占滿磁盤,你需要一個(gè) CleanStrategy。
通常,你可以使用 FileLastModifiedCleanStrategy,它會在初始化期間自動刪掉那些一段時(shí)間(如:一周)以來都未被修改的日志文件。
默認(rèn)情況下,會使用 NeverCleanStrategy,它不會做任何自動清理的工作。
壓縮日志文件
僅需調(diào)用
LogUtil.compress("<日志目錄全路徑>", "<要保存的壓縮文件全路徑>");
一個(gè) zip 文件將會被創(chuàng)建,整個(gè)日志文件夾都將被壓縮并被寫入其中,這樣你可以輕松收集到用戶日志用于問題調(diào)試。
注意:原始的日志文件不會被刪除。
攔截和過濾日志
使用 Interceptor,在每條日志被打印之前,你都會有一個(gè)機(jī)會去修改或過濾掉該日志。
你可以使用一些預(yù)定義的 Interceptor,比如 WhitelistTagsFilterInterceptor 只允許帶特定 TAG 的日志被打印,BlacklistTagsFilterInterceptor 被用來過濾掉帶特定 TAG 的日志。
你可以為單個(gè) Logger 指定多個(gè) Interceptor,這些 Interceptor 將會按被添加的順序依次獲得修改或過濾掉日志的機(jī)會。當(dāng)一條日志被某 Interceptor 過濾掉,后續(xù)的 Interceptor 將不再獲得處理該日志的機(jī)會。
格式化任意類型的對象
當(dāng)我們直接打印對象時(shí)
XLog.d(object);
默認(rèn)情況下,該對象類型的 toString 將會被調(diào)用。
有時(shí)候,對象類型的 toString 實(shí)現(xiàn)并不是你想要的,所以你需要 ObjectFormatter 來定義這種類型的對象在打印時(shí)該如何轉(zhuǎn)化成字符串。
在 Android 平臺上,我們?yōu)?Intent 和 Bundle 類型預(yù)定義了 IntentFormatter 和 BundleFormatter。
你可以為任意類型實(shí)現(xiàn)和添加你自己的 ObjectFormatter。
請注意,ObjectFormatter 僅在直接打印一個(gè)對象時(shí)有效。
類似的庫
與其他日志庫對比:
- 很好的文檔化
- 擴(kuò)展性強(qiáng),可輕松實(shí)現(xiàn)定制和功能增強(qiáng)
兼容性
為了與 Android Log 兼容,xLog 支持 Android Log 的所有方法。
請看 XLog 中定義的 Log 類.
Log.v(String, String);
Log.v(String, String, Throwable);
Log.d(String, String);
Log.d(String, String, Throwable);
Log.i(String, String);
Log.i(String, String, Throwable);
Log.w(String, String);
Log.w(String, String, Throwable);
Log.wtf(String, String);
Log.wtf(String, String, Throwable);
Log.e(String, String);
Log.e(String, String, Throwable);
Log.println(int, String, String);
Log.isLoggable(String, int);
Log.getStackTraceString(Throwable);
遷移
如果你有一個(gè)大型項(xiàng)目正在使用 Android Log, 并且很難將所有對 Android Log 的使用都換成 XLog,那么你可以使用兼容 API,簡單地把所有 'android.util.Log' 替換成 'com.elvishew.xlog.XLog.Log'。
(為了更好的性能,盡量不要使用兼容 API。)
Linux/Cygwin
grep -rl "android.util.Log" <your-source-directory> | xargs sed -i "s/android.util.Log/com.elvishew.xlog.XLog.Log/g"
Mac
grep -rl "android.util.Log" <your-source-directory> | xargs sed -i "" "s/android.util.Log/com.elvishew.xlog.XLog.Log/g"
Android Studio
- 在 'Project' 窗口中,切換到 'Project Files' 標(biāo)簽,然后右鍵點(diǎn)擊你的源碼目錄。
- 在出現(xiàn)的菜單中,點(diǎn)擊 'Replace in Path...' 選項(xiàng)。
- 在彈出的對話框中,在 'Text to find' 區(qū)域填上 'android.util.Log','Replace with' 區(qū)域填上 'com.elvishew.xlog.XLog.Log' 然后點(diǎn)擊 'Find'。
相比替換掉所有 'android.util.Log',還有另一種方式。你可以使用 LibCat 攔截所有通過 android.util.Log 打印的日志,將他們重定向到 XLog 的 Printer。
Issues
如果你在使用過程中遇到任何問題或者有任何建議,請創(chuàng)建一個(gè) Issue。
在創(chuàng)建 Issue 前,請檢查類似 Issue 是否已經(jīng)存在.
第三方詳解
Github: https://github.com/elvishew/xLog