Skip to main content

API

Network Request

Axios

SDK supports @ohos/axios library network request collection, and can use the following two methods to monitor axios:

  1. Pass in axios instance during tingyun.init
import tingyun from '@tingyun/sdk-core'
import axios from '@ohos/axios'

tingyun.init({
// Pass in axios instance object
axios: axios
})

  1. Use tingyun.registerAxiosInstance to register axios instance, and multiple instances can be registered

API definition:

export type Tingyun = {
registerAxiosInstance: (axiosInstance: AxiosInstance) => void
}

Example:

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

tingyun.registerAxiosInstance(axiosInstance1)
tingyun.registerAxiosInstance(axiosInstance2)
// ...

Description:

  1. Both methods can be used at the same time, and the same axios instance will only be registered once

  2. When starting for the first time, the SDK turns off the network request switch by default. If you need to monitor the network request at the first startup, you need to configure the network request switch to true by default during tingyun.init

tingyun.init({
network: {
enabled: true
}
})

RCP

The SDK supports monitoring of RCP (remote communication platform) by providing an interceptor. Users need to add the interceptor provided by the SDK when calling createSession

import tingyun from '@tingyun/sdk-core'

const session = rcp.createSession({
// ...
// Add interceptors
interceptors: [new tingyun.RCPInterceptor()]
})

SDK network request interface

The SDK encapsulates the HttpRequest.request interface of the system @ohos.net.http package and exposes the request interface. The network request sent by calling this interface will be collected by the SDK

API definition:

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

export type HttpRequestWrapperOptions = {
url: string
options?: http.HttpRequestOptions
// The request object returned by http.createHttp() will be created internally by the SDK if it is not passed. When httpRequest is not passed, the SDK will only call httpRequest.destroy synchronously after the callback is executed when the user passes in the callback. When httpRequest is passed in, the SDK will not call other methods except httpRequest.request. If you need to call other methods of httpRequest, control the destroy timing by yourself, and use the promise method, you need to pass in httpRequest
httpRequest?: http.HttpRequest
}
export interface HttpRequestWrapper {
(options: HttpRequestWrapperOptions, callback: AsyncCallback<http.HttpResponse>): void
(options: HttpRequestWrapperOptions): Promise<http.HttpResponse>
}

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

Usage example: Example 1: Pass URL (promise)

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

Example 2: Pass URL (callback)

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

Example 3: Pass URL and options (promise)

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

Example 4: Pass URL and options (callback)

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

Example 5: Use custom request object

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

Custom request

In order to support some custom network request libraries used by users, SDK provides a set of ArkTS layer APIs that can collect network library information collected by users themselves. In order to meet various usage scenarios, two usage modes are provided:

  1. Mode 1: Call SDK API when the request is initiated and after the request ends. SDK collects this network request when the request ends API is called

The first mode is divided into 2 usage methods:

  • Call tingyun.startRequest before the request starts and call tingyun.endRequest after the request ends. Both calls pass a self-maintained request ID to associate with the same request. This method is more flexible, but you need to maintain a request ID to identify a request. If the existing request library itself provides a request ID, or the code structure of the request start and end is scattered, this method is suitable.
  • Before the request starts, call tingyun.startRequest to get the request instance returned by the SDK. After the request ends, call the end method of the request instance to end the request. This method does not require you to maintain the request ID, but when the code structure is more complex, you may need to pass the request instance object yourself. If the logic of sending the request code is relatively simple and completed in a single function, this method is suitable.
  1. Mode 2: After the request ends or at any time, call tingyun.createRequest once to report a complete network request data

Mode 1: startRequest / endRequest

API definition:

