Skip to content

Commit b155847

Browse files
authored
Make ResourceInfo available to component providers (#1729)
* Fix declarative config resource merge order * Make resource info available in component providers
1 parent ddb222d commit b155847

File tree

7 files changed

+223
-5
lines changed

7 files changed

+223
-5
lines changed

src/API/Configuration/Context.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,41 @@
1616

1717
final class Context
1818
{
19+
/** @psalm-var class-string-map<T, T> */
20+
private array $extensions = [];
21+
1922
public function __construct(
2023
public readonly TracerProviderInterface $tracerProvider = new NoopTracerProvider(),
2124
public readonly MeterProviderInterface $meterProvider = new NoopMeterProvider(),
2225
public readonly LoggerProviderInterface $loggerProvider = new NoopLoggerProvider(),
2326
public readonly LoggerInterface $logger = new NullLogger(),
2427
) {
2528
}
29+
30+
/**
31+
* @psalm-template T of object
32+
* @psalm-param T $extension
33+
* @psalm-param class-string<T>|null $type
34+
*/
35+
public function withExtension(object $extension, ?string $type = null): self
36+
{
37+
$type ??= $extension::class;
38+
39+
$clone = clone $this;
40+
$clone->extensions[$type] = $extension;
41+
42+
return $clone;
43+
}
44+
45+
/**
46+
* @psalm-template T of object
47+
* @psalm-param class-string<T> $type
48+
* @psalm-return T|null
49+
*/
50+
public function getExtension(string $type): ?object
51+
{
52+
return $this->extensions[$type] ?? null;
53+
}
2654
}
2755

2856
/** @phpstan-ignore-next-line @phan-suppress-next-line PhanUndeclaredClassReference */

src/API/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
},
3636
"extra": {
3737
"branch-alias": {
38-
"dev-main": "1.7.x-dev"
38+
"dev-main": "1.8.x-dev"
3939
},
4040
"spi": {
4141
"OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [

src/Config/SDK/ComponentProvider/OpenTelemetrySdk.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,12 +221,14 @@ public function createPlugin(array $properties, Context $context): SdkBuilder
221221
$attributes = AttributesParser::parseAttributesList($properties['resource']['attributes_list']);
222222
$attributes = array_merge($attributes, AttributesParser::parseAttributes($properties['resource']['attributes']));
223223

224-
$resource = $resource
224+
$resource = $mandatory
225+
->merge($resource)
225226
->merge(ResourceInfo::create(
226227
attributes: Attributes::create($attributes),
227228
schemaUrl: $schemaUrl,
228-
))
229-
->merge($mandatory);
229+
));
230+
231+
$context = $context->withExtension($resource, ResourceInfo::class);
230232

