Skip to content

Commit 0a52bb3

Browse files
committed
fix: maintain correct hierarchy and add a test
1 parent 7bae9f9 commit 0a52bb3

File tree

2 files changed

+78
-4
lines changed

2 files changed

+78
-4
lines changed

src/Instrumentation/PDO/src/PDOInstrumentation.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,9 +263,10 @@ public static function register(): void
263263
if ($spanContext = $pdoTracker->getSpanForPreparedStatement($statement)) {
264264
$builder->addLink($spanContext);
265265
}
266+
$parent = Context::getCurrent();
266267
$span = $builder->startSpan();
267268

268-
Context::storage()->attach($span->storeInContext(Context::getCurrent()));
269+
Context::storage()->attach($span->storeInContext($parent));
269270
},
270271
post: static function (PDOStatement $statement, array $params, mixed $retval, ?Throwable $exception) {
271272
self::end($exception);
@@ -290,9 +291,9 @@ public static function register(): void
290291
if ($spanContext = $pdoTracker->getSpanForPreparedStatement($statement)) {
291292
$builder->addLink($spanContext);
292293
}
294+
$parent = Context::getCurrent();
293295
$span = $builder->startSpan();
294-
295-
Context::storage()->attach($span->storeInContext(Context::getCurrent()));
296+
Context::storage()->attach($span->storeInContext($parent));
296297
},
297298
post: static function (PDOStatement $statement, array $params, mixed $retval, ?Throwable $exception) {
298299
self::end($exception);

src/Instrumentation/PDO/tests/Integration/PDOInstrumentationTest.php

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
use ArrayObject;
88
use OpenTelemetry\API\Instrumentation\Configurator;
9+
use OpenTelemetry\API\Trace\SpanInterface;
10+
use OpenTelemetry\API\Trace\SpanKind;
11+
use OpenTelemetry\Context\Context;
912
use OpenTelemetry\Context\ScopeInterface;
1013
use OpenTelemetry\SDK\Trace\ImmutableSpan;
1114
use OpenTelemetry\SDK\Trace\SpanExporter\InMemoryExporter;
@@ -18,7 +21,7 @@
1821
class PDOInstrumentationTest extends TestCase
1922
{
2023
private ScopeInterface $scope;
21-
/** @var ArrayObject<int, ImmutableSpan> */
24+
/** @var ArrayObject<array-key, mixed> */
2225
private ArrayObject $storage;
2326

2427
private function createDB(): PDO
@@ -269,4 +272,74 @@ public function test_encode_db_statement_as_utf8(): void
269272
$this->assertTrue(mb_check_encoding($span_db_exec->getAttributes()->get(TraceAttributes::DB_QUERY_TEXT), 'UTF-8'));
270273
$this->assertCount(5, $this->storage);
271274
}
275+
276+
public function test_span_hierarchy_with_pdo_operations(): void
277+
{
278+
$this->assertCount(0, $this->storage);
279+
280+
// Create a server span
281+
$tracerProvider = new TracerProvider(
282+
new SimpleSpanProcessor(
283+
new InMemoryExporter($this->storage)
284+
)
285+
);
286+
$tracer = $tracerProvider->getTracer('test');
287+
/** @var SpanInterface $serverSpan */
288+
$serverSpan = $tracer->spanBuilder('HTTP GET /api/users')
289+
->setSpanKind(SpanKind::KIND_SERVER)
290+
->startSpan();
291+
292+
// Create scope for server span
293+
$serverScope = Context::storage()->attach($serverSpan->storeInContext(Context::getCurrent()));
294+
295+
// Create an internal span (simulating business logic)
296+
/** @var SpanInterface $internalSpan */
297+
$internalSpan = $tracer->spanBuilder('processUserData')
298+
->setSpanKind(SpanKind::KIND_INTERNAL)
299+
->startSpan();
300+
301+
// Create scope for internal span
302+
$internalScope = Context::storage()->attach($internalSpan->storeInContext(Context::getCurrent()));
303+
304+
// Perform PDO operations within the internal span context
305+
$db = self::createDB();
306+
$this->assertCount(1, $this->storage); // PDO constructor span
307+
308+
// Create and populate test table
309+
$db->exec($this->fillDB());
310+
$this->assertCount(2, $this->storage); // PDO exec span
311+
312+
// Query data
313+
$stmt = $db->prepare('SELECT * FROM technology WHERE name = ?');
314+
$this->assertCount(3, $this->storage); // PDO prepare span
315+
316+
$stmt->execute(['PHP']);
317+
$this->assertCount(4, $this->storage); // PDOStatement execute span
318+
319+
$result = $stmt->fetchAll();
320+
$this->assertCount(5, $this->storage); // PDOStatement fetchAll span
321+
322+
// Verify span hierarchy
323+
/** @var ImmutableSpan $pdoSpan */
324+
$pdoSpan = $this->storage->offsetGet(0);
325+
/** @var ImmutableSpan $execSpan */
326+
$execSpan = $this->storage->offsetGet(1);
327+
/** @var ImmutableSpan $prepareSpan */
328+
$prepareSpan = $this->storage->offsetGet(2);
329+
/** @var ImmutableSpan $executeSpan */
330+
$executeSpan = $this->storage->offsetGet(3);
331+
/** @var ImmutableSpan $fetchAllSpan */
332+
$fetchAllSpan = $this->storage->offsetGet(4);
333+
334+
// All PDO spans should be children of the internal span
335+
$this->assertEquals($internalSpan->getContext()->getSpanId(), $pdoSpan->getParentSpanId());
336+
$this->assertEquals($internalSpan->getContext()->getSpanId(), $execSpan->getParentSpanId());
337+
$this->assertEquals($internalSpan->getContext()->getSpanId(), $prepareSpan->getParentSpanId());
338+
$this->assertEquals($internalSpan->getContext()->getSpanId(), $executeSpan->getParentSpanId());
339+
$this->assertEquals($internalSpan->getContext()->getSpanId(), $fetchAllSpan->getParentSpanId());
340+
341+
// Detach scopes
342+
$internalScope->detach();
343+
$serverScope->detach();
344+
}
272345
}

0 commit comments

Comments
 (0)