diff --git a/src/DDTrace/OpenTelemetry/Context.php b/src/DDTrace/OpenTelemetry/Context.php
index 44910695e9f..62cdaf938f5 100644
--- a/src/DDTrace/OpenTelemetry/Context.php
+++ b/src/DDTrace/OpenTelemetry/Context.php
@@ -58,8 +58,11 @@ public static function setStorage(ContextStorageInterface $storage): void
*/
public static function storage(): ContextStorageInterface
{
- /** @psalm-suppress RedundantPropertyInitializationCheck */
- return self::$storage ??= new ContextStorage();
+ if (class_exists('\OpenTelemetry\Context\FiberBoundContextStorageExecutionAwareBC')) {
+ return self::$storage ??= new FiberBoundContextStorageExecutionAwareBC();
+ } else {
+ return self::$storage ??= new ContextStorage();
+ }
}
/**
diff --git a/src/DDTrace/OpenTelemetry/Span.php b/src/DDTrace/OpenTelemetry/Span.php
index 52779a8775e..c60af54d37e 100644
--- a/src/DDTrace/OpenTelemetry/Span.php
+++ b/src/DDTrace/OpenTelemetry/Span.php
@@ -112,28 +112,18 @@ private function __construct(
foreach ($links as $link) {
/** @var LinkInterface $link */
$linkContext = $link->getSpanContext();
-
- $spanLink = new SpanLink();
- $spanLink->traceId = $linkContext->getTraceId();
- $spanLink->spanId = $linkContext->getSpanId();
- $spanLink->traceState = (string)$linkContext->getTraceState(); // __toString()
- $spanLink->attributes = $link->getAttributes()->toArray();
- $spanLink->droppedAttributesCount = 0; // Attributes limit aren't supported/meaningful in DD
-
- // Save the link
- ObjectKVStore::put($spanLink, "link", $link);
- $span->links[] = $spanLink;
+ $span->links[] = $this->createAndSaveSpanLink($linkContext, $link->getAttributes()->toArray(), $link);
}
foreach ($events as $event) {
/** @var EventInterface $event */
$spanEvent = new SpanEvent(
- $event->getName(),
+ $event->getName(),
$event->getAttributes()->toArray(),
$event->getEpochNanos()
);
-
+
// Save the event
ObjectKVStore::put($spanEvent, "event", $event);
$span->events[] = $spanEvent;
@@ -235,17 +225,24 @@ public function toSpanData(): SpanDataInterface
$this->updateSpanLinks();
$this->updateSpanEvents();
- return new ImmutableSpan(
- $this,
- $this->getName(),
- $this->links,
- $this->events,
- Attributes::create(array_merge($this->span->meta, $this->span->metrics)),
- 0,
- StatusData::create($this->status->getCode(), $this->status->getDescription()),
- $hasEnded ? $this->span->getStartTime() + $this->span->getDuration() : 0,
- $this->hasEnded()
- );
+ $spanData = [
+ 'span' => $this,
+ 'name' => $this->getName(),
+ 'links' => $this->links,
+ 'events' => $this->events,
+ 'attributes' => Attributes::create(array_merge($this->span->meta, $this->span->metrics)),
+ 'totalRecordedEvents' => $this->totalRecordedEvents,
+ 'status' => StatusData::create($this->status->getCode(), $this->status->getDescription()),
+ 'endEpochNanos' => $hasEnded ? $this->span->getStartTime() + $this->span->getDuration() : 0,
+ 'hasEnded' => $this->hasEnded(),
+ ];
+
+ // Workaround for a breaking change introduced in open-telemetry/api 1.1.0
+ if (in_array('addLink', get_class_methods(SpanInterface::class))) {
+ $spanData['totalRecordedLinks'] = $this->totalRecordedLinks;
+ }
+
+ return new ImmutableSpan(...$spanData);
}
/**
@@ -358,6 +355,19 @@ public function setAttributes(iterable $attributes): SpanInterface
return $this;
}
+ /**
+ * @inheritDoc
+ */
+ public function addLink(SpanContextInterface $context, iterable $attributes = []): SpanInterface
+ {
+ if ($this->hasEnded() || !$context->isValid()) {
+ return $this;
+ }
+
+ $this->span->links[] = $this->createAndSaveSpanLink($context, $attributes);
+ return $this;
+ }
+
/**
* @inheritDoc
*/
@@ -365,7 +375,7 @@ public function addEvent(string $name, iterable $attributes = [], int $timestamp
{
if (!$this->hasEnded()) {
$this->span->events[] = new SpanEvent(
- $name,
+ $name,
$attributes,
$timestamp ?? (int)(microtime(true) * 1e9)
);
@@ -522,7 +532,7 @@ private function updateSpanEvents()
{
$datadogSpanEvents = $this->span->events;
$this->span->meta["events"] = count($this->events);
-
+
$otel = [];
foreach ($datadogSpanEvents as $datadogSpanEvent) {
$exceptionAttributes = [];
@@ -539,7 +549,7 @@ private function updateSpanEvents()
$event = new Event(
$datadogSpanEvent->name,
(int)$datadogSpanEvent->timestamp,
- Attributes::create(array_merge($exceptionAttributes, \is_array($datadogSpanEvent->attributes) ? $datadogSpanEvent->attributes : iterator_to_array($datadogSpanEvent->attributes)))
+ Attributes::create(array_merge($exceptionAttributes, \is_array($datadogSpanEvent->attributes) ? $datadogSpanEvent->attributes : iterator_to_array($datadogSpanEvent->attributes)))
);
// Save the event
@@ -547,9 +557,27 @@ private function updateSpanEvents()
}
$otel[] = $event;
}
-
+
// Update the events
$this->events = $otel;
$this->totalRecordedEvents = count($otel);
}
+
+ private function createAndSaveSpanLink(SpanContextInterface $context, iterable $attributes = [], LinkInterface $link = null)
+ {
+ $spanLink = new SpanLink();
+ $spanLink->traceId = $context->getTraceId();
+ $spanLink->spanId = $context->getSpanId();
+ $spanLink->traceState = (string)$context->getTraceState(); // __toString()
+ $spanLink->attributes = $attributes;
+ $spanLink->droppedAttributesCount = 0; // Attributes limit aren't supported/meaningful in DD
+
+ // Save the link
+ if (is_null($link)) {
+ $link = new Link($context, Attributes::create($attributes));
+ }
+ ObjectKVStore::put($spanLink, "link", $link);
+
+ return $spanLink;
+ }
}
diff --git a/tests/OpenTelemetry/Integration/Context/FiberTest.php b/tests/OpenTelemetry/Integration/Context/Fiber/FiberTest.php
similarity index 75%
rename from tests/OpenTelemetry/Integration/Context/FiberTest.php
rename to tests/OpenTelemetry/Integration/Context/Fiber/FiberTest.php
index 55697a32fb1..cda6c178a47 100644
--- a/tests/OpenTelemetry/Integration/Context/FiberTest.php
+++ b/tests/OpenTelemetry/Integration/Context/Fiber/FiberTest.php
@@ -13,7 +13,7 @@
use OpenTelemetry\API\Trace\Span;
use OpenTelemetry\API\Trace\SpanKind;
use OpenTelemetry\Context\Context;
-use OpenTelemetry\Context\ContextStorage;
+use OpenTelemetry\Context\ExecutionContextAwareInterface;
use OpenTelemetry\SDK\Trace\TracerProvider;
use function DDTrace\close_span;
use function DDTrace\start_span;
@@ -31,78 +31,6 @@ public function ddSetUp(): void
public function ddTearDown()
{
parent::ddTearDown();
- Context::setStorage(new ContextStorage()); // Reset OpenTelemetry context
- }
-
- /**
- * @throws \Throwable
- */
- public function test_context_switching_ffi_observer()
- {
- $key = Context::createKey('-');
- $scope = Context::getCurrent()
- ->with($key, 'main')
- ->activate();
-
- $fiber = new Fiber(function () use ($key) {
- $scope = Context::getCurrent()
- ->with($key, 'fiber')
- ->activate();
-
- $this->assertSame('fiber:fiber', 'fiber:' . Context::getCurrent()->get($key));
-
- Fiber::suspend();
-
- $this->assertSame('fiber:fiber', 'fiber:' . Context::getCurrent()->get($key));
-
- $scope->detach();
- });
-
- $this->assertSame('main:main', 'main:' . Context::getCurrent()->get($key));
-
- $fiber->start();
-
- $this->assertSame('main:main', 'main:' . Context::getCurrent()->get($key));
-
- $fiber->resume();
-
- $this->assertSame('main:main', 'main:' . Context::getCurrent()->get($key));
-
- $scope->detach();
- }
-
- public function test_context_switching_ffi_observer_registered_on_startup()
- {
- $key = Context::createKey('-');
-
- $fiber = new Fiber(function () use ($key) {
- $scope = Context::getCurrent()
- ->with($key, 'fiber')
- ->activate();
-
- $this->assertSame('fiber:fiber', 'fiber:' . Context::getCurrent()->get($key));
-
- Fiber::suspend();
-
- $this->assertSame('fiber:fiber', 'fiber:' . Context::getCurrent()->get($key));
-
- $scope->detach();
- });
-
-
- $fiber->start();
-
- $this->assertSame('main:', 'main:' . Context::getCurrent()->get($key));
-
- $scope = Context::getCurrent()
- ->with($key, 'main')
- ->activate();
-
- $fiber->resume();
-
- $this->assertSame('main:main', 'main:' . Context::getCurrent()->get($key));
-
- $scope->detach();
}
public function testFiberInteroperabilityStackSwitch()
diff --git a/tests/OpenTelemetry/Integration/Context/Fiber/test_context_switching_ffi_observer.phpt b/tests/OpenTelemetry/Integration/Context/Fiber/test_context_switching_ffi_observer.phpt
new file mode 100644
index 00000000000..c86d01dd8f2
--- /dev/null
+++ b/tests/OpenTelemetry/Integration/Context/Fiber/test_context_switching_ffi_observer.phpt
@@ -0,0 +1,46 @@
+--TEST--
+Context switches on execution context switch.
+--SKIPIF--
+
+--ENV--
+OTEL_PHP_FIBERS_ENABLED=1
+--FILE--
+with($key, 'main')
+ ->activate();
+
+$fiber = new Fiber(function() use ($key) {
+ $scope = Context::getCurrent()
+ ->with($key, 'fiber')
+ ->activate();
+
+ echo 'fiber:' . Context::getCurrent()->get($key), PHP_EOL;
+
+ Fiber::suspend();
+ echo 'fiber:' . Context::getCurrent()->get($key), PHP_EOL;
+
+ $scope->detach();
+});
+
+echo 'main:' . Context::getCurrent()->get($key), PHP_EOL;
+
+$fiber->start();
+echo 'main:' . Context::getCurrent()->get($key), PHP_EOL;
+
+$fiber->resume();
+echo 'main:' . Context::getCurrent()->get($key), PHP_EOL;
+
+$scope->detach();
+?>
+--EXPECT--
+main:main
+fiber:fiber
+main:main
+fiber:fiber
+main:main
diff --git a/tests/OpenTelemetry/Integration/Context/Fiber/test_context_switching_ffi_observer_registered_on_startup.phpt b/tests/OpenTelemetry/Integration/Context/Fiber/test_context_switching_ffi_observer_registered_on_startup.phpt
new file mode 100644
index 00000000000..0d66a7e761d
--- /dev/null
+++ b/tests/OpenTelemetry/Integration/Context/Fiber/test_context_switching_ffi_observer_registered_on_startup.phpt
@@ -0,0 +1,45 @@
+--TEST--
+Fiber handler has to be loaded before fibers are used.
+--SKIPIF--
+
+--ENV--
+OTEL_PHP_FIBERS_ENABLED=1
+--FILE--
+with($key, 'fiber')
+ ->activate();
+
+ echo 'fiber:' . Context::getCurrent()->get($key), PHP_EOL;
+
+ Fiber::suspend();
+ echo 'fiber:' . Context::getCurrent()->get($key), PHP_EOL;
+
+ $scope->detach();
+});
+
+$fiber->start();
+echo 'main:' . Context::getCurrent()->get($key), PHP_EOL;
+
+$scope = Context::getCurrent()
+ ->with($key, 'main')
+ ->activate();
+
+$fiber->resume();
+echo 'main:' . Context::getCurrent()->get($key), PHP_EOL;
+
+$scope->detach();
+
+?>
+--EXPECT--
+fiber:fiber
+main:
+fiber:fiber
+main:main
diff --git a/tests/OpenTelemetry/Integration/SDK/SpanBuilderTest.php b/tests/OpenTelemetry/Integration/SDK/SpanBuilderTest.php
index b849ce7e45f..5d8a7406b71 100644
--- a/tests/OpenTelemetry/Integration/SDK/SpanBuilderTest.php
+++ b/tests/OpenTelemetry/Integration/SDK/SpanBuilderTest.php
@@ -109,6 +109,22 @@ public function test_add_link(): void
$this->assertCount(2, $span->toSpanData()->getLinks());
}
+ /**
+ * @group trace-compliance
+ */
+ public function test_add_link_after_span_creation(): void
+ {
+ /** @var Span $span */
+ $span = $this
+ ->tracer
+ ->spanBuilder(self::SPAN_NAME)
+ ->addLink($this->sampledSpanContext)
+ ->startSpan()
+ ->addLink($this->sampledSpanContext);
+
+ $this->assertCount(2, $span->toSpanData()->getLinks());
+ }
+
public function test_add_link_invalid(): void
{
/** @var Span $span */
diff --git a/tests/OpenTelemetry/composer.json b/tests/OpenTelemetry/composer.json
index 607a2888d48..17891594b80 100644
--- a/tests/OpenTelemetry/composer.json
+++ b/tests/OpenTelemetry/composer.json
@@ -1,8 +1,9 @@
{
"name": "datadog/dd-trace-tests",
"require": {
- "open-telemetry/sdk": "@stable",
- "open-telemetry/extension-propagator-b3": "@stable",
- "open-telemetry/opentelemetry-logger-monolog": "@stable"
- }
+ "open-telemetry/sdk": "@beta",
+ "open-telemetry/extension-propagator-b3": "@beta",
+ "open-telemetry/opentelemetry-logger-monolog": "@beta"
+ },
+ "minimum-stability": "beta"
}
diff --git a/tests/phpunit.xml b/tests/phpunit.xml
index fd3902de3a4..4e36afbfc62 100644
--- a/tests/phpunit.xml
+++ b/tests/phpunit.xml
@@ -74,6 +74,7 @@
./Integrations/Laravel/Octane
+ ./OpenTelemetry/Integration/Context/Fiber
./OpenTelemetry/Unit/API
./OpenTelemetry/Unit/Context
./OpenTelemetry/Unit/Propagation