Skip to content

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 the view name based on the 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) and FTRUMConfig.setEnableTraceUserViewInFragment(true).
FTSdk.initRUMWithConfig(new FTRUMConfig()
    .setViewFragmentTrackingHandler(new FTViewFragmentTrackingHandler() {
        @Override
        public HandlerView resolveHandlerView(FragmentWrapper fragment) {
            String fragmentName = fragment.getSimpleClassName();

            // Customize the view name based on the 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 names, properties, or filter Action collection based on specific Views.
FTSdk.initRUMWithConfig(new FTRUMConfig()
    .setActionTrackingHandler(new FTActionTrackingHandler() {
        @Override
        public HandlerAction resolveHandlerAction(ActionEventWrapper actionEventWrapper) {
            // Get the operation type.
            ActionSourceType actionType = actionEventWrapper.getSourceType();
            Object source = actionEventWrapper.getSource();

            // Filter Actions for a specific View.
            if (source instanceof View) {
                View view = (View) source;
                if (view.getId() == R.id.ignore_action_view) {
                    return null;
                }
            }

            // Customize tracking based on the 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 a specific 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 Views, 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 return result of this handler 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.

Filter Collection Based on URL

FTSdk.initRUMWithConfig(new FTRUMConfig()
    .setResourceUrlHandler(new FTInTakeUrlHandler() {
        @Override
        public boolean isInTakeUrl(String url) {
            // Return true means do not collect; return false means collect.
            return url.startsWith("https://url.rule");
        }
    })
);
FTSdk.initRUMWithConfig(
    FTRUMConfig()
        .setResourceUrlHandler(object : FTInTakeUrlHandler {
            override fun isInTakeUrl(url: String): Boolean {
                // Return true means do not collect; return false means collect.
                return url.startsWith("https://url.rule")
            }
        })
)

Add Custom Properties

You can use 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
    }
}   

Filter Network Errors

You can filter local network IOException errors. You can set it via FTRUMConfig.setOkHttpResourceContentHandler(FTResourceInterceptor.ContentHandlerHelperEx), or Custom Resource.

new FTResourceInterceptor.ContentHandlerHelperEx() {
    //...

    /**
     * Returns exceptions that occur during network connection.
     *
     * @param e         IOException data that occurred during the request.
     * @param extraData Additional data.
     * @return Whether to filter local network errors of type `network_error`. true means filter and do 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 that occur during network connection.
    *
    * @param e         IOException data that occurred during the request.
    * @param extraData Additional data.
    * @return Whether to filter local network errors of type `network_error`. true means override and do 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

You can set it via FTTraceConfig.setOkHttpTraceHeaderHandler(FTTraceInterceptor.HeaderHandler), or Custom Trace. The following example uses w3c-traceContext.

 new FTTraceInterceptor.HeaderHandler() {
        private String[] splits;

        @Override
        public HashMap<String, String> getTraceHeader(Request request) {
            HashMap<String, String> map = new HashMap<>();
            // Get the custom trace_id from the header.
            String replaceTrace = request.header(CUSTOM_TRACE_HEADER);
            String headerString = FTTraceManager.get().
                    getTraceHeader(request.url().toString())
                    // Get the propagation header from the 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 the custom trace_id from the 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)
    }
}

Associate 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 the custom trace_id from the 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 the W3C trace header injected into the request.
        headers.put(FTTraceHandler.W3C_TRACEPARENT_KEY, newTraceparent);

        // Optional: Keep the custom business header.
        headers.put(CUSTOM_TRACE_HEADER, customTraceId);

        // RUM Resource association uses customTraceId, spanId continues to use the 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 the custom trace_id from the 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 the W3C trace header injected into the request.
        headers[FTTraceHandler.W3C_TRACEPARENT_KEY] = newTraceparent

        // Optional: Keep the custom business header.
        headers[CUSTOM_TRACE_HEADER] = customTraceId

        // RUM Resource association uses customTraceId, spanId continues to use the SDK-generated spanId.
        return TraceContext.Simple(headers, customTraceId, sdkSpanId)
    }
}

Note: The W3C trace_id must be a 32-character hexadecimal string and cannot be all 0s.