diff --git a/src/Propagation/Instana/README.md b/src/Propagation/Instana/README.md index c3acb6ea6..1ba3ade4b 100644 --- a/src/Propagation/Instana/README.md +++ b/src/Propagation/Instana/README.md @@ -1,6 +1,15 @@ -OpenTelemetry Instana Propagator +[![Releases](https://img.shields.io/badge/releases-purple)](https://github.com/opentelemetry-php/contrib-propagator-instana/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/Instana) +[![Mirror](https://img.shields.io/badge/mirror-opentelemetry--php--contrib-blue)](https://github.com/opentelemetry-php/contrib-propagator-instana) +[![Latest Version](http://poser.pugx.org/open-telemetry/opentelemetry-propagation-instana/v/unstable)](https://packagist.org/packages/open-telemetry/opentelemetry-propagation-instana/) +[![Stable](http://poser.pugx.org/open-telemetry/opentelemetry-propagation-instana/v/stable)](https://packagist.org/packages/open-telemetry/opentelemetry-propagation-instana/) -The OpenTelemetry Propagator for Instana provides HTTP header propagation for systems that are using IBM Observability by Instana. +This is a read-only subtree split of https://github.com/open-telemetry/opentelemetry-php-contrib. + +# OpenTelemetry Instana Propagator + +The OpenTelemetry Propagator for Instana provides HTTP header propagation and Baggage propagation for systems that are using IBM Observability by Instana. This propagator translates the Instana trace correlation headers (`X-INSTANA-T/X-INSTANA-S/X-INSTANA-L`) into the OpenTelemetry `SpanContext`, and vice versa. It does not handle `TraceState`. @@ -14,11 +23,17 @@ composer require open-telemetry/opentelemetry-propagation-instana ## Usage ``` -$propagator = InstanaMultiPropagator::getInstance(); +$propagator = InstanaPropagator::getInstance(); ``` Both of the above have extract and inject methods available to extract and inject respectively into the header. +For Baggage propagation, use opentelemetry's MultiTextMapPropagator, and pass the array list of propagators i.e Instana and Baggage propagator as below. + +``` +$propagator = new MultiTextMapPropagator(InstantPropagator::getInstance(), BaggagePropagator::getInstance()); +``` + ## Propagator Details There are three headers that the propagator handles: `X-INSTANA-T` (the trace ID), `X-INSTANA-S` (the parent span ID), and `X-INSTANA-L` (the sampling level). diff --git a/src/Propagation/Instana/_register.php b/src/Propagation/Instana/_register.php index 15b0f1425..4add57cc9 100644 --- a/src/Propagation/Instana/_register.php +++ b/src/Propagation/Instana/_register.php @@ -1,13 +1,15 @@ tests/Unit + + tests/Integration + diff --git a/src/Propagation/Instana/src/InstanaMultiPropagator.php b/src/Propagation/Instana/src/InstanaPropagator.php similarity index 97% rename from src/Propagation/Instana/src/InstanaMultiPropagator.php rename to src/Propagation/Instana/src/InstanaPropagator.php index ddae29c40..a3755c4c0 100644 --- a/src/Propagation/Instana/src/InstanaMultiPropagator.php +++ b/src/Propagation/Instana/src/InstanaPropagator.php @@ -21,7 +21,7 @@ * "instana" http headers used for trace context propagation across service * boundaries. */ -final class InstanaMultiPropagator implements TextMapPropagatorInterface +final class InstanaPropagator implements TextMapPropagatorInterface { /** * The X-INSTANA-T header is required and is encoded as 32 lower-hex characters. @@ -115,7 +115,6 @@ public function extract($carrier, ?PropagationGetterInterface $getter = null, ?C if (($traceId === '' && $spanId === '') && $level !== null) { return (new NonRecordingSpan($spanContext)) ->storeInContext($context); - } elseif (!$spanContext->isValid()) { return $context; } @@ -161,7 +160,7 @@ private static function extractImpl($carrier, PropagationGetterInterface $getter } if ($spanId && strlen($spanId) < 16) { - $spanId = str_pad($spanId, 16, '0', STR_PAD_LEFT); + $spanId = str_pad($spanId, 16, '0', STR_PAD_LEFT); } return SpanContext::createFromRemoteParent( diff --git a/src/Propagation/Instana/tests/Integration/InstanaMultiPropagatorTest.php b/src/Propagation/Instana/tests/Integration/InstanaMultiPropagatorTest.php new file mode 100644 index 000000000..5df1cf685 --- /dev/null +++ b/src/Propagation/Instana/tests/Integration/InstanaMultiPropagatorTest.php @@ -0,0 +1,143 @@ +InstanaPropagator = InstanaPropagator::getInstance(); + $instanaMultiFields = $this->InstanaPropagator->fields(); + $this->TRACE_ID = $instanaMultiFields[0]; + $this->SPAN_ID = $instanaMultiFields[1]; + $this->SAMPLED = $instanaMultiFields[2]; + } + + /** + * @dataProvider sampledValueProvider + */ + public function test_extract_sampled_context_with_baggage($sampledValue): void + { + $carrier = [ + $this->TRACE_ID => self::X_INSTANA_T, + $this->SPAN_ID => self::X_INSTANA_S, + $this->SAMPLED => $sampledValue, + 'baggage' => 'user_id=12345,request_id=abcde', + ]; + $propagator = new MultiTextMapPropagator([InstanaPropagator::getInstance(), BaggagePropagator::getInstance()]); + $context = $propagator->extract($carrier); + + $this->assertEquals( + SpanContext::createFromRemoteParent(self::X_INSTANA_T, self::X_INSTANA_S, TraceFlags::SAMPLED), + $this->getSpanContext($this->InstanaPropagator->extract($carrier)) + ); + + // Verify baggage + $baggage = Baggage::fromContext($context); + $this->assertEquals('12345', $baggage->getValue('user_id')); + $this->assertEquals('abcde', $baggage->getValue('request_id')); + + $arr = []; + + foreach ($baggage->getAll() as $key => $value) { + $arr[$key] = $value->getValue(); + } + + $this->assertEquals( + ['user_id' => '12345', 'request_id' => 'abcde'], + $arr + ); + } + + /** + * @dataProvider sampledValueProvider + */ + public function test_extract_sampled_context_with_baggage_but_instana_propagator($sampledValue): void + { + $carrier = [ + $this->TRACE_ID => self::X_INSTANA_T, + $this->SPAN_ID => self::X_INSTANA_S, + $this->SAMPLED => $sampledValue, + 'baggage' => 'user_id=12345,request_id=abcde', + ]; + $context = $this->InstanaPropagator->extract($carrier); + + $this->assertEquals( + SpanContext::createFromRemoteParent(self::X_INSTANA_T, self::X_INSTANA_S, TraceFlags::SAMPLED), + $this->getSpanContext($this->InstanaPropagator->extract($carrier)) + ); + + // Verify baggage is not propagated + $baggage = Baggage::fromContext($context); + $this->assertNull($baggage->getValue('user_id')); + $this->assertNull($baggage->getValue('request_id')); + + } + + public function test_baggage_inject(): void + { + $carrier = []; + + $propagator = new MultiTextMapPropagator([InstanaPropagator::getInstance(), BaggagePropagator::getInstance()]); + + $propagator->inject( + $carrier, + null, + Context::getRoot()->withContextValue( + Baggage::getBuilder() + ->set('nometa', 'nometa-value') + ->set('meta', 'meta-value', new Metadata('somemetadata; someother=foo')) + ->build() + ) + ); + + $this->assertSame( + ['baggage' => 'nometa=nometa-value,meta=meta-value;somemetadata; someother=foo'], + $carrier + ); + } + + public static function sampledValueProvider(): array + { + return [ + 'String sampled value' => ['1'], + 'Boolean(lower string) sampled value' => ['true'], + 'Boolean(upper string) sampled value' => ['TRUE'], + 'Boolean(camel string) sampled value' => ['True'], + ]; + } + + private function getSpanContext(ContextInterface $context): SpanContextInterface + { + return Span::fromContext($context)->getContext(); + } + +} diff --git a/src/Propagation/Instana/tests/Unit/InstanaMultiPropagatorTest.php b/src/Propagation/Instana/tests/Unit/InstanaPropagatorTest.php similarity index 87% rename from src/Propagation/Instana/tests/Unit/InstanaMultiPropagatorTest.php rename to src/Propagation/Instana/tests/Unit/InstanaPropagatorTest.php index a7324cef8..4d46d6128 100644 --- a/src/Propagation/Instana/tests/Unit/InstanaMultiPropagatorTest.php +++ b/src/Propagation/Instana/tests/Unit/InstanaPropagatorTest.php @@ -8,15 +8,16 @@ use OpenTelemetry\API\Trace\SpanContextInterface; use OpenTelemetry\API\Trace\SpanContextValidator; use OpenTelemetry\API\Trace\TraceFlags; -use OpenTelemetry\Context\Context; +use OpenTelemetry\Context\Context; use OpenTelemetry\Context\ContextInterface; -use OpenTelemetry\Contrib\Propagation\Instana\InstanaMultiPropagator as InstanaMultiPropagator; +use OpenTelemetry\Contrib\Propagation\Instana\InstanaPropagator; + use OpenTelemetry\SDK\Trace\Span; use Override; use PHPUnit\Framework\TestCase; -final class InstanaMultiPropagatorTest extends TestCase +final class InstanaPropagatorTest extends TestCase { private const X_INSTANA_T = 'ff000000000000000000000000000041'; private const X_INSTANA_S = 'ff00000000000041'; @@ -27,13 +28,13 @@ final class InstanaMultiPropagatorTest extends TestCase private $SPAN_ID; private $SAMPLED; - private InstanaMultiPropagator $instanaMultiPropagator; + private InstanaPropagator $InstanaPropagator; #[Override] protected function setUp(): void { - $this->instanaMultiPropagator = InstanaMultiPropagator::getInstance(); - $instanaMultiFields = $this->instanaMultiPropagator->fields(); + $this->InstanaPropagator = InstanaPropagator::getInstance(); + $instanaMultiFields = $this->InstanaPropagator->fields(); $this->TRACE_ID = $instanaMultiFields[0]; $this->SPAN_ID = $instanaMultiFields[1]; $this->SAMPLED = $instanaMultiFields[2]; @@ -43,14 +44,14 @@ public function test_fields(): void { $this->assertSame( ['X-INSTANA-T', 'X-INSTANA-S', 'X-INSTANA-L'], - $this->instanaMultiPropagator->fields() + $this->InstanaPropagator->fields() ); } public function test_inject_empty(): void { $carrier = []; - $this->instanaMultiPropagator->inject($carrier); + $this->InstanaPropagator->inject($carrier); $this->assertEmpty($carrier); } @@ -58,7 +59,7 @@ public function test_inject_invalid_context(): void { $carrier = []; $this - ->instanaMultiPropagator + ->InstanaPropagator ->inject( $carrier, null, @@ -78,7 +79,7 @@ public function test_inject_sampled_context(): void { $carrier = []; $this - ->instanaMultiPropagator + ->InstanaPropagator ->inject( $carrier, null, @@ -102,7 +103,7 @@ public function test_inject_non_sampled_context(): void { $carrier = []; $this - ->instanaMultiPropagator + ->InstanaPropagator ->inject( $carrier, null, @@ -126,7 +127,7 @@ public function test_inject_sampled_context_when_other_traceflags_set(): void { $carrier = []; $this - ->instanaMultiPropagator + ->InstanaPropagator ->inject( $carrier, null, @@ -156,7 +157,7 @@ public function test_extract_context_with_lowercase_headers(): void $this->assertEquals( SpanContext::createFromRemoteParent(self::X_INSTANA_T, self::X_INSTANA_S, TraceFlags::SAMPLED), - $this->getSpanContext($this->instanaMultiPropagator->extract($carrier)) + $this->getSpanContext($this->InstanaPropagator->extract($carrier)) ); } @@ -183,7 +184,7 @@ public function test_extract_sampled_context($sampledValue): void $this->assertEquals( SpanContext::createFromRemoteParent(self::X_INSTANA_T, self::X_INSTANA_S, TraceFlags::SAMPLED), - $this->getSpanContext($this->instanaMultiPropagator->extract($carrier)) + $this->getSpanContext($this->InstanaPropagator->extract($carrier)) ); } @@ -210,7 +211,7 @@ public function test_extract_non_sampled_context($sampledValue): void $this->assertEquals( SpanContext::createFromRemoteParent(self::X_INSTANA_T, self::X_INSTANA_S), - $this->getSpanContext($this->instanaMultiPropagator->extract($carrier)) + $this->getSpanContext($this->InstanaPropagator->extract($carrier)) ); } @@ -237,7 +238,7 @@ public function test_extract_default_sampled_context($sampledValue): void $this->assertEquals( SpanContext::createFromRemoteParent(self::X_INSTANA_T, self::X_INSTANA_S, TraceFlags::DEFAULT), - $this->getSpanContext($this->instanaMultiPropagator->extract($carrier)) + $this->getSpanContext($this->InstanaPropagator->extract($carrier)) ); } @@ -262,7 +263,7 @@ public function test_extract_invalid_sampled_context($sampledValue): void $this->assertEquals( SpanContext::createFromRemoteParent(self::X_INSTANA_T, self::X_INSTANA_S), - $this->getSpanContext($this->instanaMultiPropagator->extract($carrier)) + $this->getSpanContext($this->InstanaPropagator->extract($carrier)) ); } @@ -286,7 +287,7 @@ public function test_extract_context_with_sampled_no_trace_and_span_headers(): v '0000000000000000', TraceFlags::SAMPLED ), - $this->getSpanContext($this->instanaMultiPropagator->extract($carrier)) + $this->getSpanContext($this->InstanaPropagator->extract($carrier)) ); } @@ -302,7 +303,7 @@ public function test_extract_context_with_no_trace_and_span_headers(): void '0000000000000000', TraceFlags::DEFAULT ), - $this->getSpanContext($this->instanaMultiPropagator->extract($carrier)) + $this->getSpanContext($this->InstanaPropagator->extract($carrier)) ); } @@ -313,9 +314,9 @@ public function test_extract_and_inject(): void $this->SPAN_ID => self::X_INSTANA_S, $this->SAMPLED => self::IS_SAMPLED, ]; - $context = $this->instanaMultiPropagator->extract($extractCarrier); + $context = $this->InstanaPropagator->extract($extractCarrier); $injectCarrier = []; - $this->instanaMultiPropagator->inject($injectCarrier, null, $context); + $this->InstanaPropagator->inject($injectCarrier, null, $context); $this->assertSame($injectCarrier, $extractCarrier); } @@ -340,7 +341,7 @@ public function test_extract_leftpad_spand_id(): void $this->assertEquals( SpanContext::createFromRemoteParent('00000000000000004aaba1a52cf8ee09', '0007b5a2e4d86bd1', TraceFlags::SAMPLED), - $this->getSpanContext($this->instanaMultiPropagator->extract($carrier)) + $this->getSpanContext($this->InstanaPropagator->extract($carrier)) ); } @@ -403,7 +404,7 @@ private function assertInvalid(array $carrier): void { $this->assertSame( Context::getCurrent(), - $this->instanaMultiPropagator->extract($carrier), + $this->InstanaPropagator->extract($carrier), ); }