From aa9d3fae34e94645035a768cdc7f58e231f4a5a8 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Wed, 3 Sep 2025 17:26:14 +0200 Subject: [PATCH 1/2] fix: dont remove id tags and instead ignore in consumer --- .../writer/ddintake/CiTestCycleMapperV1.java | 22 +++++++++---------- .../CiTestCycleMapperV1PayloadTest.groovy | 21 ++++++++++++++++++ 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/dd-trace-core/src/main/java/datadog/trace/civisibility/writer/ddintake/CiTestCycleMapperV1.java b/dd-trace-core/src/main/java/datadog/trace/civisibility/writer/ddintake/CiTestCycleMapperV1.java index 534282701b7..7593ce9b9dd 100644 --- a/dd-trace-core/src/main/java/datadog/trace/civisibility/writer/ddintake/CiTestCycleMapperV1.java +++ b/dd-trace-core/src/main/java/datadog/trace/civisibility/writer/ddintake/CiTestCycleMapperV1.java @@ -9,6 +9,7 @@ import datadog.communication.serialization.msgpack.MsgPackWriter; import datadog.trace.api.DDTags; import datadog.trace.api.DDTraceId; +import datadog.trace.api.TagMap; import datadog.trace.api.civisibility.CiVisibilityWellKnownTags; import datadog.trace.api.civisibility.InstrumentationBridge; import datadog.trace.api.civisibility.telemetry.CiVisibilityDistributionMetric; @@ -84,16 +85,9 @@ public void map(List> trace, Writable writable) { for (final CoreSpan span : trace) { DDTraceId testSessionId = span.getTag(Tags.TEST_SESSION_ID); - span.removeTag(Tags.TEST_SESSION_ID); - Number testModuleId = span.getTag(Tags.TEST_MODULE_ID); - span.removeTag(Tags.TEST_MODULE_ID); - Number testSuiteId = span.getTag(Tags.TEST_SUITE_ID); - span.removeTag(Tags.TEST_SUITE_ID); - String itrCorrelationId = span.getTag(Tags.ITR_CORRELATION_ID); - span.removeTag(Tags.ITR_CORRELATION_ID); int topLevelTagsCount = 0; if (testSessionId != null) { @@ -323,12 +317,18 @@ MetaWriter withWritable(Writable writable) { @Override public void accept(Metadata metadata) { + TagMap tags = metadata.getTags().copy(); + + for (String ignoredTag : DEFAULT_TOP_LEVEL_TAGS) { + tags.remove(ignoredTag); + } + int metaSize = metadata.getBaggage().size() - + metadata.getTags().size() + + tags.size() + (null == metadata.getHttpStatusCode() ? 0 : 1); int metricsSize = 0; - for (Map.Entry tag : metadata.getTags().entrySet()) { + for (Map.Entry tag : tags.entrySet()) { if (tag.getValue() instanceof Number) { ++metricsSize; --metaSize; @@ -336,7 +336,7 @@ public void accept(Metadata metadata) { } writable.writeUTF8(METRICS); writable.startMap(metricsSize); - for (Map.Entry entry : metadata.getTags().entrySet()) { + for (Map.Entry entry : tags.entrySet()) { if (entry.getValue() instanceof Number) { writable.writeString(entry.getKey(), null); writable.writeObject(entry.getValue(), null); @@ -356,7 +356,7 @@ public void accept(Metadata metadata) { writable.writeUTF8(HTTP_STATUS); writable.writeUTF8(metadata.getHttpStatusCode()); } - for (Map.Entry entry : metadata.getTags().entrySet()) { + for (Map.Entry entry : tags.entrySet()) { Object value = entry.getValue(); if (!(value instanceof Number)) { writable.writeString(entry.getKey(), null); diff --git a/dd-trace-core/src/test/groovy/datadog/trace/civisibility/writer/ddintake/CiTestCycleMapperV1PayloadTest.groovy b/dd-trace-core/src/test/groovy/datadog/trace/civisibility/writer/ddintake/CiTestCycleMapperV1PayloadTest.groovy index 98b86f01b3f..8c9bf2768c6 100644 --- a/dd-trace-core/src/test/groovy/datadog/trace/civisibility/writer/ddintake/CiTestCycleMapperV1PayloadTest.groovy +++ b/dd-trace-core/src/test/groovy/datadog/trace/civisibility/writer/ddintake/CiTestCycleMapperV1PayloadTest.groovy @@ -149,6 +149,27 @@ class CiTestCycleMapperV1PayloadTest extends DDSpecification { assert !spanContent.containsKey("parent_id") } + def "verify result is not affected by successive mapping calls"(){ + setup: + def span = generateRandomSpan(InternalSpanTypes.TEST, [ + (Tags.TEST_SESSION_ID): DDTraceId.from(123), + (Tags.TEST_MODULE_ID) : 456, + (Tags.TEST_SUITE_ID) : 789, + ]) + + when: + whenASpanIsWritten(span) + Map deserializedSpan = whenASpanIsWritten(span) + + then: + verifyTopLevelTags(deserializedSpan, DDTraceId.from(123), 456, 789) + + def spanContent = (Map) deserializedSpan.get("content") + assert spanContent.containsKey("trace_id") + assert spanContent.containsKey("span_id") + assert spanContent.containsKey("parent_id") + } + private static void verifyTopLevelTags(Map deserializedSpan, DDTraceId testSessionId, Long testModuleId, Long testSuiteId) { Map deserializedSpanContent = (Map) deserializedSpan.get("content") Map deserializedMetrics = (Map) deserializedSpanContent.get("metrics") From 2126aed1888027faf6a5d3e0d18234794f613ed2 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Thu, 4 Sep 2025 11:34:13 +0200 Subject: [PATCH 2/2] fix: filter on iteration instead of using a copy of the tags --- .../writer/ddintake/CiTestCycleMapperV1.java | 52 +++++++++++-------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/dd-trace-core/src/main/java/datadog/trace/civisibility/writer/ddintake/CiTestCycleMapperV1.java b/dd-trace-core/src/main/java/datadog/trace/civisibility/writer/ddintake/CiTestCycleMapperV1.java index 7593ce9b9dd..d65295563f5 100644 --- a/dd-trace-core/src/main/java/datadog/trace/civisibility/writer/ddintake/CiTestCycleMapperV1.java +++ b/dd-trace-core/src/main/java/datadog/trace/civisibility/writer/ddintake/CiTestCycleMapperV1.java @@ -9,7 +9,6 @@ import datadog.communication.serialization.msgpack.MsgPackWriter; import datadog.trace.api.DDTags; import datadog.trace.api.DDTraceId; -import datadog.trace.api.TagMap; import datadog.trace.api.civisibility.CiVisibilityWellKnownTags; import datadog.trace.api.civisibility.InstrumentationBridge; import datadog.trace.api.civisibility.telemetry.CiVisibilityDistributionMetric; @@ -59,9 +58,14 @@ public class CiTestCycleMapperV1 implements RemoteMapper { private static final UTF8BytesString SPAN_TYPE = UTF8BytesString.create("span"); - private static final Collection DEFAULT_TOP_LEVEL_TAGS = - Arrays.asList( - Tags.TEST_SESSION_ID, Tags.TEST_MODULE_ID, Tags.TEST_SUITE_ID, Tags.ITR_CORRELATION_ID); + private static final Set DEFAULT_TOP_LEVEL_TAGS = + Collections.unmodifiableSet( + new HashSet<>( + Arrays.asList( + Tags.TEST_SESSION_ID, + Tags.TEST_MODULE_ID, + Tags.TEST_SUITE_ID, + Tags.ITR_CORRELATION_ID))); private final CiVisibilityWellKnownTags wellKnownTags; private final int size; @@ -317,29 +321,29 @@ MetaWriter withWritable(Writable writable) { @Override public void accept(Metadata metadata) { - TagMap tags = metadata.getTags().copy(); - - for (String ignoredTag : DEFAULT_TOP_LEVEL_TAGS) { - tags.remove(ignoredTag); - } - - int metaSize = - metadata.getBaggage().size() - + tags.size() - + (null == metadata.getHttpStatusCode() ? 0 : 1); + int metaSize = metadata.getBaggage().size() + (null == metadata.getHttpStatusCode() ? 0 : 1); int metricsSize = 0; - for (Map.Entry tag : tags.entrySet()) { + for (Map.Entry tag : metadata.getTags().entrySet()) { + if (DEFAULT_TOP_LEVEL_TAGS.contains(tag.getKey())) { + continue; + } if (tag.getValue() instanceof Number) { ++metricsSize; - --metaSize; + } else { + ++metaSize; } } writable.writeUTF8(METRICS); writable.startMap(metricsSize); - for (Map.Entry entry : tags.entrySet()) { - if (entry.getValue() instanceof Number) { - writable.writeString(entry.getKey(), null); - writable.writeObject(entry.getValue(), null); + for (Map.Entry entry : metadata.getTags().entrySet()) { + String key = entry.getKey(); + if (DEFAULT_TOP_LEVEL_TAGS.contains(key)) { + continue; + } + Object value = entry.getValue(); + if (value instanceof Number) { + writable.writeString(key, null); + writable.writeObject(value, null); } } @@ -356,10 +360,14 @@ public void accept(Metadata metadata) { writable.writeUTF8(HTTP_STATUS); writable.writeUTF8(metadata.getHttpStatusCode()); } - for (Map.Entry entry : tags.entrySet()) { + for (Map.Entry entry : metadata.getTags().entrySet()) { + String key = entry.getKey(); + if (DEFAULT_TOP_LEVEL_TAGS.contains(key)) { + continue; + } Object value = entry.getValue(); if (!(value instanceof Number)) { - writable.writeString(entry.getKey(), null); + writable.writeString(key, null); if (!(value instanceof Iterable)) { writable.writeObjectString(value, null); } else {