安卓to鴻蒙系列:Logger
目錄
- Guide
- Logger源碼分析
- 知識點
- 移植到鴻蒙
- 思考
Guide
本文基于https://gitee.com/openharmony-tpc/logger 分析Logger的源碼,及移植到鴻蒙需要做的工作。
在安卓to鴻蒙系列:Timber 里我就已經提到,我喜歡Logger和Timber一起使用。原因很簡單,因為Timber接口簡潔,Logger的輸出樣式好看。
常規套路:
- 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相對復雜一些,不過結構也一樣簡單,很容易讀懂源碼。和Timber一樣,Logger的擴展性也一樣優秀。
Timber內置了DebugTree(在控制臺輸出日志)。Logger內置了AndroidLogAdapter(控制臺輸出)、DiskLogAdapter(輸出日志到本地磁盤)
先看圖,有一個整體的認識:
解釋一下上面的圖:
1.Logger 也使用了 委托(delegate)模式,Logger把所以的操作都委托給了Printer printer
2.Printer 是日志能力的抽像(即,定義了日志工具的api),類似于Timber中的Tree。定義的api有:
- 日志相關api:v()、d()、i()、e()、wtf(),以及xml()、json()對xml、json數據格式的支持。
- 設置臨時tag:t()
- LogAdapter相關api:addAdapter(LogAdapter adapter)、clearLogAdapters()
Printer的默認實現是LoggerPrinter,我們可以自定義自己的實現。不過,多數情況下貌似沒必要。
3.與Timber相比,Logger對Timber中的Tree進一步抽像。以 LogAdapter 適配不同的日志能力,如Android控制臺輸出(AndroidLogAdapter)、保存到磁盤(DiskLogAdapter)。
從類名上,我們也能看出用了適配器(apapter)模式
當然我們也可以定義鴻蒙HiLog控制臺輸出的LogAdapter(取個名字:HarmonyLogAdapter)
4.FormatStrategy 格式化原始數據,決定了LogAdapter輸出日志的樣式。
AndroidLogAdapter中的默認實現是PrettyFormatStrategy(可以顯示線程信息、統一的tag:"PRETTY_LOGGER"、線程棧);
DiskLogAdapter中的默認實現是CsvFormatStrategy(csv文件的格式);
5.LogStrategy 最終決定FormatStrategy輸出日志的目標(如:控制臺LogcatLogStrategy、磁盤DiskLogStrategy)
更詳細的分析請移步:Android日志系統第三方庫------Logger 源碼分析
知識點
1.臨時tag的實現方法
很簡單,Logger.t("臨時tag").d(xxx);設置臨時tag。使用一次就刪除。最終也是委托給Printer來實現。
為了性能,LoggerPrinter和Timber一樣,也使用ThreadLocal以空間換時間。
2.多線程支持方面,只有LoggerPrinter的synchronized void log()加了鎖。
個人認為,應該把LoggerPrinter的addAdapter(LogAdapter adapter)和 clearLogAdapters()都加上鎖。
因為log()方法加鎖,所以一定要保證此方法的執行效率。 不然,如果一個線程寫日志太耗時,會影響其它線程的執行。
3.接上一條,DiskLogStrategy 在寫文件時,在獨立的HandlerThread線程中執行,可以保證LoggerPrinter的void log()方法的執行時間。
移植到鴻蒙
通過上面的分析,我們可以知道,移植到鴻蒙需要的工作有:
1.實現HarmonyLogAdapter提供 控制臺輸出 的能力,其中PrettyFormatStrategy基本可以復用,只要修改LogcatLogStrategy的實現,用HiLog替換android.util.Log相關的操作。
- public PrettyFormatStrategy build() {
- if (logStrategy == null) {
- logStrategy = new LogcatLogStrategy();
- }
- return new PrettyFormatStrategy(this);
- }
2.修改DiskLogAdapter提供 輸出日志到文件 的能力。其中CsvFormatStrategy基本可以復用,只要修改日志文件路徑的設置以及Handler相關的類。
- 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都是固定的。而為了實現自定義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);
- }
優化的方法可以參考 Timber_ohos的實現。