目录

How do I listen for native log information from ArkTS?

Question

Would you please provide an example to implement the following: register an ArkTS log listener in native code so that logs can be reported to ArkTS via a callback when the log API is called in any service on the native side?

Answer

  1. Create a Log.ts file in ArkTS (note that the file name extension is ts but not ets) and encapsulate the log listening API in a singleton.
    import { hilog } from '@kit.PerformanceAnalysisKit';
    
    
    export class GlobalThisAdapter {
      private constructor() {
      }
    
    
      private static instance: GlobalThisAdapter;
      private _logListener: LogsListener = new LogsListener();
    
    
      public static getInstance(): GlobalThisAdapter {
        if (!GlobalThisAdapter.instance) {
          GlobalThisAdapter.instance = new GlobalThisAdapter();
        }
        return GlobalThisAdapter.instance;
      }
    
    
      getLogsListener(): LogsListener | undefined {
        return this._logListener;
      }
    
    
      setLogsListener(value: LogsListener): void {
        this._logListener = value;
      }
    }
    
    
    export class LogsListener implements OnLogsListener {
      public constructor() {
      }
    
    
      onLogs(level: LogLevel, message: string): void {
        switch (level) {
          case LogLevel.DEBUG:
            hilog.debug(0x0000, 'debug', 'debug message is %{public}s', message);
            break;
          case LogLevel.INFO:
            hilog.info(0x0000, 'info', 'info message is %{public}s', message);
            break;
          case LogLevel.WARN:
            hilog.warn(0x0000, 'warn', 'warn message is %{public}s', message)
            break;
          case LogLevel.ERROR:
            hilog.error(0x0000, 'error', 'error message is %{public}s', message);
            break;
          case LogLevel.FATAL:
            hilog.fatal(0x0000, 'fatal', 'fatal message is %{public}s', message);
            break;
          default:
            hilog.info(0x0000, 'info', 'info message is %{public}s', message);
        }
      }
    }
    
    
    enum LogLevel {
      DEBUG = 3,
      INFO,
      WARN,
      ERROR,
      FATAL
    }
    
    
    export default interface OnLogsListener {
      onLogs(level: number, message: string): void;
    }
  2. Add the API implementation in native code. Note that napi_create_reference is used to create a reference, and napi_ref is used to manage the object lifecycle and is not affected by NativeScope. Use napi_get_named_property and napi_call_function to obtain OnLogsListener and onLogs and bind them to ArkTS.
    #include "napi/native_api.h"
    #include <bits/alltypes.h>
    #include <cstring>
    #include <hilog/log.h>
    
    
    napi_ref logListenerRef = nullptr;
    napi_ref onLogsFuncRef = nullptr;
    static napi_value RegisterLogListener(napi_env env, napi_callback_info info) {
        size_t argc = 1;
        napi_value globalThisAdapter = nullptr;
        napi_get_cb_info(env, info, &argc, &globalThisAdapter, nullptr, nullptr);
    
    
        napi_value getLogListenerFunc = nullptr;
        napi_get_named_property(env, globalThisAdapter, "getLogsListener", &getLogListenerFunc);
    
    
        napi_value logListener = nullptr;
        napi_call_function(env, globalThisAdapter, getLogListenerFunc, 0, nullptr, &logListener);
    
    
        napi_value onLogsFunc = nullptr;
        napi_get_named_property(env, logListener, "onLogs", &onLogsFunc);
    
    
        napi_create_reference(env, logListener, 1, &logListenerRef);
        napi_create_reference(env, onLogsFunc, 1, &onLogsFuncRef);
    
    
        return nullptr;
    }
  3. Add the API mappings in native code to implement the APIs.
    EXTERN_C_START
    static napi_value Init(napi_env env, napi_value exports) {
        napi_property_descriptor desc[] = {
            {"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr},
            {"registerLogListener", nullptr, RegisterLogListener, nullptr, nullptr, nullptr, napi_default, nullptr}};
        napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
        return exports;
    }
    EXTERN_C_END
  4. Export APIs from index.t.ets.
    import { GlobalThisAdapter } from '../../../ets/interface/Log'
    export const add: () => void;
    export const registerLogListener: (a: GlobalThisAdapter) => void;
  5. Register the log API. Use registerLogListener to register the ArkTS log implementation with the native. In this example, the onCreate method in the EntryAbility.ets file is used for registration.
    onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
      let globalThisAdapter: GlobalThisAdapter = GlobalThisAdapter.getInstance();
      testNapi.registerLogListener(globalThisAdapter);
      hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
    }
  6. Add the callback to be invoked when the API is called. When the API is called on the native side, the onLogs will be called in ArkTS.
    static void callOnLogs(napi_env env, LogLevel level, const char *message) {
    
    
        size_t argc = 2;
        napi_value argv[2] = {nullptr};
    
    
        int32_t tem = level;
        napi_create_int32(env, tem, &argv[0]);
        napi_create_string_utf8(env, message, strlen(message) + 1, &argv[1]);
        napi_value logListener = nullptr;
        napi_value onLogsFunc = nullptr;
        napi_get_reference_value(env, logListenerRef, &logListener);
        napi_get_reference_value(env, onLogsFuncRef, &onLogsFunc);
    
    
        napi_call_function(env, logListener, onLogsFunc, argc, argv, nullptr);
    }
  7. Call other service code. The following uses Add() as an example. When the callOnLogs method in Add() is called, the onLogs callback will be invoked on the ArkTS side.
    static napi_value Add(napi_env env, napi_callback_info info) {
        callOnLogs(env, LogLevel::LOG_INFO, "execute native Add function success");
        return nullptr;
    }