import errorManager from '@ohos.app.ability.errorManager';
import { BusinessError } from '@kit.BasicServicesKit';
import { WriteOptions } from '@kit.CoreFileKit';
import { appRecovery, common, Want } from '@kit.AbilityKit';
import { DateUtil } from './DateUtil';
import { LogUtil } from './LogUtil';
import { FileUtil } from './FileUtil';
import { AppUtil } from './AppUtil';
/**
* 定义异常对象的接口。
*/
interface ErrorObject {
message?: string; // 错误消息
code?: number; // 错误码
details?: string; // 错误详情
stack?: string; // 错误堆栈信息
}
/**
* 全局异常捕获与崩溃日志收集工具类。
*
* @author 鸿蒙布道师
* @since 2025/04/08
*/
export class CrashUtil {
private static observerId: number | undefined = undefined; // 错误观测器 ID
private static errorFilePath: string | undefined = undefined; // 错误日志文件路径
/**
* 注册错误观测器。
* 注册后可以捕获到应用产生的 JS Crash,应用崩溃时进程不会退出,并将异常信息写入本地文件。
*/
static onHandledException(): void {
try {
if (CrashUtil.observerId === undefined) {
CrashUtil.observerId = errorManager.on('error', {
/**
* 捕获未处理的异常信息。
*
* @param errMsg 异常信息。
*/
onUnhandledException(errMsg: string): void {
const errStr = `${DateUtil.getTodayStr()} - 未处理异常:\n${errMsg}\n\n`;
CrashUtil.writeErrorLog(errStr);
},
/**
* 捕获异常对象。
*
* @param errObject 异常对象。
*/
onException(errObject: Error | ErrorObject): void {
const errStr = `${DateUtil.getTodayStr()} - 异常对象:\n${JSON.stringify(errObject)}\n\n`;
CrashUtil.writeErrorLog(errStr);
}
});
}
} catch (err) {
LogUtil.error(`注册错误观测器失败:${err}`);
}
}
/**
* 注销错误观测器。
*/
static onExceptionDestroy(): void {
try {
if (CrashUtil.observerId !== undefined) {
errorManager.off('error', CrashUtil.observerId, (err: BusinessError) => {
if (err) {
LogUtil.error(`注销错误观测器失败:${JSON.stringify(err)}`);
}
});
CrashUtil.observerId = undefined;
}
} catch (err) {
LogUtil.error(`注销错误观测器时发生错误:${err}`);
}
}
/**
* 获取错误日志文件路径。
*
* @returns 返回错误日志文件路径。
*/
static getErrorFilePath(): string {
if (!CrashUtil.errorFilePath) {
CrashUtil.errorFilePath = FileUtil.getFilesDirPath("harmony_utils_error_log", "ErrorLog.json");
}
return CrashUtil.errorFilePath;
}
/**
* 读取错误日志文件的 JSON 字符串。
*
* @returns 返回错误日志内容的 JSON 字符串。
*/
static async getErrorJson(): Promise<string> {
const errorFilePath = CrashUtil.getErrorFilePath();
if (FileUtil.accessSync(errorFilePath)) {
return await FileUtil.readText(errorFilePath);
}
return '';
}
/**
* 将错误日志写入文件。
*
* @param logContent 日志内容。
*/
private static writeErrorLog(logContent: string): void {
const errorFilePath = CrashUtil.getErrorFilePath();
try {
CrashUtil.writeStr(errorFilePath, logContent);
} catch (err) {
LogUtil.error(`写入错误日志失败:${err}`);
}
}
/**
* 将数据写入文件,并关闭文件。
*
* @param path 文件路径。
* @param str 要写入的字符串。
* @returns 返回写入的字节数。
*/
private static writeStr(path: string, str: string): number {
const file = FileUtil.openSync(path);
const offset = FileUtil.statSync(file.fd).size;
try {
const formattedStr = offset === 0 ? `[${str}]` : `,${str}`;
const options: WriteOptions = { offset, encoding: 'utf-8' };
return FileUtil.writeSync(file.fd, formattedStr, options);
} finally {
FileUtil.closeSync(file.fd); // 确保文件始终被关闭
}
}
/**
* 启用应用恢复功能(已过时)。
* 推荐使用 `AppUtil.enableAppRecovery()`。
*/
static enableAppRecovery(
restart: appRecovery.RestartFlag = appRecovery.RestartFlag.ALWAYS_RESTART,
saveOccasion: appRecovery.SaveOccasionFlag = appRecovery.SaveOccasionFlag.SAVE_WHEN_ERROR,
saveMode: appRecovery.SaveModeFlag = appRecovery.SaveModeFlag.SAVE_WITH_FILE
): void {
AppUtil.enableAppRecovery(restart, saveOccasion, saveMode);
}
/**
* 重启应用(已过时)。
* 推荐使用 `AppUtil.restartApp()`。
*/
static restartApp(): void {
AppUtil.restartApp();
}
/**
* 设置下次恢复主动拉起场景下的 Ability(已过时)。
* 推荐使用 `AppUtil.setRestartWant()`。
*/
static setRestartWant(want: Want): void {
AppUtil.setRestartWant(want);
}
/**
* 保存当前 App 状态或主动保存 Ability 的状态(已过时)。
* 推荐使用 `AppUtil.saveAppState()`。
*/
static saveAppState(context?: common.UIAbilityContext): boolean {
return AppUtil.saveAppState(context);
}
}
代码如下:
TypeScript
import errorManager from '@ohos.app.ability.errorManager';
import { BusinessError } from '@kit.BasicServicesKit';
import { WriteOptions } from '@kit.CoreFileKit';
import { appRecovery, common, Want } from '@kit.AbilityKit';
import { DateUtil } from './DateUtil';
import { LogUtil } from './LogUtil';
import { FileUtil } from './FileUtil';
import { AppUtil } from './AppUtil';
/**
* 定义异常对象的接口。
*/
interface ErrorObject {
message?: string; // 错误消息
code?: number; // 错误码
details?: string; // 错误详情
stack?: string; // 错误堆栈信息
}
/**
* 全局异常捕获与崩溃日志收集工具类。
*
* @author 鸿蒙布道师
* @since 2025/04/08
*/
export class CrashUtil {
private static observerId: number | undefined = undefined; // 错误观测器 ID
private static errorFilePath: string | undefined = undefined; // 错误日志文件路径
/**
* 注册错误观测器。
* 注册后可以捕获到应用产生的 JS Crash,应用崩溃时进程不会退出,并将异常信息写入本地文件。
*/
static onHandledException(): void {
try {
if (CrashUtil.observerId === undefined) {
CrashUtil.observerId = errorManager.on('error', {
/**
* 捕获未处理的异常信息。
*
* @param errMsg 异常信息。
*/
onUnhandledException(errMsg: string): void {
const errStr = `${DateUtil.getTodayStr()} - 未处理异常:\n${errMsg}\n\n`;
CrashUtil.writeErrorLog(errStr);
},
/**
* 捕获异常对象。
*
* @param errObject 异常对象。
*/
onException(errObject: Error | ErrorObject): void {
const errStr = `${DateUtil.getTodayStr()} - 异常对象:\n${JSON.stringify(errObject)}\n\n`;
CrashUtil.writeErrorLog(errStr);
}
});
}
} catch (err) {
LogUtil.error(`注册错误观测器失败:${err}`);
}
}
/**
* 注销错误观测器。
*/
static onExceptionDestroy(): void {
try {
if (CrashUtil.observerId !== undefined) {
errorManager.off('error', CrashUtil.observerId, (err: BusinessError) => {
if (err) {
LogUtil.error(`注销错误观测器失败:${JSON.stringify(err)}`);
}
});
CrashUtil.observerId = undefined;
}
} catch (err) {
LogUtil.error(`注销错误观测器时发生错误:${err}`);
}
}
/**
* 获取错误日志文件路径。
*
* @returns 返回错误日志文件路径。
*/
static getErrorFilePath(): string {
if (!CrashUtil.errorFilePath) {
CrashUtil.errorFilePath = FileUtil.getFilesDirPath("harmony_utils_error_log", "ErrorLog.json");
}
return CrashUtil.errorFilePath;
}
/**
* 读取错误日志文件的 JSON 字符串。
*
* @returns 返回错误日志内容的 JSON 字符串。
*/
static async getErrorJson(): Promise<string> {
const errorFilePath = CrashUtil.getErrorFilePath();
if (FileUtil.accessSync(errorFilePath)) {
return await FileUtil.readText(errorFilePath);
}
return '';
}
/**
* 将错误日志写入文件。
*
* @param logContent 日志内容。
*/
private static writeErrorLog(logContent: string): void {
const errorFilePath = CrashUtil.getErrorFilePath();
try {
CrashUtil.writeStr(errorFilePath, logContent);
} catch (err) {
LogUtil.error(`写入错误日志失败:${err}`);
}
}
/**
* 将数据写入文件,并关闭文件。
*
* @param path 文件路径。
* @param str 要写入的字符串。
* @returns 返回写入的字节数。
*/
private static writeStr(path: string, str: string): number {
const file = FileUtil.openSync(path);
const offset = FileUtil.statSync(file.fd).size;
try {
const formattedStr = offset === 0 ? `[${str}]` : `,${str}`;
const options: WriteOptions = { offset, encoding: 'utf-8' };
return FileUtil.writeSync(file.fd, formattedStr, options);
} finally {
FileUtil.closeSync(file.fd); // 确保文件始终被关闭
}
}
/**
* 启用应用恢复功能(已过时)。
* 推荐使用 `AppUtil.enableAppRecovery()`。
*/
static enableAppRecovery(
restart: appRecovery.RestartFlag = appRecovery.RestartFlag.ALWAYS_RESTART,
saveOccasion: appRecovery.SaveOccasionFlag = appRecovery.SaveOccasionFlag.SAVE_WHEN_ERROR,
saveMode: appRecovery.SaveModeFlag = appRecovery.SaveModeFlag.SAVE_WITH_FILE
): void {
AppUtil.enableAppRecovery(restart, saveOccasion, saveMode);
}
/**
* 重启应用(已过时)。
* 推荐使用 `AppUtil.restartApp()`。
*/
static restartApp(): void {
AppUtil.restartApp();
}
/**
* 设置下次恢复主动拉起场景下的 Ability(已过时)。
* 推荐使用 `AppUtil.setRestartWant()`。
*/
static setRestartWant(want: Want): void {
AppUtil.setRestartWant(want);
}
/**
* 保存当前 App 状态或主动保存 Ability 的状态(已过时)。
* 推荐使用 `AppUtil.saveAppState()`。
*/
static saveAppState(context?: common.UIAbilityContext): boolean {
return AppUtil.saveAppState(context);
}
}