跳到主要内容

API

SDK配置选项

import common from '@ohos.app.ability.common'

export enum LogLevel {
TRACE,
DEBUG,
INFO,
WARN,
ERROR,
NONE
}

export type RecordRule = {
// 需要采集此参数的域名/URL配置。目标URL包含于这项配置才会采集,如果不配置本条规则会对所有URL生效
url?: string
// 获取全部请求头, 默认空
reqHeaders?: string[]
// 请求体配置, 默认空
reqBody?: string[]
// 获取全部返回头, 默认空
resHeaders?: string[]
// 返回体配置, 默认空
resBody?: string[]
}

export type NetworkConfig = {
// 网络请求采集开关, 默认false
enabled?: boolean
// 跨应用追踪开关, 默认false
trackingEnabled?: boolean
// 采集header body开关, 默认false
recordEnabled?: boolean
// 采集数据白名单
recordConfig?: RecordRule[]
// 采集数据黑名单
recordBlockConfig?: RecordRule[]
// 采集的body截断长度, 单位KB
bodyMaxSize?: number
// 第三方apm请求头支持
apms?: string[]
// trace propagators
propagators?: string[]
// mPaaS开关, 默认false
mPaaSEnabled?: boolean
// mPaaS配置, 默认从entry模块rawfile中的mpaas.config文件中读取, 用户也可以通过此字段手动配置, 传入数据格式同mpaas.config的JSON格式。注意: 一旦设置此字段,将不再读取mpaas.config文件,即使手动配置不合法也不会回退到读取文件的默认行为
mPaaSConfig?: Record<string, string>
}

export type CrashConfig = {
// 崩溃采集总开关, 默认true
enabled?: boolean
// js崩溃采集开关, 默认true
jsCrashEnabled?: boolean
// cpp崩溃采集开关, 默认true
cppCrashEnabled?: boolean
}

export type FreezeConfig = {
// 卡死监控开关, 默认false
enabled?: boolean
}

export type LaunchWaitingPolicy = {
cold?: {
// 启动耗时事件等待时间, 默认30000ms
launchEvent?: number
// ability生命周期等待时间, 默认10000ms
ability?: number
// page生命周期等待时间, 默认15000ms
page?: number
},
hot?: {
// 启动耗时事件等待时间, 默认15000ms
launchEvent?: number
// 启动耗时事件等待时间, 默认10000ms
ability?: number
}
}

export type UserExperienceConfig = {
// 总开关, 默认false
enabled?: boolean
// 启动监控开关, 默认true
launchEnabled?: boolean
// 页面监控开关, 默认true
pageEnabled?: boolean
// 操作监控开关, 默认true
userActionEnabled?: boolean
// 使用自定义冷启动耗时结束点, 默认false
customLaunchEnd?: boolean
// 全量trace采集开关, 默认false
traceEnabled?: boolean
// 慢操作阈值, 默认3000ms
slowUserActionThreshold?: number
// 慢启动阈值, 默认3000ms
slowLaunchThreshold?: number
// 热启动阈值, 默认30s
hotStartThreshold?: number
// 慢可交互阈值, 默认1000ms
slowPageLoadThreshold?: number
// 慢首屏阈值, 默认3000ms
slowPageDurationThreshold?: number
// 启动耗时计算等待策略
launchWaitingPolicy?: LaunchWaitingPolicy
// 判断为操作错误的错误请求占比, 默认100
actionFailureThreshold?: number
// 启动耗时上限, 超过则不上报, 设置为0则不过滤, 默认60000ms
maxLaunchDuration?: number
}

export type UserActionConfig = {
// 总开关, 默认false
enabled?: boolean
}

export type WebviewConfig = {
// 总开关, 默认false
enabled?: boolean
// web sdk注入开关, 默认true
webSdkEnabled?: boolean
// 注入jsProxy开关, 默认true
jsProxyEnabled?: boolean
// JS片段注入开关, 默认true
jsSnippetEnabled?: boolean
// web sdk数据压缩开关, 默认false
// 当平台支持压缩功能时,后端可以通过下发的配置开关控制是否启用压缩,如需手动配置压缩开关,必须确保后端平台实际支持该功能
webSdkCompressEnabled?: boolean
}

export const ViewRecordQualities = {
// 极低质量
ULTRA_LOW: 0,
// 低质量
LOW: 1,
// 高质量
HIGH: 2
} as const
export type RecordQuality = typeof ViewRecordQualities[keyof typeof ViewRecordQualities]

