If coding specification issues or errors exist in the code of an application, the application may encounter unexpected errors, for example, uncaught exceptions, while it is running. In such a case, the application may exit unexpectedly. Error logs, however, are usually stored on users' local storage devices, making it inconvenient to locate faults. With the APIs provided by the errorManager module, the related errors and logs will be reported to your service platform for fault locating before application exits.
After the errorManager APIs are used to listen for exceptions and errors, the application does not exit. You are advised to add the synchronous exit operation after the callback is executed. If you only want to obtain error logs, you are advised to use HiAppEvent to subscribe to events.
The errorManager APIs are provided by @ohos.app.ability.errorManager(Error Management Module). Before using the APIs, you need to register an error observer and import it through import. For details, see How to Develop.
errorManager APIs
| API | Description |
|---|---|
| on(type: "error", observer: ErrorObserver): number | Registers an observer for application errors. A callback is invoked when an application error is detected. This API works in a synchronous manner. The return value is the serial number (SN) of the registered observer. |
| off(type: "error", observerId: number, callback: AsyncCallback<void>): void | Unregisters an observer using a callback. The number is the SN of the registered observer. |
| off(type: "error", observerId: number): Promise<void> | Unregisters an observer using a promise. The number is the SN of the registered observer. |
| on(type: 'globalErrorOccurred', observer: GlobalObserver): void | Registers a process error observer. When the system detects an application exception, it calls back the observer. This API is synchronous, meaning that a single registration enables global listening. Note: This API is supported since API version 18. |
| off(type: 'globalErrorOccurred', observer?: GlobalObserver): void | Unregisters the observer using a callback. Note: - This API is supported since API version 18. - When unregistering the observer, you can listen for exceptions on all threads in the process. It is recommended that you call the off API during process reclamation; otherwise, the application may crash. |
| on(type: 'globalUnhandledRejectionDetected', observer: GlobalObserver): void | Registers a process error observer. When the system detects an application promise exception, it calls back the observer. This API is synchronous, meaning that a single registration enables global listening. Note: This API is supported since API version 18. |
| off(type: 'globalUnhandledRejectionDetected', observer?: GlobalObserver): void | Unregisters the observer using a callback. Note: - This API is supported since API version 18. - When unregistering the observer, you can listen for exceptions on all threads in the process. It is recommended that you call the off API during process reclamation; otherwise, the application may crash. |
| on(type: 'loopObserver', timeout: number, observer: LoopObserver): void | Registers an observer for the message processing timeouts of the main thread. This API can be called only in the main thread. A new observer will overwrite the previous one. |
| off(type: 'loopObserver', observer?: LoopObserver): void | Unregisters an observer for the message processing timeouts of the main thread in LoopObserver mode. |
| on(type: 'freeze', observer: FreezeObserver): void | Registers an observer for the main thread freeze event of the application. This API can be called only in the main thread. A new observer will overwrite the previous one. |
| off(type: 'freeze', observer?: FreezeObserver): void | Unregisters an observer for the message processing timeouts of the main thread in FreezeObserver mode. Note: This API is supported since API version 18. |
| setDefaultErrorHandler(defaultHandler?: ErrorHandler): ErrorHandler | Sets a default error handler. This API can be called only in the main thread. When the JS_CRASH exception occurs, chained callbacks are supported and the return value is the last registered handler. Note: This API is supported since API version 21. |
| setDefaultResourceUsageObserver(defaultObserver?: ResourceUsageObserver): ResourceUsageObserver; | Called only in the main thread. When app resource usage exceeds the baseline, chained callbacks are supported. The return value is the previously registered resource usage observer. Note: This API is supported since API version 24. |
| setDefaultFreezeObserver(defaultObserver?: FreezeObserver) : FreezeObserver | Called only in the main thread. When an APP_FREEZE exception occurs, chained callbacks are supported. The return value is the previously registered handler. NOTE This API is supported since API version 26.0.0. Model restriction: This API can be used only in the stage model. |
When an asynchronous callback is used, the next step can be processed in the callback.
When a promise is used, the return value can also be processed in the promise. For details about the result codes, see Result Codes for Unregistering an Observer.
ErrorObserver APIs
| API | Description |
|---|---|
| onUnhandledException(errMsg: string): void | Called when an uncaught exception is reported after the application is registered. |
| onException?(errObject: Error): void | System callback. After the app registers, this callback is invoked when an exception occurs in the app and is reported to the JS layer. |
LoopObserver APIs
| API | Description |
|---|---|
| onLoopTimeOut?(timeout: number): void | Called when the message processing of the main thread times out. |
| Result Code | Description |
|---|---|
| 0 | Normal. |
| -1 | Input number not exist. |
| -2 | Invalid parameter. |
You are advised to add a synchronous exit operation at the end of the exception callback to prevent multiple exception callbacks.
Import the header files.
import { errorManager } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit'; Customize the domain value used for HiLog output.
const DOMAIN = 0x0000;
Add an observer.
let observer: errorManager.ErrorObserver = {
onUnhandledException(errorMsg) {
hilog.error(DOMAIN, 'testErrorManage', 'onUnhandledException, errorMsg: %{public}s', errorMsg);
},
onException(errorObj) {
hilog.error(DOMAIN, 'testErrorManage', 'onException, name: %{public}s', errorObj.name);
hilog.error(DOMAIN, 'testErrorManage', 'onException, message: %{public}s', errorObj.message);
if (typeof(errorObj.stack) === 'string') {
hilog.error(DOMAIN, 'testErrorManage', 'onException, stack: %{public}s', errorObj.stack);
}
}
}; Add a trigger button.
Button('Listening for a single thread').onClick(()=>{
let observerId = -1;
try {
observerId = errorManager.on('error', observer);
} catch (paramError) {
let code = (paramError as BusinessError).code;
let message = (paramError as BusinessError).message;
hilog.error(DOMAIN, 'testErrorManage', 'error: %{public}s, %{public}s', `${code}`, `${message}`);
}
// Construct a fault scenario.
throw new Error('test errorObserver msg');
}).position({x:50, y:50}); Import the header files.
import { errorManager } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit'; Customize the domain value used for HiLog output.
const DOMAIN = 0x0000;
Add an observer.
function errorFunc(observer: errorManager.GlobalError) {
hilog.error(DOMAIN, 'testErrorManage', 'result name :%{public}s', observer.name);
hilog.error(DOMAIN, 'testErrorManage', 'result message :%{public}s', observer.message);
hilog.error(DOMAIN, 'testErrorManage', 'result stack :%{public}s', observer.stack);
hilog.error(DOMAIN, 'testErrorManage', 'result instanceName :%{public}s', observer.instanceName);
hilog.error(DOMAIN, 'testErrorManage', 'result instanceType :%{public}s', observer.instanceType);
}; Add a trigger button.
Button('Listening for process exceptions').onClick(()=>{
try {
errorManager.on('globalErrorOccurred', errorFunc);
} catch (paramError) {
let code = (paramError as BusinessError).code;
let message = (paramError as BusinessError).message;
hilog.error(DOMAIN, 'testErrorManage', 'error: %{public}s, %{public}s', `${code}`, `${message}`);
}
// Construct a fault scenario.
throw new Error('test errorFunc msg');
}).position({x:50, y:100}); Import the header files.
import { errorManager } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit'; Customize the domain value used for HiLog output.
const DOMAIN = 0x0000;
Add an observer.
function promiseFunc(observer: errorManager.GlobalError) {
hilog.error(DOMAIN, 'testErrorManage', 'result name :%{public}s', observer.name);
hilog.error(DOMAIN, 'testErrorManage', 'result message :%{public}s', observer.message);
hilog.error(DOMAIN, 'testErrorManage', 'result stack :%{public}s', observer.stack);
hilog.error(DOMAIN, 'testErrorManage', 'result instanceName :%{public}s', observer.instanceName);
hilog.error(DOMAIN, 'testErrorManage', 'result instanceType :%{public}s', observer.instanceType);
};
async function promiseFuncOne() {
throw new Error('process promise exception');
}; Add a trigger button.
Button('Listening for process promise exceptions').onClick(()=>{
try {
errorManager.on('globalUnhandledRejectionDetected', promiseFunc);
} catch (paramError) {
let code = (paramError as BusinessError).code;
let message = (paramError as BusinessError).message;
hilog.error(DOMAIN, 'testErrorManage', 'error: %{public}s, %{public}s', `${code}`, `${message}`);
}
// Construct a fault scenario.
new Promise<string>(() => {
promiseFuncOne();
}).then(() => {
throw new Error('test promiseFuncOne msg');
});
}).position({x:50, y:200}); Import the header files.
import { errorManager } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit'; Customize the domain value used for HiLog output.
const DOMAIN = 0x0000;
Add an observer.
function freezeCallback() {
hilog.error(DOMAIN, 'testErrorManage', '%{public}s', 'freezecallback');
}; Add a trigger button.
Button('Listening for main thread freeze exceptions').onClick(()=>{
try {
errorManager.on('freeze', freezeCallback);
} catch (paramError) {
let code = (paramError as BusinessError).code;
let message = (paramError as BusinessError).message;
hilog.error(DOMAIN, 'testErrorManage', 'error: %{public}s, %{public}s', `${code}`, `${message}`);
}
// Construct a fault scenario.
let date = Date.now();
while (Date.now() - date < 15000) {
};
}).position({x:50, y:300}); Import the header files.
import { errorManager } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit'; Customize the domain value used for HiLog output.
const DOMAIN = 0x0000;
Add an observer.
let loopObserver: errorManager.LoopObserver = {
onLoopTimeOut(timeout: number) {
hilog.error(DOMAIN, 'testErrorManage', 'Duration timeout: %{public}s', `${timeout}`);
}
}; Add a trigger button.
Button('Listening for main thread timeouts').onClick(()=>{
try {
errorManager.on('loopObserver', 1, loopObserver);
} catch (paramError) {
let code = (paramError as BusinessError).code;
let message = (paramError as BusinessError).message;
hilog.error(DOMAIN, 'testErrorManage', 'error: %{public}s, %{public}s', `${code}`, `${message}`);
}
// Construct a fault scenario.
let date = Date.now();
while (Date.now() - date < 4000) {
};
}).position({x:50, y:150}); Import the header files.
import { errorManager } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit'; Customize the domain value used for HiLog output.
const DOMAIN = 0x0000;
Add an observer.
let promise1 = new Promise<void>(() => {}).then(() => {
throw new Error('uncaught error');
});
let unhandledrejectionObserver: errorManager.UnhandledRejectionObserver = (reason: Error, promise: Promise<void>) => {
if (promise === promise1) {
hilog.error(DOMAIN, 'testErrorManage', '%{public}s', 'promise1 is rejected');
}
hilog.error(DOMAIN, 'testErrorManage', 'reason.name: %{public}s', reason.name);
hilog.error(DOMAIN, 'testErrorManage', 'reason.message: %{public}s', reason.message);
if (reason.stack) {
hilog.error(DOMAIN, 'testErrorManage', 'reason.stack: %{public}s', reason.stack);
}
};
async function promiseFuncTwo() {
throw new Error('process promise unhandled rejection exception');
}; Add a trigger button.
Button('Listening for process promise exceptions').onClick(()=>{
try {
errorManager.on('unhandledRejection', unhandledrejectionObserver);
} catch (paramError) {
let code = (paramError as BusinessError).code;
let message = (paramError as BusinessError).message;
hilog.error(DOMAIN, 'testErrorManage', 'error: %{public}s, %{public}s', `${code}`, `${message}`);
}
// Construct a fault scenario.
new Promise<string>(() => {
promiseFuncTwo();
}).then(() => {
throw new Error('test promiseFuncTwo msg');
});
}).position({x:50, y:250}); Define the first error handler and register the method. If no pre-handler is available, the process exits.
import { errorManager } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { process } from '@kit.ArkTS';
import { BusinessError } from '@kit.BasicServicesKit';
const DOMAIN = 0x0000;
let firstHandler: errorManager.ErrorHandler;
const firstErrorHandler: errorManager.ErrorHandler = (reason: Error) => {
// Implement the logic of the first custom error handler.
hilog.info(DOMAIN, 'testErrorManage', '%{public}s', '[FirstHandler] First uncaught exception handler invoked.');
if (firstHandler) {
firstHandler(reason);
} else {
// You are advised to add a null check. If the value is null, use a synchronous exit approach.
const processManager = new process.ProcessManager();
processManager.exit(0);
}
};
export function setFirstErrorHandler() {
try {
firstHandler = errorManager.setDefaultErrorHandler(firstErrorHandler);
} catch (paramError) {
let code = (paramError as BusinessError).code;
let message = (paramError as BusinessError).message;
hilog.error(DOMAIN, 'testErrorManage', 'error: %{public}s, %{public}s', `${code}`, `${message}`);
}
hilog.info(DOMAIN, 'testErrorManage', '%{public}s', 'Registered First Error Handler');
} Define the second error handler and register the method to implement a chain call.
import { errorManager } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { process } from '@kit.ArkTS';
import { BusinessError } from '@kit.BasicServicesKit';
const DOMAIN = 0x0000;
let secondHandler: errorManager.ErrorHandler;
const secondErrorHandler: errorManager.ErrorHandler = (reason: Error) => {
// Implement the logic of the second custom error handler.
hilog.info(DOMAIN, 'testErrorManage', '%{public}s', '[SecondHandler] Second uncaught exception handler invoked.');
if (secondHandler) {
secondHandler(reason);
} else {
const processManager = new process.ProcessManager();
processManager.exit(0);
}
};
export function setSecondErrorHandler() {
try {
secondHandler = errorManager.setDefaultErrorHandler(secondErrorHandler);
} catch (paramError) {
let code = (paramError as BusinessError).code;
let message = (paramError as BusinessError).message;
hilog.error(DOMAIN, 'testErrorManage', 'error: %{public}s, %{public}s', `${code}`, `${message}`);
}
hilog.info(DOMAIN, 'testErrorManage', '%{public}s', 'Registered Second Error Handler');
} Import the header files.
import { setFirstErrorHandler } from './FirstErrorHandler';
import { setSecondErrorHandler } from './SecondErrorHandler'; Add the constructor for chaining error handlers.
function testErrorHandlers() {
setFirstErrorHandler();
setSecondErrorHandler();
throw new Error('Test uncaught exception!');
} Trigger the test through the button for the main component, register two handlers, and throw an error to verify the handler chain.
Button ('Chaining error handlers').onClick(()=>{
testErrorHandlers();
}).position({x:50, y:350}); Define the first freeze handler and its registration method. The process exits when there is no preceding handler.
import { errorManager } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
const DOMAIN = 0x0000;
let firstHandler: errorManager.FreezeObserver = () => {};
const firstFreezeHandler: errorManager.FreezeObserver = () => {
// Custom logic for the first FreezeHandler.
hilog.info(DOMAIN, 'testErrorManage', '%{public}s', '[FirstHandler] First freeze handler invoked.');
if (firstHandler) {
firstHandler();
} else {
hilog.info(DOMAIN, 'testErrorManage', '%{public}s', '[FirstHandler] First freeze handler end.');
}
};
export function setFirstFreezeHandler() {
try {
firstHandler = errorManager.setDefaultFreezeObserver(firstFreezeHandler);
} catch (paramError) {
let code = (paramError as BusinessError).code;
let message = (paramError as BusinessError).message;
hilog.error(DOMAIN, 'testErrorManage', 'error: %{public}s, %{public}s', `${code}`, `${message}`);
}
hilog.info(DOMAIN, 'testErrorManage', '%{public}s', 'Registered First freeze Handler.');
} Define the second freeze handler and its registration method to form a chained call.
import { errorManager } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
const DOMAIN = 0x0000;
let secondHandler: errorManager.FreezeObserver = () => {};
const secondFreezeHandler: errorManager.FreezeObserver = () => {
// Custom logic for the second FreezeHandler.
hilog.info(DOMAIN, 'testErrorManage', '%{public}s', '[SecondHandler] Second freeze handler invoked.');
if (secondHandler) {
secondHandler();
} else {
hilog.info(DOMAIN, 'testErrorManage', '%{public}s', '[SecondHandler] Second freeze handler end.');
}
};
export function setSecondFreezeHandler() {
try {
secondHandler = errorManager.setDefaultFreezeObserver(secondFreezeHandler);
} catch (paramError) {
let code = (paramError as BusinessError).code;
let message = (paramError as BusinessError).message;
hilog.error(DOMAIN, 'testErrorManage', 'error: %{public}s, %{public}s', `${code}`, `${message}`);
}
hilog.info(DOMAIN, 'testErrorManage', '%{public}s', 'Registered Second freeze Handler.');
} Import the header files.
import { setFirstFreezeHandler } from './FirstFreezeHandler';
import { setSecondFreezeHandler } from './SecondFreezeHandler'; Add a function for constructing the scenario fault.
function waitTime() {
// Construct a fault scenario.
let date = Date.now();
while (Date.now() - date < 15000) {
};
} Add the constructor for the freeze handler chain of responsibility pattern.
function testFreezeHandlers() {
setFirstFreezeHandler();
setSecondFreezeHandler();
waitTime();
} The main component triggers the test through a button, registers two handlers, and calls the scenario fault construction function to verify the processing chain.
Button('freeze handler chain of responsibility pattern scenario').onClick(()=>{
testFreezeHandlers();
}).position({x:50, y:400});