安卓to鸿蒙系列:Logger 原创 精华

没用的喵叔
发布于 2021-4-24 22:50
浏览
0收藏

@toc

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(输出日志到本地磁盘)

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

安卓to鸿蒙系列:Logger-鸿蒙开发者社区

解释一下上面的图:

  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来实现。

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

  2. 多线程支持方面,只有LoggerPrintersynchronized void log()加了锁。

    个人认为,应该把LoggerPrinteraddAdapter(LogAdapter adapter) clearLogAdapters()都加上锁。

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

  3. 接上一条,DiskLogStrategy 在写文件时,在独立的HandlerThread线程中执行,可以保证LoggerPrintervoid 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

思考

安卓to鸿蒙系列:Logger-鸿蒙开发者社区

上图是最终的效果。从代码中可以看到HiLogLabel是固定的,所以domaintag都是固定的。而为了实现自定义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的实现

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
标签
已于2021-4-25 08:26:49修改
2
收藏
回复
举报
1条回复
按时间正序
/
按时间倒序
longlong899
longlong899

好文,谢谢分享!!

回复
2021-4-25 09:55:10
回复
    相关推荐