export const ViewRecordUploadTypes = {
// 仅WIFI
WIFI_ONLY: 0,
// WIFI和移动网络
WIFI_AND_MOBILE: 1
} as const
export type RecordUploadType = typeof ViewRecordUploadTypes[keyof typeof ViewRecordUploadTypes]

export type ViewRecordConfig = {
// 总开关, 默认false
enabled?: boolean
// 上传类型, 默认为仅WIFI
uploadType?: RecordUploadType
// 图像质量, 默认为极低质量
quality?: RecordQuality
// 上传失败视图采集数据缓存大小, 单位MB, 默认10MB
maxCacheSize?: number
// 页面遮罩配置
pageBlacklist?: string[]
// 组件id遮罩配置
viewIdBlacklist?: string[]
}

export type EventConfig = {
// 暴力点击监控开关, 默认false
rageClickEnabled?: boolean
}

export type CommonConfig = {
// 是否允许采集操作系统版本, 默认true
osVersionEnabled?: boolean
// 是否允许采集设备厂商, 默认true
manufacturerEnabled?: boolean
// 是否允许采集设备型号, 默认true
manufacturerModelEnabled?: boolean
// 是否允许采集运营商信息, 默认true
carrierEnabled?: boolean
// 是否允许屏幕分辨率采集, 默认true
displayResolutionEnabled?: boolean
}

/**
* 应用配置
*/
export type InitConfig = {
// redirect服务器地址
redirectHost: string
// 应用appKey
appKey: string
// 上下文
context: common.Context
// 日志级别, 默认LogLevel.INFO
logLevel?: LogLevel
// 数据是否使用http发送数据, 默认false, 使用https发送
httpEnabled?: boolean
// axios对象, 需要拦截axios时需要传入
axios?: any
// 最大获取的栈深度, 默认20
stackDepth?: number
// 是否使用历史数据协议上报(平台3.8.0.0和以下版本平台需要设置为true),默认false
legacyDataProtocol?: boolean
// 构建ID
buildId?: string
// init内部部分逻辑是否采用异步初始化, 默认false
asyncInit?: boolean
// 是否启用国密加密方式上传数据, 默认false
encEnabled?: boolean
// 是否开启数据压缩上传, 默认true
compressEnabled?: boolean
// SDK请求超时时间, 单位ms, 默认15000ms, 最小设置3000ms, 设置低于最小值将回退为默认值
timeout?: number
// 插件配置
plugins?: Plugin[]
// 公共配置
common?: CommonConfig
// 网络请求采集配置
network?: NetworkConfig
// Crash监控配置
crash?: CrashConfig
// AppFreeze监控配置
freeze?: FreezeConfig
// 用户体验配置
ue?: UserExperienceConfig
// 用户行为分析配置
ua?: UserActionConfig
// webview配置
webview?: WebviewConfig
// 视图采集配置
viewRecord?: ViewRecordConfig
// 事件配置
event?: EventConfig
}

说明:

  1. init传入的配置为SDK初始配置,在SDK与服务端通信后会优先以服务端下发的配置为准

插件

插件名称包名
视图采集插件@tingyun/sdk-plugin-record

注:具体使用方式请参考对应插件的说明文档

SDK启动状态管理

SDK启动流程为异步,调用tingyun.init后SDK并未立即完成启动。部分API需要在SDK启动完成后才能调用。

SDK提供了三个API来管理启动状态:

  • isReady - 同步判断SDK是否已启动
  • onReady - 注册启动完成回调
  • waitForReady - 异步等待启动完成

isReady

同步判断SDK是否已启动完成。

API:

export type Tingyun = {
/**
* 判断SDK是否已经启动完成
* @returns SDK是否已启动
*/
isReady: () => boolean
}

使用方式:

import tingyun from '@tingyun/sdk-core'

if (tingyun.isReady()) {
// SDK已启动,可以调用需要依赖启动状态的API
}

onReady

注册SDK启动完成的回调函数。

API:

export type SDKReadyResult = {
// 启动流程结束后SDK是否是启用状态
enabled: boolean
// 是否超时
timeout?: boolean
// 启动流程结束后的提示信息
message?: string
}

export type SDKReadyOptions = {
// 等待超时时间, 单位毫秒, 0表示无超时时间, 默认为0
timeout?: number
}

export type SDKReadyCallback = (result: SDKReadyResult) => void

export type Tingyun = {
/**
* 注册SDK启动完成的回调函数
* @param callback SDK启动完成时执行的回调
* @param options 配置选项
*/
onReady: (callback: SDKReadyCallback, options?: SDKReadyOptions) => void
}

使用方式:

