安卓to鴻蒙系列:Logger
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
目錄
- Guide
- Logger源碼分析
- 知識點
- 移植到鴻蒙
- 思考
Guide
本文基于https://gitee.com/openharmony-tpc/logger 分析Logger的源碼,及移植到鴻蒙需要做的工作。
在安卓to鴻蒙系列:Timber 里我就已經(jīng)提到,我喜歡Logger和Timber一起使用。原因很簡單,因為Timber接口簡潔,Logger的輸出樣式好看。
常規(guī)套路:
- FormatStrategy formatStrategy = PrettyFormatStrategy.newBuilder()
- .tag("DwGG") // (Optional) Global tag for every log. Default PRETTY_LOGGER
- .build();
- Logger.addLogAdapter(new AndroidLogAdapter(formatStrategy));
- Timber.plant(new Timber.DebugTree() {
- @Override
- protected void log(int priority, String tag, String message, Throwable t) {
- Logger.log(priority, tag, message, t);
- }
- });
Logger源碼分析
Timber只有一個文件,600多行代碼,Logger相對復(fù)雜一些,不過結(jié)構(gòu)也一樣簡單,很容易讀懂源碼。和Timber一樣,Logger的擴展性也一樣優(yōu)秀。
Timber內(nèi)置了DebugTree(在控制臺輸出日志)。Logger內(nèi)置了AndroidLogAdapter(控制臺輸出)、DiskLogAdapter(輸出日志到本地磁盤)
先看圖,有一個整體的認(rèn)識:
解釋一下上面的圖:
1.Logger 也使用了 委托(delegate)模式,Logger把所以的操作都委托給了Printer printer
2.Printer 是日志能力的抽像(即,定義了日志工具的api),類似于Timber中的Tree。定義的api有:
- 日志相關(guān)api:v()、d()、i()、e()、wtf(),以及xml()、json()對xml、json數(shù)據(jù)格式的支持。
- 設(shè)置臨時tag:t()
- LogAdapter相關(guān)api:addAdapter(LogAdapter adapter)、clearLogAdapters()
Printer的默認(rèn)實現(xiàn)是LoggerPrinter,我們可以自定義自己的實現(xiàn)。不過,多數(shù)情況下貌似沒必要。
3.與Timber相比,Logger對Timber中的Tree進一步抽像。以 LogAdapter 適配不同的日志能力,如Android控制臺輸出(AndroidLogAdapter)、保存到磁盤(DiskLogAdapter)。
從類名上,我們也能看出用了適配器(apapter)模式
當(dāng)然我們也可以定義鴻蒙HiLog控制臺輸出的LogAdapter(取個名字:HarmonyLogAdapter)
4.FormatStrategy 格式化原始數(shù)據(jù),決定了LogAdapter輸出日志的樣式。
AndroidLogAdapter中的默認(rèn)實現(xiàn)是PrettyFormatStrategy(可以顯示線程信息、統(tǒng)一的tag:"PRETTY_LOGGER"、線程棧);
DiskLogAdapter中的默認(rèn)實現(xiàn)是CsvFormatStrategy(csv文件的格式);
5.LogStrategy 最終決定FormatStrategy輸出日志的目標(biāo)(如:控制臺LogcatLogStrategy、磁盤DiskLogStrategy)
更詳細的分析請移步:Android日志系統(tǒng)第三方庫------Logger 源碼分析
知識點
1.臨時tag的實現(xiàn)方法
很簡單,Logger.t("臨時tag").d(xxx);設(shè)置臨時tag。使用一次就刪除。最終也是委托給Printer來實現(xiàn)。
為了性能,LoggerPrinter和Timber一樣,也使用ThreadLocal以空間換時間。
2.多線程支持方面,只有LoggerPrinter的synchronized void log()加了鎖。
個人認(rèn)為,應(yīng)該把LoggerPrinter的addAdapter(LogAdapter adapter)和 clearLogAdapters()都加上鎖。
因為log()方法加鎖,所以一定要保證此方法的執(zhí)行效率。 不然,如果一個線程寫日志太耗時,會影響其它線程的執(zhí)行。
3.接上一條,DiskLogStrategy 在寫文件時,在獨立的HandlerThread線程中執(zhí)行,可以保證LoggerPrinter的void log()方法的執(zhí)行時間。
移植到鴻蒙
通過上面的分析,我們可以知道,移植到鴻蒙需要的工作有:
1.實現(xiàn)HarmonyLogAdapter提供 控制臺輸出 的能力,其中PrettyFormatStrategy基本可以復(fù)用,只要修改LogcatLogStrategy的實現(xiàn),用HiLog替換android.util.Log相關(guān)的操作。
- public PrettyFormatStrategy build() {
- if (logStrategy == null) {
- logStrategy = new LogcatLogStrategy();
- }
- return new PrettyFormatStrategy(this);
- }
2.修改DiskLogAdapter提供 輸出日志到文件 的能力。其中CsvFormatStrategy基本可以復(fù)用,只要修改日志文件路徑的設(shè)置以及Handler相關(guān)的類。
- String diskPath = Environment.getExternalStorageDirectory().getAbsolutePath();
- String folder = diskPath + File.separatorChar + "logger";
- HandlerThread ht = new HandlerThread("AndroidFileLogger." + folder);
- ht.start();
- Handler handler = new DiskLogStrategy.WriteHandler(ht.getLooper(), folder, MAX_BYTES);
修改為:
- String folder = "/storage/emulated/0/" + File.separatorChar + "logger";
- String newThreadName = "FileLogger" + folder;
- EventRunner ht = EventRunner.create(newThreadName);
- EventHandler handler = new DiskLogStrategy.WriteHandler(ht, folder, MAX_BYTES);
同理DiskLogStrategy里的Handler也要改為EventHandler。
思考
上圖是最終的效果。從代碼中可以看到HiLogLabel是固定的,所以domain和tag都是固定的。而為了實現(xiàn)自定義tag的目的,把自定義tag輸出到了message里,所以我們可以看到兩個"PRETTY_LOGGER",視覺上不太完美。
- public class LogcatLogStrategy implements LogStrategy {
- static final String DEFAULT_TAG = "NO_TAG";
- static final String TAG_LOG = "[PRETTY_LOGGER] ";
- static final int DOMAIN_ID = 0xD000F00;
- static final HiLogLabel LABEL_LOG = new HiLogLabel(3, DOMAIN_ID, TAG_LOG);
- }
優(yōu)化的方法可以參考 Timber_ohos的實現(xiàn)。
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)