Skip to content

HarmonyOS Application Integration


By collecting Metrics data from HarmonyOS applications, analyze application performance in a visualized manner.

Prerequisites

Note: If you have enabled the RUM Headless service, the prerequisites have been automatically configured for you, and you can directly integrate the application.

Application Integration

  1. Go to RUM > Create Application > HarmonyOS;
  2. Enter the application name and application ID;
  3. Select the application integration method:
  4. Public DataWay: Directly receives RUM data without installing the DataKit collector.
  5. Local Environment Deployment: Receives RUM data after meeting the prerequisites.

Installation

HAR Package Dependency Configuration:

According to the HarmonyOS official documentation (HAR Package Import Guide), the HAR file should be placed in the libs directory of the project.

Add the dependency in the project's oh-package.json5:

{
  "dependencies": {
    "ft_sdk": "file:../libs/ft_sdk.har"
  }
}

Note:

  • Ensure the ft_sdk.har file is in the libs/ directory (create the directory if it does not exist)
  • If the HAR file is in the root directory, move it to the libs/ directory

Then run:

ohpm install

After installation, the HAR package will be installed in the oh_modules/ft_sdk/ directory.

Import Instructions:

According to the actual structure of the HAR package, use the following method to import:

import { FTSDK } from 'ft_sdk/src/main/ets/components/FTSDK';
import { FTSDKConfig, FTRUMConfig, FTLoggerConfig } from 'ft_sdk/src/main/ets/components/Configs';

Permission Instructions:

The SDK module automatically includes the following permission declarations, and users do not need to manually configure them. When the application integrates the SDK, these permissions will automatically take effect:

Permission Name Purpose Description
ohos.permission.INTERNET Internet access permission, used for data reporting and network request tracking
ohos.permission.GET_WIFI_INFO Get WiFi information, used for network type detection and signal strength collection
ohos.permission.GET_NETWORK_INFO Get network information, used for network status monitoring and type identification

Initialization

Initialize the SDK in EntryAbility.ets:

import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { FTSDK } from 'ft_sdk/src/main/ets/components/FTSDK';
import { FTSDKConfig, FTRUMConfig, FTLoggerConfig } from 'ft_sdk/src/main/ets/components/Configs';
import { TraceType } from 'ft_sdk/src/main/ets/components/trace/TraceType';

const DOMAIN = 0x0000;

export default class EntryAbility extends UIAbility {
  async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
    await this.initFTSDK();
  }

  private async initFTSDK(): Promise<void> {
    try {
      // Local Environment Deployment (Datakit)
      // const sdkConfig = FTSDKConfig.builder(datakitUrl);

      // Public DataWay
      const sdkConfig = FTSDKConfig.builder(datawayUrl, clientToken)
        .setDebug(true)
        .setServiceName('Your-App-Name');
        // dbCacheLimit defaults to 100MB, call .setDbCacheLimit(sizeInBytes) to customize
        // rumCacheLimitCount defaults to 100000, call .setRumCacheLimitCount(count) to customize
        // logCacheLimitCount defaults to 5000, call .setLogCacheLimitCount(count) to customize

      await FTSDK.install(sdkConfig, this.context);

    const rumConfig = new FTRUMConfig()
        .setSamplingRate(1.0)
        .setRumAppId('your-app-id')
        .setEnableTraceUserAction(true)
        .setEnableTraceUserView(true)
        .setEnableTraceUserResource(true)
        .setEnableTrackAppANR(true);

    await FTSDK.installRUMConfig(rumConfig);

    const logConfig = new FTLoggerConfig()
      .setSamplingRate(1.0)
      .setEnableCustomLog(true)
      .setLogCacheLimitCount(10000)
      .setEnableLinkRumData(true);

    FTSDK.installLogConfig(logConfig);

      hilog.info(DOMAIN, 'FTSDK', 'FT SDK initialized successfully');
    } catch (error) {
      const errorObj: object = error as object;
      hilog.error(DOMAIN, 'FTSDK', `Failed to initialize FT SDK: ${JSON.stringify(errorObj)}`);
    }
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        hilog.error(DOMAIN, 'FTSDK', `loadContent failed: ${err.code}`);
        return;
      }
      hilog.info(DOMAIN, 'FTSDK', 'Succeeded in loading the content.');
    });
  }
}
Field Type Required Description
datakitUrl string Yes Local environment deployment (Datakit) reporting URL address, example: http://10.0.0.1:9529, default port 9529, the device installing the SDK must be able to access this address. Note: Choose one between datakitUrl and datawayUrl
datawayUrl string Yes Public Dataway reporting URL address, obtained from the [RUM] application, example: https://open.dataway.url, the device installing the SDK must be able to access this address. Note: Choose one between datakitUrl and datawayUrl
clientToken string Yes Authentication token, must be configured with datawayUrl
debug boolean No Set whether to allow printing Debug logs, default false
env string No Environment, default prod, any character, recommended to use a single word, such as test etc
serviceName string No Set the name of the business or service, default: df_rum_harmonyos
autoSync boolean No Whether to automatically synchronize data to the server after collection. Default true. When false, use the flushSyncData method to manage data synchronization
syncPageSize number No Set the number of entries for synchronization requests. Range [5,), note: the larger the number of entries, the more computational resources the data synchronization will occupy, default is 10
dbCacheLimit number No DB cache limit size. Range [30MB,), default 100MB, unit byte
rumCacheLimitCount number No RUM data cache count limit, default 100000, minimum 10000
logCacheLimitCount number No Log data cache count limit, default 5000, minimum 1000
dbCacheDiscard string No Set the data discard rule in the database.
Discard policy: discard discard new data (default), discardOldest discard old data

