From a2dcd5d808e9a52649875bc279ec56644ce62036 Mon Sep 17 00:00:00 2001 From: Tobias Bachert Date: Sat, 24 Sep 2022 23:27:58 +0200 Subject: [PATCH] Refactor transport layer - move content type from `::send()` to transport property - move otlp exporter to `Otlp\` namespace --- Makefile | 2 +- deptrac.yaml | 3 +- examples/metrics/basic.php | 5 +- src/Contrib/Grpc/GrpcTransport.php | 14 +-- src/Contrib/Grpc/GrpcTransportFactory.php | 12 +++ src/Contrib/Newrelic/Exporter.php | 6 +- src/Contrib/Otlp/MetricExporter.php | 87 +++++++++++++++++++ src/Contrib/Otlp/ProtobufSerializer.php | 76 ++++++++++++++++ src/Contrib/Otlp/SpanExporter.php | 77 ++++++++++++++++ src/Contrib/Otlp/StreamMetricExporter.php | 74 ---------------- src/Contrib/OtlpHttp/Exporter.php | 68 ++------------- src/Contrib/OtlpHttp/MetricExporter.php | 71 +-------------- src/Contrib/Zipkin/Exporter.php | 6 +- src/Contrib/ZipkinToNewrelic/Exporter.php | 6 +- src/SDK/Common/Export/Http/PsrTransport.php | 19 +++- .../Export/Http/PsrTransportFactory.php | 2 + .../Common/Export/Stream/StreamTransport.php | 19 +++- .../Export/Stream/StreamTransportFactory.php | 45 ++++++++-- .../Export/TransportFactoryInterface.php | 8 +- src/SDK/Common/Export/TransportInterface.php | 7 +- .../Behavior/SpanExporterDecoratorTrait.php | 10 +-- src/SDK/Trace/Behavior/SpanExporterTrait.php | 6 +- src/SDK/Trace/SpanExporterInterface.php | 6 +- tests/Benchmark/OtlpBench.php | 3 +- .../TraceContextPropagatorTest.php | 4 +- .../Propagation/TraceContextValidatorTest.php | 4 +- tests/Unit/Contrib/Grpc/GrpcTransportTest.php | 12 ++- .../Contrib/Otlp/StreamMetricExporterTest.php | 87 ------------------- .../Contrib/OtlpHttp/OTLPHttpExporterTest.php | 15 ++-- .../Common/Export/Http/PsrTransportTest.php | 54 ++++++------ tests/Unit/SDK/Trace/ExporterFactoryTest.php | 6 +- 31 files changed, 429 insertions(+), 385 deletions(-) create mode 100644 src/Contrib/Otlp/MetricExporter.php create mode 100644 src/Contrib/Otlp/ProtobufSerializer.php create mode 100644 src/Contrib/Otlp/SpanExporter.php delete mode 100644 src/Contrib/Otlp/StreamMetricExporter.php delete mode 100644 tests/Unit/Contrib/Otlp/StreamMetricExporterTest.php diff --git a/Makefile b/Makefile index ea9f6d97a..f3d7f2934 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ update: ## Update dependencies $(DC_RUN_PHP) env XDEBUG_MODE=off composer update test: test-unit test-integration ## Run unit and integration tests test-unit: ## Run unit tests - $(DC_RUN_PHP) env XDEBUG_MODE=coverage vendor/bin/phpunit --testsuite unit --colors=always --coverage-text --testdox --coverage-clover coverage.clover --coverage-html=tests/coverage/html --log-junit=junit.xml + $(DC_RUN_PHP) env XDEBUG_MODE=coverage vendor/bin/phpunit --testsuite unit --colors=always --testdox --coverage-clover coverage.clover --coverage-html=tests/coverage/html --log-junit=junit.xml test-integration: ## Run integration tests $(DC_RUN_PHP) env XDEBUG_MODE=off vendor/bin/phpunit --testsuite integration --colors=always test-coverage: ## Run units tests and generate code coverage diff --git a/deptrac.yaml b/deptrac.yaml index 3baaacb1e..c2c33f0a6 100644 --- a/deptrac.yaml +++ b/deptrac.yaml @@ -109,8 +109,7 @@ deptrac: - Composer Contrib: - +SDK - - OtelProto - - Grpc + - +OtelProto - Prometheus - Thrift - JaegerThrift diff --git a/examples/metrics/basic.php b/examples/metrics/basic.php index 78a86c5e3..16f423462 100644 --- a/examples/metrics/basic.php +++ b/examples/metrics/basic.php @@ -5,8 +5,9 @@ require_once __DIR__ . '/../../vendor/autoload.php'; -use OpenTelemetry\Contrib\Otlp\StreamMetricExporter; +use OpenTelemetry\Contrib\Otlp\MetricExporter; use OpenTelemetry\SDK\Common\Attribute\Attributes; +use OpenTelemetry\SDK\Common\Export\Stream\StreamTransportFactory; use OpenTelemetry\SDK\Common\Instrumentation\InstrumentationScopeFactory; use OpenTelemetry\SDK\Common\Time\ClockFactory; use OpenTelemetry\SDK\Metrics\Aggregation\ExplicitBucketHistogramAggregation; @@ -20,7 +21,7 @@ use OpenTelemetry\SDK\Resource\ResourceInfoFactory; $clock = ClockFactory::getDefault(); -$reader = new ExportingReader(new StreamMetricExporter(STDOUT, /*Temporality::CUMULATIVE*/), $clock); +$reader = new ExportingReader(new MetricExporter((new StreamTransportFactory())->create(STDOUT, 'application/x-ndjson'), /*Temporality::CUMULATIVE*/), $clock); // Let's imagine we export the metrics as Histogram, and to simplify the story we will only have one histogram bucket (-Inf, +Inf): $views = new CriteriaViewRegistry(); diff --git a/src/Contrib/Grpc/GrpcTransport.php b/src/Contrib/Grpc/GrpcTransport.php index e606d6091..3d277887d 100644 --- a/src/Contrib/Grpc/GrpcTransport.php +++ b/src/Contrib/Grpc/GrpcTransport.php @@ -25,12 +25,12 @@ use OpenTelemetry\SDK\Common\Future\FutureInterface; use OpenTelemetry\SDK\Common\Future\NullCancellation; use RuntimeException; -use function sprintf; use Throwable; -use UnexpectedValueException; /** * @internal + * + * @template-implements TransportInterface<"application/x-protobuf"> */ final class GrpcTransport implements TransportInterface { @@ -51,14 +51,16 @@ public function __construct( $this->headers = array_change_key_case($headers); } - public function send(string $payload, string $contentType, ?CancellationInterface $cancellation = null): FutureInterface + public function contentType(): string + { + return 'application/x-protobuf'; + } + + public function send(string $payload, ?CancellationInterface $cancellation = null): FutureInterface { if ($this->closed) { return new ErrorFuture(new BadMethodCallException('Transport closed')); } - if ($contentType !== 'application/x-protobuf') { - return new ErrorFuture(new UnexpectedValueException(sprintf('Unsupported content type "%s", grpc transport supports only application/x-protobuf', $contentType))); - } $call = new Call($this->channel, $this->method, Timeval::infFuture()); diff --git a/src/Contrib/Grpc/GrpcTransportFactory.php b/src/Contrib/Grpc/GrpcTransportFactory.php index 94d8b586b..a04262b6d 100644 --- a/src/Contrib/Grpc/GrpcTransportFactory.php +++ b/src/Contrib/Grpc/GrpcTransportFactory.php @@ -20,8 +20,16 @@ final class GrpcTransportFactory implements TransportFactoryInterface { + /** + * @psalm-param "application/x-protobuf" $contentType + * @psalm-return TransportInterface<"application/x-protobuf"> + * + * @psalm-suppress MoreSpecificImplementedParamType + * @psalm-suppress ImplementedReturnTypeMismatch + */ public function create( string $endpoint, + string $contentType = 'application/x-protobuf', array $headers = [], $compression = null, float $timeout = 10., @@ -35,6 +43,10 @@ public function create( if (!isset($parts['scheme'], $parts['host'], $parts['path'])) { throw new InvalidArgumentException('Endpoint has to contain scheme, host and path'); } + /** @phpstan-ignore-next-line */ + if ($contentType !== 'application/x-protobuf') { + throw new InvalidArgumentException(sprintf('Unsupported content type "%s", grpc transport supports only application/x-protobuf', $contentType)); + } $scheme = $parts['scheme']; $method = $parts['path']; diff --git a/src/Contrib/Newrelic/Exporter.php b/src/Contrib/Newrelic/Exporter.php index 92a66da28..3db3816d4 100644 --- a/src/Contrib/Newrelic/Exporter.php +++ b/src/Contrib/Newrelic/Exporter.php @@ -49,7 +49,7 @@ public function __construct( SpanConverter $spanConverter = null, string $dataFormatVersion = Exporter::DATA_FORMAT_VERSION_DEFAULT ) { - $this->transport = (new PsrTransportFactory($client, $requestFactory, $streamFactory))->create($endpointUrl, [ + $this->transport = (new PsrTransportFactory($client, $requestFactory, $streamFactory))->create($endpointUrl, 'application/json', [ 'Api-Key' => $licenseKey, 'Data-Format' => 'newrelic', 'Data-Format-Version' => $dataFormatVersion, @@ -93,10 +93,10 @@ public static function fromConnectionString(string $endpointUrl, string $name, $ ); } - public function export(iterable $spans, ?CancellationInterface $cancellation = null): FutureInterface + public function export(iterable $batch, ?CancellationInterface $cancellation = null): FutureInterface { return $this->transport - ->send($this->serializeTrace($spans), 'application/json', $cancellation) + ->send($this->serializeTrace($batch), $cancellation) ->map(static fn (): bool => true) ->catch(static function (Throwable $throwable): bool { self::logError('Export failure', ['exception' => $throwable]); diff --git a/src/Contrib/Otlp/MetricExporter.php b/src/Contrib/Otlp/MetricExporter.php new file mode 100644 index 000000000..70eae0f67 --- /dev/null +++ b/src/Contrib/Otlp/MetricExporter.php @@ -0,0 +1,87 @@ + $transport + */ + public function __construct(TransportInterface $transport, $temporality = null) + { + $this->transport = $transport; + $this->serializer = ProtobufSerializer::forTransport($transport); + $this->temporality = $temporality; + } + + public function temporality(MetricMetadataInterface $metric) + { + return $this->temporality ?? $metric->temporality(); + } + + public function export(iterable $batch): bool + { + return $this->transport + ->send($this->serializer->serialize((new MetricConverter())->convert($batch))) + ->map(function (?string $payload): bool { + if ($payload === null) { + return true; + } + + $serviceResponse = new ExportMetricsServiceResponse(); + $this->serializer->hydrate($serviceResponse, $payload); + + $partialSuccess = $serviceResponse->getPartialSuccess(); + if ($partialSuccess !== null && $partialSuccess->getRejectedDataPoints()) { + self::logError('Export partial success', [ + 'rejected_data_points' => $partialSuccess->getRejectedDataPoints(), + 'error_message' => $partialSuccess->getErrorMessage(), + ]); + + return false; + } + if ($partialSuccess !== null && $partialSuccess->getErrorMessage()) { + self::logWarning('Export success with warnings/suggestions', ['error_message' => $partialSuccess->getErrorMessage()]); + } + + return true; + }) + ->catch(static function (Throwable $throwable): bool { + self::logError('Export failure', ['exception' => $throwable]); + + return false; + }) + ->await(); + } + + public function shutdown(): bool + { + return $this->transport->shutdown(); + } + + public function forceFlush(): bool + { + return $this->transport->forceFlush(); + } +} diff --git a/src/Contrib/Otlp/ProtobufSerializer.php b/src/Contrib/Otlp/ProtobufSerializer.php new file mode 100644 index 000000000..94a93f90d --- /dev/null +++ b/src/Contrib/Otlp/ProtobufSerializer.php @@ -0,0 +1,76 @@ +contentType = $contentType; + } + + /** + * @param TransportInterface $transport + */ + public static function forTransport(TransportInterface $transport): ProtobufSerializer + { + switch ($contentType = $transport->contentType()) { + case self::PROTOBUF: + case self::JSON: + case self::NDJSON: + return new self($contentType); + default: + throw new InvalidArgumentException(sprintf('Not supported content type "%s"', $contentType)); + } + } + + public function serialize(Message $message): string + { + switch ($this->contentType) { + case self::PROTOBUF: + return $message->serializeToString(); + case self::JSON: + return $message->serializeToJsonString(); + case self::NDJSON: + return $message->serializeToJsonString() . "\n"; + default: + throw new AssertionError(); + } + } + + public function hydrate(Message $message, string $payload): void + { + switch ($this->contentType) { + case self::PROTOBUF: + $message->mergeFromString($payload); + + break; + case self::JSON: + case self::NDJSON: + $message->mergeFromJsonString($payload); + + break; + default: + throw new AssertionError(); + } + } +} diff --git a/src/Contrib/Otlp/SpanExporter.php b/src/Contrib/Otlp/SpanExporter.php new file mode 100644 index 000000000..8aad0b7fc --- /dev/null +++ b/src/Contrib/Otlp/SpanExporter.php @@ -0,0 +1,77 @@ + $transport + */ + public function __construct(TransportInterface $transport) + { + $this->transport = $transport; + $this->serializer = ProtobufSerializer::forTransport($transport); + } + + public function export(iterable $batch, ?CancellationInterface $cancellation = null): FutureInterface + { + return $this->transport + ->send($this->serializer->serialize((new SpanConverter())->convert($batch)), $cancellation) + ->map(function (?string $payload): bool { + if ($payload === null) { + return true; + } + + $serviceResponse = new ExportTraceServiceResponse(); + $this->serializer->hydrate($serviceResponse, $payload); + + $partialSuccess = $serviceResponse->getPartialSuccess(); + if ($partialSuccess !== null && $partialSuccess->getRejectedSpans()) { + self::logError('Export partial success', [ + 'rejected_spans' => $partialSuccess->getRejectedSpans(), + 'error_message' => $partialSuccess->getErrorMessage(), + ]); + + return false; + } + if ($partialSuccess !== null && $partialSuccess->getErrorMessage()) { + self::logWarning('Export success with warnings/suggestions', ['error_message' => $partialSuccess->getErrorMessage()]); + } + + return true; + }) + ->catch(static function (Throwable $throwable): bool { + self::logError('Export failure', ['exception' => $throwable]); + + return false; + }); + } + + public function shutdown(?CancellationInterface $cancellation = null): bool + { + return $this->transport->shutdown($cancellation); + } + + public function forceFlush(?CancellationInterface $cancellation = null): bool + { + return $this->transport->forceFlush($cancellation); + } +} diff --git a/src/Contrib/Otlp/StreamMetricExporter.php b/src/Contrib/Otlp/StreamMetricExporter.php deleted file mode 100644 index 36ee82106..000000000 --- a/src/Contrib/Otlp/StreamMetricExporter.php +++ /dev/null @@ -1,74 +0,0 @@ -transport = is_string($stream) - ? (new StreamTransportFactory())->create($stream) - : new StreamTransport($stream); - $this->temporality = $temporality; - } - - public function temporality(MetricMetadataInterface $metric) - { - return $this->temporality ?? $metric->temporality(); - } - - public function export(iterable $batch): bool - { - $payload = (new MetricConverter())->convert($batch)->serializeToJsonString(); - $payload .= "\n"; - - return $this->transport - ->send($payload, 'application/x-ndjson') - ->map(static fn (): bool => true) - ->catch(static function (Throwable $throwable): bool { - self::logError('Export failure', ['exception' => $throwable]); - - return false; - }) - ->await(); - } - - public function shutdown(): bool - { - return $this->transport->shutdown(); - } - - public function forceFlush(): bool - { - return $this->transport->forceFlush(); - } -} diff --git a/src/Contrib/OtlpHttp/Exporter.php b/src/Contrib/OtlpHttp/Exporter.php index 64733f986..c2dfc3418 100644 --- a/src/Contrib/OtlpHttp/Exporter.php +++ b/src/Contrib/OtlpHttp/Exporter.php @@ -8,40 +8,27 @@ use Http\Discovery\Psr18ClientDiscovery; use InvalidArgumentException; use OpenTelemetry\API\Common\Signal\Signals; -use OpenTelemetry\Contrib\Otlp\SpanConverter; -use Opentelemetry\Proto\Collector\Trace\V1\ExportTraceServiceResponse; -use OpenTelemetry\SDK\Behavior\LogsMessagesTrait; +use OpenTelemetry\Contrib\Otlp\SpanExporter; use OpenTelemetry\SDK\Common\Environment\EnvironmentVariablesTrait; use OpenTelemetry\SDK\Common\Environment\Variables as Env; use OpenTelemetry\SDK\Common\Export\Http\PsrTransportFactory; use OpenTelemetry\SDK\Common\Export\TransportFactoryInterface; use OpenTelemetry\SDK\Common\Export\TransportInterface; -use OpenTelemetry\SDK\Common\Future\CancellationInterface; -use OpenTelemetry\SDK\Common\Future\FutureInterface; use OpenTelemetry\SDK\Common\Otlp\HttpEndpointResolver; use OpenTelemetry\SDK\Trace\SpanExporterInterface; use function sprintf; -use Throwable; -class Exporter implements SpanExporterInterface +class Exporter { - use LogsMessagesTrait; - private const DEFAULT_ENDPOINT = 'https://localhost:4318'; private const DEFAULT_COMPRESSION = 'none'; private const OTLP_PROTOCOL = 'http/protobuf'; - private TransportInterface $transport; - /** - * Exporter constructor. + * @internal + * + * @psalm-return TransportInterface<"application/x-protobuf"> */ - public function __construct(TransportInterface $transport) - { - $this->transport = $transport; - } - - /** @internal */ public static function createTransport( TransportFactoryInterface $transportFactory ): TransportInterface { @@ -77,6 +64,7 @@ public static function createTransport( return $transportFactory->create( $endpoint, + 'application/x-protobuf', $headers, $compression, 10, @@ -85,52 +73,12 @@ public static function createTransport( ); } - public static function fromConnectionString(string $endpointUrl = null, string $name = null, $args = null): Exporter + public static function fromConnectionString(string $endpointUrl = null, string $name = null, $args = null): SpanExporterInterface { - return new Exporter(self::createTransport(new PsrTransportFactory( + return new SpanExporter(self::createTransport(new PsrTransportFactory( Psr18ClientDiscovery::find(), Psr17FactoryDiscovery::findRequestFactory(), Psr17FactoryDiscovery::findStreamFactory(), ))); } - - public function export(iterable $spans, ?CancellationInterface $cancellation = null): FutureInterface - { - return $this->transport - ->send((new SpanConverter())->convert($spans)->serializeToString(), 'application/x-protobuf', $cancellation) - ->map(static function (string $payload): bool { - $serviceResponse = new ExportTraceServiceResponse(); - $serviceResponse->mergeFromString($payload); - - $partialSuccess = $serviceResponse->getPartialSuccess(); - if ($partialSuccess !== null && $partialSuccess->getRejectedSpans()) { - self::logError('Export partial success', [ - 'rejected_spans' => $partialSuccess->getRejectedSpans(), - 'error_message' => $partialSuccess->getErrorMessage(), - ]); - - return false; - } - if ($partialSuccess !== null && $partialSuccess->getErrorMessage()) { - self::logWarning('Export success with warnings/suggestions', ['error_message' => $partialSuccess->getErrorMessage()]); - } - - return true; - }) - ->catch(static function (Throwable $throwable): bool { - self::logError('Export failure', ['exception' => $throwable]); - - return false; - }); - } - - public function shutdown(?CancellationInterface $cancellation = null): bool - { - return $this->transport->shutdown($cancellation); - } - - public function forceFlush(?CancellationInterface $cancellation = null): bool - { - return $this->transport->forceFlush($cancellation); - } } diff --git a/src/Contrib/OtlpHttp/MetricExporter.php b/src/Contrib/OtlpHttp/MetricExporter.php index 7254396ad..b13a4d0ad 100644 --- a/src/Contrib/OtlpHttp/MetricExporter.php +++ b/src/Contrib/OtlpHttp/MetricExporter.php @@ -4,35 +4,15 @@ namespace OpenTelemetry\Contrib\OtlpHttp; -use OpenTelemetry\Contrib\Otlp\MetricConverter; -use Opentelemetry\Proto\Collector\Metrics\V1\ExportMetricsServiceResponse; -use OpenTelemetry\SDK\Behavior\LogsMessagesTrait; use OpenTelemetry\SDK\Common\Export\Http\PsrTransportFactory; -use OpenTelemetry\SDK\Common\Export\TransportInterface; use OpenTelemetry\SDK\Metrics\Data\Temporality; use OpenTelemetry\SDK\Metrics\MetricExporterInterface; -use OpenTelemetry\SDK\Metrics\MetricMetadataInterface; use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestFactoryInterface; use Psr\Http\Message\StreamFactoryInterface; -use Throwable; -final class MetricExporter implements MetricExporterInterface +final class MetricExporter { - use LogsMessagesTrait; - - private TransportInterface $transport; - private $temporality; - - /** - * @param string|Temporality|null $temporality - */ - public function __construct(TransportInterface $transport, $temporality = null) - { - $this->transport = $transport; - $this->temporality = $temporality; - } - /** * @param string $endpoint endpoint to connect to * @param array $headers headers to set @@ -52,9 +32,10 @@ public static function create( int $maxRetries = 1, $temporality = null ): MetricExporterInterface { - return new self( + return new \OpenTelemetry\Contrib\Otlp\MetricExporter( (new PsrTransportFactory($client, $requestFactory, $streamFactory))->create( $endpoint, + 'application/x-protobuf', $headers, $compression, $retryDelay, @@ -63,50 +44,4 @@ public static function create( $temporality, ); } - - public function temporality(MetricMetadataInterface $metric) - { - return $this->temporality ?? $metric->temporality(); - } - - public function export(iterable $batch): bool - { - return $this->transport - ->send((new MetricConverter())->convert($batch)->serializeToString(), 'application/x-protobuf') - ->map(static function (string $payload): bool { - $serviceResponse = new ExportMetricsServiceResponse(); - $serviceResponse->mergeFromString($payload); - - $partialSuccess = $serviceResponse->getPartialSuccess(); - if ($partialSuccess !== null && $partialSuccess->getRejectedDataPoints()) { - self::logError('Export partial success', [ - 'rejected_data_points' => $partialSuccess->getRejectedDataPoints(), - 'error_message' => $partialSuccess->getErrorMessage(), - ]); - - return false; - } - if ($partialSuccess !== null && $partialSuccess->getErrorMessage()) { - self::logWarning('Export success with warnings/suggestions', ['error_message' => $partialSuccess->getErrorMessage()]); - } - - return true; - }) - ->catch(static function (Throwable $throwable): bool { - self::logError('Export failure', ['exception' => $throwable]); - - return false; - }) - ->await(); - } - - public function shutdown(): bool - { - return $this->transport->shutdown(); - } - - public function forceFlush(): bool - { - return $this->transport->forceFlush(); - } } diff --git a/src/Contrib/Zipkin/Exporter.php b/src/Contrib/Zipkin/Exporter.php index 2507dc82e..e251a033b 100644 --- a/src/Contrib/Zipkin/Exporter.php +++ b/src/Contrib/Zipkin/Exporter.php @@ -39,7 +39,7 @@ public function __construct( StreamFactoryInterface $streamFactory, SpanConverterInterface $spanConverter = null ) { - $this->transport = (new PsrTransportFactory($client, $requestFactory, $streamFactory))->create($endpointUrl); + $this->transport = (new PsrTransportFactory($client, $requestFactory, $streamFactory))->create($endpointUrl, 'application/json'); $this->setSpanConverter($spanConverter ?? new SpanConverter($name)); } @@ -66,10 +66,10 @@ public static function fromConnectionString(string $endpointUrl, string $name, $ ); } - public function export(iterable $spans, ?CancellationInterface $cancellation = null): FutureInterface + public function export(iterable $batch, ?CancellationInterface $cancellation = null): FutureInterface { return $this->transport - ->send($this->serializeTrace($spans), 'application/json', $cancellation) + ->send($this->serializeTrace($batch), $cancellation) ->map(static fn (): bool => true) ->catch(static function (Throwable $throwable): bool { self::logError('Export failure', ['exception' => $throwable]); diff --git a/src/Contrib/ZipkinToNewrelic/Exporter.php b/src/Contrib/ZipkinToNewrelic/Exporter.php index af27c481a..b8d6f1b42 100644 --- a/src/Contrib/ZipkinToNewrelic/Exporter.php +++ b/src/Contrib/ZipkinToNewrelic/Exporter.php @@ -44,7 +44,7 @@ public function __construct( StreamFactoryInterface $streamFactory, SpanConverter $spanConverter = null ) { - $this->transport = (new PsrTransportFactory($client, $requestFactory, $streamFactory))->create($endpointUrl, [ + $this->transport = (new PsrTransportFactory($client, $requestFactory, $streamFactory))->create($endpointUrl, 'application/json', [ 'Api-Key' => $licenseKey, 'Data-Format' => 'zipkin', 'Data-Format-Version' => '2', @@ -80,10 +80,10 @@ public static function fromConnectionString(string $endpointUrl, string $name, $ ); } - public function export(iterable $spans, ?CancellationInterface $cancellation = null): FutureInterface + public function export(iterable $batch, ?CancellationInterface $cancellation = null): FutureInterface { return $this->transport - ->send($this->serializeTrace($spans), 'application/json', $cancellation) + ->send($this->serializeTrace($batch), $cancellation) ->map(static fn (): bool => true) ->catch(static function (Throwable $throwable): bool { self::logError('Export failure', ['exception' => $throwable]); diff --git a/src/SDK/Common/Export/Http/PsrTransport.php b/src/SDK/Common/Export/Http/PsrTransport.php index b702a201d..e84dde388 100644 --- a/src/SDK/Common/Export/Http/PsrTransport.php +++ b/src/SDK/Common/Export/Http/PsrTransport.php @@ -24,6 +24,10 @@ use function time_nanosleep; use function trim; +/** + * @psalm-template CONTENT_TYPE of string + * @template-implements TransportInterface + */ final class PsrTransport implements TransportInterface { private ClientInterface $client; @@ -31,6 +35,7 @@ final class PsrTransport implements TransportInterface private StreamFactoryInterface $streamFactory; private string $endpoint; + private string $contentType; private array $headers; private array $compression; private int $retryDelay; @@ -38,11 +43,15 @@ final class PsrTransport implements TransportInterface private bool $closed = false; + /** + * @psalm-param CONTENT_TYPE $contentType + */ public function __construct( ClientInterface $client, RequestFactoryInterface $requestFactory, StreamFactoryInterface $streamFactory, string $endpoint, + string $contentType, array $headers, array $compression, int $retryDelay, @@ -52,13 +61,19 @@ public function __construct( $this->requestFactory = $requestFactory; $this->streamFactory = $streamFactory; $this->endpoint = $endpoint; + $this->contentType = $contentType; $this->headers = $headers; $this->compression = $compression; $this->retryDelay = $retryDelay; $this->maxRetries = $maxRetries; } - public function send(string $payload, string $contentType, ?CancellationInterface $cancellation = null): FutureInterface + public function contentType(): string + { + return $this->contentType; + } + + public function send(string $payload, ?CancellationInterface $cancellation = null): FutureInterface { if ($this->closed) { return new ErrorFuture(new BadMethodCallException('Transport closed')); @@ -68,7 +83,7 @@ public function send(string $payload, string $contentType, ?CancellationInterfac $request = $this->requestFactory ->createRequest('POST', $this->endpoint) ->withBody($this->streamFactory->createStream($body)) - ->withHeader('Content-Type', $contentType) + ->withHeader('Content-Type', $this->contentType) ; if ($appliedEncodings) { $request = $request->withHeader('Content-Encoding', $appliedEncodings); diff --git a/src/SDK/Common/Export/Http/PsrTransportFactory.php b/src/SDK/Common/Export/Http/PsrTransportFactory.php index 3fc142761..b87485ce3 100644 --- a/src/SDK/Common/Export/Http/PsrTransportFactory.php +++ b/src/SDK/Common/Export/Http/PsrTransportFactory.php @@ -31,6 +31,7 @@ public function __construct( public function create( string $endpoint, + string $contentType, array $headers = [], $compression = null, float $timeout = 10., @@ -49,6 +50,7 @@ public function create( $this->requestFactory, $this->streamFactory, $endpoint, + $contentType, $headers, (array) $compression, $retryDelay, diff --git a/src/SDK/Common/Export/Stream/StreamTransport.php b/src/SDK/Common/Export/Stream/StreamTransport.php index d3723bb1f..4b99cf756 100644 --- a/src/SDK/Common/Export/Stream/StreamTransport.php +++ b/src/SDK/Common/Export/Stream/StreamTransport.php @@ -20,25 +20,36 @@ use Throwable; /** - * @psalm-internal \OpenTelemetry + * @internal + * + * @psalm-template CONTENT_TYPE of string + * @template-implements TransportInterface */ final class StreamTransport implements TransportInterface { - /** * @var resource|null */ private $stream; + private string $contentType; /** * @param resource $stream + * + * @psalm-param CONTENT_TYPE $contentType */ - public function __construct($stream) + public function __construct($stream, string $contentType) { $this->stream = $stream; + $this->contentType = $contentType; + } + + public function contentType(): string + { + return $this->contentType; } - public function send(string $payload, string $contentType, ?CancellationInterface $cancellation = null): FutureInterface + public function send(string $payload, ?CancellationInterface $cancellation = null): FutureInterface { if (!$this->stream) { return new ErrorFuture(new BadMethodCallException('Transport closed')); diff --git a/src/SDK/Common/Export/Stream/StreamTransportFactory.php b/src/SDK/Common/Export/Stream/StreamTransportFactory.php index 86f822f0c..7b7d0b458 100644 --- a/src/SDK/Common/Export/Stream/StreamTransportFactory.php +++ b/src/SDK/Common/Export/Stream/StreamTransportFactory.php @@ -7,6 +7,7 @@ use ErrorException; use function fopen; use function implode; +use function is_resource; use LogicException; use OpenTelemetry\SDK\Common\Export\TransportFactoryInterface; use OpenTelemetry\SDK\Common\Export\TransportInterface; @@ -20,8 +21,18 @@ */ final class StreamTransportFactory implements TransportFactoryInterface { + /** + * @param string|resource $endpoint + * @param array $headers + * @param string|string[]|null $compression + * + * @psalm-template CONTENT_TYPE of string + * @psalm-param CONTENT_TYPE $contentType + * @psalm-return TransportInterface + */ public function create( - string $endpoint, + $endpoint, + string $contentType, array $headers = [], $compression = null, float $timeout = 10., @@ -31,10 +42,34 @@ public function create( ?string $cert = null, ?string $key = null ): TransportInterface { + $stream = is_resource($endpoint) + ? $endpoint + : self::createStream( + $endpoint, + $contentType, + $headers, + $timeout, + $cacert, + $cert, + $key, + ); + + return new StreamTransport($stream, $contentType); + } + + private static function createStream( + $endpoint, + string $contentType, + array $headers = [], + float $timeout = 10., + ?string $cacert = null, + ?string $cert = null, + ?string $key = null + ) { $context = stream_context_create([ 'http' => [ 'method' => 'POST', - 'header' => self::createHeaderArray($headers), + 'header' => self::createHeaderArray($contentType, $headers), 'timeout' => $timeout, ], 'ssl' => [ @@ -58,14 +93,12 @@ public function create( if (!$stream) { throw new LogicException(sprintf('Failed opening stream "%s"', $endpoint)); } - - /** @phan-suppress-next-line PhanPossiblyUndeclaredVariable */ - return new StreamTransport($stream); } - private static function createHeaderArray(array $headers): array + private static function createHeaderArray(string $contentType, array $headers): array { $header = []; + $header[] = sprintf('Content-Type: %s', $contentType); foreach ($headers as $name => $value) { $header[] = sprintf('%s: %s', $name, implode(', ', (array) $value)); } diff --git a/src/SDK/Common/Export/TransportFactoryInterface.php b/src/SDK/Common/Export/TransportFactoryInterface.php index 357a28fa8..48e538443 100644 --- a/src/SDK/Common/Export/TransportFactoryInterface.php +++ b/src/SDK/Common/Export/TransportFactoryInterface.php @@ -11,11 +11,15 @@ interface TransportFactoryInterface public const COMPRESSION_BROTLI = 'br'; /** - * @param array $headers - * @param string|string[]|null $compression + * @psalm-template CONTENT_TYPE of string + * @psalm-param CONTENT_TYPE $contentType + * @psalm-param array $headers + * @psalm-param string|string[]|null $compression + * @psalm-return TransportInterface */ public function create( string $endpoint, + string $contentType, array $headers = [], $compression = null, float $timeout = 10., diff --git a/src/SDK/Common/Export/TransportInterface.php b/src/SDK/Common/Export/TransportInterface.php index fa5b8847b..5fb26eff8 100644 --- a/src/SDK/Common/Export/TransportInterface.php +++ b/src/SDK/Common/Export/TransportInterface.php @@ -7,9 +7,14 @@ use OpenTelemetry\SDK\Common\Future\CancellationInterface; use OpenTelemetry\SDK\Common\Future\FutureInterface; +/** + * @psalm-template-covariant CONTENT_TYPE of string + */ interface TransportInterface { - public function send(string $payload, string $contentType, ?CancellationInterface $cancellation = null): FutureInterface; + public function contentType(): string; + + public function send(string $payload, ?CancellationInterface $cancellation = null): FutureInterface; public function shutdown(?CancellationInterface $cancellation = null): bool; diff --git a/src/SDK/Trace/Behavior/SpanExporterDecoratorTrait.php b/src/SDK/Trace/Behavior/SpanExporterDecoratorTrait.php index 9dd2fda51..97839ec5b 100644 --- a/src/SDK/Trace/Behavior/SpanExporterDecoratorTrait.php +++ b/src/SDK/Trace/Behavior/SpanExporterDecoratorTrait.php @@ -14,14 +14,14 @@ trait SpanExporterDecoratorTrait protected SpanExporterInterface $decorated; /** - * @param iterable $spans + * @param iterable $batch * @return FutureInterface */ - public function export(iterable $spans, ?CancellationInterface $cancellation = null): FutureInterface + public function export(iterable $batch, ?CancellationInterface $cancellation = null): FutureInterface { - $spans = $this->beforeExport($spans); - $response = $this->decorated->export($spans, $cancellation); - $response->map(fn (bool $result) => $this->afterExport($spans, $result)); + $batch = $this->beforeExport($batch); + $response = $this->decorated->export($batch, $cancellation); + $response->map(fn (bool $result) => $this->afterExport($batch, $result)); return $response; } diff --git a/src/SDK/Trace/Behavior/SpanExporterTrait.php b/src/SDK/Trace/Behavior/SpanExporterTrait.php index 9d7bd10e4..bfd36c44d 100644 --- a/src/SDK/Trace/Behavior/SpanExporterTrait.php +++ b/src/SDK/Trace/Behavior/SpanExporterTrait.php @@ -30,16 +30,16 @@ public function forceFlush(?CancellationInterface $cancellation = null): bool abstract public static function fromConnectionString(string $endpointUrl, string $name, string $args); /** - * @param iterable $spans + * @param iterable $batch * @return FutureInterface */ - public function export(iterable $spans, ?CancellationInterface $cancellation = null): FutureInterface + public function export(iterable $batch, ?CancellationInterface $cancellation = null): FutureInterface { if (!$this->running) { return new CompletedFuture(false); } - return new CompletedFuture($this->doExport($spans)); /** @phpstan-ignore-line */ + return new CompletedFuture($this->doExport($batch)); /** @phpstan-ignore-line */ } /** diff --git a/src/SDK/Trace/SpanExporterInterface.php b/src/SDK/Trace/SpanExporterInterface.php index c4d0d6926..fca336896 100644 --- a/src/SDK/Trace/SpanExporterInterface.php +++ b/src/SDK/Trace/SpanExporterInterface.php @@ -12,16 +12,14 @@ */ interface SpanExporterInterface { - public static function fromConnectionString(string $endpointUrl, string $name, string $args); - /** - * @param iterable $spans Batch of spans to export + * @param iterable $batch Batch of spans to export * * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/specification/trace/sdk.md#exportbatch * * @psalm-return FutureInterface */ - public function export(iterable $spans, ?CancellationInterface $cancellation = null): FutureInterface; + public function export(iterable $batch, ?CancellationInterface $cancellation = null): FutureInterface; /** @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/specification/trace/sdk.md#shutdown-2 */ public function shutdown(?CancellationInterface $cancellation = null): bool; diff --git a/tests/Benchmark/OtlpBench.php b/tests/Benchmark/OtlpBench.php index b162b0de0..3dd2cab4b 100644 --- a/tests/Benchmark/OtlpBench.php +++ b/tests/Benchmark/OtlpBench.php @@ -7,6 +7,7 @@ use Grpc\UnaryCall; use Mockery; use OpenTelemetry\API\Trace\TracerInterface; +use OpenTelemetry\Contrib\Otlp\SpanExporter; use OpenTelemetry\Contrib\OtlpGrpc\Exporter as GrpcExporter; use OpenTelemetry\Contrib\OtlpHttp\Exporter as HttpExporter; use Opentelemetry\Proto\Collector\Trace\V1\TraceServiceClient; @@ -80,7 +81,7 @@ public function setUpGrpcHttp(): void $streamFactory = Mockery::mock(StreamFactoryInterface::class) ->allows(['createStream' => $stream]); // @phpstan-ignore-next-line - $exporter = new HttpExporter(HttpExporter::createTransport(new PsrTransportFactory($client, $requestFactory, $streamFactory))); + $exporter = new SpanExporter(HttpExporter::createTransport(new PsrTransportFactory($client, $requestFactory, $streamFactory))); $processor = new SimpleSpanProcessor($exporter); $provider = new TracerProvider($processor, $this->sampler, $this->resource); $this->tracer = $provider->getTracer('io.opentelemetry.contrib.php'); diff --git a/tests/Unit/API/Trace/Propagation/TraceContextPropagatorTest.php b/tests/Unit/API/Trace/Propagation/TraceContextPropagatorTest.php index ee8c48850..d2a1b39e0 100644 --- a/tests/Unit/API/Trace/Propagation/TraceContextPropagatorTest.php +++ b/tests/Unit/API/Trace/Propagation/TraceContextPropagatorTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenTelemetry\Tests\API\Unit\Trace\Propagation; +namespace OpenTelemetry\Tests\Unit\API\Trace\Propagation; use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator; use OpenTelemetry\API\Trace\SpanContext; @@ -16,7 +16,7 @@ use PHPUnit\Framework\TestCase; /** - * @covers OpenTelemetry\API\Trace\Propagation\TraceContextPropagator + * @covers \OpenTelemetry\API\Trace\Propagation\TraceContextPropagator */ class TraceContextPropagatorTest extends TestCase { diff --git a/tests/Unit/API/Trace/Propagation/TraceContextValidatorTest.php b/tests/Unit/API/Trace/Propagation/TraceContextValidatorTest.php index 09f39ddfa..4ae3b8038 100644 --- a/tests/Unit/API/Trace/Propagation/TraceContextValidatorTest.php +++ b/tests/Unit/API/Trace/Propagation/TraceContextValidatorTest.php @@ -2,13 +2,13 @@ declare(strict_types=1); -namespace OpenTelemetry\Tests\API\Unit\Trace; +namespace OpenTelemetry\Tests\Unit\API\Trace\Propagation; use OpenTelemetry\API\Trace\Propagation\TraceContextValidator; use PHPUnit\Framework\TestCase; /** - * @covers OpenTelemetry\API\Trace\TraceContextValidator + * @covers \OpenTelemetry\API\Trace\Propagation\TraceContextValidator */ class TraceContextValidatorTest extends TestCase { diff --git a/tests/Unit/Contrib/Grpc/GrpcTransportTest.php b/tests/Unit/Contrib/Grpc/GrpcTransportTest.php index ca985a4f5..20de892ca 100644 --- a/tests/Unit/Contrib/Grpc/GrpcTransportTest.php +++ b/tests/Unit/Contrib/Grpc/GrpcTransportTest.php @@ -5,9 +5,9 @@ namespace OpenTelemetry\Tests\Unit\Contrib\Grpc; use Exception; +use InvalidArgumentException; use OpenTelemetry\Contrib\Grpc\GrpcTransportFactory; use PHPUnit\Framework\TestCase; -use UnexpectedValueException; /** * @covers \OpenTelemetry\Contrib\Grpc\GrpcTransportFactory @@ -18,12 +18,10 @@ final class GrpcTransportTest extends TestCase public function test_grpc_transport_supports_only_protobuf(): void { $factory = new GrpcTransportFactory(); - $transport = $factory->create('http://localhost/service/method'); - - $response = $transport->send('', 'text/plain'); - $this->expectException(UnexpectedValueException::class); - $response->await(); + $this->expectException(InvalidArgumentException::class); + /** @psalm-suppress InvalidArgument @phpstan-ignore-next-line */ + $factory->create('http://localhost/service/method', 'text/plain'); } public function test_shutdown_returns_true(): void @@ -48,7 +46,7 @@ public function test_send_closed_returns_error(): void $transport = $factory->create('http://localhost/service/method'); $transport->shutdown(); - $response = $transport->send('', 'application/x-protobuf'); + $response = $transport->send(''); $this->expectException(Exception::class); $response->await(); diff --git a/tests/Unit/Contrib/Otlp/StreamMetricExporterTest.php b/tests/Unit/Contrib/Otlp/StreamMetricExporterTest.php deleted file mode 100644 index 7e01dd45e..000000000 --- a/tests/Unit/Contrib/Otlp/StreamMetricExporterTest.php +++ /dev/null @@ -1,87 +0,0 @@ -export([ - new Metric( - new InstrumentationScope('test', null, null, Attributes::create([])), - ResourceInfoFactory::emptyResource(), - 'test', - null, - null, - new Sum([ - new NumberDataPoint(5, Attributes::create([]), 17, 42), - ], Temporality::DELTA, false) - ), - ]); - - fseek($stream, 0); - $this->assertSame(<<export([ - new Metric( - new InstrumentationScope('test', null, null, Attributes::create([])), - ResourceInfoFactory::emptyResource(), - 'test', - null, - null, - new Sum([ - new NumberDataPoint(5, Attributes::create([]), 17, 42), - ], Temporality::DELTA, false) - ), - ]); - $exporter->export([ - new Metric( - new InstrumentationScope('test', null, null, Attributes::create([])), - ResourceInfoFactory::emptyResource(), - 'test', - null, - null, - new Sum([ - new NumberDataPoint(7, Attributes::create([]), 42, 57), - ], Temporality::DELTA, false) - ), - ]); - - fseek($stream, 0); - $this->assertSame(<<getClientInterfaceMock(), $this->getRequestFactoryInterfaceMock(), $this->getStreamFactoryInterfaceMock(), @@ -57,7 +58,7 @@ public function test_exporter_response_status($responseStatus, $expected): void new Response($responseStatus, []) ); /** @var ClientInterface $client */ - $exporter = new Exporter(Exporter::createTransport(new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()))); + $exporter = new SpanExporter(Exporter::createTransport(new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()))); $this->assertEquals( $expected, @@ -88,7 +89,7 @@ public function test_client_exceptions_should_decide_return_code($exception, $ex $client->method('sendRequest')->willThrowException($exception); /** @var ClientInterface $client */ - $exporter = new Exporter(Exporter::createTransport(new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()))); + $exporter = new SpanExporter(Exporter::createTransport(new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()))); $this->assertEquals( min($expected, 1), @@ -129,7 +130,7 @@ public function test_exporter_with_config_via_env_vars(?string $endpoint, string $this->setEnvironmentVariable(Env::OTEL_EXPORTER_OTLP_COMPRESSION, 'gzip'); $client = new Client(['handler' => $stack]); - $exporter = new Exporter(Exporter::createTransport(new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()))); + $exporter = new SpanExporter(Exporter::createTransport(new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()))); $exporter->export([new SpanData()])->await(); @@ -164,7 +165,7 @@ public function test_should_be_ok_to_exporter_empty_spans_collection(): void $client->method('sendRequest')->willReturn(new Response(200)); $this->assertTrue( - (new Exporter(Exporter::createTransport(new PsrTransportFactory( + (new SpanExporter(Exporter::createTransport(new PsrTransportFactory( $client, new HttpFactory(), new HttpFactory(), @@ -183,7 +184,7 @@ public function test_fails_exporter_refuses_otlp_json(): void $this->expectException(\InvalidArgumentException::class); - new Exporter(Exporter::createTransport(new PsrTransportFactory( + new SpanExporter(Exporter::createTransport(new PsrTransportFactory( $this->getClientInterfaceMock(), $this->getRequestFactoryInterfaceMock(), $this->getStreamFactoryInterfaceMock(), @@ -201,7 +202,7 @@ public function test_exporter_refuses_invalid_endpoint($endpoint): void $this->expectException(\InvalidArgumentException::class); - new Exporter(Exporter::createTransport(new PsrTransportFactory( + new SpanExporter(Exporter::createTransport(new PsrTransportFactory( $this->getClientInterfaceMock(), $this->getRequestFactoryInterfaceMock(), $this->getStreamFactoryInterfaceMock(), diff --git a/tests/Unit/SDK/Common/Export/Http/PsrTransportTest.php b/tests/Unit/SDK/Common/Export/Http/PsrTransportTest.php index c458b4293..b6cb8b392 100644 --- a/tests/Unit/SDK/Common/Export/Http/PsrTransportTest.php +++ b/tests/Unit/SDK/Common/Export/Http/PsrTransportTest.php @@ -30,7 +30,7 @@ public function test_invalid_endpoint_throws_exception(): void $client = $this->createMock(ClientInterface::class); $factory = new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()); - $factory->create('localhost'); + $factory->create('localhost', 'text/plain'); } public function test_send_propagates_body_and_content_type(): void @@ -43,9 +43,9 @@ public function test_send_propagates_body_and_content_type(): void return new Response(); }); $factory = new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()); - $transport = $factory->create('http://localhost'); + $transport = $factory->create('http://localhost', 'text/plain'); - $transport->send('abc', 'text/plain'); + $transport->send('abc'); } public function test_send_applies_compression(): void @@ -58,9 +58,9 @@ public function test_send_applies_compression(): void return new Response(); }); $factory = new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()); - $transport = $factory->create('http://localhost', [], 'gzip'); + $transport = $factory->create('http://localhost', 'text/plain', [], 'gzip'); - $transport->send('abc', 'text/plain'); + $transport->send('abc'); } public function test_send_sets_headers(): void @@ -72,9 +72,9 @@ public function test_send_sets_headers(): void return new Response(); }); $factory = new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()); - $transport = $factory->create('http://localhost', ['x-foo' => 'bar']); + $transport = $factory->create('http://localhost', 'text/plain', ['x-foo' => 'bar']); - $transport->send('abc', 'text/plain'); + $transport->send('abc'); } public function test_send_returns_response_body(): void @@ -82,9 +82,9 @@ public function test_send_returns_response_body(): void $client = $this->createMock(ClientInterface::class); $client->expects($this->once())->method('sendRequest')->willReturn(new Response(200, [], 'abc')); $factory = new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()); - $transport = $factory->create('http://localhost'); + $transport = $factory->create('http://localhost', 'text/plain'); - $this->assertSame('abc', $transport->send('', 'text/plain')->await()); + $this->assertSame('abc', $transport->send('')->await()); } public function test_send_decodes_response_body(): void @@ -92,9 +92,9 @@ public function test_send_decodes_response_body(): void $client = $this->createMock(ClientInterface::class); $client->expects($this->once())->method('sendRequest')->willReturn(new Response(200, ['Content-Encoding' => 'gzip'], gzencode('abc'))); $factory = new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()); - $transport = $factory->create('http://localhost'); + $transport = $factory->create('http://localhost', 'text/plain'); - $this->assertSame('abc', $transport->send('', 'text/plain')->await()); + $this->assertSame('abc', $transport->send('')->await()); } public function test_send_decode_unknown_encoding_returns_error(): void @@ -102,9 +102,9 @@ public function test_send_decode_unknown_encoding_returns_error(): void $client = $this->createMock(ClientInterface::class); $client->expects($this->once())->method('sendRequest')->willReturn(new Response(200, ['Content-Encoding' => 'invalid'], '')); $factory = new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()); - $transport = $factory->create('http://localhost'); + $transport = $factory->create('http://localhost', 'text/plain'); - $response = $transport->send('', 'text/plain'); + $response = $transport->send(''); $this->expectException(Exception::class); $response->await(); @@ -115,9 +115,9 @@ public function test_send_status_code4xx_returns_error(): void $client = $this->createMock(ClientInterface::class); $client->expects($this->once())->method('sendRequest')->willReturn(new Response(403)); $factory = new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()); - $transport = $factory->create('http://localhost'); + $transport = $factory->create('http://localhost', 'text/plain'); - $response = $transport->send('', 'text/plain'); + $response = $transport->send(''); $this->expectException(Exception::class); $response->await(); @@ -131,9 +131,9 @@ public function test_send_status_code5xx_retries(): void new Response(200, [], 'abc') ); $factory = new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()); - $transport = $factory->create('http://localhost', [], null, 10., 1); + $transport = $factory->create('http://localhost', 'text/plain', [], null, 10., 1); - $this->assertSame('abc', $transport->send('', 'text/plain')->await()); + $this->assertSame('abc', $transport->send('')->await()); } public function test_send_returns_error_if_retry_limit_exceeded(): void @@ -141,9 +141,9 @@ public function test_send_returns_error_if_retry_limit_exceeded(): void $client = $this->createMock(ClientInterface::class); $client->expects($this->once())->method('sendRequest')->willReturn(new Response(500)); $factory = new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()); - $transport = $factory->create('http://localhost', [], null, 10., 100, 1); + $transport = $factory->create('http://localhost', 'text/plain', [], null, 10., 100, 1); - $response = $transport->send('', 'text/plain'); + $response = $transport->send(''); $this->expectException(Exception::class); $this->expectExceptionMessage('retry limit'); @@ -155,9 +155,9 @@ public function test_send_exception_returns_error(): void $client = $this->createMock(ClientInterface::class); $client->expects($this->once())->method('sendRequest')->willThrowException(new Exception()); $factory = new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()); - $transport = $factory->create('http://localhost'); + $transport = $factory->create('http://localhost', 'text/plain'); - $response = $transport->send('', 'text/plain'); + $response = $transport->send(''); $this->expectException(Exception::class); $response->await(); @@ -167,7 +167,7 @@ public function test_shutdown_returns_true(): void { $client = $this->createMock(ClientInterface::class); $factory = new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()); - $transport = $factory->create('http://localhost'); + $transport = $factory->create('http://localhost', 'text/plain'); $this->assertTrue($transport->shutdown()); } @@ -176,7 +176,7 @@ public function test_force_flush_returns_true(): void { $client = $this->createMock(ClientInterface::class); $factory = new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()); - $transport = $factory->create('http://localhost'); + $transport = $factory->create('http://localhost', 'text/plain'); $this->assertTrue($transport->forceFlush()); } @@ -185,10 +185,10 @@ public function test_send_closed_returns_error(): void { $client = $this->createMock(ClientInterface::class); $factory = new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()); - $transport = $factory->create('http://localhost'); + $transport = $factory->create('http://localhost', 'text/plain'); $transport->shutdown(); - $response = $transport->send('', 'text/plain'); + $response = $transport->send(''); $this->expectException(Exception::class); $response->await(); @@ -198,7 +198,7 @@ public function test_shutdown_closed_returns_false(): void { $client = $this->createMock(ClientInterface::class); $factory = new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()); - $transport = $factory->create('http://localhost'); + $transport = $factory->create('http://localhost', 'text/plain'); $transport->shutdown(); $this->assertFalse($transport->shutdown()); @@ -208,7 +208,7 @@ public function test_force_flush_closed_returns_false(): void { $client = $this->createMock(ClientInterface::class); $factory = new PsrTransportFactory($client, new HttpFactory(), new HttpFactory()); - $transport = $factory->create('http://localhost'); + $transport = $factory->create('http://localhost', 'text/plain'); $transport->shutdown(); $this->assertFalse($transport->forceFlush()); diff --git a/tests/Unit/SDK/Trace/ExporterFactoryTest.php b/tests/Unit/SDK/Trace/ExporterFactoryTest.php index 1d153c810..21f22f650 100644 --- a/tests/Unit/SDK/Trace/ExporterFactoryTest.php +++ b/tests/Unit/SDK/Trace/ExporterFactoryTest.php @@ -47,7 +47,7 @@ public function endpointProvider(): array 'zipkin' => ['test.zipkin', 'zipkin+http://zipkin:9411/api/v2/spans', Contrib\Zipkin\Exporter::class], 'jaeger' => ['test.jaeger', 'jaeger+http://jaeger:9412/api/v2/spans', Contrib\Jaeger\Exporter::class], 'newrelic' => ['rest.newrelic', 'newrelic+https://trace-api.newrelic.com/trace/v1?licenseKey=abc23423423', Contrib\Newrelic\Exporter::class], - 'otlp+http' => ['test.otlp', 'otlp+http', Contrib\OtlpHttp\Exporter::class], + 'otlp+http' => ['test.otlp', 'otlp+http', Contrib\Otlp\SpanExporter::class], 'otlp+grpc' => ['test.otlpgrpc', 'otlp+grpc://otlp:4317', Contrib\OtlpGrpc\Exporter::class], 'zipkintonewrelic' => ['test.zipkintonewrelic', 'zipkintonewrelic+https://trace-api.newrelic.com/trace/v1?licenseKey=abc23423423', Contrib\ZipkinToNewrelic\Exporter::class], 'console' => ['test.console', 'console', ConsoleSpanExporter::class], @@ -106,12 +106,12 @@ public function envProvider(): array 'otlp+http/protobuf from traces protocol' => [ 'otlp', ['OTEL_EXPORTER_OTLP_TRACES_PROTOCOL' => 'http/protobuf'], - Contrib\OtlpHttp\Exporter::class, + Contrib\Otlp\SpanExporter::class, ], 'otlp+http/protobuf from protocol' => [ 'otlp', ['OTEL_EXPORTER_OTLP_PROTOCOL' => 'http/protobuf'], - Contrib\OtlpHttp\Exporter::class, + Contrib\Otlp\SpanExporter::class, ], 'otlp+grpc from traces protocol' => [ 'otlp',