基于安卓平台的日志工具组件 Timber, 实现鸿蒙的功能化迁移和重构,代码已经开源,欢迎各位开发者提出宝贵意见。
https://gitee.com/isrc_ohos/timber_ohos
Timber_ohos 是一个带有小型可扩展 API 的日志工具组件,它可以给开发者提供统一的 API 接口,来记录不同类型的日志,帮助开发者管理不同类型的 log。
同时,Timber_ohos 是项目开发时的 log 开关,通过此开关控制 log 的打印与关闭,从而形成不同的软件版本。该组件功能丰富且使用简单高效,可以被广泛应用于软件项目开发中。
组件效果展示
①测试界面
图 1:测试界面 UI 图
②Log 打印
图 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 子类实例)
Timber.plant(new Timber.DebugTree(0x001f00));
(2)创建整体的显示布局
在 XML 文件中创建一个 DirectionalLayout 作为整体显示布局,宽度和高度都跟随父控件变化而调整。
创建两个组件,分别是 Text 组件和 Button 组件,用于控制组件效果显示。
<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() 方法进行设置,才能生效并成功显示。
super.setUIContent(ResourceTable.Layout_ability_main);//设置整体显示布局
findComponentById(ResourceTable.Id_btn1).setClickedListener(new Component.ClickedListener() {
...//按钮的点击事件
}
(4)使用 Tree 实例
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:调用顺序图
下面我们着重介绍树(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 数组中(将树种植到森林中)。
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 同步代码块中进行的。
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 实例
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)
图 4:参数表
prepareLog() 中首先判断了打 log 的条件,然后将要打印的 message 信息进行了处理,最后调用了抽象方法 log 进行日志输出。
总体而言 prepareLog()算法流程如下:
获取当前线程的 tag。
当正常信息 message 不为 null 且信息长度为 0 时,这时正常信息 message 为 null。
当正常信息 message 和异常信息 t 都是 null 时,说明没有信息可以记录,方法直接返回。
异常信息 t 通过 getStackTraceString 方法转换为字符串。
正常信息 message 和可选格式化参数 args 通过 formatMessage 方法拼装成一个字符串。
调用抽象方法 log 进行日志记录,这个方法由 Tree 的子类来实现。
项目贡献人:陈丛笑、郑森文、朱伟、陈美汝、蔡志杰。
👇点击关注鸿蒙技术社区👇
了解鸿蒙一手资讯
点“阅读原文”了解更多
发表评论 取消回复