行业资讯
📅 2026/7/16 11:10:06
鸿蒙新特性实战:@ohos.hilog 打造日志诊断实验室
前言日志是开发者在调试阶段使用最频繁的工具没有之一。无论是排查线上故障、追踪调用链路还是验证业务流程都离不开日志系统。然而在鸿蒙应用开发中很多开发者对 HiLog 的认识停留在打印一条日志看看——对 domain 的分配、tag 的命名规范、isLoggable 检测、日志级别选择等细节缺乏系统性理解。HarmonyOS NEXT 提供了ohos.hilog模块它不仅提供了 debug/info/warn/error/fatal 五级日志输出还提供了isLoggable检测能力让你在调用日志之前就能判断该条日志是否会被输出避免无效的字符串格式化开销。本文用一个可交互的HiLog 日志诊断实验室把 HiLog 的核心用法讲透。全文含完整可运行代码适合需要系统化管理应用日志的中级开发者。一、HiLog 概述与架构1.1 什么是 HiLogHiLog 是 HarmonyOS 的系统级日志框架位于kit.PerformanceAnalysisKit。它和 Android 的 Logcat 类似但设计上更强调领域隔离和按标签过滤。HiLog 的三要素Domain域一个 16 位十六进制整数0x0 ~ 0xFFFF用于标识日志所属的业务模块。系统域如 0x0000 ~ 0x00FF由华为框架使用应用应使用 0x0100 ~ 0xFFFF 之间的自定义值。Tag标签不超过 32 字节的字符串标识日志的具体来源如类名、模块名。Level级别DEBUG INFO WARN ERROR FATAL共五级决定了日志的严重程度和是否被输出。这三要素组合在一起决定了这条日志从哪里来、有多重要、能不能被看到。1.2 与其他日志系统的对比维度HiLogAndroid LogcatiOS NSLog/os_log领域隔离domain (0x0~0xFFFF)无标准域概念subsystem category级别过滤五级 isLoggable 预检查七级 (V/D/I/W/E/F/S)四级 (Default/Info/Debug/Error/Fault)日志隐私%{public}s / %{private}s 强制标注无无命令行工具hilog 命令logcat 命令log 命令HiLog 最独特的设计是强制隐私标注——format 字符串中的参数必须用%{public}或%{private}修饰否则编译时不会报错但运行时日志会被隐藏。这是 HarmonyOS 在隐私保护方面的重要设计。二、核心 API 详解2.1 导入方式importhilogfromohos.hilog;注意这是default import不是从kit.ArkTS的命名导入。HiLog 属于kit.PerformanceAnalysisKit但直接 import 自ohos.hilog即可。2.2 五级日志输出hilog.debug(domain,tag,format,...args);// 调试信息hilog.info(domain,tag,format,...args);// 一般信息hilog.warn(domain,tag,format,...args);// 警告hilog.error(domain,tag,format,...args);// 错误hilog.fatal(domain,tag,format,...args);// 致命错误(FATAL会导致应用退出)每个函数的参数签名完全相同domain: number— 十六进制域值0x0 ~ 0xFFFF超出范围日志不会被打印tag: string— 日志标签最长 32 字节超出部分会被截断format: string— 格式化字符串支持%{public}s、%{private}s、%{public}d等占位符...args: any[]— 可变参数按顺序替换 format 中的占位符2.3 isLoggable —— 日志预检constlevel:hilog.LogLevelhilog.LogLevel.INFO;constprintable:booleanhilog.isLoggable(0x0011,DemoApp,level);isLoggable接受 domain、tag、level 三个参数返回 boolean表示当前系统设置下这条日志是否会被实际输出。这是一个纯判断函数不会产生任何日志输出性能开销极小。它的核心价值在于避免无效的字符串操作。考虑以下场景// 不推荐即使日志不会被输出JSON.stringify 也会执行hilog.debug(0x0011,Data,%{public}s,JSON.stringify(largeObject));// 推荐先检查再输出if(hilog.isLoggable(0x0011,Data,hilog.LogLevel.DEBUG)){hilog.debug(0x0011,Data,%{public}s,JSON.stringify(largeObject));}对于频繁调用的调试日志中包含复杂数据序列化的情况isLoggable预检可以显著减少性能浪费。2.4 LogLevel 枚举enumLogLevel{DEBUG3,// 调试级详细的开发调试信息INFO4,// 信息级常规运行信息WARN5,// 警告级潜在问题ERROR6,// 错误级可恢复的错误FATAL7// 致命级不可恢复应用将退出}注意 LogLevel 的数值从 3DEBUG到 7FATAL数值越大严重程度越高。默认情况下Release 版本的最低输出级别是 INFO4DEBUG 日志不会被输出——这也是isLoggable(domain, tag, LogLevel.DEBUG)在 Release 包中返回 false 的原因。三、实战HiLog 日志诊断实验室3.1 整体设计实验室页面的功能布局Domain 配置区Hex 输入框 4 个预设快捷按钮0x0001/0x0011/0x0F00/0xFFFFTag 配置区文本输入框 5 个预设快捷按钮DemoApp/Network/Database/UI/WorkerisLoggable 检查矩阵5 级 x 1 行的矩阵绿色可通过灰色被拦截日志级别选择 发送5 级横向按钮 消息输入 发送按钮发送历史带颜色标签的日志条目列表实时追加3.2 状态与导入import{router}fromkit.ArkUI;importhilogfromohos.hilog;import{FontSize,Spacing}from../common/Constants;interfaceLogEntry{domain:string;tag:string;level:string;levelColor:string;message:string;}EntryComponentstruct HiLogLabPage{Statedomain:string0x0011;Statetag:stringDemoApp;Statemessage:stringHello from HiLog lab!;Statelevels:string[][DEBUG,INFO,WARN,ERROR,FATAL];StateselectedLevel:number0;StatelogHistory:LogEntry[][];StateloggableMatrix:boolean[][false,false,false,false,false];privatelevelColors:string[][#4A90D9,#52C41A,#D4880F,#FF4D4F,#8C1D1D];}logHistory是一个LogEntry[]数组存储每条已发送日志的元信息。因为 HiLog 的输出直接进入系统缓冲区应用层拿不回来所以我们在应用内维护自己的发送历史用不同颜色区分日志级别。3.3 Domain 与 Tag 配置privatedomainToNum():number{constparsed:numberparseInt(this.domain,16);if(isNaN(parsed)){return0;}returnparsed;}parseInt(domainStr, 16)将用户输入的十六进制字符串转为数字。非法输入时返回 0hilog.debug(0, ...)不会被打印0 是无效域所以不影响系统。Domain 和 Tag 各自配了预设按钮privatepresetDomains:string[][0x0001,0x0011,0x0F00,0xFFFF];privatepresetTags:string[][DemoApp,Network,Database,UI,Worker];0x0011 是华为官方示例中常用的应用域0xFFFF 是最大可用值0x0001 和 0x0F00 分别代表系统底层域和中间件域。3.4 isLoggable 矩阵privatecheckLoggable():void{constd:numberthis.domainToNum();for(leti0;ithis.levels.length;i){this.loggableMatrix[i]hilog.isLoggable(d,this.tag,iashilog.LogLevel);}}这里的关键技巧i as hilog.LogLevel将数字索引直接转为 LogLevel 枚举。因为 DEBUG3, INFO4, …, FATAL7数组索引 0~4 映射到 Level 3~7。这个类型断言利用了LogLevel枚举的数值顺序。5 个级别的检查结果显示为 5 个色块——绿色#52C41A表示可通过灰色#D9D9D9表示被拦截。每修改 Domain 或 Tag 都会实时刷新矩阵。3.5 发送日志privatesendLog():void{constd:numberthis.domainToNum();if(this.selectedLevel0){hilog.debug(d,this.tag,%{public}s,this.message);}elseif(this.selectedLevel1){hilog.info(d,this.tag,%{public}s,this.message);}elseif(this.selectedLevel2){hilog.warn(d,this.tag,%{public}s,this.message);}elseif(this.selectedLevel3){hilog.error(d,this.tag,%{public}s,this.message);}else{hilog.fatal(d,this.tag,%{public}s,this.message);}// 追加到本地历史constentry:LogEntry{domain:this.domain,tag:this.tag,level:this.levels[this.selectedLevel],levelColor:this.levelColors[this.selectedLevel],message:this.message};this.logHistory[entry].concat(this.logHistory).slice(0,50);this.checkLoggable();}注意 ArkTS 严格模式下不能用动态属性名调用函数如hilog[levelName](...)所以需要显式的 if/else 分支。发送后立即追加到logHistory头部最新的排最前限制 50 条避免内存溢出。同时触发checkLoggable()刷新矩阵——因为某些级别的日志设置可能受运行时状态影响。3.6 发送历史展示每条历史记录用色标 层级标签 元信息 内容的形式展示Row(){Column(){Text(entry.level).fontSize(10).fontColor(entry.levelColor).fontWeight(FontWeight.Bold)}.width(48).height(24).borderRadius(4).backgroundColor(entry.levelColor20)// 20 12% alphaColumn(){Text([entry.domain] entry.tag).fontSize(11).fontColor(#90A4AE)Text(entry.message).fontSize(13).fontColor(#263238)}.layoutWeight(1)}entry.levelColor 20将颜色字符串拼接20得到约 12% 透明度的同色背景——这是一种在 ArkTS 中无需计算 RGBA 即可实现浅色标签背景的技巧。四、几个实战要点与坑4.1 Domain 为 0 时日志不输出当domain参数为 0 时HiLog 不会打印该日志。如果用户清空了 Domain 输入框或在 parseInt 失败时返回了 0日志将静默丢失。建议始终用非零的合法 domain 值并在domainToNum()中做校验。4.2 FATAL 会导致应用退出hilog.fatal()输出日志后系统会终止应用进程。这和 Java 的System.exit()类似——所以 FATAL 只在真正不可恢复的场景下使用。在我们的实验室中点击 FATAL 按钮时要确保这是你预期的行为。4.3 隐私标注必须使用HiLog 的 format 字符串中每个参数都必须用%{public}或%{private}标注。不标注的参数在 Release 版本中会被替换为private。常见格式hilog.info(0x0011,Tag,user: %{public}s, age: %{public}d,name,age);hilog.info(0x0011,Tag,token: %{private}s,token);// 隐私字段用 private%{public}s表示该字符串参数可以在 Release 日志中暴露%{private}s表示该参数只应在 Debug 日志中可见Release 中会被private替换。4.4 isLoggable 的性能价值在一个高频日志点如列表滚动、手势跟踪中即使日志不输出hilog.debug()调用本身也会执行参数求值和字符串格式化。对于复杂参数如JSON.stringify(someObj)这种开销不可忽略。// 反例每秒 60 帧的滚动回调中JSON.stringify 一直在执行onScroll((offset){hilog.debug(0x0011,Scroll,data: %{public}s,JSON.stringify(state));});// 正例先检查避免无效序列化onScroll((offset){if(hilog.isLoggable(0x0011,Scroll,hilog.LogLevel.DEBUG)){hilog.debug(0x0011,Scroll,data: %{public}s,JSON.stringify(state));}});isLoggable本质上是读一个标志位几乎没有性能开销。4.5 LogLevel 不是从 0 开始LogLevel.DEBUG 3不是 0 也不是 1。如果错误地以为0 DEBUG, 1 INFO, ...并在代码中硬编码数字会导致日志级别判断错误。五、Domain 分配最佳实践在一个中大型应用中如何分配 domain 值直接影响到日志检索效率。推荐的分层方案Domain 范围分配建议示例0x0000 ~ 0x00FF系统保留勿用—0x0100 ~ 0x0FFF应用级公共模块网络(0x0100)、数据库(0x0101)、缓存(0x0102)0x1000 ~ 0x1FFF业务模块 A用户(0x1000)、订单(0x1001)、支付(0x1002)0x2000 ~ 0x2FFF业务模块 B商品(0x2000)、库存(0x2001)、物流(0x2002)0xF000 ~ 0xFFFF开发调试专用临时调试(0xFF00)、原型验证(0xFFF0)每个模块分配唯一的 domain 后使用hilog命令行工具可以按 domain 精确过滤hilog-D0x0100# 只看网络模块日志hilog-D0x1000-IDEBUG# 只看用户模块的DEBUG级别hilog-D0x0100-TNetwork# 只看网络模块 Network标签5.1 Tag 命名规范Tag 建议使用模块名或类名遵循以下约定长度控制不超过 20 字节留 12 字节缓冲英文用 PascalCase中文避免保持稳定Tag 是日志过滤的主要维度之一频繁改名会破坏已有的过滤配置避免敏感信息Tag 中不要包含用户 ID、Token 等隐私数据六、小结本文以HiLog 日志诊断实验室为载体系统讲解了 HarmonyOS NEXT 的ohos.hilog核心 APIdebug/info/warn/error/fatal 五级日志输出 isLoggable 预检 LogLevel 枚举三要素domain业务隔离、tag来源定位、level严重程度三者组合构成日志的唯一标识isLoggable 的价值避免无效的字符串格式化开销在高频日志场景中尤为关键隐私标注%{public}vs%{private}的强制选择体现了 HarmonyOS 的隐私设计哲学Domain 分配方案从 0x0100 到 0xFFFF 的分层规划配合 hilog 命令行工具实现精准过滤工程注意事项Domain 不能为 0、FATAL 会终止进程、LogLevel 从 3 开始日志系统是开发者的眼睛。用好 HiLog意味着在问题排查时能快速定位、在性能优化时能精确度量、在线上监控时能有效告警。它不仅是一个print的替代品更是一个需要用心规划的基础设施。