暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

安卓日志库Logger移植到鸿蒙!

鸿蒙技术社区 2021-04-25
883

本文基于 https://gitee.com/openharmony-tpc/logger 分析 Logger 的源码,及移植到鸿蒙需要做的工作。


我喜欢 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);
    }
});


01

Logger 源码分析


Timber 只有一个文件,600 多行代码,Logger 相对复杂一些,不过结构也一样简单,很容易读懂源码。和 Timber 一样,Logger 的扩展性也一样优秀。


Timber 内置了 DebugTree(在控制台输出日志)。Logger 内置了 AndroidLogAdapter(控制台输出)、DiskLogAdapter(输出日志到本地磁盘)。


先看图,有一个整体的认识:

解释一下上面的图:


Logger 也使用了 委托(delegate)模式,Logger 把所以的操作都委托给了 Printer printer。


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,我们可以自定义自己的实现。不过,多数情况下貌似没必要。


与 Timber 相比,Logger 对 Timber 中的 Tree 进一步抽像。


以 LogAdapter 适配不同的日志能力,如 Android 控制台输出(AndroidLogAdapter)、保存到磁盘(DiskLogAdapter)。


从类名上,我们也能看出用了适配器(apapter)模式,当然我们也可以定义鸿蒙 HiLog 控制台输出的 LogAdapter(取个名字:HarmonyLogAdapter)。


FormatStrategy 格式化原始数据,决定了 LogAdapter 输出日志的样式。


AndroidLogAdapter 中的默认实现是 PrettyFormatStrategy(可以显示线程信息、统一的 tag:"PRETTY_LOGGER"、线程栈)。


DiskLogAdapter 中的默认实现是 CsvFormatStrategy(csv 文件的格式)。


LogStrategy 最终决定 FormatStrategy 输出日志的目标(如:控制台LogcatLogStrategy、磁盘 DiskLogStrategy)。


更详细的分析请移步:Android 日志系统第三方库——Logger 源码分析。


02

知识点


①临时 tag 的实现方法:很简单,Logger.t("临时 tag").d(xxx);设置临时 tag。使用一次就删除。最终也是委托给 Printer 来实现。


为了性能,LoggerPrinter 和 Timber 一样,也使用 ThreadLocal 以空间换时间。


②多线程支持方面,只有 LoggerPrinter 的 synchronized void log() 加了锁。


个人认为,应该把 LoggerPrinter 的 addAdapter(LogAdapter adapter)和 clearLogAdapters() 都加上锁。


因为 log() 方法加锁,所以一定要保证此方法的执行效率。不然,如果一个线程写日志太耗时,会影响其他线程的执行。


③接上一条,DiskLogStrategy 在写文件时,在独立的 HandlerThread 线程中执行,可以保证 LoggerPrinter 的 void log() 方法的执行时间。


03

移植到鸿蒙


通过上面的分析,我们可以知道,移植到鸿蒙需要的工作有:


实现 HarmonyLogAdapter 提供控制台输出的能力。


其中 PrettyFormatStrategy 基本可以复用,只要修改 LogcatLogStrategy 的实现,用 HiLog 替换 android.util.Log 相关的操作。
public PrettyFormatStrategy build() {
  if (logStrategy == null) {
    logStrategy = new LogcatLogStrategy();
  }
  return new PrettyFormatStrategy(this);
}


修改 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。


04

思考


上图是最终的效果。从代码中可以看到 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 的实现:
https://gitee.com/andych008/timber_ohos


送福利啦

关注鸿蒙技术社区,回复【鸿蒙】《鸿蒙应用开发实战书籍(数量有限,先到先得),还可以免费下载鸿蒙入门资料

👇点击立刻关注👇

专注开源技术,共建鸿蒙生态


“阅读原文”了解更多

文章转载自鸿蒙技术社区,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论