RUM Configuration

const rumConfig = new FTRUMConfig()
  .setRumAppId('your-app-id')
  .setSamplingRate(1.0)
  .setEnableTraceUserAction(true)
  .setEnableTraceUserView(true)
  .setEnableTraceUserResource(true)
  .setEnableTrackAppANR(true)
  .setEnableTraceWebView(true); // Enable WebView data collection

await FTSDK.installRUMConfig(rumConfig);
Method Type Required Description
setRumAppId string Yes RUM application ID, obtained from the [RUM] application
setSamplingRate number No RUM sampling rate, range [0.0, 1.0], default 1.0 (100% collection)
setEnableTraceUserAction boolean No Whether to enable automatic action tracking (UI click events), default false
setEnableTraceUserView boolean No Whether to enable page tracking, default false
setEnableTraceUserResource boolean No Whether to enable resource tracking (HTTP requests), default false
setEnableTrackAppANR boolean No Whether to enable ANR monitoring, default false
setEnableTraceWebView boolean No Whether to enable WebView data collection, default false
setRumCacheLimitCount number No RUM data cache count limit, default 100000, minimum 10000

Log Configuration

import { Status } from 'ft_sdk/src/main/ets/components/bean/Status';

const logConfig = new FTLoggerConfig()
  .setSamplingRate(1.0)
  .setEnableCustomLog(true)
  .setLogCacheLimitCount(10000)
  .setLogLevelFiltersString([
    Status.ERROR.name,
    Status.WARNING.name,
    Status.INFO.name,
    Status.DEBUG.name
  ])
  .setEnableLinkRumData(true);

FTSDK.installLogConfig(logConfig);
Method Type Required Description
setSamplingRate number No Log sampling rate, range [0.0, 1.0], default 1.0 (100% collection)
setEnableCustomLog boolean No Whether to enable custom logs, default false
setLogCacheLimitCount number No Log data cache count limit, default 5000, minimum 1000
setLogLevelFiltersString string[] No Log level filtering, options: ERRORWARNINGINFODEBUG
setEnableLinkRumData boolean No Whether to associate RUM data, default false

Trace Configuration

import { TraceType } from 'ft_sdk/src/main/ets/components/trace/TraceType';

const traceConfig = new FTTraceConfig()
  .setSamplingRate(0.8)
  .setTraceType(TraceType.DDTRACE)
  .setEnableAutoTrace(true)
  .setEnableLinkRUMData(true);

FTSDK.installTraceConfig(traceConfig);
Method Type Required Description
setSamplingRate number No Set the collection rate, range [0,1], 0 means no collection, 1 means full collection, default value is 1
setTraceType TraceType No Set the type of trace, default is DDTRACE, currently supports ZIPKIN_MULTI_HEADERZIPKIN_SINGLE_HEADERJAEGERDDTRACESKYWALKINGTRACEPARENT (W3C), if integrating OpenTelemetry, please check the supported types and agent related configuration
setEnableLinkRUMData boolean No Whether to associate with RUM data, default is false
setEnableAutoTrace boolean No Set whether to enable automatic http trace, currently supports RCP automatic tracing, default is false
setHeaderHandler HeaderHandler No Set the global FTTraceHeaderHandler, default is not set, used for custom Trace Header

RUM User Data Tracking

View

Usage

startView(viewName: string, property?: Record<string, object>): void
stopView(property?: Record<string, object>): void
updateLoadTime(loadTime: number): void

Code Example

import { FTRUMGlobalManager } from 'ft_sdk/src/main/ets/components/rum/FTRUMGlobalManager';

