HarmonyOS 应用开发《掌上英语》第40篇:Logger 日志系统——从 console.log 到分级日志
Logger 日志系统——从 console.log 到分级日志
一、为什么需要日志系统
在 HarmonyOS 应用开发中,日志是排查问题、了解运行时状态的核心手段。新手开发者往往习惯使用console.log()输出调试信息,这在简单场景下确实够用。但随着项目规模增长——多模块协作、多设备适配、线上问题排查——console.log 的局限性就会暴露无遗:
- 缺乏分级:无法区分信息、警告、错误,日志淹没在大量输出中
- 缺少 TAG:难以定位日志来自哪个模块或页面
- 性能开销:生产环境无法按需关闭,影响应用性能
- 格式不统一:不同开发者的日志风格各异,阅读困难
本项目的 Logger 类正是为解决这些问题而设计的。它基于 HarmonyOS 的@kit.PerformanceAnalysisKit中的hilogAPI 构建,提供了简洁的分级日志能力。
二、Logger 源码分析
Logger 类的完整实现位于commons/commonLib/src/main/ets/utils/Logger.ets:
import{hilog}from'@kit.PerformanceAnalysisKit';exportclassLogger{privatestaticdomain:number=0xff00;privatestaticprefix:string='HotelTemplate';privatestaticformat:string='%{public}s, %{public}s';publicstaticdebug(...args:string[]):void{hilog.debug(Logger.domain,Logger.prefix,Logger.format,args);}publicstaticinfo(...args:string[]):void{hilog.info(Logger.domain,Logger.prefix,Logger.format,args);}publicstaticwarn(...args:string[]):void{hilog.warn(Logger.domain,Logger.prefix,Logger.format,args);}publicstaticerror(...args:string[]):void{hilog.error(Logger.domain,Logger.prefix,Logger.format,args);}}2.1 核心设计解析
domain(域):0xff00是一个自定义的日志域标识。在 hilog 系统中,domain 用于划分日志的所属子系统,取值范围 0x0~0xFFFF。我们使用 0xff00 作为应用私有域,避免与系统日志冲突。
prefix(前缀):固定为'HotelTemplate',这是该应用的日志标识,在抓取日志时可以快速过滤出属于本应用的日志行。
format(格式):'%{public}s, %{public}s'定义了日志消息的格式化模板。%{public}s表示这是一个可公开的字符串参数。hilog 提供了%{public}s和%{private}s两种格式,前者在日志中明文显示,后者会被脱敏处理(显示为<private>)。在调试阶段使用 public 便于定位问题,发布前应检查敏感信息是否使用了 private。
2.2 分级体系
Logger 提供了四个静态方法,对应四种日志级别:
| 级别 | 方法 | 用途 | 生产环境建议 |
|---|---|---|---|
| DEBUG | Logger.debug() | 详细调试信息 | 关闭 |
| INFO | Logger.info() | 一般运行状态 | 开启 |
| WARN | Logger.warn() | 潜在问题警告 | 开启 |
| ERROR | Logger.error() | 错误和异常 | 开启 |
在 hilog 底层,每个级别都有独立的缓冲区。ERROR 级别的日志会被优先保留,不会被 INFO 日志覆盖。这确保了关键错误信息不会丢失。
三、使用规范
3.1 TAG 规范
在本项目的所有文件中,统一使用const TAG常量作为日志标签:
// 页面级别constTAG='[HomePage]';Logger.info(TAG,'页面加载完成');// 管理器级别Logger.error('NewWordManager',`添加生词失败:${JSON.stringify(e)}`);// 工具类级别constTAG='[ContextUtils]';Logger.error(TAG,'showToast fail, error: '+JSON.stringify(error));TAG 命名的建议:
- 页面组件使用
[页面名],如[HomePage]、[CourseHomePage] - 管理器使用类名,如
NewWordManager、StatisticsManager - 工具类使用
[类名],如[ContextUtils]、[AudioPlayer]
3.2 错误日志的 JSON 序列化
在 catch 块中捕获异常时,ArkTS 的错误对象无法直接进行字符串拼接。正确的做法是使用JSON.stringify(e)将错误转为可读的字符串:
try{// 可能出错的代码}catch(e){Logger.error('MyComponent',`操作失败:${JSON.stringify(e)}`);}四、项目中各层的日志实践
4.1 Manager 层日志
Manager 层是业务逻辑的核心,日志需要记录关键操作的入口和结果:
Logger.info('NewWordManager',`添加生词成功:${topicItem.title}`);Logger.error('NewWordManager',`添加生词失败:${JSON.stringify(e)}`);LearningPlanManager 中同样遵循此模式,每个 public 方法都有对应的日志记录:
Logger.info('LearningPlanManager','保存学习计划成功');Logger.error('LearningPlanManager',`更新连续学习天数失败:${JSON.stringify(e)}`);4.2 工具类日志
工具类如 AudioPlayer 在生命周期方法中记录详细日志:
Logger.info('AudioPlayer','开始播放: '+url);Logger.warn('AudioPlayer','音频URL为空');Logger.error('AudioPlayer',`播放失败:${JSON.stringify(e)}`);4.3 页面组件日志
页面组件在 aboutToAppear 和关键交互处记录日志:
Logger.info(TAG,'aboutToAppear');Logger.info(JSON.stringify(this.sourceRouterModel));五、与 console.log 的对比
| 对比维度 | console.log | Logger (hilog) |
|---|---|---|
| 分级能力 | 无 | debug/info/warn/error |
| 标签过滤 | 不支持 | domain + prefix + TAG |
| 生产可控 | 不可控 | 可按级别过滤 |
| 隐私保护 | 无 | %{private}s 脱敏 |
| 缓冲区管理 | 无 | 分级独立缓冲区 |
六、最佳实践总结
- 始终使用 TAG:每个文件定义唯一的 TAG,便于 grep 过滤
- 分层级输出:避免把所有信息都用 Logger.info 输出,合理使用 warn 和 error
- JSON 序列化异常:catch 块中始终使用
JSON.stringify(e)记录错误 - 避免敏感信息:hilog 默认会脱敏 private 数据,但 print 格式需手动指定
- 统一前缀:prefix 用于区分不同应用,便于日志聚合系统过滤
Logger 类的设计虽然简洁,但它在项目中扮演着"眼睛"的角色。正是通过它,开发者才能在无调试器的生产环境中,快速定位问题的根源。从 console.log 到分级日志,是每一个工程化项目必备的基础能力提升。
