Skip to content

Commit a54f48b

Browse files
Configuration\CompositeResolver and SPI discovery (#1523)
* Configuration\CompositeResolver can discover additional configuration resolvers via SPI. * Apply suggestions from code review Co-authored-by: Tobias Bachert <[email protected]> * Fix imports. * Porting EnvSourceProvider changes from Nevay. * Moved VlucasPhpdotenvProvider to sdk-configuration. * Added SdkConfigurationResolver. * Added SymfonyDotenvProvider. * Removed vlucas/phpdotenv dev package. * make style 😎 * Update deptrac for dotenv providers. * Suppress Psalm errors for optional packages. * Added phpstan ignoreErrors rules for optional dependency behavior. * Added test for SPI-backed configuration loading via EnvSourceProvider * Restructure SPI based test for EnvSourceProvider. * Bumped SPI dependency. * Added implied mixed return type to ResolverInterface::retrieveValue --------- Co-authored-by: Tobias Bachert <[email protected]>
1 parent e54a43d commit a54f48b

18 files changed

+274
-32
lines changed

composer.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,9 @@
8888
},
8989
"require-dev": {
9090
"ext-grpc": "*",
91-
"grpc/grpc": "^1.30",
9291
"bamarni/composer-bin-plugin": "^1.8",
9392
"dg/bypass-finals": "^1.4",
93+
"grpc/grpc": "^1.30",
9494
"guzzlehttp/guzzle": "^7.4",
9595
"guzzlehttp/psr7": "^2.1",
9696
"mikey179/vfsstream": "^1.6.11",
@@ -193,6 +193,15 @@
193193
],
194194
"OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\Instrumentation": [
195195
"OpenTelemetry\\Example\\ExampleInstrumentation"
196+
],
197+
"OpenTelemetry\\SDK\\Common\\Configuration\\Resolver\\ResolverInterface": [
198+
"OpenTelemetry\\SDK\\Common\\Configuration\\Resolver\\SdkConfigurationResolver"
199+
],
200+
"OpenTelemetry\\Config\\SDK\\Configuration\\Environment\\EnvSourceProvider": [
201+
"OpenTelemetry\\Config\\SDK\\Configuration\\Environment\\Adapter\\SymfonyDotenvProvider",
202+
"OpenTelemetry\\Config\\SDK\\Configuration\\Environment\\Adapter\\VlucasPhpdotenvProvider",
203+
204+
"OpenTelemetry\\Tests\\Integration\\SDK\\Common\\Configuration\\TestEnvSourceProvider"
196205
]
197206
}
198207
}

deptrac.yaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,12 @@ deptrac:
117117
value: deptrac/polyfills/.*
118118
- type: directory
119119
value: vendor/symfony/polyfill-php83
120-
120+
- name: DotenvProvider
121+
collectors:
122+
- type: className
123+
regex: ^Symfony\\Component\\Dotenv\\*
124+
- type: className
125+
regex: ^Dotenv\\*
121126
ruleset:
122127
Context:
123128
- FFI
@@ -134,6 +139,7 @@ deptrac:
134139
- Contrib
135140
- Extension
136141
- Polyfills
142+
- DotenvProvider
137143
API:
138144
- Context
139145
- PsrLog

phpstan.neon.dist

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,15 @@ parameters:
5555
message: "#.*expects Google\\\\Protobuf\\\\RepeatedField.*#"
5656
paths:
5757
- src/Contrib/Otlp
58+
-
59+
message: "#^Call to (static )?method .* on an unknown class .*#"
60+
paths:
61+
- src/Config/SDK/Configuration/Environment/Adapter/
62+
-
63+
message: "#^Instantiated class .* not found\\.#"
64+
paths:
65+
- src/Config/SDK/Configuration/Environment/Adapter/
66+
-
67+
message: "#^Caught class .* not found\\.#"
68+
paths:
69+
- src/Config/SDK/Configuration/Environment/Adapter/

