diff --git a/src/Instrumentation/PDO/README.md b/src/Instrumentation/PDO/README.md index fddbc894b..a2d624e14 100644 --- a/src/Instrumentation/PDO/README.md +++ b/src/Instrumentation/PDO/README.md @@ -45,6 +45,11 @@ or environment variable: OTEL_PHP_INSTRUMENTATION_PDO_CONTEXT_PROPAGATION=true ``` +The context sources from global propagator by default, but it can be configured using the following environment variables: +```shell +OTEL_PHP_INSTRUMENTATION_PDO_CONTEXT_PROPAGATORS=tracecontext +``` + The modified query statement by default will not update `DbAttributes::DB_QUERY_TEXT` due to high cardinality risk, but it can be configured using the following configuration directive: ``` otel.instrumentation.pdo.context_propagation.attribute = true diff --git a/src/Instrumentation/PDO/src/ContextInfoPropagator.php b/src/Instrumentation/PDO/src/ContextInfoPropagator.php deleted file mode 100644 index 8dc832eed..000000000 --- a/src/Instrumentation/PDO/src/ContextInfoPropagator.php +++ /dev/null @@ -1,14 +0,0 @@ -buildPropagator($propagators[0]); + if ($propagator !== null && is_a($propagator, NoopTextMapPropagator::class)) { + return null; + } + + return $propagator; + default: + $props = $this->buildPropagators($propagators); + if ($props) { + return new MultiTextMapPropagator($props); + } + + return null; + } + } + + /** + * @return ?list + */ + private function buildPropagators(array $names): ?array + { + $propagators = []; + foreach ($names as $name) { + $propagator = $this->buildPropagator($name); + if ($propagator !== null && !is_a($propagator, NoopTextMapPropagator::class)) { + $propagators[] = $propagator; + } + } + if (count($propagators) === 0) { + return null; + } + + return $propagators; + } + + private function buildPropagator(string $name): ?TextMapPropagatorInterface + { + try { + return Registry::textMapPropagator($name); + } catch (\RuntimeException $e) { + self::logWarning($e->getMessage()); + } + + return null; + } +} diff --git a/src/Instrumentation/PDO/src/PDOInstrumentation.php b/src/Instrumentation/PDO/src/PDOInstrumentation.php index eb434285d..a7d97f6ba 100644 --- a/src/Instrumentation/PDO/src/PDOInstrumentation.php +++ b/src/Instrumentation/PDO/src/PDOInstrumentation.php @@ -33,6 +33,7 @@ public static function register(): void Version::VERSION_1_36_0->url(), ); $pdoTracker = new PDOTracker(); + $contextPropagator = (new ContextPropagatorFactory())->create(); // Hook for the new PDO::connect static method if (method_exists(PDO::class, 'connect')) { @@ -110,7 +111,7 @@ public static function register(): void hook( PDO::class, 'query', - pre: static function (PDO $pdo, array $params, string $class, string $function, ?string $filename, ?int $lineno) use ($pdoTracker, $instrumentation) { + pre: static function (PDO $pdo, array $params, string $class, string $function, ?string $filename, ?int $lineno) use ($contextPropagator, $pdoTracker, $instrumentation) { /** @psalm-suppress ArgumentTypeCoercion */ $builder = self::makeBuilder($instrumentation, 'PDO::query', $function, $class, $filename, $lineno) ->setSpanKind(SpanKind::KIND_CLIENT); @@ -135,8 +136,15 @@ public static function register(): void case 'postgresql': case 'mysql': $comments = []; - Globals::propagator()->inject($comments); - $sqlStatement = SqlCommentPropagator::inject($sqlStatement, $comments); + if ($contextPropagator !== null) { + // Propagator passed by user + $contextPropagator->inject($comments); + } else { + // fallback to global propagator if user didn't pass one + Globals::propagator()->inject($comments); + } + // Inject comments into SQL statement + $sqlStatement = SqlCommentInjector::inject($sqlStatement, $comments); if (ContextPropagation::isAttributeEnabled()) { $span->setAttributes([ DbAttributes::DB_QUERY_TEXT => $sqlStatement, @@ -163,7 +171,7 @@ public static function register(): void hook( PDO::class, 'exec', - pre: static function (PDO $pdo, array $params, string $class, string $function, ?string $filename, ?int $lineno) use ($pdoTracker, $instrumentation) { + pre: static function (PDO $pdo, array $params, string $class, string $function, ?string $filename, ?int $lineno) use ($contextPropagator, $pdoTracker, $instrumentation) { /** @psalm-suppress ArgumentTypeCoercion */ $builder = self::makeBuilder($instrumentation, 'PDO::exec', $function, $class, $filename, $lineno) ->setSpanKind(SpanKind::KIND_CLIENT); @@ -188,8 +196,15 @@ public static function register(): void case 'postgresql': case 'mysql': $comments = []; - Globals::propagator()->inject($comments); - $sqlStatement = SqlCommentPropagator::inject($sqlStatement, $comments); + if ($contextPropagator !== null) { + // Propagator passed by user + $contextPropagator->inject($comments); + } else { + // fallback to global propagator if user didn't pass one + Globals::propagator()->inject($comments); + } + // Inject comments into SQL statement + $sqlStatement = SqlCommentInjector::inject($sqlStatement, $comments); if (ContextPropagation::isAttributeEnabled()) { $span->setAttributes([ DbAttributes::DB_QUERY_TEXT => $sqlStatement, diff --git a/src/Instrumentation/PDO/src/SqlCommentPropagator.php b/src/Instrumentation/PDO/src/SqlCommentInjector.php similarity index 94% rename from src/Instrumentation/PDO/src/SqlCommentPropagator.php rename to src/Instrumentation/PDO/src/SqlCommentInjector.php index 622f40a73..a26d2b2aa 100644 --- a/src/Instrumentation/PDO/src/SqlCommentPropagator.php +++ b/src/Instrumentation/PDO/src/SqlCommentInjector.php @@ -6,7 +6,7 @@ use OpenTelemetry\SDK\Common\Configuration\Configuration; -class SqlCommentPropagator implements SqlPropagatorInterface +class SqlCommentInjector implements SqlInjectorInterface { public static function isPrepend(): bool { diff --git a/src/Instrumentation/PDO/src/SqlPropagatorInterface.php b/src/Instrumentation/PDO/src/SqlInjectorInterface.php similarity index 83% rename from src/Instrumentation/PDO/src/SqlPropagatorInterface.php rename to src/Instrumentation/PDO/src/SqlInjectorInterface.php index 1ced81d69..61ebbdde7 100644 --- a/src/Instrumentation/PDO/src/SqlPropagatorInterface.php +++ b/src/Instrumentation/PDO/src/SqlInjectorInterface.php @@ -4,7 +4,7 @@ namespace OpenTelemetry\Contrib\Instrumentation\PDO; -interface SqlPropagatorInterface +interface SqlInjectorInterface { public static function inject(string $query, array $comments): string; } diff --git a/src/Instrumentation/PDO/tests/Unit/ContextInfoPropagatorTest.php b/src/Instrumentation/PDO/tests/Unit/ContextInfoPropagatorTest.php deleted file mode 100644 index d7a91a760..000000000 --- a/src/Instrumentation/PDO/tests/Unit/ContextInfoPropagatorTest.php +++ /dev/null @@ -1,24 +0,0 @@ - 'value1', - 'key2' => 'value2', - 'key3' => 'value3', - ]; - $query = 'SELECT 1;'; - $result = ContextInfoPropagator::inject($query, $comments); - $this->assertEquals('SELECT 1;', $result); - } - -} diff --git a/src/Instrumentation/PDO/tests/Unit/ContextPropagatorFactoryTest.php b/src/Instrumentation/PDO/tests/Unit/ContextPropagatorFactoryTest.php new file mode 100644 index 000000000..930436c4e --- /dev/null +++ b/src/Instrumentation/PDO/tests/Unit/ContextPropagatorFactoryTest.php @@ -0,0 +1,52 @@ +create(); + if ($expected === null) { + $this->assertNull($propagator); + } else { + $this->assertInstanceOf($expected, $propagator); + } + putenv('OTEL_PHP_INSTRUMENTATION_PDO_CONTEXT_PROPAGATORS'); + } + + public static function propagatorsProvider(): array + { + return [ + [KnownValues::VALUE_BAGGAGE, BaggagePropagator::class], + [KnownValues::VALUE_TRACECONTEXT, TraceContextPropagator::class], + [KnownValues::VALUE_NONE, null], + [sprintf('%s,%s', KnownValues::VALUE_TRACECONTEXT, KnownValues::VALUE_BAGGAGE), MultiTextMapPropagator::class], + ['', null], + ['invalid', null], + ]; + } +} diff --git a/src/Instrumentation/PDO/tests/Unit/SqlCommentPropagatorTest.php b/src/Instrumentation/PDO/tests/Unit/SqlCommentInjectorTest.php similarity index 77% rename from src/Instrumentation/PDO/tests/Unit/SqlCommentPropagatorTest.php rename to src/Instrumentation/PDO/tests/Unit/SqlCommentInjectorTest.php index 27ee25b6e..dad020751 100644 --- a/src/Instrumentation/PDO/tests/Unit/SqlCommentPropagatorTest.php +++ b/src/Instrumentation/PDO/tests/Unit/SqlCommentInjectorTest.php @@ -4,22 +4,22 @@ namespace OpenTelemetry\Tests\Instrumentation\PDO\tests\Unit; -use OpenTelemetry\Contrib\Instrumentation\PDO\SqlCommentPropagator; +use OpenTelemetry\Contrib\Instrumentation\PDO\SqlCommentInjector; use PHPUnit\Framework\TestCase; -class SqlCommentPropagatorTest extends TestCase +class SqlCommentInjectorTest extends TestCase { public function testIsPrependReturnsTrue() { $_SERVER['OTEL_PHP_INSTRUMENTATION_PDO_SQL_COMMENTER_PREPEND'] = true; - $result = SqlCommentPropagator::isPrepend(); + $result = SqlCommentInjector::isPrepend(); $this->assertTrue($result); } public function testIsPrependReturnsFalse() { $_SERVER['OTEL_PHP_INSTRUMENTATION_PDO_SQL_COMMENTER_PREPEND'] = false; - $result = SqlCommentPropagator::isPrepend(); + $result = SqlCommentInjector::isPrepend(); $this->assertFalse($result); } @@ -32,7 +32,7 @@ public function testInjectPrepend() 'key3' => 'value3', ]; $query = 'SELECT 1;'; - $result = SqlCommentPropagator::inject($query, $comments); + $result = SqlCommentInjector::inject($query, $comments); $this->assertEquals("/*key1='value1',key2='value2',key3='value3'*/SELECT 1;", $result); } @@ -45,7 +45,7 @@ public function testInjectAppend() 'key3' => 'value3', ]; $query = 'SELECT 1;'; - $result = SqlCommentPropagator::inject($query, $comments); + $result = SqlCommentInjector::inject($query, $comments); $this->assertEquals("SELECT 1/*key1='value1',key2='value2',key3='value3'*/;", $result); } }