Android 應(yīng)用開發(fā)必備日志庫 xLog

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);

支持所有的 CollectionMap 類型的數(shù)據(jù)。

XLog.d(array);
XLog.d(list);
XLog.d(map);

如需要,你也可以直接打印 IntentBundle 對象。

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 表示 DEBUGi for INFO,w 表示 WARNINGe 表示 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} patternPatternFlattener。

默認(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)?IntentBundle 類型預(yù)定義了 IntentFormatterBundleFormatter。

你可以為任意類型實(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

  1. 在 'Project' 窗口中,切換到 'Project Files' 標(biāo)簽,然后右鍵點(diǎn)擊你的源碼目錄。
  2. 在出現(xiàn)的菜單中,點(diǎn)擊 'Replace in Path...' 選項(xiàng)。
  3. 在彈出的對話框中,在 '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 打印的日志,將他們重定向到 XLogPrinter

Issues

如果你在使用過程中遇到任何問題或者有任何建議,請創(chuàng)建一個(gè) Issue。
在創(chuàng)建 Issue 前,請檢查類似 Issue 是否已經(jīng)存在.

第三方詳解

Github: https://github.com/elvishew/xLog

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

相關(guān)閱讀更多精彩內(nèi)容

  • Timber Google官方Demo使用的日志庫 詳細(xì)用法參考: Timber Timber說明: 默認(rèn)的Tre...
    A代碼搬運(yùn)工閱讀 1,852評論 0 0
  • 一、業(yè)務(wù)背景 用戶使用客戶端應(yīng)用過程中,會遇到各種bug, 包括奔潰、數(shù)據(jù)顯示錯(cuò)誤、交互出現(xiàn)問題等等,雖然APP已...
    Jonrencxr閱讀 11,421評論 3 6
  • 背景 在Android開發(fā)過程中難免會需要日志輸出的,日志在開發(fā)調(diào)試、異常跟蹤以及排查問題上都有很大的幫助,但是打...
    Coder蔣閱讀 3,374評論 0 1
  • 表情是什么,我認(rèn)為表情就是表現(xiàn)出來的情緒。表情可以傳達(dá)很多信息。高興了當(dāng)然就笑了,難過就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 129,553評論 2 7
  • 16宿命:用概率思維提高你的勝算 以前的我是風(fēng)險(xiǎn)厭惡者,不喜歡去冒險(xiǎn),但是人生放棄了冒險(xiǎn),也就放棄了無數(shù)的可能。 ...
    yichen大刀閱讀 7,716評論 0 4

友情鏈接更多精彩內(nèi)容