import tingyun from '@tingyun/sdk-core'

tingyun.onReady(() => {
// SDK已启动完成,调用需要依赖启动状态的API
})

说明:

  1. 如果调用onReady时SDK已经启动完成,回调会立即执行
  2. 可以多次调用onReady注册多个回调

waitForReady

异步等待SDK启动完成。

API:

export type Tingyun = {
/**
* 异步等待SDK启动完成
* @param options 配置选项
* @returns SDK启动结果
*/
waitForReady: (options?: SDKReadyOptions) => Promise<SDKReadyResult>
}

使用方式:

import tingyun from '@tingyun/sdk-core'

async function setupAfterInit() {
await tingyun.waitForReady()
// SDK已启动完成,调用需要依赖启动状态的API
}

API启动依赖说明

需要在启动后调用的API(有限制):

  • reportError - 自定义错误
  • startAction / endAction - 自定义操作
  • startSpan - 创建执行单元
  • startRequest / endRequest / createRequest - 自定义请求

无启动限制的API(可随时调用):

建议在启动后调用:

  • getSessionId - 获取会话ID(启动前调用可能返回空字符串)
  • setCustomLaunchTime - 设置自定义冷启动耗时结束点(启动前调用可能导致耗时计算不准确)
  • reportEvent - 自定义事件(启动前最多缓存5条,超出将被丢弃)

建议在启动前调用:

  • setSessionIdleTime - 设置会话闲置时长(建议在init前调用,仅首次调用生效)

无调用时机要求:

  • setUserId - 设置用户ID
  • setCustomData - 设置自定义附加信息
  • leaveBreadcrumb - 设置面包屑数据
  • setLatLon - 设置经纬度
  • startNextSession - 切换会话

说明:

  1. 需要在启动后调用的API如果在启动前调用,将被忽略且不会生效,建议使用onReadywaitForReady确保SDK启动完成后再调用

网络请求

Axios

SDK支持@ohos/axios库网络请求采集, 可以使用下面2种方式监控axios:

  1. tingyun.init时传入axios实例

    import tingyun from '@tingyun/sdk-core'
    import axios from '@ohos/axios'

    tingyun.init({
    // 传入axios实例对象
    axios: axios
    })

  2. 使用tingyun.registerAxiosInstance注册axios实例, 可以注册多个实例

    API定义:

    export type Tingyun = {
    registerAxiosInstance: (axiosInstance: AxiosInstance, options?: AxiosRegisterOptions) => void
    }

    export type AxiosRegisterOptions = {
    // 不等待网络开关初始化完成, 立即注册axios拦截器。默认情况下, 会等待网络开关初始化完成后再注册axios拦截器。
    immediate?: boolean
    }

    示例:

    import tingyun from '@tingyun/sdk-core'
    import { axiosInstance1, axiosInstance2 } from './utils/axios'

    // 普通注册(等待网络开关初始化完成)
    tingyun.registerAxiosInstance(axiosInstance1)

    // 立即注册(不等待网络开关初始化完成)
    // 使用场景:当用户需要控制拦截器注入时机,希望提前注入听云拦截器以确保拦截器执行顺序时,可以使用此选项
    tingyun.registerAxiosInstance(axiosInstance2, { immediate: true })
    // ...

说明:

  1. 两种方式可以同时使用, 相同axios实例只会被注册一次
  2. 首次启动时, SDK默认关闭网络请求开关, 如果需要监控首次启动时的网络请求, 需要在tingyun.init时配置网络请求开关默认为true
    tingyun.init({
    network: {
    enabled: true
    }
    })
  3. 使用immediate: true选项可以立即插入拦截器,用于用户对拦截器顺序有要求的场景。当用户需要控制拦截器注入时机,希望提前注入听云拦截器以确保拦截器执行顺序时,可以使用此选项

RCP

SDK通过提供拦截器的方式支持对RCP(remote communication platform)的监控。用户需要在调用createSession时加入SDK提供的拦截器

import tingyun from '@tingyun/sdk-core'

const session = rcp.createSession({
// 其他配置...
// 加入拦截器
interceptors: [new tingyun.RCPInterceptor()]
})

SDK网络请求接口

SDK对系统@ohos.net.httpHttpRequest.request接口进行了封装,对外暴露了request接口。调用这个接口发送的网络请求会被SDK采集

API定义:

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

export type HttpRequestWrapperOptions = {
url: string
options?: http.HttpRequestOptions
// http.createHttp() 返回的request对象,如果不传SDK内部会创建。不传httpRequest时,SDK内部只会在用户传入callback时在用户callback执行完后同步调用httpRequest.destroy。传入httpRequest时, SDK除了调用httpRequest.request之外不会调用其他方法。如果需要调用httpRequest其他方法、自行控制destroy时机以及使用promise方式时,需要传入httpRequest
httpRequest?: http.HttpRequest
}
export interface HttpRequestWrapper {
(options: HttpRequestWrapperOptions, callback: AsyncCallback<http.HttpResponse>): void
(options: HttpRequestWrapperOptions): Promise<http.HttpResponse>
}

export type Tingyun = {
// ...
// http request封装
request: HttpRequestWrapper
}

使用示例:
示例1: 传递URL (promise)

tingyun.request({url: 'xxx'})
.then((res) => {
// ...
})
.catch((err) => {
// ...
})

示例2: 传递URL (callback)

tingyun.request({url: 'xxx'}, (err, res) => {
// ...
})

示例3: 传递URL和options (promise)

tingyun.request({url: 'xxx', options: {
method: http.RequestMethod.POST,
header: {
'Content-Type': 'application/json'
},
extraData: {
// ...
}
}})
.then((res) => {
// ...
})
.catch((err) => {
// ...
})

示例4: 传递URL和options (callback)

tingyun.request({url: 'xxx', options: {
method: http.RequestMethod.POST,
header: {
'Content-Type': 'application/json'
},
extraData: {
// ...
}
}}, (err, res) => {
// ...
})

示例5: 使用自定义请求对象

const httpRequest = http.createHttp()
tingyun.request({
url: 'xxx',
httpRequest: httpRequest
}, (err, res) => {
// ...
})

自定义请求

为了支持用户使用的一些自定义网络请求库,SDK对外提供一套可以收集用户自身采集的网络库信息的ArkTS层API。为了满足多种使用场景, 提供两种使用模式:

  1. 模式1:请求发起时和请求结束后分别调用SDK API,SDK在请求结束API被调用时,收集这条网络请求

    第一种模式又分为2种使用方式:

    • 请求开始前调用tingyun.startRequest,结束后调用tingyun.endRequest, 两次调用都传递一个自行维护的请求ID关联同一个请求。这种方式调用比较灵活,但需要自行维护一个请求ID标识一个请求,如果现有的请求库本身提供了请求ID,或者请求开始和结束的代码结构比较分散时,适合用这种方式
    • 请求开始前调用tingyun.startRequest获取到SDK返回的请求实例,请求结束后调用请求实例的end方法结束请求。这种方式不需要自行维护请求ID,但代码结构较为复杂时,可能需要自行传递请求实例对象, 如果发送请求代码逻辑比较简单,在单一函数内完成时,适合用这种方式
  2. 模式2:请求结束后或任意时刻, 一次性调用tingyun.createRequest, 上报一条完整的网络请求数据

模式1: startRequest / endRequest

API定义:

export type Tingyun = {
// 开始自定义请求
startRequest: (options: RequestStartOptions) => IRequestInstance
// 结束自定义请求
endRequest: (options: RequestEndOptions) => void
}

export interface IRequestInstance {
end: (options?: RequestEndOptions) => void
clear: () => void
readonly id: string
}

export type RequestMethod = 'OPTIONS' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'TRACE' | 'CONNECT'
export type KeyValueStore<T> = Record<string, T> | Map<string, T>

export const LibNames = {
Flutter: 10,
OhosHttp: 13,
OhosAxios: 14,
OhosRCP: 15
} as const

export type LibName = typeof LibNames[keyof typeof LibNames]

export type RequestStartOptions = {
// 请求URL
url: string
// 请求ID: 请求唯一标识, 不传SDK将自动生成一个UUID格式字符串
id?: string
// 请求开始时间: 13位毫秒时间戳, 如果不传SDK将接口调用时间点作为开始点
startTime?: number
// method: 默认为GET
method?: RequestMethod
// 网络库名称
libName?: LibName
// 请求头
reqHeaders?: KeyValueStore<string>
// 请求体: 仅支持传文本类型, 如果是JSON格式需要序列化为字符串传入
reqBody?: string
// 发送字节数
bytesSent?: number
// 请求对象超时清理时间, 单位毫秒: 开始请求一段时间后如果不调用endRequest, SDK内会自动清理当前请求释放资源, 默认为90000ms
timeout?: number
}