231233
$spanProcessors = [];
232234
foreach ($properties['tracer_provider']['processors'] as $processor) {

src/Config/SDK/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
],
1919
"require": {
2020
"php": "^8.1",
21-
"open-telemetry/api": "^1.6",
21+
"open-telemetry/api": "^1.8",
2222
"open-telemetry/context": "^1.4",
2323
"open-telemetry/sdk": "^1.8",
2424
"symfony/config": "^5.4 || ^6.4 || ^7.0",
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\Tests\Integration\Config\ComponentProvider\Detector;
6+
7+
use OpenTelemetry\API\Configuration\Config\ComponentProvider;
8+
use OpenTelemetry\API\Configuration\Config\ComponentProviderRegistry;
9+
use OpenTelemetry\API\Configuration\Context;
10+
use OpenTelemetry\SDK\Common\Attribute\Attributes;
11+
use OpenTelemetry\SDK\Resource\Detectors\Constant;
12+
use OpenTelemetry\SDK\Resource\ResourceDetectorInterface;
13+
use OpenTelemetry\SDK\Resource\ResourceInfo;
14+
use Override;
15+
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
16+
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
17+
18+
/**
19+
* @implements ComponentProvider<ResourceDetectorInterface>
20+
*/
21+
final class ServiceName implements ComponentProvider
22+
{
23+
public function __construct(
24+
private readonly string $serviceName,
25+
) {
26+
}
27+
28+
#[Override]
29+
public function createPlugin(array $properties, Context $context): ResourceDetectorInterface
30+
{
31+
return new Constant(ResourceInfo::create(Attributes::create(['service.name' => $this->serviceName])));
32+
}
33+
34+
#[Override]
35+
public function getConfig(ComponentProviderRegistry $registry, NodeBuilder $builder): ArrayNodeDefinition
36+
{
37+
return $builder->arrayNode('service_name');
38+
}
39+
}

tests/Integration/Config/ConfigurationTest.php

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,31 @@
44

55
namespace OpenTelemetry\Tests\Integration\Config;
66

7+
use OpenTelemetry\API\Configuration\Config\ComponentProvider;
8+
use OpenTelemetry\API\Configuration\Config\ComponentProviderRegistry;
9+
use OpenTelemetry\API\Configuration\Context;
710
use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator;
11+
use OpenTelemetry\Config\SDK\ComponentProvider\OpenTelemetrySdk;
812
use OpenTelemetry\Config\SDK\ComponentProvider\OutputStreamParser;
913
use OpenTelemetry\Config\SDK\Configuration;
14+
use OpenTelemetry\Config\SDK\Configuration\ConfigurationFactory;
15+
use OpenTelemetry\Config\SDK\Configuration\Environment\EnvSourceReader;
1016
use OpenTelemetry\Context\Propagation\ResponsePropagatorInterface;
1117
use OpenTelemetry\SDK\Resource\ResourceInfo;
1218
use OpenTelemetry\SDK\Sdk;
19+
use OpenTelemetry\SDK\Trace\Sampler\AlwaysOnSampler;
20+
use OpenTelemetry\SDK\Trace\SamplerInterface;
21+
use OpenTelemetry\Tests\Integration\Config\ComponentProvider\Detector\ServiceName;
1322
use org\bovigo\vfs\vfsStream;
23+
use Override;
1424
use PHPUnit\Framework\Attributes\CoversNothing;
1525
use PHPUnit\Framework\Attributes\DataProvider;
26+
use PHPUnit\Framework\Attributes\Depends;
1627
use PHPUnit\Framework\Attributes\DoesNotPerformAssertions;
1728
use PHPUnit\Framework\TestCase;
29+
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
30+
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
31+
use Symfony\Component\Yaml\Yaml;
1832

1933
#[CoversNothing]
2034
final class ConfigurationTest extends TestCase
@@ -198,6 +212,109 @@ public function test_duplicate_response_propagators(): void
198212
$this->assertInstanceOf(ResponsePropagatorInterface::class, $responsePropagators[0]);
199213
}
200214

