refactor environment configuration (#859)

refactoring Environment access to allow configuration from multiple sources. Initially, the sources are env ($_SERVER) and php.ini, but this should allow us to add more sources in future without changing the API.
do not modify OTEL_EXPORTER_OTLP_TRACES_PROTOCOL
allow resolvers to return mixed
This commit is contained in:
Brett McBride 2022-11-10 09:48:17 +11:00 committed by GitHub
parent 92e7a0d070
commit f083b10909
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 1419 additions and 1004 deletions

View File

@ -5,10 +5,8 @@ require __DIR__ . '/../../../vendor/autoload.php';
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use OpenTelemetry\Contrib\Otlp\SpanExporter;
use OpenTelemetry\SDK\Common\Log\LoggerHolder;
use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor;
use OpenTelemetry\SDK\Trace\TracerProvider;
use OpenTelemetry\SDK\Trace\TracerProviderFactory;
use Psr\Log\LogLevel;
echo 'Starting SettingUpLogging example' . PHP_EOL;
@ -18,13 +16,11 @@ echo 'Starting SettingUpLogging example' . PHP_EOL;
LoggerHolder::set(
new Logger('otel-php', [new StreamHandler(STDOUT, LogLevel::DEBUG)])
);
$transport = (new \OpenTelemetry\Contrib\Grpc\GrpcTransportFactory())->create();
putenv('OTEL_EXPORTER_OTLP_ENDPOINT=http://does-not-exist/endpoint'); //invalid endpoint, export will fail
putenv('OTEL_EXPORTER_OTLP_PROTOCOL=grpc');
$factory = new TracerProviderFactory('otlp-logging-demo');
$tracerProvider = $factory->create();
$tracerProvider = new TracerProvider(
new SimpleSpanProcessor(
new SpanExporter($transport) //default endpoint unavailable, so exporting will fail
)
);
$tracer = $tracerProvider->getTracer('io.opentelemetry.contrib.php');
$span = $tracer->spanBuilder('root-span')->startSpan();
$span->end();

View File

@ -11,3 +11,9 @@ parameters:
# - ./examples TODO: Uncomment this once examples are updated
excludePaths:
- tests/TraceContext/W3CTestService
ignoreErrors:
-
message: "#Call to an undefined method .*#"
paths:
- tests/Unit/SDK/Common/Configuration/Resolver/PhpIniResolverTest.php
- tests/Unit/SDK/Common/Configuration/Resolver/CompositeResolverTest.php

View File

@ -3,7 +3,7 @@
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
backupGlobals="false"
backupGlobals="true"
backupStaticAttributes="false"
bootstrap="./tests/bootstrap.php"
cacheResult="false"

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace OpenTelemetry\Contrib\Otlp;
use OpenTelemetry\SDK\Common\Environment\KnownValues;
use OpenTelemetry\SDK\Common\Configuration\KnownValues;
use UnexpectedValueException;
class Protocols

View File

@ -6,22 +6,36 @@ namespace OpenTelemetry\Contrib\Otlp;
use OpenTelemetry\API\Common\Signal\Signals;
use OpenTelemetry\Contrib\Grpc\GrpcTransportFactory;
use OpenTelemetry\SDK\Common\Environment\EnvironmentVariables;
use OpenTelemetry\SDK\Common\Environment\KnownValues;
use OpenTelemetry\SDK\Common\Environment\Variables as Env;
use OpenTelemetry\SDK\Behavior\LogsMessagesTrait;
use OpenTelemetry\SDK\Common\Configuration\Configuration;
use OpenTelemetry\SDK\Common\Configuration\Defaults;
use OpenTelemetry\SDK\Common\Configuration\KnownValues;
use OpenTelemetry\SDK\Common\Configuration\Variables;
use OpenTelemetry\SDK\Common\Export\TransportFactoryInterface;
use OpenTelemetry\SDK\Common\Export\TransportInterface;
use OpenTelemetry\SDK\Common\Otlp\HttpEndpointResolver;
use OpenTelemetry\SDK\Trace\SpanExporterInterface;
use UnexpectedValueException;
class SpanExporterFactory
{
use LogsMessagesTrait;
private ?TransportFactoryInterface $transportFactory;
private const DEFAULT_COMPRESSION = 'none';
private const FACTORIES = [
KnownValues::VALUE_GRPC => GrpcTransportFactory::class,
KnownValues::VALUE_HTTP_PROTOBUF => OtlpHttpTransportFactory::class,
KnownValues::VALUE_HTTP_JSON => OtlpHttpTransportFactory::class,
KnownValues::VALUE_HTTP_NDJSON => OtlpHttpTransportFactory::class,
];
public function __construct(?TransportFactoryInterface $transportFactory = null)
{
$this->transportFactory = $transportFactory;
}
/**
* @psalm-suppress ArgumentTypeCoercion
*/
@ -37,31 +51,56 @@ class SpanExporterFactory
*/
private function buildTransport(): TransportInterface
{
$protocol = EnvironmentVariables::has(Env::OTEL_EXPORTER_OTLP_TRACES_PROTOCOL) ?
EnvironmentVariables::getEnum(Env::OTEL_EXPORTER_OTLP_TRACES_PROTOCOL) :
EnvironmentVariables::getEnum(Env::OTEL_EXPORTER_OTLP_PROTOCOL);
$protocol = $this->getProtocol();
$contentType = Protocols::contentType($protocol);
$endpoint = $this->getEndpoint($protocol);
$headers = $this->getHeaders();
$compression = $this->getCompression();
$endpoint = EnvironmentVariables::has(Env::OTEL_EXPORTER_OTLP_TRACES_ENDPOINT)
? EnvironmentVariables::getString(Env::OTEL_EXPORTER_OTLP_TRACES_ENDPOINT)
: EnvironmentVariables::getString(Env::OTEL_EXPORTER_OTLP_ENDPOINT);
if (!$this->transportFactory && !array_key_exists($protocol, self::FACTORIES)) {
throw new UnexpectedValueException('Unknown OTLP protocol: ' . $protocol);
}
$factoryClass = self::FACTORIES[$protocol];
$factory = $this->transportFactory ?: new $factoryClass();
return $factory->create($endpoint, $contentType, $headers, $compression);
}
private function getProtocol(): string
{
return Configuration::has(Variables::OTEL_EXPORTER_OTLP_TRACES_PROTOCOL) ?
Configuration::getEnum(Variables::OTEL_EXPORTER_OTLP_TRACES_PROTOCOL) :
Configuration::getEnum(Variables::OTEL_EXPORTER_OTLP_PROTOCOL);
}
private function getEndpoint(string $protocol): string
{
if (Configuration::has(Variables::OTEL_EXPORTER_OTLP_TRACES_ENDPOINT)) {
return Configuration::getString(Variables::OTEL_EXPORTER_OTLP_TRACES_ENDPOINT);
}
$endpoint = Configuration::has(Variables::OTEL_EXPORTER_OTLP_ENDPOINT)
? Configuration::getString(Variables::OTEL_EXPORTER_OTLP_ENDPOINT)
: Defaults::OTEL_EXPORTER_OTLP_ENDPOINT;
if ($protocol === Protocols::GRPC) {
$endpoint .= OtlpUtil::method(Signals::TRACE);
} else {
$endpoint = HttpEndpointResolver::create()->resolveToString($endpoint, Signals::TRACE);
return $endpoint . OtlpUtil::method(Signals::TRACE);
}
$headers = EnvironmentVariables::has(Env::OTEL_EXPORTER_OTLP_TRACES_HEADERS) ?
EnvironmentVariables::getMap(Env::OTEL_EXPORTER_OTLP_TRACES_HEADERS) :
EnvironmentVariables::getMap(Env::OTEL_EXPORTER_OTLP_HEADERS);
$headers += OtlpUtil::getUserAgentHeader();
return HttpEndpointResolver::create()->resolveToString($endpoint, Signals::TRACE);
}
$compression = EnvironmentVariables::has(Env::OTEL_EXPORTER_OTLP_TRACES_COMPRESSION) ?
EnvironmentVariables::getEnum(Env::OTEL_EXPORTER_OTLP_TRACES_COMPRESSION) :
EnvironmentVariables::getEnum(Env::OTEL_EXPORTER_OTLP_COMPRESSION, self::DEFAULT_COMPRESSION);
private function getHeaders(): array
{
$headers = Configuration::has(Variables::OTEL_EXPORTER_OTLP_TRACES_HEADERS) ?
Configuration::getMap(Variables::OTEL_EXPORTER_OTLP_TRACES_HEADERS) :
Configuration::getMap(Variables::OTEL_EXPORTER_OTLP_HEADERS);
$factoryClass = self::FACTORIES[$protocol];
return $headers + OtlpUtil::getUserAgentHeader();
}
return (new $factoryClass())->create($endpoint, $contentType, $headers, $compression);
private function getCompression(): string
{
return Configuration::has(Variables::OTEL_EXPORTER_OTLP_TRACES_COMPRESSION) ?
Configuration::getEnum(Variables::OTEL_EXPORTER_OTLP_TRACES_COMPRESSION) :
Configuration::getEnum(Variables::OTEL_EXPORTER_OTLP_COMPRESSION, self::DEFAULT_COMPRESSION);
}
}

View File

@ -0,0 +1,172 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\SDK\Common\Configuration;
use OpenTelemetry\SDK\Common\Configuration\Parser\BooleanParser;
use OpenTelemetry\SDK\Common\Configuration\Parser\ListParser;
use OpenTelemetry\SDK\Common\Configuration\Parser\MapParser;
use OpenTelemetry\SDK\Common\Configuration\Parser\RatioParser;
use OpenTelemetry\SDK\Common\Configuration\Resolver\CompositeResolver;
use OpenTelemetry\SDK\Common\Util\ClassConstantAccessor;
use UnexpectedValueException;
/**
* Configuration can come from one or more of the following sources (from highest to lowest priority):
* - values defined in php.ini
* - environment variable ($_SERVER)
* - configuration file (todo)
*/
class Configuration
{
public static function has(string $name): bool
{
return CompositeResolver::instance()->hasVariable($name);
}
public static function getInt(string $key, int $default = null): int
{
return (int) self::validateVariableValue(
CompositeResolver::instance()->resolve(
self::validateVariableType($key, VariableTypes::INTEGER),
$default
),
FILTER_VALIDATE_INT
);
}
public static function getString(string $key, string $default = ''): string
{
return (string) self::validateVariableValue(
CompositeResolver::instance()->resolve(
self::validateVariableType($key, VariableTypes::STRING),
$default
)
);
}
public static function getBoolean(string $key, bool $default = null): bool
{
return BooleanParser::parse(
self::validateVariableValue(
CompositeResolver::instance()->resolve(
self::validateVariableType($key, VariableTypes::BOOL),
null === $default ? $default : ($default ? 'true' : 'false')
)
)
);
}
public static function getMixed(string $key, string $default = null)
{
return self::validateVariableValue(
CompositeResolver::instance()->resolve(
$key,
$default
)
);
}
public static function getMap(string $key, string $default = null): array
{
return MapParser::parse(
CompositeResolver::instance()->resolve(
self::validateVariableType($key, VariableTypes::MAP),
$default
)
);
}
public static function getList(string $key, string $default = null): array
{
return ListParser::parse(
CompositeResolver::instance()->resolve(
self::validateVariableType($key, VariableTypes::LIST),
$default
)
);
}
public static function getEnum(string $key, string $default = null): string
{
return (string) self::validateVariableValue(
CompositeResolver::instance()->resolve(
self::validateVariableType($key, VariableTypes::ENUM),
$default
)
);
}
public static function getFloat(string $key, string $default = null): float
{
return (float) self::validateVariableValue(
CompositeResolver::instance()->resolve(
self::validateVariableType($key, VariableTypes::FLOAT),
$default
),
FILTER_VALIDATE_FLOAT
);
}
public static function getRatio(string $key, float $default = null): float
{
return RatioParser::parse(
self::validateVariableValue(
CompositeResolver::instance()->resolve(
self::validateVariableType($key, VariableTypes::RATIO),
$default
)
)
);
}
public static function getKnownValues(string $variableName): ?array
{
return ClassConstantAccessor::getValue(KnownValues::class, $variableName);
}
public static function getDefault(string $variableName)
{
return ClassConstantAccessor::getValue(Defaults::class, $variableName);
}
public static function getType(string $variableName): ?string
{
return ClassConstantAccessor::getValue(ValueTypes::class, $variableName);
}
public static function isEmpty($value): bool
{
// don't use 'empty()', since '0' is not considered to be empty
return $value === null || $value === '';
}
private static function validateVariableType(string $variableName, string $type): string
{
$variableType = self::getType($variableName);
if ($variableType !== null && $variableType !== $type && $variableType !== VariableTypes::MIXED) {
throw new UnexpectedValueException(
sprintf('Variable "%s" is not supposed to be of type "%s" but type "%s"', $variableName, $type, $variableType)
);
}
return $variableName;
}
private static function validateVariableValue($value, ?int $filterType = null)
{
if ($filterType !== null && filter_var($value, $filterType) === false) {
throw new UnexpectedValueException(sprintf('Value has invalid type "%s"', gettype($value)));
}
if ($value === null || $value === '') {
throw new UnexpectedValueException(
'Variable must not be null or empty'
);
}
return $value;
}
}

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace OpenTelemetry\SDK\Common\Environment;
namespace OpenTelemetry\SDK\Common\Configuration;
/**
* Default values for environment variables defined by the OpenTelemetry specification and language specific variables for the PHP SDK.

View File

@ -2,12 +2,12 @@
declare(strict_types=1);
namespace OpenTelemetry\SDK\Common\Environment;
namespace OpenTelemetry\SDK\Common\Configuration;
use Psr\Log\LogLevel;
/**
* "Known values" for OpenTelemetry environment variables.
* "Known values" for OpenTelemetry configurataion variables.
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md
* Notice: Values specific to the PHP SDK have been added
*/

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace OpenTelemetry\SDK\Common\Environment\Parser;
namespace OpenTelemetry\SDK\Common\Configuration\Parser;
use InvalidArgumentException;
@ -20,8 +20,14 @@ class BooleanParser
'0',
];
public static function parse(string $value): bool
/**
* @param string|bool $value
*/
public static function parse($value): bool
{
if (is_bool($value)) {
return $value;
}
if (in_array(strtolower($value), self::TRUTHY_VALUES)) {
return true;
}

View File

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\SDK\Common\Configuration\Parser;
class ListParser
{
private const DEFAULT_SEPARATOR = ',';
/**
* @param string|array $value
*/
public static function parse($value): array
{
if (is_array($value)) {
return $value;
}
if (trim($value) === '') {
return [];
}
return array_map(
fn ($value) => trim($value),
explode(self::DEFAULT_SEPARATOR, $value)
);
}
}

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace OpenTelemetry\SDK\Common\Environment\Parser;
namespace OpenTelemetry\SDK\Common\Configuration\Parser;
use InvalidArgumentException;
@ -11,11 +11,14 @@ class MapParser
private const VARIABLE_SEPARATOR = ',';
private const KEY_VALUE_SEPARATOR = '=';
public static function parse(string $value): array
public static function parse($value): array
{
if (is_array($value)) {
return $value;
}
$result = [];
if (trim($value) === '') {
if (null === $value || trim($value) === '') {
return $result;
}

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace OpenTelemetry\SDK\Common\Environment\Parser;
namespace OpenTelemetry\SDK\Common\Configuration\Parser;
use InvalidArgumentException;
use RangeException;
@ -12,7 +12,7 @@ class RatioParser
private const MAX_VALUE = 1;
private const MIN_VALUE = 0;
public static function parse(string $value): float
public static function parse($value): float
{
if (filter_var($value, FILTER_VALIDATE_FLOAT) === false) {
throw new InvalidArgumentException(

View File

@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\SDK\Common\Configuration\Resolver;
use OpenTelemetry\SDK\Common\Configuration\Configuration;
/**
* @interal
*/
class CompositeResolver
{
// @var array<ResolverInterface>
private array $resolvers = [];
public static function instance(): self
{
static $instance;
$instance ??= new self([
new PhpIniResolver(),
new EnvironmentResolver(),
]);
return $instance;
}
public function __construct($resolvers)
{
foreach ($resolvers as $resolver) {
$this->addResolver($resolver);
}
}
public function addResolver(ResolverInterface $resolver): void
{
$this->resolvers[] = $resolver;
}
public function getResolvers(): array
{
return $this->resolvers;
}
public function resolve(string $variableName, $default = '')
{
foreach ($this->resolvers as $resolver) {
if ($resolver->hasVariable($variableName)) {
return $resolver->retrieveValue($variableName);
}
}
return Configuration::isEmpty($default)
? Configuration::getDefault($variableName)
: $default;
}
public function hasVariable(string $variableName): bool
{
foreach ($this->resolvers as $resolver) {
if ($resolver->hasVariable($variableName)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\SDK\Common\Configuration\Resolver;
use OpenTelemetry\SDK\Common\Configuration\Configuration;
/**
* @internal
*/
class EnvironmentResolver implements ResolverInterface
{
public function hasVariable(string $variableName): bool
{
if (!Configuration::isEmpty($_SERVER[$variableName] ?? null)) {
return true;
}
$env = getenv($variableName);
if ($env === false) {
return false;
}
return !Configuration::isEmpty($env);
}
/**
* @psalm-suppress InvalidReturnStatement
* @psalm-suppress InvalidReturnType
*/
public function retrieveValue(string $variableName)
{
$value = getenv($variableName);
if ($value === false) {
$value = $_SERVER[$variableName] ?? null;
}
return $value;
}
}

View File

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\SDK\Common\Configuration\Resolver;
class PhpIniAccessor
{
/**
* Mockable accessor for php.ini values
* @internal
* @return array|false|string
*/
public function get(string $variableName)
{
return get_cfg_var($variableName);
}
}

View File

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\SDK\Common\Configuration\Resolver;
use OpenTelemetry\SDK\Common\Configuration\Configuration;
/**
* @interal
* @psalm-suppress TypeDoesNotContainType
*/
class PhpIniResolver implements ResolverInterface
{
private PhpIniAccessor $accessor;
public function __construct(?PhpIniAccessor $accessor = null)
{
$this->accessor = $accessor ?? new PhpIniAccessor();
}
public function retrieveValue(string $variableName)
{
$value = $this->accessor->get($variableName) ?: '';
if (is_array($value)) {
return implode(',', $value);
}
return $value;
}
public function hasVariable(string $variableName): bool
{
$value = $this->accessor->get($variableName);
if ($value === []) {
return false;
}
return $value !== false && !Configuration::isEmpty($value);
}
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\SDK\Common\Configuration\Resolver;
interface ResolverInterface
{
/**
* @return mixed
*/
public function retrieveValue(string $variableName);
public function hasVariable(string $variableName): bool;
}

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace OpenTelemetry\SDK\Common\Environment;
namespace OpenTelemetry\SDK\Common\Configuration;
/**
* Environment variables defined by the OpenTelemetry specification and language specific variables for the PHP SDK.

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace OpenTelemetry\SDK\Common\Environment;
namespace OpenTelemetry\SDK\Common\Configuration;
interface VariableTypes
{

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace OpenTelemetry\SDK\Common\Environment;
namespace OpenTelemetry\SDK\Common\Configuration;
/**
* Environment variables defined by the OpenTelemetry specification and language specific variables for the PHP SDK.

View File

@ -1,138 +0,0 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\SDK\Common\Environment;
use OpenTelemetry\SDK\Common\Environment\Parser\BooleanParser;
use OpenTelemetry\SDK\Common\Environment\Parser\ListParser;
use OpenTelemetry\SDK\Common\Environment\Parser\MapParser;
use OpenTelemetry\SDK\Common\Environment\Parser\RatioParser;
use UnexpectedValueException;
class Accessor
{
public static function getString(string $variableName, string $default = null): string
{
return (string) self::validateVariableValue(
Resolver::resolveValue(
self::validateVariableType($variableName, VariableTypes::STRING),
$default
)
);
}
public static function getBool(string $variableName, string $default = null): bool
{
return BooleanParser::parse(
self::validateVariableValue(
Resolver::resolveValue(
self::validateVariableType($variableName, VariableTypes::BOOL),
$default
)
)
);
}
public static function getInt(string $variableName, string $default = null): int
{
return (int) self::validateVariableValue(
Resolver::resolveValue(
self::validateVariableType($variableName, VariableTypes::INTEGER),
$default
),
FILTER_VALIDATE_INT
);
}
public static function getFloat(string $variableName, string $default = null): float
{
return (float) self::validateVariableValue(
Resolver::resolveValue(
self::validateVariableType($variableName, VariableTypes::FLOAT),
$default
),
FILTER_VALIDATE_FLOAT
);
}
public static function getRatio(string $variableName, string $default = null): float
{
return RatioParser::parse(
self::validateVariableValue(
Resolver::resolveValue(
self::validateVariableType($variableName, VariableTypes::RATIO),
$default
)
)
);
}
public static function getEnum(string $variableName, string $default = null): string
{
return (string) self::validateVariableValue(
Resolver::resolveValue(
self::validateVariableType($variableName, VariableTypes::ENUM),
$default
)
);
}
public static function getList(string $variableName, string $default = null): array
{
return ListParser::parse(
Resolver::resolveValue(
self::validateVariableType($variableName, VariableTypes::LIST),
$default
)
);
}
public static function getMap(string $variableName, string $default = null): array
{
return MapParser::parse(
Resolver::resolveValue(
self::validateVariableType($variableName, VariableTypes::MAP),
$default
)
);
}
public static function getMixed(string $variableName, string $default = null, ?string $type = null)
{
return self::validateVariableValue(
Resolver::resolveValue(
$variableName,
$default
)
);
}
private static function validateVariableType(string $variableName, string $type): string
{
$variableType = Resolver::getType($variableName);
if ($variableType !== null && $variableType !== $type && $variableType !== VariableTypes::MIXED) {
throw new UnexpectedValueException(
sprintf('Variable "%s" is not supposed to be of type "%s" but type "%s"', $variableName, $type, $variableType)
);
}
return $variableName;
}
private static function validateVariableValue($value, ?int $filterType = null)
{
if ($filterType !== null && filter_var($value, $filterType) === false) {
throw new UnexpectedValueException(sprintf('Value has invalid type "%s"', gettype($value)));
}
if ($value === null || $value === '') {
throw new UnexpectedValueException(
'Variable must not be null or empty'
);
}
return $value;
}
}

View File

@ -1,54 +0,0 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\SDK\Common\Environment;
/**
* Centralized methods for retrieving environment variables
*/
class EnvironmentVariables
{
/**
* Retrieve an integer value from an environment variable
*/
public static function getInt(string $key, int $default): int
{
return Accessor::getInt($key, (string) $default);
}
public static function getString(string $key, string $default = ''): string
{
return Accessor::getString($key, $default);
}
public static function getBoolean(string $key, bool $default = null): bool
{
return Accessor::getBool($key, null === $default ? null : ($default ? 'true' : 'false'));
}
public static function getMap(string $key, string $default = null): array
{
return Accessor::getMap($key, $default);
}
public static function getList(string $key, string $default = null): array
{
return Accessor::getList($key, $default);
}
public static function getEnum(string $key, string $default = null): string
{
return Accessor::getEnum($key, $default);
}
public static function getRatio(string $key, float $default = null): float
{
return Accessor::getRatio($key, $default ? (string) $default : null);
}
public static function has(string $key): bool
{
return Resolver::hasVariable($key);
}
}

View File

@ -1,22 +0,0 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\SDK\Common\Environment\Parser;
class ListParser
{
private const DEFAULT_SEPARATOR = ',';
public static function parse(string $value, string $separator = self::DEFAULT_SEPARATOR): array
{
if (trim($value) === '') {
return [];
}
return array_map(
fn ($value) => trim($value),
explode(self::DEFAULT_SEPARATOR, $value),
);
}
}

View File

@ -1,70 +0,0 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\SDK\Common\Environment;
use OpenTelemetry\SDK\Common\Util\ClassConstantAccessor;
class Resolver
{
public static function resolveValue(string $variableName, $default = null): string
{
$value = self::getValue($variableName);
if (self::isEmpty($value)) {
return self::isEmpty($default) ? (string) self::getDefault($variableName) : (string) $default;
}
return (string) $value;
}
public static function getValue(string $variableName): ?string
{
$value = self::getRawValue($variableName);
return $value ? trim($value) : $value;
}
public static function getRawValue(string $variableName): ?string
{
/** @psalm-suppress FalsableReturnStatement **/
return self::hasVariable($variableName) ? self::retrieveValue($variableName) : null;
}
public static function hasVariable(string $variableName): bool
{
return getenv($variableName) !== false || isset($_ENV[$variableName]);
}
public static function getDefault(string $variableName)
{
return ClassConstantAccessor::getValue(Defaults::class, $variableName);
}
public static function getType(string $variableName): ?string
{
return ClassConstantAccessor::getValue(ValueTypes::class, $variableName);
}
public static function getKnownValues(string $variableName): ?array
{
return ClassConstantAccessor::getValue(KnownValues::class, $variableName);
}
private static function isEmpty($value): bool
{
// don't use 'empty()', since '0' is not considered to be empty
return $value === null || $value === '';
}
private static function retrieveValue(string $variableName)
{
$value = getenv($variableName);
if ($value === false) {
$value = $_ENV[$variableName] ?? false;
}
return $value;
}
}

View File

@ -5,8 +5,8 @@ declare(strict_types=1);
namespace OpenTelemetry\SDK\Resource\Detectors;
use OpenTelemetry\SDK\Common\Attribute\Attributes;
use OpenTelemetry\SDK\Common\Environment\EnvironmentVariables;
use OpenTelemetry\SDK\Common\Environment\Variables as Env;
use OpenTelemetry\SDK\Common\Configuration\Configuration;
use OpenTelemetry\SDK\Common\Configuration\Variables;
use OpenTelemetry\SDK\Resource\ResourceDetectorInterface;
use OpenTelemetry\SDK\Resource\ResourceInfo;
use OpenTelemetry\SemConv\ResourceAttributes;
@ -18,12 +18,14 @@ final class Environment implements ResourceDetectorInterface
{
public function getResource(): ResourceInfo
{
$attributes = EnvironmentVariables::getMap(Env::OTEL_RESOURCE_ATTRIBUTES, '');
$attributes = Configuration::has(Variables::OTEL_RESOURCE_ATTRIBUTES)
? Configuration::getMap(Variables::OTEL_RESOURCE_ATTRIBUTES, '')
: [];
//@see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#general-sdk-configuration
$serviceName = EnvironmentVariables::has(Env::OTEL_SERVICE_NAME) ?
EnvironmentVariables::getString(Env::OTEL_SERVICE_NAME) :
null;
$serviceName = Configuration::has(Variables::OTEL_SERVICE_NAME)
? Configuration::getString(Variables::OTEL_SERVICE_NAME)
: null;
if ($serviceName) {
$attributes[ResourceAttributes::SERVICE_NAME] = $serviceName;
}

View File

@ -6,9 +6,9 @@ namespace OpenTelemetry\SDK\Resource;
use function in_array;
use OpenTelemetry\SDK\Common\Attribute\Attributes;
use OpenTelemetry\SDK\Common\Environment\Accessor;
use OpenTelemetry\SDK\Common\Environment\KnownValues as Values;
use OpenTelemetry\SDK\Common\Environment\Variables as Env;
use OpenTelemetry\SDK\Common\Configuration\Configuration;
use OpenTelemetry\SDK\Common\Configuration\KnownValues as Values;
use OpenTelemetry\SDK\Common\Configuration\Variables as Env;
class ResourceInfoFactory
{
@ -33,7 +33,7 @@ class ResourceInfoFactory
public static function defaultResource(): ResourceInfo
{
$detectors = Accessor::getList(Env::OTEL_PHP_DETECTORS);
$detectors = Configuration::getList(Env::OTEL_PHP_DETECTORS);
if (in_array(Values::VALUE_ALL, $detectors)) {
return (new Detectors\Composite([

View File

@ -7,8 +7,8 @@ namespace OpenTelemetry\SDK;
use OpenTelemetry\API\Metrics\MeterProviderInterface;
use OpenTelemetry\API\Trace\TracerProviderInterface;
use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface;
use OpenTelemetry\SDK\Common\Environment\EnvironmentVariables;
use OpenTelemetry\SDK\Common\Environment\Variables;
use OpenTelemetry\SDK\Common\Configuration\Configuration;
use OpenTelemetry\SDK\Common\Configuration\Variables;
class Sdk
{
@ -28,7 +28,7 @@ class Sdk
public static function isDisabled(): bool
{
return EnvironmentVariables::getBoolean(Variables::OTEL_SDK_DISABLED);
return Configuration::getBoolean(Variables::OTEL_SDK_DISABLED);
}
public static function builder(): SdkBuilder

View File

@ -5,12 +5,12 @@ declare(strict_types=1);
namespace OpenTelemetry\SDK\Trace;
use InvalidArgumentException;
use OpenTelemetry\SDK\Common\Configuration\Configuration;
use OpenTelemetry\SDK\Common\Configuration\KnownValues as Values;
use OpenTelemetry\SDK\Common\Configuration\Variables as Env;
use OpenTelemetry\SDK\Common\Dsn\DsnInterface;
use OpenTelemetry\SDK\Common\Dsn\Parser;
use OpenTelemetry\SDK\Common\Dsn\ParserInterface;
use OpenTelemetry\SDK\Common\Environment\EnvironmentVariables;
use OpenTelemetry\SDK\Common\Environment\KnownValues as Values;
use OpenTelemetry\SDK\Common\Environment\Variables as Env;
class ExporterFactory
{
@ -78,7 +78,7 @@ class ExporterFactory
public function fromEnvironment(): ?SpanExporterInterface
{
$envValue = EnvironmentVariables::getString(Env::OTEL_TRACES_EXPORTER, '');
$envValue = Configuration::getString(Env::OTEL_TRACES_EXPORTER, '');
$exporters = explode(',', $envValue);
//TODO "The SDK MAY accept a comma-separated list to enable setting multiple exporters"
if (1 !== count($exporters)) {

View File

@ -5,9 +5,9 @@ declare(strict_types=1);
namespace OpenTelemetry\SDK\Trace;
use InvalidArgumentException;
use OpenTelemetry\SDK\Common\Environment\EnvironmentVariables;
use OpenTelemetry\SDK\Common\Environment\KnownValues as Values;
use OpenTelemetry\SDK\Common\Environment\Variables as Env;
use OpenTelemetry\SDK\Common\Configuration\Configuration;
use OpenTelemetry\SDK\Common\Configuration\KnownValues as Values;
use OpenTelemetry\SDK\Common\Configuration\Variables as Env;
use OpenTelemetry\SDK\Trace\Sampler\AlwaysOffSampler;
use OpenTelemetry\SDK\Trace\Sampler\AlwaysOnSampler;
use OpenTelemetry\SDK\Trace\Sampler\ParentBased;
@ -19,10 +19,10 @@ class SamplerFactory
public function fromEnvironment(): SamplerInterface
{
$name = EnvironmentVariables::getString(Env::OTEL_TRACES_SAMPLER);
$name = Configuration::getString(Env::OTEL_TRACES_SAMPLER);
if (strpos($name, self::TRACEIDRATIO_PREFIX) !== false) {
$arg = EnvironmentVariables::getRatio(Env::OTEL_TRACES_SAMPLER_ARG);
$arg = Configuration::getRatio(Env::OTEL_TRACES_SAMPLER_ARG);
switch ($name) {
case Values::VALUE_TRACE_ID_RATIO:

View File

@ -6,8 +6,8 @@ namespace OpenTelemetry\SDK\Trace;
use OpenTelemetry\SDK\Common\Attribute\Attributes;
use OpenTelemetry\SDK\Common\Attribute\FilteredAttributesFactory;
use OpenTelemetry\SDK\Common\Environment\EnvironmentVariables;
use OpenTelemetry\SDK\Common\Environment\Variables as Env;
use OpenTelemetry\SDK\Common\Configuration\Configuration;
use OpenTelemetry\SDK\Common\Configuration\Variables as Env;
use OpenTelemetry\SemConv\TraceAttributes;
use const PHP_INT_MAX;
@ -111,17 +111,17 @@ class SpanLimitsBuilder
public function build(): SpanLimits
{
$attributeCountLimit = $this->attributeCountLimit
?: EnvironmentVariables::getInt(Env::OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, SpanLimits::DEFAULT_SPAN_ATTRIBUTE_COUNT_LIMIT);
?: Configuration::getInt(Env::OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, SpanLimits::DEFAULT_SPAN_ATTRIBUTE_COUNT_LIMIT);
$attributeValueLengthLimit = $this->attributeValueLengthLimit
?: EnvironmentVariables::getInt(Env::OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT, SpanLimits::DEFAULT_SPAN_ATTRIBUTE_LENGTH_LIMIT);
?: Configuration::getInt(Env::OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT, SpanLimits::DEFAULT_SPAN_ATTRIBUTE_LENGTH_LIMIT);
$eventCountLimit = $this->eventCountLimit
?: EnvironmentVariables::getInt(Env::OTEL_SPAN_EVENT_COUNT_LIMIT, SpanLimits::DEFAULT_SPAN_EVENT_COUNT_LIMIT);
?: Configuration::getInt(Env::OTEL_SPAN_EVENT_COUNT_LIMIT, SpanLimits::DEFAULT_SPAN_EVENT_COUNT_LIMIT);
$linkCountLimit = $this->linkCountLimit
?: EnvironmentVariables::getInt(Env::OTEL_SPAN_LINK_COUNT_LIMIT, SpanLimits::DEFAULT_SPAN_LINK_COUNT_LIMIT);
?: Configuration::getInt(Env::OTEL_SPAN_LINK_COUNT_LIMIT, SpanLimits::DEFAULT_SPAN_LINK_COUNT_LIMIT);
$attributePerEventCountLimit = $this->attributePerEventCountLimit
?: EnvironmentVariables::getInt(Env::OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT, SpanLimits::DEFAULT_EVENT_ATTRIBUTE_COUNT_LIMIT);
?: Configuration::getInt(Env::OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT, SpanLimits::DEFAULT_EVENT_ATTRIBUTE_COUNT_LIMIT);
$attributePerLinkCountLimit = $this->attributePerLinkCountLimit
?: EnvironmentVariables::getInt(Env::OTEL_LINK_ATTRIBUTE_COUNT_LIMIT, SpanLimits::DEFAULT_LINK_ATTRIBUTE_COUNT_LIMIT);
?: Configuration::getInt(Env::OTEL_LINK_ATTRIBUTE_COUNT_LIMIT, SpanLimits::DEFAULT_LINK_ATTRIBUTE_COUNT_LIMIT);
if ($attributeValueLengthLimit === PHP_INT_MAX) {
$attributeValueLengthLimit = null;

View File

@ -5,9 +5,9 @@ declare(strict_types=1);
namespace OpenTelemetry\SDK\Trace;
use InvalidArgumentException;
use OpenTelemetry\SDK\Common\Environment\EnvironmentVariables;
use OpenTelemetry\SDK\Common\Environment\KnownValues as Values;
use OpenTelemetry\SDK\Common\Environment\Variables as Env;
use OpenTelemetry\SDK\Common\Configuration\Configuration;
use OpenTelemetry\SDK\Common\Configuration\KnownValues as Values;
use OpenTelemetry\SDK\Common\Configuration\Variables as Env;
use OpenTelemetry\SDK\Common\Time\ClockFactory;
use OpenTelemetry\SDK\Trace\SpanProcessor\BatchSpanProcessor;
use OpenTelemetry\SDK\Trace\SpanProcessor\NoopSpanProcessor;
@ -21,16 +21,16 @@ class SpanProcessorFactory
return new NoopSpanProcessor();
}
$name = EnvironmentVariables::getEnum(Env::OTEL_PHP_TRACES_PROCESSOR);
$name = Configuration::getEnum(Env::OTEL_PHP_TRACES_PROCESSOR);
switch ($name) {
case Values::VALUE_BATCH:
return new BatchSpanProcessor(
$exporter,
ClockFactory::getDefault(),
EnvironmentVariables::getInt(Env::OTEL_BSP_MAX_QUEUE_SIZE, BatchSpanProcessor::DEFAULT_MAX_QUEUE_SIZE),
EnvironmentVariables::getInt(Env::OTEL_BSP_SCHEDULE_DELAY, BatchSpanProcessor::DEFAULT_SCHEDULE_DELAY),
EnvironmentVariables::getInt(Env::OTEL_BSP_EXPORT_TIMEOUT, BatchSpanProcessor::DEFAULT_EXPORT_TIMEOUT),
EnvironmentVariables::getInt(Env::OTEL_BSP_MAX_EXPORT_BATCH_SIZE, BatchSpanProcessor::DEFAULT_MAX_EXPORT_BATCH_SIZE),
Configuration::getInt(Env::OTEL_BSP_MAX_QUEUE_SIZE, BatchSpanProcessor::DEFAULT_MAX_QUEUE_SIZE),
Configuration::getInt(Env::OTEL_BSP_SCHEDULE_DELAY, BatchSpanProcessor::DEFAULT_SCHEDULE_DELAY),
Configuration::getInt(Env::OTEL_BSP_EXPORT_TIMEOUT, BatchSpanProcessor::DEFAULT_EXPORT_TIMEOUT),
Configuration::getInt(Env::OTEL_BSP_MAX_EXPORT_BATCH_SIZE, BatchSpanProcessor::DEFAULT_MAX_EXPORT_BATCH_SIZE),
);
case Values::VALUE_SIMPLE:
return new SimpleSpanProcessor($exporter);

View File

@ -0,0 +1,163 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\Tests\Unit\Contrib\Otlp;
use AssertWell\PHPUnitGlobalState\EnvironmentVariables;
use OpenTelemetry\Contrib\Otlp\SpanExporterFactory;
use OpenTelemetry\SDK\Common\Configuration\KnownValues;
use OpenTelemetry\SDK\Common\Configuration\Variables;
use OpenTelemetry\SDK\Common\Export\TransportFactoryInterface;
use OpenTelemetry\SDK\Common\Export\TransportInterface;
use PHPUnit\Framework\TestCase;
/**
* @covers \OpenTelemetry\Contrib\Otlp\SpanExporterFactory
* @psalm-suppress UndefinedInterfaceMethod
*/
class SpanExporterFactoryTest extends TestCase
{
use EnvironmentVariables;
private TransportFactoryInterface $transportFactory;
private TransportInterface $transport;
public function setUp(): void
{
$this->transportFactory = $this->createMock(TransportFactoryInterface::class);
$this->transport = $this->createMock(TransportInterface::class);
}
public function tearDown(): void
{
$this->restoreEnvironmentVariables();
}
public function test_unknown_protocol_exception(): void
{
$this->expectException(\UnexpectedValueException::class);
$this->setEnvironmentVariable(Variables::OTEL_EXPORTER_OTLP_PROTOCOL, 'foo');
$factory = new SpanExporterFactory();
$factory->fromEnvironment();
}
/**
* @dataProvider fromEnvironmentProvider
*/
public function test_create_from_environment(array $env, string $endpoint, string $protocol, string $compression, array $headerKeys = []): void
{
foreach ($env as $k => $v) {
$this->setEnvironmentVariable($k, $v);
}
$factory = new SpanExporterFactory($this->transportFactory);
// @phpstan-ignore-next-line
$this->transportFactory
->expects($this->once())
->method('create')
->with(
$this->equalTo($endpoint),
$this->equalTo($protocol),
$this->callback(function ($headers) use ($headerKeys) {
$this->assertEqualsCanonicalizing($headerKeys, array_keys($headers));
return true;
}),
$this->equalTo($compression)
)
->willReturn($this->transport);
// @phpstan-ignore-next-line
$this->transport->method('contentType')->willReturn($protocol);
$factory->fromEnvironment();
}
public function fromEnvironmentProvider(): array
{
$defaultHeaderKeys = ['User-Agent'];
return [
'signal-specific endpoint unchanged' => [
'env' => [
Variables::OTEL_EXPORTER_OTLP_TRACES_PROTOCOL => KnownValues::VALUE_GRPC,
Variables::OTEL_EXPORTER_OTLP_TRACES_ENDPOINT => 'http://collector:4317', //should not be changed, per spec
],
'endpoint' => 'http://collector:4317',
'protocol' => 'application/x-protobuf',
'compression' => 'none',
'headerKeys' => $defaultHeaderKeys,
],
'endpoint has path appended' => [
'env' => [
Variables::OTEL_EXPORTER_OTLP_TRACES_PROTOCOL => KnownValues::VALUE_GRPC,
Variables::OTEL_EXPORTER_OTLP_ENDPOINT => 'http://collector:4317',
],
'endpoint' => 'http://collector:4317/opentelemetry.proto.collector.trace.v1.TraceService/Export',
'protocol' => 'application/x-protobuf',
'compression' => 'none',
'headerKeys' => $defaultHeaderKeys,
],
'protocol' => [
'env' => [
Variables::OTEL_EXPORTER_OTLP_PROTOCOL => KnownValues::VALUE_HTTP_NDJSON,
],
'endpoint' => 'http://localhost:4318/v1/traces',
'protocol' => 'application/x-ndjson',
'compression' => 'none',
'headerKeys' => $defaultHeaderKeys,
],
'signal-specific protocol' => [
'env' => [
Variables::OTEL_EXPORTER_OTLP_PROTOCOL => KnownValues::VALUE_HTTP_JSON,
],
'endpoint' => 'http://localhost:4318/v1/traces',
'protocol' => 'application/json',
'compression' => 'none',
'headerKeys' => $defaultHeaderKeys,
],
'defaults' => [
'env' => [],
'endpoint' => 'http://localhost:4318/v1/traces',
'protocol' => 'application/x-protobuf',
'compression' => 'none',
'headerKeys' => $defaultHeaderKeys,
],
'compression' => [
'env' => [
Variables::OTEL_EXPORTER_OTLP_COMPRESSION => 'gzip',
],
'endpoint' => 'http://localhost:4318/v1/traces',
'protocol' => 'application/x-protobuf',
'compression' => 'gzip',
'headerKeys' => $defaultHeaderKeys,
],
'signal-specific compression' => [
'env' => [
Variables::OTEL_EXPORTER_OTLP_TRACES_COMPRESSION => 'gzip',
],
'endpoint' => 'http://localhost:4318/v1/traces',
'protocol' => 'application/x-protobuf',
'compression' => 'gzip',
'headerKeys' => $defaultHeaderKeys,
],
'headers' => [
'env' => [
Variables::OTEL_EXPORTER_OTLP_HEADERS => 'key1=foo,key2=bar',
],
'endpoint' => 'http://localhost:4318/v1/traces',
'protocol' => 'application/x-protobuf',
'compression' => 'none',
'headerKeys' => array_merge($defaultHeaderKeys, ['key1', 'key2']),
],
'signal-specific headers' => [
'env' => [
Variables::OTEL_EXPORTER_OTLP_TRACES_HEADERS => 'key3=foo,key4=bar',
],
'endpoint' => 'http://localhost:4318/v1/traces',
'protocol' => 'application/x-protobuf',
'compression' => 'none',
'headerKeys' => array_merge($defaultHeaderKeys, ['key3', 'key4']),
],
];
}
}

View File

@ -0,0 +1,456 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\Tests\Unit\SDK\Common\Configuration;
use AssertWell\PHPUnitGlobalState\EnvironmentVariables;
use Exception;
use Generator;
use OpenTelemetry\SDK\Common\Configuration\Configuration;
use OpenTelemetry\SDK\Common\Configuration\Defaults;
use OpenTelemetry\SDK\Common\Configuration\KnownValues;
use OpenTelemetry\SDK\Common\Configuration\Variables;
use OpenTelemetry\SDK\Common\Configuration\VariableTypes;
use PHPUnit\Framework\TestCase;
use UnexpectedValueException;
/**
* @covers \OpenTelemetry\SDK\Common\Configuration\Configuration
*/
class ConfigurationTest extends TestCase
{
use EnvironmentVariables;
private const ALLOW_EMPTY = [
VariableTypes::LIST,
VariableTypes::MAP,
VariableTypes::MIXED,
];
private const METHOD_NAMES = [
VariableTypes::STRING => ['getString'],
VariableTypes::BOOL => ['getBoolean'],
VariableTypes::INTEGER => ['getInt'],
VariableTypes::FLOAT => ['getFloat'],
VariableTypes::RATIO => ['getRatio'],
VariableTypes::ENUM => ['getEnum'],
VariableTypes::LIST => ['getList'],
VariableTypes::MAP => ['getMap'],
VariableTypes::MIXED => ['getMixed'],
];
private const TYPES = [
VariableTypes::STRING => [Variables::OTEL_SERVICE_NAME],
VariableTypes::BOOL => [Variables::OTEL_EXPORTER_OTLP_INSECURE],
VariableTypes::INTEGER => [Variables::OTEL_EXPORTER_JAEGER_AGENT_PORT],
VariableTypes::ENUM => [Variables::OTEL_LOG_LEVEL],
VariableTypes::LIST => [Variables::OTEL_PROPAGATORS],
VariableTypes::MAP => [Variables::OTEL_RESOURCE_ATTRIBUTES],
VariableTypes::MIXED => [Variables::OTEL_TRACES_SAMPLER_ARG],
];
private const USER_VALUES = [
VariableTypes::STRING => ['foo', 'foo'],
VariableTypes::BOOL => ['true', true],
VariableTypes::INTEGER => ['42', 42],
VariableTypes::ENUM => ['val1', 'val1'],
VariableTypes::LIST => ['val1,val2', ['val1','val2']],
VariableTypes::MAP => ['var1=val1,var2=val2', ['var1'=>'val1','var2'=>'val2']],
VariableTypes::MIXED => ['foo', 'foo'],
];
private const LIBRARY_DEFAULTS = [
VariableTypes::STRING => [Variables::OTEL_EXPORTER_OTLP_ENDPOINT, 'http://localhost:4318'],
VariableTypes::BOOL => [Variables::OTEL_EXPORTER_OTLP_INSECURE, false],
VariableTypes::INTEGER => [Variables::OTEL_EXPORTER_JAEGER_AGENT_PORT, 6831],
VariableTypes::ENUM => [Variables::OTEL_LOG_LEVEL, 'info'],
VariableTypes::LIST => [Variables::OTEL_PROPAGATORS, ['tracecontext', 'baggage']],
];
private const DEFAULT_VALUES = [
'log level' => [Variables::OTEL_LOG_LEVEL, Defaults::OTEL_LOG_LEVEL],
'attribute count limit' => [Variables::OTEL_ATTRIBUTE_COUNT_LIMIT, Defaults::OTEL_ATTRIBUTE_COUNT_LIMIT],
'trace exporter' => [Variables::OTEL_PHP_TRACES_PROCESSOR, Defaults::OTEL_PHP_TRACES_PROCESSOR],
];
private const NO_DEFAULTS = [
VariableTypes::STRING => [Variables::OTEL_SERVICE_NAME],
VariableTypes::ENUM => [Variables::OTEL_EXPORTER_OTLP_COMPRESSION],
VariableTypes::MAP => [Variables::OTEL_EXPORTER_OTLP_HEADERS],
VariableTypes::MIXED => [Variables::OTEL_TRACES_SAMPLER_ARG],
];
private const KNOWN_VALUES = [
'log level' => [Variables::OTEL_LOG_LEVEL, KnownValues::OTEL_LOG_LEVEL],
'trace sampler' => [Variables::OTEL_TRACES_SAMPLER, KnownValues::OTEL_TRACES_SAMPLER],
'trace processor' => [Variables::OTEL_PHP_TRACES_PROCESSOR, KnownValues::OTEL_PHP_TRACES_PROCESSOR],
];
public function tearDown(): void
{
$this->restoreEnvironmentVariables();
}
public function test_has_variable_from_environment(): void
{
$this->assertFalse(Configuration::has('FOO_VAR'));
$this->setEnvironmentVariable('FOO_VAR', 'FOO');
$this->assertTrue(Configuration::has('FOO_VAR'));
}
public function test_integer_get(): void
{
$this->setEnvironmentVariable('OTEL_FOO', '100');
$value = Configuration::getInt('OTEL_FOO', 999);
$this->assertSame(100, $value);
}
public function test_integer_failure(): void
{
$this->setEnvironmentVariable('OTEL_FOO', 'foo');
$this->expectException(Exception::class);
Configuration::getInt('OTEL_FOO', 99);
}
public function environment_variables_integer_uses_default_if_env_var_not_defined()
{
$this->assertSame(20, Configuration::getInt('OTEL_FOO', 20));
}
public function test_string_get(): void
{
$this->setEnvironmentVariable('OTEL_FOO', 'foo');
$value = Configuration::getString('OTEL_FOO', 'bar');
$this->assertSame('foo', $value);
}
/**
* The SDK MUST interpret an empty value of an environment variable the same way as when the variable is unset
* @dataProvider emptyProvider
*/
public function test_string_uses_default_when_empty_value(?string $input): void
{
$this->setEnvironmentVariable('OTEL_FOO', $input);
$value = Configuration::getString('OTEL_FOO', 'bar');
$this->assertSame('bar', $value);
}
/**
* @dataProvider emptyProvider
*/
public function test_int_uses_default_when_empty_value(?string $input): void
{
$this->setEnvironmentVariable('OTEL_FOO', $input);
$value = Configuration::getInt('OTEL_FOO', 99);
$this->assertSame(99, $value);
}
/**
* @dataProvider emptyProvider
*/
public function test_bool_uses_default_when_empty_value(?string $input): void
{
$this->setEnvironmentVariable('OTEL_FOO', $input);
$value = Configuration::getBoolean('OTEL_FOO', true);
$this->assertTrue($value);
}
public function emptyProvider()
{
return [
'no value' => [null],
'empty string' => [''],
];
}
/**
* @dataProvider booleanProvider
*/
public function test_bool_get(string $input, bool $default, bool $expected)
{
$this->setEnvironmentVariable('OTEL_BOOL', $input);
$this->assertSame($expected, Configuration::getBoolean('OTEL_BOOL', $default));
}
public function booleanProvider()
{
return [
'false' => ['false', true, false],
'true' => ['true', false, true],
'truthy' => ['1', false, true],
'falsey' => ['0', true, false],
'TRUE' => ['TRUE', false, true],
'FALSE' => ['FALSE', true, false],
];
}
public function test_list_get()
{
$this->setEnvironmentVariable('OTEL_LIST', 'a,b,c');
$this->assertSame(['a', 'b', 'c'], Configuration::getList('OTEL_LIST'));
}
public function test_map_get()
{
$this->setEnvironmentVariable('OTEL_MAP', 'a=b,c=d');
$this->assertSame(['a'=>'b', 'c'=>'d'], Configuration::getMap('OTEL_MAP'));
}
public function test_enum_get()
{
$this->setEnvironmentVariable('OTEL_ENUM', 'foo');
$this->assertSame('foo', Configuration::getEnum('OTEL_ENUM'));
}
public function test_ratio_get()
{
$this->setEnvironmentVariable('OTEL_RATIO', '0.5');
$this->assertSame(0.5, Configuration::getRatio('OTEL_RATIO'));
}
/**
* @dataProvider userValueProvider
*/
public function test_return_user_env_vars(string $methodName, string $variable, string $value, $result)
{
$this->setEnvironmentVariable($variable, $value);
$this->assertSame(
$result,
call_user_func([Configuration::class, $methodName], $variable)
);
}
/**
* @dataProvider userValueProvider
*/
public function test_return_user_default_value(string $methodName, string $variable, string $defaultValue, $result)
{
$this->assertSame(
$result,
call_user_func([Configuration::class, $methodName], $variable, $defaultValue)
);
}
/**
* @dataProvider libraryDefaultValueProvider
*/
public function test_return_library_default_value(string $methodName, string $variable, $result)
{
$this->assertSame(
$result,
call_user_func([Configuration::class, $methodName], $variable)
);
}
/**
* @dataProvider nonEmptyMethodNameProvider
*/
public function test_no_value_throws_exception(string $methodName)
{
$this->expectException(UnexpectedValueException::class);
call_user_func([Configuration::class, $methodName], 'FOO_BAR_' . $methodName);
}
/**
* @dataProvider invalidTypeProvider
*/
public function test_invalid_type_throws_exception(string $methodName, string $variable)
{
$this->expectException(UnexpectedValueException::class);
call_user_func([Configuration::class, $methodName], $variable);
}
/**
* @dataProvider noDefaultProvider
*/
public function test_null_result_throws_exception(string $methodName, string $variable)
{
$this->expectException(UnexpectedValueException::class);
call_user_func([Configuration::class, $methodName], $variable);
}
public function userValueProvider(): Generator
{
foreach (self::USER_VALUES as $varType => $values) {
[$default, $result] = $values;
yield $varType => [
self::METHOD_NAMES[$varType][0],
self::TYPES[$varType][0],
$default,
$result,
];
}
}
public function libraryDefaultValueProvider(): Generator
{
foreach (self::LIBRARY_DEFAULTS as $varType => $values) {
[$variable, $result] = $values;
yield $varType => [
self::METHOD_NAMES[$varType][0],
$variable,
$result,
];
}
}
public function nonEmptyMethodNameProvider(): Generator
{
foreach (self::METHOD_NAMES as $varType => $names) {
if (in_array($varType, self::ALLOW_EMPTY)) {
continue;
}
yield $varType => $names;
}
}
public function invalidTypeProvider(): Generator
{
foreach (self::METHOD_NAMES as $varType => $names) {
if ($varType === VariableTypes::MIXED) {
continue;
}
$methodName = $names[0];
foreach (self::TYPES as $methodType => $types) {
if ($varType === $methodType || $methodType === VariableTypes::MIXED) {
continue;
}
$variableName = $types[0];
yield sprintf('%s - %s', $varType, $methodType) => [$methodName, $variableName];
}
}
}
public function noDefaultProvider(): Generator
{
foreach (self::NO_DEFAULTS as $varType => $values) {
if (in_array($varType, self::ALLOW_EMPTY)) {
continue;
}
yield $varType => [self::METHOD_NAMES[$varType][0], $values[0]];
}
}
public function test_default_ratio_for_non_existent_variable(): void
{
$value = Configuration::getRatio('not-set', 0);
$this->assertSame(0.0, $value);
}
/**
* @dataProvider knownValuesProvider
*/
public function test_get_known_values(string $varName, array $varValue): void
{
$this->assertSame(
$varValue,
Configuration::getKnownValues($varName)
);
}
public function knownValuesProvider(): array
{
return self::KNOWN_VALUES;
}
public function test_retrieve_value_library_default(): void
{
$value = 'simple';
$variable = Variables::OTEL_PHP_TRACES_PROCESSOR;
$this->assertFalse(
Configuration::has($variable)
);
$this->assertSame(
$value,
Configuration::getEnum($variable, $value)
);
}
/**
* @dataProvider typeProvider
*/
public function test_get_type(string $varName, string $type): void
{
$this->assertSame(
$type,
Configuration::getType($varName)
);
}
public function typeProvider(): array
{
return [
'bool' => ['OTEL_EXPORTER_OTLP_INSECURE', VariableTypes::BOOL],
'string' => ['OTEL_SERVICE_NAME', VariableTypes::STRING],
'integer' => ['OTEL_EXPORTER_JAEGER_AGENT_PORT', VariableTypes::INTEGER],
'enum' => ['OTEL_LOG_LEVEL', VariableTypes::ENUM],
'list' => ['OTEL_PROPAGATORS', VariableTypes::LIST],
'map' => ['OTEL_RESOURCE_ATTRIBUTES', VariableTypes::MAP],
'mixed' => ['OTEL_TRACES_SAMPLER_ARG', VariableTypes::MIXED],
];
}
/**
* @dataProvider defaultValueProvider
*/
public function test_get_default_value_with_empty_var(string $varName, $varValue): void
{
$this->setEnvironmentVariable($varName, '');
$this->assertSame(
$varValue,
Configuration::getDefault($varName)
);
}
/**
* @dataProvider defaultValueProvider
*/
public function test_get_default_value(string $varName, $varValue): void
{
$this->assertSame(
$varValue,
Configuration::getDefault($varName)
);
}
public function defaultValueProvider(): array
{
return self::DEFAULT_VALUES;
}
/**
* @dataProvider nonStringProvider
*/
public function test_get_non_string_value(string $method, $value): void
{
$_SERVER['OTEL_FOO'] = $value;
$this->assertTrue(Configuration::has('OTEL_FOO'));
$this->assertSame($value, call_user_func([Configuration::class, $method], 'OTEL_FOO'));
}
public function nonStringProvider(): array
{
return [
['getFloat', 3.14159],
['getInt', 22],
['getBoolean', true],
['getRatio', 0.44],
['getMixed', [25, 'green']],
['getList', ['foo', 'bar']],
['getMap', ['key1' => 'val1', 'key2' => 'val2']],
];
}
}

View File

@ -2,14 +2,14 @@
declare(strict_types=1);
namespace OpenTelemetry\Tests\Unit\SDK\Common\Environment\Parser;
namespace OpenTelemetry\Tests\Unit\SDK\Common\Configuration\Parser;
use InvalidArgumentException;
use OpenTelemetry\SDK\Common\Environment\Parser\BooleanParser;
use OpenTelemetry\SDK\Common\Configuration\Parser\BooleanParser;
use PHPUnit\Framework\TestCase;
/**
* @covers \OpenTelemetry\SDK\Common\Environment\Parser\BooleanParser
* @covers \OpenTelemetry\SDK\Common\Configuration\Parser\BooleanParser
*/
class BooleanParserTest extends TestCase
{

View File

@ -2,13 +2,12 @@
declare(strict_types=1);
namespace OpenTelemetry\Tests\Unit\SDK\Common\Environment\Parser;
namespace OpenTelemetry\Tests\Unit\SDK\Common\Configuration\Parser;
use OpenTelemetry\SDK\Common\Environment\Parser\ListParser;
use PHPUnit\Framework\TestCase;
/**
* @covers \OpenTelemetry\SDK\Common\Environment\Parser\ListParser
* @covers \OpenTelemetry\SDK\Common\Configuration\Parser\ListParser
*/
class ListParserTest extends TestCase
{
@ -41,7 +40,7 @@ class ListParserTest extends TestCase
public function test_comma_separated_list_returns_array(string $value, array $expected): void
{
$this->assertSame(
ListParser::parse($value),
\OpenTelemetry\SDK\Common\Configuration\Parser\ListParser::parse($value),
$expected
);
}

View File

@ -2,14 +2,13 @@
declare(strict_types=1);
namespace OpenTelemetry\Tests\Unit\SDK\Common\Environment\Parser;
namespace OpenTelemetry\Tests\Unit\SDK\Common\Configuration\Parser;
use InvalidArgumentException;
use OpenTelemetry\SDK\Common\Environment\Parser\MapParser;
use PHPUnit\Framework\TestCase;
/**
* @covers \OpenTelemetry\SDK\Common\Environment\Parser\MapParser
* @covers \OpenTelemetry\SDK\Common\Configuration\Parser\MapParser
*/
class MapParserTest extends TestCase
{
@ -43,7 +42,7 @@ class MapParserTest extends TestCase
public function test_map_values_return_array(string $value, array $expected): void
{
$this->assertSame(
MapParser::parse($value),
\OpenTelemetry\SDK\Common\Configuration\Parser\MapParser::parse($value),
$expected
);
}
@ -55,7 +54,7 @@ class MapParserTest extends TestCase
{
$this->expectException(InvalidArgumentException::class);
MapParser::parse($value);
\OpenTelemetry\SDK\Common\Configuration\Parser\MapParser::parse($value);
}
public function mapValueProvider(): array

View File

@ -2,15 +2,15 @@
declare(strict_types=1);
namespace OpenTelemetry\Tests\Unit\SDK\Common\Environment\Parser;
namespace OpenTelemetry\Tests\Unit\SDK\Common\Configuration\Parser;
use InvalidArgumentException;
use OpenTelemetry\SDK\Common\Environment\Parser\RatioParser;
use OpenTelemetry\SDK\Common\Configuration\Parser\RatioParser;
use PHPUnit\Framework\TestCase;
use RangeException;
/**
* @covers \OpenTelemetry\SDK\Common\Environment\Parser\RatioParser
* @covers \OpenTelemetry\SDK\Common\Configuration\Parser\RatioParser
*/
class RatioParserTest extends TestCase
{

View File

@ -0,0 +1,92 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\Tests\Unit\SDK\Common\Configuration\Resolver;
use OpenTelemetry\SDK\Common\Configuration\Defaults;
use OpenTelemetry\SDK\Common\Configuration\Resolver\CompositeResolver;
use OpenTelemetry\SDK\Common\Configuration\Resolver\ResolverInterface;
use OpenTelemetry\SDK\Common\Configuration\Variables;
use PHPUnit\Framework\TestCase;
/**
* @covers \OpenTelemetry\SDK\Common\Configuration\Resolver\CompositeResolver
* @psalm-suppress UndefinedInterfaceMethod
*/
class CompositeResolverTest extends TestCase
{
private ResolverInterface $one;
private ResolverInterface $two;
private CompositeResolver $resolver;
public function setUp(): void
{
$this->one = $this->createMock(ResolverInterface::class);
$this->two = $this->createMock(ResolverInterface::class);
$this->resolver = new CompositeResolver([$this->one, $this->two]);
}
public function test_get_instance(): void
{
$instance = CompositeResolver::instance();
$this->assertGreaterThanOrEqual(1, count($instance->getResolvers()));
}
public function test_has_variable(): void
{
$this->one->expects($this->once())->method('hasVariable')->willReturn(false);
$this->two->expects($this->once())->method('hasVariable')->willReturn(true);
$this->assertTrue($this->resolver->hasVariable('OTEL_FOO'));
}
public function test_not_has_variable(): void
{
$this->one->expects($this->once())->method('hasVariable')->willReturn(false);
$this->two->expects($this->once())->method('hasVariable')->willReturn(false);
$this->assertFalse($this->resolver->hasVariable('OTEL_FOO'));
}
public function test_resolve_when_has_variable(): void
{
$var = 'OTEL_FOO';
$this->one->method('hasVariable')->willReturn(false);
$this->two->method('hasVariable')->willReturn(true);
$this->two
->expects($this->once())
->method('retrieveValue')
->with($var)
->willReturn('foo');
$this->assertSame('foo', $this->resolver->resolve($var));
}
public function test_resolve_uses_default_when_not_empty(): void
{
$this->one->method('hasVariable')->willReturn(false);
$this->two->method('hasVariable')->willReturn(false);
$this->assertSame('foo', $this->resolver->resolve(Variables::OTEL_EXPORTER_OTLP_PROTOCOL, 'foo'));
}
/**
* @dataProvider emptyProvider
*/
public function test_resolve_uses_library_default_when_empty(?string $value): void
{
$this->one->method('hasVariable')->willReturn(false);
$this->two->method('hasVariable')->willReturn(false);
$this->assertSame(Defaults::OTEL_EXPORTER_OTLP_PROTOCOL, $this->resolver->resolve(Variables::OTEL_EXPORTER_OTLP_PROTOCOL, $value));
}
public function emptyProvider(): array
{
return [
[''],
[null],
];
}
}

View File

@ -0,0 +1,121 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\Tests\Unit\SDK\Common\Configuration\Resolver;
use AssertWell\PHPUnitGlobalState\EnvironmentVariables;
use OpenTelemetry\SDK\Common\Configuration\Resolver\EnvironmentResolver;
use PHPUnit\Framework\TestCase;
/**
* @covers \OpenTelemetry\SDK\Common\Configuration\Resolver\EnvironmentResolver
*/
class EnvironmentResolverTest extends TestCase
{
use EnvironmentVariables;
private const RAW_VALUES = [
'string' => ['STRING_VAR', 'foo'],
'int' => ['INT_VAR', '42'],
'float' => ['FLOAT_VAR', '4.2'],
'list' => ['LIST_VAR', 'foo,bar,baz'],
'map' => ['MAP_VAR', 'foo=bar,bar=baz'],
];
private EnvironmentResolver $resolver;
public function setUp(): void
{
$this->resolver = new EnvironmentResolver();
}
public function tearDown(): void
{
$this->restoreEnvironmentVariables();
}
public function test_has_variable(): void
{
$this->assertFalse(
$this->resolver->hasVariable('FOO_VAR')
);
$this->setEnvironmentVariable('FOO_VAR', 'FOO');
$this->assertTrue(
$this->resolver->hasVariable('FOO_VAR')
);
}
public function test_has_variable_with_injected_value(): void
{
$this->assertFalse(
$this->resolver->hasVariable('FOO_VAR')
);
$this->injectEnvironmentVariable('FOO_VAR', 'FOO');
$this->assertTrue(
$this->resolver->hasVariable('FOO_VAR')
);
}
/**
* @dataProvider rawValueProvider
*/
public function test_retrieve_value(string $varName, string $varValue): void
{
$this->setEnvironmentVariable($varName, $varValue);
$this->assertSame(
$varValue,
$this->resolver->retrieveValue($varName)
);
}
public function test_retrieve_value_no_var(): void
{
$this->assertFalse(
$this->resolver->hasVariable('FOO_VAR')
);
$this->assertNull(
$this->resolver->retrieveValue('FOO_VAR')
);
}
public function test_retrieve_value_with_injected_value(): void
{
$value = 'simple';
$variable = 'OTEL_PHP_TRACES_PROCESSOR';
$this->assertFalse(
$this->resolver->hasVariable($variable)
);
$this->injectEnvironmentVariable($variable, $value);
$this->assertSame(
$value,
$this->resolver->retrieveValue($variable)
);
}
public function rawValueProvider(): array
{
return self::RAW_VALUES;
}
private function injectEnvironmentVariable(string $name, $value): void
{
$_SERVER[$name] = $value;
}
public function test_get_array_from_env(): void
{
$this->injectEnvironmentVariable('OTEL_FOO', ['foo', 'bar']);
$value = $this->resolver->retrieveValue('OTEL_FOO');
$this->assertEqualsCanonicalizing(['foo', 'bar'], $value);
}
}

View File

@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\Example\Unit\SDK\Common\Configuration\Resolver;
use OpenTelemetry\SDK\Common\Configuration\Resolver\PhpIniAccessor;
use OpenTelemetry\SDK\Common\Configuration\Resolver\PhpIniResolver;
use PHPUnit\Framework\TestCase;
/**
* @covers \OpenTelemetry\SDK\Common\Configuration\Resolver\PhpIniResolver
* @psalm-suppress UndefinedMethod
*/
class PhpIniResolverTest extends TestCase
{
private PhpIniAccessor $accessor;
private PhpIniResolver $resolver;
public function setUp(): void
{
$this->accessor = $this->createMock(PhpIniAccessor::class);
$this->resolver = new PhpIniResolver($this->accessor);
}
public function test_retrieve_array(): void
{
$this->accessor->expects($this->once())->method('get')->willReturn(['foo', 'bar', 'baz']);
$this->assertSame('foo,bar,baz', $this->resolver->retrieveValue('OTEL_FOO'));
}
public function test_retrieve_string(): void
{
$this->accessor->expects($this->once())->method('get')->willReturn('foo');
$this->assertSame('foo', $this->resolver->retrieveValue('OTEL_FOO'));
}
/**
* @dataProvider hasVariableProvider
*/
public function test_has_variable($value, bool $expected): void
{
$this->accessor->method('get')->willReturn($value);
$this->assertSame($expected, $this->resolver->hasVariable('OTEL_FOO'));
}
public function hasVariableProvider(): array
{
return [
'string' => ['foo', true],
'array' => [['foo'], true],
'empty string' => ['', false],
'null' => [null, false],
'empty array' => [[], false],
];
}
}

View File

@ -1,211 +0,0 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\Tests\Unit\SDK\Common\Environment;
use AssertWell\PHPUnitGlobalState\EnvironmentVariables;
use Generator;
use OpenTelemetry\SDK\Common\Environment\Accessor;
use OpenTelemetry\SDK\Common\Environment\Variables as Env;
use OpenTelemetry\SDK\Common\Environment\VariableTypes;
use PHPUnit\Framework\TestCase;
use UnexpectedValueException;
/**
* @covers \OpenTelemetry\SDK\Common\Environment\Accessor
*/
class AccessorTest extends TestCase
{
use EnvironmentVariables;
private const ALLOW_EMPTY = [
VariableTypes::LIST,
VariableTypes::MAP,
VariableTypes::MIXED,
];
private const METHOD_NAMES = [
VariableTypes::STRING => ['getString'],
VariableTypes::BOOL => ['getBool'],
VariableTypes::INTEGER => ['getInt'],
VariableTypes::FLOAT => ['getFloat'],
VariableTypes::RATIO => ['getRatio'],
VariableTypes::ENUM => ['getEnum'],
VariableTypes::LIST => ['getList'],
VariableTypes::MAP => ['getMap'],
VariableTypes::MIXED => ['getMixed'],
];
private const TYPES = [
VariableTypes::STRING => [Env::OTEL_SERVICE_NAME],
VariableTypes::BOOL => [Env::OTEL_EXPORTER_OTLP_INSECURE],
VariableTypes::INTEGER => [Env::OTEL_EXPORTER_JAEGER_AGENT_PORT],
VariableTypes::ENUM => [Env::OTEL_LOG_LEVEL],
VariableTypes::LIST => [Env::OTEL_PROPAGATORS],
VariableTypes::MAP => [Env::OTEL_RESOURCE_ATTRIBUTES],
VariableTypes::MIXED => [Env::OTEL_TRACES_SAMPLER_ARG],
];
private const USER_VALUES = [
VariableTypes::STRING => ['foo', 'foo'],
VariableTypes::BOOL => ['true', true],
VariableTypes::INTEGER => ['42', 42],
VariableTypes::ENUM => ['val1', 'val1'],
VariableTypes::LIST => ['val1,val2', ['val1','val2']],
VariableTypes::MAP => ['var1=val1,var2=val2', ['var1'=>'val1','var2'=>'val2']],
VariableTypes::MIXED => ['foo', 'foo'],
];
private const LIBRARY_DEFAULTS = [
VariableTypes::STRING => [Env::OTEL_EXPORTER_OTLP_ENDPOINT, 'http://localhost:4318'],
VariableTypes::BOOL => [Env::OTEL_EXPORTER_OTLP_INSECURE, false],
VariableTypes::INTEGER => [Env::OTEL_EXPORTER_JAEGER_AGENT_PORT, 6831],
VariableTypes::ENUM => [Env::OTEL_LOG_LEVEL, 'info'],
VariableTypes::LIST => [Env::OTEL_PROPAGATORS, ['tracecontext', 'baggage']],
];
private const NO_DEFAULTS = [
VariableTypes::STRING => [Env::OTEL_SERVICE_NAME],
VariableTypes::ENUM => [Env::OTEL_EXPORTER_OTLP_COMPRESSION],
VariableTypes::MAP => [Env::OTEL_EXPORTER_OTLP_HEADERS],
VariableTypes::MIXED => [Env::OTEL_TRACES_SAMPLER_ARG],
];
public function tearDown(): void
{
$this->restoreEnvironmentVariables();
}
/**
* @dataProvider userValueProvider
*/
public function test_return_user_env_vars(string $methodName, string $variable, string $value, $result)
{
$this->setEnvironmentVariable($variable, $value);
$this->assertSame(
$result,
call_user_func([Accessor::class, $methodName], $variable)
);
}
/**
* @dataProvider userValueProvider
*/
public function test_return_user_default_value(string $methodName, string $variable, string $defaultValue, $result)
{
$this->assertSame(
$result,
call_user_func([Accessor::class, $methodName], $variable, $defaultValue)
);
}
/**
* @dataProvider libraryDefaultValueProvider
*/
public function test_return_library_default_value(string $methodName, string $variable, $result)
{
$this->assertSame(
$result,
call_user_func([Accessor::class, $methodName], $variable)
);
}
/**
* @dataProvider nonEmptyMethodNameProvider
*/
public function test_no_value_throws_exception(string $methodName)
{
$this->expectException(UnexpectedValueException::class);
call_user_func([Accessor::class, $methodName], 'FOO_BAR_' . $methodName);
}
/**
* @dataProvider invalidTypeProvider
*/
public function test_invalid_type_throws_exception(string $methodName, string $variable)
{
$this->expectException(UnexpectedValueException::class);
call_user_func([Accessor::class, $methodName], $variable);
}
/**
* @dataProvider noDefaultProvider
*/
public function test_null_result_throws_exception(string $methodName, string $variable)
{
$this->expectException(UnexpectedValueException::class);
call_user_func([Accessor::class, $methodName], $variable);
}
public function userValueProvider(): Generator
{
foreach (self::USER_VALUES as $varType => $values) {
[$default, $result] = $values;
yield $varType => [
self::METHOD_NAMES[$varType][0],
self::TYPES[$varType][0],
$default,
$result,
];
}
}
public function libraryDefaultValueProvider(): Generator
{
foreach (self::LIBRARY_DEFAULTS as $varType => $values) {
[$variable, $result] = $values;
yield $varType => [
self::METHOD_NAMES[$varType][0],
$variable,
$result,
];
}
}
public function nonEmptyMethodNameProvider(): Generator
{
foreach (self::METHOD_NAMES as $varType => $names) {
if (in_array($varType, self::ALLOW_EMPTY)) {
continue;
}
yield $varType => $names;
}
}
public function invalidTypeProvider(): Generator
{
foreach (self::METHOD_NAMES as $varType => $names) {
if ($varType === VariableTypes::MIXED) {
continue;
}
$methodName = $names[0];
foreach (self::TYPES as $methodType => $types) {
if ($varType === $methodType || $methodType === VariableTypes::MIXED) {
continue;
}
$variableName = $types[0];
yield sprintf('%s - %s', $varType, $methodType) => [$methodName, $variableName];
}
}
}
public function noDefaultProvider(): Generator
{
foreach (self::NO_DEFAULTS as $varType => $values) {
if (in_array($varType, self::ALLOW_EMPTY)) {
continue;
}
yield $varType => [self::METHOD_NAMES[$varType][0], $values[0]];
}
}
}

View File

@ -1,140 +0,0 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\Tests\Unit\SDK\Common\Environment;
use AssertWell\PHPUnitGlobalState\EnvironmentVariables;
use Exception;
use OpenTelemetry\SDK\Common\Environment\EnvironmentVariables as Env;
use PHPUnit\Framework\TestCase;
/**
* @covers \OpenTelemetry\SDK\Common\Environment\EnvironmentVariables
*/
class EnvironmentVariablesTest extends TestCase
{
use EnvironmentVariables;
public function tearDown(): void
{
$this->restoreEnvironmentVariables();
}
public function test_has_environment_variable(): void
{
$this->assertFalse(Env::has('FOO_VAR'));
$this->setEnvironmentVariable('FOO_VAR', 'FOO');
$this->assertTrue(Env::has('FOO_VAR'));
}
public function test_integer_get(): void
{
$this->setEnvironmentVariable('OTEL_FOO', '100');
$value = Env::getInt('OTEL_FOO', 999);
$this->assertSame(100, $value);
}
public function test_integer_failure(): void
{
$this->setEnvironmentVariable('OTEL_FOO', 'foo');
$this->expectException(Exception::class);
Env::getInt('OTEL_FOO', 99);
}
public function environment_variables_integer_uses_default_if_env_var_not_defined()
{
$this->assertSame(20, Env::getInt('OTEL_FOO', 20));
}
public function test_string_get(): void
{
$this->setEnvironmentVariable('OTEL_FOO', 'foo');
$value = Env::getString('OTEL_FOO', 'bar');
$this->assertSame('foo', $value);
}
/**
* The SDK MUST interpret an empty value of an environment variable the same way as when the variable is unset
* @dataProvider emptyProvider
*/
public function test_string_uses_default_when_empty_value(?string $input): void
{
$this->setEnvironmentVariable('OTEL_FOO', $input);
$value = Env::getString('OTEL_FOO', 'bar');
$this->assertSame('bar', $value);
}
/**
* @dataProvider emptyProvider
*/
public function test_int_uses_default_when_empty_value(?string $input): void
{
$this->setEnvironmentVariable('OTEL_FOO', $input);
$value = Env::getInt('OTEL_FOO', 99);
$this->assertSame(99, $value);
}
/**
* @dataProvider emptyProvider
*/
public function test_bool_uses_default_when_empty_value(?string $input): void
{
$this->setEnvironmentVariable('OTEL_FOO', $input);
$value = Env::getBoolean('OTEL_FOO', true);
$this->assertTrue($value);
}
public function emptyProvider()
{
return [
'no value' => [null],
'empty string' => [''],
];
}
/**
* @dataProvider booleanProvider
*/
public function test_bool_get(string $input, bool $default, bool $expected)
{
$this->setEnvironmentVariable('OTEL_BOOL', $input);
$this->assertSame($expected, Env::getBoolean('OTEL_BOOL', $default));
}
public function booleanProvider()
{
return [
'false' => ['false', true, false],
'true' => ['true', false, true],
'truthy' => ['1', false, true],
'falsey' => ['0', true, false],
'TRUE' => ['TRUE', false, true],
'FALSE' => ['FALSE', true, false],
];
}
public function test_list_get()
{
$this->setEnvironmentVariable('OTEL_LIST', 'a,b,c');
$this->assertSame(['a', 'b', 'c'], Env::getList('OTEL_LIST'));
}
public function test_map_get()
{
$this->setEnvironmentVariable('OTEL_MAP', 'a=b,c=d');
$this->assertSame(['a'=>'b', 'c'=>'d'], Env::getMap('OTEL_MAP'));
}
public function test_enum_get()
{
$this->setEnvironmentVariable('OTEL_ENUM', 'foo');
$this->assertSame('foo', Env::getEnum('OTEL_ENUM'));
}
public function test_ratio_get()
{
$this->setEnvironmentVariable('OTEL_RATIO', '0.5');
$this->assertSame(0.5, Env::getRatio('OTEL_RATIO'));
}
}

View File

@ -1,271 +0,0 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\Tests\Unit\SDK\Common\Environment;
use AssertWell\PHPUnitGlobalState\EnvironmentVariables;
use OpenTelemetry\SDK\Common\Environment\Defaults;
use OpenTelemetry\SDK\Common\Environment\KnownValues;
use OpenTelemetry\SDK\Common\Environment\Resolver;
use OpenTelemetry\SDK\Common\Environment\VariableTypes;
use PHPUnit\Framework\TestCase;
/**
* @covers \OpenTelemetry\SDK\Common\Environment\Resolver
*/
class ResolverTest extends TestCase
{
use EnvironmentVariables;
private const RAW_VALUES = [
'string' => ['STRING_VAR', 'foo'],
'int' => ['INT_VAR', '42'],
'float' => ['FLOAT_VAR', '4.2'],
'list' => ['LIST_VAR', 'foo,bar,baz'],
'map' => ['MAP_VAR', 'foo=bar,bar=baz'],
];
private const DEFAULT_VALUES = [
'log level' => ['OTEL_LOG_LEVEL', Defaults::OTEL_LOG_LEVEL],
'attribute count limit' => ['OTEL_ATTRIBUTE_COUNT_LIMIT', Defaults::OTEL_ATTRIBUTE_COUNT_LIMIT],
'trace exporter' => ['OTEL_PHP_TRACES_PROCESSOR', Defaults::OTEL_PHP_TRACES_PROCESSOR],
];
private const TYPES = [
'bool' => ['OTEL_EXPORTER_OTLP_INSECURE', VariableTypes::BOOL],
'string' => ['OTEL_SERVICE_NAME', VariableTypes::STRING],
'integer' => ['OTEL_EXPORTER_JAEGER_AGENT_PORT', VariableTypes::INTEGER],
'enum' => ['OTEL_LOG_LEVEL', VariableTypes::ENUM],
'list' => ['OTEL_PROPAGATORS', VariableTypes::LIST],
'map' => ['OTEL_RESOURCE_ATTRIBUTES', VariableTypes::MAP],
'mixed' => ['OTEL_TRACES_SAMPLER_ARG', VariableTypes::MIXED],
];
private const KNOWN_VALUES = [
'log level' => ['OTEL_LOG_LEVEL', KnownValues::OTEL_LOG_LEVEL],
'trace sampler' => ['OTEL_TRACES_SAMPLER', KnownValues::OTEL_TRACES_SAMPLER],
'trace processor' => ['OTEL_PHP_TRACES_PROCESSOR', KnownValues::OTEL_PHP_TRACES_PROCESSOR],
];
/**
* All injected environment variables.
*/
private array $injectedEnvironmentVariables = [];
public function tearDown(): void
{
$this->resetEnvironmentVariables();
}
public function test_has_variable(): void
{
$this->assertFalse(
Resolver::hasVariable('FOO_VAR')
);
$this->setEnvironmentVariable('FOO_VAR', 'FOO');
$this->assertTrue(
Resolver::hasVariable('FOO_VAR')
);
}
public function test_has_variable_with_injected_value(): void
{
$this->assertFalse(
Resolver::hasVariable('FOO_VAR')
);
$this->injectEnvironmentVariable('FOO_VAR', 'FOO');
$this->assertTrue(
Resolver::hasVariable('FOO_VAR')
);
}
/**
* @dataProvider rawValueProvider
*/
public function test_get_raw_value(string $varName, string $varValue): void
{
$this->setEnvironmentVariable($varName, $varValue);
$this->assertSame(
$varValue,
Resolver::getRawValue($varName)
);
}
/**
* @dataProvider rawValueProvider
*/
public function test_get_raw_value_with_injected_value(string $varName, string $varValue): void
{
$this->injectEnvironmentVariable($varName, $varValue);
$this->assertSame(
$varValue,
Resolver::getRawValue($varName)
);
}
public function test_get_raw_value_no_var(): void
{
$this->assertFalse(
Resolver::hasVariable('FOO_VAR')
);
$this->assertNull(
Resolver::getRawValue('FOO_VAR')
);
}
/**
* @dataProvider defaultValueProvider
*/
public function test_get_default_value(string $varName, $varValue): void
{
$this->assertSame(
$varValue,
Resolver::getDefault($varName)
);
}
/**
* @dataProvider defaultValueProvider
*/
public function test_get_default_value_with_empty_var(string $varName, $varValue): void
{
$this->setEnvironmentVariable($varName, '');
$this->assertSame(
$varValue,
Resolver::getDefault($varName)
);
}
/**
* @dataProvider typeProvider
*/
public function test_get_type(string $varName, string $type): void
{
$this->assertSame(
$type,
Resolver::getType($varName)
);
}
/**
* @dataProvider knownValuesProvider
*/
public function test_get_known_values(string $varName, array $varValue): void
{
$this->assertSame(
$varValue,
Resolver::getKnownValues($varName)
);
}
public function test_resolve_value(): void
{
$value = 'simple';
$variable = 'OTEL_PHP_TRACES_PROCESSOR';
$this->assertFalse(
Resolver::hasVariable($variable)
);
$this->setEnvironmentVariable($variable, $value);
$this->assertSame(
$value,
Resolver::resolveValue($variable)
);
}
public function test_resolve_value_with_injected_value(): void
{
$value = 'simple';
$variable = 'OTEL_PHP_TRACES_PROCESSOR';
$this->assertFalse(
Resolver::hasVariable($variable)
);
$this->injectEnvironmentVariable($variable, $value);
$this->assertSame(
$value,
Resolver::resolveValue($variable)
);
}
public function test_resolve_value_library_default(): void
{
$value = 'simple';
$variable = 'OTEL_PHP_TRACES_PROCESSOR';
$this->assertFalse(
Resolver::hasVariable($variable)
);
$this->assertSame(
$value,
Resolver::resolveValue($variable, $value)
);
}
public function test_resolve_value_user_default(): void
{
$value = Defaults::OTEL_PHP_TRACES_PROCESSOR;
$variable = 'OTEL_PHP_TRACES_PROCESSOR';
$this->assertFalse(
Resolver::hasVariable($variable)
);
$this->assertSame(
$value,
Resolver::resolveValue($variable)
);
}
public function rawValueProvider(): array
{
return self::RAW_VALUES;
}
public function defaultValueProvider(): array
{
return self::DEFAULT_VALUES;
}
public function typeProvider(): array
{
return self::TYPES;
}
public function knownValuesProvider(): array
{
return self::KNOWN_VALUES;
}
private function injectEnvironmentVariable(string $name, string $value): void
{
if (!in_array($name, $this->injectedEnvironmentVariables, true)) {
$this->injectedEnvironmentVariables[] = $name;
}
$_ENV[$name] = $value;
}
private function resetEnvironmentVariables(): void
{
$this->restoreEnvironmentVariables();
foreach ($this->injectedEnvironmentVariables as $variable) {
unset($_ENV[$variable]);
}
}
}