export type RequestEndOptions = {
// 请求ID: 请求唯一标识, 当使用Tingyun.endRequest时必传,当使用请求实例对象结束时不传
id?: string
// http状态码
statusCode?: number
// 错误码: 网络错误或其他错误的错误码, 请求成功或发生http错误(4xx, 5xx), 不需要传
errorCode?: number
// 请求结束时间: 13位毫秒时间戳, 如果不传SDK将接口调用的时间点作为结束点
endTime?: number
// 请求总耗时, 单位毫秒: 如果不传使用endTime - startTime计算
duration?: number
// dns耗时, 单位毫秒: dns结束时间 - dns开始时间
dns?: number
// 建立连接时间, 单位毫秒: 建立连接结束时间 - 建立连接开始时间
connect?: number
// ssl握手时间, 单位毫秒: ssl握手结束时间 - ssl握手开始时间
ssl?: number
// 首包, 单位毫秒: 首包结束时间 - 首包开始时间
firstPacket?: number
// 剩余包, 单位毫秒: 剩余包结束时间 - 剩余包开始时间
remainPacket?: number
// 本地排队时间, 单位毫秒
localQueue?: number
// 发送字节数: 会覆盖startRequest时传递的bytesSent
bytesSent?: number
// 接收字节数
bytesReceived?: number
// 请求目标地址服务端IP
ip?: string
// 请求头: 会覆盖startRequest时传递的reqHeaders
reqHeaders?: KeyValueStore<string>
// 请求体: 仅支持传文本类型, 如果是JSON格式需要序列化为字符串传入. 会覆盖startRequest时传递的reqBody
reqBody?: string
// 返回头
resHeaders?: KeyValueStore<string>
// 返回体: 仅支持传文本类型的返回体, 如果是JSON格式需要序列化为字符串传入
resBody?: string
// 错误信息
errorMessage?: string
// 错误堆栈
errorStack?: string
// 链路追踪第三方apm请求头
apms?: KeyValueStore<string>
// 包含Tingyun APM的原始返回头和值
tyApm?: KeyValueStore<string>
}

示例1: 使用startRequest/endRequest,自定义请求ID

import tingyun from '@tingyun/sdk-core'

async function testRequest1() {
// 自定义请求ID
const reqId = genRequestId()
const url = 'https://example.com'
const method = 'POST'
const reqBody = {
key1: 1
}
const reqHeaders = {
'Content-Type': 'application/json;charset=UTF-8'
}
// 请求开始前, 调用startRequest开始请求
tingyun.startRequest({
id: reqId,
url: url,
method: method,
reqHeaders: reqHeaders,
reqBody: JSON.stringify(reqBody)
})

try {
// 调用自己的请求库
const response = await callCustomHttpLib(url, {
method: method,
reqBody: reqBody,
reqHeaders: reqHeaders
})

// 请求成功, 调用endRequest结束请求, 传递标识请求的请求ID
tingyun.endRequest({
id: reqId,
statusCode: response.statusCode,
resHeaders: response.headers,
resBody: response.body
})
} catch(e) {
// 请求失败, 调用endRequest结束请求, 传递标识请求的请求ID
tingyun.endRequest({
id: reqId,
errorCode: e.code
})
}
}

示例2: 使用startRequest返回的请求实例结束请求

import tingyun from '@tingyun/sdk-core'

async function testRequest2() {
const url = 'https://example.com'
const method = 'POST'
const reqBody = {
key1: 1
}
const reqHeaders = {
'Content-Type': 'application/json;charset=UTF-8'
}
// 请求开始前, 调用startRequest开始请求, 并获取请求实例
const requestInstance = tingyun.startRequest({
url: url,
method: method,
reqHeaders: reqHeaders,
reqBody: JSON.stringify(reqBody)
})

try {
// 调用自己的请求库
const response = await callCustomHttpLib(url, {
method: method,
reqBody: reqBody,
reqHeaders: reqHeaders
})

// 请求成功, 使用请求实例的end方法结束请求
requestInstance.end({
statusCode: response.statusCode,
resHeaders: response.headers,
resBody: response.body
})
} catch(e) {
// 请求失败, 使用请求实例的end方法结束请求
requestInstance.end({
errorCode: e.code
})
}
}

模式2: createRequest

API定义:

export type Tingyun = {
createRequest: (options: RequestOptions) => void
}

// RequestStartOptions RequestEndOptions 见上方
export type RequestOptions = Omit<RequestStartOptions & RequestEndOptions, 'id' | 'timeout'>

示例:

