From 6680c2bb187d0bdd36ef992e518805da73194f39 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Wed, 29 Jul 2026 19:00:24 -0400 Subject: [PATCH 1/8] Fix JSON span event encoding in v1 payloads --- .../common/writer/ddagent/TraceMapperV1.java | 23 ++++++++- .../trace/core/DDSpanSerializationTest.java | 48 +++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/dd-trace-core/src/main/java/datadog/trace/common/writer/ddagent/TraceMapperV1.java b/dd-trace-core/src/main/java/datadog/trace/common/writer/ddagent/TraceMapperV1.java index 819ff2021be..944a99c2992 100644 --- a/dd-trace-core/src/main/java/datadog/trace/common/writer/ddagent/TraceMapperV1.java +++ b/dd-trace-core/src/main/java/datadog/trace/common/writer/ddagent/TraceMapperV1.java @@ -1,6 +1,7 @@ package datadog.trace.common.writer.ddagent; import static datadog.communication.http.OkHttpUtils.msgpackRequestBodyOf; +import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; @@ -11,6 +12,7 @@ import datadog.communication.serialization.Writable; import datadog.communication.serialization.msgpack.MsgPackWriter; import datadog.environment.JavaVirtualMachine; +import datadog.json.JsonReader; import datadog.trace.api.Config; import datadog.trace.api.DDTags; import datadog.trace.api.DDTraceId; @@ -202,12 +204,12 @@ private void encodeSpanLinks( private void encodeSpanEvents(Writable writable, int fieldId, Object eventsObject) { writable.writeInt(fieldId); - if (!(eventsObject instanceof List) || ((List) eventsObject).isEmpty()) { + List events = parseSpanEvents(eventsObject); + if (events.isEmpty()) { writable.startArray(0); return; } - List events = (List) eventsObject; int encodableCount = 0; for (Object event : events) { if (isEncodableSpanEvent(event)) { @@ -236,6 +238,23 @@ private void encodeSpanEvents(Writable writable, int fieldId, Object eventsObjec } } + private List parseSpanEvents(Object eventsObject) { + if (eventsObject instanceof List) { + return (List) eventsObject; + } + if (eventsObject instanceof CharSequence) { + try (JsonReader reader = new JsonReader(eventsObject.toString())) { + Object events = reader.nextValue(); + if (events instanceof List) { + return (List) events; + } + } catch (IOException e) { + log.debug("Failed to parse span events from JSON", e); + } + } + return emptyList(); + } + private boolean isEncodableSpanEvent(Object event) { if (!(event instanceof Map)) { return false; diff --git a/dd-trace-core/src/test/java/datadog/trace/core/DDSpanSerializationTest.java b/dd-trace-core/src/test/java/datadog/trace/core/DDSpanSerializationTest.java index aecf3517072..5ec74bc7748 100644 --- a/dd-trace-core/src/test/java/datadog/trace/core/DDSpanSerializationTest.java +++ b/dd-trace-core/src/test/java/datadog/trace/core/DDSpanSerializationTest.java @@ -1,5 +1,6 @@ package datadog.trace.core; +import static datadog.trace.api.DDTags.SPAN_EVENTS; import static datadog.trace.api.DDTags.SPAN_LINKS; import static datadog.trace.api.config.GeneralConfig.EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED; import static datadog.trace.api.config.TracerConfig.TRACE_BAGGAGE_TAG_KEYS; @@ -33,6 +34,7 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.Channels; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -393,6 +395,52 @@ void serializeTraceWithSpanLinksAsStructuredLinksOnlyV1() throws Exception { tracer.close(); } + @Test + void serializeTraceWithJsonSpanEventsV1() throws Exception { + CoreTracer tracer = tracerBuilder().writer(new ListWriter()).build(); + DDSpanContext context = createSpanContext(tracer, Collections.emptyMap(), null, true, true, 1); + context.setTag( + SPAN_EVENTS, + "[{\"time_unix_nano\":1234567890,\"name\":\"event.one\"," + + "\"attributes\":{\"str\":\"value\",\"int\":42,\"double\":12.5," + + "\"bool\":true,\"arr\":[\"x\",7,2.5,false]}}," + + "{\"time_unix_nano\":1234567891,\"name\":\"event.two\"}]"); + DDSpan span = DDSpan.create("test", 0, context, null); + + V1PayloadReader.V1Span payload = V1PayloadReader.readFirstSpan(serializeV1Payload(span)); + + assertFalse(payload.getAttributes().containsKey(SPAN_EVENTS)); + assertEquals(2, payload.getEvents().size()); + V1PayloadReader.V1SpanEvent firstEvent = payload.getEvents().get(0); + assertEquals(1234567890L, firstEvent.getTimeUnixNano()); + assertEquals("event.one", firstEvent.getName()); + assertEquals("value", firstEvent.getAttributes().get("str")); + assertEquals(42L, firstEvent.getAttributes().get("int")); + assertEquals(12.5d, firstEvent.getAttributes().get("double")); + assertEquals(true, firstEvent.getAttributes().get("bool")); + assertEquals(Arrays.asList("x", 7L, 2.5d, false), firstEvent.getAttributes().get("arr")); + + V1PayloadReader.V1SpanEvent secondEvent = payload.getEvents().get(1); + assertEquals(1234567891L, secondEvent.getTimeUnixNano()); + assertEquals("event.two", secondEvent.getName()); + assertEquals(Collections.emptyMap(), secondEvent.getAttributes()); + tracer.close(); + } + + @Test + void serializeTraceWithMalformedJsonSpanEventsAsEmptyV1() throws Exception { + CoreTracer tracer = tracerBuilder().writer(new ListWriter()).build(); + DDSpanContext context = createSpanContext(tracer, Collections.emptyMap(), null, true, true, 1); + context.setTag(SPAN_EVENTS, "[{"); + DDSpan span = DDSpan.create("test", 0, context, null); + + V1PayloadReader.V1Span payload = V1PayloadReader.readFirstSpan(serializeV1Payload(span)); + + assertFalse(payload.getAttributes().containsKey(SPAN_EVENTS)); + assertEquals(Collections.emptyList(), payload.getEvents()); + tracer.close(); + } + @Test void serializeTraceWithFlatMapTagV04() throws Exception { CoreTracer tracer = tracerBuilder().writer(new ListWriter()).build(); From ffe6112ff0a3a0e32c5ea1e41b2ca04cd8269ad9 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Wed, 29 Jul 2026 19:06:03 -0400 Subject: [PATCH 2/8] Enable `v1` by default to run the tests on CI. --- .../communication/ddagent/DDAgentFeaturesDiscovery.java | 4 ++-- .../ddagent/DDAgentFeaturesDiscoveryTest.groovy | 7 ++++--- .../src/main/java/datadog/trace/api/ConfigDefaults.java | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/communication/src/main/java/datadog/communication/ddagent/DDAgentFeaturesDiscovery.java b/communication/src/main/java/datadog/communication/ddagent/DDAgentFeaturesDiscovery.java index 16be2e84b98..398435a2589 100644 --- a/communication/src/main/java/datadog/communication/ddagent/DDAgentFeaturesDiscovery.java +++ b/communication/src/main/java/datadog/communication/ddagent/DDAgentFeaturesDiscovery.java @@ -4,7 +4,7 @@ import static datadog.communication.http.OkHttpUtils.msgpackRequestBodyOf; import static datadog.communication.http.OkHttpUtils.prepareRequest; import static datadog.communication.serialization.msgpack.MsgPackWriter.FIXARRAY; -import static datadog.trace.api.ProtocolVersion.V0_4; +import static datadog.trace.api.ProtocolVersion.V1_0; import static java.util.Collections.emptyMap; import static java.util.Collections.emptySet; import static java.util.Collections.singletonList; @@ -118,7 +118,7 @@ public DDAgentFeaturesDiscovery( this.agentBaseUrl = agentUrl; this.metricsEnabled = metricsEnabled; this.ignoreAgentVersionForStats = ignoreAgentVersionForStats; - this.protocolVersion = protocolVersion != null ? protocolVersion : V0_4; + this.protocolVersion = protocolVersion != null ? protocolVersion : V1_0; this.discoveryTimer = monitoring.newTimer("trace.agent.discovery.time"); this.discoveryState = new State(); } diff --git a/communication/src/test/groovy/datadog/communication/ddagent/DDAgentFeaturesDiscoveryTest.groovy b/communication/src/test/groovy/datadog/communication/ddagent/DDAgentFeaturesDiscoveryTest.groovy index 8bcda6eb811..6fae9ca5f40 100644 --- a/communication/src/test/groovy/datadog/communication/ddagent/DDAgentFeaturesDiscoveryTest.groovy +++ b/communication/src/test/groovy/datadog/communication/ddagent/DDAgentFeaturesDiscoveryTest.groovy @@ -87,7 +87,7 @@ class DDAgentFeaturesDiscoveryTest extends DDSpecification { V1_0 | V1_ENDPOINT } - def "null protocol version falls back to v0.4 trace endpoints"() { + def "null protocol version falls back to v1.0 trace endpoints"() { setup: OkHttpClient client = Mock(OkHttpClient) DDAgentFeaturesDiscovery features = @@ -98,9 +98,10 @@ class DDAgentFeaturesDiscoveryTest extends DDSpecification { then: 1 * client.newCall({ Request request -> request.url().toString() == "http://localhost:8125/info" }) >> { Request request -> infoResponse(request, "{}") } + 1 * client.newCall({ Request request -> request.url().toString() == "http://localhost:8125/v1.0/traces" }) >> { Request request -> success(request) } 0 * client.newCall({ Request request -> request.url().toString() == "http://localhost:8125/v0.5/traces" }) >> { Request request -> success(request) } - 1 * client.newCall({ Request request -> request.url().toString() == "http://localhost:8125/v0.4/traces" }) >> { Request request -> success(request) } - features.getTraceEndpoint() == V04_ENDPOINT + 0 * client.newCall({ Request request -> request.url().toString() == "http://localhost:8125/v0.4/traces" }) >> { Request request -> success(request) } + features.getTraceEndpoint() == V1_ENDPOINT 0 * _ } diff --git a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java index 991c6fd6937..0e88b3cbf43 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java @@ -92,7 +92,7 @@ public final class ConfigDefaults { Arrays.asList("user.id", "session.id", "account.id"); static final boolean DEFAULT_JMX_FETCH_ENABLED = true; - static final String DEFAULT_TRACE_AGENT_PROTOCOL_VERSION = ProtocolVersion.V0_4.asConfigValue(); + static final String DEFAULT_TRACE_AGENT_PROTOCOL_VERSION = ProtocolVersion.V1_0.asConfigValue(); static final boolean DEFAULT_CLIENT_IP_ENABLED = false; From 0f351ca09554db2209a950aa3ac70bbd9a975272 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Thu, 30 Jul 2026 10:57:31 -0400 Subject: [PATCH 3/8] Debug SSI system-tests --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ca5a947885d..0039f2bee31 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1475,7 +1475,7 @@ publishing-gate: configure_system_tests: variables: - SYSTEM_TESTS_REF: "main" # system tests are pinned on release branches only + SYSTEM_TESTS_REF: "alexeyk/fix-v1-appsec-event-metrics-validation" # system tests are pinned on release branches only SYSTEM_TESTS_SCENARIOS_GROUPS: "simple_onboarding,simple_onboarding_profiling,simple_onboarding_appsec,docker-ssi,lib-injection" create_key: From 707372a76063d40d253c2898939bc7c572e22ca6 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Thu, 30 Jul 2026 11:41:03 -0400 Subject: [PATCH 4/8] Debug SSI system-tests --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0039f2bee31..59e82393324 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1475,7 +1475,7 @@ publishing-gate: configure_system_tests: variables: - SYSTEM_TESTS_REF: "alexeyk/fix-v1-appsec-event-metrics-validation" # system tests are pinned on release branches only + SYSTEM_TESTS_REF: "alexeyk/fix-v1-appsec-event-metrics-validation" # system-tests are pinned on release branches only SYSTEM_TESTS_SCENARIOS_GROUPS: "simple_onboarding,simple_onboarding_profiling,simple_onboarding_appsec,docker-ssi,lib-injection" create_key: From f707cb8982124ea87fc58afa519b364ce457fcc4 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Thu, 30 Jul 2026 12:39:24 -0400 Subject: [PATCH 5/8] Debug SSI system-tests: actual fix --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 59e82393324..0039f2bee31 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1475,7 +1475,7 @@ publishing-gate: configure_system_tests: variables: - SYSTEM_TESTS_REF: "alexeyk/fix-v1-appsec-event-metrics-validation" # system-tests are pinned on release branches only + SYSTEM_TESTS_REF: "alexeyk/fix-v1-appsec-event-metrics-validation" # system tests are pinned on release branches only SYSTEM_TESTS_SCENARIOS_GROUPS: "simple_onboarding,simple_onboarding_profiling,simple_onboarding_appsec,docker-ssi,lib-injection" create_key: From 7760deecc423d20dd7cb0231e71cecc5cf83d4ac Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Fri, 31 Jul 2026 16:15:57 -0400 Subject: [PATCH 6/8] Test with systemt-test from master. --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f7f8b5f7e0c..fed9f25df92 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1475,7 +1475,7 @@ publishing-gate: configure_system_tests: variables: - SYSTEM_TESTS_REF: "alexeyk/fix-v1-appsec-event-metrics-validation" # system tests are pinned on release branches only + SYSTEM_TESTS_REF: "main" # system tests are pinned on release branches only SYSTEM_TESTS_SCENARIOS_GROUPS: "simple_onboarding,simple_onboarding_profiling,simple_onboarding_appsec,docker-ssi,lib-injection" create_key: From 314a2c6ae1aa320f165c15bde9b0559c1ee3aa38 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Fri, 31 Jul 2026 20:40:26 -0400 Subject: [PATCH 7/8] Test the latest fix in system-tests. --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fed9f25df92..dfe0bcbfd46 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1475,7 +1475,7 @@ publishing-gate: configure_system_tests: variables: - SYSTEM_TESTS_REF: "main" # system tests are pinned on release branches only + SYSTEM_TESTS_REF: "alexeyk/fix-v1-trace-id-comparison" # system tests are pinned on release branches only SYSTEM_TESTS_SCENARIOS_GROUPS: "simple_onboarding,simple_onboarding_profiling,simple_onboarding_appsec,docker-ssi,lib-injection" create_key: From ed1e4321b37c7b88cc18a7019456b2dae266bd5b Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Fri, 31 Jul 2026 21:18:35 -0400 Subject: [PATCH 8/8] Test the latest fix in system-tests. --- .github/workflows/run-system-tests.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-system-tests.yaml b/.github/workflows/run-system-tests.yaml index 4b0ad663659..b38faee1cca 100644 --- a/.github/workflows/run-system-tests.yaml +++ b/.github/workflows/run-system-tests.yaml @@ -62,7 +62,7 @@ jobs: needs: - build # If you change the following comment, update the pattern in the update_system_test_reference.sh script to match. - uses: DataDog/system-tests/.github/workflows/system-tests.yml@main # system tests are pinned on release branches only + uses: DataDog/system-tests/.github/workflows/system-tests.yml@alexeyk/fix-v1-trace-id-comparison # system tests are pinned on release branches only permissions: contents: read id-token: write @@ -70,7 +70,7 @@ jobs: with: library: java # If you change the following comment, update the pattern in the update_system_test_reference.sh script to match. - ref: "main" # system tests are pinned on release branches only + ref: "alexeyk/fix-v1-trace-id-comparison" # system tests are pinned on release branches only binaries_artifact: binaries desired_execution_time: 900 # 15 minutes scenarios_groups: tracer-release