基于安卓平台的日志工具组件 Timber, 实现鸿蒙的功能化迁移和重构,代码已经开源,欢迎各位开发者提出宝贵意见。


图片
开源地址:
https://gitee.com/isrc_ohos/timber_ohos

Timber_ohos 是一个带有小型可扩展 API 的日志工具组件,它可以给开发者提供统一的 API 接口,来记录不同类型的日志,帮助开发者管理不同类型的 log。


同时,Timber_ohos 是项目开发时的 log 开关,通过此开关控制 log 的打印与关闭,从而形成不同的软件版本。该组件功能丰富且使用简单高效,可以被广泛应用于软件项目开发中。


组件效果展示


①测试界面


如图 1 所示,这是一个为了测试 Timber_ohos 功能而简单构建的 UI 页面。点击“测试”按钮即可输出相应的 log。
图片

图 1:测试界面 UI 图


②Log 打印


Timber 类的静态方法调用如图 2 中的(a)图所示。运行项目后查看 HiLog 显示,可以看到实时打印出来的日志,如图 2 中的(b)图所示。
图片

图片

图 2:HiLog 日志打印


Sample 解析


①Tree 的使用


Timber_ohos 将不同的日志操作以树(Tree)的概念进行表示,种植一种树就拥有一种日志记录功能,种植多种树就拥有多种日志记录的功能。


树的种类有很多,常见的树有:DebugTree、RealeseTree、FileTree、CrashReportingTree 等,这些树都是继承自 Tree 类。

  • DebugTree:对所有的日志进行记录。

  • RealeseTree:只对 warn,error,wtf 信息进行记录。

  • FileTree:在运行时将日志记录到文件中。

  • CrashReportingTree:对应用崩溃时的信息进行记录。


Timber_ohos 中默认已经种植了 DebugTree,由于 Timber_ohos 本身是一个可扩展的框架。


因此开发者想得到其他类型的 Log 日志时,就需要自己实现一个日志记录类 ,然后种植到 Timber_ohos 中即可。


②Sample 的实现


Sample 部分需要添加日志记录种类,并负责整体显示布局的搭建。首先为 Timber_ohos 组件添加想要的任何 Tree 子类实例(这里使用的是 DebugTree)。


然后设置简单的按钮监听器,当按动按钮时在鸿蒙常规 HiLog 中出现调试日志。


下面将详细介绍组件的使用方法:

  • 步骤 1:种树(添加 Tree 子类实例)。

  • 步骤 2:创建整体的显示布局。

  • 步骤 3:导入相关类并设置按钮监听。

  • 步骤 4:使用 Tree 实例。


(1)种树(添加 Tree 子类实例)


本步骤是在 ExampleApp 类的 onInitialize() 方法中实现的。首先需要创建 Tree 子类实例,然后调用 Timber 的 plant() 方法,同时将实例作为 plant() 方法的参数,这个过程叫做“种树”。
Timber.plant(new Timber.DebugTree(0x001f00));

(2)创建整体的显示布局


在 XML 文件中创建一个 DirectionalLayout 作为整体显示布局,宽度和高度都跟随父控件变化而调整。


创建两个组件,分别是 Text 组件和 Button 组件,用于控制组件效果显示。


整体显示布局如图 1 所示:
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:orientation="vertical"
    ohos:padding="32vp"
    ohos:background_element="#ffffff"
    ohos:alignment="horizontal_center">

    <Text   //“测试”提示
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:layout_alignment="horizontal_center"
        ohos:text="Timber测试"
        ohos:text_size="35fp"/>

    <Button  //控制按钮
        ohos:id="$+id:btn1"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:top_margin="35vp"
        ohos:text_size="25fp"
        ohos:background_element="#FF51A8DD"
        ohos:padding="10vp"
        ohos:text="测试"/>

</DirectionalLayout>

(3)导入显示布局并设置按钮监听


在 MainAbilitySlice 中,整体显示布局也需要通过 super.setUIContent() 方法进行设置,才能生效并成功显示。


