diff --git a/Jaeger/AgentExporter.php b/Jaeger/AgentExporter.php deleted file mode 100644 index 6ac19b3..0000000 --- a/Jaeger/AgentExporter.php +++ /dev/null @@ -1,57 +0,0 @@ -validateHost() //This is because the host is required downstream - ->validatePort(); //This is because the port is required downstream - - $this->spanConverter = new SpanConverter(); - $this->jaegerTransport = new JaegerTransport($parsedEndpoint); - $this->defaultServiceName = ResourceInfoFactory::defaultResource()->getAttributes()->get(ResourceAttributes::SERVICE_NAME); - } - - public function closeAgentConnection(): void - { - $this->jaegerTransport->close(); - } - - public function doExport(iterable $spans): bool - { - // UDP Transport begins here after converting to thrift format span - foreach ($spans as $span) { - $serviceName = $span->getResource()->getAttributes()->get(ResourceAttributes::SERVICE_NAME) - ?? - $this->defaultServiceName; - $this->jaegerTransport->append( - $this->spanConverter->convert([$span])[0], - $serviceName - ); - } - - return true; - } -} diff --git a/Jaeger/BatchAdapter/BatchAdapter.php b/Jaeger/BatchAdapter/BatchAdapter.php deleted file mode 100644 index 62e4df0..0000000 --- a/Jaeger/BatchAdapter/BatchAdapter.php +++ /dev/null @@ -1,23 +0,0 @@ -batchInstance = new Batch($values); - } - - public function write(TProtocol $output): void - { - $this->batchInstance->write($output); - } -} diff --git a/Jaeger/BatchAdapter/BatchAdapterFactory.php b/Jaeger/BatchAdapter/BatchAdapterFactory.php deleted file mode 100644 index 883a4a1..0000000 --- a/Jaeger/BatchAdapter/BatchAdapterFactory.php +++ /dev/null @@ -1,13 +0,0 @@ -validateHost(); //This is because the host is required downstream - - $this->sender = new HttpSender( - $client, - $requestFactory, - $streamFactory, - $parsedEndpoint - ); - } - - public function doExport(iterable $spans): bool - { - $this->sender->send($spans); - - return true; - } -} diff --git a/Jaeger/HttpSender.php b/Jaeger/HttpSender.php deleted file mode 100644 index 159d6bf..0000000 --- a/Jaeger/HttpSender.php +++ /dev/null @@ -1,130 +0,0 @@ -protocol = new TBinaryProtocol( - new ThriftHttpTransport( - $client, - $requestFactory, - $streamFactory, - $parsedEndpoint - ) - ); - - $this->batchAdapterFactory = $batchAdapterFactory ?? new BatchAdapterFactory(); - $this->defaultServiceName = ResourceInfoFactory::defaultResource()->getAttributes()->get(ResourceAttributes::SERVICE_NAME); - } - - public function send(iterable $spans): void - { - $batches = $this->createBatchesPerResource( - self::groupSpansByResource($spans) - ); - - foreach ($batches as $batch) { - $this->sendBatch($batch); - } - } - - private static function groupSpansByResource(iterable $spans): array - { - $spansGroupedByResource = []; - foreach ($spans as $span) { - /** @var ResourceInfo */ - $resource = $span->getResource(); - $resourceAsKey = $resource->serialize(); - - if (!isset($spansGroupedByResource[$resourceAsKey])) { - $spansGroupedByResource[$resourceAsKey] = [ - 'spans' => [], - 'resource' => $resource, - ]; - } - - $spansGroupedByResource[$resourceAsKey]['spans'][] = $span; - } - - return $spansGroupedByResource; - } - - 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 - { - //Defaulting to (what should be) the default resource's service name - $serviceName = $this->defaultServiceName; - - $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(); - } -} diff --git a/Jaeger/IdConverter.php b/Jaeger/IdConverter.php deleted file mode 100644 index c88f8bc..0000000 --- a/Jaeger/IdConverter.php +++ /dev/null @@ -1,41 +0,0 @@ - $traceIdLow, - 'traceIdHigh' => $traceIdHigh, - ]; - } - - public static function convertOtelToJaegerSpanId(string $spanId): int - { - return self::convert16CharHexStringToSignedInt($spanId); - } - - /** - * PHP has the limitation to correctly convert int64 from the 16 character hex only - * @param string $hex - * @return int - */ - private static function convert16CharHexStringToSignedInt(string $hex): int - { - $hi = intval(substr($hex, -16, -8), 16); - $lo = intval(substr($hex, -8, 8), 16); - - return $hi << 32 | $lo; - } -} diff --git a/Jaeger/JaegerTransport.php b/Jaeger/JaegerTransport.php deleted file mode 100644 index d14ad42..0000000 --- a/Jaeger/JaegerTransport.php +++ /dev/null @@ -1,114 +0,0 @@ -transport = new ThriftUdpTransport($parsedEndpoint); - $p = new TCompactProtocol($this->transport); - $this->client = new AgentClient($p, $p); - - $this->maxBufferSize = ($maxBufferSize > 0 ? $maxBufferSize : self::DEFAULT_BUFFER_SIZE); - } - - /** - * Submits a new span to collectors, possibly delayed and/or with buffering. - * - * @param Span $span - */ - public function append(Span $span, $serviceName): int - { - // Grab a copy of the process data, if we didn't already. - if ($this->process == null) { - $this->process = new Process([ - 'serviceName' => $serviceName, - 'tags' => $span->tags, - ]); - } - - $this->buffer[] = $span; - - // TODO(tylerc): Buffer spans and send them in as few UDP packets as possible. - return $this->flush(); - } - - /** - * Flush submits the internal buffer to the remote server. It returns the - * number of spans flushed. - * - * @param bool $force - force a flush, even on a partial buffer - */ - public function flush($force = false): int - { - $spans = count($this->buffer); - - // buffer not full yet - if (!$force && $spans < $this->maxBufferSize) { - return 0; - } - - // no spans to flush - if ($spans <= 0) { - return 0; - } - - try { - // emit a batch - $this->client->emitBatch(new Batch([ - 'process' => $this->process, - 'spans' => $this->buffer, - ])); - - // flush the UDP data - $this->transport->flush(); - - // reset the internal buffer - $this->buffer = []; - - // reset the process tag - $this->process = null; - } catch (TTransportException $e) { - self::logError('jaeger: transport failure', ['exception' => $e]); - - return 0; - } - - return $spans; - } - - /** - * Does a clean shutdown of the reporter, flushing any traces that may be - * buffered in memory. - */ - public function close(): void - { - $this->flush(true); // flush all remaining data - $this->transport->close(); - } -} diff --git a/Jaeger/ParsedEndpointUrl.php b/Jaeger/ParsedEndpointUrl.php deleted file mode 100644 index a26193f..0000000 --- a/Jaeger/ParsedEndpointUrl.php +++ /dev/null @@ -1,65 +0,0 @@ -endpointUrl = $endpointUrl; - - $this->parsedDsn = parse_url($this->endpointUrl); - - if (!is_array($this->parsedDsn)) { - throw new InvalidArgumentException('Unable to parse provided DSN for url: ' . $this->endpointUrl); - } - } - - public function getEndpointUrl(): string - { - return $this->endpointUrl; - } - - public function validateHost(): self - { - $this->validateUrlComponent('host'); - - return $this; - } - - public function getHost(): string - { - $this->validateHost(); - - return $this->parsedDsn['host']; - } - - public function validatePort(): self - { - $this->validateUrlComponent('port'); - - return $this; - } - - public function getPort(): int - { - $this->validatePort(); - - return $this->parsedDsn['port']; - } - - private function validateUrlComponent(string $componentName): void - { - if (!isset($this->parsedDsn[$componentName])) { - throw new InvalidArgumentException($this->endpointUrl . ' is missing the ' . $componentName); - } - } -} diff --git a/Jaeger/README.md b/Jaeger/README.md deleted file mode 100644 index 9de46a9..0000000 --- a/Jaeger/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# OpenTelemetry Jaeger Exporter - -A Jaeger exporter for OpenTelemetry. - -## Usage - -### HTTP (Zipkin) -https://github.com/open-telemetry/opentelemetry-php/blob/main/examples/traces/features/exporters/jaeger.php - -### Thrift -https://github.com/open-telemetry/opentelemetry-php/blob/main/examples/traces/features/exporters/jaeger_thrift.php \ No newline at end of file diff --git a/Jaeger/SpanConverter.php b/Jaeger/SpanConverter.php deleted file mode 100644 index 449e0cc..0000000 --- a/Jaeger/SpanConverter.php +++ /dev/null @@ -1,237 +0,0 @@ -convertSpan($span); - } - - return $aggregate; - } - - private function convertSpan(SpanDataInterface $span): JTSpan - { - [ - 'traceIdLow' => $traceIdLow, - 'traceIdHigh' => $traceIdHigh, - 'spanId' => $spanId, - 'parentSpanId' => $parentSpanId, - ] = self::convertOtelToJaegerIds($span); - - $startTime = TimeUtil::nanosToMicros($span->getStartEpochNanos()); - $duration = TimeUtil::nanosToMicros($span->getEndEpochNanos() - $span->getStartEpochNanos()); - - $tags = self::convertOtelSpanDataToJaegerTags($span); - - $logs = self::convertOtelEventsToJaegerLogs($span); - - $references = self::convertOtelLinksToJaegerSpanReferences($span); - - return new JTSpan([ - 'traceIdLow' => $traceIdLow, - 'traceIdHigh' => $traceIdHigh, - 'spanId' => $spanId, - 'parentSpanId' => $parentSpanId, - 'operationName' => $span->getName(), - 'flags' => $span->getContext()->getTraceFlags(), - 'startTime' => $startTime, - 'duration' => ($duration < 0) ? 0 : $duration, - 'tags' => $tags, - 'logs' => $logs, - 'references' => $references, - ]); - } - - private static function convertOtelToJaegerIds(SpanDataInterface $span): array - { - [ - 'traceIdLow' => $traceIdLow, - 'traceIdHigh' => $traceIdHigh - ] = IdConverter::convertOtelToJaegerTraceIds($span->getContext()->getTraceID()); - - $spanId = IdConverter::convertOtelToJaegerSpanId($span->getContext()->getSpanID()); - $parentSpanId = IdConverter::convertOtelToJaegerSpanId($span->getParentSpanId()); - - return [ - 'traceIdLow' => $traceIdLow, - 'traceIdHigh' => $traceIdHigh, - 'spanId' => $spanId, - 'parentSpanId' => $parentSpanId, - ]; - } - - private static function convertOtelSpanKindToJaeger(SpanDataInterface $span): ?string - { - switch ($span->getKind()) { - case SpanKind::KIND_CLIENT: - return self::JAEGER_SPAN_KIND_CLIENT; - case SpanKind::KIND_SERVER: - return self::JAEGER_SPAN_KIND_SERVER; - case SpanKind::KIND_CONSUMER: - return self::JAEGER_SPAN_KIND_CONSUMER; - case SpanKind::KIND_PRODUCER: - return self::JAEGER_SPAN_KIND_PRODUCER; - case SpanKind::KIND_INTERNAL: - return null; - } - - return null; - } - - private static function convertOtelSpanDataToJaegerTags(SpanDataInterface $span): array - { - $tags = []; - - if ($span->getStatus()->getCode() !== StatusCode::STATUS_UNSET) { - switch ($span->getStatus()->getCode()) { - case StatusCode::STATUS_OK: - $tags[self::STATUS_CODE_TAG_KEY] = self::STATUS_OK; - - break; - case StatusCode::STATUS_ERROR: - $tags[self::KEY_ERROR_FLAG] = true; - $tags[self::STATUS_CODE_TAG_KEY] = self::STATUS_ERROR; - - break; - } - - if (!empty($span->getStatus()->getDescription())) { - $tags[self::STATUS_DESCRIPTION_TAG_KEY] = $span->getStatus()->getDescription(); - } - } - - if (!empty($span->getInstrumentationScope()->getName())) { - $tags[SpanConverter::KEY_INSTRUMENTATION_SCOPE_NAME] = $span->getInstrumentationScope()->getName(); - } - - if ($span->getInstrumentationScope()->getVersion() !== null) { - $tags[SpanConverter::KEY_INSTRUMENTATION_SCOPE_VERSION] = $span->getInstrumentationScope()->getVersion(); - } - - $jaegerSpanKind = self::convertOtelSpanKindToJaeger($span); - if ($jaegerSpanKind !== null) { - $tags[self::KEY_SPAN_KIND] = $jaegerSpanKind; - } - - foreach ($span->getAttributes() as $k => $v) { - $tags[$k] = $v; - } - - foreach ($span->getResource()->getAttributes() as $k => $v) { - $tags[$k] = $v; - } - foreach ($span->getInstrumentationScope()->getAttributes() as $k => $v) { - $tags[$k] = $v; - } - - return self::buildTags($tags); - } - - private static function buildTags(array $tagPairs): array - { - $tags = []; - foreach ($tagPairs as $key => $value) { - $tags[] = TagFactory::create($key, $value); - } - - return $tags; - } - - private static function convertOtelEventsToJaegerLogs(SpanDataInterface $span): array - { - return array_map( - fn ($event) => self::convertSingleOtelEventToJaegerLog($event), - $span->getEvents() - ); - } - - private static function convertSingleOtelEventToJaegerLog(EventInterface $event): Log - { - $timestamp = TimeUtil::nanosToMicros($event->getEpochNanos()); - - $eventValue = $event->getAttributes()->get(self::EVENT_ATTRIBUTE_KEY_NAMED_EVENT) ?? $event->getName(); - $attributes = $event->getAttributes()->toArray(); - $attributes[self::JAEGER_KEY_EVENT] = $eventValue; - $attributesAsTags = self::buildTags($attributes); - - return new Log([ - 'timestamp' => $timestamp, - 'fields' => $attributesAsTags, - ]); - } - - private static function convertOtelLinksToJaegerSpanReferences(SpanDataInterface $span): array - { - return array_map( - fn ($link) => self::convertSingleOtelLinkToJaegerSpanReference($link), - $span->getLinks() - ); - } - - private static function convertSingleOtelLinkToJaegerSpanReference(LinkInterface $link): SpanRef - { - [ - 'traceIdLow' => $traceIdLow, - 'traceIdHigh' => $traceIdHigh, - ] = IdConverter::convertOtelToJaegerTraceIds($link->getSpanContext()->getTraceId()); - - $integerSpanId = IdConverter::convertOtelToJaegerSpanId($link->getSpanContext()->getSpanId()); - - return new SpanRef([ - 'refType' => SpanRefType::FOLLOWS_FROM, - 'traceIdLow' => $traceIdLow, - 'traceIdHigh' => $traceIdHigh, - 'spanId' => $integerSpanId, - ]); - } -} diff --git a/Jaeger/TagFactory/TagFactory.php b/Jaeger/TagFactory/TagFactory.php deleted file mode 100644 index 946a49e..0000000 --- a/Jaeger/TagFactory/TagFactory.php +++ /dev/null @@ -1,86 +0,0 @@ - $key, - 'vType' => TagType::BOOL, - 'vBool' => $value, - ]); - } - - if (is_int($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 implode(',', array_map(fn ($val) => 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; - } -} diff --git a/Jaeger/ThriftHttpTransport.php b/Jaeger/ThriftHttpTransport.php deleted file mode 100644 index 9461de5..0000000 --- a/Jaeger/ThriftHttpTransport.php +++ /dev/null @@ -1,101 +0,0 @@ -psr18Client = $client; - $this->requestFactory = $requestFactory; - $this->streamFactory = $streamFactory; - - $this->parsedEndpoint = $parsedEndpoint; - } - - public function isOpen() - { - throw new BadMethodCallException(__FUNCTION__ . " is unused as of this writing. See Thrift\Transport\THttpClient for a reference implementation."); - } - - public function open() - { - throw new BadMethodCallException(__FUNCTION__ . " is unused as of this writing. See Thrift\Transport\THttpClient for a reference implementation."); - } - - public function close() - { - throw new BadMethodCallException(__FUNCTION__ . " is unused as of this writing. See Thrift\Transport\THttpClient for a reference implementation."); - } - - public function read($len) - { - throw new BadMethodCallException(__FUNCTION__ . " is unused as of this writing. See Thrift\Transport\THttpClient for a reference implementation."); - } - - /** - * Writes some data into the pending buffer - * - * @param string $buf The data to write - */ - public function write($buf) - { - $this->buffer .= $buf; - } - - /** - * Opens and sends the actual request over the HTTP connection - */ - public function flush() - { - $endpointUrl = $this->parsedEndpoint->getEndpointUrl(); - $host = $this->parsedEndpoint->getHost(); - - $request = $this->requestFactory->createRequest('POST', $endpointUrl); - - $headers = [ - 'Host' => $host, //Port will be implied - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host - 'Accept' => 'application/x-thrift', - 'User-Agent' => 'PHP/THttpClient', - 'Content-Type' => 'application/x-thrift', - 'Content-Length' => TStringFuncFactory::create()->strlen($this->buffer), - ]; - foreach ($headers as $key => $value) { - $request = $request->withAddedHeader($key, $value); - } - - $request = $request->withBody( - $this->streamFactory->createStream($this->buffer) - ); - - $this->psr18Client->sendRequest($request); - - $this->buffer = ''; - } -} diff --git a/Jaeger/ThriftUdpTransport.php b/Jaeger/ThriftUdpTransport.php deleted file mode 100644 index c721f42..0000000 --- a/Jaeger/ThriftUdpTransport.php +++ /dev/null @@ -1,90 +0,0 @@ -server = $parsedEndpoint->getHost(); - $this->port = $parsedEndpoint->getPort(); - - // open a UDP socket to somewhere - if (!($this->socket = \socket_create(AF_INET, SOCK_DGRAM, SOL_UDP))) { - $errorcode = \socket_last_error(); - $errormsg = \socket_strerror($errorcode); - - error_log("jaeger: transport: Couldn't create socket: [$errorcode] $errormsg"); - - throw new TTransportException('unable to open UDP socket', TTransportException::UNKNOWN); - } - } - - public function isOpen() - { - return $this->socket != null; - } - - // Open does nothing as connection is opened on creation - // Required to maintain thrift.TTransport interface - public function open() - { - } - - public function close() - { - \socket_close($this->socket); - $this->socket = null; - } - - public function read($len) - { - // not implemented - return ''; - } - - public function write($buf) - { - // ensure that the data will still fit in a UDP packeg - if (strlen($this->buffer) + strlen($buf) > self::MAX_UDP_PACKET) { - throw new TTransportException('Data does not fit within one UDP packet', TTransportException::UNKNOWN); - } - - // buffer up some data - $this->buffer .= $buf; - } - - public function flush() - { - // no data to send; don't send a packet - if (strlen($this->buffer) == 0) { - return; - } - - // TODO(tylerc): This assumes that the whole buffer successfully sent... I believe - // that this should always be the case for UDP packets, but I could be wrong. - - // flush the buffer to the socket - if (!\socket_sendto($this->socket, $this->buffer, strlen($this->buffer), 0, $this->server, $this->port)) { - $errorcode = \socket_last_error(); - $errormsg = \socket_strerror($errorcode); - error_log("jaeger: transport: Could not flush data: [$errorcode] $errormsg"); - } - - $this->buffer = ''; // empty the buffer - } -} diff --git a/Jaeger/TransportInterface.php b/Jaeger/TransportInterface.php deleted file mode 100644 index 69199e7..0000000 --- a/Jaeger/TransportInterface.php +++ /dev/null @@ -1,33 +0,0 @@ -