export type Tingyun = {
// Start custom request
startRequest: (options: RequestStartOptions) => IRequestInstance
// End custom request
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 = {
// Request URL
url: string
// Request ID: Request unique identifier. If not passed, the SDK will automatically generate a UUID format string
id?: string
// Request start time: 13-digit millisecond timestamp. If not passed, the SDK will use the interface call time as the start time
startTime?: number
// method: GET by default
method?: RequestMethod
// Network library name
libName?: LibName
// Request header
reqHeaders?: KeyValueStore<string>
// Request body: Only text type is supported. If it is in JSON format, it needs to be serialized into a string and passed in
reqBody?: string
// Number of bytes sent
bytesSent?: number
// Request object timeout cleanup time, in milliseconds: If endRequest is not called after a period of time after starting the request, The SDK will automatically clean up the current request and release resources. The default value is 90000ms
timeout?: number
}

export type RequestEndOptions = {
// Request ID: Request unique identifier. Must be passed when using Tingyun.endRequest. Not passed when using the request instance object
id?: string
// http status code
statusCode?: number
// Error code: Error code for network error or other error. Request success or http error (4xx, 5xx) occurs. No need to pass
errorCode?: number
// Request end time: 13-digit millisecond timestamp. If not passed, the SDK will use the time of the interface call as the end point
endTime?: number
// Total request time, in milliseconds: If not passed, use endTime - startTime to calculate
duration?: number
// DNS time, in milliseconds: DNS end time - DNS start time
dns?: number
// Connection establishment time, in milliseconds: Connection establishment end time - connection establishment start time
connect?: number
// ssl handshake time, in milliseconds: ssl handshake end time - ssl handshake start time
ssl?: number
// first packet, in milliseconds: first packet end time - first packet start time
firstPacket?: number
// remaining packets, in milliseconds: remaining packet end time - remaining packet start time
remainPacket?: number
// local queue time, in milliseconds
localQueue?: number
// number of bytes sent: will overwrite bytesSent passed in startRequest
bytesSent?: number
// number of bytes received
bytesReceived?: number
// request target address server IP
ip?: string
// request header: will overwrite reqHeaders passed in startRequest
reqHeaders?: KeyValueStore<string>
// request body: only supports text type, if it is in JSON format, it needs to be serialized as a string and passed in. It will overwrite reqBody passed in startRequest
reqBody?: string
// return header
resHeaders?: KeyValueStore<string>
// Return body: Only text type return body is supported. If it is in JSON format, it needs to be serialized as a string.
resBody?: string
// Error message
errorMessage?: string
// Error stack
errorStack?: string
// Link tracking third-party apm request header
apms?: KeyValueStore<string>
// Contains the original return header and value of Tingyun APM
tyApm?: KeyValueStore<string>
}

Example 1: Use startRequest/endRequest to customize the request ID

import tingyun from '@tingyun/sdk-core'

async function testRequest1() {
// Custom request 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'
}
// Before the request starts, call startRequest to start the request
tingyun.startRequest({
id: reqId,
url: url,
method: method,
reqHeaders: reqHeaders,
reqBody: JSON.stringify(reqBody)
})

try {
// Call your own request library
const response = await callCustomHttpLib(url, {
method: method,
reqBody: reqBody,
reqHeaders: reqHeaders
})

// If the request is successful, call endRequest to end the request and pass the request ID that identifies the request
tingyun.endRequest({
id: reqId,
statusCode: response.statusCode,
resHeaders: response.headers,
resBody: response.body
})
} catch(e) {
// The request failed, call endRequest to end the request, passing the request ID that identifies the request
tingyun.endRequest({
id: reqId,
errorCode: e.code
})
}
}

Example 2: Use the request instance returned by startRequest to end the request

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'
}
// Before the request starts, call startRequest to start the request and get the request instance
const requestInstance = tingyun.startRequest({
url: url,
method: method,
reqHeaders: reqHeaders,
reqBody: JSON.stringify(reqBody)
})

try {
// Call your own request library
const response = await callCustomHttpLib(url, {
method: method,
reqBody: reqBody,
reqHeaders: reqHeaders
})

// Request successful, use the request instance's end method to end the request
requestInstance.end({
statusCode: response.statusCode,
resHeaders: response.headers,
resBody: response.body
})
} catch(e) {
// Request failed, use the request instance's end method to end the request
requestInstance.end({
errorCode: e.code
})
}
}

Mode 2: createRequest

API definition:

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

// RequestStartOptions RequestEndOptions See above
export type RequestOptions = Omit<RequestStartOptions & RequestEndOptions, 'id' | 'timeout'>

Example:

