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

一、前置思考

1.1 日志是"系统黑盒里的探照灯"

系统出问题时,第一件事就是看日志:崩溃了为什么崩?卡顿了卡在哪?数据错了错在哪?日志系统的质量直接决定排障效率

但日志不是"随便打打":

❌ 日志全打: 每秒几万条 → 性能下降 + 存储爆炸
❌ 日志不打: 出问题没线索 → 无从排查
❌ 日志裸奔: 敏感信息明文 → 安全漏洞
❌ 日志格式乱: 无法检索 → 等于没打

1.2 鸿蒙 HiLog 的定位

HiLog(Harmony Log)是鸿蒙统一的日志系统,核心特性:

① 分级过滤: DEBUG/INFO/WARN/ERROR/FATAL
② 环形缓冲: 内核环形缓冲区,不阻塞业务
③ 域+标签: 按 域(0x0001) + Tag 分类检索
④ 动态开关: 运行时可开关日志级别
⑤ 低开销: 异步写入,不影响主流程

1.3 本文价值

剖析 HiLog 内核实现(环形缓冲区)、日志分级过滤、动态日志开关、日志性能影响,以及 ftrace/动态调试等内核调试手段。

二、核心原理

2.1 HiLog 架构

┌─────────────────────────────────────────┐
│ 应用/服务 (ArkTS/C/C++)                 │
│  HiLog 接口 → 格式化 → 提交             │
├─────────────────────────────────────────┤
│ 日志服务 (hilogd)                       │
│  ┌───────────────────────────────────┐  │
│  │ 内存环形缓冲区 (Ring Buffer)       │  │
│  │  最新 N 条日志循环覆盖              │  │
│  └───────────────────────────────────┘  │
│  → 过滤 (等级/域/标签) → 持久化/输出    │
├─────────────────────────────────────────┤
│ 读取端: hilog 命令 / DevEco Log 窗口     │
└─────────────────────────────────────────┘

2.2 环形缓冲区原理

环形缓冲区 (Ring Buffer):
  ┌────┬────┬────┬────┬────┬────┬────┬────┐
  │ L1 │ L2 │ L3 │ L4 │ L5 │    │    │    │
  └────┴────┴────┴────┴────┴────┴────┴────┘
  写指针 →  ──→    (写满后回到开头覆盖)
  读指针 →  ──→    (读最新日志)

特点:
  ① 固定大小(如 1MB),内存有界
  ② 写日志从不阻塞(满了覆盖最旧)
  ③ O(1) 读写,性能开销极小
  ④ 断电丢失(如需持久化 → 另写文件)

2.3 日志等级过滤

日志等级(从低到高):
  DEBUG < INFO < WARN < ERROR < FATAL

过滤规则:
  设置"最低输出等级" = ERROR
  → 只输出 ERROR/FATAL,丢弃 DEBUG/INFO/WARN

收益:
  线上模式: 只留 ERROR → 日志量减少 90%+
  开发模式: 全量输出 → 排障信息齐全

动态调节:
  运行时修改过滤等级 → 无需重启
  问题复现时临时开 DEBUG → 排查完关掉

三、源码/API 深度解析

3.1 HiLog 环形缓冲区实现(内核)

// hilog 内核环形缓冲区核心
struct hiview_log_buffer {
    // 环形缓冲读写指针
    atomic_t write_ptr;
    atomic_t read_ptr;
    // 缓冲数据区
    char *data;
    size_t size;         // 缓冲总大小
};

// 写入日志(核心: 不阻塞 + 覆盖最旧)
static ssize_t log_buffer_write(struct hiview_log_buffer *buf,
                                const char *msg, size_t len)
{
    // 1. 取当前写位置
    size_t pos = atomic_read(&buf->write_ptr);
    // 2. 计算新写位置(环形回绕)
    size_t new_pos = (pos + len) % buf->size;
    // 3. 拷贝数据(不做磁盘 IO,极快)
    memcpy(buf->data + pos, msg, len);
    // 4. 更新写指针(原子操作,多线程安全)
    atomic_set(&buf->write_ptr, new_pos);
    return len;   // 立即返回,不阻塞调用者
}