async function testRequest3() {
const url = 'https://example.com'
const method = 'POST'
const reqBody = {
key1: 1
}
const reqHeaders = {
'Content-Type': 'application/json;charset=UTF-8'
}
// 设置开始时间
const startTime = Date.now()
let endTime
try {
// 调用自己的请求库
const response = await callCustomHttpLib(url, {
method: method,
reqBody: reqBody,
reqHeaders: reqHeaders
})

// 设置结束时间
endTime = Date.now()
// 请求成功, 调用createRequest创建自定义请求
tingyun.createRequest({
url: url,
startTime: startTime,
endTime: endTime,
statusCode: response.statusCode,
reqHeaders: reqHeaders,
resHeaders: response.headers,
resBody: response.body
})
} catch(e) {
// 设置结束时间
endTime = Date.now()
// 请求失败, 调用createRequest创建自定义请求
tingyun.createRequest({
url: url,
startTime: startTime,
endTime: endTime,
errorCode: e.code
})
}
}

mPaaS移动网关请求

SDK通过拦截器实现对mPaaS移动网关请求的监控,支持全局注册和按请求注册两种方式。

全局注册方式

适用于需要监控所有mPaaS请求的场景:

import { MPFramework } from '@mpaas/framework';
import { MPRpc } from "@mpaas/rpc";
import tingyun from '@tingyun/sdk-core'

export default class MyAbilityStage extends AbilityStage {
onCreate(): void {
// 初始化mPaaS框架 (下面仅为示例, 具体初始化方式以实际项目为准)
MPFramework.create(this.context);

// 全局注册tingyun拦截器
MPRpc.addGlobalInterceptor(new tingyun.MPaaSRPCInterceptor())

// 初始化tingyun SDK
tingyun.init({
// 其他配置...
network: {
// 其他配置...
// 开启mPaaS移动网关请求监控
mPaaSEnabled: true
}
})
}
}

按请求注册方式

适用于仅监控特定请求的场景:

import tingyun from '@tingyun/sdk-core'

// 需要先在tingyun.init中开启mPaaS监控
tingyun.init({
// 其他配置...
network: {
// 其他配置...
// 开启mPaaS移动网关请求监控
mPaaSEnabled: true
}
})

// 在具体请求中注册拦截器
MPRpc.executeRpc<string>(this, {
// 其他配置...
// 注册拦截器
interceptor: new tingyun.MPaaSRPCInterceptor()
})

用户体验监控

registerUIContext (Deprecated)

注意:该接口已废弃。新版本 SDK 已支持自动采集,不再需要手动调用此接口。

API定义:

export Tingyun = {
/**
* @deprecated 请使用自动采集,不再需要手动注册
*/
registerUIContext(uiContext: UIContext)
}

操作监控

SDK支持采集用户点击事件

使用方式: 在需要采集的组件上设置id属性或添加自定义属性_ty_name来设置操作名称, 自定义属性的优先级高于id

示例:

Button('clickTest')
// 设置组件ID
.id('<自定义操作名称>')
// 自定义属性
.customProperty('_ty_name', '<自定义操作名称>')
.onClick(() => {
})

自定义操作

API定义:

export type ActionStartOptions = {
// 父级操作的key, 不传视为顶级操作
parent?: string
// 事件唯一标识, 不传则使用name作为唯一标识
key?: string
// 操作开始时间, 毫秒时间戳, 不传取调用时间
startTime?: number
}

export type ActionEndOptions = {
// 操作名称
name?: string
// 事件唯一标识, 不传则使用name作为唯一标识
key?: string
// 操作结束时间, 毫秒时间戳, 不传取调用时间
endTime?: number
// 自定义tag
tag?: string
// 自定义信息
metaData?: KeyValueStore<ValueType>
}

export interface IActionInstance {
end: (options?: ActionEndOptions) => void
readonly key: string
readonly name: string
}

export type Tingyun = {
// 开始用户操作
startAction: (name: string, options?: ActionStartOptions) => IActionInstance
// 结束用户操作
endAction: (options: ActionEndOptions) => void
}

使用方式:

示例1: 基本使用

// 开始操作
const action = tingyun.startAction('action1')
// ...
// 结束操作
action.end()

示例2: 传递自定义tag和附加信息

// 开始操作
const action = tingyun.startAction('action1')
// ...
// 结束操作
action.end({
// tag
tag: 'tag1',
// 附加信息
metaData: {
prop1: ''
}
})

示例3: 自定义开始时间和结束时间

// 开始操作
const action = tingyun.startAction('action1', {
startTime: <13位毫秒时间戳>
})
// ...
// 结束操作
action.end({
endTime: <13位毫秒时间戳>
})

自定义冷启动耗时结束点

API定义:

export type CustomLaunchOptions = {
className?: string
}