async function testRequest3() {
const url = 'https://example.com'
const method = 'POST'
const reqBody = {
key1: 1
}
const reqHeaders = {
'Content-Type': 'application/json; charset=UTF-8'
}
// Set the start time
const startTime = Date.now()
let endTime
try {
// Call your own request library
const response = await callCustomHttpLib(url, {
method: method,
reqBody: reqBody,
reqHeaders: reqHeaders
})

// Set the end time
endTime = Date.now()
// Request successful, call createRequest to create a custom request
tingyun.createRequest({
url: url,
startTime: startTime,
endTime: endTime,
statusCode: response.statusCode,
reqHeaders: reqHeaders,
resHeaders: response.headers,
resBody: response.body
})
} catch(e) {
// Set end time
endTime = Date.now()
// Request failed, call createRequest to create a custom request
tingyun.createRequest({
url: url,
startTime: startTime,
endTime: endTime,
errorCode: e.code
})
}
}

User experience monitoring

Register UIContext

Page routing and user operation monitoring require registering UIContext

API definition:

export Tingyun = {
registerUIContext(uiContext: UIContext)
}

Usage:

import tingyun from '@tingyun/sdk-core'

export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/Index', (err, data) => {

// Get the main window of the current UIAbility
windowStage.getMainWindow((err: BusinessError, data) => {
const uiContext = data.getUIContext()

// Register the UIContext of the current window
tingyun.registerUIContext(uiContext)
})
});
}
}

Operation monitoring

SDK supports collecting user click events

Usage:

  1. Register UIContext
  2. Set the id property on the component to be collected or add a custom property _ty_name to set the operation name. The custom property has a higher priority than id

Example:

Button('clickTest')
// Set component ID
.id('<custom operation name>')
// Custom property
.customProperty('_ty_name', '<custom operation name>')
.onClick(() => {
})

Custom operation

API definition:

export type ActionStartOptions = {
// Parent operation key, if not passed, it is considered as a top-level operation
parent?: string
// Event unique identifier, if not passed, name is used as the unique identifier
key?: string
// Operation start time, millisecond timestamp, call time is not passed
startTime?: number
}

export type ActionEndOptions = {
// Operation name
name?: string
// Event unique identifier, if not passed, name is used as the unique identifier
key?: string
// Operation end time, millisecond timestamp, call time is not passed
endTime?: number
// Custom tag
tag?: string
// Custom information
metaData?: KeyValueStore<ValueType>
}

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

export type Tingyun = {
// Start user operation
startAction: (name: string, options?: ActionStartOptions) => IActionInstance
// End user operation
endAction: (options: ActionEndOptions) => void
}

Usage:

Example 1: Basic use

// Start operation
const action = tingyun.startAction('action1')
// ...
// End the operation
action.end()

Example 2: Pass custom tags and additional information

// Start the operation
const action = tingyun.startAction('action1')
// ...
// End the operation
action.end({
// tag
tag: 'tag1',
// Additional information
metaData: {
prop1: ''
}
})

Example 3: Custom start and end time

// Start the operation
const action = tingyun.startAction('action1', {
startTime: <13-digit millisecond timestamp>
})
// ...
// End the operation
action.end({
endTime: <13-digit millisecond timestamp>
})

Customize the end point of cold start time consumption

API definition:

export type CustomLaunchOptions = {
className?: string
}

export type Tingyun = {
// Custom launch end time
setCustomLaunchTime: (options?: CustomLaunchOptions) => void
}

Usage:

  1. Set customLaunchEnd to true when SDK starts
import tingyun from '@tingyun/sdk-core'

tingyun.init({
// ...
ue: {
customLaunchEnd: true
}
})
  1. Call tingyun.setCustomLaunchTime at the end of cold start
import tingyun from '@tingyun/sdk-core'

tingyun.setCustomLaunchTime()

Webview data collection

API definition:

export type Tingyun = {
// Register webController
registerWebviewController: (controller: webview.WebviewController) => void
// Get the JS code to be injected
getWebScriptItem: (scriptRules?: string[]) => any
}

Usage:

import tingyun from '@tingyun/sdk-core'

