Skip to content

Trace Configuration

Trace Initialization Configuration

FTSdk.initTraceWithConfig(new FTTraceConfig()
    .setSamplingRate(0.8f)
    .setEnableAutoTrace(true)
    .setEnableLinkRUMData(true));
FTSdk.initTraceWithConfig(
    FTTraceConfig()
        .setSamplingRate(0.8f)
        .setEnableAutoTrace(true)
        .setEnableLinkRUMData(true)
)
Method Name Type Required Description
setSamplingRate Float No Sets the sampling rate, range [0,1]. 0 means no sampling, 1 means full sampling. Default is 1.
setTraceType TraceType No Sets the type of APM. Default is DDTrace. Currently supports Zipkin, Jaeger, DDTrace, Skywalking (8.0+), TraceParent (W3C). If integrating OpenTelemetry, please check the supported types and agent configurations when selecting the corresponding APM type.
setEnableLinkRUMData Boolean No Whether to correlate with RUM data. Default is false.
setEnableAutoTrace Boolean No Sets whether to enable automatic HTTP Trace. This configuration depends on ft-plugin. Currently only supports automatic tracing for OkHttp. Default is false.
setOkHttpTraceHeaderHandler Callback No ASM sets the global FTTraceInterceptor.HeaderHandler. Not set by default. Supported from ft-sdk 1.6.8 onwards. Starting from ft-sdk 1.6.17, priority support for overriding getTraceContext(Request), which can return Header, traceId, and spanId at once. Example reference Custom Trace.

Tracer Network APM

The FTTraceConfig configuration enables enableAutoTrace to automatically add APM data, or manually use FTTraceManager to add Propagation Header in HTTP requests, as shown in the following example.

String url = "https://request.url";
String uuid = "uuid";
// Get APM 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 APM header parameters to 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 APM 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 APM header parameters to the request
    for (key in headers.keys) {
        requestBuilder.header(key!!, headers[key]!!)
    }
    val request = requestBuilder.build()

    val 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()

Customizing Resource and TraceHeader via OkHttp Interceptor

Resource

When FTRUMConfig.setEnableTraceUserResource(true) or FTRUMConfig.setOkHttpResourceContentHandler(...) is enabled, the custom Interceptor in OkHttp will be loaded first.

new OkHttpClient.Builder()
    .addInterceptor(new FTTraceInterceptor())
    .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 and read part of the body to avoid consumption from large data processing
                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())
    .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 exception cases
        }
    }))
    .eventListenerFactory(FTResourceEventListener.FTFactory())
    .build()

TraceHeader

When FTTraceConfig.setEnableAutoTrace(true) or FTTraceConfig.setOkHttpTraceHeaderHandler(...) is enabled, the custom Interceptor in OkHttp will be loaded first. The following example uses W3C Trace Context.

When ft-sdk < 1.4.1, you need to disable FTTraceConfig.setEnableAutoTrace(false). ft-sdk > 1.6.7 supports custom Trace Header correlation with RUM data. ft-sdk >= 1.6.17 adds getTraceContext(Request), which can return request Header, traceId, and spanId at once. It is recommended to use this method first.

new OkHttpClient.Builder()
    .addInterceptor(new FTTraceInterceptor(new FTTraceInterceptor.HeaderHandler() {
        @Override
        public TraceContext getTraceContext(Request request) {
            HashMap<String, String> headers = FTTraceManager.get()
                    .getTraceHeader(request.url().toString());
            return TraceContext.Simple.fromTraceType(headers);
        }
    }))
    .addInterceptor(new FTResourceInterceptor())
    .eventListenerFactory(new FTResourceEventListener.FTFactory())
    .build();
OkHttpClient.Builder()
    .addInterceptor(
        FTTraceInterceptor(object : FTTraceInterceptor.HeaderHandler() {
            override fun getTraceContext(request: Request): TraceContext? {
                val headers = FTTraceManager.get().getTraceHeader(request.url.toString())
                return TraceContext.Simple.fromTraceType(headers)
            }
        })
    )
    .addInterceptor(FTResourceInterceptor())
    .eventListenerFactory(FTResourceEventListener.FTFactory())
    .build()

Compatible Old Method: Implementing getTraceHeader / getTraceID / getSpanID Separately

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

        @Override
        public HashMap<String, String> getTraceHeader(Request request) {
            HashMap<String, String> map = new HashMap<>();

            String headerString = FTTraceManager.get()
                    .getTraceHeader(request.url().toString())
                    // Get propagation header from header
                    .get(FTTraceHandler.W3C_TRACEPARENT_KEY);

            splits = headerString.split("-");
            map.put(W3C_TRACEPARENT_KEY, headerString);
            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;
        }
    }))
    .addInterceptor(new FTResourceInterceptor())
    .eventListenerFactory(new FTResourceEventListener.FTFactory())
    .build();
OkHttpClient.Builder()
    .addInterceptor(
        FTTraceInterceptor(object : FTTraceInterceptor.HeaderHandler {
            private var splits: Array<String>? = null

            override fun getTraceHeader(request: Request): HashMap<String, String> {
                val map = HashMap<String, String>()

                val headerString = FTTraceManager.get()
                    .getTraceHeader(request.url.toString())
                    // Get propagation header from header
                    .get(FTTraceHandler.W3C_TRACEPARENT_KEY)

                splits = headerString?.split("-")?.toTypedArray()

                headerString?.let {
                    map[W3C_TRACEPARENT_KEY] = it
                }

                return map
            }

            override fun getSpanID(): String? {
                return splits?.getOrNull(2)
            }

            override fun getTraceID(): String? {
                return splits?.getOrNull(1)
            }
        })
    )
    .addInterceptor(FTResourceInterceptor())
    .eventListenerFactory(FTResourceEventListener.FTFactory())
    .build()

Adding ResourceID to OkHttp

Add uuid to OkHttp Request. It is recommended to enable this for high-frequency concurrent scenarios with the same request. For ft-plugin version 1.3.5 and above, ft-sdk version 1.6.10 and above, after enabling FTSDKConfig.setEnableOkhttpRequestTag(true), ResourceID can be automatically added to Request.

new Request.Builder()
    .tag(ResourceID.class, new ResourceID());
Request.Builder()
    .tag(ResourceID::class.java, ResourceID())