Skip to content

Commit 1bd19c0

Browse files
committed
Extend test to check link tracestate, traceflags, and span origin
1 parent 9582175 commit 1bd19c0

File tree

1 file changed

+118
-9
lines changed

1 file changed

+118
-9
lines changed

dd-trace-core/src/test/java/datadog/trace/core/otlp/trace/OtlpTraceProtoTest.java

Lines changed: 118 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import com.google.protobuf.CodedInputStream;
1818
import com.google.protobuf.WireFormat;
1919
import datadog.trace.api.DD128bTraceId;
20+
import datadog.trace.api.DDTraceId;
2021
import datadog.trace.api.TracePropagationStyle;
2122
import datadog.trace.api.sampling.PrioritySampling;
2223
import datadog.trace.api.sampling.SamplingMechanism;
@@ -173,20 +174,43 @@ SpanSpec use128BitTraceId() {
173174
this.use128BitTraceId = true;
174175
return this;
175176
}
177+
178+
/**
179+
* Sets the origin propagated via an {@link ExtractedContext} parent so that {@code
180+
* metadata.getOrigin()} is non-null and the {@code _dd.origin} attribute is written.
181+
*/
182+
SpanSpec origin(String origin) {
183+
this.origin = origin;
184+
return this;
185+
}
186+
187+
/** Trace origin carried in the extracted parent context; {@code null} = no origin. */
188+
String origin;
176189
}
177190

178-
/** Descriptor for a single span link: the index of the target span and optional attributes. */
191+
/**
192+
* Descriptor for a single span link: target span index, optional attributes, tracestate, and
193+
* flags.
194+
*/
179195
static final class LinkSpec {
180196
final int targetIndex;
181197
final SpanAttributes attributes;
198+
final String traceState;
199+
final byte traceFlags;
182200

183201
LinkSpec(int targetIndex) {
184-
this(targetIndex, SpanAttributes.EMPTY);
202+
this(targetIndex, SpanAttributes.EMPTY, "", SpanLink.DEFAULT_FLAGS);
185203
}
186204

187205
LinkSpec(int targetIndex, SpanAttributes attributes) {
206+
this(targetIndex, attributes, "", SpanLink.DEFAULT_FLAGS);
207+
}
208+
209+
LinkSpec(int targetIndex, SpanAttributes attributes, String traceState, byte traceFlags) {
188210
this.targetIndex = targetIndex;
189211
this.attributes = attributes;
212+
this.traceState = traceState;
213+
this.traceFlags = traceFlags;
190214
}
191215
}
192216

@@ -329,6 +353,10 @@ private static SpanSpec linkedSpan(String resourceName, int... targetIndices) {
329353
links);
330354
}
331355

356+
/**
357+
* A span with one {@link SpanLink} pointing to the span at {@code targetIndex}, carrying the
358+
* given {@link SpanAttributes}.
359+
*/
332360
/**
333361
* A span with one {@link SpanLink} pointing to the span at {@code targetIndex}, carrying the
334362
* given {@link SpanAttributes}.
@@ -351,6 +379,44 @@ private static SpanSpec linkedSpanWithAttrs(
351379
new LinkSpec(targetIndex, attributes));
352380
}
353381

382+
/** A span with one {@link SpanLink} carrying the given W3C tracestate string. */
383+
private static SpanSpec linkedSpanWithTracestate(
384+
String resourceName, int targetIndex, String traceState) {
385+
return new SpanSpec(
386+
resourceName,
387+
"op.linked",
388+
"web",
389+
null,
390+
BASE_MICROS,
391+
BASE_MICROS + DURATION_MICROS,
392+
false,
393+
null,
394+
0,
395+
null,
396+
new HashMap<>(),
397+
-1,
398+
new LinkSpec(targetIndex, SpanAttributes.EMPTY, traceState, SpanLink.DEFAULT_FLAGS));
399+
}
400+
401+
/** A span with one {@link SpanLink} carrying the given trace flags. */
402+
private static SpanSpec linkedSpanWithFlags(
403+
String resourceName, int targetIndex, byte traceFlags) {
404+
return new SpanSpec(
405+
resourceName,
406+
"op.linked",
407+
"web",
408+
null,
409+
BASE_MICROS,
410+
BASE_MICROS + DURATION_MICROS,
411+
false,
412+
null,
413+
0,
414+
null,
415+
new HashMap<>(),
416+
-1,
417+
new LinkSpec(targetIndex, SpanAttributes.EMPTY, "", traceFlags));
418+
}
419+
354420
private static Map<String, Object> tags(Object... keyValues) {
355421
Map<String, Object> map = new HashMap<>();
356422
for (int i = 0; i < keyValues.length; i += 2) {
@@ -441,6 +507,16 @@ static Stream<Arguments> cases() {
441507
"attr.linked",
442508
0,
443509
SpanAttributes.builder().put("link.source", "test").build()))),
510+
Arguments.of(
511+
"span link with tracestate — Link.trace_state field written",
512+
asList(
513+
span("anchor.op", "anchor.op", "web"),
514+
linkedSpanWithTracestate("tracestate.linked", 0, "vendor=abc;p=123"))),
515+
Arguments.of(
516+
"span link with non-default flags — extra flag bit preserved alongside SAMPLED",
517+
asList(
518+
span("anchor.op", "anchor.op", "web"),
519+
linkedSpanWithFlags("flags.linked", 0, (byte) 0x02))),
444520

