From 90b21ff7b9ed5e478fc704a126e60c907804dca2 Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Mon, 3 Nov 2025 14:46:52 -0600 Subject: [PATCH 01/23] Initial dsm extractors configuration --- .../test/InstrumentationSpecification.groovy | 40 +++++----- .../trace/api/config/GeneralConfig.java | 2 + .../java/datadog/trace/core/CoreTracer.java | 14 ++++ .../DataStreamsTransactionExtractors.java | 78 +++++++++++++++++++ ...ataStreamsTransactionExtractorsTest.groovy | 22 ++++++ .../main/java/datadog/trace/api/Config.java | 8 ++ .../java/datadog/trace/api/DynamicConfig.java | 22 ++++++ .../java/datadog/trace/api/TraceConfig.java | 8 ++ .../DataStreamsTransactionExtractor.java | 25 ++++++ .../instrumentation/api/AgentTracer.java | 6 ++ 10 files changed, 207 insertions(+), 18 deletions(-) create mode 100644 dd-trace-core/src/main/java/datadog/trace/core/datastreams/DataStreamsTransactionExtractors.java create mode 100644 dd-trace-core/src/test/groovy/datadog/trace/core/datastreams/DataStreamsTransactionExtractorsTest.groovy create mode 100644 internal-api/src/main/java/datadog/trace/api/datastreams/DataStreamsTransactionExtractor.java diff --git a/dd-java-agent/instrumentation-testing/src/main/groovy/datadog/trace/agent/test/InstrumentationSpecification.groovy b/dd-java-agent/instrumentation-testing/src/main/groovy/datadog/trace/agent/test/InstrumentationSpecification.groovy index 6da827a1b76..66785b71ee5 100644 --- a/dd-java-agent/instrumentation-testing/src/main/groovy/datadog/trace/agent/test/InstrumentationSpecification.groovy +++ b/dd-java-agent/instrumentation-testing/src/main/groovy/datadog/trace/agent/test/InstrumentationSpecification.groovy @@ -1,5 +1,15 @@ package datadog.trace.agent.test +import static datadog.communication.http.OkHttpUtils.buildHttpClient +import static datadog.trace.api.ConfigDefaults.DEFAULT_AGENT_HOST +import static datadog.trace.api.ConfigDefaults.DEFAULT_AGENT_TIMEOUT +import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_AGENT_PORT +import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_SNAPSHOT_URL +import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_VERIFY_BYTECODE +import static datadog.trace.api.config.TraceInstrumentationConfig.CODE_ORIGIN_FOR_SPANS_ENABLED +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.closePrevious +import static datadog.trace.util.AgentThreadFactory.AgentThread.TASK_SCHEDULER + import ch.qos.logback.classic.Level import ch.qos.logback.classic.util.ContextInitializer import com.datadog.debugger.agent.ClassesToRetransformFinder @@ -32,6 +42,7 @@ import datadog.trace.api.TraceConfig import datadog.trace.api.config.GeneralConfig import datadog.trace.api.config.TracerConfig import datadog.trace.api.datastreams.AgentDataStreamsMonitoring +import datadog.trace.api.datastreams.DataStreamsTransactionExtractor import datadog.trace.api.sampling.SamplingRule import datadog.trace.api.time.SystemTimeSource import datadog.trace.bootstrap.ActiveSubsystems @@ -55,6 +66,13 @@ import de.thetaphi.forbiddenapis.SuppressForbidden import edu.umd.cs.findbugs.annotations.SuppressFBWarnings import groovy.transform.stc.ClosureParams import groovy.transform.stc.SimpleType +import java.lang.instrument.ClassFileTransformer +import java.lang.instrument.Instrumentation +import java.nio.ByteBuffer +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.TimeUnit +import java.util.concurrent.TimeoutException +import java.util.concurrent.atomic.AtomicInteger import net.bytebuddy.agent.ByteBuddyAgent import net.bytebuddy.agent.builder.AgentBuilder import net.bytebuddy.description.type.TypeDescription @@ -68,24 +86,6 @@ import org.slf4j.LoggerFactory import org.spockframework.mock.MockUtil import spock.lang.Shared -import java.lang.instrument.ClassFileTransformer -import java.lang.instrument.Instrumentation -import java.nio.ByteBuffer -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.TimeUnit -import java.util.concurrent.TimeoutException -import java.util.concurrent.atomic.AtomicInteger - -import static datadog.communication.http.OkHttpUtils.buildHttpClient -import static datadog.trace.api.ConfigDefaults.DEFAULT_AGENT_HOST -import static datadog.trace.api.ConfigDefaults.DEFAULT_AGENT_TIMEOUT -import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_AGENT_PORT -import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_SNAPSHOT_URL -import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_VERIFY_BYTECODE -import static datadog.trace.api.config.TraceInstrumentationConfig.CODE_ORIGIN_FOR_SPANS_ENABLED -import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.closePrevious -import static datadog.trace.util.AgentThreadFactory.AgentThread.TASK_SCHEDULER - /** * A specification that automatically applies instrumentation and exposes a global trace * writer. @@ -249,6 +249,10 @@ abstract class InstrumentationSpecification extends DDSpecification implements A List getTraceSamplingRules() { return null } + + List getDataStreamsTransactionExtractors() { + return null + } } boolean originalAppSecRuntimeValue diff --git a/dd-trace-api/src/main/java/datadog/trace/api/config/GeneralConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/GeneralConfig.java index 2e441e2677b..2ecba77d301 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/config/GeneralConfig.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/config/GeneralConfig.java @@ -88,6 +88,8 @@ public final class GeneralConfig { public static final String DATA_STREAMS_ENABLED = "data.streams.enabled"; public static final String DATA_STREAMS_BUCKET_DURATION_SECONDS = "data.streams.bucket_duration.seconds"; + public static final String DATA_STREAMS_TRANSACTION_EXTRACTORS = + "data.streams.transaction_extractors"; public static final String TELEMETRY_ENABLED = "instrumentation.telemetry.enabled"; public static final String TELEMETRY_HEARTBEAT_INTERVAL = "telemetry.heartbeat.interval"; diff --git a/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java b/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java index 05c213a9c18..803b3a90136 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java @@ -86,6 +86,7 @@ import datadog.trace.context.TraceScope; import datadog.trace.core.baggage.BaggagePropagator; import datadog.trace.core.datastreams.DataStreamsMonitoring; +import datadog.trace.core.datastreams.DataStreamsTransactionExtractors; import datadog.trace.core.datastreams.DefaultDataStreamsMonitoring; import datadog.trace.core.histogram.Histograms; import datadog.trace.core.monitor.HealthMetrics; @@ -679,6 +680,16 @@ private CoreTracer( } else { traceSamplingRules = TraceSamplingRules.deserialize(traceSamplingRulesJson); } + + DataStreamsTransactionExtractors dataStreamsTransactionExtractors; + String dataStreamsTransactionExtractorsJson = config.getDataStreamsTransactionExtractors(); + if (dataStreamsTransactionExtractorsJson == null) { + dataStreamsTransactionExtractors = DataStreamsTransactionExtractors.EMPTY; + } else { + dataStreamsTransactionExtractors = + DataStreamsTransactionExtractors.deserialize(dataStreamsTransactionExtractorsJson); + } + // Get initial Span Sampling Rules from config String spanSamplingRulesJson = config.getSpanSamplingRules(); String spanSamplingRulesFile = config.getSpanSamplingRulesFile(); @@ -708,6 +719,9 @@ private CoreTracer( .setSpanSamplingRules(spanSamplingRules.getRules()) .setTraceSamplingRules(traceSamplingRules.getRules(), traceSamplingRulesJson) .setTracingTags(config.getMergedSpanTags()) + .setDataStreamsTransactionExtractors( + dataStreamsTransactionExtractors.getExtractors(), + dataStreamsTransactionExtractorsJson) .apply(); this.logs128bTraceIdEnabled = Config.get().isLogs128bitTraceIdEnabled(); diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DataStreamsTransactionExtractors.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DataStreamsTransactionExtractors.java new file mode 100644 index 00000000000..df202a6df77 --- /dev/null +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DataStreamsTransactionExtractors.java @@ -0,0 +1,78 @@ +package datadog.trace.core.datastreams; + +import com.squareup.moshi.FromJson; +import com.squareup.moshi.JsonAdapter; +import com.squareup.moshi.Moshi; +import com.squareup.moshi.ToJson; +import com.squareup.moshi.Types; +import datadog.trace.api.datastreams.DataStreamsTransactionExtractor; +import java.lang.reflect.ParameterizedType; +import java.util.Collections; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DataStreamsTransactionExtractors { + public static final DataStreamsTransactionExtractors EMPTY = + new DataStreamsTransactionExtractors(Collections.emptyList()); + private static final Logger log = LoggerFactory.getLogger(DataStreamsTransactionExtractors.class); + private static final Moshi MOSHI = + new Moshi.Builder() + .add(new DataStreamsTransactionExtractors.DataStreamsTransactionExtractorAdapter()) + .build(); + private static final ParameterizedType LIST_OF_RULES = + Types.newParameterizedType(List.class, DataStreamsTransactionExtractor.class); + private static final JsonAdapter> LIST_OF_RULES_ADAPTER = + MOSHI.adapter(LIST_OF_RULES); + + private final List extractors; + + public DataStreamsTransactionExtractors(List extractors) { + this.extractors = Collections.unmodifiableList(extractors); + } + + public static DataStreamsTransactionExtractors deserialize(String json) { + try { + return new DataStreamsTransactionExtractors(LIST_OF_RULES_ADAPTER.fromJson(json)); + } catch (Throwable ex) { + log.error("Couldn't parse Data Streams Extractors from JSON: {}", json, ex); + } + + return EMPTY; + } + + public List getExtractors() { + return extractors; + } + + private static final class JsonDataStreamsTransactionExtractor { + private static final JsonAdapter jsonAdapter = + MOSHI.adapter(JsonDataStreamsTransactionExtractor.class); + String name; + String type; + String value; + + @Override + public String toString() { + return jsonAdapter.toJson(this); + } + } + + private static final class DataStreamsTransactionExtractorAdapter { + private static DataStreamsTransactionExtractor create( + JsonDataStreamsTransactionExtractor jsonExtractor) { + return new DataStreamsTransactionExtractor( + jsonExtractor.name, jsonExtractor.type, jsonExtractor.value); + } + + @FromJson + DataStreamsTransactionExtractor fromJson(JsonDataStreamsTransactionExtractor jsonExtractor) { + return create(jsonExtractor); + } + + @ToJson + JsonDataStreamsTransactionExtractor toJson(DataStreamsTransactionExtractor extractor) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/dd-trace-core/src/test/groovy/datadog/trace/core/datastreams/DataStreamsTransactionExtractorsTest.groovy b/dd-trace-core/src/test/groovy/datadog/trace/core/datastreams/DataStreamsTransactionExtractorsTest.groovy new file mode 100644 index 00000000000..db547814c00 --- /dev/null +++ b/dd-trace-core/src/test/groovy/datadog/trace/core/datastreams/DataStreamsTransactionExtractorsTest.groovy @@ -0,0 +1,22 @@ +package datadog.trace.core.datastreams + +import datadog.trace.core.test.DDCoreSpecification + +class DataStreamsTransactionExtractorsTest extends DDCoreSpecification { + def "Deserialize from json"() { + when: + def list = DataStreamsTransactionExtractors.deserialize("""[ + {"name": "extractor", "type": "http_request_header", "value": "transaction_id"}, + {"name": "second_extractor", "type": "http_response_header", "value": "transaction_id"} + ]""") + def extractors = list.getExtractors() + then: + extractors.size() == 2 + extractors[0].getName() == "extractor" + extractors[0].getType() == "http_request_header" + extractors[0].getValue() == "transaction_id" + extractors[1].getName() == "second_extractor" + extractors[1].getType() == "http_response_header" + extractors[1].getValue() == "transaction_id" + } +} diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index 47dd855b00b..183199167f3 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -351,6 +351,7 @@ import static datadog.trace.api.config.GeneralConfig.DATA_JOBS_PARSE_SPARK_PLAN_ENABLED; import static datadog.trace.api.config.GeneralConfig.DATA_STREAMS_BUCKET_DURATION_SECONDS; import static datadog.trace.api.config.GeneralConfig.DATA_STREAMS_ENABLED; +import static datadog.trace.api.config.GeneralConfig.DATA_STREAMS_TRANSACTION_EXTRACTORS; import static datadog.trace.api.config.GeneralConfig.DOGSTATSD_ARGS; import static datadog.trace.api.config.GeneralConfig.DOGSTATSD_HOST; import static datadog.trace.api.config.GeneralConfig.DOGSTATSD_NAMED_PIPE; @@ -1200,6 +1201,7 @@ public static String getHostName() { private final boolean dataStreamsEnabled; private final float dataStreamsBucketDurationSeconds; + private final String dataStreamsTransactionExtractors; private final boolean serviceDiscoveryEnabled; @@ -2645,6 +2647,8 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) dataStreamsBucketDurationSeconds = configProvider.getFloat( DATA_STREAMS_BUCKET_DURATION_SECONDS, DEFAULT_DATA_STREAMS_BUCKET_DURATION); + dataStreamsTransactionExtractors = + configProvider.getString(DATA_STREAMS_TRANSACTION_EXTRACTORS); azureAppServices = configProvider.getBoolean(AZURE_APP_SERVICES, false); traceAgentPath = configProvider.getString(TRACE_AGENT_PATH); @@ -4447,6 +4451,10 @@ public float getDataStreamsBucketDurationSeconds() { return dataStreamsBucketDurationSeconds; } + public String getDataStreamsTransactionExtractors() { + return dataStreamsTransactionExtractors; + } + public long getDataStreamsBucketDurationNanoseconds() { // Rounds to the nearest millisecond before converting to nanos int milliseconds = Math.round(dataStreamsBucketDurationSeconds * 1000); diff --git a/internal-api/src/main/java/datadog/trace/api/DynamicConfig.java b/internal-api/src/main/java/datadog/trace/api/DynamicConfig.java index fb14e8370aa..33b5c546e02 100644 --- a/internal-api/src/main/java/datadog/trace/api/DynamicConfig.java +++ b/internal-api/src/main/java/datadog/trace/api/DynamicConfig.java @@ -14,6 +14,7 @@ import static datadog.trace.util.ConfigStrings.normalizedHeaderTag; import static datadog.trace.util.ConfigStrings.trim; +import datadog.trace.api.datastreams.DataStreamsTransactionExtractor; import datadog.trace.api.sampling.SamplingRule.SpanSamplingRule; import datadog.trace.api.sampling.SamplingRule.TraceSamplingRule; import java.util.Collection; @@ -111,6 +112,8 @@ public final class Builder { Double traceSampleRate; String preferredServiceName; + List dataStreamsTransactionExtractors; + String dataStreamsTransactionExtractorsJson; Builder() {} @@ -134,6 +137,8 @@ public final class Builder { this.tracingTags = snapshot.tracingTags; this.preferredServiceName = snapshot.preferredServiceName; + this.dataStreamsTransactionExtractors = snapshot.dataStreamsTransactionExtractors; + this.dataStreamsTransactionExtractorsJson = snapshot.dataStreamsTransactionExtractorsJson; } public Builder setRuntimeMetricsEnabled(boolean runtimeMetricsEnabled) { @@ -151,6 +156,14 @@ public Builder setDataStreamsEnabled(boolean dataStreamsEnabled) { return this; } + public Builder setDataStreamsTransactionExtractors( + List dataStreamsTransactionExtractors, + String dataStreamsTransactionExtractorsJson) { + this.dataStreamsTransactionExtractors = dataStreamsTransactionExtractors; + this.dataStreamsTransactionExtractorsJson = dataStreamsTransactionExtractorsJson; + return this; + } + public Builder setServiceMapping(Map serviceMapping) { return setServiceMapping(serviceMapping.entrySet()); } @@ -324,6 +337,8 @@ public static class Snapshot implements TraceConfig { final Map tracingTags; final String preferredServiceName; + final List dataStreamsTransactionExtractors; + final String dataStreamsTransactionExtractorsJson; protected Snapshot(DynamicConfig.Builder builder, Snapshot oldSnapshot) { @@ -345,6 +360,8 @@ protected Snapshot(DynamicConfig.Builder builder, Snapshot oldSnapshot) { this.tracingTags = nullToEmpty(builder.tracingTags); this.preferredServiceName = builder.preferredServiceName; + this.dataStreamsTransactionExtractors = builder.dataStreamsTransactionExtractors; + this.dataStreamsTransactionExtractorsJson = builder.dataStreamsTransactionExtractorsJson; } private static Map nullToEmpty(Map mapping) { @@ -415,6 +432,11 @@ public List getTraceSamplingRules() { return traceSamplingRules; } + @Override + public List getDataStreamsTransactionExtractors() { + return dataStreamsTransactionExtractors; + } + @Override public Map getTracingTags() { return tracingTags; diff --git a/internal-api/src/main/java/datadog/trace/api/TraceConfig.java b/internal-api/src/main/java/datadog/trace/api/TraceConfig.java index f07d3f56195..3a022a08e7b 100644 --- a/internal-api/src/main/java/datadog/trace/api/TraceConfig.java +++ b/internal-api/src/main/java/datadog/trace/api/TraceConfig.java @@ -1,5 +1,6 @@ package datadog.trace.api; +import datadog.trace.api.datastreams.DataStreamsTransactionExtractor; import datadog.trace.api.sampling.SamplingRule.SpanSamplingRule; import datadog.trace.api.sampling.SamplingRule.TraceSamplingRule; import java.util.List; @@ -46,4 +47,11 @@ public interface TraceConfig { * @return The tracer sampler Trace Sampling Rules, or an empty collection if no rule is defined. */ List getTraceSamplingRules(); + + /** + * Get DSM transaction extractors. + * + * @return List of Data Streams Transactions extractors. + */ + List getDataStreamsTransactionExtractors(); } diff --git a/internal-api/src/main/java/datadog/trace/api/datastreams/DataStreamsTransactionExtractor.java b/internal-api/src/main/java/datadog/trace/api/datastreams/DataStreamsTransactionExtractor.java new file mode 100644 index 00000000000..e62bd3d2db1 --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/datastreams/DataStreamsTransactionExtractor.java @@ -0,0 +1,25 @@ +package datadog.trace.api.datastreams; + +public final class DataStreamsTransactionExtractor { + private final String name; + private final String type; + private final String value; + + public DataStreamsTransactionExtractor(final String name, final String type, final String value) { + this.name = name; + this.type = type; + this.value = value; + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public String getValue() { + return value; + } +} diff --git a/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/AgentTracer.java b/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/AgentTracer.java index c78e3fffdd1..8486f3a9723 100644 --- a/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/AgentTracer.java +++ b/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/AgentTracer.java @@ -6,6 +6,7 @@ import datadog.trace.api.EndpointTracker; import datadog.trace.api.TraceConfig; import datadog.trace.api.datastreams.AgentDataStreamsMonitoring; +import datadog.trace.api.datastreams.DataStreamsTransactionExtractor; import datadog.trace.api.datastreams.NoopDataStreamsMonitoring; import datadog.trace.api.experimental.DataStreamsCheckpointer; import datadog.trace.api.gateway.CallbackProvider; @@ -797,5 +798,10 @@ public List getSpanSamplingRules() { public List getTraceSamplingRules() { return Collections.emptyList(); } + + @Override + public List getDataStreamsTransactionExtractors() { + return null; + } } } From 2295d151e2245b0d385294d2f1ced79acdda12e2 Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Wed, 5 Nov 2025 13:03:23 -0600 Subject: [PATCH 02/23] Added ability to configure extractors --- .../java/datadog/trace/core/CoreTracer.java | 4 +-- .../trace/core/TracingConfigPoller.java | 14 +++++++++- .../DataStreamsTransactionExtractors.java | 28 ++++++++++++++++++- .../DefaultDataStreamsMonitoring.java | 19 ++++++++++++- .../java/datadog/trace/api/DynamicConfig.java | 8 +----- .../DataStreamsTransactionExtractor.java | 24 +++------------- .../datadog/remoteconfig/Capabilities.java | 1 + 7 files changed, 65 insertions(+), 33 deletions(-) diff --git a/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java b/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java index 803b3a90136..33a624fe3b6 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java @@ -719,9 +719,7 @@ private CoreTracer( .setSpanSamplingRules(spanSamplingRules.getRules()) .setTraceSamplingRules(traceSamplingRules.getRules(), traceSamplingRulesJson) .setTracingTags(config.getMergedSpanTags()) - .setDataStreamsTransactionExtractors( - dataStreamsTransactionExtractors.getExtractors(), - dataStreamsTransactionExtractorsJson) + .setDataStreamsTransactionExtractors(dataStreamsTransactionExtractors.getExtractors()) .apply(); this.logs128bTraceIdEnabled = Config.get().isLogs128bitTraceIdEnabled(); diff --git a/dd-trace-core/src/main/java/datadog/trace/core/TracingConfigPoller.java b/dd-trace-core/src/main/java/datadog/trace/core/TracingConfigPoller.java index ac88784ac79..f8816553472 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/TracingConfigPoller.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/TracingConfigPoller.java @@ -12,6 +12,7 @@ import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_TRACING_SAMPLE_RATE; import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_TRACING_SAMPLE_RULES; import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_TRACING_TRACING_ENABLED; +import static datadog.remoteconfig.Capabilities.CAPABILITY_DATA_STREAMS_TRANSACTION_EXTRACTORS; import static datadog.trace.api.sampling.SamplingRule.normalizeGlob; import com.squareup.moshi.FromJson; @@ -31,6 +32,7 @@ import datadog.trace.api.debugger.DebuggerConfigBridge; import datadog.trace.api.debugger.DebuggerConfigUpdate; import datadog.trace.api.sampling.SamplingRule; +import datadog.trace.core.datastreams.DataStreamsTransactionExtractors; import datadog.trace.logging.GlobalLogLevelSwitcher; import datadog.trace.logging.LogLevel; import java.io.ByteArrayInputStream; @@ -78,7 +80,8 @@ public void start(Config config, SharedCommunicationObjects sco) { | CAPABILITY_APM_TRACING_ENABLE_EXCEPTION_REPLAY | CAPABILITY_APM_TRACING_ENABLE_CODE_ORIGIN | CAPABILITY_APM_TRACING_ENABLE_LIVE_DEBUGGING - | CAPABILITY_APM_TRACING_MULTICONFIG); + | CAPABILITY_APM_TRACING_MULTICONFIG + | CAPABILITY_DATA_STREAMS_TRANSACTION_EXTRACTORS); } stopPolling = new Updater().register(config, configPoller); } @@ -238,6 +241,10 @@ void applyConfigOverrides(LibConfig libConfig) { maybeOverride(builder::setServiceMapping, libConfig.serviceMapping); maybeOverride(builder::setHeaderTags, libConfig.headerTags); + if (null != libConfig.dataStreamsTransactionExtractors) { + builder.setDataStreamsTransactionExtractors( + libConfig.dataStreamsTransactionExtractors.getExtractors()); + } if (null != libConfig.tracingSamplingRules) { builder.setTraceSamplingRules( @@ -406,6 +413,8 @@ static final class LibConfig { @Json(name = "live_debugging_enabled") public Boolean liveDebuggingEnabled; + @Json(name = "data_streams_transaction_extractors") + public DataStreamsTransactionExtractors dataStreamsTransactionExtractors; /** * Merges a list of LibConfig objects by taking the first non-null value for each field. * @@ -454,6 +463,9 @@ public static LibConfig mergeLibConfigs(List configs) { if (merged.tracingSamplingRules == null) { merged.tracingSamplingRules = config.tracingSamplingRules; } + if (merged.dataStreamsTransactionExtractors == null) { + merged.dataStreamsTransactionExtractors = config.dataStreamsTransactionExtractors; + } if (merged.dynamicInstrumentationEnabled == null) { merged.dynamicInstrumentationEnabled = config.dynamicInstrumentationEnabled; } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DataStreamsTransactionExtractors.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DataStreamsTransactionExtractors.java index df202a6df77..44ebfc761e4 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DataStreamsTransactionExtractors.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DataStreamsTransactionExtractors.java @@ -61,7 +61,7 @@ public String toString() { private static final class DataStreamsTransactionExtractorAdapter { private static DataStreamsTransactionExtractor create( JsonDataStreamsTransactionExtractor jsonExtractor) { - return new DataStreamsTransactionExtractor( + return new DataStreamsTransactionExtractorImpl( jsonExtractor.name, jsonExtractor.type, jsonExtractor.value); } @@ -75,4 +75,30 @@ JsonDataStreamsTransactionExtractor toJson(DataStreamsTransactionExtractor extra throw new UnsupportedOperationException(); } } + + private static final class DataStreamsTransactionExtractorImpl + implements DataStreamsTransactionExtractor { + private final String name; + private final String type; + private final String value; + + public DataStreamsTransactionExtractorImpl( + final String name, final String type, final String value) { + this.name = name; + this.type = type; + this.value = value; + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public String getValue() { + return value; + } + } } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java index c86b9402081..26571744a9d 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java @@ -16,7 +16,14 @@ import datadog.context.propagation.Propagator; import datadog.trace.api.Config; import datadog.trace.api.TraceConfig; -import datadog.trace.api.datastreams.*; +import datadog.trace.api.datastreams.Backlog; +import datadog.trace.api.datastreams.DataStreamsContext; +import datadog.trace.api.datastreams.DataStreamsTags; +import datadog.trace.api.datastreams.DataStreamsTransactionExtractor; +import datadog.trace.api.datastreams.InboxItem; +import datadog.trace.api.datastreams.NoopPathwayContext; +import datadog.trace.api.datastreams.PathwayContext; +import datadog.trace.api.datastreams.StatsPoint; import datadog.trace.api.experimental.DataStreamsContextCarrier; import datadog.trace.api.time.TimeSource; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; @@ -433,6 +440,16 @@ public void onEvent(EventType eventType, String message) { private void checkDynamicConfig() { configSupportsDataStreams = traceConfigSupplier.get().isDataStreamsEnabled(); supportsDataStreams = agentSupportsDataStreams && configSupportsDataStreams; + + List extractors = + traceConfigSupplier.get().getDataStreamsTransactionExtractors(); + for (DataStreamsTransactionExtractor extractor : extractors) { + log.info( + "#### Got extractor with name {}, type {}, value {}", + extractor.getName(), + extractor.getType(), + extractor.getValue()); + } } private void checkFeatures() { diff --git a/internal-api/src/main/java/datadog/trace/api/DynamicConfig.java b/internal-api/src/main/java/datadog/trace/api/DynamicConfig.java index 33b5c546e02..4a33193b2ae 100644 --- a/internal-api/src/main/java/datadog/trace/api/DynamicConfig.java +++ b/internal-api/src/main/java/datadog/trace/api/DynamicConfig.java @@ -113,7 +113,6 @@ public final class Builder { String preferredServiceName; List dataStreamsTransactionExtractors; - String dataStreamsTransactionExtractorsJson; Builder() {} @@ -138,7 +137,6 @@ public final class Builder { this.preferredServiceName = snapshot.preferredServiceName; this.dataStreamsTransactionExtractors = snapshot.dataStreamsTransactionExtractors; - this.dataStreamsTransactionExtractorsJson = snapshot.dataStreamsTransactionExtractorsJson; } public Builder setRuntimeMetricsEnabled(boolean runtimeMetricsEnabled) { @@ -157,10 +155,8 @@ public Builder setDataStreamsEnabled(boolean dataStreamsEnabled) { } public Builder setDataStreamsTransactionExtractors( - List dataStreamsTransactionExtractors, - String dataStreamsTransactionExtractorsJson) { + List dataStreamsTransactionExtractors) { this.dataStreamsTransactionExtractors = dataStreamsTransactionExtractors; - this.dataStreamsTransactionExtractorsJson = dataStreamsTransactionExtractorsJson; return this; } @@ -338,7 +334,6 @@ public static class Snapshot implements TraceConfig { final String preferredServiceName; final List dataStreamsTransactionExtractors; - final String dataStreamsTransactionExtractorsJson; protected Snapshot(DynamicConfig.Builder builder, Snapshot oldSnapshot) { @@ -361,7 +356,6 @@ protected Snapshot(DynamicConfig.Builder builder, Snapshot oldSnapshot) { this.preferredServiceName = builder.preferredServiceName; this.dataStreamsTransactionExtractors = builder.dataStreamsTransactionExtractors; - this.dataStreamsTransactionExtractorsJson = builder.dataStreamsTransactionExtractorsJson; } private static Map nullToEmpty(Map mapping) { diff --git a/internal-api/src/main/java/datadog/trace/api/datastreams/DataStreamsTransactionExtractor.java b/internal-api/src/main/java/datadog/trace/api/datastreams/DataStreamsTransactionExtractor.java index e62bd3d2db1..9cffb955c07 100644 --- a/internal-api/src/main/java/datadog/trace/api/datastreams/DataStreamsTransactionExtractor.java +++ b/internal-api/src/main/java/datadog/trace/api/datastreams/DataStreamsTransactionExtractor.java @@ -1,25 +1,9 @@ package datadog.trace.api.datastreams; -public final class DataStreamsTransactionExtractor { - private final String name; - private final String type; - private final String value; +public interface DataStreamsTransactionExtractor { + String getName(); - public DataStreamsTransactionExtractor(final String name, final String type, final String value) { - this.name = name; - this.type = type; - this.value = value; - } + String getType(); - public String getName() { - return name; - } - - public String getType() { - return type; - } - - public String getValue() { - return value; - } + String getValue(); } diff --git a/remote-config/remote-config-api/src/main/java/datadog/remoteconfig/Capabilities.java b/remote-config/remote-config-api/src/main/java/datadog/remoteconfig/Capabilities.java index 7ddd4e0c693..ee542d36639 100644 --- a/remote-config/remote-config-api/src/main/java/datadog/remoteconfig/Capabilities.java +++ b/remote-config/remote-config-api/src/main/java/datadog/remoteconfig/Capabilities.java @@ -46,4 +46,5 @@ public interface Capabilities { long CAPABILITY_ASM_TRACE_TAGGING_RULES = 1L << 43; long CAPABILITY_ASM_EXTENDED_DATA_COLLECTION = 1L << 44; long CAPABILITY_APM_TRACING_MULTICONFIG = 1L << 45; + long CAPABILITY_DATA_STREAMS_TRANSACTION_EXTRACTORS = 1L << 47; } From b14038998882c0f62cdec83af38edd4b896d2fc0 Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Wed, 5 Nov 2025 13:26:12 -0600 Subject: [PATCH 03/23] Log extractors --- .../src/main/java/datadog/trace/core/StatusLogger.java | 2 ++ .../trace/core/datastreams/DefaultDataStreamsMonitoring.java | 1 + 2 files changed, 3 insertions(+) diff --git a/dd-trace-core/src/main/java/datadog/trace/core/StatusLogger.java b/dd-trace-core/src/main/java/datadog/trace/core/StatusLogger.java index d7507503b88..a8a3e9e4de8 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/StatusLogger.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/StatusLogger.java @@ -156,6 +156,8 @@ public void toJson(JsonWriter writer, Config config) throws IOException { } writer.name("data_streams_enabled"); writer.value(config.isDataStreamsEnabled()); + writer.name("data_streams_transaction_extractors"); + writer.value(config.getDataStreamsTransactionExtractors()); writer.endObject(); } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java index 26571744a9d..097f65f6d03 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java @@ -443,6 +443,7 @@ private void checkDynamicConfig() { List extractors = traceConfigSupplier.get().getDataStreamsTransactionExtractors(); + // reconfigure extractors here for (DataStreamsTransactionExtractor extractor : extractors) { log.info( "#### Got extractor with name {}, type {}, value {}", From d57ec63b71b82055d45f6b5bd9f70f5edf8583cd Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Wed, 5 Nov 2025 14:25:49 -0600 Subject: [PATCH 04/23] Fixed json parsing --- .../trace/core/TracingConfigPoller.java | 7 +++- .../DataStreamsTransactionExtractors.java | 40 +++++++++++++++---- .../trace/core/TracingConfigPollerTest.groovy | 9 ++++- 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/dd-trace-core/src/main/java/datadog/trace/core/TracingConfigPoller.java b/dd-trace-core/src/main/java/datadog/trace/core/TracingConfigPoller.java index f8816553472..580acb606b4 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/TracingConfigPoller.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/TracingConfigPoller.java @@ -98,7 +98,12 @@ final class Updater implements ProductListener { private final JsonAdapter TRACE_SAMPLING_RULE; { - Moshi MOSHI = new Moshi.Builder().add(new TracingSamplingRulesAdapter()).build(); + Moshi MOSHI = + new Moshi.Builder() + .add(new TracingSamplingRulesAdapter()) + .add(new DataStreamsTransactionExtractors.DataStreamsTransactionExtractorsAdapter()) + .add(new DataStreamsTransactionExtractors.DataStreamsTransactionExtractorAdapter()) + .build(); CONFIG_OVERRIDES_ADAPTER = MOSHI.adapter(ConfigOverrides.class); LIB_CONFIG_ADAPTER = MOSHI.adapter(LibConfig.class); TRACE_SAMPLING_RULE = MOSHI.adapter(TracingSamplingRule.class); diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DataStreamsTransactionExtractors.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DataStreamsTransactionExtractors.java index 44ebfc761e4..1f9321b43bc 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DataStreamsTransactionExtractors.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DataStreamsTransactionExtractors.java @@ -2,38 +2,44 @@ import com.squareup.moshi.FromJson; import com.squareup.moshi.JsonAdapter; +import com.squareup.moshi.JsonReader; import com.squareup.moshi.Moshi; import com.squareup.moshi.ToJson; import com.squareup.moshi.Types; import datadog.trace.api.datastreams.DataStreamsTransactionExtractor; +import java.io.IOException; import java.lang.reflect.ParameterizedType; import java.util.Collections; import java.util.List; +import okio.BufferedSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DataStreamsTransactionExtractors { public static final DataStreamsTransactionExtractors EMPTY = - new DataStreamsTransactionExtractors(Collections.emptyList()); + new DataStreamsTransactionExtractors("[]", Collections.emptyList()); private static final Logger log = LoggerFactory.getLogger(DataStreamsTransactionExtractors.class); private static final Moshi MOSHI = new Moshi.Builder() .add(new DataStreamsTransactionExtractors.DataStreamsTransactionExtractorAdapter()) .build(); private static final ParameterizedType LIST_OF_RULES = - Types.newParameterizedType(List.class, DataStreamsTransactionExtractor.class); - private static final JsonAdapter> LIST_OF_RULES_ADAPTER = + Types.newParameterizedType(List.class, DataStreamsTransactionExtractorImpl.class); + public static final JsonAdapter> LIST_OF_RULES_ADAPTER = MOSHI.adapter(LIST_OF_RULES); private final List extractors; + private final String json; - public DataStreamsTransactionExtractors(List extractors) { + public DataStreamsTransactionExtractors( + String json, List extractors) { this.extractors = Collections.unmodifiableList(extractors); + this.json = json; } public static DataStreamsTransactionExtractors deserialize(String json) { try { - return new DataStreamsTransactionExtractors(LIST_OF_RULES_ADAPTER.fromJson(json)); + return new DataStreamsTransactionExtractors(json, LIST_OF_RULES_ADAPTER.fromJson(json)); } catch (Throwable ex) { log.error("Couldn't parse Data Streams Extractors from JSON: {}", json, ex); } @@ -58,7 +64,27 @@ public String toString() { } } - private static final class DataStreamsTransactionExtractorAdapter { + public static final class DataStreamsTransactionExtractorsAdapter { + @FromJson + DataStreamsTransactionExtractors fromJson( + JsonReader reader, JsonAdapter> parser) + throws IOException { + if (reader.peek() == JsonReader.Token.NULL) { + return reader.nextNull(); + } + try (BufferedSource source = reader.nextSource()) { + String json = source.readUtf8(); + return new DataStreamsTransactionExtractors(json, parser.fromJson(json)); + } + } + + @ToJson + String toJson(DataStreamsTransactionExtractors extractors) { + return extractors.json; + } + } + + public static final class DataStreamsTransactionExtractorAdapter { private static DataStreamsTransactionExtractor create( JsonDataStreamsTransactionExtractor jsonExtractor) { return new DataStreamsTransactionExtractorImpl( @@ -76,7 +102,7 @@ JsonDataStreamsTransactionExtractor toJson(DataStreamsTransactionExtractor extra } } - private static final class DataStreamsTransactionExtractorImpl + public static final class DataStreamsTransactionExtractorImpl implements DataStreamsTransactionExtractor { private final String name; private final String type; diff --git a/dd-trace-core/src/test/groovy/datadog/trace/core/TracingConfigPollerTest.groovy b/dd-trace-core/src/test/groovy/datadog/trace/core/TracingConfigPollerTest.groovy index ff5b0ea4e25..df687c50010 100644 --- a/dd-trace-core/src/test/groovy/datadog/trace/core/TracingConfigPollerTest.groovy +++ b/dd-trace-core/src/test/groovy/datadog/trace/core/TracingConfigPollerTest.groovy @@ -169,7 +169,14 @@ class TracingConfigPollerTest extends DDCoreSpecification { "tag_name": "custom.header" } ], - "tracing_sampling_rate": 1.3 + "tracing_sampling_rate": 1.3, + "data_streams_transaction_extractors": [ + { + "name": "test", + "type": "type", + "value": "value" + } + ] } } """.getBytes(StandardCharsets.UTF_8), null) From 515f991c56bae7412553166a780b5c561d36f639 Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Thu, 6 Nov 2025 13:14:41 -0600 Subject: [PATCH 05/23] Added url param to DSM endpoint --- .../DefaultDataStreamsMonitoring.java | 2 +- .../extractors/TransactionExtractor.java | 75 +++++++++++++++++++ .../extractors/TransactionInfo.java | 19 +++++ .../trace/core/TracingConfigPollerTest.groovy | 7 +- 4 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 dd-trace-core/src/main/java/datadog/trace/core/datastreams/extractors/TransactionExtractor.java create mode 100644 dd-trace-core/src/main/java/datadog/trace/core/datastreams/extractors/TransactionInfo.java diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java index 097f65f6d03..352e17832a5 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java @@ -84,7 +84,7 @@ public DefaultDataStreamsMonitoring( new OkHttpSink( sharedCommunicationObjects.agentHttpClient, sharedCommunicationObjects.agentUrl.toString(), - V01_DATASTREAMS_ENDPOINT, + V01_DATASTREAMS_ENDPOINT + "?test=true", false, true, Collections.emptyMap()), diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/extractors/TransactionExtractor.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/extractors/TransactionExtractor.java new file mode 100644 index 00000000000..56a2a487018 --- /dev/null +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/extractors/TransactionExtractor.java @@ -0,0 +1,75 @@ +package datadog.trace.core.datastreams.extractors; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import datadog.communication.serialization.WritableFormatter; +import datadog.trace.api.time.TimeSource; +import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; +import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiConsumer; + +public class TransactionExtractor { + protected TransactionHolder holder; + + public TransactionExtractor(TimeSource timeSource, String name, String value) { + this.holder = new TransactionHolder(timeSource, UTF8BytesString.create(name), value); + } + + public void fromContext(C carrier, AgentPropagation.ContextVisitor contextVisitor) { + contextVisitor.forEachKeyValue(carrier, this.holder); + } + + public void flushTo(WritableFormatter writer) { + this.holder.flushTo(writer); + } + + protected static class TransactionHolder implements BiConsumer { + private final UTF8BytesString name; + private final String key; + private final TimeSource timeSource; + private final ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>(); + private final AtomicInteger size = new AtomicInteger(); + + public TransactionHolder(TimeSource timeSource, UTF8BytesString name, String key) { + this.key = key; + this.timeSource = timeSource; + this.name = name; + } + + @Override + public void accept(String key, String value) { + if (key.equals(this.key)) { + queue.add(new TransactionInfo(value, timeSource.getCurrentTimeNanos())); + size.incrementAndGet(); + } + } + + public void flushTo(WritableFormatter writer) { + // we don't care for the exact size, approximate is fine + int maxSize = size.getAndSet(0); + + // get list of items to flush + List toFlush = new ArrayList<>(maxSize); + for (int i = 0; i < maxSize; i++) { + TransactionInfo info = queue.poll(); + if (info == null) { + break; + } + toFlush.add(info); + } + + // flush + writer.startArray(toFlush.size()); + for (TransactionInfo info : toFlush) { + writer.startArray(2); + writer.writeLong(info.getTimestamp()); + writer.writeUTF8(name); + writer.writeUTF8(info.getId().getBytes(UTF_8)); + } + } + } +} diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/extractors/TransactionInfo.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/extractors/TransactionInfo.java new file mode 100644 index 00000000000..bdc1e281365 --- /dev/null +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/extractors/TransactionInfo.java @@ -0,0 +1,19 @@ +package datadog.trace.core.datastreams.extractors; + +public final class TransactionInfo { + private final String id; + private final Long timestamp; + + public TransactionInfo(String id, Long timestamp) { + this.id = id; + this.timestamp = timestamp; + } + + public String getId() { + return id; + } + + public Long getTimestamp() { + return timestamp; + } +} diff --git a/dd-trace-core/src/test/groovy/datadog/trace/core/TracingConfigPollerTest.groovy b/dd-trace-core/src/test/groovy/datadog/trace/core/TracingConfigPollerTest.groovy index df687c50010..6038861d689 100644 --- a/dd-trace-core/src/test/groovy/datadog/trace/core/TracingConfigPollerTest.groovy +++ b/dd-trace-core/src/test/groovy/datadog/trace/core/TracingConfigPollerTest.groovy @@ -8,12 +8,11 @@ import datadog.remoteconfig.Product import datadog.remoteconfig.state.ParsedConfigKey import datadog.remoteconfig.state.ProductListener import datadog.trace.core.test.DDCoreSpecification +import java.nio.charset.StandardCharsets import okhttp3.HttpUrl import okhttp3.OkHttpClient import spock.lang.Timeout -import java.nio.charset.StandardCharsets - @Timeout(10) class TracingConfigPollerTest extends DDCoreSpecification { @@ -190,6 +189,10 @@ class TracingConfigPollerTest extends DDCoreSpecification { tracer.captureTraceConfig().traceSampleRate == 1.0 // should be clamped to 1.0 tracer.captureTraceConfig().requestHeaderTags == ["x-custom-header": "custom.header"] tracer.captureTraceConfig().responseHeaderTags == ["x-custom-header": "custom.header"] + tracer.captureTraceConfig().getDataStreamsTransactionExtractors().size() == 1 + tracer.captureTraceConfig().getDataStreamsTransactionExtractors()[0].name == "test" + tracer.captureTraceConfig().getDataStreamsTransactionExtractors()[0].type == "type" + tracer.captureTraceConfig().getDataStreamsTransactionExtractors()[0].value == "value" when: // Remove service level config From eb4ebb8a83f3a9b6f43cc77ab6956727bf362aa2 Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Mon, 10 Nov 2025 13:58:58 -0600 Subject: [PATCH 06/23] Refactoring --- .../decorator/HttpClientDecorator.java | 4 + .../decorator/HttpServerDecorator.java | 1 + .../DataStreamsTransactionExtractors.java | 17 +++-- .../DefaultDataStreamsMonitoring.java | 47 +++++++++--- .../MsgPackDatastreamsPayloadWriter.java | 10 ++- .../trace/core/datastreams/StatsBucket.java | 10 +++ .../datastreams/TransactionContainer.java | 43 +++++++++++ .../extractors/TransactionExtractor.java | 75 ------------------- .../extractors/TransactionInfo.java | 19 ----- .../AgentDataStreamsMonitoring.java | 3 +- .../DataStreamsTransactionExtractor.java | 19 ++++- .../DataStreamsTransactionTracker.java | 11 +++ .../NoopDataStreamsMonitoring.java | 10 +++ .../api/datastreams/TransactionInfo.java | 54 +++++++++++++ 14 files changed, 209 insertions(+), 114 deletions(-) create mode 100644 dd-trace-core/src/main/java/datadog/trace/core/datastreams/TransactionContainer.java delete mode 100644 dd-trace-core/src/main/java/datadog/trace/core/datastreams/extractors/TransactionExtractor.java delete mode 100644 dd-trace-core/src/main/java/datadog/trace/core/datastreams/extractors/TransactionInfo.java create mode 100644 internal-api/src/main/java/datadog/trace/api/datastreams/DataStreamsTransactionTracker.java create mode 100644 internal-api/src/main/java/datadog/trace/api/datastreams/TransactionInfo.java diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java index 77e96360435..84bf7d3dc38 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java @@ -70,6 +70,10 @@ protected boolean shouldSetResourceName() { public AgentSpan onRequest(final AgentSpan span, final REQUEST request) { if (request != null) { + // AgentTracer.get().getDataStreamsMonitoring().trackTransaction(); + + // TODO: krigor + // getRequestHeader(request, "") String method = method(request); span.setTag(Tags.HTTP_METHOD, method); diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java index 76163e9bb6f..e143432ccde 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java @@ -326,6 +326,7 @@ public AgentSpan onRequest( span.setRequestBlockingAction((RequestBlockingAction) flow.getAction()); } + // TODO: krigor return span; } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DataStreamsTransactionExtractors.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DataStreamsTransactionExtractors.java index 1f9321b43bc..4476337ed55 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DataStreamsTransactionExtractors.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DataStreamsTransactionExtractors.java @@ -87,8 +87,15 @@ String toJson(DataStreamsTransactionExtractors extractors) { public static final class DataStreamsTransactionExtractorAdapter { private static DataStreamsTransactionExtractor create( JsonDataStreamsTransactionExtractor jsonExtractor) { - return new DataStreamsTransactionExtractorImpl( - jsonExtractor.name, jsonExtractor.type, jsonExtractor.value); + + DataStreamsTransactionExtractor.Type type; + try { + type = DataStreamsTransactionExtractor.Type.valueOf(jsonExtractor.type); + } catch (Throwable ex) { + type = DataStreamsTransactionExtractor.Type.UNKNOWN; + } + + return new DataStreamsTransactionExtractorImpl(jsonExtractor.name, type, jsonExtractor.value); } @FromJson @@ -105,11 +112,11 @@ JsonDataStreamsTransactionExtractor toJson(DataStreamsTransactionExtractor extra public static final class DataStreamsTransactionExtractorImpl implements DataStreamsTransactionExtractor { private final String name; - private final String type; + private final DataStreamsTransactionExtractor.Type type; private final String value; public DataStreamsTransactionExtractorImpl( - final String name, final String type, final String value) { + final String name, final DataStreamsTransactionExtractor.Type type, final String value) { this.name = name; this.type = type; this.value = value; @@ -119,7 +126,7 @@ public String getName() { return name; } - public String getType() { + public DataStreamsTransactionExtractor.Type getType() { return type; } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java index 352e17832a5..aedd9323a70 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java @@ -24,6 +24,7 @@ import datadog.trace.api.datastreams.NoopPathwayContext; import datadog.trace.api.datastreams.PathwayContext; import datadog.trace.api.datastreams.StatsPoint; +import datadog.trace.api.datastreams.TransactionInfo; import datadog.trace.api.experimental.DataStreamsContextCarrier; import datadog.trace.api.time.TimeSource; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; @@ -75,6 +76,11 @@ public class DefaultDataStreamsMonitoring implements DataStreamsMonitoring, Even private final ConcurrentHashMap schemaSamplers; private static final ThreadLocal serviceNameOverride = new ThreadLocal<>(); + // contains a list of active extractors by type + private static final Map< + DataStreamsTransactionExtractor.Type, List> + extractorsByType = new HashMap<>(); + public DefaultDataStreamsMonitoring( Config config, SharedCommunicationObjects sharedCommunicationObjects, @@ -84,7 +90,7 @@ public DefaultDataStreamsMonitoring( new OkHttpSink( sharedCommunicationObjects.agentHttpClient, sharedCommunicationObjects.agentUrl.toString(), - V01_DATASTREAMS_ENDPOINT + "?test=true", + V01_DATASTREAMS_ENDPOINT, false, true, Collections.emptyMap()), @@ -189,6 +195,18 @@ public void clearThreadServiceName() { serviceNameOverride.remove(); } + @Override + public void trackTransaction(String transactionId, String checkpointName) { + inbox.offer( + new TransactionInfo(transactionId, timeSource.getCurrentTimeNanos(), checkpointName)); + } + + @Override + public List extractorsByType( + DataStreamsTransactionExtractor.Type extractorType) { + return Collections.emptyList(); + } + private static String getThreadServiceName() { return serviceNameOverride.get(); } @@ -220,6 +238,7 @@ public void mergePathwayContextIntoSpan(AgentSpan span, DataStreamsContextCarrie } } + @Override public void trackBacklog(DataStreamsTags tags, long value) { inbox.offer(new Backlog(tags, value, timeSource.getCurrentTimeNanos(), getThreadServiceName())); } @@ -365,6 +384,10 @@ public void run() { StatsBucket statsBucket = getStatsBucket(backlog.getTimestampNanos(), backlog.getServiceNameOverride()); statsBucket.addBacklog(backlog); + } else if (payload instanceof TransactionInfo) { + TransactionInfo transactionInfo = (TransactionInfo) payload; + StatsBucket statsBucket = getStatsBucket(transactionInfo.getTimestamp(), ""); + statsBucket.addTransaction(transactionInfo); } } } catch (Exception e) { @@ -437,19 +460,23 @@ public void onEvent(EventType eventType, String message) { } } + private void updateExtractorsFromConfig() { + List extractors = + traceConfigSupplier.get().getDataStreamsTransactionExtractors(); + for (DataStreamsTransactionExtractor extractor : extractors) { + + List list = + extractorsByType.computeIfAbsent(extractor.getType(), k -> new LinkedList<>()); + list.add(extractor); + } + } + private void checkDynamicConfig() { configSupportsDataStreams = traceConfigSupplier.get().isDataStreamsEnabled(); supportsDataStreams = agentSupportsDataStreams && configSupportsDataStreams; - List extractors = - traceConfigSupplier.get().getDataStreamsTransactionExtractors(); - // reconfigure extractors here - for (DataStreamsTransactionExtractor extractor : extractors) { - log.info( - "#### Got extractor with name {}, type {}, value {}", - extractor.getName(), - extractor.getType(), - extractor.getValue()); + if (supportsDataStreams) { + updateExtractorsFromConfig(); } } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java index 0df8e7291d7..16b2aa502c5 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java @@ -37,6 +37,7 @@ public class MsgPackDatastreamsPayloadWriter implements DatastreamsPayloadWriter private static final byte[] BACKLOG_TAGS = "Tags".getBytes(ISO_8859_1); private static final byte[] PRODUCTS_MASK = "ProductMask".getBytes(ISO_8859_1); private static final byte[] PROCESS_TAGS = "ProcessTags".getBytes(ISO_8859_1); + private static final byte[] TRANSACTIONS = "Transactions".getBytes(ISO_8859_1); private static final int INITIAL_CAPACITY = 512 * 1024; @@ -121,7 +122,8 @@ public void writePayload(Collection data, String serviceNameOverrid for (StatsBucket bucket : data) { boolean hasBacklogs = !bucket.getBacklogs().isEmpty(); - writer.startMap(3 + (hasBacklogs ? 1 : 0)); + boolean hasTransactions = !bucket.getTransactions().isEmpty(); + writer.startMap(3 + (hasBacklogs ? 1 : 0) + (hasTransactions ? 1 : 0)); /* 1 */ writer.writeUTF8(START); @@ -139,6 +141,12 @@ public void writePayload(Collection data, String serviceNameOverrid /* 4 */ writeBacklogs(bucket.getBacklogs(), writer); } + + if (hasTransactions) { + /* 5 */ + writer.writeUTF8(TRANSACTIONS); + writer.writeBinary(bucket.getTransactions().getData()); + } } /* 8 */ diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/StatsBucket.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/StatsBucket.java index c61550d0e3e..e9031219934 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/StatsBucket.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/StatsBucket.java @@ -3,6 +3,7 @@ import datadog.trace.api.datastreams.Backlog; import datadog.trace.api.datastreams.DataStreamsTags; import datadog.trace.api.datastreams.StatsPoint; +import datadog.trace.api.datastreams.TransactionInfo; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -12,6 +13,7 @@ public class StatsBucket { private final long bucketDurationNanos; private final Map hashToGroup = new HashMap<>(); private final Map backlogs = new HashMap<>(); + private final TransactionContainer transactions = new TransactionContainer(1024); public StatsBucket(long startTimeNanos, long bucketDurationNanos) { this.startTimeNanos = startTimeNanos; @@ -40,6 +42,10 @@ public void addBacklog(Backlog backlog) { (k, v) -> (v == null) ? backlog.getValue() : Math.max(v, backlog.getValue())); } + public void addTransaction(TransactionInfo transaction) { + transactions.add(transaction); + } + public long getStartTimeNanos() { return startTimeNanos; } @@ -55,4 +61,8 @@ public Collection getGroups() { public Collection> getBacklogs() { return backlogs.entrySet(); } + + public TransactionContainer getTransactions() { + return transactions; + } } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/TransactionContainer.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/TransactionContainer.java new file mode 100644 index 00000000000..5ba0ff88f36 --- /dev/null +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/TransactionContainer.java @@ -0,0 +1,43 @@ +package datadog.trace.core.datastreams; + +import datadog.trace.api.datastreams.TransactionInfo; +import java.util.Arrays; + +public class TransactionContainer { + // we store data as an array of bytes, since the number of object can be significant + private byte[] data; + private int size; + + public TransactionContainer(Integer initialSizeBytes) { + this.data = new byte[initialSizeBytes]; + } + + public void add(TransactionInfo transactionInfo) { + // check if we need to resize + byte[] transactionBytes = transactionInfo.getBytes(); + + // resize buffer if needed + if (data.length - size < transactionBytes.length) { + byte[] resized = new byte[data.length * 2]; + System.arraycopy(data, 0, resized, 0, size); + data = resized; + } + + // add data + System.arraycopy(transactionBytes, 0, data, size, transactionBytes.length); + size += transactionBytes.length; + } + + public boolean isEmpty() { + return size == 0; + } + + public boolean clear() { + size = 0; + return true; + } + + public byte[] getData() { + return Arrays.copyOf(data, size); + } +} diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/extractors/TransactionExtractor.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/extractors/TransactionExtractor.java deleted file mode 100644 index 56a2a487018..00000000000 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/extractors/TransactionExtractor.java +++ /dev/null @@ -1,75 +0,0 @@ -package datadog.trace.core.datastreams.extractors; - -import static java.nio.charset.StandardCharsets.UTF_8; - -import datadog.communication.serialization.WritableFormatter; -import datadog.trace.api.time.TimeSource; -import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; -import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.BiConsumer; - -public class TransactionExtractor { - protected TransactionHolder holder; - - public TransactionExtractor(TimeSource timeSource, String name, String value) { - this.holder = new TransactionHolder(timeSource, UTF8BytesString.create(name), value); - } - - public void fromContext(C carrier, AgentPropagation.ContextVisitor contextVisitor) { - contextVisitor.forEachKeyValue(carrier, this.holder); - } - - public void flushTo(WritableFormatter writer) { - this.holder.flushTo(writer); - } - - protected static class TransactionHolder implements BiConsumer { - private final UTF8BytesString name; - private final String key; - private final TimeSource timeSource; - private final ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>(); - private final AtomicInteger size = new AtomicInteger(); - - public TransactionHolder(TimeSource timeSource, UTF8BytesString name, String key) { - this.key = key; - this.timeSource = timeSource; - this.name = name; - } - - @Override - public void accept(String key, String value) { - if (key.equals(this.key)) { - queue.add(new TransactionInfo(value, timeSource.getCurrentTimeNanos())); - size.incrementAndGet(); - } - } - - public void flushTo(WritableFormatter writer) { - // we don't care for the exact size, approximate is fine - int maxSize = size.getAndSet(0); - - // get list of items to flush - List toFlush = new ArrayList<>(maxSize); - for (int i = 0; i < maxSize; i++) { - TransactionInfo info = queue.poll(); - if (info == null) { - break; - } - toFlush.add(info); - } - - // flush - writer.startArray(toFlush.size()); - for (TransactionInfo info : toFlush) { - writer.startArray(2); - writer.writeLong(info.getTimestamp()); - writer.writeUTF8(name); - writer.writeUTF8(info.getId().getBytes(UTF_8)); - } - } - } -} diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/extractors/TransactionInfo.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/extractors/TransactionInfo.java deleted file mode 100644 index bdc1e281365..00000000000 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/extractors/TransactionInfo.java +++ /dev/null @@ -1,19 +0,0 @@ -package datadog.trace.core.datastreams.extractors; - -public final class TransactionInfo { - private final String id; - private final Long timestamp; - - public TransactionInfo(String id, Long timestamp) { - this.id = id; - this.timestamp = timestamp; - } - - public String getId() { - return id; - } - - public Long getTimestamp() { - return timestamp; - } -} diff --git a/internal-api/src/main/java/datadog/trace/api/datastreams/AgentDataStreamsMonitoring.java b/internal-api/src/main/java/datadog/trace/api/datastreams/AgentDataStreamsMonitoring.java index b7c51bd36ec..3165a0f30ba 100644 --- a/internal-api/src/main/java/datadog/trace/api/datastreams/AgentDataStreamsMonitoring.java +++ b/internal-api/src/main/java/datadog/trace/api/datastreams/AgentDataStreamsMonitoring.java @@ -5,7 +5,8 @@ import datadog.trace.bootstrap.instrumentation.api.Schema; import datadog.trace.bootstrap.instrumentation.api.SchemaIterator; -public interface AgentDataStreamsMonitoring extends DataStreamsCheckpointer { +public interface AgentDataStreamsMonitoring + extends DataStreamsCheckpointer, DataStreamsTransactionTracker { void trackBacklog(DataStreamsTags tags, long value); /** diff --git a/internal-api/src/main/java/datadog/trace/api/datastreams/DataStreamsTransactionExtractor.java b/internal-api/src/main/java/datadog/trace/api/datastreams/DataStreamsTransactionExtractor.java index 9cffb955c07..bf0b8fde6b3 100644 --- a/internal-api/src/main/java/datadog/trace/api/datastreams/DataStreamsTransactionExtractor.java +++ b/internal-api/src/main/java/datadog/trace/api/datastreams/DataStreamsTransactionExtractor.java @@ -1,9 +1,22 @@ package datadog.trace.api.datastreams; public interface DataStreamsTransactionExtractor { - String getName(); - - String getType(); + enum Type { + UNKNOWN, + /** HTTP_OUT_HEADERS targets outgoing HTTP requests */ + HTTP_OUT_HEADERS, + /** HTTP_IN_HEADERS targets incoming HTTP requests */ + HTTP_IN_HEADERS, + /** KAFKA_CONSUME_HEADERS targets headers from consumed messages (after consume) */ + KAFKA_CONSUME_HEADERS, + /** KAFKA_CONSUME_HEADERS targets headers from produced messages (before produce) */ + KAFKA_PRODUCE_HEADERS + } + /** getName returns transaction extractor name */ + String getName(); + /** getType returns transaction extractor type */ + Type getType(); + /** getType returns transaction extractor value */ String getValue(); } diff --git a/internal-api/src/main/java/datadog/trace/api/datastreams/DataStreamsTransactionTracker.java b/internal-api/src/main/java/datadog/trace/api/datastreams/DataStreamsTransactionTracker.java new file mode 100644 index 00000000000..5a0cd3a0ad4 --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/datastreams/DataStreamsTransactionTracker.java @@ -0,0 +1,11 @@ +package datadog.trace.api.datastreams; + +import java.util.List; + +public interface DataStreamsTransactionTracker { + /** trackTransaction used to emit "seen" even for transactions */ + void trackTransaction(String transactionId, String checkpointName); + /** extractorsByType returns the list of extractors */ + List extractorsByType( + DataStreamsTransactionExtractor.Type extractorType); +} diff --git a/internal-api/src/main/java/datadog/trace/api/datastreams/NoopDataStreamsMonitoring.java b/internal-api/src/main/java/datadog/trace/api/datastreams/NoopDataStreamsMonitoring.java index f5cdcb0c82f..2d72492065b 100644 --- a/internal-api/src/main/java/datadog/trace/api/datastreams/NoopDataStreamsMonitoring.java +++ b/internal-api/src/main/java/datadog/trace/api/datastreams/NoopDataStreamsMonitoring.java @@ -4,6 +4,7 @@ import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.Schema; import datadog.trace.bootstrap.instrumentation.api.SchemaIterator; +import java.util.List; public class NoopDataStreamsMonitoring implements AgentDataStreamsMonitoring { public static final NoopDataStreamsMonitoring INSTANCE = new NoopDataStreamsMonitoring(); @@ -46,6 +47,15 @@ public void setThreadServiceName(String serviceName) {} @Override public void clearThreadServiceName() {} + @Override + public void trackTransaction(String transactionId, String checkpointName) {} + + @Override + public List extractorsByType( + DataStreamsTransactionExtractor.Type extractorType) { + return null; + } + @Override public void setConsumeCheckpoint(String type, String source, DataStreamsContextCarrier carrier) {} diff --git a/internal-api/src/main/java/datadog/trace/api/datastreams/TransactionInfo.java b/internal-api/src/main/java/datadog/trace/api/datastreams/TransactionInfo.java new file mode 100644 index 00000000000..852441df667 --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/datastreams/TransactionInfo.java @@ -0,0 +1,54 @@ +package datadog.trace.api.datastreams; + +import datadog.trace.api.cache.DDCache; +import datadog.trace.api.cache.DDCaches; +import java.util.concurrent.atomic.AtomicInteger; + +public final class TransactionInfo implements InboxItem { + private static final DDCache CACHE = DDCaches.newFixedSizeCache(64); + private static final AtomicInteger COUNTER = new AtomicInteger(); + + private final String id; + private final long timestamp; + private final int checkpointId; + + public TransactionInfo(String id, Long timestamp, String checkpoint) { + this.id = id; + this.timestamp = timestamp; + this.checkpointId = CACHE.computeIfAbsent(checkpoint, k -> COUNTER.incrementAndGet()); + } + + public String getId() { + return id; + } + + public Long getTimestamp() { + return timestamp; + } + + public Integer getCheckpointId() { + return checkpointId; + } + + public byte[] getBytes() { + byte[] idBytes = id.getBytes(); + byte[] result = new byte[idBytes.length + 10]; + // up to 1 byte for checkpoint id + result[0] = (byte) (checkpointId); + // 8 bytes for timestamp + result[1] = (byte) (timestamp >> 56); + result[2] = (byte) (timestamp >> 48); + result[3] = (byte) (timestamp >> 40); + result[4] = (byte) (timestamp >> 32); + ; + result[5] = (byte) (timestamp >> 24); + result[6] = (byte) (timestamp >> 16); + result[7] = (byte) (timestamp >> 8); + result[8] = (byte) (timestamp); + // id size, up to 256 bytes + result[9] = (byte) (idBytes.length); + // add id bytes + System.arraycopy(idBytes, 0, result, 9, idBytes.length); + return result; + } +} From d27db1921e5e4e9fc7154a96b26fa573992c4373 Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Mon, 10 Nov 2025 15:10:58 -0600 Subject: [PATCH 07/23] Transaction extractors implementation for http and kafka --- .../decorator/HttpClientDecorator.java | 21 +++++++++++++--- .../decorator/HttpServerDecorator.java | 25 ++++++++++++++++++- .../decorator/HttpServerDecoratorTest.groovy | 5 ++++ .../akkahttp/AkkaHttpServerDecorator.java | 6 +++++ .../jetty10/JettyDecorator.java | 5 ++++ .../jetty11/JettyDecorator.java | 5 ++++ .../jetty12/JettyDecorator.java | 5 ++++ .../jetty70/JettyDecorator.java | 5 ++++ .../jetty76/JettyDecorator.java | 5 ++++ .../jetty9/JettyDecorator.java | 5 ++++ .../KafkaProducerInstrumentation.java | 22 ++++++++++++++++ .../kafka_clients/TracingIterator.java | 20 +++++++++++++++ .../kafka_clients38/TracingIterator.java | 20 +++++++++++++++ .../restlet/RestletDecorator.java | 5 ++++ .../servlet2/Servlet2Decorator.java | 5 ++++ .../servlet3/Servlet3Decorator.java | 5 ++++ .../SpringWebHttpServerDecorator.java | 5 ++++ .../SpringWebHttpServerDecorator.java | 5 ++++ .../tomcat/TomcatDecorator.java | 5 ++++ 19 files changed, 174 insertions(+), 5 deletions(-) diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java index 84bf7d3dc38..c89444260f0 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java @@ -11,6 +11,8 @@ import datadog.trace.api.InstrumenterConfig; import datadog.trace.api.ProductActivation; import datadog.trace.api.appsec.HttpClientRequest; +import datadog.trace.api.datastreams.AgentDataStreamsMonitoring; +import datadog.trace.api.datastreams.DataStreamsTransactionExtractor; import datadog.trace.api.gateway.BlockResponseFunction; import datadog.trace.api.gateway.Flow; import datadog.trace.api.gateway.RequestContext; @@ -27,6 +29,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.BitSet; +import java.util.List; import java.util.Map; import java.util.function.BiFunction; import org.slf4j.Logger; @@ -70,10 +73,20 @@ protected boolean shouldSetResourceName() { public AgentSpan onRequest(final AgentSpan span, final REQUEST request) { if (request != null) { - // AgentTracer.get().getDataStreamsMonitoring().trackTransaction(); - - // TODO: krigor - // getRequestHeader(request, "") + // apply extractors if any (disabled if DSM is off) + AgentDataStreamsMonitoring dataStreamsMonitoring = + AgentTracer.get().getDataStreamsMonitoring(); + List extractorList = + dataStreamsMonitoring.extractorsByType( + DataStreamsTransactionExtractor.Type.HTTP_OUT_HEADERS); + if (!extractorList.isEmpty()) { + for (DataStreamsTransactionExtractor extractor : extractorList) { + String transactionId = getRequestHeader(request, extractor.getValue()); + if (transactionId != null && !transactionId.isEmpty()) { + dataStreamsMonitoring.trackTransaction(transactionId, extractor.getName()); + } + } + } String method = method(request); span.setTag(Tags.HTTP_METHOD, method); diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java index e143432ccde..2e5a3554f64 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java @@ -13,6 +13,8 @@ import datadog.context.propagation.Propagators; import datadog.trace.api.Config; import datadog.trace.api.DDTags; +import datadog.trace.api.datastreams.AgentDataStreamsMonitoring; +import datadog.trace.api.datastreams.DataStreamsTransactionExtractor; import datadog.trace.api.function.TriConsumer; import datadog.trace.api.function.TriFunction; import datadog.trace.api.gateway.BlockResponseFunction; @@ -39,6 +41,7 @@ import datadog.trace.bootstrap.instrumentation.decorator.http.ClientIpAddressResolver; import java.net.InetAddress; import java.util.BitSet; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.ExecutionException; @@ -95,6 +98,10 @@ public abstract class HttpServerDecorator extractorList = + dataStreamsMonitoring.extractorsByType( + DataStreamsTransactionExtractor.Type.HTTP_IN_HEADERS); + if (!extractorList.isEmpty()) { + for (DataStreamsTransactionExtractor extractor : extractorList) { + String transactionId = getRequestHeader(request, extractor.getValue()); + if (transactionId != null) { + dataStreamsMonitoring.trackTransaction(transactionId, extractor.getName()); + } + } + } + } + return span; } diff --git a/dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecoratorTest.groovy b/dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecoratorTest.groovy index 7c3efb1d48c..c56efc5f014 100644 --- a/dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecoratorTest.groovy +++ b/dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecoratorTest.groovy @@ -468,6 +468,11 @@ class HttpServerDecoratorTest extends ServerDecoratorTest { protected int status(Map m) { return m.status == null ? 0 : m.status } + + @Override + protected String getRequestHeader(Map map, String key) { + return map.getOrDefault(key, null) + } } } diff --git a/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/AkkaHttpServerDecorator.java b/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/AkkaHttpServerDecorator.java index efa73e608cd..c4ffd1d83b0 100644 --- a/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/AkkaHttpServerDecorator.java +++ b/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/AkkaHttpServerDecorator.java @@ -88,6 +88,12 @@ protected int status(final HttpResponse httpResponse) { return httpResponse.status().intValue(); } + @Override + protected String getRequestHeader(HttpRequest httpRequest, String key) { + Optional header = httpRequest.getHeader(key); + return header.map(HttpHeader::value).orElse(null); + } + @Override protected boolean isAppSecOnResponseSeparate() { return true; diff --git a/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-10.0/src/main/java11/datadog/trace/instrumentation/jetty10/JettyDecorator.java b/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-10.0/src/main/java11/datadog/trace/instrumentation/jetty10/JettyDecorator.java index ed42375dd12..8a009440cb5 100644 --- a/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-10.0/src/main/java11/datadog/trace/instrumentation/jetty10/JettyDecorator.java +++ b/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-10.0/src/main/java11/datadog/trace/instrumentation/jetty10/JettyDecorator.java @@ -78,6 +78,11 @@ protected boolean isAppSecOnResponseSeparate() { return true; } + @Override + protected String getRequestHeader(final Request request, String key) { + return request.getHeader(key); + } + @Override protected BlockResponseFunction createBlockResponseFunction(Request request, Request connection) { return new JettyBlockResponseFunction(request); diff --git a/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-11.0/src/main/java11/datadog/trace/instrumentation/jetty11/JettyDecorator.java b/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-11.0/src/main/java11/datadog/trace/instrumentation/jetty11/JettyDecorator.java index 9b74d1f418e..014d32bb26a 100644 --- a/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-11.0/src/main/java11/datadog/trace/instrumentation/jetty11/JettyDecorator.java +++ b/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-11.0/src/main/java11/datadog/trace/instrumentation/jetty11/JettyDecorator.java @@ -78,6 +78,11 @@ protected boolean isAppSecOnResponseSeparate() { return true; } + @Override + protected String getRequestHeader(final Request request, String key) { + return request.getHeader(key); + } + public AgentSpan onResponse(AgentSpan span, HttpChannel channel) { Request request = channel.getRequest(); Response response = channel.getResponse(); diff --git a/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-12.0/src/main/java17/datadog/trace/instrumentation/jetty12/JettyDecorator.java b/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-12.0/src/main/java17/datadog/trace/instrumentation/jetty12/JettyDecorator.java index 979cb5d62a5..0450fa2b44e 100644 --- a/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-12.0/src/main/java17/datadog/trace/instrumentation/jetty12/JettyDecorator.java +++ b/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-12.0/src/main/java17/datadog/trace/instrumentation/jetty12/JettyDecorator.java @@ -83,6 +83,11 @@ protected int status(final Response response) { return response.getStatus(); } + @Override + protected String getRequestHeader(final Request request, String key) { + return request.getHeaders().get(key); + } + public AgentSpan onResponse(AgentSpan span, HttpChannelState channel) { Request request = channel.getRequest(); Response response = channel.getResponse(); diff --git a/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-7.0/src/main/java/datadog/trace/instrumentation/jetty70/JettyDecorator.java b/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-7.0/src/main/java/datadog/trace/instrumentation/jetty70/JettyDecorator.java index 4822293650c..7aa3f5a4c13 100644 --- a/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-7.0/src/main/java/datadog/trace/instrumentation/jetty70/JettyDecorator.java +++ b/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-7.0/src/main/java/datadog/trace/instrumentation/jetty70/JettyDecorator.java @@ -77,6 +77,11 @@ protected boolean isAppSecOnResponseSeparate() { return true; } + @Override + protected String getRequestHeader(final Request request, String key) { + return request.getHeader(key); + } + public AgentSpan onResponse(AgentSpan span, HttpConnection channel) { Request request = channel.getRequest(); Response response = channel.getResponse(); diff --git a/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-7.6/src/main/java/datadog/trace/instrumentation/jetty76/JettyDecorator.java b/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-7.6/src/main/java/datadog/trace/instrumentation/jetty76/JettyDecorator.java index f158ee1c418..b86623b8061 100644 --- a/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-7.6/src/main/java/datadog/trace/instrumentation/jetty76/JettyDecorator.java +++ b/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-7.6/src/main/java/datadog/trace/instrumentation/jetty76/JettyDecorator.java @@ -77,6 +77,11 @@ protected boolean isAppSecOnResponseSeparate() { return true; } + @Override + protected String getRequestHeader(final Request request, String key) { + return request.getHeader(key); + } + public AgentSpan onResponse(AgentSpan span, AbstractHttpConnection connection) { Request request = connection.getRequest(); Response response = connection.getResponse(); diff --git a/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-9.0/src/main/java/datadog/trace/instrumentation/jetty9/JettyDecorator.java b/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-9.0/src/main/java/datadog/trace/instrumentation/jetty9/JettyDecorator.java index 87b50fea628..6234b00dd5d 100644 --- a/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-9.0/src/main/java/datadog/trace/instrumentation/jetty9/JettyDecorator.java +++ b/dd-java-agent/instrumentation/jetty/jetty-server/jetty-server-9.0/src/main/java/datadog/trace/instrumentation/jetty9/JettyDecorator.java @@ -77,6 +77,11 @@ protected boolean isAppSecOnResponseSeparate() { return true; } + @Override + protected String getRequestHeader(final Request request, String key) { + return request.getHeader(key); + } + public AgentSpan onResponse(AgentSpan span, HttpChannel channel) { Request request = channel.getRequest(); Response response = channel.getResponse(); diff --git a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java index ba66a01b6da..edca23c45eb 100644 --- a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java +++ b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java @@ -27,14 +27,18 @@ import datadog.trace.agent.tooling.Instrumenter; import datadog.trace.agent.tooling.InstrumenterModule; import datadog.trace.api.Config; +import datadog.trace.api.datastreams.AgentDataStreamsMonitoring; import datadog.trace.api.datastreams.DataStreamsContext; import datadog.trace.api.datastreams.DataStreamsTags; +import datadog.trace.api.datastreams.DataStreamsTransactionExtractor; import datadog.trace.api.datastreams.StatsPoint; import datadog.trace.bootstrap.InstrumentationContext; import datadog.trace.bootstrap.instrumentation.api.AgentScope; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentTracer; import datadog.trace.bootstrap.instrumentation.api.InstrumentationTags; +import java.nio.charset.StandardCharsets; +import java.util.List; import java.util.Map; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatcher; @@ -44,6 +48,7 @@ import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.kafka.clients.producer.internals.Sender; +import org.apache.kafka.common.header.Header; import org.apache.kafka.common.record.RecordBatch; @AutoService(InstrumenterModule.class) @@ -178,6 +183,23 @@ record = if (TIME_IN_QUEUE_ENABLED) { setter.injectTimeInQueue(record.headers()); } + + // track transactions on produce + AgentDataStreamsMonitoring dataStreamsMonitoring = + AgentTracer.get().getDataStreamsMonitoring(); + List extractors = + dataStreamsMonitoring.extractorsByType( + DataStreamsTransactionExtractor.Type.KAFKA_PRODUCE_HEADERS); + if (extractors != null) { + for (DataStreamsTransactionExtractor extractor : extractors) { + Header header = record.headers().lastHeader(extractor.getValue()); + if (header != null && header.value() != null) { + dataStreamsMonitoring.trackTransaction( + new String(header.value(), StandardCharsets.UTF_8), extractor.getName()); + } + } + } + return activateSpan(span); } diff --git a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/TracingIterator.java b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/TracingIterator.java index f634706ca5e..15882890141 100644 --- a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/TracingIterator.java +++ b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/TracingIterator.java @@ -21,14 +21,19 @@ import datadog.context.propagation.Propagator; import datadog.context.propagation.Propagators; import datadog.trace.api.Config; +import datadog.trace.api.datastreams.AgentDataStreamsMonitoring; import datadog.trace.api.datastreams.DataStreamsContext; import datadog.trace.api.datastreams.DataStreamsTags; +import datadog.trace.api.datastreams.DataStreamsTransactionExtractor; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentSpanContext; import datadog.trace.bootstrap.instrumentation.api.AgentTracer; import datadog.trace.bootstrap.instrumentation.api.InstrumentationTags; +import java.nio.charset.StandardCharsets; import java.util.Iterator; +import java.util.List; import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.common.header.Header; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -128,6 +133,21 @@ protected void startNewRecordSpan(ConsumerRecord val) { if (null != queueSpan) { queueSpan.finish(); } + + AgentDataStreamsMonitoring dataStreamsMonitoring = + AgentTracer.get().getDataStreamsMonitoring(); + List extractors = + dataStreamsMonitoring.extractorsByType( + DataStreamsTransactionExtractor.Type.KAFKA_CONSUME_HEADERS); + if (extractors != null) { + for (DataStreamsTransactionExtractor extractor : extractors) { + Header header = val.headers().lastHeader(extractor.getValue()); + if (header != null && header.value() != null) { + dataStreamsMonitoring.trackTransaction( + new String(header.value(), StandardCharsets.UTF_8), extractor.getName()); + } + } + } } } catch (final Exception e) { log.debug("Error starting new record span", e); diff --git a/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/main/java17/datadog/trace/instrumentation/kafka_clients38/TracingIterator.java b/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/main/java17/datadog/trace/instrumentation/kafka_clients38/TracingIterator.java index 1d5e93031e6..f48d06ccad3 100644 --- a/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/main/java17/datadog/trace/instrumentation/kafka_clients38/TracingIterator.java +++ b/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/main/java17/datadog/trace/instrumentation/kafka_clients38/TracingIterator.java @@ -16,16 +16,21 @@ import datadog.context.propagation.Propagator; import datadog.context.propagation.Propagators; import datadog.trace.api.Config; +import datadog.trace.api.datastreams.AgentDataStreamsMonitoring; import datadog.trace.api.datastreams.DataStreamsContext; import datadog.trace.api.datastreams.DataStreamsTags; +import datadog.trace.api.datastreams.DataStreamsTransactionExtractor; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentSpanContext; import datadog.trace.bootstrap.instrumentation.api.AgentTracer; import datadog.trace.bootstrap.instrumentation.api.InstrumentationTags; import datadog.trace.instrumentation.kafka_common.StreamingContext; import datadog.trace.instrumentation.kafka_common.Utils; +import java.nio.charset.StandardCharsets; import java.util.Iterator; +import java.util.List; import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.common.header.Header; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -128,6 +133,21 @@ protected void startNewRecordSpan(ConsumerRecord val) { if (null != queueSpan) { queueSpan.finish(); } + + AgentDataStreamsMonitoring dataStreamsMonitoring = + AgentTracer.get().getDataStreamsMonitoring(); + List extractors = + dataStreamsMonitoring.extractorsByType( + DataStreamsTransactionExtractor.Type.KAFKA_CONSUME_HEADERS); + if (extractors != null) { + for (DataStreamsTransactionExtractor extractor : extractors) { + Header header = val.headers().lastHeader(extractor.getValue()); + if (header != null && header.value() != null) { + dataStreamsMonitoring.trackTransaction( + new String(header.value(), StandardCharsets.UTF_8), extractor.getName()); + } + } + } } } catch (final Exception e) { log.debug("Error starting new record span", e); diff --git a/dd-java-agent/instrumentation/restlet-2.2/src/main/java/datadog/trace/instrumentation/restlet/RestletDecorator.java b/dd-java-agent/instrumentation/restlet-2.2/src/main/java/datadog/trace/instrumentation/restlet/RestletDecorator.java index 58c37ffe5c2..627def26a91 100644 --- a/dd-java-agent/instrumentation/restlet-2.2/src/main/java/datadog/trace/instrumentation/restlet/RestletDecorator.java +++ b/dd-java-agent/instrumentation/restlet-2.2/src/main/java/datadog/trace/instrumentation/restlet/RestletDecorator.java @@ -67,4 +67,9 @@ protected int peerPort(final HttpExchange exchange) { protected int status(final HttpExchange exchange) { return exchange.getResponseCode(); } + + @Override + protected String getRequestHeader(final HttpExchange exchange, String key) { + return exchange.getRequestHeaders().getFirst(key); + } } diff --git a/dd-java-agent/instrumentation/servlet/javax-servlet/javax-servlet-2.2/src/main/java/datadog/trace/instrumentation/servlet2/Servlet2Decorator.java b/dd-java-agent/instrumentation/servlet/javax-servlet/javax-servlet-2.2/src/main/java/datadog/trace/instrumentation/servlet2/Servlet2Decorator.java index aab83420d51..b93877f73ed 100644 --- a/dd-java-agent/instrumentation/servlet/javax-servlet/javax-servlet-2.2/src/main/java/datadog/trace/instrumentation/servlet2/Servlet2Decorator.java +++ b/dd-java-agent/instrumentation/servlet/javax-servlet/javax-servlet-2.2/src/main/java/datadog/trace/instrumentation/servlet2/Servlet2Decorator.java @@ -69,6 +69,11 @@ protected int peerPort(final HttpServletRequest httpServletRequest) { return 0; } + @Override + protected String getRequestHeader(final HttpServletRequest request, String key) { + return request.getHeader(key); + } + @Override protected int status(final Integer status) { return status; diff --git a/dd-java-agent/instrumentation/servlet/javax-servlet/javax-servlet-3.0/src/main/java/datadog/trace/instrumentation/servlet3/Servlet3Decorator.java b/dd-java-agent/instrumentation/servlet/javax-servlet/javax-servlet-3.0/src/main/java/datadog/trace/instrumentation/servlet3/Servlet3Decorator.java index c153e2c747e..61a8d22a2b8 100644 --- a/dd-java-agent/instrumentation/servlet/javax-servlet/javax-servlet-3.0/src/main/java/datadog/trace/instrumentation/servlet3/Servlet3Decorator.java +++ b/dd-java-agent/instrumentation/servlet/javax-servlet/javax-servlet-3.0/src/main/java/datadog/trace/instrumentation/servlet3/Servlet3Decorator.java @@ -77,6 +77,11 @@ protected String requestedSessionId(final HttpServletRequest request) { return request.getRequestedSessionId(); } + @Override + protected String getRequestHeader(final HttpServletRequest request, String key) { + return request.getHeader(key); + } + @Override public AgentSpan onRequest( final AgentSpan span, diff --git a/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/SpringWebHttpServerDecorator.java b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/SpringWebHttpServerDecorator.java index e4e759e2cc2..52ce8855048 100644 --- a/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/SpringWebHttpServerDecorator.java +++ b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/src/main/java/datadog/trace/instrumentation/springweb/SpringWebHttpServerDecorator.java @@ -92,6 +92,11 @@ protected int status(final HttpServletResponse httpServletResponse) { return httpServletResponse.getStatus(); } + @Override + protected String getRequestHeader(final HttpServletRequest request, String key) { + return request.getHeader(key); + } + @Override public AgentSpan onRequest( final AgentSpan span, diff --git a/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/SpringWebHttpServerDecorator.java b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/SpringWebHttpServerDecorator.java index 9811ab6c662..79b89c1aad5 100644 --- a/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/SpringWebHttpServerDecorator.java +++ b/dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-6.0/src/main/java17/datadog/trace/instrumentation/springweb6/SpringWebHttpServerDecorator.java @@ -86,6 +86,11 @@ protected String peerHostIP(final HttpServletRequest httpServletRequest) { return httpServletRequest.getRemoteAddr(); } + @Override + protected String getRequestHeader(final HttpServletRequest request, String key) { + return request.getHeader(key); + } + @Override protected int peerPort(final HttpServletRequest httpServletRequest) { return httpServletRequest.getRemotePort(); diff --git a/dd-java-agent/instrumentation/tomcat/tomcat-common/src/main/java/datadog/trace/instrumentation/tomcat/TomcatDecorator.java b/dd-java-agent/instrumentation/tomcat/tomcat-common/src/main/java/datadog/trace/instrumentation/tomcat/TomcatDecorator.java index 8beb6991b45..2ab1d6693f1 100644 --- a/dd-java-agent/instrumentation/tomcat/tomcat-common/src/main/java/datadog/trace/instrumentation/tomcat/TomcatDecorator.java +++ b/dd-java-agent/instrumentation/tomcat/tomcat-common/src/main/java/datadog/trace/instrumentation/tomcat/TomcatDecorator.java @@ -72,6 +72,11 @@ protected int peerPort(final Request request) { return request.getRemotePort(); } + @Override + protected String getRequestHeader(final Request request, String key) { + return request.getHeader(key); + } + @Override protected int status(final Response response) { int status = response.getStatus(); From 1123717efe76b10c7e20d661501fad19d4ef4d34 Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Mon, 10 Nov 2025 15:24:25 -0600 Subject: [PATCH 08/23] Added some logs --- .../instrumentation/decorator/HttpClientDecorator.java | 2 ++ .../instrumentation/decorator/HttpServerDecorator.java | 2 ++ .../kafka_clients/KafkaProducerInstrumentation.java | 1 + .../trace/instrumentation/kafka_clients/TracingIterator.java | 1 + .../instrumentation/kafka_clients38/TracingIterator.java | 1 + .../trace/core/datastreams/DefaultDataStreamsMonitoring.java | 4 +++- 6 files changed, 10 insertions(+), 1 deletion(-) diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java index c89444260f0..eb9afcbf23f 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java @@ -73,6 +73,7 @@ protected boolean shouldSetResourceName() { public AgentSpan onRequest(final AgentSpan span, final REQUEST request) { if (request != null) { + System.out.println("### applying http_out extractors"); // apply extractors if any (disabled if DSM is off) AgentDataStreamsMonitoring dataStreamsMonitoring = AgentTracer.get().getDataStreamsMonitoring(); @@ -82,6 +83,7 @@ public AgentSpan onRequest(final AgentSpan span, final REQUEST request) { if (!extractorList.isEmpty()) { for (DataStreamsTransactionExtractor extractor : extractorList) { String transactionId = getRequestHeader(request, extractor.getValue()); + System.out.println("### -> http_out extractor " + extractor.getName()); if (transactionId != null && !transactionId.isEmpty()) { dataStreamsMonitoring.trackTransaction(transactionId, extractor.getName()); } diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java index 2e5a3554f64..b85fb33ad45 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java @@ -334,6 +334,7 @@ public AgentSpan onRequest( } if (request != null) { + System.out.println("### applying http_in extractors"); // apply extractors if any (disabled if DSM is off) AgentDataStreamsMonitoring dataStreamsMonitoring = AgentTracer.get().getDataStreamsMonitoring(); @@ -342,6 +343,7 @@ public AgentSpan onRequest( DataStreamsTransactionExtractor.Type.HTTP_IN_HEADERS); if (!extractorList.isEmpty()) { for (DataStreamsTransactionExtractor extractor : extractorList) { + System.out.println("### -> http_in extractor " + extractor.getName()); String transactionId = getRequestHeader(request, extractor.getValue()); if (transactionId != null) { dataStreamsMonitoring.trackTransaction(transactionId, extractor.getName()); diff --git a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java index edca23c45eb..d1a69373599 100644 --- a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java +++ b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java @@ -191,6 +191,7 @@ record = dataStreamsMonitoring.extractorsByType( DataStreamsTransactionExtractor.Type.KAFKA_PRODUCE_HEADERS); if (extractors != null) { + System.out.println("### applying KAFKA_PRODUCE_HEADERS extractors"); for (DataStreamsTransactionExtractor extractor : extractors) { Header header = record.headers().lastHeader(extractor.getValue()); if (header != null && header.value() != null) { diff --git a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/TracingIterator.java b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/TracingIterator.java index 15882890141..69d9e552c12 100644 --- a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/TracingIterator.java +++ b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/TracingIterator.java @@ -140,6 +140,7 @@ protected void startNewRecordSpan(ConsumerRecord val) { dataStreamsMonitoring.extractorsByType( DataStreamsTransactionExtractor.Type.KAFKA_CONSUME_HEADERS); if (extractors != null) { + System.out.println("### applying KAFKA_CONSUME_HEADERS extractors"); for (DataStreamsTransactionExtractor extractor : extractors) { Header header = val.headers().lastHeader(extractor.getValue()); if (header != null && header.value() != null) { diff --git a/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/main/java17/datadog/trace/instrumentation/kafka_clients38/TracingIterator.java b/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/main/java17/datadog/trace/instrumentation/kafka_clients38/TracingIterator.java index f48d06ccad3..97638789824 100644 --- a/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/main/java17/datadog/trace/instrumentation/kafka_clients38/TracingIterator.java +++ b/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/main/java17/datadog/trace/instrumentation/kafka_clients38/TracingIterator.java @@ -140,6 +140,7 @@ protected void startNewRecordSpan(ConsumerRecord val) { dataStreamsMonitoring.extractorsByType( DataStreamsTransactionExtractor.Type.KAFKA_CONSUME_HEADERS); if (extractors != null) { + System.out.println("### applying KAFKA_PRODUCE_HEADERS extractors"); for (DataStreamsTransactionExtractor extractor : extractors) { Header header = val.headers().lastHeader(extractor.getValue()); if (header != null && header.value() != null) { diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java index aedd9323a70..e761b7958af 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java @@ -461,10 +461,12 @@ public void onEvent(EventType eventType, String message) { } private void updateExtractorsFromConfig() { + System.out.println("### updateExtractorsFromConfig"); List extractors = traceConfigSupplier.get().getDataStreamsTransactionExtractors(); for (DataStreamsTransactionExtractor extractor : extractors) { - + System.out.println( + "### Extractor: " + extractor.getName() + ", value: " + extractor.getValue()); List list = extractorsByType.computeIfAbsent(extractor.getType(), k -> new LinkedList<>()); list.add(extractor); From 135d9de08c7c049edc41bc94ad4a98b979dc30f5 Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Mon, 10 Nov 2025 16:19:08 -0600 Subject: [PATCH 09/23] Added value to supported configurations --- metadata/supported-configurations.json | 1 + 1 file changed, 1 insertion(+) diff --git a/metadata/supported-configurations.json b/metadata/supported-configurations.json index c52e1470ac9..165d5646268 100644 --- a/metadata/supported-configurations.json +++ b/metadata/supported-configurations.json @@ -141,6 +141,7 @@ "DD_DATA_JOBS_OPENLINEAGE_TIMEOUT_ENABLED": ["A"], "DD_DATA_STREAMS_BUCKET_DURATION_SECONDS": ["A"], "DD_DATA_STREAMS_ENABLED": ["A"], + "DD_DATA_STREAMS_TRANSACTION_EXTRACTORS": ["A"], "DD_DBM_INJECT_SQL_BASEHASH": ["A"], "DD_DBM_ALWAYS_APPEND_SQL_COMMENT": ["A"], "DD_DBM_PROPAGATION_MODE": ["A"], From 9d4b3c33c921a0421ddd1243f99dd7dda670938f Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Tue, 11 Nov 2025 10:07:21 -0600 Subject: [PATCH 10/23] Added some logs --- .../trace/core/datastreams/DefaultDataStreamsMonitoring.java | 1 + .../trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java | 1 + 2 files changed, 2 insertions(+) diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java index e761b7958af..802a943c517 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java @@ -197,6 +197,7 @@ public void clearThreadServiceName() { @Override public void trackTransaction(String transactionId, String checkpointName) { + System.out.println("### trackTransaction " + transactionId); inbox.offer( new TransactionInfo(transactionId, timeSource.getCurrentTimeNanos(), checkpointName)); } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java index 16b2aa502c5..f47a975d590 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java @@ -143,6 +143,7 @@ public void writePayload(Collection data, String serviceNameOverrid } if (hasTransactions) { + System.out.println("#### writing transactions ####"); /* 5 */ writer.writeUTF8(TRANSACTIONS); writer.writeBinary(bucket.getTransactions().getData()); From 6ebc3d326fd92978a01230122e73af094033e371 Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Tue, 11 Nov 2025 10:35:46 -0600 Subject: [PATCH 11/23] Added public API + toString implementation --- .../decorator/HttpClientDecorator.java | 3 ++- .../api/experimental/DataStreamsCheckpointer.java | 9 +++++++++ .../DataStreamsTransactionExtractors.java | 12 ++++++++++++ .../datastreams/DefaultDataStreamsMonitoring.java | 3 +-- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java index eb9afcbf23f..3db97dc22dc 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java @@ -73,13 +73,14 @@ protected boolean shouldSetResourceName() { public AgentSpan onRequest(final AgentSpan span, final REQUEST request) { if (request != null) { - System.out.println("### applying http_out extractors"); + System.out.println("### applying http_out extractors " + request.getClass().getSimpleName()); // apply extractors if any (disabled if DSM is off) AgentDataStreamsMonitoring dataStreamsMonitoring = AgentTracer.get().getDataStreamsMonitoring(); List extractorList = dataStreamsMonitoring.extractorsByType( DataStreamsTransactionExtractor.Type.HTTP_OUT_HEADERS); + System.out.println("### extractor list contains " + extractorList.size() + " extractors"); if (!extractorList.isEmpty()) { for (DataStreamsTransactionExtractor extractor : extractorList) { String transactionId = getRequestHeader(request, extractor.getValue()); diff --git a/dd-trace-api/src/main/java/datadog/trace/api/experimental/DataStreamsCheckpointer.java b/dd-trace-api/src/main/java/datadog/trace/api/experimental/DataStreamsCheckpointer.java index 574b9fdb2a0..6ee9365f336 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/experimental/DataStreamsCheckpointer.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/experimental/DataStreamsCheckpointer.java @@ -24,6 +24,12 @@ static DataStreamsCheckpointer get() { */ void setConsumeCheckpoint(String type, String source, DataStreamsContextCarrier carrier); + /** + * @param transactionId Transaction ID to track. + * @param checkpointName Unique checkpoint name. + */ + void trackTransaction(String transactionId, String checkpointName); + /** * @param type The type of the checkpoint, usually the streaming technology being used. Examples: * kafka, kinesis, sns etc. @@ -45,5 +51,8 @@ public void setConsumeCheckpoint( @Override public void setProduceCheckpoint( String type, String target, DataStreamsContextCarrier carrier) {} + + @Override + public void trackTransaction(String transactionId, String checkpointName) {} } } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DataStreamsTransactionExtractors.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DataStreamsTransactionExtractors.java index 4476337ed55..1e028b2a743 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DataStreamsTransactionExtractors.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DataStreamsTransactionExtractors.java @@ -133,5 +133,17 @@ public DataStreamsTransactionExtractor.Type getType() { public String getValue() { return value; } + + @Override + public String toString() { + return "DataStreamsTransactionExtractorImpl{" + + "name='" + + name + + "', type='" + + type.name() + + "', value='" + + value + + "'}"; + } } } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java index 802a943c517..006fe34d5e8 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java @@ -466,8 +466,7 @@ private void updateExtractorsFromConfig() { List extractors = traceConfigSupplier.get().getDataStreamsTransactionExtractors(); for (DataStreamsTransactionExtractor extractor : extractors) { - System.out.println( - "### Extractor: " + extractor.getName() + ", value: " + extractor.getValue()); + System.out.println(extractor.toString()); List list = extractorsByType.computeIfAbsent(extractor.getType(), k -> new LinkedList<>()); list.add(extractor); From d624be7df81e5e1ba9baf85637e5c377b07f4e77 Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Tue, 11 Nov 2025 11:26:43 -0600 Subject: [PATCH 12/23] Fixed get extractors --- .../instrumentation/decorator/HttpClientDecorator.java | 2 +- .../instrumentation/decorator/HttpServerDecorator.java | 2 +- .../kafka_clients/KafkaProducerInstrumentation.java | 2 +- .../trace/instrumentation/kafka_clients/TracingIterator.java | 2 +- .../instrumentation/kafka_clients38/TracingIterator.java | 2 +- .../trace/core/datastreams/DefaultDataStreamsMonitoring.java | 4 ++-- .../trace/api/datastreams/DataStreamsTransactionTracker.java | 2 +- .../trace/api/datastreams/NoopDataStreamsMonitoring.java | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java index 3db97dc22dc..1cda41fec5b 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java @@ -78,7 +78,7 @@ public AgentSpan onRequest(final AgentSpan span, final REQUEST request) { AgentDataStreamsMonitoring dataStreamsMonitoring = AgentTracer.get().getDataStreamsMonitoring(); List extractorList = - dataStreamsMonitoring.extractorsByType( + dataStreamsMonitoring.getTransactionExtractorsByType( DataStreamsTransactionExtractor.Type.HTTP_OUT_HEADERS); System.out.println("### extractor list contains " + extractorList.size() + " extractors"); if (!extractorList.isEmpty()) { diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java index b85fb33ad45..eaf02cf4fad 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java @@ -339,7 +339,7 @@ public AgentSpan onRequest( AgentDataStreamsMonitoring dataStreamsMonitoring = AgentTracer.get().getDataStreamsMonitoring(); List extractorList = - dataStreamsMonitoring.extractorsByType( + dataStreamsMonitoring.getTransactionExtractorsByType( DataStreamsTransactionExtractor.Type.HTTP_IN_HEADERS); if (!extractorList.isEmpty()) { for (DataStreamsTransactionExtractor extractor : extractorList) { diff --git a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java index d1a69373599..cbc722c04d9 100644 --- a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java +++ b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java @@ -188,7 +188,7 @@ record = AgentDataStreamsMonitoring dataStreamsMonitoring = AgentTracer.get().getDataStreamsMonitoring(); List extractors = - dataStreamsMonitoring.extractorsByType( + dataStreamsMonitoring.getTransactionExtractorsByType( DataStreamsTransactionExtractor.Type.KAFKA_PRODUCE_HEADERS); if (extractors != null) { System.out.println("### applying KAFKA_PRODUCE_HEADERS extractors"); diff --git a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/TracingIterator.java b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/TracingIterator.java index 69d9e552c12..9cd9d583d0b 100644 --- a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/TracingIterator.java +++ b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/TracingIterator.java @@ -137,7 +137,7 @@ protected void startNewRecordSpan(ConsumerRecord val) { AgentDataStreamsMonitoring dataStreamsMonitoring = AgentTracer.get().getDataStreamsMonitoring(); List extractors = - dataStreamsMonitoring.extractorsByType( + dataStreamsMonitoring.getTransactionExtractorsByType( DataStreamsTransactionExtractor.Type.KAFKA_CONSUME_HEADERS); if (extractors != null) { System.out.println("### applying KAFKA_CONSUME_HEADERS extractors"); diff --git a/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/main/java17/datadog/trace/instrumentation/kafka_clients38/TracingIterator.java b/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/main/java17/datadog/trace/instrumentation/kafka_clients38/TracingIterator.java index 97638789824..98c9d945b86 100644 --- a/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/main/java17/datadog/trace/instrumentation/kafka_clients38/TracingIterator.java +++ b/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/main/java17/datadog/trace/instrumentation/kafka_clients38/TracingIterator.java @@ -137,7 +137,7 @@ protected void startNewRecordSpan(ConsumerRecord val) { AgentDataStreamsMonitoring dataStreamsMonitoring = AgentTracer.get().getDataStreamsMonitoring(); List extractors = - dataStreamsMonitoring.extractorsByType( + dataStreamsMonitoring.getTransactionExtractorsByType( DataStreamsTransactionExtractor.Type.KAFKA_CONSUME_HEADERS); if (extractors != null) { System.out.println("### applying KAFKA_PRODUCE_HEADERS extractors"); diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java index 006fe34d5e8..38ccd14b67c 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java @@ -203,9 +203,9 @@ public void trackTransaction(String transactionId, String checkpointName) { } @Override - public List extractorsByType( + public List getTransactionExtractorsByType( DataStreamsTransactionExtractor.Type extractorType) { - return Collections.emptyList(); + return extractorsByType.getOrDefault(extractorType, Collections.emptyList()); } private static String getThreadServiceName() { diff --git a/internal-api/src/main/java/datadog/trace/api/datastreams/DataStreamsTransactionTracker.java b/internal-api/src/main/java/datadog/trace/api/datastreams/DataStreamsTransactionTracker.java index 5a0cd3a0ad4..7107d62bc84 100644 --- a/internal-api/src/main/java/datadog/trace/api/datastreams/DataStreamsTransactionTracker.java +++ b/internal-api/src/main/java/datadog/trace/api/datastreams/DataStreamsTransactionTracker.java @@ -6,6 +6,6 @@ public interface DataStreamsTransactionTracker { /** trackTransaction used to emit "seen" even for transactions */ void trackTransaction(String transactionId, String checkpointName); /** extractorsByType returns the list of extractors */ - List extractorsByType( + List getTransactionExtractorsByType( DataStreamsTransactionExtractor.Type extractorType); } diff --git a/internal-api/src/main/java/datadog/trace/api/datastreams/NoopDataStreamsMonitoring.java b/internal-api/src/main/java/datadog/trace/api/datastreams/NoopDataStreamsMonitoring.java index 2d72492065b..ad2519550cd 100644 --- a/internal-api/src/main/java/datadog/trace/api/datastreams/NoopDataStreamsMonitoring.java +++ b/internal-api/src/main/java/datadog/trace/api/datastreams/NoopDataStreamsMonitoring.java @@ -51,7 +51,7 @@ public void clearThreadServiceName() {} public void trackTransaction(String transactionId, String checkpointName) {} @Override - public List extractorsByType( + public List getTransactionExtractorsByType( DataStreamsTransactionExtractor.Type extractorType) { return null; } From 576c9a9436f10cd2886727b624b44ce4a320de68 Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Tue, 11 Nov 2025 12:35:19 -0600 Subject: [PATCH 13/23] Added serialization tests, added TRANSACTION_CHECKPOINT_IDS to DSM checkpoints --- .../MsgPackDatastreamsPayloadWriter.java | 7 ++- .../TransactionContainerTest.groovy | 36 +++++++++++ .../api/datastreams/TransactionInfo.java | 43 ++++++++++++- .../datastreams/TransactionInfoTest.groovy | 61 +++++++++++++++++++ 4 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 dd-trace-core/src/test/groovy/datadog/trace/core/datastreams/TransactionContainerTest.groovy create mode 100644 internal-api/src/test/groovy/datadog/trace/api/datastreams/TransactionInfoTest.groovy diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java index f47a975d590..90fd05d1dba 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java @@ -10,6 +10,7 @@ import datadog.trace.api.ProcessTags; import datadog.trace.api.WellKnownTags; import datadog.trace.api.datastreams.DataStreamsTags; +import datadog.trace.api.datastreams.TransactionInfo; import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; import datadog.trace.common.metrics.Sink; import java.util.Collection; @@ -38,6 +39,8 @@ public class MsgPackDatastreamsPayloadWriter implements DatastreamsPayloadWriter private static final byte[] PRODUCTS_MASK = "ProductMask".getBytes(ISO_8859_1); private static final byte[] PROCESS_TAGS = "ProcessTags".getBytes(ISO_8859_1); private static final byte[] TRANSACTIONS = "Transactions".getBytes(ISO_8859_1); + private static final byte[] TRANSACTION_CHECKPOINT_IDS = + "TransactionCheckpointIds".getBytes(ISO_8859_1); private static final int INITIAL_CAPACITY = 512 * 1024; @@ -123,7 +126,7 @@ public void writePayload(Collection data, String serviceNameOverrid for (StatsBucket bucket : data) { boolean hasBacklogs = !bucket.getBacklogs().isEmpty(); boolean hasTransactions = !bucket.getTransactions().isEmpty(); - writer.startMap(3 + (hasBacklogs ? 1 : 0) + (hasTransactions ? 1 : 0)); + writer.startMap(3 + (hasBacklogs ? 1 : 0) + (hasTransactions ? 2 : 0)); /* 1 */ writer.writeUTF8(START); @@ -147,6 +150,8 @@ public void writePayload(Collection data, String serviceNameOverrid /* 5 */ writer.writeUTF8(TRANSACTIONS); writer.writeBinary(bucket.getTransactions().getData()); + writer.writeUTF8(TRANSACTION_CHECKPOINT_IDS); + writer.writeBinary(TransactionInfo.getCheckpointIdCacheBytes()); } } diff --git a/dd-trace-core/src/test/groovy/datadog/trace/core/datastreams/TransactionContainerTest.groovy b/dd-trace-core/src/test/groovy/datadog/trace/core/datastreams/TransactionContainerTest.groovy new file mode 100644 index 00000000000..01181a53ad4 --- /dev/null +++ b/dd-trace-core/src/test/groovy/datadog/trace/core/datastreams/TransactionContainerTest.groovy @@ -0,0 +1,36 @@ +package datadog.trace.core.datastreams + +import datadog.trace.api.datastreams.TransactionInfo +import datadog.trace.core.test.DDCoreSpecification + +class TransactionContainerTest extends DDCoreSpecification { + def "test with no resize"() { + given: + TransactionInfo.resetCache() + def container = new TransactionContainer(1024) + container.add(new TransactionInfo("1", 1, "1")) + container.add(new TransactionInfo("2", 2, "2")) + def data = container.getData() + + expect: + data.size() == 22 + data == new byte[] { + 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 49, 2, 0, 0, 0, 0, 0, 0, 0, 2, 1, 50 + } + } + + def "test with with resize"() { + given: + TransactionInfo.resetCache() + def container = new TransactionContainer(10) + container.add(new TransactionInfo("1", 1, "1")) + container.add(new TransactionInfo("2", 2, "2")) + def data = container.getData() + + expect: + data.size() == 22 + data == new byte[] { + 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 49, 2, 0, 0, 0, 0, 0, 0, 0, 2, 1, 50 + } + } +} diff --git a/internal-api/src/main/java/datadog/trace/api/datastreams/TransactionInfo.java b/internal-api/src/main/java/datadog/trace/api/datastreams/TransactionInfo.java index 852441df667..87a6fa43aed 100644 --- a/internal-api/src/main/java/datadog/trace/api/datastreams/TransactionInfo.java +++ b/internal-api/src/main/java/datadog/trace/api/datastreams/TransactionInfo.java @@ -1,7 +1,11 @@ package datadog.trace.api.datastreams; +import datadog.trace.api.Pair; import datadog.trace.api.cache.DDCache; import datadog.trace.api.cache.DDCaches; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; import java.util.concurrent.atomic.AtomicInteger; public final class TransactionInfo implements InboxItem { @@ -48,7 +52,44 @@ public byte[] getBytes() { // id size, up to 256 bytes result[9] = (byte) (idBytes.length); // add id bytes - System.arraycopy(idBytes, 0, result, 9, idBytes.length); + System.arraycopy(idBytes, 0, result, 10, idBytes.length); return result; } + + static void resetCache() { + CACHE.clear(); + COUNTER.set(0); + } + + public static byte[] getCheckpointIdCacheBytes() { + // get all values + List> pairs = new LinkedList<>(); + CACHE.visit( + (key, value) -> { + pairs.add(Pair.of(key, value)); + }); + + // serialize + byte[] result = new byte[1024]; + int index = 0; + for (Pair pair : pairs) { + byte[] keyBytes = pair.getLeft().getBytes(); + // resize the buffer if needed + if (result.length - index <= keyBytes.length + 2) { + byte[] resized = new byte[result.length * 2]; + System.arraycopy(result, 0, resized, 0, result.length); + result = resized; + } + + result[index] = pair.getRight().byteValue(); + index++; + result[index] = (byte) (keyBytes.length); + index++; + + System.arraycopy(keyBytes, 0, result, index, keyBytes.length); + index += keyBytes.length; + } + + return Arrays.copyOf(result, index); + } } diff --git a/internal-api/src/test/groovy/datadog/trace/api/datastreams/TransactionInfoTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/datastreams/TransactionInfoTest.groovy new file mode 100644 index 00000000000..c82b6a839b2 --- /dev/null +++ b/internal-api/src/test/groovy/datadog/trace/api/datastreams/TransactionInfoTest.groovy @@ -0,0 +1,61 @@ +package datadog.trace.api.datastreams + +import datadog.trace.api.Pair +import spock.lang.Specification + +class TransactionInfoTest extends Specification { + + def "test checkpoint id cache serialization multiple"() { + given: + TransactionInfo.resetCache() + def controlSize = 10 + // generate multiple transaction ids to trigger cache updates + for (int i = 0; i < controlSize; i++) { + new TransactionInfo("id " + i, i, "checkpoint " + i) + } + + def items = new LinkedList>() + // get cache data + def data = TransactionInfo.getCheckpointIdCacheBytes() + def i = 0 + while (i < data.size()) { + def id = data[i] + i++ + + def size = data[i] + i++ + + def str = new String(data, i, size) + i += size + + items.add(Pair.of(str, id) as Pair) + } + + expect: + items.size() == controlSize + + for (def item in items) { + item.left == "Checkpoint " + item.right + } + } + + def "test checkpoint id cache serialization"() { + given: + TransactionInfo.resetCache() + new TransactionInfo("id", 1, "checkpoint") + def bytes = TransactionInfo.getCheckpointIdCacheBytes() + expect: + bytes.size() == 12 + bytes == new byte[] {1, 10, 99, 104, 101, 99, 107, 112, 111, 105, 110, 116} + } + + def "test transaction id serialization"() { + given: + TransactionInfo.resetCache() + def test = new TransactionInfo("id", 1, "checkpoint") + def bytes = test.getBytes() + expect: + bytes.size() == 12 + bytes == new byte[] {1, 0, 0, 0, 0, 0, 0, 0, 1, 2, 105, 100} + } +} From 184a07fa07b0a85000c866e46bec34fc410e78ee Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Tue, 11 Nov 2025 13:15:31 -0600 Subject: [PATCH 14/23] Added extractors lock --- .../DefaultDataStreamsMonitoring.java | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java index 38ccd14b67c..df9254dfcd0 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java @@ -44,6 +44,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Supplier; import org.jctools.queues.MpscArrayQueue; import org.slf4j.Logger; @@ -75,6 +76,7 @@ public class DefaultDataStreamsMonitoring implements DataStreamsMonitoring, Even private volatile boolean configSupportsDataStreams = false; private final ConcurrentHashMap schemaSamplers; private static final ThreadLocal serviceNameOverride = new ThreadLocal<>(); + private final ReentrantReadWriteLock extractorsLock = new ReentrantReadWriteLock(); // contains a list of active extractors by type private static final Map< @@ -205,7 +207,12 @@ public void trackTransaction(String transactionId, String checkpointName) { @Override public List getTransactionExtractorsByType( DataStreamsTransactionExtractor.Type extractorType) { - return extractorsByType.getOrDefault(extractorType, Collections.emptyList()); + extractorsLock.readLock().lock(); + try { + return extractorsByType.getOrDefault(extractorType, Collections.emptyList()); + } finally { + extractorsLock.readLock().unlock(); + } } private static String getThreadServiceName() { @@ -462,14 +469,19 @@ public void onEvent(EventType eventType, String message) { } private void updateExtractorsFromConfig() { - System.out.println("### updateExtractorsFromConfig"); - List extractors = - traceConfigSupplier.get().getDataStreamsTransactionExtractors(); - for (DataStreamsTransactionExtractor extractor : extractors) { - System.out.println(extractor.toString()); - List list = - extractorsByType.computeIfAbsent(extractor.getType(), k -> new LinkedList<>()); - list.add(extractor); + extractorsLock.writeLock().lock(); + try { + extractorsByType.clear(); + List extractors = + traceConfigSupplier.get().getDataStreamsTransactionExtractors(); + for (DataStreamsTransactionExtractor extractor : extractors) { + System.out.println(extractor.toString()); + List list = + extractorsByType.computeIfAbsent(extractor.getType(), k -> new LinkedList<>()); + list.add(extractor); + } + } finally { + extractorsLock.writeLock().unlock(); } } From 76167c9156837b0575345437d254d4963075fc37 Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Wed, 12 Nov 2025 09:19:25 -0600 Subject: [PATCH 15/23] Cleanup + test fixes --- .../instrumentation/decorator/HttpClientDecorator.java | 3 --- .../instrumentation/decorator/HttpServerDecorator.java | 2 -- .../kafka_clients/KafkaProducerInstrumentation.java | 1 - .../trace/instrumentation/kafka_clients/TracingIterator.java | 1 - .../instrumentation/kafka_clients38/TracingIterator.java | 1 - .../trace/core/datastreams/DefaultDataStreamsMonitoring.java | 4 +++- .../core/datastreams/MsgPackDatastreamsPayloadWriter.java | 1 - 7 files changed, 3 insertions(+), 10 deletions(-) diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java index 1cda41fec5b..2ff178ef751 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java @@ -73,18 +73,15 @@ protected boolean shouldSetResourceName() { public AgentSpan onRequest(final AgentSpan span, final REQUEST request) { if (request != null) { - System.out.println("### applying http_out extractors " + request.getClass().getSimpleName()); // apply extractors if any (disabled if DSM is off) AgentDataStreamsMonitoring dataStreamsMonitoring = AgentTracer.get().getDataStreamsMonitoring(); List extractorList = dataStreamsMonitoring.getTransactionExtractorsByType( DataStreamsTransactionExtractor.Type.HTTP_OUT_HEADERS); - System.out.println("### extractor list contains " + extractorList.size() + " extractors"); if (!extractorList.isEmpty()) { for (DataStreamsTransactionExtractor extractor : extractorList) { String transactionId = getRequestHeader(request, extractor.getValue()); - System.out.println("### -> http_out extractor " + extractor.getName()); if (transactionId != null && !transactionId.isEmpty()) { dataStreamsMonitoring.trackTransaction(transactionId, extractor.getName()); } diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java index eaf02cf4fad..ce44538f957 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java @@ -334,7 +334,6 @@ public AgentSpan onRequest( } if (request != null) { - System.out.println("### applying http_in extractors"); // apply extractors if any (disabled if DSM is off) AgentDataStreamsMonitoring dataStreamsMonitoring = AgentTracer.get().getDataStreamsMonitoring(); @@ -343,7 +342,6 @@ public AgentSpan onRequest( DataStreamsTransactionExtractor.Type.HTTP_IN_HEADERS); if (!extractorList.isEmpty()) { for (DataStreamsTransactionExtractor extractor : extractorList) { - System.out.println("### -> http_in extractor " + extractor.getName()); String transactionId = getRequestHeader(request, extractor.getValue()); if (transactionId != null) { dataStreamsMonitoring.trackTransaction(transactionId, extractor.getName()); diff --git a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java index cbc722c04d9..a0a82aed565 100644 --- a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java +++ b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java @@ -191,7 +191,6 @@ record = dataStreamsMonitoring.getTransactionExtractorsByType( DataStreamsTransactionExtractor.Type.KAFKA_PRODUCE_HEADERS); if (extractors != null) { - System.out.println("### applying KAFKA_PRODUCE_HEADERS extractors"); for (DataStreamsTransactionExtractor extractor : extractors) { Header header = record.headers().lastHeader(extractor.getValue()); if (header != null && header.value() != null) { diff --git a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/TracingIterator.java b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/TracingIterator.java index 9cd9d583d0b..c4167cecab9 100644 --- a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/TracingIterator.java +++ b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/TracingIterator.java @@ -140,7 +140,6 @@ protected void startNewRecordSpan(ConsumerRecord val) { dataStreamsMonitoring.getTransactionExtractorsByType( DataStreamsTransactionExtractor.Type.KAFKA_CONSUME_HEADERS); if (extractors != null) { - System.out.println("### applying KAFKA_CONSUME_HEADERS extractors"); for (DataStreamsTransactionExtractor extractor : extractors) { Header header = val.headers().lastHeader(extractor.getValue()); if (header != null && header.value() != null) { diff --git a/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/main/java17/datadog/trace/instrumentation/kafka_clients38/TracingIterator.java b/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/main/java17/datadog/trace/instrumentation/kafka_clients38/TracingIterator.java index 98c9d945b86..48f248ad4bc 100644 --- a/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/main/java17/datadog/trace/instrumentation/kafka_clients38/TracingIterator.java +++ b/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/main/java17/datadog/trace/instrumentation/kafka_clients38/TracingIterator.java @@ -140,7 +140,6 @@ protected void startNewRecordSpan(ConsumerRecord val) { dataStreamsMonitoring.getTransactionExtractorsByType( DataStreamsTransactionExtractor.Type.KAFKA_CONSUME_HEADERS); if (extractors != null) { - System.out.println("### applying KAFKA_PRODUCE_HEADERS extractors"); for (DataStreamsTransactionExtractor extractor : extractors) { Header header = val.headers().lastHeader(extractor.getValue()); if (header != null && header.value() != null) { diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java index df9254dfcd0..eaca3c25641 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java @@ -199,7 +199,6 @@ public void clearThreadServiceName() { @Override public void trackTransaction(String transactionId, String checkpointName) { - System.out.println("### trackTransaction " + transactionId); inbox.offer( new TransactionInfo(transactionId, timeSource.getCurrentTimeNanos(), checkpointName)); } @@ -474,6 +473,9 @@ private void updateExtractorsFromConfig() { extractorsByType.clear(); List extractors = traceConfigSupplier.get().getDataStreamsTransactionExtractors(); + if (extractors == null) { + return; + } for (DataStreamsTransactionExtractor extractor : extractors) { System.out.println(extractor.toString()); List list = diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java index 90fd05d1dba..3124087380e 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java @@ -146,7 +146,6 @@ public void writePayload(Collection data, String serviceNameOverrid } if (hasTransactions) { - System.out.println("#### writing transactions ####"); /* 5 */ writer.writeUTF8(TRANSACTIONS); writer.writeBinary(bucket.getTransactions().getData()); From 8e40194a1b7bc0e48f1e6213bbff689954cd4785 Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Wed, 12 Nov 2025 09:22:10 -0600 Subject: [PATCH 16/23] Use constant for no extractors case --- .../trace/core/datastreams/DefaultDataStreamsMonitoring.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java index eaca3c25641..55d8403b6e8 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java @@ -54,6 +54,7 @@ public class DefaultDataStreamsMonitoring implements DataStreamsMonitoring, Even private static final Logger log = LoggerFactory.getLogger(DefaultDataStreamsMonitoring.class); static final long FEATURE_CHECK_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(5); + static final List NO_EXTRACTORS = Collections.emptyList(); private static final StatsPoint REPORT = new StatsPoint(DataStreamsTags.EMPTY, 0, 0, 0, 0, 0, 0, 0, null); @@ -208,7 +209,7 @@ public List getTransactionExtractorsByType( DataStreamsTransactionExtractor.Type extractorType) { extractorsLock.readLock().lock(); try { - return extractorsByType.getOrDefault(extractorType, Collections.emptyList()); + return extractorsByType.getOrDefault(extractorType, NO_EXTRACTORS); } finally { extractorsLock.readLock().unlock(); } From 08c9fa94a95f1656115c4780f3be04cfbc7c37e4 Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Wed, 12 Nov 2025 09:32:20 -0600 Subject: [PATCH 17/23] Avoid read lock if DSM is not enabled --- .../trace/core/datastreams/DefaultDataStreamsMonitoring.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java index 55d8403b6e8..f0f58a58602 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java @@ -207,6 +207,10 @@ public void trackTransaction(String transactionId, String checkpointName) { @Override public List getTransactionExtractorsByType( DataStreamsTransactionExtractor.Type extractorType) { + if (!supportsDataStreams) { + return NO_EXTRACTORS; + } + extractorsLock.readLock().lock(); try { return extractorsByType.getOrDefault(extractorType, NO_EXTRACTORS); From 57b1db592232a5b82d5eaeaf9b16fa97851a57a9 Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Wed, 12 Nov 2025 09:36:22 -0600 Subject: [PATCH 18/23] Removed one more System.out --- .../trace/core/datastreams/DefaultDataStreamsMonitoring.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java index f0f58a58602..34d0bb95d42 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/DefaultDataStreamsMonitoring.java @@ -482,10 +482,10 @@ private void updateExtractorsFromConfig() { return; } for (DataStreamsTransactionExtractor extractor : extractors) { - System.out.println(extractor.toString()); List list = extractorsByType.computeIfAbsent(extractor.getType(), k -> new LinkedList<>()); list.add(extractor); + log.debug("Added data streams transaction extractor: {}", extractor); } } finally { extractorsLock.writeLock().unlock(); From d5627fdb51d97c1d949f9d933a8555dbc145458b Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Wed, 12 Nov 2025 10:30:33 -0600 Subject: [PATCH 19/23] Fixed more tests --- .../instrumentation/decorator/HttpClientDecorator.java | 2 +- .../instrumentation/decorator/HttpServerDecorator.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java index 2ff178ef751..cd899a957e8 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java @@ -79,7 +79,7 @@ public AgentSpan onRequest(final AgentSpan span, final REQUEST request) { List extractorList = dataStreamsMonitoring.getTransactionExtractorsByType( DataStreamsTransactionExtractor.Type.HTTP_OUT_HEADERS); - if (!extractorList.isEmpty()) { + if (extractorList != null) { for (DataStreamsTransactionExtractor extractor : extractorList) { String transactionId = getRequestHeader(request, extractor.getValue()); if (transactionId != null && !transactionId.isEmpty()) { diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java index ce44538f957..f4d3d6b62b0 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java @@ -340,7 +340,7 @@ public AgentSpan onRequest( List extractorList = dataStreamsMonitoring.getTransactionExtractorsByType( DataStreamsTransactionExtractor.Type.HTTP_IN_HEADERS); - if (!extractorList.isEmpty()) { + if (extractorList != null) { for (DataStreamsTransactionExtractor extractor : extractorList) { String transactionId = getRequestHeader(request, extractor.getValue()); if (transactionId != null) { From 092afea9b97af35e887031cd8d88809e37e21fe5 Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Wed, 12 Nov 2025 10:59:17 -0600 Subject: [PATCH 20/23] Removed RC for now --- .../src/main/java/datadog/trace/core/TracingConfigPoller.java | 4 +--- .../src/main/java/datadog/remoteconfig/Capabilities.java | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/dd-trace-core/src/main/java/datadog/trace/core/TracingConfigPoller.java b/dd-trace-core/src/main/java/datadog/trace/core/TracingConfigPoller.java index 580acb606b4..1466f1f4fb7 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/TracingConfigPoller.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/TracingConfigPoller.java @@ -12,7 +12,6 @@ import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_TRACING_SAMPLE_RATE; import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_TRACING_SAMPLE_RULES; import static datadog.remoteconfig.Capabilities.CAPABILITY_APM_TRACING_TRACING_ENABLED; -import static datadog.remoteconfig.Capabilities.CAPABILITY_DATA_STREAMS_TRANSACTION_EXTRACTORS; import static datadog.trace.api.sampling.SamplingRule.normalizeGlob; import com.squareup.moshi.FromJson; @@ -80,8 +79,7 @@ public void start(Config config, SharedCommunicationObjects sco) { | CAPABILITY_APM_TRACING_ENABLE_EXCEPTION_REPLAY | CAPABILITY_APM_TRACING_ENABLE_CODE_ORIGIN | CAPABILITY_APM_TRACING_ENABLE_LIVE_DEBUGGING - | CAPABILITY_APM_TRACING_MULTICONFIG - | CAPABILITY_DATA_STREAMS_TRANSACTION_EXTRACTORS); + | CAPABILITY_APM_TRACING_MULTICONFIG); } stopPolling = new Updater().register(config, configPoller); } diff --git a/remote-config/remote-config-api/src/main/java/datadog/remoteconfig/Capabilities.java b/remote-config/remote-config-api/src/main/java/datadog/remoteconfig/Capabilities.java index ee542d36639..7ddd4e0c693 100644 --- a/remote-config/remote-config-api/src/main/java/datadog/remoteconfig/Capabilities.java +++ b/remote-config/remote-config-api/src/main/java/datadog/remoteconfig/Capabilities.java @@ -46,5 +46,4 @@ public interface Capabilities { long CAPABILITY_ASM_TRACE_TAGGING_RULES = 1L << 43; long CAPABILITY_ASM_EXTENDED_DATA_COLLECTION = 1L << 44; long CAPABILITY_APM_TRACING_MULTICONFIG = 1L << 45; - long CAPABILITY_DATA_STREAMS_TRANSACTION_EXTRACTORS = 1L << 47; } From d3fa445dc00832028d9c8891426c0d94807cc91e Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Wed, 12 Nov 2025 11:21:14 -0600 Subject: [PATCH 21/23] Mark traces with transaction id --- .../instrumentation/decorator/HttpClientDecorator.java | 2 ++ .../instrumentation/decorator/HttpServerDecorator.java | 2 ++ .../kafka_clients/KafkaProducerInstrumentation.java | 7 +++++-- .../instrumentation/kafka_clients/TracingIterator.java | 7 +++++-- .../instrumentation/kafka_clients38/TracingIterator.java | 7 +++++-- .../datadog/trace/bootstrap/instrumentation/api/Tags.java | 2 ++ 6 files changed, 21 insertions(+), 6 deletions(-) diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java index cd899a957e8..ac9b20d10aa 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpClientDecorator.java @@ -84,6 +84,8 @@ public AgentSpan onRequest(final AgentSpan span, final REQUEST request) { String transactionId = getRequestHeader(request, extractor.getValue()); if (transactionId != null && !transactionId.isEmpty()) { dataStreamsMonitoring.trackTransaction(transactionId, extractor.getName()); + span.setTag(Tags.DSM_TRANSACTION_ID, transactionId); + span.setTag(Tags.DSM_TRANSACTION_CHECKPOINT, extractor.getName()); } } } diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java index f4d3d6b62b0..8205f01210a 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/HttpServerDecorator.java @@ -345,6 +345,8 @@ public AgentSpan onRequest( String transactionId = getRequestHeader(request, extractor.getValue()); if (transactionId != null) { dataStreamsMonitoring.trackTransaction(transactionId, extractor.getName()); + span.setTag(Tags.DSM_TRANSACTION_ID, transactionId); + span.setTag(Tags.DSM_TRANSACTION_CHECKPOINT, extractor.getName()); } } } diff --git a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java index a0a82aed565..cec22bee967 100644 --- a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java +++ b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java @@ -37,6 +37,7 @@ import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentTracer; import datadog.trace.bootstrap.instrumentation.api.InstrumentationTags; +import datadog.trace.bootstrap.instrumentation.api.Tags; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; @@ -194,8 +195,10 @@ record = for (DataStreamsTransactionExtractor extractor : extractors) { Header header = record.headers().lastHeader(extractor.getValue()); if (header != null && header.value() != null) { - dataStreamsMonitoring.trackTransaction( - new String(header.value(), StandardCharsets.UTF_8), extractor.getName()); + String transactionId = new String(header.value(), StandardCharsets.UTF_8); + dataStreamsMonitoring.trackTransaction(transactionId, extractor.getName()); + span.setTag(Tags.DSM_TRANSACTION_ID, transactionId); + span.setTag(Tags.DSM_TRANSACTION_CHECKPOINT, extractor.getName()); } } } diff --git a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/TracingIterator.java b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/TracingIterator.java index c4167cecab9..49bf03153d0 100644 --- a/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/TracingIterator.java +++ b/dd-java-agent/instrumentation/kafka/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/TracingIterator.java @@ -29,6 +29,7 @@ import datadog.trace.bootstrap.instrumentation.api.AgentSpanContext; import datadog.trace.bootstrap.instrumentation.api.AgentTracer; import datadog.trace.bootstrap.instrumentation.api.InstrumentationTags; +import datadog.trace.bootstrap.instrumentation.api.Tags; import java.nio.charset.StandardCharsets; import java.util.Iterator; import java.util.List; @@ -143,8 +144,10 @@ protected void startNewRecordSpan(ConsumerRecord val) { for (DataStreamsTransactionExtractor extractor : extractors) { Header header = val.headers().lastHeader(extractor.getValue()); if (header != null && header.value() != null) { - dataStreamsMonitoring.trackTransaction( - new String(header.value(), StandardCharsets.UTF_8), extractor.getName()); + String transactionId = new String(header.value(), StandardCharsets.UTF_8); + dataStreamsMonitoring.trackTransaction(transactionId, extractor.getName()); + span.setTag(Tags.DSM_TRANSACTION_ID, transactionId); + span.setTag(Tags.DSM_TRANSACTION_CHECKPOINT, extractor.getName()); } } } diff --git a/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/main/java17/datadog/trace/instrumentation/kafka_clients38/TracingIterator.java b/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/main/java17/datadog/trace/instrumentation/kafka_clients38/TracingIterator.java index 48f248ad4bc..bbf4a6361e5 100644 --- a/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/main/java17/datadog/trace/instrumentation/kafka_clients38/TracingIterator.java +++ b/dd-java-agent/instrumentation/kafka/kafka-clients-3.8/src/main/java17/datadog/trace/instrumentation/kafka_clients38/TracingIterator.java @@ -24,6 +24,7 @@ import datadog.trace.bootstrap.instrumentation.api.AgentSpanContext; import datadog.trace.bootstrap.instrumentation.api.AgentTracer; import datadog.trace.bootstrap.instrumentation.api.InstrumentationTags; +import datadog.trace.bootstrap.instrumentation.api.Tags; import datadog.trace.instrumentation.kafka_common.StreamingContext; import datadog.trace.instrumentation.kafka_common.Utils; import java.nio.charset.StandardCharsets; @@ -143,8 +144,10 @@ protected void startNewRecordSpan(ConsumerRecord val) { for (DataStreamsTransactionExtractor extractor : extractors) { Header header = val.headers().lastHeader(extractor.getValue()); if (header != null && header.value() != null) { - dataStreamsMonitoring.trackTransaction( - new String(header.value(), StandardCharsets.UTF_8), extractor.getName()); + String transactionId = new String(header.value(), StandardCharsets.UTF_8); + dataStreamsMonitoring.trackTransaction(transactionId, extractor.getName()); + span.setTag(Tags.DSM_TRANSACTION_ID, transactionId); + span.setTag(Tags.DSM_TRANSACTION_CHECKPOINT, extractor.getName()); } } } diff --git a/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/Tags.java b/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/Tags.java index 3eaa1e292cc..20629daf051 100644 --- a/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/Tags.java +++ b/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/Tags.java @@ -174,4 +174,6 @@ public class Tags { public static final String LLMOBS_TOOL_SPAN_KIND = "tool"; public static final String LLMOBS_EMBEDDING_SPAN_KIND = "embedding"; public static final String LLMOBS_RETRIEVAL_SPAN_KIND = "retrieval"; + public static final String DSM_TRANSACTION_ID = "dsm.transaction.id"; + public static final String DSM_TRANSACTION_CHECKPOINT = "dsm.transaction.checkpoint"; } From d9349f379a4e3c008fc4c88d6c57f1be923ea050 Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Wed, 19 Nov 2025 11:58:09 -0600 Subject: [PATCH 22/23] Some debug logs --- .../MsgPackDatastreamsPayloadWriter.java | 5 ++++- .../datastreams/TransactionContainerTest.groovy | 13 +++++++++++++ .../trace/api/datastreams/TransactionInfo.java | 1 - 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java index 3124087380e..27e21bdcd34 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java @@ -97,9 +97,12 @@ public void writePayload(Collection data, String serviceNameOverrid /* 2 */ writer.writeUTF8(SERVICE); - if (serviceNameOverride != null) { + if (serviceNameOverride != null && !serviceNameOverride.isEmpty()) { + System.out.println("### Service name from override: " + serviceNameOverride); writer.writeUTF8(serviceNameOverride.getBytes(ISO_8859_1)); } else { + System.out.println( + "### Well known tags: " + wellKnownTags + ", service " + wellKnownTags.getService()); writer.writeUTF8(wellKnownTags.getService()); } diff --git a/dd-trace-core/src/test/groovy/datadog/trace/core/datastreams/TransactionContainerTest.groovy b/dd-trace-core/src/test/groovy/datadog/trace/core/datastreams/TransactionContainerTest.groovy index 01181a53ad4..a7593acb19e 100644 --- a/dd-trace-core/src/test/groovy/datadog/trace/core/datastreams/TransactionContainerTest.groovy +++ b/dd-trace-core/src/test/groovy/datadog/trace/core/datastreams/TransactionContainerTest.groovy @@ -33,4 +33,17 @@ class TransactionContainerTest extends DDCoreSpecification { 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 49, 2, 0, 0, 0, 0, 0, 0, 0, 2, 1, 50 } } + + def "test checkpoint map"() { + given: + TransactionInfo.resetCache() + new TransactionInfo("1", 1, "1") + new TransactionInfo("2", 2, "2") + def data = TransactionInfo.getCheckpointIdCacheBytes() + expect: + data.size() == 6 + data == new byte[] { + 1, 1, 49, 2, 1, 50 + } + } } diff --git a/internal-api/src/main/java/datadog/trace/api/datastreams/TransactionInfo.java b/internal-api/src/main/java/datadog/trace/api/datastreams/TransactionInfo.java index 87a6fa43aed..4615f4d9b55 100644 --- a/internal-api/src/main/java/datadog/trace/api/datastreams/TransactionInfo.java +++ b/internal-api/src/main/java/datadog/trace/api/datastreams/TransactionInfo.java @@ -44,7 +44,6 @@ public byte[] getBytes() { result[2] = (byte) (timestamp >> 48); result[3] = (byte) (timestamp >> 40); result[4] = (byte) (timestamp >> 32); - ; result[5] = (byte) (timestamp >> 24); result[6] = (byte) (timestamp >> 16); result[7] = (byte) (timestamp >> 8); From 413efac28e6c5f7c2ebfed477d4e6981377577d3 Mon Sep 17 00:00:00 2001 From: Igor Kravchenko Date: Wed, 19 Nov 2025 12:32:51 -0600 Subject: [PATCH 23/23] Added toString to wellKnownTags --- .../MsgPackDatastreamsPayloadWriter.java | 3 --- .../java/datadog/trace/api/WellKnownTags.java | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java index 27e21bdcd34..03128f559ca 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/datastreams/MsgPackDatastreamsPayloadWriter.java @@ -98,11 +98,8 @@ public void writePayload(Collection data, String serviceNameOverrid /* 2 */ writer.writeUTF8(SERVICE); if (serviceNameOverride != null && !serviceNameOverride.isEmpty()) { - System.out.println("### Service name from override: " + serviceNameOverride); writer.writeUTF8(serviceNameOverride.getBytes(ISO_8859_1)); } else { - System.out.println( - "### Well known tags: " + wellKnownTags + ", service " + wellKnownTags.getService()); writer.writeUTF8(wellKnownTags.getService()); } diff --git a/internal-api/src/main/java/datadog/trace/api/WellKnownTags.java b/internal-api/src/main/java/datadog/trace/api/WellKnownTags.java index f01e8d154ef..fa00fea11cb 100644 --- a/internal-api/src/main/java/datadog/trace/api/WellKnownTags.java +++ b/internal-api/src/main/java/datadog/trace/api/WellKnownTags.java @@ -49,4 +49,21 @@ public UTF8BytesString getVersion() { public UTF8BytesString getLanguage() { return language; } + + public String toString() { + return "WellKnownTags{" + + "runtimeId=" + + runtimeId + + ", hostname=" + + hostname + + ", env=" + + env + + ", service=" + + service + + ", version=" + + version + + ", language=" + + language + + "}"; + } }