3.2 HiLog 使用(ArkTS)

import { hilog } from '@kit.PerformanceAnalysisKit';

// 1. 定义域和标签
const DOMAIN: number = 0x0001;          // 应用域
const TAG: string = 'MyApp';

// 2. 分级打日志
hilog.debug(DOMAIN, TAG, '调试信息: %{public}s', 'detail');
hilog.info(DOMAIN, TAG, '业务日志: 用户 %{public}d 登录', userId);
hilog.warn(DOMAIN, TAG, '警告: 缓存命中率下降');
hilog.error(DOMAIN, TAG, '错误: 网络请求失败 code=%{public}d', code);

// 3. 隐私保护: %{private} 隐藏敏感数据
hilog.info(DOMAIN, TAG, '用户手机号: %{private}s', phoneNumber);
// 输出: 用户手机号: ***

// 4. 断言
hilog.fatal(DOMAIN, TAG, '致命错误: %{public}s', '系统状态不一致');

3.3 动态日志开关

// 运行期动态控制日志等级
class LogManager {
  private static level: hilog.LogLevel = hilog.LogLevel.INFO;

  // 设置全局日志等级(可在调试页/远程下发)
  static setLevel(level: hilog.LogLevel): void {
    LogManager.level = level;
    hilog.setLogLevel(level);
    LoggerUtil.info(TAG, '日志等级已切换: ' + level);
  }

  // 按模块开关(特性开关)
  static setModuleEnabled(module: string, enabled: boolean): void {
    // 模块级过滤: 只开问题模块的 DEBUG
    moduleFlags.set(module, enabled);
  }

  static shouldLog(module: string, level: hilog.LogLevel): boolean {
    return moduleFlags.get(module) === true &&
           level >= LogManager.level;
  }
}

// 使用: 排查时开启特定模块的详细日志
LogManager.setLevel(hilog.LogLevel.DEBUG);
LogManager.setModuleEnabled('network', true);
// 排查完恢复
LogManager.setLevel(hilog.LogLevel.INFO);
LogManager.setModuleEnabled('network', false);

3.4 内核动态调试(ftrace/dynamic debug)

# 1. 内核动态调试 (dynamic_debug)
# 动态开启某个函数的日志(无需重编译内核)
echo 'file drivers/sensor/sensor_core.c +p' > /sys/kernel/debug/dynamic_debug/control

# 2. ftrace 跟踪内核调用
cd /sys/kernel/debug/tracing
echo function > current_tracer          # 函数跟踪
echo sensor_* > set_ftrace_filter       # 只跟踪传感器相关
echo 1 > tracing_on                     # 开始跟踪
# ... 复现问题 ...
echo 0 > tracing_on
cat trace                                # 查看调用链

# 3. 内核日志等级
# 控制台日志等级 (1: emerg ~ 7: debug)
echo "4 4 1 7" > /proc/sys/kernel/printk

四、企业级实战落地

4.1 日志体系清单

环节 动作 说明
分级 定义日志等级规范 DEBUG 不带上线
隐私 敏感字段 %{private} 合规要求
过滤 线上只留 WARN+ 日志量可控
动态 运行期可调等级 排查效率
监控 日志量/错误率告警 问题早发现

4.2 完整示例:日志系统演示

@Entry
@ComponentV2
struct KernelLogDemo {
  @Local logs: string[] = [];
  @Local logLevel: number = 2;   // 0:DEBUG 1:INFO 2:WARN 3:ERROR
  @Local logCount: number = 0;

  private levelNames: string[] = ['DEBUG', 'INFO', 'WARN', 'ERROR'];

  private writeLog(level: number, msg: string): void {
    if (level < this.logLevel) { return; }  // 等级过滤
    this.logs.push('[' + this.levelNames[level] + '] ' + msg);
    this.logCount++;
  }

