Jaeger exporter (#70)
* make zipkin-exporter implementation simpler * add Jaeger exporter and fix zipkin exporter timestamps * move zipkin converter logic outside of exporters * start containers before running examples * fix open-telemetry badge codecov.io url * fix phpunit whitelist after api and sdk splitting
This commit is contained in:
parent
783ccdde06
commit
e5eda43ad9
5
Makefile
5
Makefile
|
@ -5,11 +5,14 @@ install:
|
|||
update:
|
||||
$(DC_RUN_PHP) composer update
|
||||
test:
|
||||
$(DC_RUN_PHP) php ./vendor/bin/phpunit --colors=always
|
||||
$(DC_RUN_PHP) php ./vendor/bin/phpunit --colors=always --coverage-text
|
||||
phan:
|
||||
$(DC_RUN_PHP) env PHAN_DISABLE_XDEBUG_WARN=1 php ./vendor/bin/phan
|
||||
examples: FORCE
|
||||
docker-compose up -d
|
||||
$(DC_RUN_PHP) php ./examples/AlwaysOnTraceExample.php
|
||||
$(DC_RUN_PHP) php ./examples/AlwaysOffTraceExample.php
|
||||
$(DC_RUN_PHP) php ./examples/JaegerExporterExample.php
|
||||
bash:
|
||||
$(DC_RUN_PHP) bash
|
||||
style:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# OpenTelemetry php library
|
||||
[](https://gitter.im/open-telemetry/opentelemetry-php?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
[](https://travis-ci.org/open-telemetry/opentelemetry-php)
|
||||
[](https://codecov.io/gh/opentelemety/opentelemetry-php)
|
||||
[](https://codecov.io/gh/open-telemetry/opentelemetry-php)
|
||||
|
||||
## Communication
|
||||
Most of our communication is done on gitter.im in the [opentelemetry-php](https://gitter.im/open-telemetry/opentelemetry-php) channel.
|
||||
|
|
|
@ -10,3 +10,10 @@ services:
|
|||
image: openzipkin/zipkin-slim
|
||||
ports:
|
||||
- 9411:9411
|
||||
jaeger:
|
||||
image: jaegertracing/all-in-one
|
||||
environment:
|
||||
COLLECTOR_ZIPKIN_HTTP_PORT: 9412
|
||||
ports:
|
||||
- 9412:9412
|
||||
- 16686:16686
|
||||
|
|
|
@ -36,5 +36,7 @@ if (SamplingResult::RECORD_AND_SAMPLED === $samplingResult) {
|
|||
$span->end(); // pass status as an optional argument
|
||||
print_r($span); // print the span as a resulting output
|
||||
} else {
|
||||
echo 'Sampling is not enabled';
|
||||
echo PHP_EOL . 'Sampling is not enabled';
|
||||
}
|
||||
|
||||
echo PHP_EOL;
|
||||
|
|
|
@ -29,9 +29,12 @@ if (SamplingResult::RECORD_AND_SAMPLED === $samplingResult->getDecision()) {
|
|||
[new SimpleSpanProcessor($zipkinExporter)]
|
||||
))
|
||||
->getTracer('io.opentelemetry.contrib.php');
|
||||
|
||||
echo PHP_EOL . sprintf('Trace with id %s started ', $tracer->getActiveSpan()->getContext()->getTraceId());
|
||||
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
// start a span, register some events
|
||||
$span = $tracer->startAndActivateSpan('session.generate.span' . $i);
|
||||
$span = $tracer->startAndActivateSpan('session.generate.span.' . time());
|
||||
$tracer->setActiveSpan($span);
|
||||
|
||||
$span->setAttribute('remote_ip', '1.2.3.4')
|
||||
|
@ -47,7 +50,9 @@ if (SamplingResult::RECORD_AND_SAMPLED === $samplingResult->getDecision()) {
|
|||
|
||||
$tracer->endActiveSpan();
|
||||
}
|
||||
echo 'AlwaysOnTraceExample complete! See the results at http://localhost:9411/';
|
||||
echo PHP_EOL . 'AlwaysOnTraceExample complete! See the results at http://localhost:9411/';
|
||||
} else {
|
||||
echo 'Sampling is not enabled';
|
||||
echo PHP_EOL . 'Sampling is not enabled';
|
||||
}
|
||||
|
||||
echo PHP_EOL;
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use OpenTelemetry\Sdk\Trace\AlwaysOnSampler;
|
||||
use OpenTelemetry\Sdk\Trace\Attributes;
|
||||
use OpenTelemetry\Sdk\Trace\JaegerExporter;
|
||||
use OpenTelemetry\Sdk\Trace\SimpleSpanProcessor;
|
||||
use OpenTelemetry\Sdk\Trace\TracerProvider;
|
||||
|
||||
$sampler = (new AlwaysOnSampler())->shouldSample();
|
||||
|
||||
$exporter = new JaegerExporter(
|
||||
'jaegerExporterExample',
|
||||
'http://jaeger:9412/api/v2/spans'
|
||||
);
|
||||
|
||||
if ($sampler) {
|
||||
echo 'Starting JaegerExporterExample';
|
||||
$tracer = (TracerProvider::getInstance(
|
||||
[new SimpleSpanProcessor($exporter)]
|
||||
))
|
||||
->getTracer('io.opentelemetry.contrib.php');
|
||||
|
||||
echo PHP_EOL . sprintf('Trace with id %s started ', $tracer->getActiveSpan()->getContext()->getTraceId());
|
||||
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
// start a span, register some events
|
||||
$span = $tracer->startAndActivateSpan('session.generate.span' . time());
|
||||
$tracer->setActiveSpan($span);
|
||||
|
||||
$span->setAttribute('remote_ip', '1.2.3.4')
|
||||
->setAttribute('country', 'USA');
|
||||
|
||||
$span->addEvent('found_login' . $i, new Attributes([
|
||||
'id' => $i,
|
||||
'username' => 'otuser' . $i,
|
||||
]));
|
||||
$span->addEvent('generated_session', new Attributes([
|
||||
'id' => md5((string) microtime(true)),
|
||||
]));
|
||||
|
||||
$tracer->endActiveSpan();
|
||||
}
|
||||
echo PHP_EOL . 'JaegerExporterExample complete! See the results at http://localhost:16686/';
|
||||
} else {
|
||||
echo PHP_EOL . 'Sampling is not enabled';
|
||||
}
|
||||
|
||||
echo PHP_EOL;
|
|
@ -32,7 +32,10 @@
|
|||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>src</directory>
|
||||
<directory>sdk</directory>
|
||||
<exclude>
|
||||
<directory>sdk/Tests</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
|
|
|
@ -10,9 +10,7 @@ use OpenTelemetry\Sdk\Trace\Attributes;
|
|||
use OpenTelemetry\Sdk\Trace\SpanContext;
|
||||
use OpenTelemetry\Sdk\Trace\SpanStatus;
|
||||
use OpenTelemetry\Sdk\Trace\Tracer;
|
||||
use OpenTelemetry\Sdk\Trace\ZipkinExporter;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use ReflectionMethod;
|
||||
|
||||
class TracingTest extends TestCase
|
||||
{
|
||||
|
@ -222,34 +220,4 @@ class TracingTest extends TestCase
|
|||
$this->assertNull($global->getParent());
|
||||
$this->assertNotNull($request->getParent());
|
||||
}
|
||||
|
||||
public function testZipkinConverter()
|
||||
{
|
||||
$tracer = new Tracer();
|
||||
$span = $tracer->startAndActivateSpan('guard.validate');
|
||||
$span->setAttribute('service', 'guard');
|
||||
$span->addEvent('validators.list', new Attributes(['job' => 'stage.updateTime']));
|
||||
$span->end();
|
||||
|
||||
$method = new ReflectionMethod(ZipkinExporter::class, 'convertSpan');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$exporter = new ZipkinExporter(
|
||||
'test.name',
|
||||
'http://host:123/path'
|
||||
);
|
||||
|
||||
$row = $method->invokeArgs($exporter, ['span' => $span]);
|
||||
$this->assertSame($row['name'], $span->getSpanName());
|
||||
|
||||
self::assertCount(1, $row['tags']);
|
||||
self::assertEquals($span->getAttribute('service')->getValue(), $row['tags']['service']);
|
||||
|
||||
self::assertCount(1, $row['annotations']);
|
||||
[$annotation] = $row['annotations'];
|
||||
self::assertEquals('validators.list', $annotation['value']);
|
||||
|
||||
[$event] = \iterator_to_array($span->getEvents());
|
||||
self::assertEquals($event->getTimestamp(), $annotation['timestamp']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Sdk\Trace;
|
||||
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use OpenTelemetry\Sdk\Trace\Zipkin\SpanConverter;
|
||||
use OpenTelemetry\Trace as API;
|
||||
|
||||
class JaegerExporter implements Exporter
|
||||
{
|
||||
const IMPLEMENTED_FORMATS = [
|
||||
'/api/v1/spans',
|
||||
'/api/v2/spans',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $endpointUrl;
|
||||
|
||||
/**
|
||||
* @var SpanConverter
|
||||
*/
|
||||
private $spanConverter;
|
||||
|
||||
public function __construct($name, string $endpointUrl, SpanConverter $spanConverter = null)
|
||||
{
|
||||
$url = parse_url($endpointUrl);
|
||||
|
||||
if (!is_array($url)) {
|
||||
throw new InvalidArgumentException('Unable to parse provided DSN');
|
||||
}
|
||||
|
||||
if (!isset($url['scheme'])
|
||||
|| !isset($url['host'])
|
||||
|| !isset($url['port'])
|
||||
|| !isset($url['path'])
|
||||
) {
|
||||
throw new InvalidArgumentException('Endpoint should have scheme, host, port and path');
|
||||
}
|
||||
|
||||
if (!in_array($url['path'], self::IMPLEMENTED_FORMATS)) {
|
||||
throw new InvalidArgumentException(
|
||||
sprintf("Current implementation supports only '%s' format", implode(' or ', self::IMPLEMENTED_FORMATS))
|
||||
);
|
||||
}
|
||||
|
||||
$this->endpointUrl = $endpointUrl;
|
||||
|
||||
$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 (empty($spans)) {
|
||||
return Exporter::SUCCESS;
|
||||
}
|
||||
|
||||
$convertedSpans = [];
|
||||
foreach ($spans as &$span) {
|
||||
array_push($convertedSpans, $this->spanConverter->convert($span));
|
||||
}
|
||||
|
||||
try {
|
||||
$json = json_encode($convertedSpans);
|
||||
$contextOptions = [
|
||||
'http' => [
|
||||
'method' => 'POST',
|
||||
'header' => 'Content-Type: application/json',
|
||||
'content' => $json,
|
||||
],
|
||||
];
|
||||
$context = stream_context_create($contextOptions);
|
||||
@file_get_contents($this->endpointUrl, false, $context);
|
||||
} catch (Exception $e) {
|
||||
return Exporter::FAILED_RETRYABLE;
|
||||
}
|
||||
|
||||
return Exporter::SUCCESS;
|
||||
}
|
||||
|
||||
public function shutdown(): void
|
||||
{
|
||||
// TODO: Implement shutdown() method.
|
||||
}
|
||||
}
|
|
@ -100,14 +100,18 @@ class Tracer implements API\Tracer
|
|||
|
||||
public function endActiveSpan(?string $timestamp = null)
|
||||
{
|
||||
// todo: should processors be called before or after end()?
|
||||
if ($this->getActiveSpan()->isRecording()) {
|
||||
/**
|
||||
* a span should be ended before is sent to exporters, because the exporters need's span duration.
|
||||
*/
|
||||
$span = $this->getActiveSpan();
|
||||
$wasRecording = $span->isRecording();
|
||||
$span->end();
|
||||
|
||||
if ($wasRecording) {
|
||||
foreach ($this->spanProcessors as $spanProcessor) {
|
||||
$spanProcessor->onEnd($this->getActiveSpan());
|
||||
$spanProcessor->onEnd($span);
|
||||
}
|
||||
}
|
||||
|
||||
$this->getActiveSpan()->end($timestamp);
|
||||
}
|
||||
|
||||
private function generateSpanInstance($name, API\SpanContext $context): Span
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Sdk\Trace\Zipkin;
|
||||
|
||||
use OpenTelemetry\Trace\Span;
|
||||
|
||||
class SpanConverter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $serviceName;
|
||||
|
||||
public function __construct(string $serviceName)
|
||||
{
|
||||
$this->serviceName = $serviceName;
|
||||
}
|
||||
|
||||
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) ((float) $span->getStartTimestamp() * 1000),
|
||||
'duration' => (int) ((float) $span->getEndTimestamp() * 1000 - (float) $span->getStartTimestamp() * 1000),
|
||||
];
|
||||
|
||||
foreach ($span->getAttributes() as $k => $v) {
|
||||
if (!array_key_exists('tags', $row)) {
|
||||
$row['tags'] = [];
|
||||
}
|
||||
$v = $v->getValue();
|
||||
if (is_bool($v)) {
|
||||
$v = (string) $v;
|
||||
}
|
||||
$row['tags'][$k] = $v;
|
||||
}
|
||||
|
||||
foreach ($span->getEvents() as $event) {
|
||||
if (!array_key_exists('annotations', $row)) {
|
||||
$row['annotations'] = [];
|
||||
}
|
||||
$row['annotations'][] = [
|
||||
'timestamp' => (int) round((float) $event->getTimestamp() * 1000),
|
||||
'value' => $event->getName(),
|
||||
];
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ namespace OpenTelemetry\Sdk\Trace;
|
|||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
|
||||
use OpenTelemetry\Sdk\Trace\Zipkin\SpanConverter;
|
||||
use OpenTelemetry\Trace as API;
|
||||
|
||||
/**
|
||||
|
@ -15,23 +16,35 @@ use OpenTelemetry\Trace as API;
|
|||
*/
|
||||
class ZipkinExporter implements Exporter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $endpointUrl;
|
||||
|
||||
/**
|
||||
* @var $endpoint array to send Spans to
|
||||
* @var SpanConverter
|
||||
*/
|
||||
private $endpoint;
|
||||
private $name;
|
||||
private $spanConverter;
|
||||
|
||||
public function __construct($name, string $endpointDsn)
|
||||
public function __construct($name, string $endpointUrl, SpanConverter $spanConverter = null)
|
||||
{
|
||||
$parsedDsn = parse_url($endpointDsn);
|
||||
$parsedDsn = parse_url($endpointUrl);
|
||||
|
||||
if (!is_array($parsedDsn)) {
|
||||
throw new InvalidArgumentException('Unable to parse provided DSN');
|
||||
}
|
||||
|
||||
$this->setEndpoint($parsedDsn);
|
||||
$this->name = $name;
|
||||
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->spanConverter = $spanConverter ?? new SpanConverter($name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -48,7 +61,7 @@ class ZipkinExporter implements Exporter
|
|||
|
||||
$convertedSpans = [];
|
||||
foreach ($spans as &$span) {
|
||||
array_push($convertedSpans, $this->convertSpan($span));
|
||||
array_push($convertedSpans, $this->spanConverter->convert($span));
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -61,7 +74,7 @@ class ZipkinExporter implements Exporter
|
|||
],
|
||||
];
|
||||
$context = stream_context_create($contextOptions);
|
||||
file_get_contents($this->getEndpointUrl(), false, $context);
|
||||
@file_get_contents($this->endpointUrl, false, $context);
|
||||
} catch (Exception $e) {
|
||||
return Exporter::FAILED_RETRYABLE;
|
||||
}
|
||||
|
@ -69,93 +82,6 @@ class ZipkinExporter implements Exporter
|
|||
return Exporter::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts spans to Zipkin format for export
|
||||
*
|
||||
* @param API\Span $span
|
||||
* @return array
|
||||
*/
|
||||
private function convertSpan(API\Span $span) : array
|
||||
{
|
||||
$row = [
|
||||
'id' => $span->getContext()->getSpanId(),
|
||||
'traceId' => $span->getContext()->getTraceId(),
|
||||
'parentId' => $span->getParent() ? $span->getParent()->getSpanId() : null,
|
||||
'localEndpoint' => [
|
||||
'serviceName' => $this->name,
|
||||
'port' => $this->getEndpoint()['port'] ?? 0,
|
||||
],
|
||||
'name' => $span->getSpanName(),
|
||||
'timestamp' => (int) round((float) $span->getStartTimestamp()),
|
||||
'duration' => (int) round((float) $span->getEndTimestamp()) - round((float) $span->getStartTimestamp()),
|
||||
];
|
||||
|
||||
foreach ($span->getAttributes() as $k => $v) {
|
||||
if (!array_key_exists('tags', $row)) {
|
||||
$row['tags'] = [];
|
||||
}
|
||||
$v = $v->getValue();
|
||||
if (is_bool($v)) {
|
||||
$v = (string) $v;
|
||||
}
|
||||
$row['tags'][$k] = $v;
|
||||
}
|
||||
|
||||
foreach ($span->getEvents() as $event) {
|
||||
if (!array_key_exists('annotations', $row)) {
|
||||
$row['annotations'] = [];
|
||||
}
|
||||
$row['annotations'][] = [
|
||||
'timestamp' => $event->getTimestamp(),
|
||||
'value' => $event->getName(),
|
||||
];
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configured endpoint for the Zipkin exporter
|
||||
*
|
||||
* @return array |null
|
||||
*/
|
||||
public function getEndpoint(): ?array
|
||||
{
|
||||
return $this->endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the configured endpoint for the zipkin exportedr
|
||||
*
|
||||
* @param array $endpoint
|
||||
* @return $this
|
||||
*/
|
||||
public function setEndpoint(array $endpoint) : self
|
||||
{
|
||||
if (!isset($endpoint['scheme'])
|
||||
|| !isset($endpoint['host'])
|
||||
|| !isset($endpoint['port'])
|
||||
|| !isset($endpoint['path'])
|
||||
) {
|
||||
throw new InvalidArgumentException('Endpoint should have scheme, host, port and path');
|
||||
}
|
||||
|
||||
$this->endpoint = $endpoint;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getEndpointUrl(): string
|
||||
{
|
||||
return sprintf(
|
||||
'%s://%s:%s%s',
|
||||
$this->endpoint['scheme'],
|
||||
$this->endpoint['host'],
|
||||
$this->endpoint['port'],
|
||||
$this->endpoint['path']
|
||||
);
|
||||
}
|
||||
|
||||
public function shutdown(): void
|
||||
{
|
||||
// TODO: Implement shutdown() method.
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Tests\Unit\Exporter;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use OpenTelemetry\Sdk\Trace\JaegerExporter;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class JaegerExporterTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
* @dataProvider invalidDsnDataProvider
|
||||
*/
|
||||
public function shouldThrowExceptionIfInvalidDsnIsPassed($invalidDsn)
|
||||
{
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
|
||||
new JaegerExporter('test.zipkin', $invalidDsn);
|
||||
}
|
||||
|
||||
public function invalidDsnDataProvider()
|
||||
{
|
||||
return [
|
||||
'missing scheme' => ['host:123/path'],
|
||||
'missing host' => ['scheme://123/path'],
|
||||
'missing port' => ['scheme://host/path'],
|
||||
'missing path' => ['scheme://host:123'],
|
||||
'invalid port' => ['scheme://host:port/path'],
|
||||
'invalid scheme' => ['1234://host:port/path'],
|
||||
'invalid host' => ['scheme:///end:1234/path'],
|
||||
'unimplemented path' => ['scheme:///host:1234/api/v1/spans'],
|
||||
];
|
||||
}
|
||||
}
|
|
@ -10,19 +10,6 @@ use PHPUnit\Framework\TestCase;
|
|||
|
||||
class ZipkinExporterTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function shouldParseAnValidDsn()
|
||||
{
|
||||
$exporter = new ZipkinExporter('test.zipkin', 'scheme://host:1234/path');
|
||||
|
||||
$this->assertArrayHasKey('scheme', $exporter->getEndpoint());
|
||||
$this->assertArrayHasKey('host', $exporter->getEndpoint());
|
||||
$this->assertArrayHasKey('port', $exporter->getEndpoint());
|
||||
$this->assertArrayHasKey('path', $exporter->getEndpoint());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @dataProvider invalidDsnDataProvider
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace unit\Exporter;
|
||||
|
||||
use OpenTelemetry\Sdk\Trace\Attributes;
|
||||
use OpenTelemetry\Sdk\Trace\Tracer;
|
||||
use OpenTelemetry\Sdk\Trace\Zipkin\SpanConverter;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ZipkinSpanConverterTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function shouldConvertASpanToAPayloadForZipkin()
|
||||
{
|
||||
$tracer = new Tracer();
|
||||
$span = $tracer->startAndActivateSpan('guard.validate');
|
||||
$span->setAttribute('service', 'guard');
|
||||
$span->addEvent('validators.list', 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(1, $row['tags']);
|
||||
$this->assertEquals($span->getAttribute('service')->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']);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue