Android Application Integration¶
By collecting metrics data from Android applications, analyze application performance visually.
Prerequisites¶
Note
If you have already activated the RUM Headless service, the prerequisites are automatically configured, and you can directly integrate the application.
- Install DataKit;
- Configure the RUM Collector;
- Configure DataKit to be accessible via the public network and install the IP Geolocation Database.
Application Integration¶
- Go to User Access Monitoring > Create Application > Android;
- Enter the application name;
- Enter the application ID;
-
Select the application integration method:
- Public DataWay: Directly receives RUM data without installing the DataKit collector.
- Local Environment Deployment: Receives RUM data after meeting the prerequisites.
Installation¶
Source Code: https://github.com/GuanceCloud/datakit-android
Demo: https://github.com/GuanceDemo/guance-app-demo
Gradle Configuration¶
- Add the
SDK
remote repository address in the root directory'sbuild.gradle
file.
buildscript {
//...
repositories {
//...
//Add the SDK remote repository address
maven {
url 'https://mvnrepo.jiagouyun.com/repository/maven-releases'
}
}
dependencies {
//...
//Add the Plugin dependency, requires AGP 7.4.2 or higher, Gradle 7.2.0 or higher
classpath 'com.cloudcare.ft.mobile.sdk.tracker.plugin:ft-plugin:[latest_version]'
// For AGP versions below 7.4.2, use ft-plugin-legacy
//classpath 'com.cloudcare.ft.mobile.sdk.tracker.plugin:ft-plugin-legacy:[latest_version]'
}
}
allprojects {
repositories {
//...
//Add the SDK remote repository address
maven {
url 'https://mvnrepo.jiagouyun.com/repository/maven-releases'
}
}
}
//setting.gradle
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
//Add the SDK remote repository address
maven {
url('https://mvnrepo.jiagouyun.com/repository/maven-releases')
}
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
//Add the SDK remote repository address
maven {
url('https://mvnrepo.jiagouyun.com/repository/maven-releases')
}
}
}
//build.gradle
plugins{
//Add the Plugin dependency, requires AGP 7.4.2 or higher, Gradle 7.2.0 or higher
id 'com.cloudcare.ft.mobile.sdk.tracker.plugin' version '[lastest_version]' apply false
// For AGP versions below 7.4.2, use ft-plugin-legacy
//id 'com.cloudcare.ft.mobile.sdk.tracker.plugin.legacy' version '[lastest_version]' apply false
}
- Add the
SDK
dependency andPlugin
usage in the main moduleapp
'sbuild.gradle
file.
dependencies {
//Add the SDK dependency
implementation 'com.cloudcare.ft.mobile.sdk.tracker.agent:ft-sdk:[latest_version]'
//Dependency for capturing native layer crash information, must be used with ft-sdk, cannot be used alone
implementation 'com.cloudcare.ft.mobile.sdk.tracker.agent:ft-native:[latest_version]'
// json serialization
implementation 'com.google.code.gson:gson:2.8.+'
//Optional, required if automatic network request collection and automatic trace are needed, minimum compatible version 3.12.+
implementation 'com.squareup.okhttp3:okhttp:4.+'
}
//Apply the plugin in the app build.gradle, missing configuration will affect the following automatic collection features.
//
// Plugin` configuration purpose:
// * Automatic configuration collection: App startup, OkHttp requests, WebView activities, Activity jumps
// * Automatic collection of View click events, Console Logcat
// * Automatic collection of Console Logcat
apply plugin: 'ft-plugin' //If using ft-plugin-legacy, use the same configuration
//Configure plugin usage parameters (optional)
FTExt {
//Whether to display Plugin logs, default is false
//showLog = true
//Set ASM version, supports asm7 - asm9, default is asm9
//asmVersion='asm7'
//ASM ignore path configuration, . and / are equivalent in the path
//ignorePackages=['com.ft','com/ft']
}
android{
//...omitted code
defaultConfig {
//...omitted code
ndk {
//When using ft-native to capture native layer crash information, select the supported abi architecture based on the application's adaptation to different platforms
//Currently, ft-native includes the following abi architectures: 'arm64-v8a',
// 'armeabi-v7a', 'x86', 'x86_64'
abiFilters 'armeabi-v7a'
}
}
compileOptions {
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
}
For the latest version, refer to the version names of ft-sdk, ft-plugin, and ft-native above.
Application Configuration¶
The best place to initialize the SDK is in the Application
's onCreate
method. If your application does not yet have an Application
, you need to create one and declare it in AndroidManifest.xml
. For an example, refer to here.
R8 / Proguard Obfuscation Configuration¶
If you need to set minifyEnabled
= true in android.buildTypes
, you need to enable the following configuration:
-dontwarn com.ft.sdk.**
### ft-sdk library
-keep class com.ft.sdk.**{*;}
### ft-native library
-keep class ftnative.*{*;}
### Prevent Action class names from being obfuscated in action_name ###
-keepnames class * extends android.view.View
-keepnames class * extends android.view.MenuItem
SDK Initialization¶
Basic Configuration¶
public class DemoApplication extends Application {
@Override
public void onCreate() {
//Local environment deployment, Datakit deployment
FTSDKConfig config = FTSDKConfig.builder(datakitUrl);
//Use public DataWay
FTSDKConfig config = FTSDKConfig.builder(datawayUrl, clientToken);
//...
//config.setDebug(true); //debug mode
config.setCompressIntakeRequests(true); //Compress intake requests
FTSdk.install(config);
}
}
class DemoApplication : Application() {
override fun onCreate() {
//Local environment deployment, Datakit deployment
val config = FTSDKConfig.builder(datakitUrl)
//Use public DataWay
val config = FTSDKConfig.builder(datawayUrl, clientToken)
//...
//config.setDebug(true); //debug mode
config.setCompressIntakeRequests(true) //Compress intake requests
FTSdk.install(config)
}
}
Method Name | Type | Required | Description |
---|---|---|---|
datakitUrl | String | Yes | Local environment deployment (Datakit) intake URL, example: http://10.0.0.1:9529, default port is 9529, the device installing the SDK must be able to access this address. Note: Only one of datakitUrl and datawayUrl should be configured |
datawayUrl | String | Yes | Public DataWay intake URL, obtained from the [User Access Monitoring] application, example: https://open.dataway.url, the device installing the SDK must be able to access this address. Note: Only one of datakitUrl and datawayUrl should be configured |
clientToken | String | Yes | Authentication token, must be configured with datawayUrl |
setDebug | Boolean | No | Whether to enable debug mode. Default is false , enabling it will print SDK runtime logs |
setEnv | EnvType | No | Set the intake environment, default is EnvType.PROD , |
setEnv | String | No | Set the intake environment, default is prod . Note: Only one of String or EnvType type needs to be configured |
setOnlySupportMainProcess | Boolean | No | Whether to only support running in the main process, default is true , set to false if execution in other processes is needed |
setEnableAccessAndroidID | Boolean | No | Enable access to Android ID , default is true , set to false to stop collecting device_uuid field data, related to market privacy audits see here |
addGlobalContext | Dictionary | No | Add SDK global attributes, for adding rules see here |
setServiceName | String | No | Set the service name, affects the service field data in Log and RUM, default is df_rum_android |
setAutoSync | Boolean | No | Whether to automatically sync data to the server after collection, default is true . When false , use FTSdk.flushSyncData() to manage data sync manually |
setSyncPageSize | Int | No | Set the number of entries per sync request, SyncPageSize.MINI 5 entries, SyncPageSize.MEDIUM 10 entries, SyncPageSize.LARGE 50 entries, default is SyncPageSize.MEDIUM |
setCustomSyncPageSize | Enum | No | Set the number of entries per sync request, range [5,), note that larger entry numbers mean more computational resources are used for data sync, default is 10 Note: Only one of setSyncPageSize and setCustomSyncPageSize needs to be configured |
setSyncSleepTime | Int | No | Set the sync interval time, range [0,5000], default is not set |
enableDataIntegerCompatible | Void | No | Recommended to enable when coexisting with web data. This configuration is used to handle web data type storage compatibility issues. Enabled by default in version 1.6.9 |
setNeedTransformOldCache | Boolean | No | Whether to be compatible with old cache data from ft-sdk versions below 1.6.0, default is false |
setCompressIntakeRequests | Boolean | No | Compress intake sync data with deflate, default is off, supported in ft-sdk 1.6.3 and above |
enableLimitWithDbSize | Void | No | Enable limiting data size with db, default is 100MB, unit is Byte, larger database means more disk pressure, default is off. Note: After enabling, FTLoggerConfig.setLogCacheLimitCount and FTRUMConfig.setRumCacheLimitCount will be invalid. Supported in ft-sdk 1.6.6 and above |
setEnableOkhttpRequestTag | bool | No | Automatically add unique ResourceID to Okhttp Request, for scenarios with high concurrency of the same request. Supported in ft-sdk 1.6.10 and above, ft-plugin 1.3.5 and above |
setProxy | java.net.Proxy | No | Set Proxy for data network sync requests, only supports okhttp3, supported in ft-sdk 1.6.10 and above |
setProxyAuthenticator | okhttp3.Authenticator | No | Set Proxy Authenticator for data sync network requests, only supports okhttp3, supported in ft-sdk 1.6.10 and above |
setDns | okhttp3.Dns | No | Support custom Dns for domain name resolution in data sync network requests, only supports okhttp3, supported in ft-sdk 1.6.10 and above |
setDataModifier | DataModifier | No | Modify a single field. Supported in ft-sdk 1.6.11 and above, for usage examples see here |
setLineDataModifier | LineDataModifier | No | Modify a single line of data. Supported in ft-sdk 1.6.11 and above, for usage examples see here |
setRemoteConfiguration | Int | No | Whether to enable remote configuration for data collection, default is false . After enabling, SDK initialization or application hot start will trigger data updates. Supported in ft-sdk 1.6.12 and above. Datakit version requirement >=1.60 or use public dataway |
setRemoteConfigMiniUpdateInterval | Int | No | Set the minimum update interval for data updates, in seconds, default is 12 hours. Supported in ft-sdk 1.6.12 and above |
RUM Configuration¶
FTSdk.initRUMWithConfig(
new FTRUMConfig()
.setRumAppId(RUM_APP_ID)
.setEnableTraceUserView(true)
.setDeviceMetricsMonitorType(DeviceMetricsMonitorType.ALL.getValue())
.setEnableTraceUserAction(true)
.setEnableTraceUserResource(true)
.setEnableTrackAppUIBlock(true)
.setEnableTrackAppCrash(true)
.setEnableTrackAppANR(true)
.setExtraMonitorTypeWithError(ErrorMonitorType.ALL.getValue())
);
FTSdk.initRUMWithConfig(
FTRUMConfig()
.setRumAppId(RUM_APP_ID)
.setEnableTraceUserView(true)
.setDeviceMetricsMonitorType(DeviceMetricsMonitorType.ALL.getValue())
.setEnableTraceUserAction(true)
.setEnableTraceUserResource(true)
.setEnableTrackAppUIBlock(true)
.setEnableTrackAppCrash(true)
.setEnableTrackAppANR(true)
.setExtraMonitorTypeWithError(ErrorMonitorType.ALL.getValue())
)
Method Name | Type | Required | Description |
---|---|---|---|
setRumAppId | String | Yes | Set Rum AppId . Corresponds to setting RUM appid , which enables RUM collection, get appid method |
setSamplingRate | Float | No | Set collection rate, range [0,1], 0 means no collection, 1 means full collection, default is 1. Scope is all View, Action, LongTask, Error data under the same session_id |
setSessionErrorSampleRate | Float | No | Set error collection rate, when a session is not sampled by setSamplingRate , if an error occurs during the session, data from 1 minute before the error can be collected, range [0,1], 0 means no collection, 1 means full collection, default is 0. Scope is all View, Action, LongTask, Error data under the same session_id. Supported in ft-sdk 1.6.11 and above |
setEnableTrackAppCrash | Boolean | No | Whether to report App crash logs, default is false , enabling it will display error stack data in error analysis.About retracing obfuscated content in crash logs. In ft-sdk 1.5.1 and above, you can use extraLogCatWithJavaCrash , extraLogCatWithNativeCrash to display logcat in Java Crash and Native Crash |
setExtraMonitorTypeWithError | Array | No | Set auxiliary monitoring information, add additional monitoring data to Rum crash data, ErrorMonitorType.BATTERY for battery level, ErrorMonitorType.MEMORY for memory usage, ErrorMonitorType.CPU for CPU usage, default is not set |
setDeviceMetricsMonitorType | Array | No | Set View monitoring information, add monitoring data during View lifecycle, DeviceMetricsMonitorType.BATTERY monitors the highest output current of the current page, DeviceMetricsMonitorType.MEMORY monitors the current application memory usage, DeviceMetricsMonitorType.CPU monitors CPU jumps, DeviceMetricsMonitorType.FPS monitors screen frame rate. Monitoring cycle, DetectFrequency.DEFAULT 500 ms, DetectFrequency.FREQUENT 100 ms, DetectFrequency.RARE 1 second, default is not set |
setEnableTrackAppANR | Boolean | No | Whether to enable ANR detection, default is false .In ft-sdk 1.5.1 and above, you can use extraLogCatWithANR to display logcat in ANR |
setEnableTrackAppUIBlock | Boolean, long | No | Whether to enable UI block detection, default is false , in ft-sdk 1.6.4 and above, you can use blockDurationMs to control the detection time range [100,), unit is ms, default is 1 second |
setEnableTraceUserAction | Boolean | No | Whether to automatically track user actions, currently only supports user startup and click actions, default is false |
setEnableTraceUserView | Boolean | No | Whether to automatically track user page actions, default is false |
setEnableTraceUserViewInFragment | Boolean | No | Whether to automatically track Fragment type page data, default is false , supported in ft-sdk 1.6.11 and above |
setEnableTraceUserResource | Boolean | No | Whether to automatically track user network requests, only supports Okhttp , default is false |
setEnableResourceHostIP | Boolean | No | Whether to collect the IP address of the request target domain. Scope: only affects the default collection when EnableTraceUserResource is true. For custom Resource collection, use FTResourceEventListener.FTFactory(true) to enable this feature. Additionally, a single Okhttp caches the IP for the same domain, the same OkhttpClient will only generate once if the server IP does not change |
setResourceUrlHandler | Callback | No | Set conditions for filtering Resources, default is no filtering |
setOkHttpEventListenerHandler | Callback | No | ASM set global Okhttp EventListener, default is not set |
setOkHttpResourceContentHandler | Callback | No | ASM set global FTResourceInterceptor.ContentHandlerHelper , default is not set, supported in ft-sdk 1.6.7 and above, custom Resource |
addGlobalContext | Dictionary | No | Add custom tags for distinguishing user monitoring data sources, if tracing is needed, the parameter key should be track_id , value can be any value, for adding rules see here |
setRumCacheLimitCount | int | No | Local cache RUM limit count [10_000,), default is 100_000. Supported in ft-sdk 1.6.6 and above |
setEnableTraceWebView | Boolean | No | Whether to enable WebView data collection via Android SDK, default is true. Supported in ft-sdk 1.6.12 and above |
setAllowWebViewHost | Array | No | Set allowed WebView host addresses for data tracking, null means all are collected, default is null. Supported in ft-sdk 1.6.12 and above |
setViewActivityTrackingHandler | FTViewActivityTrackingHandler | No | Used to customize how Activity views are tracked. When an Activity lifecycle event occurs, this handler is called to decide how to track the Activity, default is no processing. Supported in ft-sdk 1.6.13 and above |
setViewFragmentTrackingHandler | FTViewFragmentTrackingHandler | No | Used to customize how Fragment views are tracked. When a Fragment lifecycle event occurs, this handler is called to decide how to track the Fragment. Supported in ft-sdk 1.6.13 and above |
setActionTrackingHandler | FTActionTrackingHandler | No | Used to customize how user actions (app startup, clicks) are tracked. When a user performs an action, this handler is called to decide how to track the action, default is no processing. Supported in ft-sdk 1.6.13 and above |
Log Configuration¶
Method Name | Type | Required | Description |
---|---|---|---|
setSamplingRate | Float | No | Set collection rate, range [0,1], 0 means no collection, 1 means full collection, default is 1. |
setEnableConsoleLog | Boolean | No | Whether to report console logs, default false , log level correspondenceLog.v -> ok; Log.i -> info; Log.d -> debug; Log.e -> error; Log.w -> warning, prefix is the console prefix filter parameter, default is not set. Note: Android console logs are voluminous, to avoid affecting application performance and unnecessary resource waste, it is recommended to use prefix to filter valuable logs. ft-plugin 1.3.5 and above, supports capturing Log.println logs |
setEnableLinkRUMData | Boolean | No | Whether to link with RUM data, default is false |
setEnableCustomLog | Boolean | No | Whether to upload custom logs, default is false |
setLogLevelFilters | Array | No | Set log level filters, default is not set |
addGlobalContext | Dictionary | No | Add log global attributes, for adding rules see here |
setLogCacheLimitCount | Int | No | Local cache maximum log entry count limit [1000,), larger logs mean more disk cache pressure, default is 5000 |
setLogCacheDiscardStrategy | LogCacheDiscard | No | Set log discard rules when the limit is reached, default is LogCacheDiscard.DISCARD , DISCARD discards additional data, DISCARD_OLDEST discards old data |
Trace Configuration¶
Method Name | Type | Required | Description |
---|---|---|---|
setSamplingRate | Float | No | Set collection rate, range [0,1], 0 means no collection, 1 means full collection, default is 1. |
setTraceType | TraceType | No | Set trace type, default is DDTrace , currently supports Zipkin , Jaeger , DDTrace , Skywalking (8.0+), TraceParent (W3C), if integrating with OpenTelemetry, please check the supported types and agent configurations |
setEnableLinkRUMData | Boolean | No | Whether to link with RUM data, default is false |
setEnableAutoTrace | Boolean | No | Whether to enable automatic http trace, currently only supports OKhttp automatic tracing, default is false |
setOkHttpTraceHeaderHandler | Callback | No | ASM set global FTTraceInterceptor.HeaderHandler , default is not set, supported in ft-sdk 1.6.8 and above, example reference custom Trace |
RUM User Data Tracking¶
In FTRUMConfig
configure enableTraceUserAction
, enableTraceUserView
, enableTraceUserResource
,setEnableTrackAppUIBlock
,setEnableTrackAppCrash
and setEnableTrackAppANR
to achieve automatic collection tracking of Action
, View
, Resource
, LongTask
, Error
data. For custom collection, you can use FTRUMGlobalManager
to report data, as shown below:
Action¶
Usage¶
/**
* Add Action
*
* @param actionName action name
* @param actionType action type
* @param property additional property parameters (optional)
*/
public void startAction(String actionName, String actionType, HashMap<String, Object> property)
/**
* Add Action, this type of data cannot be linked with Error, Resource, LongTask data
*
* @param actionName action name
* @param actionType action type
* @param duration nanoseconds, duration (optional)
* @param property additional properties (optional)
*/
public void addAction(String actionName, String actionType, long duration, HashMap<String, Object> property)
/**
* Add action
*
* @param actionName action name
* @param actionType action type
* @param property additional property parameters (optional)
*/
fun startAction(actionName: String, actionType: String, property: HashMap<String, Any>)
/**
* Add Action
*
* @param actionName action name
* @param actionType action type
* @param duration nanoseconds, duration (optional)
* @param property additional properties (optional)
*/
fun addAction(actionName: String, actionType: String, duration: Long, property: HashMap<String, Any>)
startAction internally calculates time consumption, during which it tries to link with nearby Resource, LongTask, Error data, with a 100 ms frequent trigger protection, recommended for user operation type data. For frequent calls, use addAction, this data will not conflict with startAction and will not link with current Resource, LongTask, Error data
Code Example¶
// Scenario 1
FTRUMGlobalManager.get().startAction("login", "action_type");
// Scenario 2: Dynamic parameters
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key", "ft_value");
FTRUMGlobalManager.get().startAction("login", "action_type", map);
// Scenario 1
FTRUMGlobalManager.get().addAction("login", "action_type");
// Scenario 2: Dynamic parameters
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key", "ft_value");
FTRUMGlobalManager.get().addAction("login", "action_type", map);
// Scenario 1
FTRUMGlobalManager.get().startAction("login", "action_type")
// Scenario 2: Dynamic parameters
val map = HashMap<String,Any>()
map["ft_key"]="ft_value"
FTRUMGlobalManager.get().startAction("login","action_type",map)
// Scenario 1
FTRUMGlobalManager.get().startAction("login", "action_type")
// Scenario 2: Dynamic parameters
val map = HashMap<String,Any>()
map["ft_key"]="ft_value"
FTRUMGlobalManager.get().startAction("login","action_type",map)
View¶
Usage¶
/**
* view start
*
* @param viewName current page name
* @param property additional property parameters (optional)
*/
public void startView(String viewName, HashMap<String, Object> property)
/**
* view end
*
* @param property additional property parameters (optional)
*/
public void stopView(HashMap<String, Object> property)
/**
* Update current view loading_time metric, unit nanoseconds,
* @param duration
*/
public void updateLoadTime(long duration)
/**
* view start
*
* @param viewName current page name
* @param property additional property parameters (optional)
*/
fun startView(viewName: String, property: HashMap<String, Any>)
/**
* view end
*
* @param property additional property parameters (optional)
*/
fun stopView(property: HashMap<String, Any>)
/**
* Update current view loading_time metric, unit nanoseconds
* @param duration
*/
fun updateLoadTime(duration: Long)
Code Example¶
@Override
protected void onResume() {
super.onResume();
// Scenario 1
FTRUMGlobalManager.get().startView("Current Page Name");
// Scenario 2: Dynamic parameters
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key", "ft_value");
map.put("ft_key_will_change", "ft_value");
FTRUMGlobalManager.get().startView("Current Page Name", map);
}
@Override
protected void onPause() {
super.onPause();
// Scenario 1
FTRUMGlobalManager.get().stopView();
// Scenario 2 : Dynamic parameters
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key_will_change", "ft_value_change"); //ft_key_will_change value will be modified to ft_value_change in stopView
FTRUMGlobalManager.get().startView("Current Page Name", map);
}
override fun onResume() {
super.onResume()
// Scenario 1
FTRUMGlobalManager.get().startView("Current Page Name")
// Scenario 2: Dynamic parameters
val map = HashMap<String, Any>()
map["ft_key"] = "ft_value"
map["ft_key_will_change"] = "ft_value"
FTRUMGlobalManager.get().startView("Current Page Name", map)
}
override fun onPause() {
super.onPause()
// Scenario 1
FTRUMGlobalManager.get().stopView()
// Scenario 2 : Dynamic parameters
val map = HashMap<String, Any>()
map["ft_key_will_change"] = "ft_value_change" //ft_key_will_change value will be modified to ft_value_change in stopView
FTRUMGlobalManager.get().startView("Current Page Name", map)
}
Error¶
Usage¶
/**
* Add error information
*
* @param log log
* @param message message
* @param errorType error type, ErrorType
* @param state program running state
* @param dateline occurrence time, nanoseconds (optional)
* @param property additional properties (optional)
*/
public void addError(String log, String message, long dateline, ErrorType errorType,
AppState state, HashMap<String, Object> property)
/**
* Add error information
*
* @param log log
* @param message message
* @param errorType error type, String
* @param state program running state
* @param dateline occurrence time, nanoseconds (optional)
* @param property additional properties (optional)
*/
public void addError(String log, String message, long dateline, String errorType,
AppState state, HashMap<String, Object> property)
/**
* Add error information
*
* @param log log
* @param message message
* @param errorType error type, ErrorType
* @param state program running state
* @param dateline occurrence time, nanoseconds (optional)
* @param property additional properties (optional)
*/
fun addError(log: String, message: String, dateline: Long, errorType: ErrorType,state: AppState, property: HashMap<String, Any>)
/**
* Add error information
*
* @param log log
* @param message message
* @param errorType error type, String
* @param state program running state
* @param dateline occurrence time, nanoseconds (optional)
* @param property additional properties (optional)
*/
fun addError(log: String, message: String, dateline: Long, errorType: String,state: AppState, property: HashMap<String, Any>)
Code Example¶
// Scenario 1:
FTRUMGlobalManager.get().addError("error log", "error msg", ErrorType.JAVA, AppState.RUN);
// Scenario 2: Delay recording the occurred error, here the time is usually the time the error occurred
FTRUMGlobalManager.get().addError("error log", "error msg", 16789000000000000000L, ErrorType.JAVA, AppState.RUN);
// Scenario 3:Dynamic parameters
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key", "ft_value");
FTRUMGlobalManager.get().addError("error log", "error msg", ErrorType.JAVA, AppState.RUN, map);
// Scenario 1:
FTRUMGlobalManager.get().addError("error log", "error msg", ErrorType.JAVA, AppState.RUN)
// Scenario 2: Delay recording the occurred error, here the time is usually the time the error occurred
FTRUMGlobalManager.get().addError("error log", "error msg", 16789000000000000000, ErrorType.JAVA, AppState.RUN)
// Scenario 3:Dynamic parameters
val map = HashMap<String, Any>()
map["ft_key"] = "ft_value"
FTRUMGlobalManager.get().addError("error log", "error msg",ErrorType.JAVA,AppState.RUN,map)
LongTask¶
Usage¶
Code Example¶
Resource¶
Usage¶
/**
* resource start
*
* @param resourceId resource Id
* @param property additional property parameters (optional)
*/
public void startResource(String resourceId, HashMap<String, Object> property)
/**
* resource end
*
* @param resourceId resource Id
* @param property additional property parameters (optional)
*/
public void stopResource(final String resourceId, HashMap<String, Object> property)
/**
* Set network transmission content
*
* @param resourceId
* @param params
* @param netStatusBean
*/
public void addResource(String resourceId, ResourceParams params, NetStatusBean netStatusBean)
/**
* resource start
*
* @param resourceId resource Id(optional)
*/
fun startResource(resourceId: String, property: HashMap<String, Any>)
/**
* resource end
*
* @param resourceId resource Id
* @param property additional property parameters (optional)
*/
fun stopResource(resourceId: String, property: HashMap<String, Any>)
/**
* Set network transmission content
*
* @param resourceId
* @param params
* @param netStatusBean
*/
fun addResource(resourceId: String, params: ResourceParams, netStatusBean: NetStatusBean)
Code Example¶
// Scenario 1
// Request start
FTRUMGlobalManager.get().startResource("resourceId");
//...
// Request end
FTRUMGlobalManager.get().stopResource("resourceId");
// Finally, after the request ends, send the request-related data metrics
ResourceParams params = new ResourceParams();
params.setUrl("https://truewatch.com");
params.setResponseContentType(response.header("Content-Type"));
params.setResponseConnection(response.header("Connection"));
params.setResponseContentEncoding(response.header("Content-Encoding"));
params.setResponseHeader(response.headers().toString());
params.setRequestHeader(request.headers().toString());
params.setResourceStatus(response.code());
params.setResourceMethod(request.method());
NetStatusBean bean = new NetStatusBean();
bean.setTcpStartTime(60000000);
//...
FTRUMGlobalManager.get().addResource("resourceId", params, bean);
// Scenario 2 :Dynamic parameters usage
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key", "ft_value");
map.put("ft_key_will_change", "ft_value");
FTRUMGlobalManager.get().startResource("resourceId",map);
//...
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key_will_change", "ft_value_change"); //ft_key_will_change value will be modified to ft_value_change in stopResource
FTRUMGlobalManager.get().stopResource(uuid,map);
// Scenario 1
//Request start
FTRUMGlobalManager.get().startResource("resourceId")
//Request end
FTRUMGlobalManager.get().stopResource("resourceId")
//Finally, after the request ends, send the request-related data metrics
val params = ResourceParams()
params.url = "https://truewatch.com"
params.responseContentType = response.header("Content-Type")
arams.responseConnection = response.header("Connection")
params.responseContentEncoding = response.header("Content-Encoding")
params.responseHeader = response.headers.toString()
params.requestHeader = request.headers.toString()
params.resourceStatus = response.code
params.resourceMethod = request.method
val bean = NetStatusBean()
bean.tcpStartTime = 60000000
//...
FTRUMGlobalManager.get().addResource("resourceId",params,bean)
// Scenario 2 :Dynamic parameters usage
val map = hashMapOf<String, Any>(
"ft_key" to "ft_value",
"ft_key_will_change" to "ft_value"
)
FTRUMGlobalManager.get().startResource("resourceId", map)
//...
val map = hashMapOf<String, Any>(
"ft_key_will_change" to "ft_value_change"
)
// ft_key_will_change value will be modified to ft_value_change in stopResource
FTRUMGlobalManager.get().stopResource(uuid, map)
Method Name | Required | Description | Notes |
---|---|---|---|
NetStatusBean.fetchStartTime | No | Request start time | |
NetStatusBean.tcpStartTime | No | tcp connection time | |
NetStatusBean.tcpEndTime | No | tcp end time | |
NetStatusBean.dnsStartTime | No | dns start time | |
NetStatusBean.dnsEndTime | No | dns end time | |
NetStatusBean.responseStartTime | No | Response start time | |
NetStatusBean.responseEndTime | No | Response end time | |
NetStatusBean.sslStartTime | No | ssl start time | |
NetStatusBean.sslEndTime | No | ssl end time | |
NetStatusBean.property | No | Additional properties | |
ResourceParams.url | Yes | url address | |
ResourceParams.requestHeader | No | Request header parameters | |
ResourceParams.responseHeader | No | Response header parameters | |
ResourceParams.responseConnection | No | Response connection | |
ResourceParams.responseContentType | No | Response ContentType | |
ResourceParams.responseContentEncoding | No | Response ContentEncoding | |
ResourceParams.resourceMethod | No | Request method | GET,POST, etc. |
ResourceParams.responseBody | No | Return body content | |
ResourceParams.property | No | Additional properties |
Logger Log Printing¶
Use FTLogger
for custom log output, requires enabling FTLoggerConfig.setEnableCustomLog(true)
.
Currently, log content is limited to 30 KB, exceeding characters will be truncated.
Usage¶
/**
* Store single log data locally and sync
*
* @param content log content
* @param status log level, enum Status
* @param property additional properties (optional)
*/
public void logBackground(String content, Status status, HashMap<String, Object> property)
/**
* Store single log data locally and sync
*
* @param content log content
* @param status log level, String
* @param property additional properties (optional)
*/
public void logBackground(String content, String status, HashMap<String, Object> property)
/**
* Store multiple log data locally and sync
*
* @param logDataList {@link LogData} list
*/
public void logBackground(List<LogData> logDataList)
/**
* Store single log data locally and sync
*
* @param content log content
* @param status log level
* @param property log properties (optional)
*/
fun logBackground(content: String, status: Status, property: HashMap<String, Any>)
/**
* Store single log data locally and sync
*
* @param content log content
* @param status log level
* @param property log properties (optional)
*/
fun logBackground(content: String, status: String, property: HashMap<String, Any>)
/**
* Store multiple log data locally and sync
*
* @param logDataList log data list
*/
fun logBackground(logDataList: List<LogData>)
Log Levels¶
Method Name | Description |
---|---|
Status.DEBUG | Debug |
Status.INFO | Info |
Status.WARNING | Warning |
Status.ERROR | Error |
Status.CRITICAL | Critical |
Status.OK | Recovery |
Code Example¶
// Upload single log
FTLogger.getInstance().logBackground("test", Status.INFO);
// Pass parameters to HashMap
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key", "ft_value");
FTLogger.getInstance().logBackground("test", Status.INFO, map);
// Batch upload logs
List<LogData> logList = new ArrayList<>();
logList.add(new LogData("test", Status.INFO));
FTLogger.getInstance().logBackground(logList);
//Upload single log
FTLogger.getInstance().logBackground("test", Status.INFO)
//Pass parameters to HashMap
val map = HashMap<String,Any>()
map["ft_key"]="ft_value"
FTLogger.getInstance().logBackground("test", Status.INFO,map)
//Batch upload logs
FTLogger.getInstance().logBackground(mutableListOf(LogData("test",Status.INFO)))
Tracer Network Trace¶
FTTraceConfig
configures enableAutoTrace
to automatically add trace data, or manually use FTTraceManager
in Http requests Propagation Header
, as shown below:
String url = "https://request.url";
String uuid = "uuid";
// Get trace header parameters
Map<String, String> headers = FTTraceManager.get().getTraceHeader(uuid, url);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(chain -> {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder();
// Add trace header parameters in the request
for (String key : headers.keySet()) {
requestBuilder.header(key, headers.get(key));
}
Request request = requestBuilder.build();
Response response = chain.proceed(request);
if (response != null) {
Map<String, String> requestHeaderMap = new HashMap<>();
Map<String, String> responseHeaderMap = new HashMap<>();
for (Pair<String, String> header : response.request().headers()) {
requestHeaderMap.put(header.first, header.second);
}
for (Pair<String, String> header : response.headers()) {
responseHeaderMap.put(header.first, header.second);
}
}
return response;
}).build();
Request.Builder builder = new Request.Builder().url(url).method(RequestMethod.GET.name(), null);
client.newCall(builder.build()).execute();
val url = "https://request.url"
val uuid ="uuid"
//Get trace header parameters
val headers = FTTraceManager.get().getTraceHeader(uuid, url)
val client: OkHttpClient = OkHttpClient.Builder().addInterceptor { chain ->
val original = chain.request()
val requestBuilder = original.newBuilder()
//Add trace header parameters in the request
for (key in headers.keys) {
requestBuilder.header(key!!, headers[key]!!)
}
val request = requestBuilder.build()
response = chain.proceed(request)
if (response != null) {
val requestHeaderMap = HashMap<String, String>()
val responseHeaderMap = HashMap<String, String>()
request.headers.forEach {
requestHeaderMap[it.first] = it.second
}
response!!.headers.forEach {
responseHeaderMap[it.first] = it.second
}
}
response!!
}.build()
val builder: Request.Builder = Request.Builder().url(url).method(RequestMethod.GET.name, null)
client.newCall(builder.build()).execute()
Custom Resource and TraceHeader via OKHttp Interceptor¶
When both FTRUMConfig
's enableTraceUserResource
and FTTraceConfig
's enableAutoTrace
are enabled, custom Interceptor
configurations are loaded first,
ft-sdk < 1.4.1, need to disable
FTRUMConfig
'senableTraceUserResource
andFTTraceConfig
'senableAutoTrace
. ft-sdk > 1.6.7 supports custom Trace Header linking with RUM data
new OkHttpClient.Builder()
.addInterceptor(new FTTraceInterceptor(new FTTraceInterceptor.HeaderHandler() {
@Override
public HashMap<String, String> getTraceHeader(Request request) {
HashMap<String, String> map = new HashMap<>();
map.put("custom_header","custom_value");
return map;
}
// 1.6.7 and above support
@Override
public String getSpanID() {
return "span_id";
}
// 1.6.7 and above support
@Override
public String getTraceID() {
return "trace_id";
}
}))
.addInterceptor(new FTResourceInterceptor(new FTResourceInterceptor.ContentHandlerHelper() {
@Override
public void onRequest(Request request, HashMap<String, Object> extraData) {
String contentType = request.header("Content-Type");
extraData.put("df_request_header", request.headers().toString());
if ("application/json".equals(contentType) ||
"application/x-www-form-urlencoded".equals(contentType) ||
"application/xml".equals(contentType)) {
extraData.put("df_request_body", request.body());
@Override
public void onResponse(Response response, HashMap<String, Object> extraData) throws IOException {
String contentType = response.header("Content-Type");
extraData.put("df_response_header", response.headers().toString());
if ("application/json".equals(contentType) ||
"application/xml".equals(contentType)) {
//copy part of the body to avoid large data consumption
ResponseBody body = response.peekBody(33554432);
extraData.put("df_response_body", body.string());
}
@Override
public void onException(Exception e, HashMap<String, Object> extraData)
}
}))
.eventListenerFactory(new FTResourceEventListener.FTFactory())
.build();
OkHttpClient.Builder()
.addInterceptor(FTTraceInterceptor(object : FTTraceInterceptor.HeaderHandler {
override fun getTraceHeader(request: Request): HashMap<String, String> {
val map = HashMap<String, String>()
map["custom_header"] = "custom_value"
return map
}
}))
.addInterceptor(FTResourceInterceptor(object : FTResourceInterceptor.ContentHandlerHelper {
override fun onRequest(request: Request, extraData: HashMap<String, Any>) {
val contentType = request.header("Content-Type")
extraData["df_request_header"] = request.headers().toString()
if ("application/json" == contentType ||
"application/x-www-form-urlencoded" == contentType ||
"application/xml" == contentType) {
extraData["df_request_body"] = request.body()
}
}
override fun onResponse(response: Response, extraData: HashMap<String, Any>) {
val contentType = response.header("Content-Type")
extraData["df_response_header"] = response.headers().toString()
if ("application/json" == contentType ||
"application/xml" == contentType) {
// Copy part of the response body to avoid large data consumption
val body = response.peekBody(33554432)
extraData["df_response_body"] = body.string()
}
}
override fun onException(e: Exception, extraData: HashMap<String, Any>) {
// Handle exceptions
}
}))
.eventListenerFactory(FTResourceEventListener.FTFactory())
.build()
ContentHandlerHelperEx Local Network Error Filtering¶
ContentHandlerHelperEx is an enhancement over ContentHandlerHelper, allowing filtering of local network IOException errors.
new FTResourceInterceptor.ContentHandlerHelperEx() {
//...
/**
* Return exceptions during network connection
*
* @param e IOException data occurred during the request
* @param extraData additional data
* @return Whether to filter local network network_error type errors. true, to override
*/
@Override
public boolean onExceptionWithFilter(Exception e, HashMap<String, Object> extraData) {
if (e instanceof SocketTimeoutException) { //Network timeout
return true;
}
return super.onExceptionWithFilter(e, extraData);
}
}
object : FTResourceInterceptor.ContentHandlerHelperEx() {
//...
/**
* Return exceptions during network connection
*
* @param e IOException data occurred during the request
* @param extraData additional data
* @return Whether to filter local network network_error type errors. true, to override
*/
override fun onExceptionWithFilter(e: Exception, extraData: HashMap<String, Any>): Boolean {
return if (e is SocketTimeoutException) {
true
} else {
super.onExceptionWithFilter(e, extraData)
}
}
}
OKhttp Adding ResourceID¶
Add uuid to Okhttp Request, recommended for high concurrency scenarios of the same request. ft-plugin 1.3.5 and above, ft-sdk 1.6.10 and above, enable FTSDKConfig.setEnableOkhttpRequestTag(true)
to automatically add ResourceID
to Request
User Information Binding and Unbinding¶
Use FTSdk
to bind and unbind user information
Usage¶
UserData¶
Method Name | Description | Required | Notes |
---|---|---|---|
setId | Set user ID | No | |
setName | Set username | No | |
setEmail | Set email | No | |
setExts | Set user extensions | No | For adding rules see here |
Code Example¶
// Can call this method after user login to bind user information
FTSdk.bindRumUserData("001");
UserData userData = new UserData();
userData.setName("test.user");
userData.setId("test.id");
userData.setEmail("[email protected]");
Map<String, String> extMap = new HashMap<>();
extMap.put("ft_key", "ft_value");
userData.setExts(extMap);
FTSdk.bindRumUserData(userData);
// Can call this method after user logout to unbind user information
FTSdk.unbindRumUserData();
//Can call this method after user login to bind user information
FTSdk.bindRumUserData("001")
//Bind more user data
val userData = UserData()
userData.name = "test.user"
userData.id = "test.id"
userData("[email protected]")
val extMap = HashMap<String, String>()
extMap["ft_key"] = "ft_value"
userData.setExts(extMap)
FTSdk.bindRumUserData(userData)
//Can call this method after user logout to unbind user information
FTSdk.unbindRumUserData()
Shutdown SDK¶
Use FTSdk
to shutdown SDK, If dynamically changing SDK configuration, need to shutdown first to avoid generating erroneous data
Clear SDK Cache Data¶
Use FTSdk
to clear unuploaded cache data
Actively Sync Data¶
Use FTSdk
to actively sync data.
FTSdk.setAutoSync(false) 时, 才需要自行进行数据同步
Actively Sync Dynamic Configuration¶
Use FTSdk
to actively sync dynamic configuration. When automatic updates do not meet requirements, adjust the update timing by actively calling.
/**
* Actively update remote configuration, call frequency is affected by FTSDKConfig.setRemoteConfigMiniUpdateInterval
*/
FTSdk.updateRemoteConfig();
/**
* Actively update remote configuration, this method ignores FTSDKConfig.setRemoteConfigMiniUpdateInterva configuration
*
* @param remoteConfigMiniUpdateInterval Remote configuration update interval, in seconds [0,)
* @param result Return update result
*/
FTSdk.updateRemoteConfig(int remoteConfigMiniUpdateInterval, FTRemoteConfigManager.FetchResult result);
/**
* Actively update remote configuration, call frequency is affected by FTSDKConfig.setRemoteConfigMiniUpdateInterval
*/
FTSdk.updateRemoteConfig()
/**
* Actively update remote configuration, this method ignores FTSDKConfig.setRemoteConfigMiniUpdateInterva configuration
*
* @param remoteConfigMiniUpdateInterval Remote configuration update interval, in seconds [0,)
* @param result Return update result
*/
FTSdk.updateRemoteConfig(remoteConfigMiniUpdateInterval:Int,result:FTRemoteConfigManager.FetchResult)
Dynamically Enable and Disable Access to AndroidID¶
Use FTSdk
to set whether to access Android ID in SDK
Add Custom Tags¶
Use FTSdk
to dynamically add tags during SDK runtime
Usage¶
/**
* Dynamically set global tag
* @param globalContext
*/
public static void appendGlobalContext(HashMap<String,Object> globalContext)
/**
* Dynamically set RUM global tag
* @param globalContext
*/
public static void appendRUMGlobalContext(HashMap<String,Object> globalContext)
/**
* Dynamically set log global tag
* @param globalContext
*/
public static void appendLogGlobalContext(HashMap<String,Object> globalContext)
/**
* Dynamically set global tag
* @param globalContext
*/
fun appendGlobalContext(globalContext: HashMap<String, Any>)
/**
* Dynamically set RUM global tag
* @param globalContext
*/
fun appendRUMGlobalContext(globalContext: HashMap<String, Any>)
/**
* Dynamically set log global tag
* @param globalContext
*/
fun appendLogGlobalContext(globalContext: HashMap<String, Any>)
Code Example¶
HashMap<String, Object> globalContext = new HashMap<>();
globalContext.put("global_key", "global_value");
FTSdk.appendGlobalContext(globalContext);
HashMap<String, Object> rumGlobalContext = new HashMap<>();
rumGlobalContext.put("rum_key", "rum_value");
FTSdk.appendRUMGlobalContext(rumGlobalContext);
HashMap<String, Object> logGlobalContext = new HashMap<>();
logGlobalContext.put("log_key", "log_value");
FTSdk.appendLogGlobalContext(logGlobalContext);
val globalContext = hashMapOf<String, Any>(
"global_key" to "global_value"
)
FTSdk.appendGlobalContext(globalContext)
val rumGlobalContext = hashMapOf<String, Any>(
"rum_key" to "rum_value"
)
FTSdk.appendRUMGlobalContext(rumGlobalContext)
val logGlobalContext = hashMapOf<String, Any>(
"log_key" to "log_value"
)
FTSdk.appendLogGlobalContext(logGlobalContext)
Symbol File Upload¶
plugin Upload (Only supports datakit【local deployment】)¶
ft-plugin
version needs 1.3.0
or above to support the latest symbol file upload rules, supports productFlavor
multi-version management, plugin will execute symbol file upload after gradle task assembleRelease
, detailed configuration can refer to SDK Demo
FTExt {
//...
autoUploadMap = true // Upload mapping.txt file, default is false
autoUploadNativeDebugSymbol = true // Upload c/c++ symbol so file, default is false
datakitUrl = 'https://datakit.url' // datakit upload address, generateSourceMapOnly=true 时, no need to configure
datawayToken = 'dataway_token' // Space token, generateSourceMapOnly=true 时, no need to configure
appId = "appid_xxxxx" // appid, generateSourceMapOnly=true 时, no need to configure
env = 'common' // Environment, generateSourceMapOnly=true 时, no need to configure
// native so specified path, only need to specify the upper directory of the abi file
// |-stripped_native_libs
// |-release
// |-out
// |-lib
// |-arm64-v8a
// |-armeabi-v7a
// |-...
//nativeLibPath='/build/intermediates/merged_native_libs/release/out/lib'
generateSourceMapOnly = false //Only generate sourcemap, default is false, path example: /app/build/tmp/ft{flavor}SourceMapMerge-release.zip, ft-plugin:1.3.4 and above support
prodFlavors { //prodFlavors configuration will override outer settings
prodTest {
autoUploadMap = false
autoUploadNativeDebugSymbol = false
datakitUrl = 'https://datakit.url'
datawayToken = 'dataway_token'
appId = "appid_prodTest"
env = "gray"
}
prodPublish {
autoUploadMap = true
autoUploadNativeDebugSymbol = true
datakitUrl = 'https://datakit.url'
datawayToken = 'dataway_token'
appId = "appid_prodPublish"
env = "prod"
}
}
}
Manual Upload¶
Use plugin
to enable generateSourceMapOnly = true
, execute gradle task assembleRelease
to generate, or manually package into zip
file, then manually upload to datakit
or upload from TrueWatch Studio, recommend using zip
command line to package, avoid packaging system hidden files into zip
package, symbol upload refer to sourcemap upload
Unity Native Symbol file refer to official documentation
Permission Configuration Description¶
Name | Required | Usage Reason |
---|---|---|
READ_PHONE_STATE |
No | Used to get mobile cellular network device information |
For how to apply for dynamic permissions, refer to Android Developer
Plugin AOP Ignore¶
Add @IngoreAOP
to methods covered by Plugin AOP to ignore ASM insertion. For batch ignore, use ft-plugin
FTExt
ignorePackages
to ignore.
WebView Data Monitoring¶
WebView data monitoring requires integrating Web Monitoring SDK in the WebView access page.
Data Masking¶
If you want to fully mask fields, recommend using setDataModifier
, which performs better. For detailed rule replacement, recommend setLineDataModifier
Do not use complex or high-latency methods in the callback, this will greatly affect SDK data writing performance
FTSdk.install(
FTSDKConfig.builder("xxx")
.setDataModifier(new DataModifier() {
/**
* Modify a single field
*
* @param key field name
* @param value field value (original value)
* @return new value, return null to not modify
*/
@Override
public Object modify(String key, Object value) {
if (key.equals("device_uuid")) {
return "xxx";
}
return null;
}
}).setLineDataModifier(new LineDataModifier() {
/***
* Modify a single line of data
*
* @param measurement data measurement type view, action, resource,
* longtask, error, df_rum_android_log
* @param data original data key-value pairs
* @return key-value pairs to modify, (return null or empty map to not modify)
*/
@Override
public Map<String, Object> modify(String measurement, HashMap<String, Object> data) {
if(measurement.equals("view")){
HashMap<String,Object> changeMap = new HashMap<String,Object>();
changeMap.put("view_url", "xxx");
}
return null;
}
}))
FTSdk.install(
FTSDKConfig.builder("xxx")
.setDataModifier(object : DataModifier {
/**
* Modify a single field
*
* @param key field name
* @param value field value (original value)
* @return new value, return null to not modify
*/
override fun modify(key: String, value: Any?): Any? {
return if (key == "device_uuid") {
"xxx" // Replace with custom device_uuid
} else {
null
}
}
})
// Batch modify certain fields in a single line of data
.setLineDataModifier(object : LineDataModifier {
/**
* Modify a single line of data
*
* @param measurement data measurement type view, action, resource,
* longtask, error, df_rum_android_log
* @param data original data key-value pairs
* @return key-value pairs to modify, (return null or empty map to not modify)
*/
override fun modify(
measurement: String,
data: HashMap<String, Any>
): Map<String, Any>? {
return if (measurement == "view") {
hashMapOf("view_url" to "xxx")
} else {
null
}
}
}))
RUM Custom Collection Rules¶
View¶
Activity¶
- Requires
FTRUMConfig.setEnableTraceUserView(true)
to be enabled
FTSdk.initRUMWithConfig(new FTRUMConfig()
.setViewActivityTrackingHandler(new FTViewActivityTrackingHandler() {
@Override
public HandlerView resolveHandlerView(Activity activity) {
String activityName = activity.getClass().getSimpleName();
// Customize view name based on Activity name
if (activityName.startsWith("Main")) {
return new HandlerView("Custom Main Page");
} else if (activityName.startsWith("Detail")) {
HashMap<String, Object> properties = new HashMap<>();
properties.put("extra_key", "extra_value");
return new HandlerView("Custom Detail Page", properties);
}
// Return null to skip tracking
return null;
}
})
);
FTSdk.initRUMWithConfig(
FTRUMConfig()
.setViewActivityTrackingHandler(object : FTViewActivityTrackingHandler {
override fun resolveHandlerView(activityactivity: Activity): HandlerView? {
val activityName = activity.javaClass.simpleName
return when {
activityName.startsWith("Main") ->
HandlerView("Custom Main Page")
activityName.startsWith("Detail") -> {
val properties = hashMapOf<String, Any>("extra_key" to "extra_value")
HandlerView("Custom Detail Page", properties)
}
else -> null // Skip tracking
}
}
})
)
Fragment¶
- Requires both
FTRUMConfig.setEnableTraceUserView(true)
andFTRUMConfig.setEnableTraceUserViewInFragment(true)
to be enabled
FTSdk.initRUMWithConfig(new FTRUMConfig()
.setViewFragmentTrackingHandler(new FTViewFragmentTrackingHandler() {
@Override
public HandlerView resolveHandlerView(FragmentWrapper fragment) {
String fragmentName = fragment.getSimpleClassName();
// Customize view name based on Fragment name
if (fragmentName.equals("HomeFragment")) {
return new HandlerView("Custom Home Fragment");
} else if (fragmentName.startsWith("Detail")) {
HashMap<String, Object> properties = new HashMap<>();
properties.put("extra_key", "extra_value");
return new HandlerView("Custom Detail Fragment", properties);
}
// Return null to skip tracking
return null;
}
})
);
FTSdk.initRUMWithConfig(
FTRUMConfig()
.setViewFragmentTrackingHandler(object : FTViewFragmentTrackingHandler {
override fun resolveHandlerView(fragment: FragmentWrapper): HandlerView? {
val fragmentName = fragment.simpleClassName
return when {
fragmentName == "HomeFragment" ->
HandlerView("Custom Home Fragment")
fragmentName.startsWith("Detail") -> {
val properties = hashMapOf<String, Any>("extra_key" to "extra_value")
HandlerView("Custom Detail Fragment", properties)
}
else -> null // Skip tracking
}
}
})
)
Action¶
- Requires
FTRUMConfig.setEnableTraceUserAction(true)
to be enabled
FTSdk.initRUMWithConfig(new FTRUMConfig()
.setActionTrackingHandler(new FTActionTrackingHandler() {
@Override
public HandlerAction resolveHandlerAction(ActionEventWrapper actionEventWrapper) {
// Get action type
ActionSourceType actionType = actionEventWrapper.getSourceType();
// Customize tracking based on action type
if (actionType == ActionSourceType.CLICK_VIEW) {
HashMap<String, Object> properties = new HashMap<>();
properties.put("extra_key", "extra_value");
return new HandlerAction("Custom Button Click", properties);
} else if (actionType == ActionSourceType.CLICK_LIST_ITEM) {
return new HandlerAction("Custom List Item Click");
}
// Return null to skip tracking
return null;
}
})
);
FTSdk.initRUMWithConfig(
FTRUMConfig()
.setActionTrackingHandler(object : FTActionTrackingHandler {
override fun resolveHandlerAction(actionEventWrapper: ActionEventWrapper): HandlerAction? {
return when (actionEventWrapper.sourceType) {
ActionSourceType.CLICK_VIEW -> {
val properties = hashMapOf<String, Any>("extra_key" to "extra_value")
HandlerAction("Custom Button Click", properties)
}
ActionSourceType.CLICK_LIST_ITEM ->
HandlerAction("Custom List Item Click")
else -> null // Skip tracking
}
}
})
)
Resource¶
- Requires
FTRUMConfig.setEnableTraceUserResource(true)
to be enabled
Custom Tag Usage Example¶
Compile Configuration Method¶
- Create multiple
productFlavors
inbuild.gradle
to distinguish tags
android{
//…
productFlavors {
prodTest {
buildConfigField "String", "CUSTOM_VALUE", "\"Custom Test Value\""
//…
}
prodPublish {
buildConfigField "String", "CUSTOM_VALUE", "\"Custom Publish Value\""
//…
}
}
}
- Add corresponding
BuildConfig
constants inRUM
configuration
Runtime File Read/Write Method¶
- Store file type data, such as
SharedPreferences
, configure usingSDK
, add code to get tag data in the configuration.
SharedPreferences sp = context.getSharedPreferences(SP_STORE_DATA, MODE_PRIVATE);
String customDynamicValue = sp.getString(CUSTOM_DYNAMIC_TAG, "not set");
// Configure RUM
FTSdk.initRUMWithConfig(
new FTRUMConfig().addGlobalContext(CUSTOM_DYNAMIC_TAG, customDynamicValue)
//… Add other configurations
);
- Add methods to change file data anywhere.
- Finally, restart the application. For detailed details, see SDK Demo
SDK Runtime Addition¶
After SDK initialization, use FTSdk.appendGlobalContext(globalContext)
, FTSdk.appendRUMGlobalContext(globalContext)
, FTSdk.appendLogGlobalContext(globalContext)
to dynamically add tags, set up, will take effect immediately. Subsequently, RUM or Log subsequent reported data will automatically add tag data. This usage is suitable for delayed data acquisition scenarios, such as tag data requires network requests to obtain.
//SDK initialization pseudo code, get parameters from the network, then set tags
FTSdk.init()
getInfoFromNet(info){
HashMap<String, Object> globalContext = new HashMap<>();
globalContext.put("delay_key", info.value);
FTSdk.appendGlobalContext(globalContext)
}
Frequently Asked Questions¶
Add Global Variables to Avoid Conflict Fields¶
To avoid conflicts between custom fields and SDK data, it is recommended to add project abbreviation prefixes to tag names, such as df_tag_name
, project key
values can be queried in the source code. When the same variable appears in SDK global variables and RUM, Log, RUM, Log will overwrite the global variables in SDK.
SDK Compatibility¶
Dealing with Market Privacy Audits¶
Privacy Statement¶
Method 1: SDK AndroidID Configuration¶
SDK uses Android ID to better associate the same user data. If you need to publish on the application market, you need to deal with market privacy audits in the following ways.
public class DemoApplication extends Application {
@Override
public void onCreate() {
// Set setEnableAccessAndroidID to false during initialization
FTSDKConfig config = new FTSDKConfig.Builder(DATAKIT_URL)
.setEnableAccessAndroidID(false)
.build();
FTSdk.install(config);
// ...
}
}
// Enable after user agrees to privacy policy
FTSdk.setEnableAccessAndroidID(true);
class DemoApplication : Application() {
override fun onCreate() {
//Set setEnableAccessAndroidID to false during initialization
val config = FTSDKConfig
.builder(DATAKIT_URL)
. setEnableAccessAndroidID(false)
FTSdk.install(config)
//...
}
}
//Enable after user agrees to privacy policy
FTSdk.setEnableAccessAndroidID(true);
Method 2: Delay SDK Initialization¶
If you need to delay loading SDK in the application, it is recommended to initialize in the following way.
// Application
public class DemoApplication extends Application {
@Override
public void onCreate() {
//If already agreed to the agreement, initialize in Application
if(agreeProtocol){
FTSdk.init(); //SDK initialization pseudo code
}
}
}
// Privacy Statement Activity Page
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
//Not read privacy statement
if ( notReadProtocol ) {
//Privacy statement popup
showProtocolView();
//If agree to privacy statement
if( agreeProtocol ){
FTSdk.init(); //SDK initialization pseudo code
}
}
}
}
// Application
class DemoApplication : Application() {
override fun onCreate() {
// If already agreed to the agreement, initialize in Application
if (agreeProtocol) {
FTSdk.init() //SDK initialization pseudo code
}
}
}
// Privacy Statement Activity Page
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
// Not read privacy statement
if (notReadProtocol) {
// Privacy statement popup
showProtocolView()
// If agree to privacy statement
if (agreeProtocol) {
FTSdk.init() //SDK initialization pseudo code
}
}
}
}
Third-party Frameworks¶
flutter
, react-native
, uni-app
, unity
can use similar native Android delayed initialization methods to deal with application market privacy audits.
Jetpack Compose Support¶
Currently, automatic collection of compose component-generated pages is not supported, but you can manually track click events and page navigation events through custom interfaces for Action
and View
, refer to here
How to Integrate SDK Without Using ft-plugin¶
TrueWatch uses Androig Grale Plugin Transformation to implement code injection, thereby achieving automatic data collection. However, due to some compatibility issues, there may be problems that cannot use ft-plugin
or ft-plugin-legacy
. Affected include RUM Action
, Resource
, and android.util.Log
, Java and Kotlin println
console log automatic capture, and automatic upload of symbol files.
Currently, for this situation, we have another integration solution, the response plan is as follows:
- Application startup event, source code example reference DemoForManualSet.kt
- Button and other events need to be added manually at the trigger, for example, Button onClick event, source code example reference ManualActivity.kt:
OKhttp
throughaddInterceptor
,eventListener
method to integrateResource
,Trace
, examples are as follows, source code example reference ManualActivity.kt:
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.addInterceptor(new FTTraceInterceptor())
.addInterceptor(new FTResourceInterceptor())
.eventListenerFactory(new FTResourceEventListener.FTFactory());
//.eventListenerFactory(new FTResourceEventListener.FTFactory(true));
OkHttpClient client = builder.build();
-
Other network frameworks need to manually implement using
FTRUMGlobalManager
startResource
,stopResource
,addResource
,FTTraceManager.getTraceHeader
. For specific implementation methods, please refer to the source code example ManualActivity.kt -
WebView data collection configuration