然后给按钮设置点击事件,当用户需要使用 Tree 子类实例时,可通过手指进行点击。
super.setUIContent(ResourceTable.Layout_ability_main);//设置整体显示布局
findComponentById(ResourceTable.Id_btn1).setClickedListener(new Component.ClickedListener() {
    ...//按钮的点击事件
}

(4)使用 Tree 实例


当用户需要打印调试日志的时候,调用 Timber 的静态方法,就会在鸿蒙常规 HiLog 上出现调试日志。调试日志如组件效果展示部分的图 2 所示。
Timber.e  ("Timber.e 测试成功!!!");
Timber.d  ("Timber.d 测试成功!!!");
Timber.i  ("Timber.i 测试成功!!!");
Timber.w   ("Timber.w 测试成功!!!");
Timber.wtf   ("Timber.wtf测试成功!!!");


Library 解析


Library 主要为 Timber_ohos 组件提供日志输出的统一接口。


以 Sample 中种植的调试树(DebugTree)为例,当使用 Timber 的静态方法 Timber.e 时,从 MainAbilitySlice 到 Timber.e 打印 log 的地方可以分为 5 个步骤。


整体调用的流程如图 3 所示:

图片

图 3:调用顺序图


下面我们着重介绍树(Tree 类)在 Library 中的实现,核心算法 prepareLog() 内部的逻辑结构这两个方面的内容。


①树(Tree)的实现


Tree 类是一种概念形式的日志操作,具体可分为DebugTree、ReleaseTree、FileTree 等。


而在 Library 内部,Tree 类也实现了一系列方法,以便于对森林中的各类树进行增加、删除、修改等操作。


(1)在 Timber_ohos 组件中维护一个森林对象(FOREST)


森林对象由不同类型的日志树组合而成,并提供对外的接口进行日志的打印。


每种类型的树都可以通过种植操作来把自己添加到森林对象中,或者通过移除操作从森林对象中删除,从而实现该类型日志记录的开启和关闭。
private static final List<Tree> FOREST = new ArrayList<>();

(2)种树


调用 plant() 方法,把 Tree 实例添加进 FOREST 里面 可以种植一棵树,也可以种植多棵树。这里以种一棵树为例。


可以看到,树的种植是在 plant() 静态方法的 synchronized 同步代码块中进行的。


具体流程是先将树对象添加到 FOREST 列表中,然后将日志树保存到 forestAsArray 数组中(将树种植到森林中)。


需要注意的是,如果树为空,则抛出空指针异常的错误;如果开发者手动种植灵魂之树(TREE_OF_SOULS),Timber_ohos 将会抛出非法数据异常。
public static void plant(@NotNull Tree tree{
  if (tree == null) {
    throw new NullPointerException("tree == null");
  }
  if (tree == TREE_OF_SOULS) {
    throw new IllegalArgumentException("Cannot plant Timber into itself.");
  }
  synchronized (FOREST) {
    FOREST.add(tree);
    forestAsArray = FOREST.toArray(new Tree[FOREST.size()]);
  }
}

(3)移除 Tree 实例


同样的,树的移除也是在静态方法 uproot() 中的 synchronized 同步代码块中进行的。


如果没有该树可以移除,则 Timber_ohos 组件将抛出一个非法数据异常;反之,Timber_ohos 组件将根据移除该树后的 FOREST 列表生成新的 forestAsArray 数组。
public static void uproot(@NotNull Tree tree{
  synchronized (FOREST) {
    if (!FOREST.remove(tree)) {
      throw new IllegalArgumentException("Cannot uproot tree which is not planted: " + tree);
    }
    forestAsArray = FOREST.toArray(new Tree[FOREST.size()]);
  }
}

(4)清除森林里面全部的 Tree 实例


移除森林里所有的 Tree 实例,首先使用 FOREST 的 clear() 方法清除所有的 Tree 实例,将会自动生成一个对应的新的 Tree 数组,而 forestAsArray 就是这个数组的引用。因此 forestAsArray 数组被设置为空数组。
public static void uprootAll() {
  synchronized (FOREST) {
    FOREST.clear();
    forestAsArray = TREE_ARRAY_EMPTY;
  }
}

(5) 灵魂之树(TREE_OF_SOULS)


估计很多同学好奇上述 TREE_OF_SOULS。代码实现中,在这里运用的是经典设计模式中的代理模式。


TREE_OF_SOULS 本质上是一个代理对象,森林中所有其他普通的树对象都是被代理对象,代理对象通过 for 循环来依次调用被代理对象的同名方法,从而实现不同类型的日志记录。


如下所示:
private static final Tree TREE_OF_SOULS = new Tree() {
  @Override public void v(String message, Object... args) {
    Tree[] forest = forestAsArray;
    for (Tree tree : forest) {
      tree.v(message, args);
    }
  }


②核心算法(prepareLog)


Timber_ohos 组件的日志记录功能的核心算法在抽象类 Tree 的私有化 prepareLog() 方法中,该方法接收四个参数,如图 4 所示:

图片

图 4:参数表


prepareLog() 中首先判断了打 log 的条件,然后将要打印的 message 信息进行了处理,最后调用了抽象方法 log 进行日志输出。


总体而言 prepareLog()算法流程如下:

  • 获取当前线程的 tag。

  • 当正常信息 message 不为 null 且信息长度为 0 时,这时正常信息 message 为 null。

  • 当正常信息 message 和异常信息 t 都是 null 时,说明没有信息可以记录,方法直接返回。

  • 异常信息 t 通过 getStackTraceString 方法转换为字符串。

  • 正常信息 message 和可选格式化参数 args 通过 formatMessage 方法拼装成一个字符串。

  • 调用抽象方法 log 进行日志记录,这个方法由 Tree 的子类来实现。


项目贡献人:陈丛笑、郑森文朱伟陈美汝蔡志杰。


👇点击关注鸿蒙技术社区👇

鸿蒙技术社区
鸿蒙技术社区
HarmonyOS(鸿蒙)技术社区是由51CTO和华为共同打造的综合性开发和应用技术社区。
147篇原创内容
公众号

了解鸿蒙一手资讯


图片

“阅读原文”了解更多

来源:https://mp.weixin.qq.com/s/Re8PfROPSIzOmGb0BpxSvg
点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信小程序

微信扫一扫体验

立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部