From f55e887fadaaeb5a4ea922d1ed5c052ab69b219c Mon Sep 17 00:00:00 2001 From: Ritick Gautam Date: Wed, 4 Nov 2020 14:40:45 +0530 Subject: [PATCH 01/13] Added OTLP/HTTP JSON Exporter Signed-off-by:Ritick Gautam --- contrib/otlp/Exporter.php | 162 +++++++++++++++++++++++++++++++ contrib/otlp/SpanConverter.php | 76 +++++++++++++++ examples/AlwaysOnOTLPExample.php | 65 +++++++++++++ 3 files changed, 303 insertions(+) create mode 100644 contrib/otlp/Exporter.php create mode 100644 contrib/otlp/SpanConverter.php create mode 100644 examples/AlwaysOnOTLPExample.php diff --git a/contrib/otlp/Exporter.php b/contrib/otlp/Exporter.php new file mode 100644 index 000000000..94b69c28d --- /dev/null +++ b/contrib/otlp/Exporter.php @@ -0,0 +1,162 @@ +endpointURL = getenv("OTEL_EXPORTER_OTLP_ENDPOINT") ?: "localhost:55680"; + $this->protocol = getenv("OTEL_EXPORTER_OTLP_PROTOCOL") ?: "grpc"; + $this->insecure = getenv("OTEL_EXPORTER_OTLP_INSECURE") ?: "false"; + $this->certificateFile = getenv("OTEL_EXPORTER_OTLP_CERTIFICATE") ?: "none"; + $this->headers[] = getenv("OTEL_EXPORTER_OTLP_HEADERS") ?: "none"; + $this->compression = getenv("OTEL_EXPORTER_OTLP_COMPRESSION") ?: "none"; + $this->timeout = getenv("OTEL_EXPORTER_OTLP_TIMEOUT") ?: 10; + + + $this->client = $this->createDefaultClient(); + $this->spanConverter = new SpanConverter($serviceName); + } + + /** + * Exports the provided Span data via the OTLP protocol + * + * @param iterable $spans Array of Spans + * @return int return code, defined on the Exporter interface + */ + public function export(iterable $spans): int + { + if (empty($spans)) { + return Trace\Exporter::SUCCESS; + } + + $convertedSpans = []; + foreach ($spans as $span) { + array_push($convertedSpans, $this->spanConverter->convert($span)); + } + + try { + $json = json_encode($convertedSpans); + + $this->headers[] = ''; + + if($this->protocol == "grpc") { + $headers = ['content-type' => 'application/x-protobuf']; + } + + else if($this->protocol == "json") { + $headers = ['content-type' => 'application/json', "Content-Encoding" => "gzip"]; + + } + + $request = new Request('POST', $this->endpointURL, $this->headers, $json); + $response = $this->client->sendRequest($request); + } catch (RequestExceptionInterface $e) { + return Trace\Exporter::FAILED_NOT_RETRYABLE; + } catch (NetworkExceptionInterface | ClientExceptionInterface $e) { + return Trace\Exporter::FAILED_RETRYABLE; + } + + if ($response->getStatusCode() >= 400 && $response->getStatusCode() < 500) { + return Trace\Exporter::FAILED_NOT_RETRYABLE; + } + + if ($response->getStatusCode() >= 500 && $response->getStatusCode() < 600) { + return Trace\Exporter::FAILED_RETRYABLE; + } + + return Trace\Exporter::SUCCESS; + } + + public function shutdown(): void + { + $this->running = false; + } + + protected function createDefaultClient(): ClientInterface + { + $container = []; + $history = Middleware::history($container); + $stack = HandlerStack::create(); + // Add the history middleware to the handler stack. + $stack->push($history); + + return Client::createWithConfig([ + 'handler' => $stack, + 'timeout' => 30, + ]); + } +} diff --git a/contrib/otlp/SpanConverter.php b/contrib/otlp/SpanConverter.php new file mode 100644 index 000000000..e148e20cf --- /dev/null +++ b/contrib/otlp/SpanConverter.php @@ -0,0 +1,76 @@ +serviceName = $serviceName; + } + + private function sanitiseTagValue($value) + { + // Casting false to string makes an empty string + if (is_bool($value)) { + return $value ? 'true' : 'false'; + } + + // OTLP tags must be strings, but opentelemetry + // accepts strings, booleans, numbers, and lists of each. + if (is_array($value)) { + return join(',', array_map([$this, 'sanitiseTagValue'], $value)); + } + + // 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; + } + + public function convert(Span $span) + { + $row = [ + 'id' => $span->getContext()->getSpanId(), + 'traceId' => $span->getContext()->getTraceId(), + 'parentId' => $span->getParent() ? $span->getParent()->getSpanId() : null, + 'localEndpoint' => [ + 'serviceName' => $this->serviceName, + ], + 'name' => $span->getSpanName(), + 'timestamp' => (int) ($span->getStartEpochTimestamp() / 1e3), // RealtimeClock in microseconds + 'duration' => (int) (($span->getEnd() - $span->getStart()) / 1e3), // Diff in microseconds + ]; + + foreach ($span->getAttributes() as $k => $v) { + if (!array_key_exists('tags', $row)) { + $row['tags'] = []; + } + $row['tags'][$k] = $this->sanitiseTagValue($v->getValue()); + } + + foreach ($span->getEvents() as $event) { + if (!array_key_exists('annotations', $row)) { + $row['annotations'] = []; + } + $row['annotations'][] = [ + 'timestamp' => (int) ($event->getTimestamp() / 1e3), // RealtimeClock in microseconds + 'value' => $event->getName(), + ]; + } + + return $row; + } +} diff --git a/examples/AlwaysOnOTLPExample.php b/examples/AlwaysOnOTLPExample.php new file mode 100644 index 000000000..2b0ef1425 --- /dev/null +++ b/examples/AlwaysOnOTLPExample.php @@ -0,0 +1,65 @@ +shouldSample( + null, + md5((string) microtime(true)), + substr(md5((string) microtime(true)), 16), + 'io.opentelemetry.example', + API\SpanKind::KIND_INTERNAL +); +$Exporter = new OTLPExporter( + 'OTLP Example Service'); + +if (SamplingResult::RECORD_AND_SAMPLED === $samplingResult->getDecision()) { + echo 'Starting OTLPExample'; + $tracer = (new TracerProvider()) + ->addSpanProcessor(new SimpleSpanProcessor($Exporter)) + ->getTracer('io.opentelemetry.contrib.php'); + + + for ($i = 0; $i < 5; $i++) { + // start a span, register some events + $timestamp = Clock::get()->timestamp(); + $span = $tracer->startAndActivateSpan('session.generate.span.' . microtime(true)); + + + echo sprintf( + PHP_EOL . 'Exporting Trace: %s, Parent: %s, Span: %s', + $span->getContext()->getTraceId(), + $span->getParent() ? $span->getParent()->getSpanId() : 'None', + $span->getContext()->getSpanId() + ); + + $span->setAttribute('remote_ip', '1.2.3.4') + ->setAttribute('country', 'USA'); + + $span->addEvent('found_login' . $i, $timestamp, new Attributes([ + 'id' => $i, + 'username' => 'otuser' . $i, + ])); + $span->addEvent('generated_session', $timestamp, new Attributes([ + 'id' => md5((string) microtime(true)), + ])); + + $tracer->endActiveSpan(); + } + echo PHP_EOL . 'OTLPExample complete! '; +} else { + echo PHP_EOL . 'OTLPExample tracing is not enabled'; +} + +echo PHP_EOL; From 3d76f60e5472aa842d64283895354a9f98273a26 Mon Sep 17 00:00:00 2001 From: Ritick Gautam Date: Wed, 4 Nov 2020 21:32:18 +0530 Subject: [PATCH 02/13] Updated SpanConverter --- contrib/otlp/SpanConverter.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/otlp/SpanConverter.php b/contrib/otlp/SpanConverter.php index e148e20cf..f8e5216f2 100644 --- a/contrib/otlp/SpanConverter.php +++ b/contrib/otlp/SpanConverter.php @@ -42,10 +42,11 @@ private function sanitiseTagValue($value) public function convert(Span $span) { + $spanParent = $span->getParent(); $row = [ 'id' => $span->getContext()->getSpanId(), 'traceId' => $span->getContext()->getTraceId(), - 'parentId' => $span->getParent() ? $span->getParent()->getSpanId() : null, + 'parentId' => $spanParent ? $spanParent->getSpanId() : null, 'localEndpoint' => [ 'serviceName' => $this->serviceName, ], From e7b3cd960d1fb901cea969217c897509bccdab0a Mon Sep 17 00:00:00 2001 From: Ritick Gautam Date: Wed, 4 Nov 2020 22:29:59 +0530 Subject: [PATCH 03/13] Fix vendor/bin/psalm --- contrib/otlp/Exporter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/otlp/Exporter.php b/contrib/otlp/Exporter.php index 94b69c28d..53583fef7 100644 --- a/contrib/otlp/Exporter.php +++ b/contrib/otlp/Exporter.php @@ -84,7 +84,7 @@ public function __construct( $this->certificateFile = getenv("OTEL_EXPORTER_OTLP_CERTIFICATE") ?: "none"; $this->headers[] = getenv("OTEL_EXPORTER_OTLP_HEADERS") ?: "none"; $this->compression = getenv("OTEL_EXPORTER_OTLP_COMPRESSION") ?: "none"; - $this->timeout = getenv("OTEL_EXPORTER_OTLP_TIMEOUT") ?: 10; + $this->timeout =(int)getenv("OTEL_EXPORTER_OTLP_TIMEOUT"); $this->client = $this->createDefaultClient(); From e5bfbfa768b2f3cd2d604fb54a1eb9b47df94a8c Mon Sep 17 00:00:00 2001 From: Ritick Gautam Date: Wed, 4 Nov 2020 23:08:15 +0530 Subject: [PATCH 04/13] Updated TestCase --- contrib/otlp/Exporter.php | 2 +- tests/Contrib/Unit/OTLPExporterTest.php | 92 +++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 tests/Contrib/Unit/OTLPExporterTest.php diff --git a/contrib/otlp/Exporter.php b/contrib/otlp/Exporter.php index 53583fef7..803a4374f 100644 --- a/contrib/otlp/Exporter.php +++ b/contrib/otlp/Exporter.php @@ -84,7 +84,7 @@ public function __construct( $this->certificateFile = getenv("OTEL_EXPORTER_OTLP_CERTIFICATE") ?: "none"; $this->headers[] = getenv("OTEL_EXPORTER_OTLP_HEADERS") ?: "none"; $this->compression = getenv("OTEL_EXPORTER_OTLP_COMPRESSION") ?: "none"; - $this->timeout =(int)getenv("OTEL_EXPORTER_OTLP_TIMEOUT"); + $this->timeout =(int)getenv("OTEL_EXPORTER_OTLP_TIMEOUT") ?: 10; $this->client = $this->createDefaultClient(); diff --git a/tests/Contrib/Unit/OTLPExporterTest.php b/tests/Contrib/Unit/OTLPExporterTest.php new file mode 100644 index 000000000..ccf66de7c --- /dev/null +++ b/tests/Contrib/Unit/OTLPExporterTest.php @@ -0,0 +1,92 @@ +method('sendRequest')->willReturn( + new Response($responseStatus) + ); + + $exporter = new Exporter('test.zipkin'); + + $this->assertEquals( + $expected, + $exporter->export([new Span('test.otlp.span', SpanContext::generate())]) + ); + } + + public function exporterResponseStatusesDataProvider() + { + return [ + 'server error' => [500, Exporter::FAILED_RETRYABLE], + 'timeout' => [503, Exporter::FAILED_RETRYABLE], + 'bad gateway' => [502, Exporter::FAILED_RETRYABLE], + ]; + } + + /** + * @test + * @dataProvider clientExceptionsShouldDecideReturnCodeDataProvider + */ + public function clientExceptionsShouldDecideReturnCode($exception, $expected) + { + $client = self::createMock(ClientInterface::class); + $client->method('sendRequest')->willThrowException($exception); + + $exporter = new Exporter('test.otlp'); + + $this->assertEquals( + $expected, + $exporter->export([new Span('test.otlp.span', SpanContext::generate())]) + ); + } + + public function clientExceptionsShouldDecideReturnCodeDataProvider() + { + return [ + 'client' => [ + self::createMock(ClientExceptionInterface::class), + Exporter::FAILED_RETRYABLE, + ], + 'network' => [ + self::createMock(NetworkExceptionInterface::class), + Exporter::FAILED_RETRYABLE, + ], + ]; + } + + /** + * @test + */ + public function shouldBeOkToExporterEmptySpansCollection() + { + $this->assertEquals( + Exporter::SUCCESS, + (new Exporter('test.otlp'))->export([]) + ); + } + +} \ No newline at end of file From 542887e5d0bb6d10aac2b3a61635aebadce8cabc Mon Sep 17 00:00:00 2001 From: Ritick Gautam Date: Thu, 5 Nov 2020 14:34:07 +0530 Subject: [PATCH 05/13] Updated OtlpExportertest accordingly #208 Updated TestCase --- examples/AlwaysOnOTLPExample.php | 4 +- tests/Contrib/Unit/OTLPSpanConverterTest.php | 131 +++++++++++++++++++ 2 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 tests/Contrib/Unit/OTLPSpanConverterTest.php diff --git a/examples/AlwaysOnOTLPExample.php b/examples/AlwaysOnOTLPExample.php index 2b0ef1425..0d1100333 100644 --- a/examples/AlwaysOnOTLPExample.php +++ b/examples/AlwaysOnOTLPExample.php @@ -36,11 +36,11 @@ $timestamp = Clock::get()->timestamp(); $span = $tracer->startAndActivateSpan('session.generate.span.' . microtime(true)); - + $spanParent = $span->getParent(); echo sprintf( PHP_EOL . 'Exporting Trace: %s, Parent: %s, Span: %s', $span->getContext()->getTraceId(), - $span->getParent() ? $span->getParent()->getSpanId() : 'None', + $spanParent ? $spanParent->getSpanId() : 'None', $span->getContext()->getSpanId() ); diff --git a/tests/Contrib/Unit/OTLPSpanConverterTest.php b/tests/Contrib/Unit/OTLPSpanConverterTest.php new file mode 100644 index 000000000..e950c4374 --- /dev/null +++ b/tests/Contrib/Unit/OTLPSpanConverterTest.php @@ -0,0 +1,131 @@ +getTracer('OpenTelemetry.OtlpTest'); + + $timestamp = Clock::get()->timestamp(); + + /** @var Span $span */ + $span = $tracer->startAndActivateSpan('guard.validate'); + $span->setAttribute('service', 'guard'); + $span->addEvent('validators.list', $timestamp, new Attributes(['job' => 'stage.updateTime'])); + $span->end(); + + $converter = new SpanConverter('test.name'); + $row = $converter->convert($span); + + $this->assertSame($span->getContext()->getSpanId(), $row['id']); + $this->assertSame($span->getContext()->getTraceId(), $row['traceId']); + + $this->assertSame('test.name', $row['localEndpoint']['serviceName']); + $this->assertSame($span->getSpanName(), $row['name']); + + $this->assertIsInt($row['timestamp']); + // timestamp should be in microseconds + $this->assertGreaterThan(1e15, $row['timestamp']); + + $this->assertIsInt($row['duration']); + $this->assertGreaterThan(0, $row['duration']); + + $this->assertCount(3, $row['tags']); + + /** @var Attribute $attribute */ + $attribute = $span->getAttribute('service'); + $this->assertEquals($attribute->getValue(), $row['tags']['service']); + + $this->assertCount(1, $row['annotations']); + [$annotation] = $row['annotations']; + $this->assertEquals('validators.list', $annotation['value']); + + [$event] = \iterator_to_array($span->getEvents()); + $this->assertIsInt($annotation['timestamp']); + + // timestamp should be in microseconds + $this->assertGreaterThan(1e15, $annotation['timestamp']); + } + + /** + * @test + */ + public function durationShouldBeInMicroseconds() + { + $span = new Span('duration.test', SpanContext::generate()); + + $row = (new SpanConverter('duration.test'))->convert($span); + + $this->assertEquals( + (int) (($span->getEnd() - $span->getStart()) / 1000), + $row['duration'] + ); + } + + /** + * @test + */ + public function tagsAreCoercedCorrectlyToStrings() + { + $span = new Span('tags.test', SpanContext::generate()); + + $listOfStrings = ['string-1','string-2']; + $listOfNumbers = [1,2,3,3.1415,42]; + $listOfBooleans = [true,true,false,true]; + $listOfRandoms = [true,[1,2,3],false,'string-1',3.1415]; + + $span->setAttribute('string', 'string'); + $span->setAttribute('integer-1', 1024); + $span->setAttribute('integer-2', 0); + $span->setAttribute('float', '1.2345'); + $span->setAttribute('boolean-1', true); + $span->setAttribute('boolean-2', false); + $span->setAttribute('list-of-strings', $listOfStrings); + $span->setAttribute('list-of-numbers', $listOfNumbers); + $span->setAttribute('list-of-booleans', $listOfBooleans); + $span->setAttribute('list-of-random', $listOfRandoms); + + $tags = (new SpanConverter('tags.test'))->convert($span)['tags']; + + // Check that we can convert all attributes to tags + $this->assertCount(12, $tags); + + // Tags destined for Otlp must be pairs of strings + foreach ($tags as $tagKey => $tagValue) { + $this->assertIsString($tagKey); + $this->assertIsString($tagValue); + } + + $this->assertEquals($tags['string'], 'string'); + $this->assertEquals($tags['integer-1'], '1024'); + $this->assertEquals($tags['integer-2'], '0'); + $this->assertEquals($tags['float'], '1.2345'); + $this->assertEquals($tags['boolean-1'], 'true'); + $this->assertEquals($tags['boolean-2'], 'false'); + + // Lists must be casted to strings and joined with a separator + $this->assertEquals($tags['list-of-strings'], join(',', $listOfStrings)); + $this->assertEquals($tags['list-of-numbers'], join(',', $listOfNumbers)); + $this->assertEquals($tags['list-of-booleans'], 'true,true,false,true'); + + // This currently works, but OpenTelemetry\Trace\Span should stop arrays + // containing multiple value types from being passed to the Exporter. + $this->assertEquals($tags['list-of-random'], 'true,1,2,3,false,string-1,3.1415'); + } +} \ No newline at end of file From 635da25241491bf68b50b97de941529508f2485e Mon Sep 17 00:00:00 2001 From: Ritick Gautam Date: Thu, 5 Nov 2020 14:42:06 +0530 Subject: [PATCH 06/13] Updated OTLPSpanConverterTest --- tests/Contrib/Unit/OTLPSpanConverterTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Contrib/Unit/OTLPSpanConverterTest.php b/tests/Contrib/Unit/OTLPSpanConverterTest.php index e950c4374..139859b55 100644 --- a/tests/Contrib/Unit/OTLPSpanConverterTest.php +++ b/tests/Contrib/Unit/OTLPSpanConverterTest.php @@ -46,7 +46,7 @@ public function shouldConvertASpanToAPayloadForOtlp() $this->assertIsInt($row['duration']); $this->assertGreaterThan(0, $row['duration']); - $this->assertCount(3, $row['tags']); + $this->assertCount(1, $row['tags']); /** @var Attribute $attribute */ $attribute = $span->getAttribute('service'); @@ -104,7 +104,7 @@ public function tagsAreCoercedCorrectlyToStrings() $tags = (new SpanConverter('tags.test'))->convert($span)['tags']; // Check that we can convert all attributes to tags - $this->assertCount(12, $tags); + $this->assertCount(10, $tags); // Tags destined for Otlp must be pairs of strings foreach ($tags as $tagKey => $tagValue) { From dedd9f7d59dc44b19ee68f94106f797a7c5890fc Mon Sep 17 00:00:00 2001 From: Ritick Gautam Date: Thu, 5 Nov 2020 17:03:24 +0530 Subject: [PATCH 07/13] Updated OTLPExportertest --- tests/Contrib/Unit/OTLPExporterTest.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/Contrib/Unit/OTLPExporterTest.php b/tests/Contrib/Unit/OTLPExporterTest.php index ccf66de7c..659063f0d 100644 --- a/tests/Contrib/Unit/OTLPExporterTest.php +++ b/tests/Contrib/Unit/OTLPExporterTest.php @@ -41,6 +41,10 @@ public function exporterResponseStatuses($responseStatus, $expected) public function exporterResponseStatusesDataProvider() { return [ + 'not found' => [404, Exporter::FAILED_NOT_RETRYABLE], + 'not authorized' => [401, Exporter::FAILED_NOT_RETRYABLE], + 'bad request' => [402, Exporter::FAILED_NOT_RETRYABLE], + 'too many requests' => [429, Exporter::FAILED_NOT_RETRYABLE], 'server error' => [500, Exporter::FAILED_RETRYABLE], 'timeout' => [503, Exporter::FAILED_RETRYABLE], 'bad gateway' => [502, Exporter::FAILED_RETRYABLE], @@ -88,5 +92,16 @@ public function shouldBeOkToExporterEmptySpansCollection() (new Exporter('test.otlp'))->export([]) ); } + /** + * @test + */ + public function failsIfNotRunning() + { + $exporter = new Exporter('test.otlp'); + $span = $this->createMock(Span::class); + $exporter->shutdown(); + + $this->assertEquals($exporter->export([$span]), \Opentelemetry\Sdk\Trace\Exporter::FAILED_NOT_RETRYABLE); + } } \ No newline at end of file From a4309057d18055851f537e74c868adf91ee567a7 Mon Sep 17 00:00:00 2001 From: Ritick Gautam Date: Thu, 5 Nov 2020 17:07:21 +0530 Subject: [PATCH 08/13] BugFixed --- tests/Contrib/Unit/OTLPExporterTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Contrib/Unit/OTLPExporterTest.php b/tests/Contrib/Unit/OTLPExporterTest.php index 659063f0d..6de543c27 100644 --- a/tests/Contrib/Unit/OTLPExporterTest.php +++ b/tests/Contrib/Unit/OTLPExporterTest.php @@ -101,7 +101,7 @@ public function failsIfNotRunning() $span = $this->createMock(Span::class); $exporter->shutdown(); - $this->assertEquals($exporter->export([$span]), \Opentelemetry\Sdk\Trace\Exporter::FAILED_NOT_RETRYABLE); + $this->assertEquals($exporter->export([$span]), \OpenTelemetry\Sdk\Trace\Exporter::FAILED_NOT_RETRYABLE); } } \ No newline at end of file From d026aa3a269f10cac0ee6e94566f08b2a1afede7 Mon Sep 17 00:00:00 2001 From: Ritick Gautam Date: Thu, 5 Nov 2020 23:58:08 +0530 Subject: [PATCH 09/13] bug fixed --- contrib/otlp/Exporter.php | 4 ++++ tests/Contrib/Unit/OTLPExporterTest.php | 8 ++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contrib/otlp/Exporter.php b/contrib/otlp/Exporter.php index 803a4374f..9cb453aea 100644 --- a/contrib/otlp/Exporter.php +++ b/contrib/otlp/Exporter.php @@ -99,6 +99,10 @@ public function __construct( */ public function export(iterable $spans): int { + if (!$this->running) { + return Exporter::FAILED_NOT_RETRYABLE; + } + if (empty($spans)) { return Trace\Exporter::SUCCESS; } diff --git a/tests/Contrib/Unit/OTLPExporterTest.php b/tests/Contrib/Unit/OTLPExporterTest.php index 6de543c27..8a2407f6a 100644 --- a/tests/Contrib/Unit/OTLPExporterTest.php +++ b/tests/Contrib/Unit/OTLPExporterTest.php @@ -30,7 +30,7 @@ public function exporterResponseStatuses($responseStatus, $expected) new Response($responseStatus) ); - $exporter = new Exporter('test.zipkin'); + $exporter = new Exporter('test.otlp'); $this->assertEquals( $expected, @@ -41,10 +41,6 @@ public function exporterResponseStatuses($responseStatus, $expected) public function exporterResponseStatusesDataProvider() { return [ - 'not found' => [404, Exporter::FAILED_NOT_RETRYABLE], - 'not authorized' => [401, Exporter::FAILED_NOT_RETRYABLE], - 'bad request' => [402, Exporter::FAILED_NOT_RETRYABLE], - 'too many requests' => [429, Exporter::FAILED_NOT_RETRYABLE], 'server error' => [500, Exporter::FAILED_RETRYABLE], 'timeout' => [503, Exporter::FAILED_RETRYABLE], 'bad gateway' => [502, Exporter::FAILED_RETRYABLE], @@ -101,7 +97,7 @@ public function failsIfNotRunning() $span = $this->createMock(Span::class); $exporter->shutdown(); - $this->assertEquals($exporter->export([$span]), \OpenTelemetry\Sdk\Trace\Exporter::FAILED_NOT_RETRYABLE); + $this->assertEquals(\OpenTelemetry\Sdk\Trace\Exporter::FAILED_NOT_RETRYABLE, $exporter->export([$span])); } } \ No newline at end of file From 5b974a9a6d3877951bbb876c55d16b183b2c43cc Mon Sep 17 00:00:00 2001 From: Ritick Gautam Date: Fri, 6 Nov 2020 14:25:05 +0530 Subject: [PATCH 10/13] Updated: otlpExporter Updated: testcase --- contrib/otlp/Exporter.php | 13 +++++-------- tests/Contrib/Unit/OTLPExporterTest.php | 8 ++++++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/contrib/otlp/Exporter.php b/contrib/otlp/Exporter.php index 9cb453aea..11a48f658 100644 --- a/contrib/otlp/Exporter.php +++ b/contrib/otlp/Exporter.php @@ -73,13 +73,14 @@ class Exporter implements Trace\Exporter * @param string $serviceName */ public function __construct( - $serviceName + $serviceName, + ClientInterface $client=null ) { // Set default values based on presence of env variable $this->endpointURL = getenv("OTEL_EXPORTER_OTLP_ENDPOINT") ?: "localhost:55680"; - $this->protocol = getenv("OTEL_EXPORTER_OTLP_PROTOCOL") ?: "grpc"; + $this->protocol = getenv("OTEL_EXPORTER_OTLP_PROTOCOL") ?: "json"; $this->insecure = getenv("OTEL_EXPORTER_OTLP_INSECURE") ?: "false"; $this->certificateFile = getenv("OTEL_EXPORTER_OTLP_CERTIFICATE") ?: "none"; $this->headers[] = getenv("OTEL_EXPORTER_OTLP_HEADERS") ?: "none"; @@ -87,7 +88,7 @@ public function __construct( $this->timeout =(int)getenv("OTEL_EXPORTER_OTLP_TIMEOUT") ?: 10; - $this->client = $this->createDefaultClient(); + $this->client = $client ?? $this->createDefaultClient(); $this->spanConverter = new SpanConverter($serviceName); } @@ -117,11 +118,7 @@ public function export(iterable $spans): int $this->headers[] = ''; - if($this->protocol == "grpc") { - $headers = ['content-type' => 'application/x-protobuf']; - } - - else if($this->protocol == "json") { + if($this->protocol == "json") { $headers = ['content-type' => 'application/json', "Content-Encoding" => "gzip"]; } diff --git a/tests/Contrib/Unit/OTLPExporterTest.php b/tests/Contrib/Unit/OTLPExporterTest.php index 8a2407f6a..6e686d992 100644 --- a/tests/Contrib/Unit/OTLPExporterTest.php +++ b/tests/Contrib/Unit/OTLPExporterTest.php @@ -4,7 +4,6 @@ namespace OpenTelemetry\Tests\Contrib\Unit; - use GuzzleHttp\Psr7\Response; use InvalidArgumentException; use OpenTelemetry\Contrib\otlp\Exporter; @@ -30,7 +29,7 @@ public function exporterResponseStatuses($responseStatus, $expected) new Response($responseStatus) ); - $exporter = new Exporter('test.otlp'); + $exporter = new Exporter('test.otlp', $client); $this->assertEquals( $expected, @@ -41,6 +40,11 @@ public function exporterResponseStatuses($responseStatus, $expected) public function exporterResponseStatusesDataProvider() { return [ + 'ok' => [200, Exporter::SUCCESS], + 'not found' => [404, Exporter::FAILED_NOT_RETRYABLE], + 'not authorized' => [401, Exporter::FAILED_NOT_RETRYABLE], + 'bad request' => [402, Exporter::FAILED_NOT_RETRYABLE], + 'too many requests' => [429, Exporter::FAILED_NOT_RETRYABLE], 'server error' => [500, Exporter::FAILED_RETRYABLE], 'timeout' => [503, Exporter::FAILED_RETRYABLE], 'bad gateway' => [502, Exporter::FAILED_RETRYABLE], From b3c327367429e884e0344228cd6dd74a5b4e54e7 Mon Sep 17 00:00:00 2001 From: Ritick Gautam Date: Sat, 7 Nov 2020 11:25:17 +0530 Subject: [PATCH 11/13] Updated namespace with StuldyCase --- contrib/otlp/Exporter.php | 2 +- contrib/otlp/SpanConverter.php | 2 +- examples/AlwaysOnOTLPExample.php | 2 +- tests/Contrib/Unit/OTLPExporterTest.php | 2 +- tests/Contrib/Unit/OTLPSpanConverterTest.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/contrib/otlp/Exporter.php b/contrib/otlp/Exporter.php index 11a48f658..ea62cf570 100644 --- a/contrib/otlp/Exporter.php +++ b/contrib/otlp/Exporter.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenTelemetry\Contrib\otlp; +namespace OpenTelemetry\Contrib\Otlp; use GuzzleHttp\HandlerStack; use GuzzleHttp\Middleware; diff --git a/contrib/otlp/SpanConverter.php b/contrib/otlp/SpanConverter.php index f8e5216f2..f00045fd4 100644 --- a/contrib/otlp/SpanConverter.php +++ b/contrib/otlp/SpanConverter.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OpenTelemetry\Contrib\otlp; +namespace OpenTelemetry\Contrib\Otlp; use OpenTelemetry\Trace\Span; diff --git a/examples/AlwaysOnOTLPExample.php b/examples/AlwaysOnOTLPExample.php index 0d1100333..2ab92c100 100644 --- a/examples/AlwaysOnOTLPExample.php +++ b/examples/AlwaysOnOTLPExample.php @@ -4,7 +4,7 @@ require __DIR__ . '/../vendor/autoload.php'; -use OpenTelemetry\Contrib\otlp\Exporter as OTLPExporter; +use OpenTelemetry\Contrib\Otlp\Exporter as OTLPExporter; use OpenTelemetry\Sdk\Trace\Attributes; use OpenTelemetry\Sdk\Trace\Clock; use OpenTelemetry\Sdk\Trace\Sampler\AlwaysOnSampler; diff --git a/tests/Contrib/Unit/OTLPExporterTest.php b/tests/Contrib/Unit/OTLPExporterTest.php index 6e686d992..747d96aca 100644 --- a/tests/Contrib/Unit/OTLPExporterTest.php +++ b/tests/Contrib/Unit/OTLPExporterTest.php @@ -6,7 +6,7 @@ use GuzzleHttp\Psr7\Response; use InvalidArgumentException; -use OpenTelemetry\Contrib\otlp\Exporter; +use OpenTelemetry\Contrib\Otlp\Exporter; use OpenTelemetry\Sdk\Trace\Span; use OpenTelemetry\Sdk\Trace\SpanContext; use PHPUnit\Framework\TestCase; diff --git a/tests/Contrib/Unit/OTLPSpanConverterTest.php b/tests/Contrib/Unit/OTLPSpanConverterTest.php index 139859b55..09c25b042 100644 --- a/tests/Contrib/Unit/OTLPSpanConverterTest.php +++ b/tests/Contrib/Unit/OTLPSpanConverterTest.php @@ -4,7 +4,7 @@ namespace OpenTelemetry\Tests\Contrib\Unit; -use OpenTelemetry\Contrib\otlp\SpanConverter; +use OpenTelemetry\Contrib\Otlp\SpanConverter; use OpenTelemetry\Sdk\Trace\Attribute; use OpenTelemetry\Sdk\Trace\Attributes; use OpenTelemetry\Sdk\Trace\Clock; From bdd4ca3f19dcb3d416bae1dfebb55fe7aab46205 Mon Sep 17 00:00:00 2001 From: Ritick Gautam Date: Sat, 7 Nov 2020 11:54:21 +0530 Subject: [PATCH 12/13] Renamed the namespace --- contrib/{otlp => Otlp}/Exporter.php | 0 contrib/{otlp => Otlp}/SpanConverter.php | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename contrib/{otlp => Otlp}/Exporter.php (100%) rename contrib/{otlp => Otlp}/SpanConverter.php (100%) diff --git a/contrib/otlp/Exporter.php b/contrib/Otlp/Exporter.php similarity index 100% rename from contrib/otlp/Exporter.php rename to contrib/Otlp/Exporter.php diff --git a/contrib/otlp/SpanConverter.php b/contrib/Otlp/SpanConverter.php similarity index 100% rename from contrib/otlp/SpanConverter.php rename to contrib/Otlp/SpanConverter.php From 16e66040b36e43b4fc052cf13a3f9dde13591402 Mon Sep 17 00:00:00 2001 From: Ritick Gautam Date: Mon, 9 Nov 2020 10:57:36 +0530 Subject: [PATCH 13/13] Updated php-cs-fixer --- contrib/Otlp/Exporter.php | 38 +++++++++----------- contrib/Otlp/SpanConverter.php | 2 +- examples/AlwaysOnOTLPExample.php | 7 ++-- tests/Contrib/Unit/OTLPExporterTest.php | 9 ++--- tests/Contrib/Unit/OTLPSpanConverterTest.php | 2 +- 5 files changed, 25 insertions(+), 33 deletions(-) diff --git a/contrib/Otlp/Exporter.php b/contrib/Otlp/Exporter.php index ea62cf570..2cee29d11 100644 --- a/contrib/Otlp/Exporter.php +++ b/contrib/Otlp/Exporter.php @@ -8,20 +8,19 @@ use GuzzleHttp\Middleware; use GuzzleHttp\Psr7\Request; use Http\Adapter\Guzzle6\Client; +use OpenTelemetry\Sdk\Trace; +use OpenTelemetry\Trace as API; use Psr\Http\Client\ClientExceptionInterface; use Psr\Http\Client\ClientInterface; use Psr\Http\Client\NetworkExceptionInterface; use Psr\Http\Client\RequestExceptionInterface; -use OpenTelemetry\Sdk\Trace; -use OpenTelemetry\Trace as API; - class Exporter implements Trace\Exporter { /** * @var string */ - private $endpointURL; + private $endpointURL; /** * @var string @@ -57,9 +56,9 @@ class Exporter implements Trace\Exporter */ private $spanConverter; - /** - * @var bool - */ + /** + * @var bool + */ private $running = true; /** @@ -70,23 +69,21 @@ class Exporter implements Trace\Exporter /** * Exporter constructor. - * @param string $serviceName + * @param string $serviceName */ public function __construct( $serviceName, ClientInterface $client=null - ) - { + ) { // Set default values based on presence of env variable - $this->endpointURL = getenv("OTEL_EXPORTER_OTLP_ENDPOINT") ?: "localhost:55680"; - $this->protocol = getenv("OTEL_EXPORTER_OTLP_PROTOCOL") ?: "json"; - $this->insecure = getenv("OTEL_EXPORTER_OTLP_INSECURE") ?: "false"; - $this->certificateFile = getenv("OTEL_EXPORTER_OTLP_CERTIFICATE") ?: "none"; - $this->headers[] = getenv("OTEL_EXPORTER_OTLP_HEADERS") ?: "none"; - $this->compression = getenv("OTEL_EXPORTER_OTLP_COMPRESSION") ?: "none"; - $this->timeout =(int)getenv("OTEL_EXPORTER_OTLP_TIMEOUT") ?: 10; - + $this->endpointURL = getenv('OTEL_EXPORTER_OTLP_ENDPOINT') ?: 'localhost:55680'; + $this->protocol = getenv('OTEL_EXPORTER_OTLP_PROTOCOL') ?: 'json'; + $this->insecure = getenv('OTEL_EXPORTER_OTLP_INSECURE') ?: 'false'; + $this->certificateFile = getenv('OTEL_EXPORTER_OTLP_CERTIFICATE') ?: 'none'; + $this->headers[] = getenv('OTEL_EXPORTER_OTLP_HEADERS') ?: 'none'; + $this->compression = getenv('OTEL_EXPORTER_OTLP_COMPRESSION') ?: 'none'; + $this->timeout =(int) getenv('OTEL_EXPORTER_OTLP_TIMEOUT') ?: 10; $this->client = $client ?? $this->createDefaultClient(); $this->spanConverter = new SpanConverter($serviceName); @@ -118,9 +115,8 @@ public function export(iterable $spans): int $this->headers[] = ''; - if($this->protocol == "json") { - $headers = ['content-type' => 'application/json', "Content-Encoding" => "gzip"]; - + if ($this->protocol == 'json') { + $headers = ['content-type' => 'application/json', 'Content-Encoding' => 'gzip']; } $request = new Request('POST', $this->endpointURL, $this->headers, $json); diff --git a/contrib/Otlp/SpanConverter.php b/contrib/Otlp/SpanConverter.php index f00045fd4..a4688f4cd 100644 --- a/contrib/Otlp/SpanConverter.php +++ b/contrib/Otlp/SpanConverter.php @@ -42,7 +42,7 @@ private function sanitiseTagValue($value) public function convert(Span $span) { - $spanParent = $span->getParent(); + $spanParent = $span->getParent(); $row = [ 'id' => $span->getContext()->getSpanId(), 'traceId' => $span->getContext()->getTraceId(), diff --git a/examples/AlwaysOnOTLPExample.php b/examples/AlwaysOnOTLPExample.php index 2ab92c100..171a61cbd 100644 --- a/examples/AlwaysOnOTLPExample.php +++ b/examples/AlwaysOnOTLPExample.php @@ -3,7 +3,6 @@ declare(strict_types=1); require __DIR__ . '/../vendor/autoload.php'; - use OpenTelemetry\Contrib\Otlp\Exporter as OTLPExporter; use OpenTelemetry\Sdk\Trace\Attributes; use OpenTelemetry\Sdk\Trace\Clock; @@ -22,7 +21,8 @@ API\SpanKind::KIND_INTERNAL ); $Exporter = new OTLPExporter( - 'OTLP Example Service'); + 'OTLP Example Service' +); if (SamplingResult::RECORD_AND_SAMPLED === $samplingResult->getDecision()) { echo 'Starting OTLPExample'; @@ -30,11 +30,10 @@ ->addSpanProcessor(new SimpleSpanProcessor($Exporter)) ->getTracer('io.opentelemetry.contrib.php'); - for ($i = 0; $i < 5; $i++) { // start a span, register some events $timestamp = Clock::get()->timestamp(); - $span = $tracer->startAndActivateSpan('session.generate.span.' . microtime(true)); + $span = $tracer->startAndActivateSpan('session.generate.span.' . microtime(true)); $spanParent = $span->getParent(); echo sprintf( diff --git a/tests/Contrib/Unit/OTLPExporterTest.php b/tests/Contrib/Unit/OTLPExporterTest.php index 747d96aca..c6cec50ed 100644 --- a/tests/Contrib/Unit/OTLPExporterTest.php +++ b/tests/Contrib/Unit/OTLPExporterTest.php @@ -5,7 +5,6 @@ namespace OpenTelemetry\Tests\Contrib\Unit; use GuzzleHttp\Psr7\Response; -use InvalidArgumentException; use OpenTelemetry\Contrib\Otlp\Exporter; use OpenTelemetry\Sdk\Trace\Span; use OpenTelemetry\Sdk\Trace\SpanContext; @@ -13,7 +12,6 @@ use Psr\Http\Client\ClientExceptionInterface; use Psr\Http\Client\ClientInterface; use Psr\Http\Client\NetworkExceptionInterface; -use Psr\Http\Client\RequestExceptionInterface; class OTLPExporterTest extends TestCase { @@ -35,7 +33,7 @@ public function exporterResponseStatuses($responseStatus, $expected) $expected, $exporter->export([new Span('test.otlp.span', SpanContext::generate())]) ); - } + } public function exporterResponseStatusesDataProvider() { @@ -95,7 +93,7 @@ public function shouldBeOkToExporterEmptySpansCollection() /** * @test */ - public function failsIfNotRunning() + public function failsIfNotRunning() { $exporter = new Exporter('test.otlp'); $span = $this->createMock(Span::class); @@ -103,5 +101,4 @@ public function failsIfNotRunning() $this->assertEquals(\OpenTelemetry\Sdk\Trace\Exporter::FAILED_NOT_RETRYABLE, $exporter->export([$span])); } - -} \ No newline at end of file +} diff --git a/tests/Contrib/Unit/OTLPSpanConverterTest.php b/tests/Contrib/Unit/OTLPSpanConverterTest.php index 09c25b042..4a7e0b89a 100644 --- a/tests/Contrib/Unit/OTLPSpanConverterTest.php +++ b/tests/Contrib/Unit/OTLPSpanConverterTest.php @@ -128,4 +128,4 @@ public function tagsAreCoercedCorrectlyToStrings() // containing multiple value types from being passed to the Exporter. $this->assertEquals($tags['list-of-random'], 'true,1,2,3,false,string-1,3.1415'); } -} \ No newline at end of file +}