鸿蒙掌上驾考宝典应用开发36:日志系统——Logger 封装与 hilog 实践
·
第36篇:日志系统——Logger 封装与 hilog 实践

一、引言
日志是应用开发中最重要的调试工具之一。在鸿蒙系统中,hilog 提供了高性能的日志输出能力。DriverLicenseExam 项目通过 Logger 工具类对 hilog 进行了二次封装,提供了统一的日志接口,并支持日志分级、标签管理、脱敏处理等功能。本文将深入解析日志系统的设计与实现。
二、hilog 基础
2.1 hilog 是什么
hilog 是鸿蒙系统提供的高性能日志系统,具有以下特点:
- 高性能:异步日志写入,不影响主线程性能
- 分级输出:debug/info/warn/error 四级日志
- 隐私保护:内置 %{public}s / %{private}s 脱敏机制
- 领域隔离:通过 domain 区分不同模块的日志
2.2 hilog 的基本用法
import { hilog } from '@kit.PerformanceAnalysisKit';
// hilog 的完整签名
hilog.info(domain, tag, format, args);
// 示例
hilog.info(0xff00, 'MyApp', 'Hello, %{public}s', 'World');
三、Logger 封装
3.1 封装设计
项目中封装了 Logger 工具类,统一管理日志输出:
// commons/commonLib/src/main/ets/utils/Logger.ets
import { hilog } from '@kit.PerformanceAnalysisKit';
export class Logger {
private static _domain: number = 0xff00;
private static _prefix: string = 'EmptyTemplate';
private static _format: string = '%{public}s, %{public}s';
public static debug(...args: string[]): void {
hilog.debug(Logger._domain, Logger._prefix, Logger._format, args);
}
public static info(...args: string[]): void {
hilog.info(Logger._domain, Logger._prefix, Logger._format, args);
}
public static warn(...args: string[]): void {
hilog.warn(Logger._domain, Logger._prefix, Logger._format, args);
}
public static error(...args: string[]): void {
hilog.error(Logger._domain, Logger._prefix, Logger._format, args);
}
}
3.2 封装的好处
- 统一配置:domain、prefix、format 等参数集中管理
- 简化调用:调用方不需要关心 hilog 的细节参数
- 便于扩展:可以添加日志文件输出、日志上传等扩展功能
- 切换灵活:未来可以替换底层的日志实现而不影响调用方
四、日志标签规范
4.1 标签定义
项目中每个模块定义自己的日志标签:
// EntryAbility.ets
const TAG: string = '[EntryAbility]';
// HomeView.ets
const TAG: string = '[HomeView]';
// ExamService.ets
const TAG: string = '[ExamService]';
// MineView.ets
logTag: string = 'PrepareLoginPage';
domainId: number = 0x0000;
4.2 日志使用示例
// 信息日志
Logger.info(TAG, 'Ability onWindowStageCreate');
// 错误日志
Logger.error(TAG, 'Failed to load content: %{public}s', JSON.stringify(err) ?? '');
// 调试日志
Logger.debug(TAG, 'currentIndex: %{public}d', this.currentIndex);
// 警告日志
Logger.warn(TAG, 'Network request timeout: %{public}s', url);
4.3 日志输出格式
Logger 输出的日志格式为:
[domain] [prefix] [level] message
例如:
[0xff00] [EmptyTemplate] [INFO] [EntryAbility] Ability onWindowStageCreate
[0xff00] [EmptyTemplate] [ERROR] [ExamService] Failed to load exam data
[0xff00] [EmptyTemplate] [DEBUG] [HomeView] currentIndex: 2
五、日志脱敏
5.1 隐私保护机制
鸿蒙的 hilog 内置了隐私保护机制,通过格式化占位符控制:
// %{public}s - 公开信息,正常输出
Logger.info(TAG, 'Succeeded in loading the content.');
// %{private}s - 敏感信息,在 release 版本中自动脱敏
Logger.info(TAG, 'User phone: %{private}s', userPhone);
Logger.info(TAG, 'User token: %{private}s', accessToken);
// %{public}d - 公开数字
Logger.info(TAG, 'Question index: %{public}d', currentIndex);
5.2 项目中全面使用脱敏日志
// Logger 的 format 预设为 %{public}s, %{public}s
private static _format: string = '%{public}s, %{public}s';
// 默认所有参数以 %{public}s 输出
Logger.info(TAG, 'User info updated');
对于需要输出私有信息的场景,直接调用 hilog:
// 敏感信息使用 %{private}s
hilog.info(0x0000, 'QuickLoginPage',
'User phone: %{private}s', userPhone);
六、日志最佳实践
6.1 日志级别选择
| 级别 | 使用场景 | 示例 |
|---|---|---|
| debug | 开发调试,不保留到生产环境 | Logger.debug(TAG, 'Detail data: ' + JSON.stringify(data)) |
| info | 正常流程的关键节点 | Logger.info(TAG, 'Exam started, total: ' + count) |
| warn | 非预期的但可恢复的情况 | Logger.warn(TAG, 'Network timeout, retrying...') |
| error | 不可恢复的错误 | Logger.error(TAG, 'Failed to load: ' + err.message) |
6.2 日志内容规范
// 好的日志 - 包含上下文信息
Logger.error(TAG, 'Failed to load exam data. userId: %{public}s, examId: %{public}s',
userId, examId);
// 不好的日志 - 缺乏上下文
Logger.error(TAG, 'Error occurred');
6.3 避免过度日志
// 避免:循环中频繁输出日志
for (let i = 0; i < 1000; i++) {
Logger.debug(TAG, 'Processing item: ' + i); // 不要这样做!
}
// 推荐:输出概要信息
Logger.info(TAG, 'Processing 1000 items started');
// 处理逻辑
Logger.info(TAG, 'Processing 1000 items completed');
6.4 日志文件管理
虽然 Logger 目前只输出到控制台,但可以扩展为支持文件输出:
// 扩展:支持日志文件输出(可选)
export class Logger {
private static enableFileLog: boolean = false;
static enableFileLogging() {
Logger.enableFileLog = true;
}
private static writeToFile(level: string, tag: string, message: string) {
if (!Logger.enableFileLog) return;
// 写入文件逻辑
// 可以使用 FileIo 写入沙箱目录
}
}
七、日志在项目中的实际应用
7.1 生命周期日志
// EntryAbility 的生命周期日志
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
Logger.info(TAG, 'Ability onCreate');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
Logger.info(TAG, 'Ability onWindowStageCreate');
// ...
}
7.2 网络请求日志
// 网络请求错误日志
request(config) {
return new Promise((resolve, reject) => {
this._instance.request(config)
.then(res => resolve(res))
.catch(err => {
Logger.error('Network request failed: ' + config.url, err.message);
reject(err);
});
});
}
7.3 用户操作日志
// 用户操作日志
Logger.info(TAG, 'User clicked start exam button');
Logger.info(TAG, 'User selected city: ' + city);
Logger.info(TAG, 'User completed mock exam, score: ' + score);
7.4 数据分析日志
除了调试用途,日志还可以用于数据分析:
// 记录用户行为用于分析(注意脱敏)
Logger.info(TAG, 'User action: exam_start, type: ' + examType);
Logger.info(TAG, 'User action: exam_complete, score: ' + score + ', time: ' + duration);
Logger.info(TAG, 'User action: video_watch, videoId: ' + videoId);
八、日志与性能监控
8.1 性能日志
// 记录操作耗时
const startTime = performance.now();
// 执行操作
const endTime = performance.now();
Logger.info(TAG, 'Operation completed in ' + (endTime - startTime) + 'ms');
8.2 异常日志
// 捕获并记录异常
try {
// 可能出错的代码
} catch (error) {
Logger.error(TAG, 'Exception caught: ' + error.message);
Logger.error(TAG, 'Stack trace: ' + error.stack);
}
九、总结
日志系统是应用开发中不可或缺的基础设施。DriverLicenseExam 项目通过 Logger 封装实现了:
- 统一接口:通过 Logger 静态方法简化日志调用
- 分级输出:支持 debug/info/warn/error 四级日志
- 标签管理:每个模块使用独立的 TAG,便于日志过滤
- 隐私保护:通过 %{public}s / %{private}s 实现日志脱敏
- 可扩展性:未来可以添加文件输出、日志上传等功能
关键源码文件:
commons/commonLib/src/main/ets/utils/Logger.ets— 日志封装products/entry/src/main/ets/entryability/EntryAbility.ets— 生命周期日志commons/network/src/main/ets/models/AxiosHttpModel.ets— 网络请求日志products/entry/src/main/ets/pages/mine/QuickLoginPage.ets— 用户操作日志
更多推荐




所有评论(0)