export type Tingyun = {
// 设置自定义冷启动耗时结束点
setCustomLaunchTime: (options?: CustomLaunchOptions) => void
}

使用方式:

  1. SDK启动时设置customLaunchEnd为true

    import tingyun from '@tingyun/sdk-core'

    tingyun.init({
    // ...
    ue: {
    customLaunchEnd: true
    }
    })
  2. 在冷启动结束时调用tingyun.setCustomLaunchTime

    import tingyun from '@tingyun/sdk-core'

    tingyun.setCustomLaunchTime()

Webview数据采集

SDK 提供以下 API 用于 Webview 数据采集:

export type Tingyun = {
// 注册 WebviewController
registerWebviewController: (controller: webview.WebviewController) => void
// 获取需要注入的 JS 脚本
getWebScriptItem: (scriptRules?: string[]) => any
}

原生 Webview

import tingyun from '@tingyun/sdk-core'

Web({ src: 'https://example.com', controller: this.controller })
// 确保 javaScriptAccess 和 domStorageAccess 都是开启状态
.javaScriptAccess(true)
.domStorageAccess(true)
.onControllerAttached(() => {
// onControllerAttached回调中调用tingyun.registerWebviewController
tingyun.registerWebviewController(this.controller)
})
// 注入webview监控所需的JS代码
.javaScriptOnDocumentStart([tingyun.getWebScriptItem()])

mPaaS Webview

mPaaS Webview 通过自定义 H5WebClientProvider 来集成监控能力。

  1. 实现自定义 Provider

    示例:

    import { H5WebClientProvider, Page } from '@mpaas/hriver'
    import tingyun from '@tingyun/sdk-core'

    export class H5WebClientProviderImpl extends H5WebClientProvider {
    onPageBegin(page: Page | undefined, url: string | undefined): void {
    const controller = page?.webcontroller
    if (controller) {
    try {
    // 注入tingyun web SDK以及采集webview网络请求的JS代码
    controller.runJavaScript(tingyun.getWebScriptItem().script)
    } catch (e) {
    console.error('Failed to inject Tingyun script:', e)
    }
    }
    }

    onControllerAttached(page: Page | undefined): void {
    const controller = page?.webcontroller
    if (controller) {
    // 注册jsBridge
    tingyun.registerWebviewController(controller)
    }
    }
    }
  2. 注册 Provider

    HRiver.setProvider(H5WebClientProvider.name, new H5WebClientProviderImpl())

自定义错误

API:

export const ErrorTypes = {
// 自定义错误
CUSTOM: 1,
// SDK捕获的错误
CAPTURED: 2
} as const
export type ErrorType = typeof ErrorTypes[keyof typeof ErrorTypes]

export type ErrorInfo = {
// 堆栈
stack?: string
// 线程id
threadId?: number
// 线程名称
threadName?: string
}

export type KeyValueStore<T> = Record<string, T> | Map<string, T>
export type ValueType = string | number | boolean | object

export type ErrorOptions = {
// 错误类型, 默认为自定义错误
type?: ErrorType
// 错误补充信息
error?: ErrorInfo
// 附加信息
metaData?: KeyValueStore<ValueType>
}

export type Tingyun = {
// 自定义错误
reportError: (message: string, options?: ErrorOptions) => void
}

使用方式:

import tingyun from '@tingyun/sdk-core'

tingyun.reportError('<customError>')

自定义用户附加信息

API:

export type Tingyun = {
setCustomData: (key: string, message: string) => void
}

使用方式:

import tingyun from '@tingyun/sdk-core'

tingyun.setCustomData('<key>', '<value>')

自定义面包屑

API:

export type Tingyun = {
leaveBreadcrumb: (breadcrumb: string) => void
}

使用方式:

import tingyun from '@tingyun/sdk-core'

tingyun.leaveBreadcrumb('<message>')

用户ID

API:

export type Tingyun = {
setUserId: (userId: string) => void
}

使用方式:

import tingyun from '@tingyun/sdk-core'

tingyun.setUserId('<userId>')

说明:

  1. SDK初始化结束之前如果多次设置用户ID,只会取最后一次设置的值

会话

切换会话

API:

export type Tingyun = {
startNextSession: (sessionId?: string) => void
}

使用方式:

import tingyun from '@tingyun/sdk-core'

tingyun.startNextSession()

设置会话闲置时长

API:

export type Tingyun = {
/**
* 设置会话空闲时间, 建议在SDK init前调用, 仅首次调用生效
* @param idleTime 会话空闲时间, 单位秒, 最低设置60, 可以设置为特殊值0,表示不进行会话闲置时长判断,仅应用退出或调用startNextSession时会切换会话
*/
setSessionIdleTime: (idleTime: number) => void
}