  private simulateLogs(): void {
    this.logs = [];
    this.logCount = 0;
    this.writeLog(0, '调试: 传感器原始值 0x1A2B');
    this.writeLog(1, '信息: 用户登录成功 (uid=1001)');
    this.writeLog(2, '警告: 缓存命中率降至 60%');
    this.writeLog(3, '错误: 网络请求超时 (code=408)');
  }

  build() {
    Column({ space: 12 }) {
      Text('📋 HiLog 日志系统').fontSize(20).fontWeight(FontWeight.Bold)

      // 等级选择
      Row({ space: 6 }) {
        ForEach(this.levelNames, (ln: string, i: number) => {
          Button(ln).layoutWeight(1).height(36).fontSize(11)
            .backgroundColor(i === this.logLevel ? '#4FC3F7' : 'rgba(255,255,255,0.08)')
            .fontColor(i === this.logLevel ? '#0D1B2A' : '#FFFFFF')
            .onClick(() => { this.logLevel = i; this.simulateLogs(); })
        }, (ln: string) => ln)
      }
      .width('100%')

      Text('过滤等级: ' + this.levelNames[this.logLevel] +
        ' → 输出 ' + this.logCount + ' 条(环形缓冲)')
        .fontSize(11).fontColor('#69F0AE').width('100%')

      Button('▶ 模拟日志输出').width('100%').height(44)
        .onClick(() => this.simulateLogs())

      Scroll() {
        Column() {
          ForEach(this.logs, (l: string) => {
            Text(l).fontSize(11).fontFamily('monospace').lineHeight(18)
              .fontColor(l.startsWith('[ERROR]') ? '#EF5350'
                : l.startsWith('[WARN]') ? '#FFD54F'
                : l.startsWith('[INFO]') ? '#4FC3F7' : 'rgba(255,255,255,0.6)')
              .width('100%')
          }, (l: string, i: number) => l + i)
        }.width('100%')
      }
      .layoutWeight(1).width('100%').scrollBar(BarState.Off)
    }
    .width('100%').height('100%').padding(16)
    .backgroundColor('#0D1B2A')
  }
}

4.3 日志等级与线上策略

等级 用途 线上是否开启 日志量占比
DEBUG 调试细节 ❌ 默认关 70%
INFO 业务主流程 20%
WARN 异常预警 8%
ERROR 错误记录 2%
FATAL 致命错误 <0.1%

五、问题排查与性能优化

问题 原因 解决
日志丢数据 环形缓冲溢出覆盖 提高等级过滤
性能下降 日志量过大 异步 + 等级过滤
隐私泄露 敏感字段明文 %{private}
无法定位 无 Tag 规范 域+标签规范
日志刷屏 循环错误日志 错误去重 + 限频
断电丢日志 纯内存缓冲 关键日志持久化

5.1 日志性能优化

1. 异步写入: 日志提交立即返回,不阻塞业务
2. 等级过滤前置: 低于等级的日志直接丢弃(零开销)
3. 字符串缓存: 高频日志避免重复格式化
4. 错误去重: 相同错误限频(如 1s 内只记 1 条)
5. 采样日志: 海量日志按比例采样(1/100)

六、高阶总结与最佳实践

  1. 分级即规范:DEBUG 不上线、INFO 记流程、ERROR 记错误——等级是日志的第一规范。
  2. 环形缓冲保性能:有界 + 覆盖最旧 + 异步写,日志零阻塞。
  3. 隐私是红线:敏感字段一律 %{private},合规是底线。
  4. 动态开关提效率:问题复现时动态开 DEBUG,排查完恢复——别让日志干扰生产。
  5. 监控告警闭环:日志量、错误率、FATAL 都要有告警,问题早发现早处理。

一句话记住:HiLog = 环形缓冲(快而不阻塞)+ 分级过滤(省而精准)+ 动态开关(查而可控)+ 隐私保护(合规底线)——日志是排障的探照灯,但灯不能晃眼、不能漏光。

Logo

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

更多推荐