Data Collection Custom Rules¶
View¶
Activity¶
- First, you need to enable it via
FTRUMConfig.setEnableTraceUserView(true)
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(activity: 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¶
- You need to enable both
FTRUMConfig.setEnableTraceUserView(true)andFTRUMConfig.setEnableTraceUserViewInFragment(true)
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 - Purpose: Customize Action name, properties, or filter Action collection based on specific View
FTSdk.initRUMWithConfig(new FTRUMConfig()
.setActionTrackingHandler(new FTActionTrackingHandler() {
@Override
public HandlerAction resolveHandlerAction(ActionEventWrapper actionEventWrapper) {
// Get operation type
ActionSourceType actionType = actionEventWrapper.getSourceType();
Object source = actionEventWrapper.getSource();
// Filter Actions for specified View
if (source instanceof View) {
View view = (View) source;
if (view.getId() == R.id.ignore_action_view) {
return null;
}
}
// Customize tracking based on operation 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? {
val source = actionEventWrapper.source
// Filter Actions for specified View
if (source is View && source.id == R.id.ignore_action_view) {
return null
}
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
}
}
})
)
Note
You can get the source object of the current operation via ActionEventWrapper.getSource(). For Actions triggered by View, such as normal clicks, list item clicks, Tab clicks, etc., the source is usually the corresponding View. After setting setActionTrackingHandler, the SDK will use the handler's return result as the basis for Action collection. Actions returning null will be skipped; Actions that need to continue collection should return HandlerAction.
Resource¶
- Requires
FTRUMConfig.setEnableTraceUserResource(true)to be enabled
Collection Filtering Based on Url¶
Adding Custom Properties¶
Can be done via FTRUMConfig.setOkHttpResourceContentHandler(FTResourceInterceptor.ContentHandlerHelperEx)
new FTResourceInterceptor.ContentHandlerHelperEx() {
@Override
public void onRequest(Request request, HashMap<String, Object> extraData) {
extraData.put("df_custom_key","custom_value");
}
@Override
public void onResponse(Response response, HashMap<String, Object> extraData) throws IOException {
extraData.put("df_custom_key","custom_value");
}
@Override
public boolean onExceptionWithFilter(Exception e, HashMap<String, Object> extraData) {
extraData.put("df_custom_key","custom_value");
return false;
}
}
object : FTResourceInterceptor.ContentHandlerHelperEx() {
override fun onRequest(
request: Request,
extraData: HashMap<String, Any>
) {
extraData["df_custom_key"] = "custom_value"
}
override fun onResponse(
response: Response,
extraData: HashMap<String, Any>
) {
extraData["df_custom_key"] = "custom_value"
}
override fun onExceptionWithFilter(
e: Exception,
extraData: HashMap<String, Any>
): Boolean {
extraData["df_custom_key"] = "custom_value"
return false
}
}
Filtering Network Errors¶
You can filter local network IOException errors. Can be set via FTRUMConfig.setOkHttpResourceContentHandler(FTResourceInterceptor.ContentHandlerHelperEx), or Custom Resource
new FTResourceInterceptor.ContentHandlerHelperEx() {
//...
/**
* Returns exceptions during network connection
*
* @param e IOException data from the request
* @param extraData Additional data
* @return Whether to filter local network errors of type `network_error`. true means filter and not collect
*/
@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() {
//...
/**
* Returns exceptions during network connection
*
* @param e IOException data from the request
* @param extraData Additional data
* @return Whether to filter local network errors of type `network_error`. true means override and not collect
*/
override fun onExceptionWithFilter(e: Exception, extraData: HashMap<String, Any>): Boolean {
return if (e is SocketTimeoutException) {
true
} else {
super.onExceptionWithFilter(e, extraData)
}
}
}
Custom TraceHeader¶
Can be set via FTTraceConfig.setOkHttpTraceHeaderHandler(FTTraceInterceptor.HeaderHandler), or Custom Trace, using w3c-traceContext as an example below
new FTTraceInterceptor.HeaderHandler() {
private String[] splits;
@Override
public HashMap<String, String> getTraceHeader(Request request) {
HashMap<String, String> map = new HashMap<>();
//Get custom trace_id from header
String replaceTrace = request.header(CUSTOM_TRACE_HEADER);
String headerString = FTTraceManager.get().
getTraceHeader(request.url().toString())
// Get propagation header from header
.get(FTTraceHandler.W3C_TRACEPARENT_KEY);
splits = headerString.split("-");
String originTraceId = splits[1];
splits[1] = replaceTrace;
map.put(FTTraceHandler.W3C_TRACEPARENT_KEY, headerString.replace(originTraceId, replaceTrace));
return map;
}
@Override
public String getSpanID() {
if (splits != null) {
return splits[2];
}
return null;
}
@Override
public String getTraceID() {
if (splits != null) {
return splits[1];
}
return null;
}
};
object : FTTraceInterceptor.HeaderHandler() {
private var splits: List<String>? = null
override fun getTraceHeader(request: Request): HashMap<String, String> {
val map = HashMap<String, String>()
// Get custom trace_id from header
val replaceTrace = request.header(CUSTOM_TRACE_HEADER)
val headerString = FTTraceManager.get()
.getTraceHeader(request.url().toString())
.get(FTTraceHandler.W3C_TRACEPARENT_KEY)
splits = headerString?.split("-")
val originTraceId = splits?.getOrNull(1)
if (headerString != null && originTraceId != null && replaceTrace != null) {
map[FTTraceHandler.W3C_TRACEPARENT_KEY] =
headerString.replace(originTraceId, replaceTrace)
}
return map
}
override fun getSpanID(): String? {
return splits?.getOrNull(2)
}
override fun getTraceID(): String? {
return splits?.getOrNull(1)
}
}
Associating RUM Resource Using Custom trace_id¶
If the application side already has a custom trace_id, for example, the request header key is custom_trace_id, you can update the trace_id based on the traceparent generated by the SDK, and specify the trace_id used for RUM Resource association via TraceContext.Simple.
new FTTraceInterceptor.HeaderHandler() {
@Override
public TraceContext getTraceContext(Request request) {
HashMap<String, String> headers = FTTraceManager.get()
.getTraceHeader(request.url().toString());
// Get custom trace_id from request header, e.g., custom_trace_id
String customTraceId = request.header(CUSTOM_TRACE_HEADER);
TraceContext.Simple sdkContext = TraceContext.Simple.fromTraceType(headers);
if (customTraceId == null
|| customTraceId.length() == 0
|| sdkContext == null
|| sdkContext.getTraceId() == null) {
return sdkContext;
}
String traceparent = headers.get(FTTraceHandler.W3C_TRACEPARENT_KEY);
if (traceparent == null) {
return sdkContext;
}
String newTraceparent = traceparent.replace(
sdkContext.getTraceId(),
customTraceId
);
// Update W3C trace header injected into the request
headers.put(FTTraceHandler.W3C_TRACEPARENT_KEY, newTraceparent);
// Optional: Keep custom business header
headers.put(CUSTOM_TRACE_HEADER, customTraceId);
// RUM Resource association uses customTraceId, spanId continues to use SDK-generated spanId
return new TraceContext.Simple(headers, customTraceId, sdkContext.getSpanId());
}
};
object : FTTraceInterceptor.HeaderHandler() {
override fun getTraceContext(request: Request): TraceContext? {
val headers = FTTraceManager.get()
.getTraceHeader(request.url().toString())
// Get custom trace_id from request header
val customTraceId = request.header(CUSTOM_TRACE_HEADER)
val sdkContext = TraceContext.Simple.fromTraceType(headers)
val sdkTraceId = sdkContext?.traceId
val sdkSpanId = sdkContext?.spanId
if (customTraceId.isNullOrEmpty() || sdkTraceId.isNullOrEmpty()) {
return sdkContext
}
val traceparent = headers[FTTraceHandler.W3C_TRACEPARENT_KEY] ?: return sdkContext
val newTraceparent = traceparent.replace(sdkTraceId, customTraceId)
// Update W3C trace header injected into the request
headers[FTTraceHandler.W3C_TRACEPARENT_KEY] = newTraceparent
// Optional: Keep custom business header
headers[CUSTOM_TRACE_HEADER] = customTraceId
// RUM Resource association uses customTraceId, spanId continues to use SDK-generated spanId
return TraceContext.Simple(headers, customTraceId, sdkSpanId)
}
}
Note: W3C trace_id must be a 32-character hexadecimal string and cannot be all 0s.