Map OTEL Resources to Jaeger Process Tags (#636)
* Commit in progress work because Gitpod is weird * Finish initial stab at the changes * Forgot to update the exporter * Add in proper handling of serialization of ResourceInfo * ksort doesn't return the array... * Only PHP 8.1 supports key-ed array destructuring * Creating some adapters to break the hard dependency on Batch to make testing easier * Refactoring to break the hard dependency on Batch * Add initial unit tests around the new logic in HttpSender * Some refactoring of the test's helpers * Another small refactor * Some more refactoring * Rename ResourceInfo's test file to match it's file name * Move a test our of REsourceInfoTest into a more appropriate file * Add some basic conformance tests around the new ResourceInfo::serialize method * Add a test to try and catch future properties that aren't added to ResourceInfo::serialize * CLean up comments * Replace string with constant * Improve variable names * Cleaning up comment * Split out tag creation into a helper class * Phan didn't like the type docs * Fix style * Fix tests after merge from main * Fix coverage reporting issues on 2 of the new files * Fix style * Rename vals to values * Shortening the batch adapter factory method name to create * Inline a method call * Inline some method calls * Shorten factory method to just "create" * Inline some code * Split assertions into 3 tests * Rename parameter in interface implementation for psalm + update the rest of the file to match Co-authored-by: Timo Michna <timomichna@yahoo.de>
This commit is contained in:
		
							parent
							
								
									a2f64053da
								
							
						
					
					
						commit
						39c99e5a2f
					
				|  | @ -0,0 +1,23 @@ | |||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace OpenTelemetry\Contrib\Jaeger\BatchAdapter; | ||||
| 
 | ||||
| use Jaeger\Thrift\Batch; | ||||
| use Thrift\Protocol\TProtocol; | ||||
| 
 | ||||
| class BatchAdapter implements BatchAdapterInterface | ||||
| { | ||||
|     private Batch $batchInstance; | ||||
| 
 | ||||
|     public function __construct(array $values) | ||||
|     { | ||||
|         $this->batchInstance = new Batch($values); | ||||
|     } | ||||
| 
 | ||||
|     public function write(TProtocol $output): void | ||||
|     { | ||||
|         $this->batchInstance->write($output); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,13 @@ | |||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace OpenTelemetry\Contrib\Jaeger\BatchAdapter; | ||||
| 
 | ||||
| class BatchAdapterFactory implements BatchAdapterFactoryInterface | ||||
| { | ||||
|     public function create(array $values): BatchAdapterInterface | ||||
|     { | ||||
|         return new BatchAdapter($values); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,10 @@ | |||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace OpenTelemetry\Contrib\Jaeger\BatchAdapter; | ||||
| 
 | ||||
| interface BatchAdapterFactoryInterface | ||||
| { | ||||
|     public function create(array $values): BatchAdapterInterface; | ||||
| } | ||||
|  | @ -0,0 +1,12 @@ | |||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace OpenTelemetry\Contrib\Jaeger\BatchAdapter; | ||||
| 
 | ||||
| use Thrift\Protocol\TProtocol; | ||||
| 
 | ||||
| interface BatchAdapterInterface | ||||
| { | ||||
|     public function write(TProtocol $output): void; | ||||
| } | ||||
|  | @ -18,8 +18,6 @@ class HttpCollectorExporter implements SpanExporterInterface | |||
|     use UsesSpanConverterTrait; | ||||
|     use SpanExporterTrait; | ||||
| 
 | ||||
|     private SpanConverter $spanConverter; | ||||
| 
 | ||||
|     private HttpSender $sender; | ||||
| 
 | ||||
|     public function __construct( | ||||
|  | @ -39,8 +37,6 @@ class HttpCollectorExporter implements SpanExporterInterface | |||
|             $name, | ||||
|             $parsedEndpoint | ||||
|         ); | ||||
| 
 | ||||
|         $this->spanConverter = new SpanConverter(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | @ -48,9 +44,7 @@ class HttpCollectorExporter implements SpanExporterInterface | |||
|      */ | ||||
|     public function doExport(iterable $spans): int | ||||
|     { | ||||
|         $this->sender->send( | ||||
|             $this->spanConverter->convert($spans) | ||||
|         ); | ||||
|         $this->sender->send($spans); | ||||
| 
 | ||||
|         return SpanExporterInterface::STATUS_SUCCESS; | ||||
|     } | ||||
|  |  | |||
|  | @ -4,10 +4,14 @@ declare(strict_types=1); | |||
| 
 | ||||
| namespace OpenTelemetry\Contrib\Jaeger; | ||||
| 
 | ||||
| use Jaeger\Thrift\Batch; | ||||
| use Jaeger\Thrift\Process; | ||||
| use Jaeger\Thrift\Span as JTSpan; | ||||
| use OpenTelemetry\Contrib\Jaeger\BatchAdapter\BatchAdapterFactory; | ||||
| use OpenTelemetry\Contrib\Jaeger\BatchAdapter\BatchAdapterFactoryInterface; | ||||
| use OpenTelemetry\Contrib\Jaeger\BatchAdapter\BatchAdapterInterface; | ||||
| use OpenTelemetry\Contrib\Jaeger\TagFactory\TagFactory; | ||||
| use OpenTelemetry\SDK\Behavior\LogsMessagesTrait; | ||||
| use OpenTelemetry\SDK\Resource\ResourceInfo; | ||||
| use OpenTelemetry\SemConv\ResourceAttributes; | ||||
| use Psr\Http\Client\ClientInterface; | ||||
| use Psr\Http\Message\RequestFactoryInterface; | ||||
| use Psr\Http\Message\StreamFactoryInterface; | ||||
|  | @ -22,75 +26,104 @@ class HttpSender | |||
| 
 | ||||
|     private TProtocol $protocol; | ||||
| 
 | ||||
|     private BatchAdapterFactoryInterface $batchAdapterFactory; | ||||
| 
 | ||||
|     public function __construct( | ||||
|         ClientInterface $client, | ||||
|         RequestFactoryInterface $requestFactory, | ||||
|         StreamFactoryInterface $streamFactory, | ||||
|         string $serviceName, | ||||
|         ParsedEndpointUrl $parsedEndpoint | ||||
|         ParsedEndpointUrl $parsedEndpoint, | ||||
|         BatchAdapterFactoryInterface $batchAdapterFactory = null | ||||
|     ) { | ||||
|         $this->serviceName = $serviceName; | ||||
| 
 | ||||
|         $transport = (new ThriftHttpTransport( | ||||
|         $this->protocol = new TBinaryProtocol( | ||||
|             new ThriftHttpTransport( | ||||
|                 $client, | ||||
|                 $requestFactory, | ||||
|                 $streamFactory, | ||||
|                 $parsedEndpoint | ||||
|         )); | ||||
|             ) | ||||
|         ); | ||||
| 
 | ||||
|         $this->protocol = new TBinaryProtocol($transport); | ||||
|         $this->batchAdapterFactory = $batchAdapterFactory ?? new BatchAdapterFactory(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param JTSpan[] $spans | ||||
|      */ | ||||
|     public function send(array $spans): void | ||||
|     public function send(iterable $spans): void | ||||
|     { | ||||
|         ///** @var Tag[] $tags */ TODO - uncomment this once the code below is uncommented/adapted
 | ||||
|         $tags = []; | ||||
|         $batches = $this->createBatchesPerResource( | ||||
|             self::groupSpansByResource($spans) | ||||
|         ); | ||||
| 
 | ||||
|         //TODO - determine what of this is still needed and how to adapt it for spec compliance
 | ||||
|         // foreach ($this->tracer->getTags() as $k => $v) {
 | ||||
|         //     if (!in_array($k, $this->mapper->getSpecialSpanTags())) {
 | ||||
|         //         if (strpos($k, $this->mapper->getProcessTagsPrefix()) !== 0) {
 | ||||
|         //             continue ;
 | ||||
|         //         }
 | ||||
|         foreach ($batches as $batch) { | ||||
|             $this->sendBatch($batch); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|         //         $quoted = preg_quote($this->mapper->getProcessTagsPrefix());
 | ||||
|         //         $k = preg_replace(sprintf('/^%s/', $quoted), '', $k);
 | ||||
|         //     }
 | ||||
|     private static function groupSpansByResource(iterable $spans): array | ||||
|     { | ||||
|         $spansGroupedByResource = []; | ||||
|         foreach ($spans as $span) { | ||||
|             /** @var ResourceInfo */ | ||||
|             $resource = $span->getResource(); | ||||
|             $resourceAsKey = $resource->serialize(); | ||||
| 
 | ||||
|         //     if ($k === JAEGER_HOSTNAME_TAG_KEY) {
 | ||||
|         //         $k = "hostname";
 | ||||
|         //     }
 | ||||
|             if (!isset($spansGroupedByResource[$resourceAsKey])) { | ||||
|                 $spansGroupedByResource[$resourceAsKey] = [ | ||||
|                     'spans' => [], | ||||
|                     'resource' => $resource, | ||||
|                 ]; | ||||
|             } | ||||
| 
 | ||||
|         //     $tags[] = new Tag([
 | ||||
|         //         "key" => $k,
 | ||||
|         //         "vType" => TagType::STRING,
 | ||||
|         //         "vStr" => $v
 | ||||
|         //     ]);
 | ||||
|         // }
 | ||||
|             $spansGroupedByResource[$resourceAsKey]['spans'][] = $span; | ||||
|         } | ||||
| 
 | ||||
|         // $tags[] = new Tag([
 | ||||
|         //     "key" => "format",
 | ||||
|         //     "vType" => TagType::STRING,
 | ||||
|         //     "vStr" => "jaeger.thrift"
 | ||||
|         // ]);
 | ||||
|         return $spansGroupedByResource; | ||||
|     } | ||||
| 
 | ||||
|         // $tags[] = new Tag([
 | ||||
|         //     "key" => "ip",
 | ||||
|         //     "vType" => TagType::STRING,
 | ||||
|         //     "vStr" => $this->tracer->getIpAddress()
 | ||||
|         // ]);
 | ||||
| 
 | ||||
|         $batch = new Batch([ | ||||
|             'spans' => $spans, | ||||
|             'process' => new Process([ | ||||
|                 'serviceName' => $this->serviceName, | ||||
|                 'tags' => $tags, | ||||
|             ]), | ||||
|     private function createBatchesPerResource(array $spansGroupedByResource): array | ||||
|     { | ||||
|         $batches = []; | ||||
|         foreach ($spansGroupedByResource as $unused => $dataForBatch) { | ||||
|             $batch = $this->batchAdapterFactory->create([ | ||||
|                 'spans' => (new SpanConverter())->convert( | ||||
|                     $dataForBatch['spans'] | ||||
|                 ), | ||||
|                 'process' => $this->createProcessFromResource( | ||||
|                     $dataForBatch['resource'] | ||||
|                 ), | ||||
|             ]); | ||||
| 
 | ||||
|             $batches[] = $batch; | ||||
|         } | ||||
| 
 | ||||
|         return $batches; | ||||
|     } | ||||
| 
 | ||||
|     private function createProcessFromResource(ResourceInfo $resource): Process | ||||
|     { | ||||
|         $serviceName = $this->serviceName; //Defaulting to (what should be) the default resource's service name
 | ||||
| 
 | ||||
|         $tags = []; | ||||
|         foreach ($resource->getAttributes() as $key => $value) { | ||||
|             if ($key === ResourceAttributes::SERVICE_NAME) { | ||||
|                 $serviceName = (string) $value; | ||||
| 
 | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             $tags[] = TagFactory::create($key, $value); | ||||
|         } | ||||
| 
 | ||||
|         return new Process([ | ||||
|             'serviceName' => $serviceName, | ||||
|             'tags' => $tags, | ||||
|         ]); | ||||
|     } | ||||
| 
 | ||||
|     private function sendBatch(BatchAdapterInterface $batch): void | ||||
|     { | ||||
|         $batch->write($this->protocol); | ||||
|         $this->protocol->getTransport()->flush(); | ||||
|     } | ||||
|  |  | |||
|  | @ -8,10 +8,9 @@ use Jaeger\Thrift\Log; | |||
| use Jaeger\Thrift\Span as JTSpan; | ||||
| use Jaeger\Thrift\SpanRef; | ||||
| use Jaeger\Thrift\SpanRefType; | ||||
| use Jaeger\Thrift\Tag; | ||||
| use Jaeger\Thrift\TagType; | ||||
| use OpenTelemetry\API\Trace\SpanKind; | ||||
| use OpenTelemetry\API\Trace\StatusCode; | ||||
| use OpenTelemetry\Contrib\Jaeger\TagFactory\TagFactory; | ||||
| use OpenTelemetry\SDK\Common\Time\Util as TimeUtil; | ||||
| use OpenTelemetry\SDK\Trace\EventInterface; | ||||
| use OpenTelemetry\SDK\Trace\LinkInterface; | ||||
|  | @ -197,89 +196,12 @@ class SpanConverter implements SpanConverterInterface | |||
|     { | ||||
|         $tags = []; | ||||
|         foreach ($tagPairs as $key => $value) { | ||||
|             $tags[] = self::buildTag($key, $value); | ||||
|             $tags[] = TagFactory::create($key, $value); | ||||
|         } | ||||
| 
 | ||||
|         return $tags; | ||||
|     } | ||||
| 
 | ||||
|     private static function buildTag(string $key, $value): Tag | ||||
|     { | ||||
|         return self::createJaegerTagInstance( | ||||
|             $key, | ||||
|             self::convertValueToTypeJaegerTagsSupport($value) | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     private static function convertValueToTypeJaegerTagsSupport($value) | ||||
|     { | ||||
|         if (is_array($value)) { | ||||
|             return self::serializeArrayToString($value); | ||||
|         } | ||||
| 
 | ||||
|         return $value; | ||||
|     } | ||||
| 
 | ||||
|     private static function createJaegerTagInstance(string $key, $value) | ||||
|     { | ||||
|         if (is_bool($value)) { | ||||
|             return new Tag([ | ||||
|                 'key' => $key, | ||||
|                 'vType' => TagType::BOOL, | ||||
|                 'vBool' => $value, | ||||
|             ]); | ||||
|         } | ||||
| 
 | ||||
|         if (is_integer($value)) { | ||||
|             return new Tag([ | ||||
|                 'key' => $key, | ||||
|                 'vType' => TagType::LONG, | ||||
|                 'vLong' => $value, | ||||
|             ]); | ||||
|         } | ||||
| 
 | ||||
|         if (is_numeric($value)) { | ||||
|             return new Tag([ | ||||
|                 'key' => $key, | ||||
|                 'vType' => TagType::DOUBLE, | ||||
|                 'vDouble' => $value, | ||||
|             ]); | ||||
|         } | ||||
| 
 | ||||
|         return new Tag([ | ||||
|             'key' => $key, | ||||
|             'vType' => TagType::STRING, | ||||
|             'vStr' => (string) $value, | ||||
|         ]); | ||||
|     } | ||||
| 
 | ||||
|     private static function serializeArrayToString(array $arrayToSerialize): string | ||||
|     { | ||||
|         return self::recursivelySerializeArray($arrayToSerialize); | ||||
|     } | ||||
| 
 | ||||
|     private static function recursivelySerializeArray($value): string | ||||
|     { | ||||
|         if (is_array($value)) { | ||||
|             return join(',', array_map(function ($val) { | ||||
|                 return self::recursivelySerializeArray($val); | ||||
|             }, $value)); | ||||
|         } | ||||
| 
 | ||||
|         // Casting false to string makes an empty string
 | ||||
|         if (is_bool($value)) { | ||||
|             return $value ? 'true' : 'false'; | ||||
|         } | ||||
| 
 | ||||
|         // Floats will lose precision if their string representation
 | ||||
|         // is >=14 or >=17 digits, depending on PHP settings.
 | ||||
|         // Can also throw E_RECOVERABLE_ERROR if $value is an object
 | ||||
|         // without a __toString() method.
 | ||||
|         // This is possible because OpenTelemetry\Trace\Span does not verify
 | ||||
|         // setAttribute() $value input.
 | ||||
|         return (string) $value; | ||||
|     } | ||||
| 
 | ||||
|     private static function convertOtelEventsToJaegerLogs(SpanDataInterface $span): array | ||||
|     { | ||||
|         return array_map( | ||||
|  |  | |||
|  | @ -0,0 +1,88 @@ | |||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace OpenTelemetry\Contrib\Jaeger\TagFactory; | ||||
| 
 | ||||
| use Jaeger\Thrift\Tag; | ||||
| use Jaeger\Thrift\TagType; | ||||
| 
 | ||||
| class TagFactory | ||||
| { | ||||
|     public static function create(string $key, $value): Tag | ||||
|     { | ||||
|         return self::createJaegerTagInstance( | ||||
|             $key, | ||||
|             self::convertValueToTypeJaegerTagsSupport($value) | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     private static function convertValueToTypeJaegerTagsSupport($value) | ||||
|     { | ||||
|         if (is_array($value)) { | ||||
|             return self::serializeArrayToString($value); | ||||
|         } | ||||
| 
 | ||||
|         return $value; | ||||
|     } | ||||
| 
 | ||||
|     private static function createJaegerTagInstance(string $key, $value) | ||||
|     { | ||||
|         if (is_bool($value)) { | ||||
|             return new Tag([ | ||||
|                 'key' => $key, | ||||
|                 'vType' => TagType::BOOL, | ||||
|                 'vBool' => $value, | ||||
|             ]); | ||||
|         } | ||||
| 
 | ||||
|         if (is_integer($value)) { | ||||
|             return new Tag([ | ||||
|                 'key' => $key, | ||||
|                 'vType' => TagType::LONG, | ||||
|                 'vLong' => $value, | ||||
|             ]); | ||||
|         } | ||||
| 
 | ||||
|         if (is_numeric($value)) { | ||||
|             return new Tag([ | ||||
|                 'key' => $key, | ||||
|                 'vType' => TagType::DOUBLE, | ||||
|                 'vDouble' => $value, | ||||
|             ]); | ||||
|         } | ||||
| 
 | ||||
|         return new Tag([ | ||||
|             'key' => $key, | ||||
|             'vType' => TagType::STRING, | ||||
|             'vStr' => (string) $value, | ||||
|         ]); | ||||
|     } | ||||
| 
 | ||||
|     private static function serializeArrayToString(array $arrayToSerialize): string | ||||
|     { | ||||
|         return self::recursivelySerializeArray($arrayToSerialize); | ||||
|     } | ||||
| 
 | ||||
|     private static function recursivelySerializeArray($value): string | ||||
|     { | ||||
|         if (is_array($value)) { | ||||
|             return join(',', array_map(function ($val) { | ||||
|                 return self::recursivelySerializeArray($val); | ||||
|             }, $value)); | ||||
|         } | ||||
| 
 | ||||
|         // Casting false to string makes an empty string
 | ||||
|         if (is_bool($value)) { | ||||
|             return $value ? 'true' : 'false'; | ||||
|         } | ||||
| 
 | ||||
|         // Floats will lose precision if their string representation
 | ||||
|         // is >=14 or >=17 digits, depending on PHP settings.
 | ||||
|         // Can also throw E_RECOVERABLE_ERROR if $value is an object
 | ||||
|         // without a __toString() method.
 | ||||
|         // This is possible because OpenTelemetry\Trace\Span does not verify
 | ||||
|         // setAttribute() $value input.
 | ||||
|         return (string) $value; | ||||
|     } | ||||
| } | ||||
|  | @ -41,6 +41,18 @@ class ResourceInfo | |||
|         return $this->schemaUrl; | ||||
|     } | ||||
| 
 | ||||
|     public function serialize(): string | ||||
|     { | ||||
|         $copyOfAttributesAsArray = array_slice($this->attributes->toArray(), 0); //This may be overly cautious (in trying to avoid mutating the source array)
 | ||||
|         ksort($copyOfAttributesAsArray); //sort the associative array by keys since the serializer will consider equal arrays different otherwise
 | ||||
| 
 | ||||
|         //The exact return value doesn't matter, as long as it can distingusih between instances that represent the same/different resources
 | ||||
|         return serialize([ | ||||
|             'schemaUrl' => $this->schemaUrl, | ||||
|             'attributes' => $copyOfAttributesAsArray, | ||||
|         ]); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Backward compatibility methods | ||||
|      * | ||||
|  |  | |||
|  | @ -14,6 +14,9 @@ use PHPUnit\Framework\TestCase; | |||
|  * @covers OpenTelemetry\Contrib\Jaeger\HttpSender | ||||
|  * @covers OpenTelemetry\Contrib\Jaeger\ThriftHttpTransport | ||||
|  * @covers OpenTelemetry\Contrib\Jaeger\ParsedEndpointUrl | ||||
|  * @covers OpenTelemetry\Contrib\Jaeger\BatchAdapter\BatchAdapter | ||||
|  * @covers OpenTelemetry\Contrib\Jaeger\BatchAdapter\BatchAdapterFactory | ||||
|  * | ||||
|  */ | ||||
| class JaegerHttpCollectorExporterTest extends TestCase | ||||
| { | ||||
|  |  | |||
|  | @ -0,0 +1,180 @@ | |||
| <?php | ||||
| 
 | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| namespace OpenTelemetry\Tests\Unit\Contrib; | ||||
| 
 | ||||
| use OpenTelemetry\Contrib\Jaeger\BatchAdapter\BatchAdapterFactoryInterface; | ||||
| use OpenTelemetry\Contrib\Jaeger\BatchAdapter\BatchAdapterInterface; | ||||
| use OpenTelemetry\Contrib\Jaeger\HttpSender; | ||||
| use OpenTelemetry\Contrib\Jaeger\ParsedEndpointUrl; | ||||
| use OpenTelemetry\SDK\Common\Attribute\Attributes; | ||||
| use OpenTelemetry\SDK\Resource\ResourceInfo; | ||||
| use OpenTelemetry\Tests\Unit\SDK\Util\SpanData; | ||||
| use PHPUnit\Framework\TestCase; | ||||
| use Thrift\Protocol\TProtocol; | ||||
| 
 | ||||
| /** | ||||
|  * @covers OpenTelemetry\Contrib\Jaeger\HttpSender | ||||
|  */ | ||||
| class JaegerHttpSenderTest extends TestCase | ||||
| { | ||||
|     use UsesHttpClientTrait; | ||||
| 
 | ||||
|     private function createSenderAndMocks(array $inputs): array | ||||
|     { | ||||
|         [ | ||||
|             'serviceName' => $serviceName | ||||
|         ] = $inputs; | ||||
| 
 | ||||
|         $mockBatchAdapterFactory = $this->createBatchAdapterFactoryMock(); | ||||
| 
 | ||||
|         /** | ||||
|          * @psalm-suppress PossiblyInvalidArgument | ||||
|          */ | ||||
|         $sender = new HttpSender( | ||||
|             $this->getClientInterfaceMock(), | ||||
|             $this->getRequestFactoryInterfaceMock(), | ||||
|             $this->getStreamFactoryInterfaceMock(), | ||||
|             $serviceName, | ||||
|             $this->createParsedEndpointUrlMock(), | ||||
|             $mockBatchAdapterFactory | ||||
|         ); | ||||
| 
 | ||||
|         return [ | ||||
|             'sender' => $sender, | ||||
|             'mockBatchAdapterFactory' => $mockBatchAdapterFactory, | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     private function createParsedEndpointUrlMock(): ParsedEndpointUrl | ||||
|     { | ||||
|         /** @var ParsedEndpointUrl */ | ||||
|         $mock = $this->createMock(ParsedEndpointUrl::class); | ||||
| 
 | ||||
|         return $mock; | ||||
|     } | ||||
| 
 | ||||
|     private function createBatchAdapterFactoryMock(): BatchAdapterFactoryInterface | ||||
|     { | ||||
|         return new class() implements BatchAdapterFactoryInterface { | ||||
|             //Just enough spy functionality for what was needed for now. Generalize and extend as needed
 | ||||
|             private array $interceptedValues = []; | ||||
| 
 | ||||
|             public function getInterceptedValues() | ||||
|             { | ||||
|                 return $this->interceptedValues; | ||||
|             } | ||||
| 
 | ||||
|             public function create(array $values): BatchAdapterInterface | ||||
|             { | ||||
|                 $this->interceptedValues[] = $values; | ||||
| 
 | ||||
|                 $mockBatchAdapter = new class() implements BatchAdapterInterface { | ||||
|                     public function write(TProtocol $output): void | ||||
|                     { | ||||
|                     } | ||||
|                 }; | ||||
| 
 | ||||
|                 return $mockBatchAdapter; | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     public function test_span_and_process_data_are_batched_by_resource(): void | ||||
|     { | ||||
|         [ | ||||
|             'sender' => $sender, | ||||
|             'mockBatchAdapterFactory' => $mockBatchAdapterFactory | ||||
|         ] = $this->createSenderAndMocks([ | ||||
|             'serviceName' => 'nameOfThe1stLogicalApp', | ||||
|         ]); | ||||
| 
 | ||||
|         $spans = [ | ||||
|             (new SpanData())->setResource(ResourceInfo::create( | ||||
|                 new Attributes(), //code should default service.name from how its set above
 | ||||
|             )), | ||||
|             (new SpanData())->setResource(ResourceInfo::create( | ||||
|                 new Attributes([ | ||||
|                     'service.name' => 'nameOfThe2ndLogicalApp', | ||||
|                 ]), | ||||
|             )), | ||||
|         ]; | ||||
| 
 | ||||
|         $sender->send($spans); | ||||
| 
 | ||||
|         $interceptedValues = $mockBatchAdapterFactory->getInterceptedValues(); | ||||
|         $this->assertSame(2, count($interceptedValues)); | ||||
| 
 | ||||
|         //1st batch
 | ||||
|         $this->assertSame(1, count($interceptedValues[0]['spans'])); //Detailed tests for the span conversion live elsewhere
 | ||||
| 
 | ||||
|         //2nd batch
 | ||||
|         $this->assertSame(1, count($interceptedValues[1]['spans'])); //Detailed tests for the span conversion live elsewhere
 | ||||
|     } | ||||
| 
 | ||||
|     public function test_process_service_names_are_correctly_set_from_resource_attributes_or_the_default_service_name(): void | ||||
|     { | ||||
|         [ | ||||
|             'sender' => $sender, | ||||
|             'mockBatchAdapterFactory' => $mockBatchAdapterFactory | ||||
|         ] = $this->createSenderAndMocks([ | ||||
|             'serviceName' => 'nameOfThe1stLogicalApp', | ||||
|         ]); | ||||
| 
 | ||||
|         $spans = [ | ||||
|             (new SpanData())->setResource(ResourceInfo::create( | ||||
|                 new Attributes(), //code should default service.name from how its set above
 | ||||
|             )), | ||||
|             (new SpanData())->setResource(ResourceInfo::create( | ||||
|                 new Attributes([ | ||||
|                     'service.name' => 'nameOfThe2ndLogicalApp', | ||||
|                 ]), | ||||
|             )), | ||||
|         ]; | ||||
| 
 | ||||
|         $sender->send($spans); | ||||
| 
 | ||||
|         $interceptedValues = $mockBatchAdapterFactory->getInterceptedValues(); | ||||
| 
 | ||||
|         //1st batch
 | ||||
|         $this->assertSame('nameOfThe1stLogicalApp', $interceptedValues[0]['process']->serviceName); | ||||
| 
 | ||||
|         //2nd batch
 | ||||
|         $this->assertSame('nameOfThe2ndLogicalApp', $interceptedValues[1]['process']->serviceName); | ||||
|     } | ||||
| 
 | ||||
|     public function test_tags_are_correctly_set_from_resource_attributes(): void | ||||
|     { | ||||
|         [ | ||||
|             'sender' => $sender, | ||||
|             'mockBatchAdapterFactory' => $mockBatchAdapterFactory | ||||
|         ] = $this->createSenderAndMocks([ | ||||
|             'serviceName' => 'someServiceName', | ||||
|         ]); | ||||
| 
 | ||||
|         $spans = [ | ||||
|             (new SpanData())->setResource(ResourceInfo::create( | ||||
|                 new Attributes(), | ||||
|             )), | ||||
|             (new SpanData())->setResource(ResourceInfo::create( | ||||
|                 new Attributes([ | ||||
|                     'telemetry.sdk.name' => 'opentelemetry', | ||||
|                 ]), | ||||
|             )), | ||||
|         ]; | ||||
| 
 | ||||
|         $sender->send($spans); | ||||
| 
 | ||||
|         $interceptedValues = $mockBatchAdapterFactory->getInterceptedValues(); | ||||
| 
 | ||||
|         //1st batch
 | ||||
|         $this->assertSame(0, count($interceptedValues[0]['process']->tags)); | ||||
| 
 | ||||
|         //2nd batch
 | ||||
|         $this->assertSame(1, count($interceptedValues[1]['process']->tags)); | ||||
| 
 | ||||
|         $this->assertSame('telemetry.sdk.name', $interceptedValues[1]['process']->tags[0]->key); | ||||
|         $this->assertSame('opentelemetry', $interceptedValues[1]['process']->tags[0]->vStr); | ||||
|     } | ||||
| } | ||||
|  | @ -19,6 +19,7 @@ use PHPUnit\Framework\TestCase; | |||
| 
 | ||||
| /** | ||||
|  * @covers OpenTelemetry\Contrib\Jaeger\SpanConverter | ||||
|  * @covers OpenTelemetry\Contrib\Jaeger\TagFactory\TagFactory | ||||
|  */ | ||||
| class JaegerSpanConverterTest extends TestCase | ||||
| { | ||||
|  |  | |||
|  | @ -25,4 +25,12 @@ class ComposerTest extends TestCase | |||
|         $this->assertSame($name, $resource->getAttributes()->get(ResourceAttributes::SERVICE_NAME)); | ||||
|         $this->assertSame($version, $resource->getAttributes()->get(ResourceAttributes::SERVICE_VERSION)); | ||||
|     } | ||||
| 
 | ||||
|     public function test_composer_detector(): void | ||||
|     { | ||||
|         $resource = (new Detectors\Composer())->getResource(); | ||||
| 
 | ||||
|         $this->assertNotNull($resource->getAttributes()->get(ResourceAttributes::SERVICE_NAME)); | ||||
|         $this->assertNotNull($resource->getAttributes()->get(ResourceAttributes::SERVICE_VERSION)); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ use PHPUnit\Framework\TestCase; | |||
| /** | ||||
|  * @covers OpenTelemetry\SDK\Resource\ResourceInfo | ||||
|  */ | ||||
| class ResourceTest extends TestCase | ||||
| class ResourceInfoTest extends TestCase | ||||
| { | ||||
|     use EnvironmentVariables; | ||||
| 
 | ||||
|  | @ -322,11 +322,55 @@ class ResourceTest extends TestCase | |||
|         $this->assertEquals('foo', $resource->getAttributes()->get('service.name')); | ||||
|     } | ||||
| 
 | ||||
|     public function test_composer_detector(): void | ||||
|     /** | ||||
|      * @dataProvider sameResourcesProvider | ||||
|      */ | ||||
|     public function test_serialize_returns_same_output_for_objects_representing_the_same_resource(ResourceInfo $resource1, ResourceInfo $resource2): void | ||||
|     { | ||||
|         $resource = (new Detectors\Composer())->getResource(); | ||||
|         $this->assertSame($resource1->serialize(), $resource2->serialize()); | ||||
|     } | ||||
| 
 | ||||
|         $this->assertNotNull($resource->getAttributes()->get(ResourceAttributes::SERVICE_NAME)); | ||||
|         $this->assertNotNull($resource->getAttributes()->get(ResourceAttributes::SERVICE_VERSION)); | ||||
|     public function sameResourcesProvider(): iterable | ||||
|     { | ||||
|         yield 'Attribute keys sorted in ascending order vs Attribute keys sorted in descending order' => [ | ||||
|             ResourceInfo::create(new Attributes([ | ||||
|                 'a' => 'someValue', | ||||
|                 'b' => 'someValue', | ||||
|                 'c' => 'someValue', | ||||
|             ])), | ||||
|             ResourceInfo::create(new Attributes([ | ||||
|                 'c' => 'someValue', | ||||
|                 'b' => 'someValue', | ||||
|                 'a' => 'someValue', | ||||
|             ])), | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @dataProvider differentResourcesProvider | ||||
|      */ | ||||
|     public function test_serialize_returns_different_output_for_objects_representing_different_resources(ResourceInfo $resource1, ResourceInfo $resource2): void | ||||
|     { | ||||
|         $this->assertNotSame($resource1->serialize(), $resource2->serialize()); | ||||
|     } | ||||
| 
 | ||||
|     public function differentResourcesProvider(): iterable | ||||
|     { | ||||
|         yield 'Null schema url vs Some schema url' => [ | ||||
|             ResourceInfo::create(new Attributes(), null), | ||||
|             ResourceInfo::create(new Attributes(), 'someSchemaUrl'), | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     public function test_serialize_incorporates_all_properties(): void | ||||
|     { | ||||
|         $resource = ResourceInfoFactory::emptyResource(); | ||||
|         $properties = (new \ReflectionClass($resource))->getProperties(); | ||||
| 
 | ||||
|         $serializedResource = $resource->serialize(); | ||||
| 
 | ||||
|         foreach ($properties as $property) { | ||||
|             $this->assertStringContainsString($property->getName(), $serializedResource); | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue