catch (err) 以后只打印一句失败够吗?BusinessError 错误码日志模板【鸿蒙心迹】

你是不是也在想——“鸿蒙这么火,我能不能学会?”
答案是:当然可以!
这个专栏专为零基础小白设计,不需要编程基础,也不需要懂原理、背术语。我们会用最通俗易懂的语言、最贴近生活的案例,手把手带你从安装开发工具开始,一步步学会开发自己的鸿蒙应用。
不管你是学生、上班族、打算转行,还是单纯对技术感兴趣,只要你愿意花一点时间,就能在这里搞懂鸿蒙开发,并做出属于自己的App!
📌 关注本专栏《零基础学鸿蒙开发》,一起变强!
每一节内容我都会持续更新,配图+代码+解释全都有,欢迎点个关注,不走丢,我是小白酷爱学习,我们一起上路 🚀
全文目录:
前言
在 ArkTS 里写异步代码,很多人处理错误的方式大概是这样的:
try {
await fs.readFile(path);
} catch (err) {
console.error('读取失败');
}
出了问题,日志里只有"读取失败"四个字。排查时根本不知道是路径不存在、权限被拒,还是参数写错了。改成打印 err 本身也解决不了问题——很多开发者发现打出来的是 [object Object],或者信息残缺。
问题出在没有正确使用 BusinessError 类型。这篇文章从这个问题出发,整理一个可以在实际项目里直接复用的错误日志模板。
一、HarmonyOS 里的错误到底长什么样
HarmonyOS ArkTS 的系统 API 在运行失败时,抛出的是 BusinessError 类型的对象,而不是普通的 Error。
BusinessError 定义在 @ohos.base 模块中,继承自 JavaScript 的 Error,起始版本是 API 7:
| 属性 | 类型 | 说明 | 起始版本 |
|---|---|---|---|
code | number | 系统错误码,每个 Kit 都有独立的错误码体系 | API 7 |
message | string | 错误描述字符串 | API 7 |
data | T(泛型,可选) | 附加错误数据 | API 9 |
导入方式:
import { BusinessError } from '@ohos.base';
code 才是排查问题的关键。以文件管理为例,13900002 表示文件不存在,13900012 表示权限被拒——两种情况处理逻辑完全不同,但如果只打印"操作失败",完全无从区分。
二、为什么 console.error(err) 打不出有效信息
BusinessError 继承自 Error,但直接把对象传给 console.error() 时,ArkTS runtime 对它的序列化结果并不可靠——有时只输出 Error: message,code 字段丢失;有时输出 [object Object],信息为空。
原因在于 code 是自定义属性,不在标准 Error 的可枚举范围内,依赖 console.error(err) 的隐式序列化无法保证它出现在日志里。
正确做法是显式访问 err.code 和 err.message。
三、通用日志模板
一个在实际项目中有意义的 catch 块,至少要记录四项内容:
操作名称 ← 当前在做什么("读取用户配置文件",不是"文件操作")
error.code ← 系统错误码,用于查文档、区分原因
error.message ← 错误描述,辅助阅读
必要上下文 ← 操作的关键入参,例如路径、abilityName、bundleName
将这四项封装成一个工具函数:
import { BusinessError } from '@ohos.base';
/**
* 统一记录 BusinessError。
* @param operation 当前操作的描述,建议精确到"动词+操作对象"
* @param err catch 块捕获的 BusinessError
* @param context 与操作直接相关的关键参数,便于复现问题
*/
function logBusinessError(
operation: string,
err: BusinessError,
context?: Record<string, string | number | boolean>
): void {
const contextStr = context ? JSON.stringify(context) : '无';
console.error(
`[ERROR] 操作: ${operation} | 错误码: ${err.code} | 信息: ${err.message} | 上下文: ${contextStr}`
);
}
这里把四项信息拼成一行,方便在 DevEco Studio 的 HiLog 或日志过滤器里直接搜索 [ERROR] 快速定位。
四、用文件 API 主动触发一次错误
下面用 @ohos.file.fs 的 readFile 接口,故意读取一个不存在的文件,观察 BusinessError 的实际内容。
fs.readFile 要求使用应用沙箱路径,在 Stage 模型中通过 context.filesDir 获取。
前置条件:
- Stage 模型
- API Level ≥ 9
- 无需额外权限(访问应用自身沙箱目录)
import fs from '@ohos.file.fs';
import { BusinessError } from '@ohos.base';
import common from '@ohos.app.ability.common';
// 在 UIAbility 或 Page 中获取 context
// 这里假设已经通过组件的 getContext() 拿到 context
async function triggerFileReadError(context: common.UIAbilityContext): Promise<void> {
// 构造一个确定不存在的路径
const fakePath = context.filesDir + '/this_file_does_not_exist.txt';
try {
// fs.readFile 在 API 9 引入,返回 Promise<ArrayBuffer>
await fs.readFile(fakePath);
} catch (e) {
const err = e as BusinessError;
logBusinessError(
'读取用户本地配置文件',
err,
{ path: fakePath }
);
}
}
根据官方文件管理错误码说明,读取不存在的路径时,err.code 应为 13900002,err.message 为 No such file or directory。
日志输出结构将类似:
[ERROR] 操作: 读取用户本地配置文件 | 错误码: 13900002 | 信息: No such file or directory | 上下文:
{"path":"/data/storage/el2/base/files/this_file_does_not_exist.txt"}
五、用 Ability API 主动触发一次错误
UIAbilityContext.startAbility 在目标 Ability 不存在时会抛出 BusinessError,错误码为 16000001。
import common from '@ohos.app.ability.common';
import Want from '@ohos.app.ability.Want';
import { BusinessError } from '@ohos.base';
async function triggerAbilityStartError(context: common.UIAbilityContext): Promise<void> {
const want: Want = {
bundleName: 'com.example.nonexistent',
abilityName: 'FakeAbility'
};
try {
// startAbility 在 API 9 引入,Stage 模型使用
await context.startAbility(want);
} catch (e) {
const err = e as BusinessError;
logBusinessError(
'启动目标页面 Ability',
err,
{
bundleName: want.bundleName ?? '',
abilityName: want.abilityName ?? ''
}
);
}
}
根据官方 Ability 错误码说明,16000001 对应"指定的 Ability 不存在",排查方向是核对 bundleName 和 abilityName 是否正确。把这两个值写入 context,排查时不需要去翻业务代码就能知道当时传的是什么。
六、几个容易忽略的细节
1. 类型断言不能省
catch (e) 捕获到的 e 在 ArkTS 中是 unknown 类型,不是 BusinessError。直接访问 e.code 会在编译阶段报错。必须先做类型断言:
const err = e as BusinessError;
这里是断言,不是类型保护。如果某个地方真的有可能抛出非 BusinessError 的异常(比如自己写的逻辑错误),断言会让 err.code 拿到 undefined。想防御这种情况,可以加一个简单判断:
if (typeof (e as BusinessError).code === 'number') {
const err = e as BusinessError;
logBusinessError(operation, err, context);
} else {
console.error(`[ERROR] 操作: ${operation} | 非 BusinessError 异常: ${String(e)}`);
}
2. error.message 是英文,不是中文提示
BusinessError.message 是系统内部的英文描述,例如 No such file or directory。它的用途是辅助开发者查文档,不能直接展示给用户。面向用户的错误提示需要根据 err.code 自行映射。
3. context 里只放关键入参,不要放敏感信息
日志里的 context 是为了复现问题,记录路径、Ability 名、操作类型这类信息即可。用户 ID、token、密码不应出现在日志里。
4. data 属性不是所有错误都有
BusinessError.data 是泛型可选属性,起始版本 API 9,但大多数系统 API 的错误对象不会填充这个字段。不要把模板里默认依赖 data,优先从 code 和 message 入手。
七、排查问题时的顺序
当看到日志中的 BusinessError 后,建议按下面的顺序来定位:
- 先看
code:找到对应 Kit 的错误码文档(文件管理 / Ability / 网络 / 媒体……),确认这个码的含义和官方建议的排查方向。 - 再看
context:日志里记录的入参,确认调用时的实际值是否符合预期。 - 检查权限:如果
code是13900012(Permission denied)或16000004(无法启动不可见组件),先查module.json5里的requestPermissions配置。 - 检查路径格式:文件操作的路径必须是应用沙箱路径,不能是 URI 或相对路径。
- 检查 API Level:确认使用的接口在当前设备的 HarmonyOS 版本上是否已支持。
- 检查 Stage 模型限制:
@ohos.file.fs、UIAbilityContext.startAbility等接口仅支持 Stage 模型,FA 模型下的调用方式不同。
开发经验总结
catch (e)里必须访问err.code和err.message,直接序列化err对象无法保证code出现在日志里。- 把"操作名称 + 错误码 + 错误信息 + 关键入参"固定为一行结构化日志,是让错误可以被快速复现的最低要求。
BusinessError.message是英文系统描述,面向用户的提示需要根据code另行处理。- 日志里的上下文只记录排查必须的入参,不记录任何敏感字段。
err.data是可选属性,实际使用中多数系统 API 的错误不填充它,不要把模板逻辑依赖在这个字段上。
如果你的项目里有多个地方调用文件操作或 Ability 启动,可以把 logBusinessError 抽进一个公共工具模块,统一日志格式。维护期间过滤 [ERROR] 标签时,所有 BusinessError 就都能被统一捕获,不会散落在各处业务代码里。
❤️ 如果本文帮到了你…
- 请点个赞,让我知道你还在坚持阅读技术长文!
- 请收藏本文,因为你以后一定还会用上!
- 如果你在学习过程中遇到bug,请留言,我帮你踩坑!
更多推荐



所有评论(0)