// Scenario 1
FTRUMGlobalManager.getInstance().startView('ProductPage');

// Scenario 2: Dynamic parameters
// PropertyWrapper class must be defined (ArkTS requirement)
class PropertyWrapper {
  value: string | number | boolean;
  constructor(v: string | number | boolean) {
    this.value = v;
  }
}

class ViewProperty {
  private properties: Map<string, string | number | boolean> = new Map();

  set(key: string, value: string | number | boolean): void {
    this.properties.set(key, value);
  }

  toObject(): Record<string, object> {
    const result: Record<string, object> = {} as Record<string, object>;
    this.properties.forEach((value, key) => {
      result[key] = new PropertyWrapper(value) as object;
    });
    return result;
  }
}

const viewProperty = new ViewProperty();
viewProperty.set('page_category', 'product');
viewProperty.set('page_id', '12345');
FTRUMGlobalManager.getInstance().startView('ProductPage', viewProperty.toObject());

FTRUMGlobalManager.getInstance().updateLoadTime(100000000);

// Scenario 1
FTRUMGlobalManager.getInstance().stopView();

// Scenario 2: Dynamic parameters
const stopViewProperty = new ViewProperty();
stopViewProperty.set('view_duration', 1000);
FTRUMGlobalManager.getInstance().stopView(stopViewProperty.toObject());

Action

Usage

addAction(actionName: string, actionType: string, duration: number, startTime: number, property?: Record<string, object>): void

Code Example

import { FTRUMGlobalManager } from 'ft_sdk/src/main/ets/components/rum/FTRUMGlobalManager';

// Scenario 1: Direct completion
FTRUMGlobalManager.getInstance().addAction('buy_button_click', 'click');

// Scenario 2: Dynamic parameters
// PropertyWrapper class must be defined (ArkTS requirement)
class PropertyWrapper {
  value: string | number | boolean;
  constructor(v: string | number | boolean) {
    this.value = v;
  }
}

class ActionProperty {
  private properties: Map<string, string | number | boolean> = new Map();

  set(key: string, value: string | number | boolean): void {
    this.properties.set(key, value);
  }

  toObject(): Record<string, object> {
    const result: Record<string, object> = {} as Record<string, object>;
    this.properties.forEach((value, key) => {
      result[key] = new PropertyWrapper(value) as object;
    });
    return result;
  }
}

const actionProperty = new ActionProperty();
actionProperty.set('product_id', '12345');
actionProperty.set('product_name', 'iPhone 15');
FTRUMGlobalManager.getInstance().addActionWithProperty('buy_button_click', 'click', actionProperty.toObject());

// Scenario 3: Start Action (includes duration calculation mechanism)
FTRUMGlobalManager.getInstance().startAction('buy_button_click', 'click');
// ... Perform operation ...
FTRUMGlobalManager.getInstance().stopAction();

Automatic Tracking:

After enabling enableTraceUserAction in the RUM configuration, the SDK will automatically track UI component click events. Add customProperty to the Button component to get the button text:

Button('Buy Now')
  .id('buy-btn')
  .customProperty('buy-btn', 'Buy Now')
  .onClick(() => {
    // Click events will be automatically tracked
  })

Resource

Usage

startResource(resourceId: string, property?: Record<string, object>): void
stopResource(resourceId: string): void
addResource(resourceId: string, resourceParams: ResourceParams, netStatusBean?: NetStatusBean): void

Automatic Tracking:

After enabling enableTraceUserResource in the RUM configuration, the SDK will automatically track HTTP requests sent via RCP.

Code Example

import { FTRUMGlobalManager } from 'ft_sdk/src/main/ets/components/rum/FTRUMGlobalManager';
import { ResourceParams } from 'ft_sdk/src/main/ets/components/rum/bean/ResourceParams';
import { NetStatusBean } from 'ft_sdk/src/main/ets/components/rum/bean/NetStatusBean';

const resourceId = 'https://api.example.com/data';

// Start resource tracking
FTRUMGlobalManager.getInstance().startResource(resourceId);

const resourceParams = new ResourceParams();
resourceParams.setUrl(resourceId);
resourceParams.setResourceStatus(200);
resourceParams.setResponseContentLength(1024);
resourceParams.resourceType = 'xhr';

const netStatusBean = new NetStatusBean();
netStatusBean.setResourceHostIP('192.168.1.1');
netStatusBean.setDNSTime(10000000);
netStatusBean.setTcpTime(20000000);
netStatusBean.setTTFB(50000000);
netStatusBean.setResponseTime(100000000);