215+
public function test_resource_attributes_take_precedence_over_default_attributes(): void
216+
{
217+
$factory = new ConfigurationFactory(
218+
[],
219+
new OpenTelemetrySdk(),
220+
new EnvSourceReader([]),
221+
);
222+
223+
$sdk = $factory->process([Yaml::parse(/** @lang yaml */<<<'YAML'
224+
file_format: "0.4"
225+
resource:
226+
attributes:
227+
- { name: service.name, value: test-service }
228+
YAML)]);
229+
$resource = $this->getResource($sdk->create(new Context())->build());
230+
231+
$this->assertSame('test-service', $resource->getAttributes()->get('service.name'));
232+
}
233+
234+
public function test_resource_detectors_take_precedence_over_default_attributes(): void
235+
{
236+
$factory = new ConfigurationFactory(
237+
[new ServiceName('test-service')],
238+
new OpenTelemetrySdk(),
239+
new EnvSourceReader([]),
240+
);
241+
242+
$sdk = $factory->process([Yaml::parse(/** @lang yaml */<<<'YAML'
243+
file_format: "0.4"
244+
resource:
245+
detection/development:
246+
detectors:
247+
- service_name:
248+
YAML)]);
249+
$resource = $this->getResource($sdk->create(new Context())->build());
250+
251+
$this->assertSame('test-service', $resource->getAttributes()->get('service.name'));
252+
}
253+
254+
#[Depends('test_resource_attributes_take_precedence_over_default_attributes')]
255+
#[Depends('test_resource_detectors_take_precedence_over_default_attributes')]
256+
public function test_resource_attributes_take_precedence_over_resource_detectors(): void
257+
{
258+
$factory = new ConfigurationFactory(
259+
[new ServiceName('should-be-overridden')],
260+
new OpenTelemetrySdk(),
261+
new EnvSourceReader([]),
262+
);
263+
264+
$sdk = $factory->process([Yaml::parse(/** @lang yaml */<<<'YAML'
265+
file_format: "0.4"
266+
resource:
267+
attributes:
268+
- { name: service.name, value: test-service }
269+
detection/development:
270+
detectors:
271+
- service_name:
272+
YAML)]);
273+
$resource = $this->getResource($sdk->create(new Context())->build());
274+
275+
$this->assertSame('test-service', $resource->getAttributes()->get('service.name'));
276+
}
277+
278+
public function test_samplers_have_access_to_resource_info_extension(): void
279+
{
280+
$samplerProvider = new /** @implements ComponentProvider<SamplerInterface> */ class() implements ComponentProvider {
281+
public ?string $serviceName = null;
282+
283+
#[Override]
284+
public function createPlugin(array $properties, Context $context): SamplerInterface
285+
{
286+
$this->serviceName = $context->getExtension(ResourceInfo::class)?->getAttributes()->get('service.name');
287+
288+
return new AlwaysOnSampler();
289+
}
290+
291+
#[Override]
292+
public function getConfig(ComponentProviderRegistry $registry, NodeBuilder $builder): ArrayNodeDefinition
293+
{
294+
return $builder->arrayNode('remote_sampler');
295+
}
296+
};
297+
298+
$factory = new ConfigurationFactory(
299+
[$samplerProvider],
300+
new OpenTelemetrySdk(),
301+
new EnvSourceReader([]),
302+
);
303+
304+
$sdk = $factory->process([Yaml::parse(/** @lang yaml */<<<'YAML'
305+
file_format: "0.4"
306+
resource:
307+
attributes:
308+
- { name: service.name, value: test-service }
309+
tracer_provider:
310+
sampler:
311+
remote_sampler:
312+
YAML)]);
313+
$sdk->create(new Context());
314+
315+
$this->assertSame('test-service', $samplerProvider->serviceName);
316+
}
317+
201318
private function getResource(Sdk $sdk): ResourceInfo
202319
{
203320
$tracer = $sdk->getTracerProvider()->getTracer('test');
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace API\Configuration;
6+
7+
use OpenTelemetry\API\Configuration\Context;
8+
use OpenTelemetry\SDK\Common\Attribute\Attributes;
9+
use OpenTelemetry\SDK\Resource\ResourceInfo;
10+
use PHPUnit\Framework\Attributes\CoversClass;
11+
use PHPUnit\Framework\TestCase;
12+
13+
#[CoversClass(Context::class)]
14+
final class ConfigurationContextTest extends TestCase
15+
{
16+
public function test_context_missing_extensions_returns_null(): void
17+
{
18+
$context = new Context();
19+
20+
$this->assertNull($context->getExtension(ResourceInfo::class));
21+
}
22+
23+
public function test_context_extension_returns_assigned_value(): void
24+
{
25+
$context = new Context();
26+
$context = $context->withExtension(ResourceInfo::create(Attributes::create([
27+
'service.name' => 'test-service',
28+
])));
29+
30+
$this->assertSame('test-service', $context->getExtension(ResourceInfo::class)?->getAttributes()->get('service.name'));
31+
}
32+
}

0 commit comments

Comments
 (0)