Skip to content

Instantly share code, notes, and snippets.

@dkarlovi
Created April 8, 2021 10:53
Show Gist options
  • Save dkarlovi/ec38d1c4cc47b85609c6e2bffa2451e5 to your computer and use it in GitHub Desktop.
Save dkarlovi/ec38d1c4cc47b85609c6e2bffa2451e5 to your computer and use it in GitHub Desktop.
Zipkin exporter decoupled from Guzzle
<?php
declare(strict_types=1);
namespace OpenTelemetry\Contrib\Zipkin;
use InvalidArgumentException;
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 Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
/**
* Class ZipkinExporter - implements the export interface for data transfer via Zipkin protocol
* @package OpenTelemetry\Exporter
*/
class Exporter implements Trace\Exporter
{
/**
* @var string
*/
private $endpointUrl;
/**
* @var SpanConverter
*/
private $spanConverter;
/**
* @var bool
*/
private $running = true;
/**
* @var ClientInterface|null
*/
private $client;
private $requestFactory;
private $streamFactory;
public function __construct(
$name,
string $endpointUrl,
SpanConverter $spanConverter = null,
ClientInterface $client = null,
RequestFactoryInterface $requestFactory,
StreamFactoryInterface $streamFactory
) {
$parsedDsn = parse_url($endpointUrl);
if (!is_array($parsedDsn)) {
throw new InvalidArgumentException('Unable to parse provided DSN');
}
if (
!isset($parsedDsn['scheme'])
|| !isset($parsedDsn['host'])
|| !isset($parsedDsn['port'])
|| !isset($parsedDsn['path'])
) {
throw new InvalidArgumentException('Endpoint should have scheme, host, port and path');
}
$this->endpointUrl = $endpointUrl;
$this->client = $client;
$this->requestFactory = $requestFactory;
$this->streamFactory = $streamFactory;
$this->spanConverter = $spanConverter ?? new SpanConverter($name);
}
/**
* Exports the provided Span data via the Zipkin protocol
*
* @param iterable<API\Span> $spans Array of Spans
* @return int return code, defined on the Exporter interface
*/
public function export(iterable $spans): int
{
if (!$this->running) {
return Exporter::FAILED_NOT_RETRYABLE;
}
if (empty($spans)) {
return Trace\Exporter::SUCCESS;
}
$convertedSpans = [];
foreach ($spans as $span) {
array_push($convertedSpans, $this->spanConverter->convert($span));
}
try {
$body = $this->streamFactory->createStream(json_encode($convertedSpans));
$request = $this->requestFactory
->createRequest('POST', $this->endpointUrl)
->withBody($body)
->withHeader('content-type', 'application/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;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment