From 91f483d875dc8bae23315e3d4b7fb3fa8e77ecc6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 19 May 2025 01:18:06 +0000 Subject: [PATCH 1/7] chore: update dependabot configuration [skip ci] --- .github/dependabot.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b73a41309..dedd628f6 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -88,6 +88,7 @@ updates: - "/src/Instrumentation/Yii" - "/src/Logs/Monolog" - "/src/MetaPackages/opentelemetry" + - "/src/Propagation/Instana" - "/src/Propagation/ServerTiming" - "/src/Propagation/TraceResponse" - "/src/ResourceDetectors/Azure" From 62718ec581bdf42e335ef3325d175cbd4e4c38a6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 6 Jul 2025 01:24:17 +0000 Subject: [PATCH 2/7] chore: update dependabot configuration [skip ci] --- .github/dependabot.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 9b6161135..d46c3f056 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -62,6 +62,8 @@ updates: - "/src/AutoInstrumentationInstaller" - "/src/Aws" - "/src/Context/Swoole" + - "/src/Exporter/Instana" + - "/src/Instrumentation/AwsSdk" - "/src/Instrumentation/CakePHP" - "/src/Instrumentation/CodeIgniter" - "/src/Instrumentation/Curl" From 2f9b7712b8f4d9d0077b3f337fea4ffafeff8a84 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 22 Jul 2025 01:21:31 +0000 Subject: [PATCH 3/7] chore: update dependabot configuration [skip ci] --- .github/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d46c3f056..ffc764237 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -57,10 +57,10 @@ updates: # Maintain dependencies for all packages - package-ecosystem: "composer" directories: - - "/examples/aws/AwsClientApp" - "/examples/instrumentation/Wordpress" - "/src/AutoInstrumentationInstaller" - "/src/Aws" + - "/src/Aws/examples/AwsClientApp" - "/src/Context/Swoole" - "/src/Exporter/Instana" - "/src/Instrumentation/AwsSdk" From da0bc1b912fa8c91b60810ac48021998641d95a8 Mon Sep 17 00:00:00 2001 From: Weslen Teche Date: Sun, 3 Aug 2025 11:04:57 -0300 Subject: [PATCH 4/7] migrate cloudtrace propagator --- .github/dependabot.yml | 1 + .github/workflows/php.yml | 1 + .gitsplit.yml | 2 + composer.json | 3 + src/Propagation/CloudTrace/.gitattributes | 12 ++ src/Propagation/CloudTrace/.gitignore | 1 + src/Propagation/CloudTrace/.php-cs-fixer.php | 43 ++++ src/Propagation/CloudTrace/README.md | 35 +++ src/Propagation/CloudTrace/_register.php | 20 ++ src/Propagation/CloudTrace/composer.json | 39 ++++ src/Propagation/CloudTrace/phpstan.neon.dist | 9 + src/Propagation/CloudTrace/phpunit.xml.dist | 44 ++++ src/Propagation/CloudTrace/psalm.xml.dist | 15 ++ .../CloudTrace/src/CloudTraceFormatter.php | 57 +++++ .../CloudTrace/src/CloudTracePropagator.php | 99 +++++++++ src/Propagation/CloudTrace/src/Utils.php | 122 +++++++++++ .../tests/Unit/CloudTraceFormatterTest.php | 51 +++++ .../Unit/CloudTracePropagatorOneWayTest.php | 177 +++++++++++++++ .../tests/Unit/CloudTracePropagatorTest.php | 201 ++++++++++++++++++ .../CloudTrace/tests/Unit/UtilsTest.php | 123 +++++++++++ 20 files changed, 1055 insertions(+) create mode 100644 src/Propagation/CloudTrace/.gitattributes create mode 100644 src/Propagation/CloudTrace/.gitignore create mode 100644 src/Propagation/CloudTrace/.php-cs-fixer.php create mode 100644 src/Propagation/CloudTrace/README.md create mode 100644 src/Propagation/CloudTrace/_register.php create mode 100644 src/Propagation/CloudTrace/composer.json create mode 100644 src/Propagation/CloudTrace/phpstan.neon.dist create mode 100644 src/Propagation/CloudTrace/phpunit.xml.dist create mode 100644 src/Propagation/CloudTrace/psalm.xml.dist create mode 100644 src/Propagation/CloudTrace/src/CloudTraceFormatter.php create mode 100644 src/Propagation/CloudTrace/src/CloudTracePropagator.php create mode 100644 src/Propagation/CloudTrace/src/Utils.php create mode 100644 src/Propagation/CloudTrace/tests/Unit/CloudTraceFormatterTest.php create mode 100644 src/Propagation/CloudTrace/tests/Unit/CloudTracePropagatorOneWayTest.php create mode 100644 src/Propagation/CloudTrace/tests/Unit/CloudTracePropagatorTest.php create mode 100644 src/Propagation/CloudTrace/tests/Unit/UtilsTest.php diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c9a6bc9d5..77571d99e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -91,6 +91,7 @@ updates: - "/src/Instrumentation/Yii" - "/src/Logs/Monolog" - "/src/MetaPackages/opentelemetry" + - "/src/Propagation/CloudTrace" - "/src/Propagation/Instana" - "/src/Propagation/ServerTiming" - "/src/Propagation/TraceResponse" diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 022d20add..e833c354f 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -54,6 +54,7 @@ jobs: 'Instrumentation/Symfony', 'Instrumentation/Yii', 'Logs/Monolog', + 'Propagation/CloudTrace', 'Propagation/Instana', 'Propagation/ServerTiming', 'Propagation/TraceResponse', diff --git a/.gitsplit.yml b/.gitsplit.yml index 66cac9025..96eb94827 100644 --- a/.gitsplit.yml +++ b/.gitsplit.yml @@ -72,6 +72,8 @@ splits: target: "https://${GH_TOKEN}@github.com/opentelemetry-php/contrib-logger-monolog.git" - prefix: "src/MetaPackages/opentelemetry" target: "https://${GH_TOKEN}@github.com/opentelemetry-php/opentelemetry-meta.git" + - prefix: "src/Propagation/CloudTrace" + target: "https://${GH_TOKEN}@github.com/opentelemetry-php/extension-propagator-cloudtrace.git" - prefix: "src/Propagation/Instana" target: "https://${GH_TOKEN}@github.com/opentelemetry-php/contrib-propagator-instana.git" - prefix: "src/Propagation/ServerTiming" diff --git a/composer.json b/composer.json index 56945bd4b..8560e0b75 100644 --- a/composer.json +++ b/composer.json @@ -54,6 +54,7 @@ "OpenTelemetry\\Contrib\\Sampler\\RuleBased\\": "src/Sampler/RuleBased/src", "OpenTelemetry\\Contrib\\Shim\\OpenTracing\\": "src/Shims/OpenTracing/src", "OpenTelemetry\\Contrib\\Symfony\\": "src/Symfony/src", + "OpenTelemetry\\Extension\\Propagator\\CloudTrace\\": "src/Propagation/CloudTrace/src", "OpenTelemetry\\TestUtils\\": "src/Utils/Test/src" }, "files": [ @@ -84,6 +85,7 @@ "src/Instrumentation/Symfony/_register.php", "src/Instrumentation/Wordpress/_register.php", "src/Instrumentation/Yii/_register.php", + "src/Propagation/CloudTrace/_register.php", "src/Propagation/Instana/_register.php", "src/ResourceDetectors/Azure/_register.php", "src/ResourceDetectors/Container/_register.php", @@ -131,6 +133,7 @@ "open-telemetry/detector-azure": "self.version", "open-telemetry/detector-container": "self.version", "open-telemetry/detector-digitalocean": "self.version", + "open-telemetry/extension-propagator-cloudtrace": "self.version", "open-telemetry/opentelemetry-auto-aws-sdk": "self.version", "open-telemetry/opentelemetry-auto-cakephp": "self.version", "open-telemetry/opentelemetry-auto-codeigniter": "self.version", diff --git a/src/Propagation/CloudTrace/.gitattributes b/src/Propagation/CloudTrace/.gitattributes new file mode 100644 index 000000000..1676cf825 --- /dev/null +++ b/src/Propagation/CloudTrace/.gitattributes @@ -0,0 +1,12 @@ +* text=auto + +*.md diff=markdown +*.php diff=php + +/.gitattributes export-ignore +/.gitignore export-ignore +/.php-cs-fixer.php export-ignore +/phpstan.neon.dist export-ignore +/phpunit.xml.dist export-ignore +/psalm.xml.dist export-ignore +/tests export-ignore diff --git a/src/Propagation/CloudTrace/.gitignore b/src/Propagation/CloudTrace/.gitignore new file mode 100644 index 000000000..57872d0f1 --- /dev/null +++ b/src/Propagation/CloudTrace/.gitignore @@ -0,0 +1 @@ +/vendor/ diff --git a/src/Propagation/CloudTrace/.php-cs-fixer.php b/src/Propagation/CloudTrace/.php-cs-fixer.php new file mode 100644 index 000000000..e35fa078c --- /dev/null +++ b/src/Propagation/CloudTrace/.php-cs-fixer.php @@ -0,0 +1,43 @@ +exclude('vendor') + ->exclude('var/cache') + ->in(__DIR__); + +$config = new PhpCsFixer\Config(); +return $config->setRules([ + 'concat_space' => ['spacing' => 'one'], + 'declare_equal_normalize' => ['space' => 'none'], + 'is_null' => true, + 'modernize_types_casting' => true, + 'ordered_imports' => true, + 'php_unit_construct' => true, + 'single_line_comment_style' => true, + 'yoda_style' => false, + '@PSR2' => true, + 'array_syntax' => ['syntax' => 'short'], + 'blank_line_after_opening_tag' => true, + 'blank_line_before_statement' => true, + 'cast_spaces' => true, + 'declare_strict_types' => true, + 'type_declaration_spaces' => true, + 'include' => true, + 'lowercase_cast' => true, + 'new_with_parentheses' => true, + 'no_extra_blank_lines' => true, + 'no_leading_import_slash' => true, + 'echo_tag_syntax' => true, + 'no_unused_imports' => true, + 'no_useless_else' => true, + 'no_useless_return' => true, + 'phpdoc_order' => true, + 'phpdoc_scalar' => true, + 'phpdoc_types' => true, + 'short_scalar_cast' => true, + 'blank_lines_before_namespace' => true, + 'single_quote' => true, + 'trailing_comma_in_multiline' => true, + ]) + ->setRiskyAllowed(true) + ->setFinder($finder); + diff --git a/src/Propagation/CloudTrace/README.md b/src/Propagation/CloudTrace/README.md new file mode 100644 index 000000000..d045cd997 --- /dev/null +++ b/src/Propagation/CloudTrace/README.md @@ -0,0 +1,35 @@ +[![Releases](https://img.shields.io/badge/releases-purple)](https://github.com/opentelemetry-php/extension-propagator-cloudtrace/releases) +[![Issues](https://img.shields.io/badge/issues-pink)](https://github.com/open-telemetry/opentelemetry-php/issues) +[![Source](https://img.shields.io/badge/source-contrib-green)](https://github.com/open-telemetry/opentelemetry-php-contrib/tree/main/src/Propagation/CloudTrace) +[![Mirror](https://img.shields.io/badge/mirror-opentelemetry--php--contrib-blue)](https://github.com/opentelemetry-php/extension-propagator-cloudtrace) +[![Latest Version](http://poser.pugx.org/open-telemetry/opentelemetry-propagation-instana/v/unstable)](https://packagist.org/packages/open-telemetry/extension-propagator-cloudtrace/) +[![Stable](http://poser.pugx.org/open-telemetry/opentelemetry-propagation-instana/v/stable)](https://packagist.org/packages/open-telemetry/extension-propagator-cloudtrace/) + +This is a read-only subtree split of https://github.com/open-telemetry/opentelemetry-php-contrib. + +# OpenTelemetry CloudTrace Propagator + +CloudTrace is a propagator that supports the specification for the header "x-cloud-trace-context" used for trace context propagation across +service boundaries. (https://cloud.google.com/trace/docs/setup#force-trace). OpenTelemetry PHP CloudTrace Propagator Extension provides +option to use it bi-directionally or one-way. One-way does not inject the header for downstream consumption, it only processes the incoming headers +and returns the correct span context. It only attaches to existing X-Cloud-Trace-Context traces and does not create downstream ones. + +## Installation + +```sh +composer require open-telemetry/extension-propagator-cloudtrace +``` + +## Usage + +For one-way CloudTrace: + +``` +$propagator = CloudTracePropagator::getOneWayInstance(); +``` + +For bi-directional CloudTrace: + +``` +$propagator = CloudTracePropagator::getInstance(); +``` diff --git a/src/Propagation/CloudTrace/_register.php b/src/Propagation/CloudTrace/_register.php new file mode 100644 index 000000000..5fcac7243 --- /dev/null +++ b/src/Propagation/CloudTrace/_register.php @@ -0,0 +1,20 @@ + + + + + + + src + + + + + + + + + + + + + tests/Unit + + + + diff --git a/src/Propagation/CloudTrace/psalm.xml.dist b/src/Propagation/CloudTrace/psalm.xml.dist new file mode 100644 index 000000000..155711712 --- /dev/null +++ b/src/Propagation/CloudTrace/psalm.xml.dist @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/src/Propagation/CloudTrace/src/CloudTraceFormatter.php b/src/Propagation/CloudTrace/src/CloudTraceFormatter.php new file mode 100644 index 000000000..f64d32e7d --- /dev/null +++ b/src/Propagation/CloudTrace/src/CloudTraceFormatter.php @@ -0,0 +1,57 @@ +[/][;o=]`. + * The options are a bitmask of options. Currently the only option is the + * least significant bit which signals whether the request was traced or not + * (1 = traced, 0 = not traced). + */ +final class CloudTraceFormatter +{ + const CONTEXT_HEADER_FORMAT = '/([0-9a-fA-F]{32})(?:\/(\d+))?(?:;o=(\d+))?/'; + + /** + * Generate a SpanContext object from the Trace Context header + */ + public static function deserialize(string $header) : SpanContextInterface + { + $matched = preg_match(self::CONTEXT_HEADER_FORMAT, $header, $matches); + + if (!$matched) { + return SpanContext::getInvalid(); + } + if (!array_key_exists(2, $matches) || empty($matches[2])) { + return SpanContext::getInvalid(); + } + if (!array_key_exists(3, $matches)) { + return SpanContext::getInvalid(); + } + + return SpanContext::createFromRemoteParent( + strtolower($matches[1]), + Utils::leftZeroPad(Utils::decToHex($matches[2])), + (int) ($matches[3] === '1') + ); + } + + /** + * Convert a SpanContextInterface to header string + */ + public static function serialize(SpanContextInterface $context) : string + { + $ret = $context->getTraceId(); + if ($context->getSpanId()) { + $ret .= '/' . Utils::hexToDec($context->getSpanId()); + } + + return $ret . (';o=' . ($context->isSampled() ? '1' : '0')); + } +} diff --git a/src/Propagation/CloudTrace/src/CloudTracePropagator.php b/src/Propagation/CloudTrace/src/CloudTracePropagator.php new file mode 100644 index 000000000..8b1bc7b1c --- /dev/null +++ b/src/Propagation/CloudTrace/src/CloudTracePropagator.php @@ -0,0 +1,99 @@ +oneWay) { + return; + } + + $setter ??= ArrayAccessGetterSetter::getInstance(); + $context ??= Context::getCurrent(); + $spanContext = Span::fromContext($context)->getContext(); + + if (!$spanContext->isValid()) { + return; + } + + $headerValue = CloudTraceFormatter::serialize($spanContext); + $setter->set($carrier, self::XCLOUD, $headerValue); + } + + /** {@inheritdoc} */ + #[\Override] + public function extract($carrier, ?PropagationGetterInterface $getter = null, ?ContextInterface $context = null): ContextInterface + { + $getter ??= ArrayAccessGetterSetter::getInstance(); + $context ??= Context::getCurrent(); + + $headerValue = $getter->get($carrier, self::XCLOUD); + if ($headerValue === null) { + return $context; + } + + $spanContext = CloudTraceFormatter::deserialize($headerValue); + if (!$spanContext->isValid()) { + return $context; + } + + return $context->withContextValue(Span::wrap($spanContext)); + } +} diff --git a/src/Propagation/CloudTrace/src/Utils.php b/src/Propagation/CloudTrace/src/Utils.php new file mode 100644 index 000000000..250186266 --- /dev/null +++ b/src/Propagation/CloudTrace/src/Utils.php @@ -0,0 +1,122 @@ += and no >, because this function is used in context of what method to use + * to convert to some base (in our case hex to octal and vice versa). + * So it's ok if we use >=, because it means that only for MAX_INT we will use the slower baseConvert + * method. + * + * @param int|float $number The number to test. + * @return bool Whether it was bigger or not than the max. + */ + public static function isBigNum($number) : bool + { + return $number >= PHP_INT_MAX; + } + + /** + * Custom function to convert a number in string format from one base to another. + * Built-in functions, specifically for hex, do not work well in PHP under + * all versions (32/64-bit) or if the number only fits into an unsigned long. + * PHP does not have unsigned longs, so this function is necessary. + * + * @param string $num The number to convert (in some base). + * @param int $fromBase The base to convert from. + * @param int $toBase The base to convert to. + * @return string Converted number in the new base. + */ + public static function baseConvert(string $num, int $fromBase, int $toBase) : string + { + $num = strtolower($num); + $chars = '0123456789abcdefghijklmnopqrstuvwxyz'; + $newstring = substr($chars, 0, $toBase); + + $length = strlen($num); + $result = ''; + + $number = []; + for ($i = 0; $i < $length; $i++) { + $number[$i] = strpos($chars, $num[$i]); + } + + do { + $divide = 0; + $newlen = 0; + for ($i = 0; $i < $length; $i++) { + if (!isset($number[$i]) || $number[$i] === false) { + return ''; + } + $divide = $divide * $fromBase + $number[$i]; + if ($divide >= $toBase) { + $number[$newlen++] = (int) ($divide / $toBase); + $divide %= $toBase; + } elseif ($newlen > 0) { + $number[$newlen++] = 0; + } + } + $length = $newlen; + $result = $newstring[$divide] . $result; + } while ($newlen != 0); + + return $result; + } +} diff --git a/src/Propagation/CloudTrace/tests/Unit/CloudTraceFormatterTest.php b/src/Propagation/CloudTrace/tests/Unit/CloudTraceFormatterTest.php new file mode 100644 index 000000000..9f798dfde --- /dev/null +++ b/src/Propagation/CloudTrace/tests/Unit/CloudTraceFormatterTest.php @@ -0,0 +1,51 @@ +assertEquals($result->getTraceId(), $traceId, "Given deserialize($header), traceId != $traceId (result={$result->getTraceId()}"); + $this->assertEquals($result->getSpanId(), $spanId, "Given deserialize($header), spanId != $spanId (result={$result->getSpanId()}"); + $this->assertEquals($result->getTraceFlags(), $sample, "Given deserialize($header), traceFlags != $sample (result={$result->getTraceFlags()}"); + } + + public static function for_test_deserialize() : array + { + return [ + ['00000000000000000000000000000001/1;o=0', '00000000000000000000000000000001', '0000000000000001', 0], + ['10000000000000000000000000000001/10;o=1', '10000000000000000000000000000001', '000000000000000a', 1], + ]; + } + + /** + * @dataProvider for_test_serialize + */ + public function test_serialize(SpanContextInterface $span, string $header) : void + { + $result = CloudTraceFormatter::serialize($span); + $this->assertEquals($result, $header, "Given serialize(header), result != $header (result=$result"); + } + + public static function for_test_serialize() : array + { + return [ + [SpanContext::createFromRemoteParent('00000000000000000000000000000001', '0000000000000001', TraceFlags::DEFAULT), '00000000000000000000000000000001/1;o=0'], + [SpanContext::createFromRemoteParent('00000000000000000000000000000001', '000000000000000a', TraceFlags::SAMPLED), '00000000000000000000000000000001/10;o=1'], + ]; + } +} diff --git a/src/Propagation/CloudTrace/tests/Unit/CloudTracePropagatorOneWayTest.php b/src/Propagation/CloudTrace/tests/Unit/CloudTracePropagatorOneWayTest.php new file mode 100644 index 000000000..043fca507 --- /dev/null +++ b/src/Propagation/CloudTrace/tests/Unit/CloudTracePropagatorOneWayTest.php @@ -0,0 +1,177 @@ +cloudTracePropagator = CloudTracePropagator::getOneWayInstance(); + [$this->xcloud] = $this->cloudTracePropagator->fields(); + } + + public function test_fields(): void + { + $this->assertSame( + ['x-cloud-trace-context'], + $this->cloudTracePropagator->fields() + ); + } + + public function test_inject_invalid_context(): void + { + $carrier = []; + $this + ->cloudTracePropagator + ->inject( + $carrier, + null, + $this->withSpanContext( + SpanContext::create( + SpanContextValidator::INVALID_TRACE, + SpanContextValidator::INVALID_SPAN, + TraceFlags::SAMPLED + ), + Context::getCurrent() + ) + ); + $this->assertEmpty($carrier); + } + + public function test_inject_sampled_context(): void + { + $carrier = []; + $this + ->cloudTracePropagator + ->inject( + $carrier, + null, + $this->withSpanContext( + SpanContext::create(self::TRACE_ID_BASE16, self::SPAN_ID_BASE16, TraceFlags::SAMPLED), + Context::getCurrent() + ) + ); + $this->assertEmpty($carrier); + } + + public function test_inject_non_sampled_context(): void + { + $carrier = []; + $this + ->cloudTracePropagator + ->inject( + $carrier, + null, + $this->withSpanContext( + SpanContext::create(self::TRACE_ID_BASE16, self::SPAN_ID_BASE16, TraceFlags::DEFAULT), + Context::getCurrent() + ) + ); + $this->assertEmpty($carrier); + } + + public function test_inject_non_sampled_default_context(): void + { + $carrier = []; + $this + ->cloudTracePropagator + ->inject( + $carrier, + null, + $this->withSpanContext( + SpanContext::create(self::TRACE_ID_BASE16, self::SPAN_ID_BASE16), + Context::getCurrent() + ) + ); + $this->assertEmpty($carrier); + } + + public function test_extract_nothing(): void + { + $this->assertSame( + Context::getCurrent(), + $this->cloudTracePropagator->extract([]) + ); + } + + public function test_extract_sampled_context(): void + { + $carrier = [ + $this->xcloud => $this->generateTraceIdHeaderValue( + self::TRACE_ID_BASE16, + self::SPAN_ID_BASE10, + self::TRACE_ENABLED + ), + ]; + + $context = $this->cloudTracePropagator->extract($carrier); + + $this->assertEquals( + SpanContext::createFromRemoteParent(self::TRACE_ID_BASE16, self::SPAN_ID_BASE16, TraceFlags::SAMPLED), + $this->getSpanContext($context) + ); + } + + public function test_extract_non_sampled_context(): void + { + $carrier = [ + $this->xcloud => $this->generateTraceIdHeaderValue( + self::TRACE_ID_BASE16, + self::SPAN_ID_BASE10, + self::TRACE_DISABLED + ), + ]; + + $context = $this->cloudTracePropagator->extract($carrier); + + $this->assertEquals( + SpanContext::createFromRemoteParent(self::TRACE_ID_BASE16, self::SPAN_ID_BASE16, TraceFlags::DEFAULT), + $this->getSpanContext($context) + ); + } + + private function getSpanContext(ContextInterface $context): SpanContextInterface + { + return Span::fromContext($context)->getContext(); + } + + private function withSpanContext(SpanContextInterface $spanContext, ContextInterface $context): ContextInterface + { + return $context->withContextValue(Span::wrap($spanContext)); + } + + private function generateTraceIdHeaderValue( + string $traceId, + string $spanId, + int $traceEnabled + ): string { + return sprintf('%s/%s;o=%d', $traceId, $spanId, $traceEnabled); + } +} diff --git a/src/Propagation/CloudTrace/tests/Unit/CloudTracePropagatorTest.php b/src/Propagation/CloudTrace/tests/Unit/CloudTracePropagatorTest.php new file mode 100644 index 000000000..553045e6f --- /dev/null +++ b/src/Propagation/CloudTrace/tests/Unit/CloudTracePropagatorTest.php @@ -0,0 +1,201 @@ +cloudTracePropagator = CloudTracePropagator::getInstance(); + [$this->xcloud] = $this->cloudTracePropagator->fields(); + } + + public function test_fields(): void + { + $this->assertSame( + ['x-cloud-trace-context'], + $this->cloudTracePropagator->fields() + ); + } + + public function test_inject_invalid_context(): void + { + $carrier = []; + $this + ->cloudTracePropagator + ->inject( + $carrier, + null, + $this->withSpanContext( + SpanContext::create( + SpanContextValidator::INVALID_TRACE, + SpanContextValidator::INVALID_SPAN, + TraceFlags::SAMPLED + ), + Context::getCurrent() + ) + ); + $this->assertEmpty($carrier); + } + + public function test_inject_sampled_context(): void + { + $carrier = []; + $this + ->cloudTracePropagator + ->inject( + $carrier, + null, + $this->withSpanContext( + SpanContext::create(self::TRACE_ID_BASE16, self::SPAN_ID_BASE16, TraceFlags::SAMPLED), + Context::getCurrent() + ) + ); + + $this->assertSame( + [$this->xcloud => $this->generateTraceIdHeaderValue( + self::TRACE_ID_BASE16, + self::SPAN_ID_BASE10, + self::TRACE_ENABLED + )], + $carrier + ); + } + + public function test_inject_non_sampled_context(): void + { + $carrier = []; + $this + ->cloudTracePropagator + ->inject( + $carrier, + null, + $this->withSpanContext( + SpanContext::create(self::TRACE_ID_BASE16, self::SPAN_ID_BASE16, TraceFlags::DEFAULT), + Context::getCurrent() + ) + ); + + $this->assertSame( + [$this->xcloud => $this->generateTraceIdHeaderValue( + self::TRACE_ID_BASE16, + self::SPAN_ID_BASE10, + self::TRACE_DISABLED + )], + $carrier + ); + } + + public function test_inject_non_sampled_default_context(): void + { + $carrier = []; + $this + ->cloudTracePropagator + ->inject( + $carrier, + null, + $this->withSpanContext( + SpanContext::create(self::TRACE_ID_BASE16, self::SPAN_ID_BASE16), + Context::getCurrent() + ) + ); + + $this->assertSame( + [$this->xcloud => $this->generateTraceIdHeaderValue( + self::TRACE_ID_BASE16, + self::SPAN_ID_BASE10, + self::TRACE_DISABLED + )], + $carrier + ); + } + + public function test_extract_nothing(): void + { + $this->assertSame( + Context::getCurrent(), + $this->cloudTracePropagator->extract([]) + ); + } + + public function test_extract_sampled_context(): void + { + $carrier = [ + $this->xcloud => $this->generateTraceIdHeaderValue( + self::TRACE_ID_BASE16, + self::SPAN_ID_BASE10, + self::TRACE_ENABLED + ), + ]; + + $context = $this->cloudTracePropagator->extract($carrier); + + $this->assertEquals( + SpanContext::createFromRemoteParent(self::TRACE_ID_BASE16, self::SPAN_ID_BASE16, TraceFlags::SAMPLED), + $this->getSpanContext($context) + ); + } + + public function test_extract_non_sampled_context(): void + { + $carrier = [ + $this->xcloud => $this->generateTraceIdHeaderValue( + self::TRACE_ID_BASE16, + self::SPAN_ID_BASE10, + self::TRACE_DISABLED + ), + ]; + + $context = $this->cloudTracePropagator->extract($carrier); + + $this->assertEquals( + SpanContext::createFromRemoteParent(self::TRACE_ID_BASE16, self::SPAN_ID_BASE16, TraceFlags::DEFAULT), + $this->getSpanContext($context) + ); + } + + private function getSpanContext(ContextInterface $context): SpanContextInterface + { + return Span::fromContext($context)->getContext(); + } + + private function withSpanContext(SpanContextInterface $spanContext, ContextInterface $context): ContextInterface + { + return $context->withContextValue(Span::wrap($spanContext)); + } + + private function generateTraceIdHeaderValue( + string $traceId, + string $spanId, + int $traceEnabled + ): string { + return sprintf('%s/%s;o=%d', $traceId, $spanId, $traceEnabled); + } +} diff --git a/src/Propagation/CloudTrace/tests/Unit/UtilsTest.php b/src/Propagation/CloudTrace/tests/Unit/UtilsTest.php new file mode 100644 index 000000000..b881e7044 --- /dev/null +++ b/src/Propagation/CloudTrace/tests/Unit/UtilsTest.php @@ -0,0 +1,123 @@ +assertEquals(Utils::leftZeroPad($pad, $howMuch), $equalsTo, "Given leftZeroPad($pad, $howMuch) != $equalsTo"); + } + + public static function for_test_left_zero_pad() : array + { + return [ + ['a', 3, '00a'], + ['aaa', 3, 'aaa'], + ['aaa', 16, '0000000000000aaa'], + ['', 1, '0'], + ]; + } + + /** + * @dataProvider for_test_dec_to_hex + */ + public function test_dec_to_hex(string $decNum, string $equalsTo) : void + { + $this->assertEquals(Utils::decToHex($decNum), $equalsTo, "Given decToHex($decNum) != $equalsTo"); + } + + public static function for_test_dec_to_hex() : array + { + return [ + ['10', 'a'], + ['1', '1'], + ['9223372036854775807', '7fffffffffffffff'], + ['18446744073709551615', 'ffffffffffffffff'], + ['28446744073709551615', '18ac7230489e7ffff'], + ]; + } + + /** + * @dataProvider for_test_hex_to_dec + */ + public function test_hex_to_dec(string $hexNum, string $equalsTo) : void + { + $this->assertEquals(Utils::hexToDec($hexNum), $equalsTo, "Given hexToDec($hexNum) != $equalsTo"); + } + + public static function for_test_hex_to_dec() : array + { + return [ + ['a', '10'], + ['B', '11'], + ['0xc', '12'], + ['1', '1'], + ['7fffffffffffffff', '9223372036854775807'], + ['1ffffffffffffffA', '2305843009213693946'], + ['1fffffffffffffff', '2305843009213693951'], + ['8fffffffffffffff', '10376293541461622783'], + ['7fffffffffffffff', '9223372036854775807'], + ['8000000000000000', '9223372036854775808'], + ['ffffffffffffffff', '18446744073709551615'], + ['18ac7230489e7ffff', '28446744073709551615'], + ]; + } + + /** + * @dataProvider for_test_is_big_num + */ + public function test_is_big_num($num, bool $equalsTo) : void + { + $this->assertEquals(Utils::isBigNum($num), $equalsTo, "Given isBigNum($num) != $equalsTo"); + } + + public static function for_test_is_big_num() : array + { + return [ + [-100.5, false], + [-1, false], + [1, false], + [100.5, false], + [9223372036854775806, false], + [9223372036854775807, true], + [9223372036854775808, true], + [18446744073709551615, true], + [28446744073709551615, true], + ]; + } + + /** + * @dataProvider for_test_base_convert + */ + public function test_base_convert(string $num, int $fromBase, int $toBase, string $equalsTo) : void + { + $result = Utils::baseConvert($num, $fromBase, $toBase); + $this->assertEquals($result, $equalsTo, "Given baseConvert($num, $fromBase, $toBase) != $equalsTo (result=$result)"); + } + + public static function for_test_base_convert() : array + { + return [ + ['b', 16, 10, '11'], + ['c', 16, 10, '12'], + ['fffffffffffff', 16, 10, '4503599627370495'], + ['7fffffff', 16, 10, '2147483647'], + ['7ffffffffffffffe', 16, 10, '9223372036854775806'], + ['7fffffffffffffff', 16, 10, '9223372036854775807'], + ['8000000000000000', 16, 10, '9223372036854775808'], // bigger than signed int max 64 bit + ['18ac7230489e7ffff', 16, 10, '28446744073709551615'], + ['28446744073709551615', 10, 16, '18ac7230489e7ffff'], + ['10', 10, 16, 'a'], + ]; + } +} From ebfbc2f1c8226fa9d12663105715909167bdbabd Mon Sep 17 00:00:00 2001 From: Weslen Teche Date: Sun, 3 Aug 2025 11:28:50 -0300 Subject: [PATCH 5/7] fix: dependabot --- .github/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 77571d99e..5f3115503 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -57,10 +57,10 @@ updates: # Maintain dependencies for all packages - package-ecosystem: "composer" directories: + - "/examples/aws/AwsClientApp" - "/examples/instrumentation/Wordpress" - "/src/AutoInstrumentationInstaller" - "/src/Aws" - - "/src/Aws/examples/AwsClientApp" - "/src/Context/Swoole" - "/src/Exporter/Instana" - "/src/Instrumentation/AwsSdk" From 8ab2c92999614160e8d06f1b33fc5aae2469c3ec Mon Sep 17 00:00:00 2001 From: Weslen Teche Date: Sun, 3 Aug 2025 11:35:17 -0300 Subject: [PATCH 6/7] update readme --- src/Propagation/CloudTrace/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Propagation/CloudTrace/README.md b/src/Propagation/CloudTrace/README.md index d045cd997..f1e048aba 100644 --- a/src/Propagation/CloudTrace/README.md +++ b/src/Propagation/CloudTrace/README.md @@ -2,8 +2,8 @@ [![Issues](https://img.shields.io/badge/issues-pink)](https://github.com/open-telemetry/opentelemetry-php/issues) [![Source](https://img.shields.io/badge/source-contrib-green)](https://github.com/open-telemetry/opentelemetry-php-contrib/tree/main/src/Propagation/CloudTrace) [![Mirror](https://img.shields.io/badge/mirror-opentelemetry--php--contrib-blue)](https://github.com/opentelemetry-php/extension-propagator-cloudtrace) -[![Latest Version](http://poser.pugx.org/open-telemetry/opentelemetry-propagation-instana/v/unstable)](https://packagist.org/packages/open-telemetry/extension-propagator-cloudtrace/) -[![Stable](http://poser.pugx.org/open-telemetry/opentelemetry-propagation-instana/v/stable)](https://packagist.org/packages/open-telemetry/extension-propagator-cloudtrace/) +[![Latest Version](http://poser.pugx.org/open-telemetry/extension-propagator-cloudtrace/v/unstable)](https://packagist.org/packages/open-telemetry/extension-propagator-cloudtrace/) +[![Stable](http://poser.pugx.org/open-telemetry/extension-propagator-cloudtrace/v/stable)](https://packagist.org/packages/open-telemetry/extension-propagator-cloudtrace/) This is a read-only subtree split of https://github.com/open-telemetry/opentelemetry-php-contrib. From 34b3809b870c598379e881c9cdf365c05320f3ed Mon Sep 17 00:00:00 2001 From: Weslen Teche Date: Thu, 7 Aug 2025 21:06:58 -0300 Subject: [PATCH 7/7] applying code review adjustments --- .gitsplit.yml | 2 +- composer.json | 5 +++-- src/Propagation/CloudTrace/_register.php | 2 +- src/Propagation/CloudTrace/composer.json | 11 ++++++++--- .../CloudTrace/src/CloudTraceFormatter.php | 2 +- .../CloudTrace/src/CloudTracePropagator.php | 2 +- src/Propagation/CloudTrace/src/Utils.php | 2 +- .../CloudTrace/tests/Unit/CloudTraceFormatterTest.php | 4 ++-- .../tests/Unit/CloudTracePropagatorOneWayTest.php | 4 ++-- .../tests/Unit/CloudTracePropagatorTest.php | 4 ++-- src/Propagation/CloudTrace/tests/Unit/UtilsTest.php | 4 ++-- 11 files changed, 24 insertions(+), 18 deletions(-) diff --git a/.gitsplit.yml b/.gitsplit.yml index 96eb94827..7830c9b64 100644 --- a/.gitsplit.yml +++ b/.gitsplit.yml @@ -73,7 +73,7 @@ splits: - prefix: "src/MetaPackages/opentelemetry" target: "https://${GH_TOKEN}@github.com/opentelemetry-php/opentelemetry-meta.git" - prefix: "src/Propagation/CloudTrace" - target: "https://${GH_TOKEN}@github.com/opentelemetry-php/extension-propagator-cloudtrace.git" + target: "https://${GH_TOKEN}@github.com/opentelemetry-php/contrib-propagator-cloudtrace.git" - prefix: "src/Propagation/Instana" target: "https://${GH_TOKEN}@github.com/opentelemetry-php/contrib-propagator-instana.git" - prefix: "src/Propagation/ServerTiming" diff --git a/composer.json b/composer.json index 8560e0b75..3c64f2e40 100644 --- a/composer.json +++ b/composer.json @@ -45,6 +45,7 @@ "OpenTelemetry\\Contrib\\Instrumentation\\Wordpress\\": "src/Instrumentation/Wordpress/src", "OpenTelemetry\\Contrib\\Instrumentation\\Yii\\": "src/Instrumentation/Yii/src", "OpenTelemetry\\Contrib\\Logs\\Monolog\\": "src/Logs/Monolog/src", + "OpenTelemetry\\Contrib\\Propagation\\CloudTrace\\": "src/Propagation/CloudTrace/src", "OpenTelemetry\\Contrib\\Propagation\\Instana\\": "src/Propagation/Instana/src", "OpenTelemetry\\Contrib\\Propagation\\ServerTiming\\": "src/Propagation/ServerTiming/src", "OpenTelemetry\\Contrib\\Propagation\\TraceResponse\\": "src/Propagation/TraceResponse/src", @@ -54,7 +55,6 @@ "OpenTelemetry\\Contrib\\Sampler\\RuleBased\\": "src/Sampler/RuleBased/src", "OpenTelemetry\\Contrib\\Shim\\OpenTracing\\": "src/Shims/OpenTracing/src", "OpenTelemetry\\Contrib\\Symfony\\": "src/Symfony/src", - "OpenTelemetry\\Extension\\Propagator\\CloudTrace\\": "src/Propagation/CloudTrace/src", "OpenTelemetry\\TestUtils\\": "src/Utils/Test/src" }, "files": [ @@ -119,6 +119,7 @@ "OpenTelemetry\\Tests\\Instrumentation\\Symfony\\tests\\": "src/Instrumentation/Symfony/tests", "OpenTelemetry\\Tests\\Instrumentation\\Wordpress\\": "src/Instrumentation/Wordpress/tests", "OpenTelemetry\\Tests\\Instrumentation\\Yii\\": "src/Instrumentation/Yii/tests", + "OpenTelemetry\\Tests\\Propagation\\CloudTrace\\": "src/Propagation/CloudTrace/tests", "OpenTelemetry\\Tests\\Resource\\Detector\\Azure\\": "src/ResourceDetectors/Azure/tests", "OpenTelemetry\\Contrib\\Resource\\Detector\\DigitalOcean\\": "src/ResourceDetectors/DigitalOcean/tests", "OpenTelemetry\\Tests\\Contrib\\Symfony\\": "src/Symfony/tests", @@ -133,7 +134,6 @@ "open-telemetry/detector-azure": "self.version", "open-telemetry/detector-container": "self.version", "open-telemetry/detector-digitalocean": "self.version", - "open-telemetry/extension-propagator-cloudtrace": "self.version", "open-telemetry/opentelemetry-auto-aws-sdk": "self.version", "open-telemetry/opentelemetry-auto-cakephp": "self.version", "open-telemetry/opentelemetry-auto-codeigniter": "self.version", @@ -163,6 +163,7 @@ "open-telemetry/opentelemetry-exporter-instana": "self.version", "open-telemetry/opentelemetry-instrumentation-installer": "self.version", "open-telemetry/opentelemetry-logger-monolog": "self.version", + "open-telemetry/opentelemetry-propagation-cloudtrace": "self.version", "open-telemetry/opentelemetry-propagation-instana": "self.version", "open-telemetry/opentelemetry-propagation-server-timing": "self.version", "open-telemetry/opentelemetry-propagation-traceresponse": "self.version", diff --git a/src/Propagation/CloudTrace/_register.php b/src/Propagation/CloudTrace/_register.php index 5fcac7243..5244f8fea 100644 --- a/src/Propagation/CloudTrace/_register.php +++ b/src/Propagation/CloudTrace/_register.php @@ -2,7 +2,7 @@ declare(strict_types=1); -use OpenTelemetry\Extension\Propagator\CloudTrace\CloudTracePropagator; +use OpenTelemetry\Contrib\Propagation\CloudTrace\CloudTracePropagator; use OpenTelemetry\SDK\Registry; if (!class_exists(Registry::class)) { diff --git a/src/Propagation/CloudTrace/composer.json b/src/Propagation/CloudTrace/composer.json index d1e7364f1..dab6bd19f 100644 --- a/src/Propagation/CloudTrace/composer.json +++ b/src/Propagation/CloudTrace/composer.json @@ -1,5 +1,5 @@ { - "name": "open-telemetry/extension-propagator-cloudtrace", + "name": "open-telemetry/opentelemetry-propagation-cloudtrace", "description": "CloudTraceContext propagator extension for OpenTelemetry PHP.", "keywords": ["opentelemetry", "otel", "tracing", "apm", "extension", "propagator", "cloudtrace"], "type": "library", @@ -14,12 +14,17 @@ }, "autoload": { "psr-4":{ - "OpenTelemetry\\Extension\\Propagator\\CloudTrace\\": "src/" + "OpenTelemetry\\Contrib\\Propagation\\CloudTrace\\": "src/" }, "files":[ "_register.php" ] }, + "autoload-dev": { + "psr-4": { + "OpenTelemetry\\Tests\\Propagation\\CloudTrace\\": "tests/" + } + }, "require-dev": { "friendsofphp/php-cs-fixer": "^3", "phan/phan": "^5.0", @@ -36,4 +41,4 @@ "tbachert/spi": true } } -} \ No newline at end of file +} diff --git a/src/Propagation/CloudTrace/src/CloudTraceFormatter.php b/src/Propagation/CloudTrace/src/CloudTraceFormatter.php index f64d32e7d..816ab9ad3 100644 --- a/src/Propagation/CloudTrace/src/CloudTraceFormatter.php +++ b/src/Propagation/CloudTrace/src/CloudTraceFormatter.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenTelemetry\Extension\Propagator\CloudTrace; +namespace OpenTelemetry\Contrib\Propagation\CloudTrace; use OpenTelemetry\API\Trace\SpanContext; use OpenTelemetry\API\Trace\SpanContextInterface; diff --git a/src/Propagation/CloudTrace/src/CloudTracePropagator.php b/src/Propagation/CloudTrace/src/CloudTracePropagator.php index 8b1bc7b1c..90e56ee52 100644 --- a/src/Propagation/CloudTrace/src/CloudTracePropagator.php +++ b/src/Propagation/CloudTrace/src/CloudTracePropagator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenTelemetry\Extension\Propagator\CloudTrace; +namespace OpenTelemetry\Contrib\Propagation\CloudTrace; use OpenTelemetry\API\Trace\Span; use OpenTelemetry\Context\Context; diff --git a/src/Propagation/CloudTrace/src/Utils.php b/src/Propagation/CloudTrace/src/Utils.php index 250186266..700bf7095 100644 --- a/src/Propagation/CloudTrace/src/Utils.php +++ b/src/Propagation/CloudTrace/src/Utils.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenTelemetry\Extension\Propagator\CloudTrace; +namespace OpenTelemetry\Contrib\Propagation\CloudTrace; /** * This class contains utilities that are used by the CloudTracePropagator. diff --git a/src/Propagation/CloudTrace/tests/Unit/CloudTraceFormatterTest.php b/src/Propagation/CloudTrace/tests/Unit/CloudTraceFormatterTest.php index 9f798dfde..0a7af692e 100644 --- a/src/Propagation/CloudTrace/tests/Unit/CloudTraceFormatterTest.php +++ b/src/Propagation/CloudTrace/tests/Unit/CloudTraceFormatterTest.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace OpenTelemetry\Tests\Unit\Extension\Propagator\CloudTrace; +namespace OpenTelemetry\Tests\Propagation\CloudTrace\Unit; use OpenTelemetry\API\Trace\SpanContext; use OpenTelemetry\API\Trace\SpanContextInterface; use OpenTelemetry\API\Trace\TraceFlags; -use OpenTelemetry\Extension\Propagator\CloudTrace\CloudTraceFormatter; +use OpenTelemetry\Contrib\Propagation\CloudTrace\CloudTraceFormatter; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; diff --git a/src/Propagation/CloudTrace/tests/Unit/CloudTracePropagatorOneWayTest.php b/src/Propagation/CloudTrace/tests/Unit/CloudTracePropagatorOneWayTest.php index 043fca507..0282a4524 100644 --- a/src/Propagation/CloudTrace/tests/Unit/CloudTracePropagatorOneWayTest.php +++ b/src/Propagation/CloudTrace/tests/Unit/CloudTracePropagatorOneWayTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenTelemetry\Tests\Unit\Extension\Propagator\CloudTrace; +namespace OpenTelemetry\Tests\Propagation\CloudTrace\Unit; use OpenTelemetry\API\Trace\SpanContext; use OpenTelemetry\API\Trace\SpanContextInterface; @@ -11,7 +11,7 @@ use OpenTelemetry\Context\Context; use OpenTelemetry\Context\ContextInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; -use OpenTelemetry\Extension\Propagator\CloudTrace\CloudTracePropagator; +use OpenTelemetry\Contrib\Propagation\CloudTrace\CloudTracePropagator; use OpenTelemetry\SDK\Trace\Span; use PHPUnit\Framework\TestCase; diff --git a/src/Propagation/CloudTrace/tests/Unit/CloudTracePropagatorTest.php b/src/Propagation/CloudTrace/tests/Unit/CloudTracePropagatorTest.php index 553045e6f..50d2b7e2b 100644 --- a/src/Propagation/CloudTrace/tests/Unit/CloudTracePropagatorTest.php +++ b/src/Propagation/CloudTrace/tests/Unit/CloudTracePropagatorTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenTelemetry\Tests\Unit\Extension\Propagator\CloudTrace; +namespace OpenTelemetry\Tests\Propagation\CloudTrace\Unit; use OpenTelemetry\API\Trace\SpanContext; use OpenTelemetry\API\Trace\SpanContextInterface; @@ -11,7 +11,7 @@ use OpenTelemetry\Context\Context; use OpenTelemetry\Context\ContextInterface; use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; -use OpenTelemetry\Extension\Propagator\CloudTrace\CloudTracePropagator; +use OpenTelemetry\Contrib\Propagation\CloudTrace\CloudTracePropagator; use OpenTelemetry\SDK\Trace\Span; use PHPUnit\Framework\TestCase; diff --git a/src/Propagation/CloudTrace/tests/Unit/UtilsTest.php b/src/Propagation/CloudTrace/tests/Unit/UtilsTest.php index b881e7044..ae00d241e 100644 --- a/src/Propagation/CloudTrace/tests/Unit/UtilsTest.php +++ b/src/Propagation/CloudTrace/tests/Unit/UtilsTest.php @@ -2,9 +2,9 @@ declare(strict_types=1); -namespace OpenTelemetry\Tests\Unit\Extension\Propagator\CloudTrace; +namespace OpenTelemetry\Tests\Propagation\CloudTrace\Unit; -use OpenTelemetry\Extension\Propagator\CloudTrace\Utils; +use OpenTelemetry\Contrib\Propagation\CloudTrace\Utils; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase;