Android 应用接入¶
通过收集 Android 应用的指标数据,以可视化方式分析应用性能。
前置条件¶
注意
若已开通 RUM Headless 服务,前置条件已自动配置,可直接接入应用。
- 安装 DataKit;
- 配置 RUM 采集器;
- DataKit 配置为公网可访问,并且安装 IP 地理信息库。
应用接入¶
- 进入用户访问监测 > 新建应用 > Android;
- 输入应用名称;
- 输入应用 ID;
-
选择应用接入方式:
- 公网 DataWay:直接接收 RUM 数据,无需安装 DataKit 采集器。
- 本地环境部署:满足前置条件后接收 RUM 数据。
安装¶
源码地址:https://github.com/GuanceCloud/datakit-android
Demo:https://github.com/GuanceDemo/guance-app-demo
Gradle 配置¶
- 在项目的根目录的
build.gradle
文件中添加SDK
的远程仓库地址
buildscript {
//...
repositories {
//...
//添加 SDK 的远程仓库地址
maven {
url 'https://mvnrepo.jiagouyun.com/repository/maven-releases'
}
}
dependencies {
//...
//添加 Plugin 的插件,依赖 AGP 7.4.2 以上,Gradle 7.2.0 以上
classpath 'com.cloudcare.ft.mobile.sdk.tracker.plugin:ft-plugin:[latest_version]'
// AGP 7.4.2 以下版本,请使用 ft-plugin-legacy
//classpath 'com.cloudcare.ft.mobile.sdk.tracker.plugin:ft-plugin-legacy:[latest_version]'
}
}
allprojects {
repositories {
//...
//添加 SDK 的远程仓库地址
maven {
url 'https://mvnrepo.jiagouyun.com/repository/maven-releases'
}
}
}
//setting.gradle
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
//添加 SDK 的远程仓库地址
maven {
url('https://mvnrepo.jiagouyun.com/repository/maven-releases')
}
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
//添加 SDK 的远程仓库地址
maven {
url('https://mvnrepo.jiagouyun.com/repository/maven-releases')
}
}
}
//build.gradle
plugins{
//添加 Plugin 的插件,依赖 AGP 7.4.2 以上,Gradle 7.2.0 以上
id 'com.cloudcare.ft.mobile.sdk.tracker.plugin' version '[lastest_version]' apply false
// AGP 7.4.2 以下版本,请使用 ft-plugin-legacy
//id 'com.cloudcare.ft.mobile.sdk.tracker.plugin.legacy' version '[lastest_version]' apply false
//plugin 配置作用:
// 1. 自动配置采集:App 启动、OkHttp 请求、WebView 活动、Activity 跳转
// 2. 自动采集 View 点击事件,Console Logcat
// 3. 自动采集 Console Logcat
}
- 在项目主模块
app
的build.gradle
文件中添加SDK
的依赖及Plugin
的使用。
dependencies {
//添加 SDK 的依赖
implementation 'com.cloudcare.ft.mobile.sdk.tracker.agent:ft-sdk:[latest_version]'
//捕获 native 层崩溃信息的依赖,需要配合 ft-sdk 使用不能单独使用
implementation 'com.cloudcare.ft.mobile.sdk.tracker.agent:ft-native:[latest_version]'
// json 序列化
implementation 'com.google.code.gson:gson:2.8.+'
//可选,如果需要自动采网络请求和自动开启链路必须添加
implementation 'com.squareup.okhttp3:okhttp:4.+'
}
//应用插件
apply plugin: 'ft-plugin' //如果使用旧版本兼容 ft-plugin-legacy,无需更改
//配置插件使用参数 (可选)
FTExt {
//是否显示 Plugin 日志,默认为 false
//showLog = true
//设置 ASM 版本,支持 asm7 - asm9,默认 asm9
//asmVersion='asm7'
//ASM 忽略路径配置,路径中 . 和 / 等效
//ignorePackages=['com.ft','com/ft']
}
android{
//...省略部分代码
defaultConfig {
//...省略部分代码
ndk {
//当使用 ft-native 捕获 native 层的崩溃信息时,应该根据应用适配的不同的平台
//来选择支持的 abi 架构,目前 ft-native 中包含的 abi 架构有 'arm64-v8a',
// 'armeabi-v7a', 'x86', 'x86_64'
abiFilters 'armeabi-v7a'
}
}
compileOptions {
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
}
最新的版本请看上方的 ft-sdk 、ft-plugin 、ft-native 的版本名
Application 配置¶
最佳的 SDK 初始化位置是在 Application
的 onCreate
方法中,如果您的应用尚未创建 Application
,您需要创建一个,并在 AndroidManifest.xml
中声明,示例请参考这里。
R8 / Proguard 混淆配置¶
如果需要 android.buildTypes
设置 minifyEnabled
= true ,需要开启如下配置:
-dontwarn com.ft.sdk.**
### ft-sdk 库
-keep class com.ft.sdk.**{*;}
### ft-native 库
-keep class ftnative.*{*;}
### 防止 Action 获取时 action_name 中类名被混淆###
-keepnames class * extends android.view.View
-keepnames class * extends android.view.MenuItem
SDK 初始化¶
基础配置¶
public class DemoApplication extends Application {
@Override
public void onCreate() {
//本地环境部署、Datakit 部署
FTSDKConfig config = FTSDKConfig.builder(datakitUrl);
//使用公网 DataWay
FTSDKConfig config = FTSDKConfig.builder(datawayUrl, clientToken);
//...
//config.setDebug(true); //debug 模式
config.setCompressIntakeRequests(true); //上报数据压缩
FTSdk.install(config);
}
}
class DemoApplication : Application() {
override fun onCreate() {
//本地环境部署、Datakit 部署
val config = FTSDKConfig.builder(datakitUrl)
//使用公网 DataWay
val config = FTSDKConfig.builder(datawayUrl, clientToken)
//...
//config.setDebug(true); //debug 模式
config.setCompressIntakeRequests(true) //上报数据压缩
FTSdk.install(config)
}
}
方法名 | 类型 | 必须 | 含义 |
---|---|---|---|
datakitUrl | String | 是 | 本地环境部署(Datakit)上报 URL 地址,例子:http://10.0.0.1:9529,端口默认 9529,安装 SDK 设备需能访问该地址。注意:datakitUrl 和 datawayUrl 配置两者二选一 |
datawayUrl | String | 是 | 公网 Dataway 上报 URL 地址,从 [用户访问监测] 应用中获取,例子:https://open.dataway.url,安装 SDK 设备需能访问这地址。注意:datakitUrl 和 datawayUrl 配置两者二选一 |
clientToken | String | 是 | 认证 token,需要与 datawayUrl 同时配置 |
setDebug | Boolean | 否 | 是否开启调试模式 。默认为 false ,开启后方可打印 SDK 运行日志 |
setEnv | EnvType | 否 | 设置采集环境, 默认为 EnvType.PROD , |
setEnv | String | 否 | 设置采集环境,默认为 prod 。注意: String 或 EnvType 类型只需配置一个 |
setOnlySupportMainProcess | Boolean | 否 | 是否只支持在主进程运行,默认为 true ,如果需要在其他进程中执行需要将该字段设置为 false |
setEnableAccessAndroidID | Boolean | 否 | 开启获取 Android ID ,默认为 true ,设置为 false ,则 device_uuid 字段数据将不进行采集,市场隐私审核相关查看这里 |
addGlobalContext | Dictionary | 否 | 添加 SDK 全局属性,添加规则请查阅此处 |
setServiceName | String | 否 | 设置服务名,影响 Log 和 RUM 中 service 字段数据,默认为 df_rum_android |
setAutoSync | Boolean | 否 | 是否在采集数据后自动同步到服务器,默认为 true 。当为 false 时使用 FTSdk.flushSyncData() 自行管理数据同步 |
setSyncPageSize | Int | 否 | 设置同步请求条目数,SyncPageSize.MINI 5 条,SyncPageSize.MEDIUM 10 条,SyncPageSize.LARGE 50 条,默认 SyncPageSize.MEDIUM |
setCustomSyncPageSize | Enum | 否 | 设置同步请求条目数,范围 [5,),注意请求条目数越大,代表数据同步占用更大的计算资源,默认为 10 注意:setSyncPageSize 和 setCustomSyncPageSize 只需要配置一个 |
setSyncSleepTime | Int | 否 | 设置同步间歇时间,范围 [0,5000],默认不设置 |
enableDataIntegerCompatible | Void | 否 | 需要与 web 数据共存情况下,建议开启。此配置用于处理 web 数据类型存储兼容问题。1.6.9 版本默认开启 |
setNeedTransformOldCache | Boolean | 否 | 是否需要兼容同步 ft-sdk 1.6.0 以下的版本的旧缓存数据,默认为 false |
setCompressIntakeRequests | Boolean | 否 | 对上传同步数据进行 deflate 压缩,默认关闭,ft-sdk 1.6.3 以上版本支持这个方法 |
enableLimitWithDbSize | Void | 否 | 开启使用 db 限制数据大小,默认 100MB,单位 Byte,数据库越大,磁盘压力越大,默认不开启。 注意: 开启之后 FTLoggerConfig.setLogCacheLimitCount 及 FTRUMConfig.setRumCacheLimitCount 将失效。ft-sdk 1.6.6 以上版本支持这个方法 |
setEnableOkhttpRequestTag | bool | 否 | 对 Okhttp Request 自动添加唯一 ResourceID ,用于相同请求高并发的场景。ft-sdk 1.6.10 以上支持,ft-plugin 1.3.5 以上支持 |
setProxy | java.net.Proxy | 否 | 对数据网络同步请求进行 Proxy 代理设置, 仅支持 okhttp3, ft-sdk 1.6.10 以上支持 |
setProxyAuthenticator | okhttp3.Authenticator | 否 | 对数据同步网络请求进行 Proxy 代理设置,仅支持 okhttp3, ft-sdk 1.6.10 以上支持 |
setDns | okhttp3.Dns | 否 | 数据同步网络请求支持自定义 Dns 对域名解析进来自定义处理,仅支持 okhttp3, ft-sdk 1.6.10 以上支持 |
setDataModifier | DataModifier | 否 | 对单个字段进行更改。 ft-sdk 1.6.11 以上支持,使用示例请看这里 |
setLineDataModifier | LineDataModifier | 否 | 对单条数据数据进行更改。 ft-sdk 1.6.11 以上支持,使用示例请看这里 |
setRemoteConfiguration | Int | 否 | 是否开启数据采集的远程配置功能,默认为 false 。开启之后,SDK 初始化或应用热启动会触发数据更新。 ft-sdk 1.6.12 以上支持。 datakit 版本要求 >=1.60 或使用公网 dataway |
setRemoteConfigMiniUpdateInterval | Int | 否 | 设置数据更新最短间隔,单位秒,默认12小时。 ft-sdk 1.6.12 以上支持 |
RUM 配置¶
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())
)
方法名 | 类型 | 必须 | 含义 |
---|---|---|---|
setRumAppId | String | 是 | 设置Rum AppId 。对应设置 RUM appid ,才会开启RUM 的采集功能,获取 appid 方法 |
setSamplingRate | Float | 否 | 设置采集率,取值范围 [0,1],0 表示不采集,1 表示全采集,默认值为 1。作用域为同一 session_id 下所有 View,Action,LongTask,Error 数据 |
setSessionErrorSampleRate | Float | 否 | 设置错误采集率,当会话未被 setSamplingRate 采样时,若会话期间发生错误,可以采集到错误前 1 分钟范围的数据,取值范围 [0,1],0 表示不采集,1 表示全采集,默认值为 0。作用域为同一 session_id 下所有 View,Action,LongTask,Error 数据。ft-sdk 1.6.11 以上支持 |
setEnableTrackAppCrash | Boolean | 否 | 是否上报 App 崩溃日志,默认为 false ,开启后会在错误分析中显示错误堆栈数据。关于崩溃日志中混淆内容转换的问题。 ft-sdk 1.5.1 以上版本,可以通过 extraLogCatWithJavaCrash 、extraLogCatWithNativeCrash 设置在 Java Crash 和 Native Crash 是否显示 logcat |
setExtraMonitorTypeWithError | Array | 否 | 设置辅助监控信息,添加附加监控数据到 Rum 崩溃数据中,ErrorMonitorType.BATTERY 为电池余量,ErrorMonitorType.MEMORY 为内存用量,ErrorMonitorType.CPU 为 CPU 占有率,默认不设置 |
setDeviceMetricsMonitorType | Array | 否 | 设置 View 监控信息,在 View 周期中,添加监控数据,DeviceMetricsMonitorType.BATTERY 监控当前页的最高输出电流输出情况,DeviceMetricsMonitorType.MEMORY 监控当前应用使用内存情况,DeviceMetricsMonitorType.CPU 监控 CPU 跳动次数 ,DeviceMetricsMonitorType.FPS 监控屏幕帧率。监控周期,DetectFrequency.DEFAULT 500 毫秒,DetectFrequency.FREQUENT 100毫秒,DetectFrequency.RARE 1 秒,默认不设置 |
setEnableTrackAppANR | Boolean | 否 | 是否开启 ANR 检测,默认为 false 。ft-sdk 1.5.1 以上版本,可以通过 extraLogCatWithANR 设置 ANR 中是否显示 logcat |
setEnableTrackAppUIBlock | Boolean, long | 否 | 是否开启 UI 卡顿检测,默认为 false ,ft-sdk 1.6.4 以上版本可以通过 blockDurationMs 控制检测时间范围 [100,),单位毫秒, 默认是 1 秒 |
setEnableTraceUserAction | Boolean | 否 | 是否自动追踪用户操作,目前只支持用户启动和点击操作,默认为 false |
setEnableTraceUserView | Boolean | 否 | 是否自动追踪用户页面操作,默认为 false |
setEnableTraceUserViewInFragment | Boolean | 否 | 是否自动追踪 Fragment 类型的页面数据,默认为 false , ft-sdk 1.6.11 以上支持 |
setEnableTraceUserResource | Boolean | 否 | 是否自动追动用户网络请求 ,仅支持 Okhttp ,默认为 false |
setEnableResourceHostIP | Boolean | 否 | 是否采集请求目标域名地址的 IP。作用域:只影响 EnableTraceUserResource 为 true 的默认采集。自定义 Resource 采集,需要使用 FTResourceEventListener.FTFactory(true) 来开启这个功能。另外,单个 Okhttp 对相同域名存在 IP 缓存机制,相同 OkhttpClient ,在连接服务端 IP 不发生变化的前提下,只会生成一次 |
setResourceUrlHandler | Callback | 否 | 设置需要过滤的 Resource 条件,默认不过滤 |
setOkHttpEventListenerHandler | Callback | 否 | ASM 设置全局 Okhttp EventListener,默认不设置 |
setOkHttpResourceContentHandler | Callback | 否 | ASM 设置全局 FTResourceInterceptor.ContentHandlerHelper ,默认不设置, ft-sdk 1.6.7 以上支持,自定义 Resource |
addGlobalContext | Dictionary | 否 | 添加自定义标签,用于用户监测数据源区分,如果需要使用追踪功能,则参数 key 为 track_id ,value 为任意数值,添加规则注意事项请查阅此处 |
setRumCacheLimitCount | int | 否 | 本地缓存 RUM 限制数量 [10_000,),默认是 100_000。ft-sdk 1.6.6 以上支持 |
setEnableTraceWebView | Boolean | 否 | 配置是否开启通过 Android SDK 采集 WebView 数据,默认为 true。ft-sdk 1.6.12 以上支持 |
setAllowWebViewHost | Array | 否 | 设置允许数据追踪的 WebView host 地址,null 为全采集,默认为 null。ft-sdk 1.6.12 以上支持 |
Log 配置¶
方法名 | 类型 | 必须 | 含义 |
---|---|---|---|
setSamplingRate | Float | 否 | 设置采集率,取值范围 [0,1],0 表示不采集,1 表示全采集,默认值为 1。 |
setEnableConsoleLog | Boolean | 否 | 是否上报控制台日志,默认 false ,日志等级对应关系Log.v -> ok; Log.i -> info; Log.d -> debug; Log.e -> error; Log.w -> warning, prefix 为控制前缀过滤参数,默认不设置过滤。注意:Android 控制台量是很大的,为了避免影响应用性能,减少不必要的资源浪费,建议使用 prefix 过滤出有价值的日志。ft-plugin 1.3.5 以上版本,支持捕获 Log.println 打印的日志 |
setEnableLinkRUMData | Boolean | 否 | 是否与 RUM 数据关联,默认为 false |
setEnableCustomLog | Boolean | 否 | 是否上传自定义日志,默认为 false |
setLogLevelFilters | Array | 否 | 设置等级日志过滤,默认不设置 |
addGlobalContext | Dictionary | 否 | 添加 log 全局属性,添加规则请查阅此处 |
setLogCacheLimitCount | Int | 否 | 本地缓存最大日志条目数量限制 [1000,),日志越大,代表磁盘缓存压力越大,默认为 5000 |
setLogCacheDiscardStrategy | LogCacheDiscard | 否 | 设置日志达到限制上限以后的日志丢弃规则,默认为 LogCacheDiscard.DISCARD ,DISCARD 为丢弃追加数据,DISCARD_OLDEST 丢弃老数据 |
Trace 配置¶
方法名 | 类型 | 必须 | 含义 |
---|---|---|---|
setSamplingRate | Float | 否 | 设置采集率,取值范围 [0,1],0 表示不采集,1 表示全采集,默认值为 1。 |
setTraceType | TraceType | 否 | 设置链路追踪的类型,默认为 DDTrace ,目前支持 Zipkin , Jaeger , DDTrace ,Skywalking (8.0+),TraceParent (W3C),如果接入 OpenTelemetry 选择对应链路类型时,请注意查阅支持类型及 agent 相关配置 |
setEnableLinkRUMData | Boolean | 否 | 是否与 RUM 数据关联,默认为 false |
setEnableAutoTrace | Boolean | 否 | 设置是否开启自动 http trace,目前只支持 OKhttp 的自动追踪,默认为 false |
setOkHttpTraceHeaderHandler | Callback | 否 | ASM 设置全局 FTTraceInterceptor.HeaderHandler ,默认不设置, ft-sdk 1.6.8 以上支持,示例参考自定义 Trace |
RUM 用户数据追踪¶
在FTRUMConfig
配置 enableTraceUserAction
, enableTraceUserView
, enableTraceUserResource
,setEnableTrackAppUIBlock
,setEnableTrackAppCrash
和 setEnableTrackAppANR
来实现 Action
、View
、Resource
、LongTask
、Error
的自动采集追踪获取数据。如果想自定义采集可以通过 FTRUMGlobalManager
来上报数据,示例如下:
Action¶
使用方法¶
/**
* 添加 Action
*
* @param actionName action 名称
* @param actionType action 类型
* @param property 附加属性参数(可选)
*/
public void startAction(String actionName, String actionType, HashMap<String, Object> property)
/**
* 添加 Action, 此类数据无法关联 Error,Resource,LongTask 数据
*
* @param actionName action 名称
* @param actionType action 类型
* @param duration 纳秒,持续时间(可选)
* @param property 扩展属性(可选)
*/
public void addAction(String actionName, String actionType, long duration, HashMap<String, Object> property)
/**
* 添加 action
*
* @param actionName action 名称
* @param actionType action 类型
* @param property 附加属性参数(可选)
*/
fun startAction(actionName: String, actionType: String, property: HashMap<String, Any>)
/**
* 添加 Action
*
* @param actionName action 名称
* @param actionType action 类型
* @param duration 纳秒,持续时间(可选)
* @param property 扩展属性(可选)
*/
fun addAction(actionName: String, actionType: String, duration: Long, property: HashMap<String, Any>)
startAction 内部有计算耗时算法,计算期间会尽量与附近发生的 Resource,LongTask,Error 数据做数据关联,会有 100 ms 频繁触发保护,建议使用于用户操作类型的数据。如果有频繁调用的需求请使用 addAction,这个数据不会于 startAction 发生冲突,并且不与当下 Resource,LongTask,Error 进行数据关联
代码示例¶
// 场景1
FTRUMGlobalManager.get().startAction("login", "action_type");
// 场景2: 动态参数
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key", "ft_value");
FTRUMGlobalManager.get().startAction("login", "action_type", map);
// 场景1
FTRUMGlobalManager.get().addAction("login", "action_type");
// 场景2: 动态参数
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key", "ft_value");
FTRUMGlobalManager.get().addAction("login", "action_type", map);
// 场景1
FTRUMGlobalManager.get().startAction("login", "action_type")
// 场景2: 动态参数
val map = HashMap<String,Any>()
map["ft_key"]="ft_value"
FTRUMGlobalManager.get().startAction("login","action_type",map)
// 场景1
FTRUMGlobalManager.get().startAction("login", "action_type")
// 场景2: 动态参数
val map = HashMap<String,Any>()
map["ft_key"]="ft_value"
FTRUMGlobalManager.get().startAction("login","action_type",map)
View¶
使用方法¶
代码示例¶
@Override
protected void onResume() {
super.onResume();
// 场景 1
FTRUMGlobalManager.get().startView("Current Page Name");
// 场景 2: 动态参数
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();
// 场景 1
FTRUMGlobalManager.get().stopView();
// 场景 2 : 动态参数
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key_will_change", "ft_value_change"); //ft_key_will_change 这个数值,会在 stopView 时候被修改为 ft_value_change
FTRUMGlobalManager.get().startView("Current Page Name", map);
}
override fun onResume() {
super.onResume()
// 场景 1
FTRUMGlobalManager.get().startView("Current Page Name")
// 场景 2: 动态参数
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()
// 场景 1
FTRUMGlobalManager.get().stopView()
// 场景 2 : 动态参数
val map = HashMap<String, Any>()
map["ft_key_will_change"] = "ft_value_change" //ft_key_will_change 这个数值,会在 stopView 时候被修改为 ft_value_change
FTRUMGlobalManager.get().startView("Current Page Name", map)
}
Error¶
使用方法¶
/**
* 添加错误信息
*
* @param log 日志
* @param message 消息
* @param errorType 错误类型, ErrorType
* @param state 程序运行状态
* @param dateline 发生时间,纳秒(可选)
* @param property 附加属性(可选)
*/
public void addError(String log, String message, long dateline, ErrorType errorType,
AppState state, HashMap<String, Object> property)
/**
* 添加错误信息
*
* @param log 日志
* @param message 消息
* @param errorType 错误类型, String
* @param state 程序运行状态
* @param dateline 发生时间,纳秒(可选)
* @param property 附加属性(可选)
*/
public void addError(String log, String message, long dateline, String errorType,
AppState state, HashMap<String, Object> property)
/**
* 添加错误信息
*
* @param log 日志
* @param message 消息
* @param errorType 错误类型, ErrorType
* @param state 程序运行状态
* @param dateline 发生时间,纳秒(可选)
* @param property 附加属性(可选)
*/
fun addError(log: String, message: String, dateline: Long, errorType: ErrorType,state: AppState, property: HashMap<String, Any>)
/**
* 添加错误信息
*
* @param log 日志
* @param message 消息
* @param errorType 错误类型, String
* @param state 程序运行状态
* @param dateline 发生时间,纳秒(可选)
* @param property 附加属性(可选)
*/
fun addError(log: String, message: String, dateline: Long, errorType: String,state: AppState, property: HashMap<String, Any>)
代码示例¶
// 场景 1:
FTRUMGlobalManager.get().addError("error log", "error msg", ErrorType.JAVA, AppState.RUN);
// 场景 2:延迟记录发生的错误,这里的时间一般为错误发生的时间
FTRUMGlobalManager.get().addError("error log", "error msg", 16789000000000000000L, ErrorType.JAVA, AppState.RUN);
// 场景 3:动态参数
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key", "ft_value");
FTRUMGlobalManager.get().addError("error log", "error msg", ErrorType.JAVA, AppState.RUN, map);
// 场景 1:
FTRUMGlobalManager.get().addError("error log", "error msg", ErrorType.JAVA, AppState.RUN)
// 场景 2:延迟记录发生的错误,这里的时间一般为错误发生的时间
FTRUMGlobalManager.get().addError("error log", "error msg", 16789000000000000000, ErrorType.JAVA, AppState.RUN)
// 场景 3:动态参数
val map = HashMap<String, Any>()
map["ft_key"] = "ft_value"
FTRUMGlobalManager.get().addError("error log", "error msg",ErrorType.JAVA,AppState.RUN,map)
LongTask¶
使用方法¶
代码示例¶
Resource¶
使用方法¶
/**
* resource 起始
*
* @param resourceId 资源 Id
* @param property 附加属性参数(可选)
*/
public void startResource(String resourceId, HashMap<String, Object> property)
/**
* resource 终止
*
* @param resourceId 资源 Id
* @param property 附加属性参数(可选)
*/
public void stopResource(final String resourceId, HashMap<String, Object> property)
/**
* 设置网络传输内容
*
* @param resourceId
* @param params
* @param netStatusBean
*/
public void addResource(String resourceId, ResourceParams params, NetStatusBean netStatusBean)
/**
* resource 起始
*
* @param resourceId 资源 Id(可选)
*/
fun startResource(resourceId: String, property: HashMap<String, Any>)
/**
* resource 终止
*
* @param resourceId 资源 Id
* @param property 附加属性参数(可选)
*/
fun stopResource(resourceId: String, property: HashMap<String, Any>)
/**
* 设置网络传输内容
*
* @param resourceId
* @param params
* @param netStatusBean
*/
fun addResource(resourceId: String, params: ResourceParams, netStatusBean: NetStatusBean)
代码示例¶
// 场景 1
// 请求开始
FTRUMGlobalManager.get().startResource("resourceId");
//...
// 请求结束
FTRUMGlobalManager.get().stopResource("resourceId");
// 最后,在请求结束之后,发送请求相关的数据指标
ResourceParams params = new ResourceParams();
params.setUrl("https://guance.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);
// 场景 2 :动态参数使用
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 这个数值,会在 stopResource 时候被修改为 ft_value_change
FTRUMGlobalManager.get().stopResource(uuid,map);
// 场景 1
//请求开始
FTRUMGlobalManager.get().startResource("resourceId")
//请求结束
FTRUMGlobalManager.get().stopResource("resourceId")
//最后,在请求结束之后,发送请求相关的数据指标
val params = ResourceParams()
params.url = "https://guance.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)
// 场景 2 :动态参数使用
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 这个数值,会在 stopResource 时候被修改为 ft_value_change
FTRUMGlobalManager.get().stopResource(uuid, map)
方法名 | 必须 | 含义 | 说明 |
---|---|---|---|
NetStatusBean.fetchStartTime | 否 | 请求开始时间 | |
NetStatusBean.tcpStartTime | 否 | tcp 连接时间 | |
NetStatusBean.tcpEndTime | 否 | tcp 结束时间 | |
NetStatusBean.dnsStartTime | 否 | dns 开始时间 | |
NetStatusBean.dnsEndTime | 否 | dns 结束时间 | |
NetStatusBean.responseStartTime | 否 | 响应开始时间 | |
NetStatusBean.responseEndTime | 否 | 响应结束时间 | |
NetStatusBean.sslStartTime | 否 | ssl 开始时间 | |
NetStatusBean.sslEndTime | 否 | ssl 结束时间 | |
NetStatusBean.property | 否 | 附加属性 | |
ResourceParams.url | 是 | url 地址 | |
ResourceParams.requestHeader | 否 | 请求头参数 | |
ResourceParams.responseHeader | 否 | 响应头参数 | |
ResourceParams.responseConnection | 否 | 响应 connection | |
ResourceParams.responseContentType | 否 | 响应 ContentType | |
ResourceParams.responseContentEncoding | 否 | 响应 ContentEncoding | |
ResourceParams.resourceMethod | 否 | 请求方法 | GET,POST 等 |
ResourceParams.responseBody | 否 | 返回 body 内容 | |
ResourceParams.property | 否 | 附加属性 |
Logger 日志打印¶
通过使用 FTLogger
进行自定义日志输出,需要开启 FTLoggerConfig.setEnableCustomLog(true)
。
目前日志内容限制为 30 KB,字符超出部分会进行截断处理
使用方法¶
/**
* 将单条日志数据存入本地同步
*
* @param content 日志内容
* @param status 日志等级,enum Status
* @param property 附加属性(可选)
*/
public void logBackground(String content, Status status, HashMap<String, Object> property)
/**
* 将单条日志数据存入本地同步
*
* @param content 日志内容
* @param status 日志等级,String
* @param property 附加属性(可选)
*/
public void logBackground(String content, String status, HashMap<String, Object> property)
/**
* 将多条日志数据存入本地同步
*
* @param logDataList {@link LogData} 列表
*/
public void logBackground(List<LogData> logDataList)
/**
* 将单条日志数据存入本地同步
*
* @param content 日志内容
* @param status 日志等级
* @param property 日志属性(可选)
*/
fun logBackground(content: String, status: Status, property: HashMap<String, Any>)
/**
* 将单条日志数据存入本地同步
*
* @param content 日志内容
* @param status 日志等级
* @param property 日志属性(可选)
*/
fun logBackground(content: String, status: String, property: HashMap<String, Any>)
/**
* 将多条日志数据存入本地同步
*
* @param logDataList 日志数据列表
*/
fun logBackground(logDataList: List<LogData>)
日志等级¶
方法名 | 含义 |
---|---|
Status.DEBUG | 调试 |
Status.INFO | 提示 |
Status.WARNING | 警告 |
Status.ERROR | 错误 |
Status.CRITICAL | 严重 |
Status.OK | 恢复 |
代码示例¶
// 上传单个日志
FTLogger.getInstance().logBackground("test", Status.INFO);
// 传递参数到 HashMap
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key", "ft_value");
FTLogger.getInstance().logBackground("test", Status.INFO, map);
// 批量上传日志
List<LogData> logList = new ArrayList<>();
logList.add(new LogData("test", Status.INFO));
FTLogger.getInstance().logBackground(logList);
Tracer 网络链路追踪¶
FTTraceConfig
配置开启enableAutoTrace
自动添加链路数据,或手动使用 FTTraceManager
在 Http 请求中 Propagation Header
,示例如下:
String url = "https://request.url";
String uuid = "uuid";
// 获取链路头参数
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();
// 在请求中,添加链路头参数
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"
//获取链路头参数
val headers = FTTraceManager.get().getTraceHeader(uuid, url)
val client: OkHttpClient = OkHttpClient.Builder().addInterceptor { chain ->
val original = chain.request()
val requestBuilder = original.newBuilder()
//在请求中,添加链路头参数
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()
通过 OKHttp Interceptor 自定义 Resource 和 TraceHeader¶
FTRUMConfig
的enableTraceUserResource
,FTTraceConfig
的 enableAutoTrace
配置,同时开启,优先加载自定义 Interceptor
配置,
ft-sdk < 1.4.1,需要关闭
FTRUMConfig
的enableTraceUserResource
,FTTraceConfig
的enableAutoTrace
。 ft-sdk > 1.6.7 支持自定 Trace Header 与 RUM 数据做关联
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 以上版本支持
@Override
public String getSpanID() {
return "span_id";
}
// 1.6.7 以上版本支持
@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 读取部分 body,避免大数据消费
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) {
// 复制部分响应体以避免大数据消耗
val body = response.peekBody(33554432)
extraData["df_response_body"] = body.string()
}
}
override fun onException(e: Exception, extraData: HashMap<String, Any>) {
// 处理异常情况
}
}))
.eventListenerFactory(FTResourceEventListener.FTFactory())
.build()
ContentHandlerHelperEx 本地网络错误过滤¶
ContentHandlerHelperEx 是在 ContentHandlerHelper 上强化类,可以针对本地网络 IOException 错误进行过滤。
new FTResourceInterceptor.ContentHandlerHelperEx() {
//...
/**
* 返回网络链接过程中的异常
*
* @param e 请求发生的 IOException 数据
* @param extraData 附加数据
* @return 是否过滤本地网络 network_error 类型的错误。true,为覆盖
*/
@Override
public boolean onExceptionWithFilter(Exception e, HashMap<String, Object> extraData) {
if (e instanceof SocketTimeoutException) { //网络超时
return true;
}
return super.onExceptionWithFilter(e, extraData);
}
}
object : FTResourceInterceptor.ContentHandlerHelperEx() {
//...
/**
* 返回网络链接过程中的异常
*
* @param e 请求发生的 IOException 数据
* @param extraData 附加数据
* @return 是否过滤本地网络 network_error 类型的错误。true,为覆盖
*/
override fun onExceptionWithFilter(e: Exception, extraData: HashMap<String, Any>): Boolean {
return if (e is SocketTimeoutException) {
true
} else {
super.onExceptionWithFilter(e, extraData)
}
}
}
OKhttp 添加 ResourceID¶
对 Okhttp Request 添加 uuid,如果有相同请求高频并发场景建议开启。ft-plugin 1.3.5 以上版本,ft-sdk 1.6.10 以上版本开启FTSDKConfig.setEnableOkhttpRequestTag(true)
可以自动在 Request 添加 ResourceID
用户信息绑定与解绑¶
使用 FTSdk
进行用户的绑定和解绑
使用方法¶
UserData¶
方法名 | 含义 | 必须 | 注意 |
---|---|---|---|
setId | 设置用户 ID | 否 | |
setName | 设置用户名 | 否 | |
setEmail | 设置邮箱 | 否 | |
setExts | 设置用户扩展 | 否 | 添加规则请查阅此处 |
代码示例¶
// 可以在用户登录成功后调用此方法用来绑定用户信息
FTSdk.bindRumUserData("001");
UserData userData = new UserData();
userData.setName("test.user");
userData.setId("test.id");
userData.setEmail("test@mail.com");
Map<String, String> extMap = new HashMap<>();
extMap.put("ft_key", "ft_value");
userData.setExts(extMap);
FTSdk.bindRumUserData(userData);
// 可以在用户退出登录后调用此方法来解绑用户信息
FTSdk.unbindRumUserData();
//可以在用户登录成功后调用此方法用来绑定用户信息
FTSdk.bindRumUserData("001")
//绑定用户更多数据
val userData = UserData()
userData.name = "test.user"
userData.id = "test.id"
userData("test@mail.com")
val extMap = HashMap<String, String>()
extMap["ft_key"] = "ft_value"
userData.setExts(extMap)
FTSdk.bindRumUserData(userData)
//可以在用户退出登录后调用此方法来解绑用户信息
FTSdk.unbindRumUserData()
关闭 SDK¶
使用 FTSdk
关闭 SDK, 如果动态改变 SDK 配置,需要先关闭,以避免错误数据的产生
清理 SDK 缓存数据¶
使用 FTSdk
清理未上报的缓存数据
主动同步数据¶
使用 FTSdk
主动同步数据。
FTSdk.setAutoSync(false) 时, 才需要自行进行数据同步
主动同步动态配置¶
使用 FTSdk
主动同步动态配置。在自动更新不满足需求时,通过主动调用方式,调整更新时机。
/**
* 主动更新远程远程配置,调用频次受 FTSDKConfig.setRemoteConfigMiniUpdateInterval 影响
*/
FTSdk.updateRemoteConfig();
/**
* 主动更新远程远程配置,这个方法无视 FTSDKConfig.setRemoteConfigMiniUpdateInterva 配置
*
* @param remoteConfigMiniUpdateInterval 远程配置时间间隔,单位秒 [0,)
* @param result 返回更新结果
*/
FTSdk.updateRemoteConfig(int remoteConfigMiniUpdateInterval, FTRemoteConfigManager.FetchResult result);
/**
* 主动更新远程远程配置,调用频次受 FTSDKConfig.setRemoteConfigMiniUpdateInterval 影响
*/
FTSdk.updateRemoteConfig()
/**
* 主动更新远程远程配置,这个方法无视 FTSDKConfig.setRemoteConfigMiniUpdateInterva 配置
*
* @param remoteConfigMiniUpdateInterval 远程配置时间间隔,单位秒 [0,)
* @param result 返回更新结果
*/
FTSdk.updateRemoteConfig(remoteConfigMiniUpdateInterval:Int,result:FTRemoteConfigManager.FetchResult)
动态开启和关闭获取 AndroidID¶
使用 FTSdk
设置是否在 SDK中获取 Android ID
添加自定义标签¶
使用 FTSdk
在 SDK运行时,动态添加标签
使用方法¶
/**
* 动态设置全局 tag
* @param globalContext
*/
public static void appendGlobalContext(HashMap<String,Object> globalContext)
/**
* 动态设置 RUM 全局 tag
* @param globalContext
*/
public static void appendRUMGlobalContext(HashMap<String,Object> globalContext)
/**
* 动态设置 log 全局 tag
* @param globalContext
*/
public static void appendLogGlobalContext(HashMap<String,Object> globalContext)
/**
* 动态设置全局 tag
* @param globalContext
*/
fun appendGlobalContext(globalContext: HashMap<String, Any>)
/**
* 动态设置 RUM 全局 tag
* @param globalContext
*/
fun appendRUMGlobalContext(globalContext: HashMap<String, Any>)
/**
* 动态设置 log 全局 tag
* @param globalContext
*/
fun appendLogGlobalContext(globalContext: HashMap<String, Any>)
代码示例¶
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)
符号文件上传¶
plugin 上传 (仅支持 datakit【本地部署】)¶
ft-plugin
版本需要 1.3.0
以上版本支持最新的符号文件上传规则,支持 productFlavor
多版本区分管理,plugin 会在 gradle task assembleRelease
之后执行上传符号文件,详细配置可以参考 SDK Demo
FTExt {
//...
autoUploadMap = true // 上报 mapping.txt 文件,默认为 false
autoUploadNativeDebugSymbol = true // 上报 c/c++ symbol so 文件,默认为 false
datakitUrl = 'https://datakit.url' // datakit 上报地址, generateSourceMapOnly=true 时,无需配置
datawayToken = 'dataway_token' // 空间 token , generateSourceMapOnly=true 时,无需配置
appId = "appid_xxxxx" // appid , generateSourceMapOnly=true 时,无需配置
env = 'common' // 环境, generateSourceMapOnly=true 时,无需配置
// native so 指定路径,只要指定到 abi 文件的上层目录
// |-stripped_native_libs
// |-release
// |-out
// |-lib
// |-arm64-v8a
// |-armeabi-v7a
// |-...
//nativeLibPath='/build/intermediates/merged_native_libs/release/out/lib'
generateSourceMapOnly = false //仅生成 sourcemap,默认为 false,路径示例:/app/build/tmp/ft{flavor}SourceMapMerge-release.zip,ft-plugin:1.3.4 以上版本支持
prodFlavors { //prodFlavors 配置会覆盖外层设置
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"
}
}
}
手动上传¶
使用 plugin
开启 generateSourceMapOnly = true
, 执行 gradle task assembleRelease
生成,或自行打包成 zip
文件,然后自行上传至 datakit
或从观测云 Studio 上传,推荐使用 zip
命令行进行打包,避免将一些系统隐藏文件打入 zip
包中,符号上传请参考 sourcemap 上传
Unity Native Symbol 文件请参考官方文档
权限配置说明¶
名称 | 必须 | 使用原因 |
---|---|---|
READ_PHONE_STATE |
否 | 用于获取手机的蜂窝网设备信息 |
关于如何申请动态权限,具体详情参考 Android Developer
Plugin AOP 忽略¶
通过 Plugin AOP 覆盖方法中添加 @IngoreAOP
来忽略 ASM 插入。如果需要批量忽略,请通过使用 ft-plugin
FTExt
中 ignorePackages
进行忽略。
WebView 数据监测¶
WebView 数据监测,需要在 WebView 访问页面集成Web 监测 SDK
数据脱敏¶
如果是希望对字段做全脱敏,推荐使用 setDataModifier
,表现的性能更好。如果需要细致规则替换推荐 setLineDataModifier
请勿在回调方法中使用复杂或者延迟较高的方法,这会极大影响 SDK 数据写入性能
FTSdk.install(
FTSDKConfig.builder("xxx")
.setDataModifier(new DataModifier() {
/**
* 对某个字段进行更改
*
* @param key 字段名
* @param value 字段值(原始值)
* @return 新的值,返回 null 表示不做更改
*/
@Override
public Object modify(String key, Object value) {
if (key.equals("device_uuid")) {
return "xxx";
}
return null;
}
}).setLineDataModifier(new LineDataModifier() {
/***
* 对某一行数据进行修改
*
* @param measurement 数据指标类型 view,action,resource,
* longtask,error,df_rum_android_log
* @param data 原始数据的 key-value 对
* @return 需要修改的 key-value,(返回 null 或空 map 均为不更改)
*/
@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 {
/**
* 对某个字段进行更改
*
* @param key 字段名
* @param value 字段值(原始值)
* @return 新的值,返回 null 表示不做更改
*/
override fun modify(key: String, value: Any?): Any? {
return if (key == "device_uuid") {
"xxx" // 替换为自定义 device_uuid
} else {
null
}
}
})
// 批量修改单条数据中的某些字段
.setLineDataModifier(object : LineDataModifier {
/**
* 对某一行数据进行修改
*
* @param measurement 数据指标类型 view,action,resource,
* longtask,error,df_rum_android_log
* @param data 原始数据的 key-value 对
* @return 需要修改的 key-value,(返回 null 或空 map 均为不更改)
*/
override fun modify(
measurement: String,
data: HashMap<String, Any>
): Map<String, Any>? {
return if (measurement == "view") {
hashMapOf("view_url" to "xxx")
} else {
null
}
}
})
自定义标签使用示例¶
编译配置方式¶
- 在
build.gradle
中创建多个productFlavors
来做区分区分标签
android{
//…
productFlavors {
prodTest {
buildConfigField "String", "CUSTOM_VALUE", "\"Custom Test Value\""
//…
}
prodPublish {
buildConfigField "String", "CUSTOM_VALUE", "\"Custom Publish Value\""
//…
}
}
}
- 在
RUM
配置中添加对应BuildConfig
常量
运行时读写文件方式¶
- 通过存文件类型数据,例如
SharedPreferences
,配置使用SDK
,在配置处添加获取标签数据的代码。
- 在任意处添加改变文件数据的方法。
3.最后重启应用,详细细节请见 SDK Demo
SDK 运行时添加¶
在 SDK 初始化完毕之后,使用FTSdk.appendGlobalContext(globalContext)
、FTSdk.appendRUMGlobalContext(globalContext)
、FTSdk.appendLogGlobalContext(globalContext)
,可以动态添加标签,设置完毕,会立即生效。随后,RUM 或 Log 后续上报的数据会自动添加标签数据。这种使用方式适合延迟获取数据的场景,例如标签数据需要网络请求获取。
//SDK 初始化伪代码,从网络获取到参数后,再进行标签设置
FTSdk.init()
getInfoFromNet(info){
HashMap<String, Object> globalContext = new HashMap<>();
globalContext.put("delay_key", info.value);
FTSdk.appendGlobalContext(globalContext)
}
常见问题¶
添加局变量避免冲突字段¶
为了避免自定义字段与 SDK 数据冲突,建议标签命名添加 项目缩写 的前缀,例如 df_tag_name
,项目中使用 key
值可查询源码。SDK 全局变量中出现与 RUM、Log 相同变量时,RUM、Log 会覆盖 SDK 中的全局变量。
SDK 兼容性¶
应对市场隐私审核¶
隐私声明¶
方式 1: SDK AndroidID 配置¶
SDK 为更好关联相同用户数据,会使用 Android ID。如果需要在应用市场上架,需要通过如下方式对应市场隐私审核。
public class DemoApplication extends Application {
@Override
public void onCreate() {
// 在初始化设置时将 setEnableAccessAndroidID 设置为 false
FTSDKConfig config = new FTSDKConfig.Builder(DATAKIT_URL)
.setEnableAccessAndroidID(false)
.build();
FTSdk.install(config);
// ...
}
}
// 用户同意隐私协议后再开启
FTSdk.setEnableAccessAndroidID(true);
方式 2:延迟初始化 SDK¶
如果需要在应用中延迟加载 SDK,建议使用如下方式初始化。
// Application
public class DemoApplication extends Application {
@Override
public void onCreate() {
//如果已经同意协议,在 Application 中初始化
if(agreeProtocol){
FTSdk.init(); //SDK 初始化伪代码
}
}
}
// 隐私声明 Activity 页面
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
//未阅读隐私声明
if ( notReadProtocol ) {
//隐私声明弹出弹窗
showProtocolView();
//如果同意隐私声明
if( agreeProtocol ){
FTSdk.init(); //SDK 初始化伪代码
}
}
}
}
// Application
class DemoApplication : Application() {
override fun onCreate() {
// 如果已经同意协议,在 Application 中初始化
if (agreeProtocol) {
FTSdk.init() //SDK 初始化伪代码
}
}
}
// 隐私声明 Activity 页面
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
// 未阅读隐私声明
if (notReadProtocol) {
// 隐私声明弹出弹窗
showProtocolView()
// 如果同意隐私声明
if (agreeProtocol) {
FTSdk.init() //SDK 初始化伪代码
}
}
}
}
第三方框架¶
flutter
、react-native
、uni-app
、unity
可以采用与以上原生 Android 相似延迟初始化方式,来应对应用市场隐私审核。
Jetpack Compose 支持¶
目前暂时不支持自动采集 compose 组件生成的页面,但是可以通过手动 Action
和 View
的自定义接口,点击事件和页面跳转事件进行追踪,可以参考这里
不使用 ft-plugin 情况下如何接入 SDK¶
观测云使用的 Androig Grale Plugin Transformation 实现的代码注入,从而实现数据自动收集。但是由于一些兼容性问题,可能存在无法使用 ft-plugin
的问题。受影响包括 RUM Action
,Resource
,和 android.util.Log
,Java 与 Kotlin println
控制台日志自动抓取,以及符号文件的自动上传。
目前针对这种情况,我们有另外一种集成方案,应对方案如下:
- Application 应用启动事件, 源码示例参考DemoForManualSet.kt
- 按键等事件需要在触发处自行添加,例如,Button onClick 事件为例,源码示例参考 ManualActivity.kt:
OKhttp
通过addInterceptor
,eventListener
方式接入Resource
,Trace
,示例如下,源码示例参考 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();
-
其他网络框架需要自行实现使用
FTRUMGlobalManager
中startResource
,stopResource
,addResource
,FTTraceManager.getTraceHeader
。具体实现方式,请参考源码示例 ManualActivity.kt -
WebView 数据采集配置