Skip to content

Commit 39c99e5

Browse files
Grunettidal
andauthored
Map OTEL Resources to Jaeger Process Tags (#636)
* Commit in progress work because Gitpod is weird * Finish initial stab at the changes * Forgot to update the exporter * Add in proper handling of serialization of ResourceInfo * ksort doesn't return the array... * Only PHP 8.1 supports key-ed array destructuring * Creating some adapters to break the hard dependency on Batch to make testing easier * Refactoring to break the hard dependency on Batch * Add initial unit tests around the new logic in HttpSender * Some refactoring of the test's helpers * Another small refactor * Some more refactoring * Rename ResourceInfo's test file to match it's file name * Move a test our of REsourceInfoTest into a more appropriate file * Add some basic conformance tests around the new ResourceInfo::serialize method * Add a test to try and catch future properties that aren't added to ResourceInfo::serialize * CLean up comments * Replace string with constant * Improve variable names * Cleaning up comment * Split out tag creation into a helper class * Phan didn't like the type docs * Fix style * Fix tests after merge from main * Fix coverage reporting issues on 2 of the new files * Fix style * Rename vals to values * Shortening the batch adapter factory method name to create * Inline a method call * Inline some method calls * Shorten factory method to just "create" * Inline some code * Split assertions into 3 tests * Rename parameter in interface implementation for psalm + update the rest of the file to match Co-authored-by: Timo Michna <[email protected]>
1 parent a2f6405 commit 39c99e5

File tree

14 files changed

+490
-147
lines changed

14 files changed

+490
-147
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\Contrib\Jaeger\BatchAdapter;
6+
7+
use Jaeger\Thrift\Batch;
8+
use Thrift\Protocol\TProtocol;
9+
10+
class BatchAdapter implements BatchAdapterInterface
11+
{
12+
private Batch $batchInstance;
13+
14+
public function __construct(array $values)
15+
{
16+
$this->batchInstance = new Batch($values);
17+
}
18+
19+
public function write(TProtocol $output): void
20+
{
21+
$this->batchInstance->write($output);
22+
}
23+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\Contrib\Jaeger\BatchAdapter;
6+
7+
class BatchAdapterFactory implements BatchAdapterFactoryInterface
8+
{
9+
public function create(array $values): BatchAdapterInterface
10+
{
11+
return new BatchAdapter($values);
12+
}
13+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\Contrib\Jaeger\BatchAdapter;
6+
7+
interface BatchAdapterFactoryInterface
8+
{
9+
public function create(array $values): BatchAdapterInterface;
10+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\Contrib\Jaeger\BatchAdapter;
6+
7+
use Thrift\Protocol\TProtocol;
8+
9+
interface BatchAdapterInterface
10+
{
11+
public function write(TProtocol $output): void;
12+
}

src/Contrib/Jaeger/HttpCollectorExporter.php

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ class HttpCollectorExporter implements SpanExporterInterface
1818
use UsesSpanConverterTrait;
1919
use SpanExporterTrait;
2020

21-
private SpanConverter $spanConverter;
22-
2321
private HttpSender $sender;
2422

2523
public function __construct(
@@ -39,18 +37,14 @@ public function __construct(
3937
$name,
4038
$parsedEndpoint
4139
);
42-
43-
$this->spanConverter = new SpanConverter();
4440
}
4541

4642
/**
4743
* @psalm-return SpanExporterInterface::STATUS_*
4844
*/
4945
public function doExport(iterable $spans): int
5046
{
51-
$this->sender->send(
52-
$this->spanConverter->convert($spans)
53-
);
47+
$this->sender->send($spans);
5448

5549
return SpanExporterInterface::STATUS_SUCCESS;
5650
}

src/Contrib/Jaeger/HttpSender.php

Lines changed: 88 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@
44

55
namespace OpenTelemetry\Contrib\Jaeger;
66

7-
use Jaeger\Thrift\Batch;
87
use Jaeger\Thrift\Process;
9-
use Jaeger\Thrift\Span as JTSpan;
8+
use OpenTelemetry\Contrib\Jaeger\BatchAdapter\BatchAdapterFactory;
9+
use OpenTelemetry\Contrib\Jaeger\BatchAdapter\BatchAdapterFactoryInterface;
10+
use OpenTelemetry\Contrib\Jaeger\BatchAdapter\BatchAdapterInterface;
11+
use OpenTelemetry\Contrib\Jaeger\TagFactory\TagFactory;
1012
use OpenTelemetry\SDK\Behavior\LogsMessagesTrait;
13+
use OpenTelemetry\SDK\Resource\ResourceInfo;
14+
use OpenTelemetry\SemConv\ResourceAttributes;
1115
use Psr\Http\Client\ClientInterface;
1216
use Psr\Http\Message\RequestFactoryInterface;
1317
use Psr\Http\Message\StreamFactoryInterface;
@@ -22,75 +26,104 @@ class HttpSender
2226

2327
private TProtocol $protocol;
2428

29+
private BatchAdapterFactoryInterface $batchAdapterFactory;
30+
2531
public function __construct(
2632
ClientInterface $client,
2733
RequestFactoryInterface $requestFactory,
2834
StreamFactoryInterface $streamFactory,
2935
string $serviceName,
30-
ParsedEndpointUrl $parsedEndpoint
36+
ParsedEndpointUrl $parsedEndpoint,
37+
BatchAdapterFactoryInterface $batchAdapterFactory = null
3138
) {
3239
$this->serviceName = $serviceName;
3340

34-
$transport = (new ThriftHttpTransport(
35-
$client,
36-
$requestFactory,
37-
$streamFactory,
38-
$parsedEndpoint
39-
));
41+
$this->protocol = new TBinaryProtocol(
42+
new ThriftHttpTransport(
43+
$client,
44+
$requestFactory,
45+
$streamFactory,
46+
$parsedEndpoint
47+
)
48+
);
49+
50+
$this->batchAdapterFactory = $batchAdapterFactory ?? new BatchAdapterFactory();
51+
}
52+
53+
public function send(iterable $spans): void
54+
{
55+
$batches = $this->createBatchesPerResource(
56+
self::groupSpansByResource($spans)
57+
);
58+
59+
foreach ($batches as $batch) {
60+
$this->sendBatch($batch);
61+
}
62+
}
63+
64+
private static function groupSpansByResource(iterable $spans): array
65+
{
66+
$spansGroupedByResource = [];
67+
foreach ($spans as $span) {
68+
/** @var ResourceInfo */
69+
$resource = $span->getResource();
70+
$resourceAsKey = $resource->serialize();
71+
72+
if (!isset($spansGroupedByResource[$resourceAsKey])) {
73+
$spansGroupedByResource[$resourceAsKey] = [
74+
'spans' => [],
75+
'resource' => $resource,
76+
];
77+
}
78+
79+
$spansGroupedByResource[$resourceAsKey]['spans'][] = $span;
80+
}
81+
82+
return $spansGroupedByResource;
83+
}
4084

41-
$this->protocol = new TBinaryProtocol($transport);
85+
private function createBatchesPerResource(array $spansGroupedByResource): array
86+
{
87+
$batches = [];
88+
foreach ($spansGroupedByResource as $unused => $dataForBatch) {
89+
$batch = $this->batchAdapterFactory->create([
90+
'spans' => (new SpanConverter())->convert(
91+
$dataForBatch['spans']
92+
),
93+
'process' => $this->createProcessFromResource(
94+
$dataForBatch['resource']
95+
),
96+
]);
97+
98+
$batches[] = $batch;
99+
}
100+
101+
return $batches;
42102
}
43103

44-
/**
45-
* @param JTSpan[] $spans
46-
*/
47-
public function send(array $spans): void
104+
private function createProcessFromResource(ResourceInfo $resource): Process
48105
{
49-
///** @var Tag[] $tags */ TODO - uncomment this once the code below is uncommented/adapted
106+
$serviceName = $this->serviceName; //Defaulting to (what should be) the default resource's service name
107+
50108
$tags = [];
109+
foreach ($resource->getAttributes() as $key => $value) {
110+
if ($key === ResourceAttributes::SERVICE_NAME) {
111+
$serviceName = (string) $value;
51112

52-
//TODO - determine what of this is still needed and how to adapt it for spec compliance
53-
// foreach ($this->tracer->getTags() as $k => $v) {
54-
// if (!in_array($k, $this->mapper->getSpecialSpanTags())) {
55-
// if (strpos($k, $this->mapper->getProcessTagsPrefix()) !== 0) {
56-
// continue ;
57-
// }
58-
59-
// $quoted = preg_quote($this->mapper->getProcessTagsPrefix());
60-
// $k = preg_replace(sprintf('/^%s/', $quoted), '', $k);
61-
// }
62-
63-
// if ($k === JAEGER_HOSTNAME_TAG_KEY) {
64-
// $k = "hostname";
65-
// }
66-
67-
// $tags[] = new Tag([
68-
// "key" => $k,
69-
// "vType" => TagType::STRING,
70-
// "vStr" => $v
71-
// ]);
72-
// }
73-
74-
// $tags[] = new Tag([
75-
// "key" => "format",
76-
// "vType" => TagType::STRING,
77-
// "vStr" => "jaeger.thrift"
78-
// ]);
79-
80-
// $tags[] = new Tag([
81-
// "key" => "ip",
82-
// "vType" => TagType::STRING,
83-
// "vStr" => $this->tracer->getIpAddress()
84-
// ]);
85-
86-
$batch = new Batch([
87-
'spans' => $spans,
88-
'process' => new Process([
89-
'serviceName' => $this->serviceName,
90-
'tags' => $tags,
91-
]),
113+
continue;
114+
}
115+
116+
$tags[] = TagFactory::create($key, $value);
117+
}
118+
119+
return new Process([
120+
'serviceName' => $serviceName,
121+
'tags' => $tags,
92122
]);
123+
}
93124

125+
private function sendBatch(BatchAdapterInterface $batch): void
126+
{
94127
$batch->write($this->protocol);
95128
$this->protocol->getTransport()->flush();
96129
}

src/Contrib/Jaeger/SpanConverter.php

Lines changed: 2 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@
88
use Jaeger\Thrift\Span as JTSpan;
99
use Jaeger\Thrift\SpanRef;
1010
use Jaeger\Thrift\SpanRefType;
11-
use Jaeger\Thrift\Tag;
12-
use Jaeger\Thrift\TagType;
1311
use OpenTelemetry\API\Trace\SpanKind;
1412
use OpenTelemetry\API\Trace\StatusCode;
13+
use OpenTelemetry\Contrib\Jaeger\TagFactory\TagFactory;
1514
use OpenTelemetry\SDK\Common\Time\Util as TimeUtil;
1615
use OpenTelemetry\SDK\Trace\EventInterface;
1716
use OpenTelemetry\SDK\Trace\LinkInterface;
@@ -197,89 +196,12 @@ private static function buildTags(array $tagPairs): array
197196
{
198197
$tags = [];
199198
foreach ($tagPairs as $key => $value) {
200-
$tags[] = self::buildTag($key, $value);
199+
$tags[] = TagFactory::create($key, $value);
201200
}
202201

203202
return $tags;
204203
}
205204

206-
private static function buildTag(string $key, $value): Tag
207-
{
208-
return self::createJaegerTagInstance(
209-
$key,
210-
self::convertValueToTypeJaegerTagsSupport($value)
211-
);
212-
}
213-
214-
private static function convertValueToTypeJaegerTagsSupport($value)
215-
{
216-
if (is_array($value)) {
217-
return self::serializeArrayToString($value);
218-
}
219-
220-
return $value;
221-
}
222-
223-
private static function createJaegerTagInstance(string $key, $value)
224-
{
225-
if (is_bool($value)) {
226-
return new Tag([
227-
'key' => $key,
228-
'vType' => TagType::BOOL,
229-
'vBool' => $value,
230-
]);
231-
}
232-
233-
if (is_integer($value)) {
234-
return new Tag([
235-
'key' => $key,
236-
'vType' => TagType::LONG,
237-
'vLong' => $value,
238-
]);
239-
}
240-
241-
if (is_numeric($value)) {
242-
return new Tag([
243-
'key' => $key,
244-
'vType' => TagType::DOUBLE,
245-
'vDouble' => $value,
246-
]);
247-
}
248-
249-
return new Tag([
250-
'key' => $key,
251-
'vType' => TagType::STRING,
252-
'vStr' => (string) $value,
253-
]);
254-
}
255-
256-
private static function serializeArrayToString(array $arrayToSerialize): string
257-
{
258-
return self::recursivelySerializeArray($arrayToSerialize);
259-
}
260-
261-
private static function recursivelySerializeArray($value): string
262-
{
263-
if (is_array($value)) {
264-
return join(',', array_map(function ($val) {
265-
return self::recursivelySerializeArray($val);
266-
}, $value));
267-
}
268-
269-
// Casting false to string makes an empty string
270-
if (is_bool($value)) {
271-
return $value ? 'true' : 'false';
272-
}
273-
274-
// Floats will lose precision if their string representation
275-
// is >=14 or >=17 digits, depending on PHP settings.
276-
// Can also throw E_RECOVERABLE_ERROR if $value is an object
277-
// without a __toString() method.
278-
// This is possible because OpenTelemetry\Trace\Span does not verify
279-
// setAttribute() $value input.
280-
return (string) $value;
281-
}
282-
283205
private static function convertOtelEventsToJaegerLogs(SpanDataInterface $span): array
284206
{
285207
return array_map(

0 commit comments

Comments
 (0)