// End resource tracking and add resource data
FTRUMGlobalManager.getInstance().stopResource(resourceId);
FTRUMGlobalManager.getInstance().addResource(resourceId, resourceParams, netStatusBean);

Error

Usage

addError(log: string, message: string, errorType: ErrorType, appState: AppState, property?: Record<string, object>): void

Code Example

import { FTRUMGlobalManager } from 'ft_sdk/src/main/ets/components/rum';
import { ErrorType, AppState } from 'ft_sdk/src/main/ets/components/rum';

const rumManager = FTRUMGlobalManager.getInstance();

// Option 1: Use ErrorProperty class (recommended for ArkTS)
// Note: ErrorProperty is a helper class, you can create your own or use Map
// PropertyWrapper class must be defined outside the method (ArkTS requirement)
class PropertyWrapper {
  value: string | number | boolean;
  constructor(v: string | number | boolean) {
    this.value = v;
  }
}

class ErrorProperty {
  private properties: Map<string, string | number | boolean> = new Map();

  set(key: string, value: string | number | boolean): void {
    this.properties.set(key, value);
  }

  toObject(): Record<string, object> {
    const result: Record<string, object> = {} as Record<string, object>;
    this.properties.forEach((value, key) => {
      // Create wrapper object using class (ArkTS requirement)
      result[key] = new PropertyWrapper(value) as object;
    });
    return result;
  }
}

const property = new ErrorProperty();
property.set('error_code', 'ERR_001');
property.set('user_id', 'test_user');

rumManager.addError(
  'Error stack trace:\n  at ProductPage.onClick\n  at Button.onClick',
  'Failed to load product data',
  ErrorType.CUSTOM,
  AppState.RUN,
  property.toObject()
);

// Option 2: Pass null if no properties needed
rumManager.addError(
  'Error stack trace:\n  at ProductPage.onClick\n  at Button.onClick',
  'Failed to load product data',
  ErrorType.CUSTOM,
  AppState.RUN
);

Automatic Tracking:

After enabling enableTrackAppCrash and enableTrackAppANR in the RUM configuration, the SDK will automatically capture application crashes and ANR events.

LongTask

Usage

addLongTask(taskName: string, duration: number, property?: Record<string, string | number | boolean>): void

Code Example

import { FTRUMGlobalManager } from 'ft_sdk/src/main/ets/components/rum';

const rumManager = FTRUMGlobalManager.getInstance();

// Option 1: Use Map for properties (recommended for ArkTS)
const property = new Map<string, string | number | boolean>();
property.set('task_type', 'data_processing');
property.set('data_size', 10000);

// Convert Map to Record (ArkTS compatible)
const propertyRecord: Record<string, string | number | boolean> = {} as Record<string, string | number | boolean>;
property.forEach((value, key) => {
  propertyRecord[key] = value;
});

const duration = 500000000;
rumManager.addLongTask('Data Processing', duration, propertyRecord);

// Option 2: Pass empty object or omit property parameter
rumManager.addLongTask('Data Processing', duration);

Logging Functionality

Custom Logs

Usage

FTLogger.getInstance().logBackground(content: string, status: Status): void
FTLogger.getInstance().logBackground(content: string, status: Status, property?: Map<string, object>): void
FTLogger.getInstance().logBackground(content: string, status: string, property?: Map<string, object>): void

Code Example

import { FTLogger } from 'ft_sdk/src/main/ets/components/log/FTLogger';
import { Status } from 'ft_sdk/src/main/ets/components/bean/Status';

// Upload a single log
FTLogger.getInstance().logBackground('Failed to load product data', Status.ERROR);

// Pass parameters
const property = new Map<string, object>();
property.set('error_code', 'ERR_001');
property.set('user_id', 'test_user');
FTLogger.getInstance().logBackground('Network request timeout', Status.WARN, property);

// Use string status
FTLogger.getInstance().logBackground('User logged in successfully', 'info');

Note: - FTLogger is a public logging API provided to external users - To enable SDK internal debug logs, use the FTSDKConfig.setDebug(true) configuration

Tracer Network Trace

After enabling enableAutoTrace in the Trace Configuration, the SDK will automatically inject trace headers into HTTP requests. You can also manually use FTTraceManager to add Propagation Header to HTTP requests.

Automatic Tracing

After enabling enableAutoTrace in the Trace configuration, the SDK will automatically inject Trace Headers into HTTP requests sent via RCP:

import http from '@ohos.net.http';

const httpRequest = http.createHttp();
const response = await httpRequest.request('https://api.example.com/data', {
  method: http.RequestMethod.GET
});
// Trace Headers will be automatically injected into the request

Manually Obtain Trace Header