Web({ src: 'xxxx', controller: this.controller })
// Make sure both javaScriptAccess and domStorageAccess are enabled
.javaScriptAccess(true)
.domStorageAccess(true)
.onControllerAttached(() => {
// Call tingyun.registerWebviewController in onControllerAttached callback
tingyun.registerWebviewController(this.controller)
})
// Inject JS code required for webview monitoring
.javaScriptOnDocumentStart([tingyun.getWebScriptItem()])

Custom error

API:

export const ErrorTypes = {
// Custom error
CUSTOM: 1,
// Error captured by SDK
CAPTURED: 2
} as const
export type ErrorType = typeof ErrorTypes[keyof typeof ErrorTypes]

export type ErrorInfo = {
// Stack
stack?: string
// Thread id
threadId?: number
// Thread name
threadName?: string
}

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

export type ErrorOptions = {
// Error type, default is custom error
type?: ErrorType
// Error supplementary information
error?: ErrorInfo
// Additional information
metaData?: KeyValueStore<ValueType>
}

export type Tingyun = {
// Custom error
reportError: (message: string, options?: ErrorOptions) => void
}

Usage:

import tingyun from '@tingyun/sdk-core'

tingyun.reportError('<customError>')

Customize user additional information

API:

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

Usage:

import tingyun from '@tingyun/sdk-core'

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

Customize breadcrumbs

API:

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

Usage:

import tingyun from '@tingyun/sdk-core'

tingyun.leaveBreadcrumb('<message>')

User ID

API:

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

Usage:

import tingyun from '@tingyun/sdk-core'

tingyun.setUserId('<userId>')

Description:

  1. If the user ID is set multiple times before the SDK is initialized, only the last value will be used

Switch session

API:

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

Usage:

import tingyun from '@tingyun/sdk-core'

tingyun.startNextSession()

Longitude and latitude

API:

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

Usage:

import tingyun from '@tingyun/sdk-core'

tingyun.setLatLon(<latitude>, <longitude>)

Description:

  1. The valid value of latitude is [-90, 90]

  2. The valid value of longitude is [-180, 180]

  3. If one of the latitude and longitude passes an invalid value, the API call is invalid

Protocol extension

API:

export type Tingyun = {
/**
* Create an execution unit
* @param name Name, cannot be empty, length limit is 1024 characters, more than 1024 characters are cut off
* @param operation Data type, cannot be empty, length limit is 128 characters, more than 128 characters are cut off
* @returns
*/
startSpan: (name: string, operation: string) => ISpan
}

export interface ISpan {
/**
* Finish the execution unit
* @param status Execution status, default is SpanStatuses.OK
*/
finish: (status?: SpanStatus) => void
/**
* Create a child execution unit
* @param name Name, cannot be empty, length limit is 1024 characters, more than 1024 characters are cut off
* @param operation Data type, cannot be empty, length limit is 128 characters, if it exceeds 128 characters, the first 128 characters will be cut off
* @param description description information, length limit is 1024 characters, if it exceeds 1024 characters, the first 1024 characters will be cut off
* @returns
*/
startChild: (name: string, operation: string, description?: string) => ISpan
/**
* Set Data data
* @param key data's key
* @param value data's value
*/
setData: (key: string, value: Object) => void
/**
* Remove Data according to key
* @param key data's key
*/
removeData: (key: string) => void
/**
* Set Tag data
* @param key tag's key
* @param value tag's value
*/
setTag: (key: string, value: string) => void
/**
* Remove Tag according to key
* @param key tag's key
*/
removeTag: (key: string) => void
/**
* Set Metric data
* @param key metric key
* @param value metric value
* @param unit metric unit
*/
setMetric: (key: string, value: number, unit?: string) => void
/**
* Remove Metric based on key
* @param key metric key
*/
removeMetric: (key: string) => void
/**
* Set execution status
* @param status
*/
setStatus: (status: SpanStatus) => void
/**
* Set execution unit duration, unit ms
* @param duration
*/
setDuration: (duration: number) => void
/**
* Set status code
* @param code status code, default is "0"
*/
setStatusCode: (code: string) => void
}

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

export type SpanStatus = typeof SpanStatuses[keyof typeof SpanStatuses]

//Predefined units
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

How to use:

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(SpanStatus.OK)
span.setStatusCode('200')
const childSpan = span.startChild('child_span_name', 'child_span_operation')
childSpan.finish()
span.finish()