前言

日志是开发者在调试阶段使用最频繁的工具,没有之一。无论是排查线上故障、追踪调用链路,还是验证业务流程,都离不开日志系统。然而在鸿蒙应用开发中,很多开发者对 HiLog 的认识停留在"打印一条日志看看"——对 domain 的分配、tag 的命名规范、isLoggable 检测、日志级别选择等细节缺乏系统性理解。

HarmonyOS NEXT 提供了 @ohos.hilog 模块,它不仅提供了 debug/info/warn/error/fatal 五级日志输出,还提供了 isLoggable 检测能力,让你在调用日志之前就能判断该条日志是否会被输出,避免无效的字符串格式化开销。本文用一个可交互的"HiLog 日志诊断实验室",把 HiLog 的核心用法讲透。

全文含完整可运行代码,适合需要系统化管理应用日志的中级开发者。


一、HiLog 概述与架构

1.1 什么是 HiLog

HiLog 是 HarmonyOS 的系统级日志框架,位于 @kit.PerformanceAnalysisKit。它和 Android 的 Logcat 类似,但设计上更强调"领域隔离"和"按标签过滤"。

HiLog 的三要素:

  • Domain(域):一个 16 位十六进制整数(0x0 ~ 0xFFFF),用于标识日志所属的业务模块。系统域(如 0x0000 ~ 0x00FF)由华为框架使用,应用应使用 0x0100 ~ 0xFFFF 之间的自定义值。
  • Tag(标签):不超过 32 字节的字符串,标识日志的具体来源(如类名、模块名)。
  • Level(级别):DEBUG < INFO < WARN < ERROR < FATAL,共五级,决定了日志的严重程度和是否被输出。

这三要素组合在一起,决定了"这条日志从哪里来、有多重要、能不能被看到"。

1.2 与其他日志系统的对比

维度 HiLog Android Logcat iOS 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 导入方式

import hilog from '@ohos.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 —— 日志预检

const level: hilog.LogLevel = hilog.LogLevel.INFO;
const printable: boolean = hilog.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 枚举

enum LogLevel {
  DEBUG = 3,   // 调试级:详细的开发调试信息
  INFO = 4,    // 信息级:常规运行信息
  WARN = 5,    // 警告级:潜在问题
  ERROR = 6,   // 错误级:可恢复的错误
  FATAL = 7    // 致命级:不可恢复,应用将退出
}

注意 LogLevel 的数值从 3(DEBUG)到 7(FATAL),数值越大严重程度越高。默认情况下,Release 版本的最低输出级别是 INFO(4),DEBUG 日志不会被输出——这也是 isLoggable(domain, tag, LogLevel.DEBUG) 在 Release 包中返回 false 的原因。


在这里插入图片描述
在这里插入图片描述

三、实战:HiLog 日志诊断实验室

3.1 整体设计

实验室页面的功能布局:

  1. Domain 配置区:Hex 输入框 + 4 个预设快捷按钮(0x0001/0x0011/0x0F00/0xFFFF)
  2. Tag 配置区:文本输入框 + 5 个预设快捷按钮(DemoApp/Network/Database/UI/Worker)
  3. isLoggable 检查矩阵:5 级 x 1 行的矩阵,绿色=可通过,灰色=被拦截
  4. 日志级别选择 + 发送:5 级横向按钮 + 消息输入 + 发送按钮
  5. 发送历史:带颜色标签的日志条目列表,实时追加

3.2 状态与导入

import { router } from '@kit.ArkUI';
import hilog from '@ohos.hilog';
import { FontSize, Spacing } from '../common/Constants';

interface LogEntry {
  domain: string;
  tag: string;
  level: string;
  levelColor: string;
  message: string;
}

@Entry
@Component
struct HiLogLabPage {
  @State domain: string = '0x0011';
  @State tag: string = 'DemoApp';
  @State message: string = 'Hello from HiLog lab!';
  @State levels: string[] = ['DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'];
  @State selectedLevel: number = 0;
  @State logHistory: LogEntry[] = [];
  @State loggableMatrix: boolean[] = [false, false, false, false, false];

  private levelColors: string[] = ['#4A90D9', '#52C41A', '#D4880F', '#FF4D4F', '#8C1D1D'];
}

logHistory 是一个 LogEntry[] 数组,存储每条已发送日志的元信息。因为 HiLog 的输出直接进入系统缓冲区,应用层拿不回来,所以我们在应用内维护自己的"发送历史",用不同颜色区分日志级别。

3.3 Domain 与 Tag 配置

private domainToNum(): number {
  const parsed: number = parseInt(this.domain, 16);
  if (isNaN(parsed)) {
    return 0;
  }
  return parsed;
}

parseInt(domainStr, 16) 将用户输入的十六进制字符串转为数字。非法输入时返回 0,hilog.debug(0, ...) 不会被打印(0 是无效域),所以不影响系统。

Domain 和 Tag 各自配了预设按钮:

private presetDomains: string[] = ['0x0001', '0x0011', '0x0F00', '0xFFFF'];
private presetTags: string[] = ['DemoApp', 'Network', 'Database', 'UI', 'Worker'];

0x0011 是华为官方示例中常用的应用域;0xFFFF 是最大可用值;0x0001 和 0x0F00 分别代表系统底层域和中间件域。

3.4 isLoggable 矩阵

private checkLoggable(): void {
  const d: number = this.domainToNum();
  for (let i = 0; i < this.levels.length; i++) {
    this.loggableMatrix[i] = hilog.isLoggable(d, this.tag, i as hilog.LogLevel);
  }
}

这里的关键技巧:i as hilog.LogLevel 将数字索引直接转为 LogLevel 枚举。因为 DEBUG=3, INFO=4, …, FATAL=7,数组索引 0~4 映射到 Level 3~7。这个类型断言利用了 LogLevel 枚举的数值顺序。

5 个级别的检查结果显示为 5 个色块——绿色 #52C41A 表示可通过,灰色 #D9D9D9 表示被拦截。每修改 Domain 或 Tag 都会实时刷新矩阵。

3.5 发送日志

private sendLog(): void {
  const d: number = this.domainToNum();

  if (this.selectedLevel === 0) {
    hilog.debug(d, this.tag, '%{public}s', this.message);
  } else if (this.selectedLevel === 1) {
    hilog.info(d, this.tag, '%{public}s', this.message);
  } else if (this.selectedLevel === 2) {
    hilog.warn(d, this.tag, '%{public}s', this.message);
  } else if (this.selectedLevel === 3) {
    hilog.error(d, this.tag, '%{public}s', this.message);
  } else {
    hilog.fatal(d, this.tag, '%{public}s', this.message);
  }

  // 追加到本地历史
  const entry: 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.levelColor + '20')  // 20 = 12% alpha

  Column() {
    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 -D 0x0100              # 只看网络模块日志
hilog -D 0x1000 -I DEBUG     # 只看用户模块的DEBUG级别
hilog -D 0x0100 -T "Network" # 只看网络模块 + Network标签

5.1 Tag 命名规范

Tag 建议使用模块名或类名,遵循以下约定:

  • 长度控制:不超过 20 字节(留 12 字节缓冲),英文用 PascalCase,中文避免
  • 保持稳定:Tag 是日志过滤的主要维度之一,频繁改名会破坏已有的过滤配置
  • 避免敏感信息:Tag 中不要包含用户 ID、Token 等隐私数据

六、小结

本文以"HiLog 日志诊断实验室"为载体,系统讲解了 HarmonyOS NEXT 的 @ohos.hilog

  • 核心 API:debug/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 的替代品,更是一个需要用心规划的基础设施。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