HarmonyOS 应用接入¶
通过收集 HarmonyOS 应用的指标数据,以可视化方式分析应用性能。
前置条件¶
注意:若您开通了 RUM Headless 服务,前置条件已自动帮您配置完成,直接接入应用即可。
- 安装 DataKit;
- 配置 RUM 采集器;
- DataKit 配置为公网可访问,并且安装 IP 地理信息库。
应用接入¶
- 进入 用户访问监测 > 新建应用 > HarmonyOS;
- 输入应用名称和应用 ID;
- 选择应用接入方式:
- 公网 DataWay:直接接收 RUM 数据,无需安装 DataKit 采集器。
- 本地环境部署:满足前置条件后接收 RUM 数据。
安装¶
* 下载最新
ft_sdk.har 文件:https://static.guance.com/ft-sdk-package/harmony_os_sdk_har/0.1.0-alpha01/ft_sdk.har
HAR 包依赖配置:
根据 HarmonyOS 官方文档(HAR 包导入指南),HAR 文件应放在项目的 libs 目录下。
在项目的 oh-package.json5 中添加依赖:
注意:
- 确保
ft_sdk.har文件在libs/目录下(如果不存在,请创建该目录) - 如果 HAR 文件在根目录,需要移动到
libs/目录
然后运行:
安装完成后,HAR 包会被安装到 oh_modules/ft_sdk/ 目录下。
导入说明:
根据 HAR 包的实际结构,使用以下方式导入:
import { FTSDK } from 'ft_sdk/src/main/ets/components/FTSDK';
import { FTSDKConfig, FTRUMConfig, FTLoggerConfig } from 'ft_sdk/src/main/ets/components/Configs';
权限说明:
SDK 模块已自动包含以下权限声明,使用者无需手动配置。当应用集成 SDK 时,这些权限会自动生效:
| 权限名称 | 用途说明 |
|---|---|
ohos.permission.INTERNET |
网络访问权限,用于数据上报和网络请求追踪 |
ohos.permission.GET_WIFI_INFO |
获取 WiFi 信息,用于网络类型检测和信号强度采集 |
ohos.permission.GET_NETWORK_INFO |
获取网络信息,用于网络状态监控和类型识别 |
初始化¶
在 EntryAbility.ets 中初始化 SDK:
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 {
// 本地环境部署(Datakit)
// const sdkConfig = FTSDKConfig.builder(datakitUrl);
// 公网 DataWay
const sdkConfig = FTSDKConfig.builder(datawayUrl, clientToken)
.setDebug(true)
.setServiceName('Your-App-Name');
// dbCacheLimit 默认值为 100MB,如需自定义可调用 .setDbCacheLimit(sizeInBytes)
// rumCacheLimitCount 默认值为 100000,如需自定义可调用 .setRumCacheLimitCount(count)
// logCacheLimitCount 默认值为 5000,如需自定义可调用 .setLogCacheLimitCount(count)
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.');
});
}
}
| 字段 | 类型 | 必须 | 说明 |
|---|---|---|---|
| datakitUrl | string | 是 | 本地环境部署(Datakit)上报 URL 地址,例子:http://10.0.0.1:9529,端口默认 9529,安装 SDK 设备需能访问该地址。注意:datakitUrl 和 datawayUrl 配置两者二选一 |
| datawayUrl | string | 是 | 公网 Dataway 上报 URL 地址,从 [用户访问监测] 应用中获取,例子:https://open.dataway.url,安装 SDK 设备需能访问这地址。注意:datakitUrl 和 datawayUrl 配置两者二选一 |
| clientToken | string | 是 | 认证 token,需要与 datawayUrl 同时配置 |
| debug | boolean | 否 | 设置是否允许打印 Debug 日志,默认false |
| env | string | 否 | 环境,默认prod,任意字符,建议使用单个单词,例如 test 等 |
| serviceName | string | 否 | 设置所属业务或服务的名称 默认:df_rum_harmonyos |
| autoSync | boolean | 否 | 是否在采集数据后自动同步到服务器。默认 true。当为 false 时使用 flushSyncData 方法自行管理数据同步 |
| syncPageSize | number | 否 | 设置同步请求条目数。范围 [5,),注意:请求条目数越大,代表数据同步占用更大的计算资源,默认为 10 |
| dbCacheLimit | number | 否 | DB 缓存限制大小。范围 [30MB,),默认 100MB,单位 byte |
| rumCacheLimitCount | number | 否 | RUM 数据缓存数量限制,默认 100000,最小值 10000 |
| logCacheLimitCount | number | 否 | 日志数据缓存数量限制,默认 5000,最小值 1000 |
| dbCacheDiscard | string | 否 | 设置数据库中数据丢弃规则。 丢弃策略: discard丢弃新数据(默认)、discardOldest丢弃旧数据 |
RUM 配置¶
const rumConfig = new FTRUMConfig()
.setRumAppId('your-app-id')
.setSamplingRate(1.0)
.setEnableTraceUserAction(true)
.setEnableTraceUserView(true)
.setEnableTraceUserResource(true)
.setEnableTrackAppANR(true)
.setEnableTraceWebView(true); // 开启 WebView 数据采集
await FTSDK.installRUMConfig(rumConfig);
| 方法 | 类型 | 必须 | 说明 |
|---|---|---|---|
| setRumAppId | string | 是 | RUM 应用 ID,从 [用户访问监测] 应用中获取 |
| setSamplingRate | number | 否 | RUM 采样率,范围 [0.0, 1.0],默认 1.0(100% 采集) |
| setEnableTraceUserAction | boolean | 否 | 是否开启自动动作追踪(UI 点击事件),默认 false |
| setEnableTraceUserView | boolean | 否 | 是否开启页面追踪,默认 false |
| setEnableTraceUserResource | boolean | 否 | 是否开启资源追踪(HTTP 请求),默认 false |
| setEnableTrackAppANR | boolean | 否 | 是否开启 ANR 监控,默认 false |
| setEnableTraceWebView | boolean | 否 | 是否开启 WebView 数据采集,默认 false |
| setRumCacheLimitCount | number | 否 | RUM 数据缓存数量限制,默认 100000,最小值 10000 |
日志配置¶
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);
| 方法 | 类型 | 必须 | 说明 |
|---|---|---|---|
| setSamplingRate | number | 否 | 日志采样率,范围 [0.0, 1.0],默认 1.0(100% 采集) |
| setEnableCustomLog | boolean | 否 | 是否启用自定义日志,默认 false |
| setLogCacheLimitCount | number | 否 | 日志数据缓存数量限制,默认 5000,最小值 1000 |
| setLogLevelFiltersString | string[] | 否 | 日志级别过滤,可选值:ERROR、WARNING、INFO、DEBUG |
| setEnableLinkRumData | boolean | 否 | 是否关联 RUM 数据,默认 false |
Trace 配置¶
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);
| 方法 | 类型 | 必须 | 说明 |
|---|---|---|---|
| setSamplingRate | number | 否 | 设置采集率,取值范围 [0,1],0 表示不采集,1 表示全采集,默认值为 1 |
| setTraceType | TraceType | 否 | 设置链路追踪的类型,默认为 DDTRACE,目前支持 ZIPKIN_MULTI_HEADER、ZIPKIN_SINGLE_HEADER、JAEGER、DDTRACE、SKYWALKING、TRACEPARENT (W3C),如果接入 OpenTelemetry 选择对应链路类型时,请注意查阅支持类型及 agent 相关配置 |
| setEnableLinkRUMData | boolean | 否 | 是否与 RUM 数据关联,默认为 false |
| setEnableAutoTrace | boolean | 否 | 设置是否开启自动 http trace,目前支持 RCP 的自动追踪,默认为 false |
| setHeaderHandler | HeaderHandler | 否 | 设置全局 FTTraceHeaderHandler,默认不设置,用于自定义 Trace Header |
RUM 用户数据追踪¶
View¶
使用方法¶
startView(viewName: string, property?: Record<string, object>): void
stopView(property?: Record<string, object>): void
updateLoadTime(loadTime: number): void
代码示例¶
import { FTRUMGlobalManager } from 'ft_sdk/src/main/ets/components/rum/FTRUMGlobalManager';
// 场景 1
FTRUMGlobalManager.getInstance().startView('ProductPage');
// 场景 2: 动态参数
// 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);
// 场景 1
FTRUMGlobalManager.getInstance().stopView();
// 场景 2: 动态参数
const stopViewProperty = new ViewProperty();
stopViewProperty.set('view_duration', 1000);
FTRUMGlobalManager.getInstance().stopView(stopViewProperty.toObject());
Action¶
使用方法¶
addAction(actionName: string, actionType: string, duration: number, startTime: number, property?: Record<string, object>): void
代码示例¶
import { FTRUMGlobalManager } from 'ft_sdk/src/main/ets/components/rum/FTRUMGlobalManager';
// 场景 1: 直接完成
FTRUMGlobalManager.getInstance().addAction('buy_button_click', 'click');
// 场景 2: 动态参数
// 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());
// 场景 3: 开始 Action(包含时长计算机制)
FTRUMGlobalManager.getInstance().startAction('buy_button_click', 'click');
// ... 执行操作 ...
FTRUMGlobalManager.getInstance().stopAction();
自动追踪:
在 RUM 配置中开启 enableTraceUserAction 后,SDK 会自动追踪 UI 组件的点击事件。为 Button 组件添加 customProperty 以获取按钮文本:
Button('Buy Now')
.id('buy-btn')
.customProperty('buy-btn', 'Buy Now')
.onClick(() => {
// 点击事件会自动被追踪
})
Resource¶
使用方法¶
startResource(resourceId: string, property?: Record<string, object>): void
stopResource(resourceId: string): void
addResource(resourceId: string, resourceParams: ResourceParams, netStatusBean?: NetStatusBean): void
自动追踪:
在 RUM 配置中开启 enableTraceUserResource 后,SDK 会自动追踪通过 RCP 发送的 HTTP 请求。
代码示例¶
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';
// 开始资源追踪
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);
// 结束资源追踪并添加资源数据
FTRUMGlobalManager.getInstance().stopResource(resourceId);
FTRUMGlobalManager.getInstance().addResource(resourceId, resourceParams, netStatusBean);
Error¶
使用方法¶
addError(log: string, message: string, errorType: ErrorType, appState: AppState, property?: Record<string, object>): void
代码示例¶
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
);
自动追踪:
在 RUM 配置中开启 enableTrackAppCrash 和 enableTrackAppANR 后,SDK 会自动捕获应用崩溃和 ANR 事件。
LongTask¶
使用方法¶
addLongTask(taskName: string, duration: number, property?: Record<string, string | number | boolean>): void
代码示例¶
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);
日志功能¶
自定义日志¶
使用方法¶
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
代码示例¶
import { FTLogger } from 'ft_sdk/src/main/ets/components/log/FTLogger';
import { Status } from 'ft_sdk/src/main/ets/components/bean/Status';
// 上传单个日志
FTLogger.getInstance().logBackground('Failed to load product data', Status.ERROR);
// 传递参数
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);
// 使用字符串状态
FTLogger.getInstance().logBackground('User logged in successfully', 'info');
注意:
- FTLogger 是提供给外部用户使用的公共日志 API
- 如需启用 SDK 内部调试日志,请使用 FTSDKConfig.setDebug(true) 配置
Tracer 网络链路追踪¶
在 Trace 配置 中开启 enableAutoTrace 后,SDK 会自动为 HTTP 请求注入链路追踪 Headers。也可以手动使用 FTTraceManager 在 HTTP 请求中添加 Propagation Header。
自动追踪¶
在 Trace 配置中开启 enableAutoTrace 后,SDK 会自动为通过 RCP 发送的 HTTP 请求注入 Trace Headers:
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 会自动注入到请求中
手动获取 Trace Header¶
如果使用其他网络框架,可以手动获取 Trace Header 并添加到请求中:
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'; // 用于标识资源的唯一 ID
// 获取链路头参数
const traceHeaders: Record<string, string> = FTTraceManager.getInstance().getTraceHeader(resourceId, url);
// 将 Trace Headers 添加到请求中
const httpRequest = http.createHttp();
const response = await httpRequest.request(url, {
method: http.RequestMethod.GET,
header: traceHeaders
});
Trace 类型¶
| Trace 类型 | 协议 | 主要 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 |
用户数据绑定¶
使用方法¶
/**
* 绑定用户信息(仅用户 ID)
* @param id 用户 ID
*/
static bindRumUserDataById(id: string): void
/**
* 绑定用户信息(完整用户数据)
* @param userData 用户数据对象
*/
static async bindRumUserData(userData: UserData): Promise<void>
/**
* 解绑用户信息
*/
static async unbindRumUserData(): Promise<void>
UserData¶
| 方法名 | 含义 | 必须 | 注意 |
|---|---|---|---|
| setId | 设置用户 ID | 否 | |
| setName | 设置用户名 | 否 | |
| setEmail | 设置邮箱 | 否 | |
| setExts | 设置用户扩展 | 否 | 添加规则请查阅此处 |
代码示例¶
import { FTSDK } from 'ft_sdk/src/main/ets/components/FTSDK';
import { UserData } from 'ft_sdk/src/main/ets/components/rum/bean/UserData';
// 方式一:仅绑定用户 ID(推荐用于快速绑定)
FTSDK.bindRumUserDataById('user_001');
// 方式二:绑定完整用户信息(推荐用于需要更多用户信息的场景)
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);
// 解绑用户信息(用户退出登录时调用)
await FTSDK.unbindRumUserData();
添加自定义标签¶
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);
主动同步数据¶
当配置 autoSync 为 false 时,需要主动触发数据同步:
清理 SDK 缓存数据¶
注意:clearAllData() 会删除所有未上报的缓存数据,包括:
- 同步数据表(sync_data_flat)中的所有数据
- RUM 视图数据表(rum_view)中的所有数据
- RUM 动作数据表(rum_action)中的所有数据
WebView 集成¶
在 RUM 配置中开启 enableTraceWebView 后,SDK 会自动采集 WebView 中的 RUM 数据。
在 WebView 页面中使用
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() {
// 只需要传入 webController,配置会从应用入口的 RUM 配置中读取
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))
}
}