445521
// ── metadata paths ────────────────────────────────────────────────────
446522
Arguments.of(
@@ -449,6 +525,9 @@ static Stream<Arguments> cases() {
449525
Arguments.of(
450526
"span with http status code — http.status_code written via setHttpStatusCode",
451527
asList(span("GET /resource", "servlet.request", "web").httpStatusCode(404))),
528+
Arguments.of(
529+
"span with origin — _dd.origin attribute written",
530+
asList(span("GET /api", "servlet.request", "web").origin("rum"))),
452531
Arguments.of(
453532
"span with 128-bit trace ID — high-order trace_id bytes non-zero",
454533
asList(span("GET /api", "servlet.request", "web").use128BitTraceId())),
@@ -560,6 +639,17 @@ private static List<DDSpan> buildSpans(List<SpanSpec> specs) {
560639
PropagationTags.factory().empty(),
561640
TracePropagationStyle.DATADOG);
562641
agentSpan = TRACER.startSpan("test", spec.operationName, parent128, spec.startMicros);
642+
} else if (spec.origin != null) {
643+
ExtractedContext parentWithOrigin =
644+
new ExtractedContext(
645+
DDTraceId.from(1L),
646+
0L,
647+
PrioritySampling.UNSET,
648+
spec.origin,
649+
PropagationTags.factory().empty(),
650+
TracePropagationStyle.DATADOG);
651+
agentSpan =
652+
TRACER.startSpan("test", spec.operationName, parentWithOrigin, spec.startMicros);
563653
} else if (spec.parentIndex >= 0) {
564654
agentSpan =
565655
TRACER.startSpan(
@@ -606,13 +696,11 @@ private static List<DDSpan> buildSpans(List<SpanSpec> specs) {
606696

607697
for (LinkSpec link : spec.links) {
608698
agentSpan.addLink(
609-
link.attributes.isEmpty()
610-
? SpanLink.from(spans.get(link.targetIndex).context())
611-
: SpanLink.from(
612-
spans.get(link.targetIndex).context(),
613-
SpanLink.DEFAULT_FLAGS,
614-
"",
615-
link.attributes));
699+
SpanLink.from(
700+
spans.get(link.targetIndex).context(),
701+
link.traceFlags,
702+
link.traceState,
703+
link.attributes));
616704
}
617705

618706
agentSpan.finish(spec.finishMicros);
@@ -859,6 +947,11 @@ private static void verifySpan(CodedInputStream sp, DDSpan span, SpanSpec spec,
859947
+ caseName
860948
+ "]");
861949
}
950+
if (spec.origin != null) {
951+
assertTrue(
952+
attrKeys.contains("_dd.origin"),
953+
"attributes must include '_dd.origin' when origin is set [" + caseName + "]");
954+
}
862955

863956
// ── status (field 15) ─────────────────────────────────────────────────────
864957
if (spec.error) {
@@ -892,7 +985,9 @@ private static void verifyLink(CodedInputStream link, LinkSpec linkSpec, String
892985
throws IOException {
893986
byte[] traceId = null;
894987
byte[] spanId = null;
988+
String parsedTraceState = null;
895989
Set<String> linkAttrKeys = new HashSet<>();
990+
int parsedFlags = 0;
896991
while (!link.isAtEnd()) {
897992
int tag = link.readTag();
898993
switch (WireFormat.getTagFieldNumber(tag)) {
@@ -902,9 +997,15 @@ private static void verifyLink(CodedInputStream link, LinkSpec linkSpec, String
902997
case 2:
903998
spanId = link.readBytes().toByteArray();
904999
break;
1000+
case 3:
1001+
parsedTraceState = link.readString();
1002+
break;
9051003
case 4:
9061004
linkAttrKeys.add(readKeyValueKey(link.readBytes().newCodedInput()));
9071005
break;
1006+
case 6:
1007+
parsedFlags = link.readFixed32();
1008+
break;
9081009
default:
9091010
link.skipField(tag);
9101011
}
@@ -913,6 +1014,14 @@ private static void verifyLink(CodedInputStream link, LinkSpec linkSpec, String
9131014
assertEquals(16, traceId.length, "Link.trace_id must be 16 bytes [" + caseName + "]");
9141015
assertNotNull(spanId, "Link.span_id must be present [" + caseName + "]");
9151016
assertEquals(8, spanId.length, "Link.span_id must be 8 bytes [" + caseName + "]");
1017+
if (!linkSpec.traceState.isEmpty()) {
1018+
assertEquals(
1019+
linkSpec.traceState, parsedTraceState, "Link.trace_state mismatch [" + caseName + "]");
1020+
}
1021+
// SpanLink.from() ORs in the SAMPLED_FLAG (0x01) when the target context has positive
1022+
// sampling priority, which all test anchor spans have via the default tracer sampler.
1023+
int expectedFlags = Byte.toUnsignedInt((byte) (linkSpec.traceFlags | 0x01));
1024+
assertEquals(expectedFlags, parsedFlags, "Link.flags mismatch [" + caseName + "]");
9161025
for (String expectedKey : linkSpec.attributes.asMap().keySet()) {
9171026
assertTrue(
9181027
linkAttrKeys.contains(expectedKey),

0 commit comments

Comments
 (0)