HarmonyOS 应用开发之日志体系与调试:Logger 工具类与 hilog 实践详解
日志体系与调试:Logger 工具类与 hilog 实践
一、引言
短视频应用是典型的"问题潜伏型"工程:播放器状态机流转、窗口断点变化、沉浸式切换、多设备差异化分支,任何一个环节出错,现场往往一闪而过。如果没有一套统一、分级、可过滤的日志体系,排查问题只能靠"加打印 → 复现 → 删打印"的原始循环,效率极低。multi-short-video 工程在公共模块中封装了统一的 Logger 工具类,所有特性模块与产品模块复用同一套日志出口,配合 hilog 的级别与隐私过滤能力,形成从"打日志"到"看日志"的完整闭环。
本文先讲 hilog 的能力底座,再拆解工程 Logger.ets 的封装设计,随后以窗口管理、播放器、入口 Ability 的真实调用为例,最后给出日志查看、过滤与调试的实战技巧。

二、hilog 基础与日志级别
HarmonyOS 的日志系统由 @kit.PerformanceAnalysisKit 提供 hilog 接口。其核心概念有三个:domain(业务域,0~0xFFFF 的十六进制整数,用于按域过滤)、tag(标签字符串,标识日志来源)、format(格式化模板与参数列表)。hilog 提供五个级别,自低到高为:
| 级别 | 接口 | 语义 | 典型场景 |
| DEBUG | hilog.debug | 调试细节 | 数据源内容、断点值 |
| INFO | hilog.info | 关键流程 | 窗口创建、全屏设置成功 |
| WARN | hilog.warn | 可恢复异常 | 缓冲不足、重试前提示 |
| ERROR | hilog.error | 功能失败 | 播放失败、窗口接口报错 |
| FATAL | hilog.fatal | 致命错误 | 数据损坏、不可恢复 |
级别不仅是语义约定,还决定输出与过滤行为:系统日志缓冲区按级别分层,DevEco Studio 与命令行均可按级别过滤。工程中大部分业务日志使用 INFO 与 ERROR 两级,调试过程数据用 DEBUG,避免刷屏。
三、Logger 工具类封装
直接在业务代码中调用 hilog.debug(domain, tag, format, args) 存在三个问题:domain/tag 到处重复、格式串不统一、后续想加时间戳或脱敏要改动所有调用点。工程在 common/multishortvideobase/src/main/ets/utils/Logger.ets 中做了统一封装,完整实现如下:
// d:\HarmonyOS\WorkSpace\multi-short-video\common\multishortvideobase\src\main\ets\utils\Logger.ets
import { hilog } from '@kit.PerformanceAnalysisKit';
/**
* Common logger utility class.
*/
class Logger {
private domain: number;
private prefix: string;
private format: string = '%{public}s, %{public}s';
public constructor(prefix: string) {
this.prefix = prefix;
this.domain = 0x0000;
}
public debug(...args: Object[]): void {
hilog.debug(this.domain, this.prefix, this.format, args);
}
public info(...args: Object[]): void {
hilog.info(this.domain, this.prefix, this.format, args);
}
public warn(...args: Object[]): void {
hilog.warn(this.domain, this.prefix, this.format, args);
}
public error(...args: Object[]): void {
hilog.error(this.domain, this.prefix, this.format, args);
}
}
export default new Logger('[multishortvideo]');这段封装的设计要点:
- 统一 TAG:单例以
[multishortvideo]为前缀导出,全工程日志共享同一标签,Log 面板中一条过滤条件即可看全应用日志; - 统一 domain:
0x0000集中在构造器内,避免散落各处; - 统一格式模板:
'%{public}s, %{public}s'把两个参数都标记为 public(详见第五节脱敏说明),调用方只需传内容; - API 简洁:
debug/info/warn/error与 hilog 同名,学习成本为零。
调用方通常再叠加一层"模块 TAG"前缀,例如窗口工具中 const TAG = 'WindowUtil',然后 Logger.error(TAG, ...),这样既能按应用过滤,又能按模块定位。
四、Logger 在工程中的真实使用
工程中 Logger 覆盖了三个典型场景。窗口管理(common/multishortvideobase/.../utils/WindowUtil.ets)在获取主窗口、注册监听、设置沉浸式的关键节点打日志,并统一捕获异步异常:
setFullScreen(isFullScreen: boolean): void {
try {
this.mainWindow!.setWindowLayoutFullScreen(isFullScreen)
.then(() => {
Logger.info(TAG, `Succeeded in setting full-screen mode.`);
this.mainWindowInfo.isFullScreen = isFullScreen;
})
.catch((err: BusinessError) => {
Logger.error(TAG, `Failed to set full-screen mode. Code: ${err.code}, message: ${err.message}`);
});
} catch (error) {
let err = error as BusinessError;
Logger.error(TAG, `setWindowLayoutFullScreen sync error. Code: ${err.code}, message: ${err.message}`);
}
}播放器(features/multishortvideoadaptivevideo/.../view/AdaptiveAVPlayer.ets)在创建、prepare、play、pause、seek、release 全生命周期打日志,异常分支一律带 err.code 与 err.message:
avPlayer.on('error', (err: BusinessError) => {
Logger.error(TAG, `Invoke prepare failed, code is ${err.code}, message is ${err.message}`);
});
Logger.info(TAG, `changePortraitVideo immersionInfo = ${JSON.stringify(immersionInfo)}`);入口 Ability(products/default/.../defaultability/MultiShortVideoDefaultAbility.ets)则暴露了两类用法的对比——工程模板自带的裸 hilog(hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate'))与业务代码采用的 Logger(Logger.info('Succeeded in setting the system bar properties.'))。裸 hilog 的 tag testTag 与工程的 [multishortvideo] 不统一,过滤时容易遗漏,这也是封装 Logger 的初衷之一:入口层也应收口到统一日志出口。
五、日志脱敏与分级
日志中常包含设备信息、业务数据甚至用户输入,直接打印存在隐私风险。hilog 的格式化参数支持两种隐私标识:%{public}s 表示该参数明文输出,%{private}s 表示脱敏输出(真机上显示为 {private})。工程 Logger 的默认模板 '%{public}s, %{public}s' 把参数统一标记为 public,适用于 tag 类内部标识;一旦需要记录用户评论、账号等敏感字段,应改用显式 %{private}s 模板,或只记录长度、哈希等非敏感派生值。
分级方面建议遵循"INFO 记流程、ERROR 记失败、DEBUG 记细节":正常路径的进入/成功用 INFO,异常分支用 ERROR 并携带错误码与消息,需要追踪的具体数值(如窗口尺寸、断点枚举)用 DEBUG。避免用 ERROR 打印"可预期的分支"(如用户取消操作),否则 ERROR 级别会失去信噪比,真正的问题反被淹没。
日志的性能开销同样值得关注。hilog 在调试态会全量输出,但发布版应裁剪日志级别:建议把 Logger.debug 级别的输出在 release 包中关闭,仅保留 INFO 及以上,避免高频日志(如播放进度、窗口尺寸回调)在线上产生额外 IO 与功耗。工程可在 Logger 封装中增加一个编译期开关(如 isDebug 常量),在 hvigor 的 release 构建中注入 false,让 debug 调用在构建期被裁剪,而不是在运行期做判断——这与 ArkTS 强类型约束下"编译期解决"的理念一脉相承。另外,打印大对象(如整个数据源数组)会显著放大日志体积,建议打印摘要字段(长度、首元素、关键状态),需要全量时再临时放开。
六、日志查看与过滤
日志输出后,两种主流查看方式:
- DevEco Studio Log 面板:真机或模拟器运行后,Log 面板按"级别 + 关键字"实时过滤,可直接按 tag 输入
multishortvideo过滤全工程日志,再叠加WindowUtil、AdaptiveAVPlayer等模块关键字缩小范围;日志默认按时间排序,可配合"按进程/按域"分组。 - 命令行 hilog:连接设备后执行
hdc shell hilog,可用管道与 grep 组合过滤,例如hdc shell hilog | findstr multishortvideo,适合在自动化脚本或设备端抓取长时日志;hilog 还支持按 domain 范围(hilog -D 0x0000)、按级别(-L)等参数精确裁剪。
排查技巧上,工程经验是"复现一次、抓全三样":完整日志(确定错误码与堆栈)、现场状态(窗口尺寸、断点、播放器 state)、操作序列(用户路径)。三者交叉比对,通常能把问题收敛到某个 Logger.error 附近。
七、调试技巧:断点、ArkTS 调试器与状态面板
日志之外,DevEco Studio 的调试能力与日志互补:
- 断点调试:在 ArkTS 源码行号左侧打点,运行到断点后查看变量、表达式与调用栈;配合"条件断点"(如
curIndex === 2)可在循环或列表场景精准命中。 - ArkTS 调试器:支持单步、步入/步出、求值表达式,适合追踪
@Monitor触发链路与状态变化顺序。 - 状态面板(UI Inspector):运行中通过 ArkUI Inspector 查看组件树与属性、
@State/@Trace实时值,定位"状态已变但 UI 未刷新"类问题——这类问题日志往往无迹可寻,状态面板一锤定音。 - V2 状态观察:对
@Trace字段在@Monitor回调中临时加Logger.debug,可确认状态变化是否被监听、监听是否晚于预期。
Logger.debug 的采样或缩短抓取窗口。八、常见日志问题排查案例
把前文理论落到真实问题,三个案例最具代表性。案例一:播放器反复起播失败。现象是视频页面偶发黑屏,AdaptiveAVPlayer 的 Logger.error 记录 Invoke prepare failed, code is ...。排查过程:先在 Log 面板按 multishortvideo + AdaptiveAVPlayer 过滤,确认错误集中在 avPlayer.prepare() 被调用时播放器仍未进入 initialized 状态——根源是快速滑动列表时同一个播放器实例被并发控制,修复方式是保证状态机流转严格串行(先 reset 再 prepare),并在每次状态变更处补 Logger.info 记录当前 state,让"状态顺序"有日志可查。
案例二:窗口断点不刷新。现象是折叠屏展开后布局未切换。日志中 onWindowSizeChange 与断点获取均无异常输出,但 widthBp 没有变化——进一步在 WindowUtil.updateWindowInfo 的监听注册处补打日志后确认,监听注册发生在 setUIContext 之前,uiContext 为 undefined 导致断点取不到。修复调用顺序后问题消失。这个案例说明:日志要打在"能力调用的边界",而不是只打在成功/失败的结果上,否则中间态丢失,问题无法定位。
案例三:日志"看着像没走"。现象是某分支明明执行了,Log 面板却搜不到。原因通常是过滤条件过窄(domain/tag/级别任一不匹配)或真机日志缓冲被新日志刷掉。排查时先放开过滤条件确认日志确实存在,再逐步收紧;必要时在 onWindowStageCreate 这类早期入口打 Logger.info 确认进程生命周期正常。三个案例的共同经验是:日志点位的设计比日志量更重要——在状态机的每一次迁移、窗口注册的每一个边界、异常抛出的每一个位置打点,才能让问题"看见即定位"。
九、总结与最佳实践
日志体系与调试沉淀为五条最佳实践:
- 统一出口:所有模块复用
common/.../utils/Logger.ets的单例,统一 domain、tag([multishortvideo])与格式模板,Log 面板一条过滤条件看全应用。 - 分级克制:INFO 记流程、ERROR 记失败(必带
err.code/err.message)、DEBUG 记细节;可用预期的分支不要用 ERROR。 - 敏感字段脱敏:默认模板
%{public}s用于内部标识,用户数据等敏感字段改用%{private}s或只记派生值。 - 模块 TAG 前缀:在调用方叠加
const TAG = 'WindowUtil'之类模块标识,实现"应用级过滤 + 模块级定位"两级收敛。 - 日志与调试器互补:问题不明时优先看日志收敛范围,再上断点与状态面板定位状态/UI 不一致问题。
更多推荐
所有评论(0)