使用方式:

import tingyun from '@tingyun/sdk-core'

tingyun.setSessionIdleTime(<闲置时长>)

获取会话ID

API:

export type Tingyun = {
/**
* 获取会话id, 如果会话未初始化, 返回空字符串
* @returns
*/
getSessionId: () => string
}

使用方式:

import tingyun from '@tingyun/sdk-core'

tingyun.getSessionId()

经纬度

API:

export type Tingyun = {
setLatLon: (latitude: number, longitude: number) => void
}

使用方式:

import tingyun from '@tingyun/sdk-core'

tingyun.setLatLon(<纬度>, <经度>)

说明:

  1. 纬度有效值为 [-90, 90]
  2. 经度有效值为 [-180, 180]
  3. 经纬度其中之一传无效值,API调用无效

协议扩展

API:

export type Tingyun = {
/**
* 创建执行单元
* @param name 名称,不能为空,长度限制为1024字符,超过截取前1024字符
* @param operation 数据类型,不能为空,长度限制为128字符,超过截取前128字符
* @returns
*/
startSpan: (name: string, operation: string) => ISpan
}

export interface ISpan {
/**
* 完成执行单元
* @param status 执行状态, 默认为SpanStatuses.OK
*/
finish: (status?: SpanStatus) => void
/**
* 创建子执行单元
* @param name 名称,不能为空,长度限制为1024字符,超过截取前1024字符
* @param operation 数据类型,不能为空,长度限制为128字符,超过截取前128字符
* @param description 描述信息,长度限制为1024字符,超过截取前1024字符
* @returns
*/
startChild: (name: string, operation: string, description?: string) => ISpan
/**
* 设置Data数据
* @param key data的key
* @param value data的value
*/
setData: (key: string, value: Object) => void
/**
* 根据key移除Data
* @param key data的key
*/
removeData: (key: string) => void
/**
* 设置Tag数据
* @param key tag的key
* @param value tag的value
*/
setTag: (key: string, value: string) => void
/**
* 根据key移除Tag
* @param key tag的key
*/
removeTag: (key: string) => void
/**
* 设置Metric数据
* @param key metric的key
* @param value metric的value
* @param unit metric的单位
*/
setMetric: (key: string, value: number, unit?: string) => void
/**
* 根据key移除Metric
* @param key Metric的key
*/
removeMetric: (key: string) => void
/**
* 设置执行状态
* @param status
*/
setStatus: (status: SpanStatus) => void
/**
* 设置执行单元耗时, 单位ms
* @param duration
*/
setDuration: (duration: number) => void
/**
* 设置状态码
* @param code 状态码,默认为"0"
*/
setStatusCode: (code: string) => void
}

export const SpanStatuses = {
UNKNOWN: 0,
OK: 1,
ERROR: -1
} as const

export type SpanStatus = typeof SpanStatuses[keyof typeof SpanStatuses]

// 预定义单位
export const SpanMetricUnits = {
Bit: "b",
Byte: "B",
Kilobytes: "KB",
Megabytes: "MB",
Millisecond: "ms",
Second: "s",
Minute: "m",
Hour: "h",
Day: "d",
Bitps: "b/s",
Byteps: "B/s",
KBps: "KB/s",
MBps: "MB/s",
} as const

使用方式:

import tingyun, {SpanStatuses} from '@tingyun/sdk-core'

const span = tingyun.startSpan('span_name', 'span_operation')
span.setData('dataKey', 'dataValue')
span.setTag('tagKey', 'tagValue')
span.setMetric('time', 50, SpanMetricUnits.Millisecond)
span.setDuration(100)
span.setStatus(SpanStatuses.OK)
span.setStatusCode('200')

const childSpan = span.startChild('child_span_name', 'child_span_operation')
childSpan.finish()
span.finish()

自定义事件

API:

export type EventAttributeValue = string | number | boolean | { [key: string]: EventAttributeValue } | EventAttributeValue[]
export type EventAttributes = Record<string, EventAttributeValue>

export type Tingyun = {
/**
* 上报自定义事件
* @param name 事件名称, 最大长度1024, 超过最大长度将被截断
* @param attributes 事件属性, 最大总长度128KB
*/
reportEvent: (name: string, attributes?: EventAttributes) => void
}

使用方式:

import tingyun from '@tingyun/sdk-core'

tingyun.reportEvent('<eventName>', <attributes>)