src/Config/SDK/Configuration.php

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
use OpenTelemetry\API\Configuration\Context;
1111
use OpenTelemetry\Config\SDK\ComponentProvider\OpenTelemetrySdk;
1212
use OpenTelemetry\Config\SDK\Configuration\ConfigurationFactory;
13+
use OpenTelemetry\Config\SDK\Configuration\Environment\EnvReader;
1314
use OpenTelemetry\Config\SDK\Configuration\Environment\EnvSourceReader;
1415
use OpenTelemetry\Config\SDK\Configuration\Environment\PhpIniEnvSource;
1516
use OpenTelemetry\Config\SDK\Configuration\Environment\ServerEnvSource;
1617
use OpenTelemetry\SDK\SdkBuilder;
18+
use WeakMap;
1719

1820
final class Configuration
1921
{
@@ -37,24 +39,28 @@ public static function parseFile(
3739
string|array $file,
3840
?string $cacheFile = null,
3941
bool $debug = true,
42+
?EnvReader $envReader = null,
4043
): Configuration {
41-
return new self(self::factory()->parseFile($file, $cacheFile, $debug));
44+
return new self(self::factory($envReader)->parseFile($file, $cacheFile, $debug));
4245
}
4346

4447
/**
4548
* @return ConfigurationFactory<SdkBuilder>
4649
*/
47-
private static function factory(): ConfigurationFactory
50+
private static function factory(?EnvReader $envReader): ConfigurationFactory
4851
{
49-
static $factory;
52+
static $defaultEnvReader;
53+
static $factories = new WeakMap();
5054

51-
return $factory ??= new ConfigurationFactory(
55+
$envReader ??= $defaultEnvReader ??= new EnvSourceReader([
56+
new ServerEnvSource(),
57+
new PhpIniEnvSource(),
58+
]);
59+
60+
return $factories[$envReader] ??= new ConfigurationFactory(
5261
self::loadComponentProviders(),
5362
new OpenTelemetrySdk(),
54-
new EnvSourceReader([
55-
new ServerEnvSource(),
56-
new PhpIniEnvSource(),
57-
]),
63+
$envReader,
5864
);
5965
}
6066

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\Config\SDK\Configuration\Environment\Adapter;
6+
7+
use function array_diff_key;
8+
use Composer\InstalledVersions;
9+
use Nevay\SPI\ServiceProviderDependency\PackageDependency;
10+
use OpenTelemetry\Config\SDK\Configuration\Environment\ArrayEnvSource;
11+
use OpenTelemetry\Config\SDK\Configuration\Environment\EnvSource;
12+
use OpenTelemetry\Config\SDK\Configuration\Environment\EnvSourceProvider;
13+
use Symfony\Component\Dotenv\Dotenv;
14+
use Symfony\Component\Dotenv\Exception\PathException;
15+
16+
#[PackageDependency('symfony/dotenv', '^5.4 || ^6.4 || ^7.0')]
17+
final class SymfonyDotenvProvider implements EnvSourceProvider
18+
{
19+
/** @psalm-suppress UndefinedClass */
20+
public function getEnvSource(): EnvSource
21+
{
22+
$installPath = InstalledVersions::getRootPackage()['install_path'];
23+
24+
$backup = [$_SERVER, $_ENV];
25+
$env = [];
26+
27+
try {
28+
(new Dotenv())->bootEnv($installPath . '/.env');
29+
$env = $_SERVER;
30+
} catch (PathException) {
31+
} finally {
32+
[$_SERVER, $_ENV] = $backup;
33+
}
34+
35+
return new ArrayEnvSource(array_diff_key($env, $_SERVER));
36+
}
37+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\Config\SDK\Configuration\Environment\Adapter;
6+
7+
use Composer\InstalledVersions;
8+
use Dotenv\Dotenv;
9+
use Dotenv\Exception\InvalidPathException;
10+
use Nevay\SPI\ServiceProviderDependency\PackageDependency;
11+
use OpenTelemetry\Config\SDK\Configuration\Environment\ArrayEnvSource;
12+
use OpenTelemetry\Config\SDK\Configuration\Environment\EnvSource;
13+
use OpenTelemetry\Config\SDK\Configuration\Environment\EnvSourceProvider;
14+
15+
#[PackageDependency('vlucas/phpdotenv', '^4.0 || ^5.0')]
16+
final class VlucasPhpdotenvProvider implements EnvSourceProvider
17+
{
18+
/** @psalm-suppress UndefinedClass */
19+
public function getEnvSource(): EnvSource
20+
{
21+
$backup = [$_SERVER, $_ENV];
22+
$env = [];
23+
24+
try {
25+
$env = Dotenv::createImmutable([InstalledVersions::getRootPackage()['install_path']])->load();
26+
} catch (InvalidPathException) {
27+
} finally {
28+
[$_SERVER, $_ENV] = $backup;
29+
}
30+
31+
return new ArrayEnvSource(array_diff_key($env, $_SERVER));
32+
}
33+
}
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\Config\SDK\Configuration\Environment;
6+
7+
interface EnvSourceProvider
8+
{
9+
public function getEnvSource(): EnvSource;
10+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\Config\SDK\Configuration\Environment;
6+
7+
use Closure;
8+
9+
final class LazyEnvSource implements EnvSource
10+
{
11+
/**
12+
* @param Closure(): EnvSource|EnvSource $env
13+
*/
14+
public function __construct(
15+
private Closure|EnvSource $env,
16+
) {
17+
}
18+
19+
public function readRaw(string $name): mixed
20+
{
21+
if (!$this->env instanceof EnvSource) {
22+
$this->env = ($this->env)();
23+
}
24+
25+
return $this->env->readRaw($name);
26+
}
27+
}

src/Config/SDK/Instrumentation.php

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\ConfigurationRegistry;
1212
use OpenTelemetry\Config\SDK\ComponentProvider\InstrumentationConfigurationRegistry;
1313
use OpenTelemetry\Config\SDK\Configuration\ConfigurationFactory;
14+
use OpenTelemetry\Config\SDK\Configuration\Environment\EnvReader;
1415
use OpenTelemetry\Config\SDK\Configuration\Environment\EnvSourceReader;
1516
use OpenTelemetry\Config\SDK\Configuration\Environment\PhpIniEnvSource;
1617
use OpenTelemetry\Config\SDK\Configuration\Environment\ServerEnvSource;
18+
use WeakMap;
1719

1820
final class Instrumentation
1921
{
@@ -39,24 +41,28 @@ public static function parseFile(
3941
string|array $file,
4042
?string $cacheFile = null,
4143
bool $debug = true,
44+
?EnvReader $envReader = null,
4245
): Instrumentation {
43-
return new self(self::factory()->parseFile($file, $cacheFile, $debug));
46+
return new self(self::factory($envReader)->parseFile($file, $cacheFile, $debug));
4447
}
4548

4649
/**
4750
* @return ConfigurationFactory<ConfigurationRegistry>
4851
*/
49-
private static function factory(): ConfigurationFactory
52+
private static function factory(?EnvReader $envReader): ConfigurationFactory
5053
{
51-
static $factory;
54+
static $defaultEnvReader;
55+
static $factories = new WeakMap();
5256

53-
return $factory ??= new ConfigurationFactory(
57+
$envReader ??= $defaultEnvReader ??= new EnvSourceReader([
58+
new ServerEnvSource(),
59+
new PhpIniEnvSource(),
60+
]);
61+
62+
return $factories[$envReader] ??= new ConfigurationFactory(
5463
self::loadComponentProviders(),
5564
new InstrumentationConfigurationRegistry(),
56-
new EnvSourceReader([
57-
new ServerEnvSource(),
58-
new PhpIniEnvSource(),
59-
]),
65+
$envReader,
6066
);
6167
}
6268

src/Config/SDK/composer.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"open-telemetry/context": "^1.0",
2323
"open-telemetry/sdk": "^1.0",
2424
"symfony/config": "^5.4 || ^6.4 || ^7.0",
25-
"tbachert/spi": "^1.0.1"
25+
"tbachert/spi": "^1.0.5"
2626
},
2727
"autoload": {
2828
"psr-4": {
@@ -82,6 +82,10 @@
8282

8383
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Instrumentation\\General\\HttpConfigProvider",
8484
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Instrumentation\\General\\PeerConfigProvider"
85+
],
86+
"OpenTelemetry\\Config\\SDK\\Configuration\\Environment\\EnvSourceProvider": [
87+
"OpenTelemetry\\Config\\SDK\\Configuration\\Environment\\Adapter\\SymfonyDotenvProvider",
88+
"OpenTelemetry\\Config\\SDK\\Configuration\\Environment\\Adapter\\VlucasPhpdotenvProvider"
8589
]
8690
}
8791
}

0 commit comments

Comments
 (0)