Skip to content

Commit 1dfd0db

Browse files
committed
feat: Partially handle on-the-fly addition of span links
1 parent daa99d1 commit 1dfd0db

File tree

2 files changed

+83
-0
lines changed

2 files changed

+83
-0
lines changed

src/DDTrace/OpenTelemetry/Span.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ public function toSpanData(): SpanDataInterface
198198
{
199199
$hasEnded = $this->hasEnded();
200200

201+
$this->updateSpanLinks();
202+
201203
return new ImmutableSpan(
202204
$this,
203205
$this->getName(),
@@ -436,4 +438,30 @@ public function getDDSpan(): SpanData
436438
{
437439
return $this->span;
438440
}
441+
442+
private function updateSpanLinks()
443+
{
444+
// Important: Links are assumed not to be removable to update the span links from Datadog to OpenTelemetry
445+
// For instance, if there are 4 links in Datadog and only 2 in OpenTelemetry, the 2 missing links will be added
446+
// to OpenTelemetry
447+
$datadogSpanLinks = $this->span->links;
448+
$otelSpanLinks = $this->links;
449+
450+
$datadogSpanLinksCount = count($datadogSpanLinks);
451+
$otelSpanLinksCount = count($otelSpanLinks);
452+
453+
for ($i = $otelSpanLinksCount; $i < $datadogSpanLinksCount; $i++) {
454+
$spanLink = $datadogSpanLinks[$i];
455+
456+
$linkSpanContext = API\SpanContext::create(
457+
$spanLink->traceId,
458+
$spanLink->spanId,
459+
API\TraceFlags::DEFAULT,
460+
new API\TraceState($spanLink->traceState ?? null),
461+
);
462+
463+
$this->links[] = new Link($linkSpanContext, Attributes::create($spanLink->attributes ?? []));
464+
$this->totalRecordedLinks++;
465+
}
466+
}
439467
}

tests/OpenTelemetry/Integration/InteroperabilityTest.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -975,4 +975,59 @@ public function testSpanLinksInteroperabilityFromOpenTelemetrySpan()
975975
$this->assertCount(1, $traces[0]);
976976
$this->assertSame("[{\"trace_id\":\"12345678876543211234567887654321\",\"span_id\":\"8765432112345678\",\"trace_state\":\"dd=t.dm:-0\",\"attributes\":{\"arg1\":\"value1\"},\"dropped_attributes_count\":0}]", $traces[0][0]['meta']['_dd.span_links']);
977977
}
978+
979+
public function testSpanLinksInteroperabilityBothTypes()
980+
{
981+
$sampledSpanContext = SpanContext::create(
982+
'12345678876543211234567887654321',
983+
'8765432112345678',
984+
TraceFlags::SAMPLED,
985+
new TraceState('dd=t.dm:-0')
986+
);
987+
988+
$traces = $this->isolateTracer(function () use ($sampledSpanContext) {
989+
// Add 1 span link using the OTel API
990+
$otelSpan = self::getTracer()->spanBuilder("otel.span")
991+
->addLink($sampledSpanContext, ['arg1' => 'value1'])
992+
->startSpan();
993+
994+
// Add 1 span link using the DD API
995+
$newSpanLink = new SpanLink();
996+
$newSpanLink->traceId = "ff0000000000051791e0000000000041";
997+
$newSpanLink->spanId = "ff00000000000517";
998+
active_span()->links[] = $newSpanLink;
999+
1000+
// Verify the span links from DD's POV
1001+
$datadogSpanLinks = active_span()->links;
1002+
$this->assertCount(2, $datadogSpanLinks);
1003+
1004+
$this->assertSame('12345678876543211234567887654321', $datadogSpanLinks[0]->traceId);
1005+
$this->assertSame('8765432112345678', $datadogSpanLinks[0]->spanId);
1006+
$this->assertSame('dd=t.dm:-0', $datadogSpanLinks[0]->traceState);
1007+
$this->assertSame(['arg1' => 'value1'], $datadogSpanLinks[0]->attributes);
1008+
$this->assertEquals(0, $datadogSpanLinks[0]->droppedAttributesCount);
1009+
1010+
$this->assertSame('ff0000000000051791e0000000000041', $datadogSpanLinks[1]->traceId);
1011+
$this->assertSame('ff00000000000517', $datadogSpanLinks[1]->spanId);
1012+
1013+
// Verify the span links from OTel's POV
1014+
$otelSpanLinks = $otelSpan->toSpanData()->getLinks();
1015+
1016+
$firstSpanLinkContext = $otelSpanLinks[0]->getSpanContext();
1017+
$this->assertSame('12345678876543211234567887654321', $firstSpanLinkContext->getTraceId());
1018+
$this->assertSame('8765432112345678', $firstSpanLinkContext->getSpanId());
1019+
$this->assertSame('dd=t.dm:-0', (string) $firstSpanLinkContext->getTraceState());
1020+
$this->assertSame(['arg1' => 'value1'], $otelSpanLinks[0]->getAttributes()->toArray());
1021+
1022+
$secondSpanLinkContext = $otelSpanLinks[1]->getSpanContext();
1023+
$this->assertSame('ff0000000000051791e0000000000041', $secondSpanLinkContext->getTraceId());
1024+
$this->assertSame('ff00000000000517', $secondSpanLinkContext->getSpanId());
1025+
1026+
1027+
$otelSpan->end();
1028+
});
1029+
1030+
$this->assertCount(1, $traces[0]);
1031+
$this->assertSame("[{\"trace_id\":\"12345678876543211234567887654321\",\"span_id\":\"8765432112345678\",\"trace_state\":\"dd=t.dm:-0\",\"attributes\":{\"arg1\":\"value1\"},\"dropped_attributes_count\":0},{\"trace_id\":\"ff0000000000051791e0000000000041\",\"span_id\":\"ff00000000000517\"}]", $traces[0][0]['meta']['_dd.span_links']);
1032+
}
9781033
}

0 commit comments

Comments
 (0)