If using other network frameworks, you can manually obtain the Trace Header and add it to the request:

import { FTTraceManager } from 'ft_sdk/src/main/ets/components/trace/FTTraceManager';
import http from '@ohos.net.http';

const url = 'https://api.example.com/data';
const resourceId = 'unique-resource-id'; // Unique ID for identifying the resource

// Get trace header parameters
const traceHeaders: Record<string, string> = FTTraceManager.getInstance().getTraceHeader(resourceId, url);

// Add Trace Headers to the request
const httpRequest = http.createHttp();
const response = await httpRequest.request(url, {
  method: http.RequestMethod.GET,
  header: traceHeaders
});

Trace Types

Trace Type Protocol Main Headers
DDTRACE DataDog Trace x-datadog-trace-id, x-datadog-parent-id, x-datadog-origin, x-datadog-sampling-priority
ZIPKIN_MULTI_HEADER Zipkin Multi-Header X-B3-TraceId, X-B3-SpanId, X-B3-ParentSpanId, X-B3-Sampled
ZIPKIN_SINGLE_HEADER Zipkin Single-Header (b3) b3
JAEGER Jaeger uber-trace-id
TRACEPARENT W3C Trace Context traceparent, tracestate
SKYWALKING SkyWalking sw8

User Data Binding

Usage

/**
 * Bind user information (only user ID)
 * @param id User ID
 */
static bindRumUserDataById(id: string): void

/**
 * Bind user information (complete user data)
 * @param userData User data object
 */
static async bindRumUserData(userData: UserData): Promise<void>

/**
 * Unbind user information
 */
static async unbindRumUserData(): Promise<void>

UserData

Method Name Meaning Required Note
setId Set user ID No
setName Set username No
setEmail Set email No
setExts Set user extensions No Add rules refer to here

Code Example

import { FTSDK } from 'ft_sdk/src/main/ets/components/FTSDK';
import { UserData } from 'ft_sdk/src/main/ets/components/rum/bean/UserData';

// Method 1: Only bind user ID (recommended for quick binding)
FTSDK.bindRumUserDataById('user_001');

// Method 2: Bind complete user information (recommended for scenarios requiring more user information)
  const userData = new UserData();
userData.setId('user_001');
userData.setName('test.user');
userData.setEmail('test@mail.com');
const exts: Record<string, string> = {
  'ft_key': 'ft_value',
  'user_type': 'vip'
};
userData.setExts(exts);
await FTSDK.bindRumUserData(userData);

// Unbind user information (call when user logs out)
await FTSDK.unbindRumUserData();

Add Custom Tags

import { FTSDK } from 'ft_sdk/src/main/ets/components/FTSDK';

const globalContext = new Map<string, string>();
globalContext.set('platform', 'harmonyos');
globalContext.set('version', '1.0.0');
FTSDK.appendGlobalContext(globalContext);

const rumGlobalContext = new Map<string, string>();
rumGlobalContext.set('user_type', 'vip');
rumGlobalContext.set('channel', 'official');
FTSDK.appendRUMGlobalContext(rumGlobalContext);

Actively Synchronize Data

When configuring autoSync to false, you need to actively trigger data synchronization:

import { FTSDK } from 'ft_sdk/src/main/ets/components/FTSDK';

FTSDK.flushSyncData();

Clear SDK Cache Data

import { FTSDK } from 'ft_sdk/src/main/ets/components/FTSDK';

await FTSDK.clearAllData();

Note: clearAllData() will delete all unsynchronized cache data, including: - All data in the sync data table (sync_data_flat) - All data in the RUM view data table (rum_view) - All data in the RUM action data table (rum_action)

WebView Integration

After enabling enableTraceWebView in the RUM configuration, the SDK will automatically collect RUM data in WebView.

Use in WebView Pages

import { FTWebViewHandler } from 'ft_sdk/src/main/ets/components/web/FTWebViewHandler';
import { webview } from '@kit.ArkWeb';

@Component
export struct MyWebViewPage {
  @State webController: webview.WebviewController = new webview.WebviewController();
  private webViewHandler: FTWebViewHandler = new FTWebViewHandler();

  aboutToAppear() {
    // Only need to pass in webController, the configuration will be read from the RUM configuration in the application entry
    this.webViewHandler.setWebView(this.webController);
  }

  aboutToDisappear() {
    this.webViewHandler.clearWebController();
  }

  build() {
    Web({ 
      src: 'https://example.com', 
      controller: this.webController 
    })
    .javaScriptAccess(true)
    .javaScriptProxy(this.webViewHandler.getJavaScriptProxy(this.webController))
  }
}

Feedback

Is this page helpful? ×