auto-instrumentation registration (#1304)
* [WIP] Add instrumentation configuration * add autoloading for auto-instrumentations using SPI * allow autoloading and non-autoloading to work * fix attribute * experimental config file * fixing invalid dependencies - deptrac was rightly complaining that API (indirectly) depended on SDK through config/sdk. For now, remove usage of Config\SDK\Configuration\Context - update deptrac config to allow some new dependencies * dont register hook manager globally or in sdk * remove unused function, psalm ignore missing extension function * possibly fixing type-hint psalm doesn't complain now, so that should be good * load config files relative to cwd * fixing hook manager enable/disable + psalm complaints * fixing 8.1 psalm error * use context to pass providers to instrumentations - make "register global" a function of Sdk, but keep the sdk builder's interface intact - invent an API instrumentation context, similar to the one in config/sdk, to pass providers to instrumentations - add an initial test of autoloading from a config file * adding tests for sdk::registerGlobal in passing, remove some dead code for handling invalid booleans - config already handles this correctly * linting * test coverage for globals * add opentelemetry extension to developer image and actions * always register instrumentations via SPI * register globals initializer for file-config sdk allow SDK created from config file to coexist with components using globals initializer * linting * remove globals init function * fix phan warning * simplify hook manager - drop storage from hook manager: can't guarantee that something else, eg swoole, won't modify storage - drop context from hook manager: we must lazy-load globals via initializers, because not all instrumentations use SPI (although that may change in future) - default hook manager to enabled * add todo to deprecate Registry in future * autoload instrumentations without config if no config provided, still try to load them. wrap registration in a try/catch/log * fixing phan ref, update doc * remove phan suppress * fix example * bump SPI to 0.2.1 * adding late-binding tracer+provider per review from Nevay, this will allow instrumentations to get things from Globals as late as possible * adding late binding logger and meter providers * more late binding test coverage * tidy * dont use CoversMethod yet not available in phpunit 10, so comment out and leave a todo * kitchen sink, remove unused var * instrumentation config as a map, per config file spec * adding general config * move general config into sdk * test config caching * move general instrumentation config to api * avoid bad version of sebastian/exporter * bump config yaml files to 0.3 * fix tests * disable hook manager during file-based init * cleanup * support multiple config files in autoloader * move hook manager enable/disable out of extension hook manager The most obvious place to do this is in a class named HookManager, which _was_ an interface for hook managers. Rename existing HookManager to HookManagerInterface, then re-purpose HookManager for globally enabling/disabling hook managers as well as determining the disabled status. * update spi config --------- Co-authored-by: Tobias Bachert <git@b-privat.de>
This commit is contained in:
parent
4ed0d870f7
commit
68b1b43cab
|
|
@ -21,7 +21,7 @@ jobs:
|
|||
experimental: true
|
||||
composer_args: "--ignore-platform-reqs"
|
||||
env:
|
||||
extensions: ast, grpc, protobuf
|
||||
extensions: ast, grpc, opentelemetry, protobuf
|
||||
|
||||
steps:
|
||||
- name: Set cache key
|
||||
|
|
|
|||
|
|
@ -373,6 +373,7 @@ return [
|
|||
'vendor/composer',
|
||||
'vendor/grpc/grpc/src/lib',
|
||||
'vendor/guzzlehttp',
|
||||
'vendor/tbachert/spi/src',
|
||||
'vendor/psr',
|
||||
'vendor/php-http',
|
||||
'vendor/phpunit/phpunit/src',
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
"symfony/config": "^5.4 || ^6.4 || ^7.0",
|
||||
"symfony/polyfill-mbstring": "^1.23",
|
||||
"symfony/polyfill-php82": "^1.26",
|
||||
"tbachert/spi": "^0.2"
|
||||
"tbachert/spi": ">= 0.2.1"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true,
|
||||
|
|
@ -101,6 +101,7 @@
|
|||
"phpstan/phpstan-mockery": "^1.1",
|
||||
"phpstan/phpstan-phpunit": "^1.3",
|
||||
"phpunit/phpunit": "^10 || ^11",
|
||||
"sebastian/exporter": "<= 6.0.1 || >= 6.1.3",
|
||||
"symfony/http-client": "^5.2",
|
||||
"symfony/var-exporter": "^5.4 || ^6.4 || ^7.0",
|
||||
"symfony/yaml": "^5.4 || ^6.4 || ^7.0"
|
||||
|
|
@ -148,7 +149,18 @@
|
|||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterConsole",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterOtlp",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordProcessorBatch",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordProcessorSimple"
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordProcessorSimple",
|
||||
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Instrumentation\\General\\HttpConfigProvider",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Instrumentation\\General\\PeerConfigProvider",
|
||||
|
||||
"OpenTelemetry\\Example\\ExampleConfigProvider"
|
||||
],
|
||||
"OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [
|
||||
"OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\ExtensionHookManager"
|
||||
],
|
||||
"OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\Instrumentation": [
|
||||
"OpenTelemetry\\Example\\ExampleInstrumentation"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
29
deptrac.yaml
29
deptrac.yaml
|
|
@ -25,6 +25,10 @@ deptrac:
|
|||
collectors:
|
||||
- type: directory
|
||||
value: src/SDK/.*
|
||||
- name: ConfigSDK
|
||||
collectors:
|
||||
- type: directory
|
||||
value: src/Config/SDK/.*
|
||||
- name: Context
|
||||
collectors:
|
||||
- type: directory
|
||||
|
|
@ -85,6 +89,18 @@ deptrac:
|
|||
value: ^GuzzleHttp\\*
|
||||
- type: className
|
||||
value: ^Buzz\\*
|
||||
- name: SPI
|
||||
collectors:
|
||||
- type: className
|
||||
value: ^Nevay\\SPI\\*
|
||||
- name: SymfonyConfig
|
||||
collectors:
|
||||
- type: className
|
||||
value: ^Symfony\\Component\\Config\\*
|
||||
- type: className
|
||||
value: ^Symfony\\Component\\Yaml\\*
|
||||
- type: className
|
||||
value: ^Symfony\\Component\\VarExporter\\*
|
||||
- name: RamseyUuid
|
||||
collectors:
|
||||
- type: className
|
||||
|
|
@ -94,16 +110,29 @@ deptrac:
|
|||
Context:
|
||||
- FFI
|
||||
SemConv: ~
|
||||
ConfigSDK:
|
||||
- SymfonyConfig
|
||||
- API
|
||||
- SDK
|
||||
- SPI
|
||||
- PsrLog
|
||||
- Composer
|
||||
- Context
|
||||
- Contrib
|
||||
- Extension
|
||||
API:
|
||||
- Context
|
||||
- PsrLog
|
||||
- SPI
|
||||
SDK:
|
||||
- +API
|
||||
- ConfigSDK
|
||||
- SemConv
|
||||
- PsrHttp
|
||||
- HttpPlug
|
||||
- Composer
|
||||
- HttpClients
|
||||
- SPI
|
||||
- RamseyUuid
|
||||
Contrib:
|
||||
- +SDK
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ RUN chmod +x /usr/local/bin/install-php-extensions \
|
|||
grpc \
|
||||
intl\
|
||||
opcache \
|
||||
opentelemetry \
|
||||
pcntl \
|
||||
protobuf \
|
||||
sockets \
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace _;
|
||||
|
||||
use Nevay\SPI\ServiceLoader;
|
||||
use OpenTelemetry\API\Globals;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\ExtensionHookManager;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Instrumentation;
|
||||
use OpenTelemetry\API\Logs\NoopLoggerProvider;
|
||||
use OpenTelemetry\API\Metrics\Noop\NoopMeterProvider;
|
||||
use OpenTelemetry\Config\SDK\Configuration;
|
||||
use OpenTelemetry\Config\SDK\Configuration\Context;
|
||||
use OpenTelemetry\Example\Example;
|
||||
use const PHP_EOL;
|
||||
|
||||
/**
|
||||
* This example uses SPI (see root composer.json extra.spi) to configure an example auto-instrumentation from a YAML file
|
||||
*/
|
||||
// EXAMPLE_INSTRUMENTATION_SPAN_NAME=test1234 php examples/instrumentation/configure_instrumentation.php
|
||||
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
Configuration::parseFile(__DIR__ . '/otel-sdk.yaml')->create(new Context())->setAutoShutdown(true)->buildAndRegisterGlobal();
|
||||
$configuration = \OpenTelemetry\Config\SDK\Instrumentation::parseFile(__DIR__ . '/otel-sdk.yaml')->create();
|
||||
$hookManager = new ExtensionHookManager();
|
||||
$context = new \OpenTelemetry\API\Instrumentation\AutoInstrumentation\Context(Globals::tracerProvider(), new NoopMeterProvider(), new NoopLoggerProvider());
|
||||
|
||||
foreach (ServiceLoader::load(Instrumentation::class) as $instrumentation) {
|
||||
$instrumentation->register($hookManager, $configuration, $context);
|
||||
}
|
||||
|
||||
echo (new Example())->test(), PHP_EOL;
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace _;
|
||||
|
||||
use OpenTelemetry\Example\Example;
|
||||
use const PHP_EOL;
|
||||
|
||||
/**
|
||||
* This example uses SPI (see root composer.json extra.spi) to configure an example auto-instrumentation from a YAML file.
|
||||
* The YAML file paths are relative to the current working directory.
|
||||
*/
|
||||
// EXAMPLE_INSTRUMENTATION_SPAN_NAME=test1234 php examples/instrumentation/configure_instrumentation_global.php
|
||||
putenv('OTEL_PHP_AUTOLOAD_ENABLED=true');
|
||||
putenv('OTEL_EXPERIMENTAL_CONFIG_FILE=examples/instrumentation/otel-sdk.yaml');
|
||||
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
echo (new Example())->test(), PHP_EOL;
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
file_format: '0.3'
|
||||
|
||||
tracer_provider:
|
||||
processors:
|
||||
- simple:
|
||||
exporter:
|
||||
console: {}
|
||||
|
||||
instrumentation:
|
||||
php:
|
||||
example_instrumentation:
|
||||
span_name: ${EXAMPLE_INSTRUMENTATION_SPAN_NAME:-example span}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
file_format: '0.1'
|
||||
file_format: '0.3'
|
||||
|
||||
resource:
|
||||
attributes:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
file_format: '0.1'
|
||||
file_format: '0.3'
|
||||
|
||||
disabled: ${OTEL_SDK_DISABLED}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Example;
|
||||
|
||||
final class Example
|
||||
{
|
||||
|
||||
public function test(): int
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Example;
|
||||
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\InstrumentationConfiguration;
|
||||
|
||||
final class ExampleConfig implements InstrumentationConfiguration
|
||||
{
|
||||
|
||||
public function __construct(
|
||||
public readonly string $spanName,
|
||||
public readonly bool $enabled = true,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Example;
|
||||
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\InstrumentationConfiguration;
|
||||
use OpenTelemetry\Config\SDK\Configuration\ComponentProvider;
|
||||
use OpenTelemetry\Config\SDK\Configuration\ComponentProviderRegistry;
|
||||
use OpenTelemetry\Config\SDK\Configuration\Context;
|
||||
use OpenTelemetry\Config\SDK\Configuration\Validation;
|
||||
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
|
||||
|
||||
/**
|
||||
* @implements ComponentProvider<InstrumentationConfiguration>
|
||||
*/
|
||||
final class ExampleConfigProvider implements ComponentProvider
|
||||
{
|
||||
|
||||
/**
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
* @param array{
|
||||
* span_name: string,
|
||||
* enabled: bool,
|
||||
* } $properties
|
||||
*/
|
||||
public function createPlugin(array $properties, Context $context): InstrumentationConfiguration
|
||||
{
|
||||
return new ExampleConfig(
|
||||
spanName: $properties['span_name'],
|
||||
enabled: $properties['enabled'],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-suppress UndefinedInterfaceMethod,PossiblyNullReference
|
||||
*/
|
||||
public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition
|
||||
{
|
||||
$root = new ArrayNodeDefinition('example_instrumentation');
|
||||
$root
|
||||
->children()
|
||||
->scalarNode('span_name')->isRequired()->validate()->always(Validation::ensureString())->end()->end()
|
||||
->end()
|
||||
->canBeDisabled()
|
||||
;
|
||||
|
||||
return $root;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Example;
|
||||
|
||||
use Exception;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\ConfigurationRegistry;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Context as InstrumentationContext;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\HookManagerInterface;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Instrumentation;
|
||||
use OpenTelemetry\API\Trace\Span;
|
||||
use OpenTelemetry\Context\Context;
|
||||
|
||||
final class ExampleInstrumentation implements Instrumentation
|
||||
{
|
||||
|
||||
public function register(HookManagerInterface $hookManager, ConfigurationRegistry $configuration, InstrumentationContext $context): void
|
||||
{
|
||||
$config = $configuration->get(ExampleConfig::class) ?? throw new Exception('example instrumentation must be configured');
|
||||
if (!$config->enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tracer = $context->tracerProvider->getTracer('example-instrumentation');
|
||||
|
||||
$hookManager->hook(
|
||||
Example::class,
|
||||
'test',
|
||||
static function () use ($tracer, $config): void {
|
||||
$context = Context::getCurrent();
|
||||
|
||||
$span = $tracer
|
||||
->spanBuilder($config->spanName)
|
||||
->setParent($context)
|
||||
->startSpan();
|
||||
|
||||
Context::storage()->attach($span->storeInContext($context));
|
||||
},
|
||||
static function (): void {
|
||||
if (!$scope = Context::storage()->scope()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope->detach();
|
||||
|
||||
$span = Span::fromContext($scope->context());
|
||||
$span->end();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,11 @@
|
|||
<referencedClass name="GMP"/>
|
||||
</errorLevel>
|
||||
</UndefinedClass>
|
||||
<UndefinedFunction>
|
||||
<errorLevel type="suppress">
|
||||
<referencedFunction name="OpenTelemetry\Instrumentation\hook"/>
|
||||
</errorLevel>
|
||||
</UndefinedFunction>
|
||||
<ArgumentTypeCoercion>
|
||||
<errorLevel type="suppress">
|
||||
<directory name="./examples"/>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ namespace OpenTelemetry\API;
|
|||
|
||||
use function assert;
|
||||
use Closure;
|
||||
use const E_USER_WARNING;
|
||||
use OpenTelemetry\API\Behavior\LogsMessagesTrait;
|
||||
use OpenTelemetry\API\Instrumentation\Configurator;
|
||||
use OpenTelemetry\API\Instrumentation\ContextKeys;
|
||||
use OpenTelemetry\API\Logs\EventLoggerProviderInterface;
|
||||
|
|
@ -17,13 +17,14 @@ use OpenTelemetry\Context\Context;
|
|||
use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface;
|
||||
use function sprintf;
|
||||
use Throwable;
|
||||
use function trigger_error;
|
||||
|
||||
/**
|
||||
* Provides access to the globally configured instrumentation instances.
|
||||
*/
|
||||
final class Globals
|
||||
{
|
||||
use LogsMessagesTrait;
|
||||
|
||||
/** @var Closure[] */
|
||||
private static array $initializers = [];
|
||||
private static ?self $globals = null;
|
||||
|
|
@ -67,6 +68,7 @@ final class Globals
|
|||
*
|
||||
* @internal
|
||||
* @psalm-internal OpenTelemetry
|
||||
* @todo In a future (breaking) change, we can remove `Registry` and globals initializers, in favor of SPI.
|
||||
*/
|
||||
public static function registerInitializer(Closure $initializer): void
|
||||
{
|
||||
|
|
@ -90,7 +92,7 @@ final class Globals
|
|||
try {
|
||||
$configurator = $initializer($configurator);
|
||||
} catch (Throwable $e) {
|
||||
trigger_error(sprintf("Error during opentelemetry initialization: %s\n%s", $e->getMessage(), $e->getTraceAsString()), E_USER_WARNING);
|
||||
self::logWarning(sprintf("Error during opentelemetry initialization: %s\n%s", $e->getMessage(), $e->getTraceAsString()));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\API\Instrumentation\AutoInstrumentation;
|
||||
|
||||
final class ConfigurationRegistry
|
||||
{
|
||||
|
||||
private array $configurations = [];
|
||||
|
||||
public function add(InstrumentationConfiguration $configuration): self
|
||||
{
|
||||
$this->configurations[$configuration::class] = $configuration;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template C of InstrumentationConfiguration
|
||||
* @param class-string<C> $id
|
||||
* @return C|null
|
||||
*/
|
||||
public function get(string $id): ?InstrumentationConfiguration
|
||||
{
|
||||
return $this->configurations[$id] ?? null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\API\Instrumentation\AutoInstrumentation;
|
||||
|
||||
use OpenTelemetry\API\Logs\LoggerProviderInterface;
|
||||
use OpenTelemetry\API\Logs\NoopLoggerProvider;
|
||||
use OpenTelemetry\API\Metrics\MeterProviderInterface;
|
||||
use OpenTelemetry\API\Metrics\Noop\NoopMeterProvider;
|
||||
use OpenTelemetry\API\Trace\NoopTracerProvider;
|
||||
use OpenTelemetry\API\Trace\TracerProviderInterface;
|
||||
|
||||
/**
|
||||
* Context used for component creation.
|
||||
*/
|
||||
final class Context
|
||||
{
|
||||
public function __construct(
|
||||
public readonly TracerProviderInterface $tracerProvider = new NoopTracerProvider(),
|
||||
public readonly MeterProviderInterface $meterProvider = new NoopMeterProvider(),
|
||||
public readonly LoggerProviderInterface $loggerProvider = new NoopLoggerProvider(),
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\API\Instrumentation\AutoInstrumentation;
|
||||
|
||||
use function assert;
|
||||
use Closure;
|
||||
use function extension_loaded;
|
||||
use Nevay\SPI\ServiceProviderDependency\ExtensionDependency;
|
||||
use ReflectionFunction;
|
||||
|
||||
/** @phan-file-suppress PhanUndeclaredClassAttribute */
|
||||
|
||||
#[ExtensionDependency('opentelemetry', '^1.0')]
|
||||
final class ExtensionHookManager implements HookManagerInterface
|
||||
{
|
||||
/**
|
||||
* @phan-suppress PhanUndeclaredFunction
|
||||
*/
|
||||
public function hook(?string $class, string $function, ?Closure $preHook = null, ?Closure $postHook = null): void
|
||||
{
|
||||
assert(extension_loaded('opentelemetry'));
|
||||
|
||||
/** @noinspection PhpFullyQualifiedNameUsageInspection */
|
||||
\OpenTelemetry\Instrumentation\hook($class, $function, $this->bindHookScope($preHook), $this->bindHookScope($postHook));
|
||||
}
|
||||
|
||||
private function bindHookScope(?Closure $closure): ?Closure
|
||||
{
|
||||
if (!$closure) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$reflection = new ReflectionFunction($closure);
|
||||
|
||||
// TODO Add an option flag to ext-opentelemetry `hook` that configures whether return values should be used?
|
||||
if (!$reflection->getReturnType() || (string) $reflection->getReturnType() === 'void') {
|
||||
return static function (mixed ...$args) use ($closure): void {
|
||||
if (HookManager::disabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$closure(...$args);
|
||||
};
|
||||
}
|
||||
|
||||
return static function (mixed ...$args) use ($closure): mixed {
|
||||
if (HookManager::disabled()) {
|
||||
return $args[2];
|
||||
}
|
||||
|
||||
return $closure(...$args);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\API\Instrumentation\AutoInstrumentation;
|
||||
|
||||
interface GeneralInstrumentationConfiguration extends InstrumentationConfiguration
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\API\Instrumentation\AutoInstrumentation;
|
||||
|
||||
use OpenTelemetry\Context\Context;
|
||||
use OpenTelemetry\Context\ContextInterface;
|
||||
use OpenTelemetry\Context\ContextKeyInterface;
|
||||
|
||||
class HookManager
|
||||
{
|
||||
public static function enable(?ContextInterface $context = null): ContextInterface
|
||||
{
|
||||
$context ??= Context::getCurrent();
|
||||
|
||||
return $context->with(self::contextKey(), true);
|
||||
}
|
||||
|
||||
public static function disable(?ContextInterface $context = null): ContextInterface
|
||||
{
|
||||
$context ??= Context::getCurrent();
|
||||
|
||||
return $context->with(self::contextKey(), false);
|
||||
}
|
||||
|
||||
public static function disabled(?ContextInterface $context = null): bool
|
||||
{
|
||||
$context ??= Context::getCurrent();
|
||||
|
||||
return $context->get(self::contextKey()) === false;
|
||||
}
|
||||
|
||||
private static function contextKey(): ContextKeyInterface
|
||||
{
|
||||
static $contextKey;
|
||||
|
||||
return $contextKey ??= Context::createKey(self::class);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\API\Instrumentation\AutoInstrumentation;
|
||||
|
||||
use Closure;
|
||||
use Throwable;
|
||||
|
||||
interface HookManagerInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Closure(object|string|null,array,string,string,string|null,int|null):void|null $preHook
|
||||
* @param Closure(object|string|null,array,mixed,Throwable|null,string,string,string|null,int|null):void|null $postHook
|
||||
*/
|
||||
public function hook(?string $class, string $function, ?Closure $preHook = null, ?Closure $postHook = null): void;
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\API\Instrumentation\AutoInstrumentation;
|
||||
|
||||
interface Instrumentation
|
||||
{
|
||||
public function register(HookManagerInterface $hookManager, ConfigurationRegistry $configuration, Context $context): void;
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\API\Instrumentation\AutoInstrumentation;
|
||||
|
||||
interface InstrumentationConfiguration
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\API\Instrumentation\AutoInstrumentation;
|
||||
|
||||
use Closure;
|
||||
|
||||
final class NoopHookManager implements HookManagerInterface
|
||||
{
|
||||
public function hook(?string $class, string $function, ?Closure $preHook = null, ?Closure $postHook = null): void
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\API\Instrumentation\Configuration\General;
|
||||
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\GeneralInstrumentationConfiguration;
|
||||
|
||||
class HttpConfig implements GeneralInstrumentationConfiguration
|
||||
{
|
||||
public function __construct(public readonly array $config)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\API\Instrumentation\Configuration\General;
|
||||
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\GeneralInstrumentationConfiguration;
|
||||
|
||||
class PeerConfig implements GeneralInstrumentationConfiguration
|
||||
{
|
||||
public function __construct(public readonly array $config)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\API\Logs;
|
||||
|
||||
use Closure;
|
||||
|
||||
class LateBindingLogger implements LoggerInterface
|
||||
{
|
||||
private ?LoggerInterface $logger = null;
|
||||
|
||||
/** @param Closure(): LoggerInterface $factory */
|
||||
public function __construct(
|
||||
private readonly Closure $factory,
|
||||
) {
|
||||
}
|
||||
|
||||
public function emit(LogRecord $logRecord): void
|
||||
{
|
||||
($this->logger ??= ($this->factory)())->emit($logRecord);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\API\Logs;
|
||||
|
||||
use Closure;
|
||||
|
||||
class LateBindingLoggerProvider implements LoggerProviderInterface
|
||||
{
|
||||
private ?LoggerProviderInterface $loggerProvider = null;
|
||||
|
||||
/** @param Closure(): LoggerProviderInterface $factory */
|
||||
public function __construct(
|
||||
private readonly Closure $factory,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getLogger(string $name, ?string $version = null, ?string $schemaUrl = null, iterable $attributes = []): LoggerInterface
|
||||
{
|
||||
return $this->loggerProvider?->getLogger($name, $version, $schemaUrl, $attributes)
|
||||
?? new LateBindingLogger(fn (): LoggerInterface => ($this->loggerProvider ??= ($this->factory)())->getLogger($name, $version, $schemaUrl, $attributes));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\API\Metrics;
|
||||
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* @psalm-suppress InvalidArgument
|
||||
*/
|
||||
class LateBindingMeter implements MeterInterface
|
||||
{
|
||||
private ?MeterInterface $meter = null;
|
||||
|
||||
/** @param Closure(): MeterInterface $factory */
|
||||
public function __construct(
|
||||
private readonly Closure $factory,
|
||||
) {
|
||||
}
|
||||
|
||||
public function batchObserve(callable $callback, AsynchronousInstrument $instrument, AsynchronousInstrument ...$instruments): ObservableCallbackInterface
|
||||
{
|
||||
return ($this->meter ??= ($this->factory)())->batchObserve($callback, $instrument, ...$instruments);
|
||||
}
|
||||
|
||||
public function createCounter(string $name, ?string $unit = null, ?string $description = null, array $advisory = []): CounterInterface
|
||||
{
|
||||
return ($this->meter ??= ($this->factory)())->createCounter($name, $unit, $description, $advisory);
|
||||
}
|
||||
|
||||
public function createObservableCounter(string $name, ?string $unit = null, ?string $description = null, callable|array $advisory = [], callable ...$callbacks): ObservableCounterInterface
|
||||
{
|
||||
return ($this->meter ??= ($this->factory)())->createObservableCounter($name, $unit, $description, $advisory, $callbacks);
|
||||
}
|
||||
|
||||
public function createHistogram(string $name, ?string $unit = null, ?string $description = null, array $advisory = []): HistogramInterface
|
||||
{
|
||||
return ($this->meter ??= ($this->factory)())->createHistogram($name, $unit, $description, $advisory);
|
||||
}
|
||||
|
||||
public function createGauge(string $name, ?string $unit = null, ?string $description = null, array $advisory = []): GaugeInterface
|
||||
{
|
||||
return ($this->meter ??= ($this->factory)())->createGauge($name, $unit, $description, $advisory);
|
||||
}
|
||||
|
||||
public function createObservableGauge(string $name, ?string $unit = null, ?string $description = null, callable|array $advisory = [], callable ...$callbacks): ObservableGaugeInterface
|
||||
{
|
||||
return ($this->meter ??= ($this->factory)())->createObservableGauge($name, $unit, $description, $advisory, $callbacks);
|
||||
}
|
||||
|
||||
public function createUpDownCounter(string $name, ?string $unit = null, ?string $description = null, array $advisory = []): UpDownCounterInterface
|
||||
{
|
||||
return ($this->meter ??= ($this->factory)())->createUpDownCounter($name, $unit, $description, $advisory);
|
||||
}
|
||||
|
||||
public function createObservableUpDownCounter(string $name, ?string $unit = null, ?string $description = null, callable|array $advisory = [], callable ...$callbacks): ObservableUpDownCounterInterface
|
||||
{
|
||||
return ($this->meter ??= ($this->factory)())->createObservableUpDownCounter($name, $unit, $description, $advisory, $callbacks);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\API\Metrics;
|
||||
|
||||
use Closure;
|
||||
|
||||
class LateBindingMeterProvider implements MeterProviderInterface
|
||||
{
|
||||
private ?MeterProviderInterface $meterProvider = null;
|
||||
|
||||
/** @param Closure(): MeterProviderInterface $factory */
|
||||
public function __construct(
|
||||
private readonly Closure $factory,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getMeter(string $name, ?string $version = null, ?string $schemaUrl = null, iterable $attributes = []): MeterInterface
|
||||
{
|
||||
return $this->meterProvider?->getMeter($name, $version, $schemaUrl, $attributes)
|
||||
?? new LateBindingMeter(fn (): MeterInterface => ($this->meterProvider ??= ($this->factory)())->getMeter($name, $version, $schemaUrl, $attributes));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\API\Trace;
|
||||
|
||||
use Closure;
|
||||
|
||||
class LateBindingTracer implements TracerInterface
|
||||
{
|
||||
private ?TracerInterface $tracer = null;
|
||||
|
||||
/** @param Closure(): TracerInterface $factory */
|
||||
public function __construct(
|
||||
private readonly Closure $factory,
|
||||
) {
|
||||
}
|
||||
|
||||
public function spanBuilder(string $spanName): SpanBuilderInterface
|
||||
{
|
||||
return ($this->tracer ??= ($this->factory)())->spanBuilder($spanName);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\API\Trace;
|
||||
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* Late binding providers are designed to be used by Instrumentation, while we do not have control over when all components (propagators, etc)
|
||||
* which are registered through composer.autoload.files are actually loaded. It means that tracers etc are not fetched
|
||||
* from Globals until the last possible instant (ie, when they try to create a span, get an instrument, etc).
|
||||
* In the future, when everything uses SPI, this will be removed.
|
||||
*/
|
||||
class LateBindingTracerProvider implements TracerProviderInterface
|
||||
{
|
||||
private ?TracerProviderInterface $tracerProvider = null;
|
||||
|
||||
/** @param Closure(): TracerProviderInterface $factory */
|
||||
public function __construct(
|
||||
private readonly Closure $factory,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getTracer(string $name, ?string $version = null, ?string $schemaUrl = null, iterable $attributes = []): TracerInterface
|
||||
{
|
||||
return $this->tracerProvider?->getTracer($name, $version, $schemaUrl, $attributes)
|
||||
?? new LateBindingTracer(fn (): TracerInterface => ($this->tracerProvider ??= ($this->factory)())->getTracer($name, $version, $schemaUrl, $attributes));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Config\SDK\ComponentProvider\Instrumentation\General;
|
||||
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\GeneralInstrumentationConfiguration;
|
||||
use OpenTelemetry\API\Instrumentation\Configuration\General\HttpConfig;
|
||||
use OpenTelemetry\Config\SDK\Configuration\ComponentProvider;
|
||||
use OpenTelemetry\Config\SDK\Configuration\ComponentProviderRegistry;
|
||||
use OpenTelemetry\Config\SDK\Configuration\Context;
|
||||
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
|
||||
|
||||
/**
|
||||
* @implements ComponentProvider<GeneralInstrumentationConfiguration>
|
||||
*/
|
||||
class HttpConfigProvider implements ComponentProvider
|
||||
{
|
||||
|
||||
public function createPlugin(array $properties, Context $context): GeneralInstrumentationConfiguration
|
||||
{
|
||||
return new HttpConfig($properties);
|
||||
}
|
||||
|
||||
public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition
|
||||
{
|
||||
$node = new ArrayNodeDefinition('http');
|
||||
$node
|
||||
->children()
|
||||
->append($this->capturedHeaders('client'))
|
||||
->append($this->capturedHeaders('server'))
|
||||
->end()
|
||||
;
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function capturedHeaders(string $name): ArrayNodeDefinition
|
||||
{
|
||||
$node = new ArrayNodeDefinition($name);
|
||||
$node
|
||||
->children()
|
||||
->arrayNode('request_captured_headers')
|
||||
->scalarPrototype()->end()
|
||||
->end()
|
||||
->arrayNode('response_captured_headers')
|
||||
->scalarPrototype()->end()
|
||||
->end()
|
||||
->end()
|
||||
;
|
||||
|
||||
return $node;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Config\SDK\ComponentProvider\Instrumentation\General;
|
||||
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\GeneralInstrumentationConfiguration;
|
||||
use OpenTelemetry\API\Instrumentation\Configuration\General\PeerConfig;
|
||||
use OpenTelemetry\Config\SDK\Configuration\ComponentProvider;
|
||||
use OpenTelemetry\Config\SDK\Configuration\ComponentProviderRegistry;
|
||||
use OpenTelemetry\Config\SDK\Configuration\Context;
|
||||
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
|
||||
|
||||
/**
|
||||
* @implements ComponentProvider<GeneralInstrumentationConfiguration>
|
||||
*/
|
||||
class PeerConfigProvider implements ComponentProvider
|
||||
{
|
||||
public function createPlugin(array $properties, Context $context): GeneralInstrumentationConfiguration
|
||||
{
|
||||
return new PeerConfig($properties);
|
||||
}
|
||||
|
||||
public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition
|
||||
{
|
||||
$node = new ArrayNodeDefinition('peer');
|
||||
$node
|
||||
->children()
|
||||
->arrayNode('service_mapping')
|
||||
->arrayPrototype()
|
||||
->children()
|
||||
->scalarNode('peer')->end()
|
||||
->scalarNode('service')->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
;
|
||||
|
||||
return $node;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Config\SDK\ComponentProvider;
|
||||
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\ConfigurationRegistry;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\GeneralInstrumentationConfiguration;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\InstrumentationConfiguration;
|
||||
use OpenTelemetry\Config\SDK\Configuration\ComponentPlugin;
|
||||
use OpenTelemetry\Config\SDK\Configuration\ComponentProvider;
|
||||
use OpenTelemetry\Config\SDK\Configuration\ComponentProviderRegistry;
|
||||
use OpenTelemetry\Config\SDK\Configuration\Context;
|
||||
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @todo In a future release, when all instrumentations use SPI (and not autoload.files), this could be moved into {@see OpenTelemetrySdk}>
|
||||
* @implements ComponentProvider<ConfigurationRegistry>
|
||||
*/
|
||||
class InstrumentationConfigurationRegistry implements ComponentProvider
|
||||
{
|
||||
/**
|
||||
* @param array{
|
||||
* instrumentation: array{
|
||||
* php: list<ComponentPlugin<InstrumentationConfiguration>>,
|
||||
* general: list<ComponentPlugin<InstrumentationConfiguration>>
|
||||
* }
|
||||
* } $properties
|
||||
*/
|
||||
public function createPlugin(array $properties, Context $context): ConfigurationRegistry
|
||||
{
|
||||
$configurationRegistry = new ConfigurationRegistry();
|
||||
/** @phpstan-ignore-next-line */
|
||||
foreach ($properties['instrumentation']['php'] ?? [] as $configuration) {
|
||||
$configurationRegistry->add($configuration->create($context));
|
||||
}
|
||||
/** @phpstan-ignore-next-line */
|
||||
foreach ($properties['instrumentation']['general'] ?? [] as $configuration) {
|
||||
$configurationRegistry->add($configuration->create($context));
|
||||
}
|
||||
|
||||
return $configurationRegistry;
|
||||
}
|
||||
|
||||
public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition
|
||||
{
|
||||
$root = new ArrayNodeDefinition('open_telemetry');
|
||||
$root
|
||||
->ignoreExtraKeys()
|
||||
->children()
|
||||
->arrayNode('instrumentation')
|
||||
->ignoreExtraKeys()
|
||||
->children()
|
||||
->append($registry->componentList('php', InstrumentationConfiguration::class))
|
||||
->append($registry->componentList('general', GeneralInstrumentationConfiguration::class))
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
;
|
||||
|
||||
return $root;
|
||||
}
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ final class OpenTelemetrySdk implements ComponentProvider
|
|||
|
||||
/**
|
||||
* @param array{
|
||||
* file_format: '0.1',
|
||||
* file_format: '0.3',
|
||||
* disabled: bool,
|
||||
* resource: array{
|
||||
* attributes: array,
|
||||
|
|
@ -263,7 +263,7 @@ final class OpenTelemetrySdk implements ComponentProvider
|
|||
->isRequired()
|
||||
->example('0.1')
|
||||
->validate()->always(Validation::ensureString())->end()
|
||||
->validate()->ifNotInArray(['0.1'])->thenInvalid('unsupported version')->end()
|
||||
->validate()->ifNotInArray(['0.3'])->thenInvalid('unsupported version')->end()
|
||||
->end()
|
||||
->booleanNode('disabled')->defaultFalse()->end()
|
||||
->append($this->getResourceConfig())
|
||||
|
|
@ -323,7 +323,7 @@ final class OpenTelemetrySdk implements ComponentProvider
|
|||
->end()
|
||||
->end()
|
||||
->append($registry->component('sampler', SamplerInterface::class))
|
||||
->append($registry->componentList('processors', SpanProcessorInterface::class))
|
||||
->append($registry->componentArrayList('processors', SpanProcessorInterface::class))
|
||||
->end()
|
||||
;
|
||||
|
||||
|
|
@ -374,7 +374,7 @@ final class OpenTelemetrySdk implements ComponentProvider
|
|||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->append($registry->componentList('readers', MetricReaderInterface::class))
|
||||
->append($registry->componentArrayList('readers', MetricReaderInterface::class))
|
||||
->end()
|
||||
;
|
||||
|
||||
|
|
@ -394,7 +394,7 @@ final class OpenTelemetrySdk implements ComponentProvider
|
|||
->integerNode('attribute_count_limit')->min(0)->defaultNull()->end()
|
||||
->end()
|
||||
->end()
|
||||
->append($registry->componentList('processors', LogRecordProcessorInterface::class))
|
||||
->append($registry->componentArrayList('processors', LogRecordProcessorInterface::class))
|
||||
->end()
|
||||
;
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ interface ComponentProvider
|
|||
*
|
||||
* @see ComponentProviderRegistry::component()
|
||||
* @see ComponentProviderRegistry::componentList()
|
||||
* @see ComponentProviderRegistry::componentArrayList()
|
||||
* @see ComponentProviderRegistry::componentNames()
|
||||
*/
|
||||
public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition;
|
||||
|
|
|
|||
|
|
@ -37,6 +37,26 @@ interface ComponentProviderRegistry
|
|||
*
|
||||
* ```
|
||||
* $name:
|
||||
* provider1:
|
||||
* property: value
|
||||
* anotherProperty: value
|
||||
* provider2:
|
||||
* property: value
|
||||
* anotherProperty: value
|
||||
* ```
|
||||
*
|
||||
* @param string $name name of configuration node
|
||||
* @param string $type type of the component plugin
|
||||
*/
|
||||
public function componentList(string $name, string $type): ArrayNodeDefinition;
|
||||
|
||||
/**
|
||||
* Creates a node to specify a list of component plugins represented as an array.
|
||||
*
|
||||
* `$name: list<ComponentPlugin<$type>>`
|
||||
*
|
||||
* ```
|
||||
* $name:
|
||||
* - provider1:
|
||||
* property: value
|
||||
* anotherProperty: value
|
||||
|
|
@ -48,8 +68,7 @@ interface ComponentProviderRegistry
|
|||
* @param string $name name of configuration node
|
||||
* @param string $type type of the component plugin
|
||||
*/
|
||||
public function componentList(string $name, string $type): ArrayNodeDefinition;
|
||||
|
||||
public function componentArrayList(string $name, string $type): ArrayNodeDefinition;
|
||||
/**
|
||||
* Creates a node to specify a list of component plugin names.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ namespace OpenTelemetry\Config\SDK\Configuration;
|
|||
|
||||
use function class_exists;
|
||||
use Exception;
|
||||
use function getcwd;
|
||||
use function is_file;
|
||||
use OpenTelemetry\Config\SDK\Configuration\Environment\EnvReader;
|
||||
use OpenTelemetry\Config\SDK\Configuration\Environment\EnvResourceChecker;
|
||||
|
|
@ -98,7 +99,7 @@ final class ConfigurationFactory
|
|||
}
|
||||
|
||||
$loader = new ConfigurationLoader($resources);
|
||||
$locator = new FileLocator();
|
||||
$locator = new FileLocator(getcwd());
|
||||
$fileLoader = new DelegatingLoader(new LoaderResolver([
|
||||
new YamlSymfonyFileLoader($loader, $locator),
|
||||
new YamlExtensionFileLoader($loader, $locator),
|
||||
|
|
@ -115,7 +116,7 @@ final class ConfigurationFactory
|
|||
class_exists(VarExporter::class)
|
||||
? sprintf('<?php return %s;', VarExporter::export($configuration))
|
||||
: sprintf('<?php return unserialize(%s);', var_export(serialize($configuration), true)),
|
||||
$resources->toArray() //@todo $resources possible null
|
||||
$resources->toArray()
|
||||
);
|
||||
|
||||
return $configuration;
|
||||
|
|
|
|||
|
|
@ -69,6 +69,14 @@ final class ComponentProviderRegistry implements \OpenTelemetry\Config\SDK\Confi
|
|||
}
|
||||
|
||||
public function componentList(string $name, string $type): ArrayNodeDefinition
|
||||
{
|
||||
$node = new ArrayNodeDefinition($name);
|
||||
$this->applyToArrayNode($node, $type, true);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function componentArrayList(string $name, string $type): ArrayNodeDefinition
|
||||
{
|
||||
$node = new ArrayNodeDefinition($name);
|
||||
$this->applyToArrayNode($node->arrayPrototype(), $type);
|
||||
|
|
@ -107,7 +115,7 @@ final class ComponentProviderRegistry implements \OpenTelemetry\Config\SDK\Confi
|
|||
return $node;
|
||||
}
|
||||
|
||||
private function applyToArrayNode(ArrayNodeDefinition $node, string $type): void
|
||||
private function applyToArrayNode(ArrayNodeDefinition $node, string $type, bool $forceArray = false): void
|
||||
{
|
||||
$node->info(sprintf('Component "%s"', $type));
|
||||
$node->performNoDeepMerging();
|
||||
|
|
@ -122,21 +130,35 @@ final class ComponentProviderRegistry implements \OpenTelemetry\Config\SDK\Confi
|
|||
}
|
||||
}
|
||||
|
||||
$node->validate()->always(function (array $value) use ($type): ComponentPlugin {
|
||||
if (count($value) !== 1) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Component "%s" must have exactly one element defined, got %s',
|
||||
$type,
|
||||
implode(', ', array_map(json_encode(...), array_keys($value)) ?: ['none'])
|
||||
));
|
||||
}
|
||||
if ($forceArray) {
|
||||
// if the config was a map rather than an array, force it back to an array
|
||||
$node->validate()->always(function (array $value) use ($type): array {
|
||||
$validated = [];
|
||||
foreach ($value as $name => $v) {
|
||||
$provider = $this->providers[$type][$name];
|
||||
$this->resources?->addClassResource($provider);
|
||||
$validated[] = new ComponentPlugin($v, $this->providers[$type][$name]);
|
||||
}
|
||||
|
||||
$name = array_key_first($value);
|
||||
$provider = $this->providers[$type][$name];
|
||||
$this->resources?->addClassResource($provider);
|
||||
return $validated;
|
||||
});
|
||||
} else {
|
||||
$node->validate()->always(function (array $value) use ($type): ComponentPlugin {
|
||||
if (count($value) !== 1) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Component "%s" must have exactly one element defined, got %s',
|
||||
$type,
|
||||
implode(', ', array_map(json_encode(...), array_keys($value)) ?: ['none'])
|
||||
));
|
||||
}
|
||||
|
||||
return new ComponentPlugin($value[$name], $this->providers[$type][$name]);
|
||||
});
|
||||
$name = array_key_first($value);
|
||||
$provider = $this->providers[$type][$name];
|
||||
$this->resources?->addClassResource($provider);
|
||||
|
||||
return new ComponentPlugin($value[$name], $this->providers[$type][$name]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Config\SDK;
|
||||
|
||||
use Nevay\SPI\ServiceLoader;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\ConfigurationRegistry;
|
||||
use OpenTelemetry\Config\SDK\ComponentProvider\InstrumentationConfigurationRegistry;
|
||||
use OpenTelemetry\Config\SDK\Configuration\ComponentPlugin;
|
||||
use OpenTelemetry\Config\SDK\Configuration\ComponentProvider;
|
||||
use OpenTelemetry\Config\SDK\Configuration\ConfigurationFactory;
|
||||
use OpenTelemetry\Config\SDK\Configuration\Context;
|
||||
use OpenTelemetry\Config\SDK\Configuration\Environment\EnvSourceReader;
|
||||
use OpenTelemetry\Config\SDK\Configuration\Environment\PhpIniEnvSource;
|
||||
use OpenTelemetry\Config\SDK\Configuration\Environment\ServerEnvSource;
|
||||
|
||||
final class Instrumentation
|
||||
{
|
||||
|
||||
/**
|
||||
* @param ComponentPlugin<ConfigurationRegistry> $plugin
|
||||
*/
|
||||
private function __construct(
|
||||
private readonly ComponentPlugin $plugin,
|
||||
) {
|
||||
}
|
||||
|
||||
public function create(Context $context = new Context()): ConfigurationRegistry
|
||||
{
|
||||
$plugin = $this->plugin;
|
||||
|
||||
return $plugin->create($context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|list<string> $file
|
||||
*/
|
||||
public static function parseFile(
|
||||
string|array $file,
|
||||
?string $cacheFile = null,
|
||||
bool $debug = true,
|
||||
): Instrumentation {
|
||||
return new self(self::factory()->parseFile($file, $cacheFile, $debug));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ConfigurationFactory<ConfigurationRegistry>
|
||||
*/
|
||||
private static function factory(): ConfigurationFactory
|
||||
{
|
||||
static $factory;
|
||||
|
||||
return $factory ??= new ConfigurationFactory(
|
||||
ServiceLoader::load(ComponentProvider::class),
|
||||
new InstrumentationConfigurationRegistry(),
|
||||
new EnvSourceReader([
|
||||
new ServerEnvSource(),
|
||||
new PhpIniEnvSource(),
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
"open-telemetry/context": "^1.0",
|
||||
"open-telemetry/sdk": "^1.0",
|
||||
"symfony/config": "^5.4 || ^6.4 || ^7.0",
|
||||
"tbachert/spi": "^0.2"
|
||||
"tbachert/spi": ">= 0.2.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
|
@ -64,7 +64,10 @@
|
|||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterConsole",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordExporterOtlp",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordProcessorBatch",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordProcessorSimple"
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Logs\\LogRecordProcessorSimple",
|
||||
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Instrumentation\\General\\HttpConfigProvider",
|
||||
"OpenTelemetry\\Config\\SDK\\ComponentProvider\\Instrumentation\\General\\PeerConfigProvider"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,4 +119,5 @@ interface Defaults
|
|||
public const OTEL_PHP_DISABLED_INSTRUMENTATIONS = [];
|
||||
public const OTEL_PHP_LOGS_PROCESSOR = 'batch';
|
||||
public const OTEL_PHP_LOG_DESTINATION = 'default';
|
||||
public const OTEL_EXPERIMENTAL_CONFIG_FILE = 'sdk-config.yaml';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,4 +140,5 @@ interface Variables
|
|||
public const OTEL_PHP_INTERNAL_METRICS_ENABLED = 'OTEL_PHP_INTERNAL_METRICS_ENABLED'; //whether the SDK should emit its own metrics
|
||||
public const OTEL_PHP_DISABLED_INSTRUMENTATIONS = 'OTEL_PHP_DISABLED_INSTRUMENTATIONS';
|
||||
public const OTEL_PHP_EXCLUDED_URLS = 'OTEL_PHP_EXCLUDED_URLS';
|
||||
public const OTEL_EXPERIMENTAL_CONFIG_FILE = 'OTEL_EXPERIMENTAL_CONFIG_FILE';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ use TypeError;
|
|||
/**
|
||||
* A registry to enable central registration of components that the SDK requires but which may be provided
|
||||
* by non-SDK modules, such as contrib and extension.
|
||||
* @todo [breaking] deprecate this mechanism of setting up components, in favor of using SPI.
|
||||
*/
|
||||
class Registry
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,9 +4,24 @@ declare(strict_types=1);
|
|||
|
||||
namespace OpenTelemetry\SDK;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Nevay\SPI\ServiceLoader;
|
||||
use OpenTelemetry\API\Behavior\LogsMessagesTrait;
|
||||
use OpenTelemetry\API\Globals;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Context as InstrumentationContext;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\HookManager;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\HookManagerInterface;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Instrumentation;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\NoopHookManager;
|
||||
use OpenTelemetry\API\Instrumentation\Configurator;
|
||||
use OpenTelemetry\API\Logs\LateBindingLoggerProvider;
|
||||
use OpenTelemetry\API\Logs\LoggerProviderInterface;
|
||||
use OpenTelemetry\API\Metrics\LateBindingMeterProvider;
|
||||
use OpenTelemetry\API\Metrics\MeterProviderInterface;
|
||||
use OpenTelemetry\API\Trace\LateBindingTracerProvider;
|
||||
use OpenTelemetry\API\Trace\TracerProviderInterface;
|
||||
use OpenTelemetry\Config\SDK\Configuration as SdkConfiguration;
|
||||
use OpenTelemetry\Config\SDK\Instrumentation as SdkInstrumentation;
|
||||
use OpenTelemetry\Context\Context;
|
||||
use OpenTelemetry\SDK\Common\Configuration\Configuration;
|
||||
use OpenTelemetry\SDK\Common\Configuration\Variables;
|
||||
use OpenTelemetry\SDK\Common\Util\ShutdownHandler;
|
||||
|
|
@ -19,76 +34,167 @@ use OpenTelemetry\SDK\Trace\ExporterFactory;
|
|||
use OpenTelemetry\SDK\Trace\SamplerFactory;
|
||||
use OpenTelemetry\SDK\Trace\SpanProcessorFactory;
|
||||
use OpenTelemetry\SDK\Trace\TracerProviderBuilder;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* @psalm-suppress RedundantCast
|
||||
*/
|
||||
class SdkAutoloader
|
||||
{
|
||||
use LogsMessagesTrait;
|
||||
|
||||
public static function autoload(): bool
|
||||
{
|
||||
if (!self::isEnabled() || self::isExcludedUrl()) {
|
||||
return false;
|
||||
}
|
||||
Globals::registerInitializer(function (Configurator $configurator) {
|
||||
$propagator = (new PropagatorFactory())->create();
|
||||
if (Sdk::isDisabled()) {
|
||||
//@see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/configuration/sdk-environment-variables.md#general-sdk-configuration
|
||||
return $configurator->withPropagator($propagator);
|
||||
}
|
||||
$emitMetrics = Configuration::getBoolean(Variables::OTEL_PHP_INTERNAL_METRICS_ENABLED);
|
||||
|
||||
$resource = ResourceInfoFactory::defaultResource();
|
||||
$exporter = (new ExporterFactory())->create();
|
||||
$meterProvider = (new MeterProviderFactory())->create($resource);
|
||||
$spanProcessor = (new SpanProcessorFactory())->create($exporter, $emitMetrics ? $meterProvider : null);
|
||||
$tracerProvider = (new TracerProviderBuilder())
|
||||
->addSpanProcessor($spanProcessor)
|
||||
->setResource($resource)
|
||||
->setSampler((new SamplerFactory())->create())
|
||||
->build();
|
||||
|
||||
$loggerProvider = (new LoggerProviderFactory())->create($emitMetrics ? $meterProvider : null, $resource);
|
||||
$eventLoggerProvider = (new EventLoggerProviderFactory())->create($loggerProvider);
|
||||
|
||||
ShutdownHandler::register($tracerProvider->shutdown(...));
|
||||
ShutdownHandler::register($meterProvider->shutdown(...));
|
||||
ShutdownHandler::register($loggerProvider->shutdown(...));
|
||||
|
||||
return $configurator
|
||||
->withTracerProvider($tracerProvider)
|
||||
->withMeterProvider($meterProvider)
|
||||
->withLoggerProvider($loggerProvider)
|
||||
->withEventLoggerProvider($eventLoggerProvider)
|
||||
->withPropagator($propagator)
|
||||
;
|
||||
});
|
||||
if (Configuration::has(Variables::OTEL_EXPERIMENTAL_CONFIG_FILE)) {
|
||||
Globals::registerInitializer(fn ($configurator) => self::fileBasedInitializer($configurator));
|
||||
} else {
|
||||
Globals::registerInitializer(fn ($configurator) => self::environmentBasedInitializer($configurator));
|
||||
}
|
||||
self::registerInstrumentations();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether a request URI is set, and if it matches the excluded urls configuration option
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function isIgnoredUrl(): bool
|
||||
private static function environmentBasedInitializer(Configurator $configurator): Configurator
|
||||
{
|
||||
$ignoreUrls = Configuration::getList(Variables::OTEL_PHP_EXCLUDED_URLS, []);
|
||||
if ($ignoreUrls === []) {
|
||||
return false;
|
||||
$propagator = (new PropagatorFactory())->create();
|
||||
if (Sdk::isDisabled()) {
|
||||
//@see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/configuration/sdk-environment-variables.md#general-sdk-configuration
|
||||
return $configurator->withPropagator($propagator);
|
||||
}
|
||||
$url = $_SERVER['REQUEST_URI'] ?? null;
|
||||
if (!$url) {
|
||||
return false;
|
||||
}
|
||||
foreach ($ignoreUrls as $ignore) {
|
||||
if (preg_match(sprintf('|%s|', $ignore), (string) $url) === 1) {
|
||||
return true;
|
||||
}
|
||||
$emitMetrics = Configuration::getBoolean(Variables::OTEL_PHP_INTERNAL_METRICS_ENABLED);
|
||||
|
||||
$resource = ResourceInfoFactory::defaultResource();
|
||||
$exporter = (new ExporterFactory())->create();
|
||||
$meterProvider = (new MeterProviderFactory())->create($resource);
|
||||
$spanProcessor = (new SpanProcessorFactory())->create($exporter, $emitMetrics ? $meterProvider : null);
|
||||
$tracerProvider = (new TracerProviderBuilder())
|
||||
->addSpanProcessor($spanProcessor)
|
||||
->setResource($resource)
|
||||
->setSampler((new SamplerFactory())->create())
|
||||
->build();
|
||||
|
||||
$loggerProvider = (new LoggerProviderFactory())->create($emitMetrics ? $meterProvider : null, $resource);
|
||||
$eventLoggerProvider = (new EventLoggerProviderFactory())->create($loggerProvider);
|
||||
|
||||
ShutdownHandler::register($tracerProvider->shutdown(...));
|
||||
ShutdownHandler::register($meterProvider->shutdown(...));
|
||||
ShutdownHandler::register($loggerProvider->shutdown(...));
|
||||
|
||||
return $configurator
|
||||
->withTracerProvider($tracerProvider)
|
||||
->withMeterProvider($meterProvider)
|
||||
->withLoggerProvider($loggerProvider)
|
||||
->withEventLoggerProvider($eventLoggerProvider)
|
||||
->withPropagator($propagator)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @phan-suppress PhanPossiblyUndeclaredVariable
|
||||
*/
|
||||
private static function fileBasedInitializer(Configurator $configurator): Configurator
|
||||
{
|
||||
$file = Configuration::getString(Variables::OTEL_EXPERIMENTAL_CONFIG_FILE);
|
||||
$config = SdkConfiguration::parseFile($file);
|
||||
|
||||
//disable hook manager during SDK to avoid autoinstrumenting SDK exporters.
|
||||
$scope = HookManager::disable(Context::getCurrent())->activate();
|
||||
|
||||
try {
|
||||
$sdk = $config
|
||||
->create()
|
||||
->setAutoShutdown(true)
|
||||
->build();
|
||||
} finally {
|
||||
$scope->detach();
|
||||
}
|
||||
|
||||
return false;
|
||||
return $configurator
|
||||
->withTracerProvider($sdk->getTracerProvider())
|
||||
->withMeterProvider($sdk->getMeterProvider())
|
||||
->withLoggerProvider($sdk->getLoggerProvider())
|
||||
->withPropagator($sdk->getPropagator())
|
||||
->withEventLoggerProvider($sdk->getEventLoggerProvider())
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all {@link Instrumentation} configured through SPI
|
||||
* @psalm-suppress ArgumentTypeCoercion
|
||||
*/
|
||||
private static function registerInstrumentations(): void
|
||||
{
|
||||
$files = Configuration::has(Variables::OTEL_EXPERIMENTAL_CONFIG_FILE)
|
||||
? Configuration::getList(Variables::OTEL_EXPERIMENTAL_CONFIG_FILE)
|
||||
: [];
|
||||
$configuration = SdkInstrumentation::parseFile($files)->create();
|
||||
$hookManager = self::getHookManager();
|
||||
$tracerProvider = self::createLateBindingTracerProvider();
|
||||
$meterProvider = self::createLateBindingMeterProvider();
|
||||
$loggerProvider = self::createLateBindingLoggerProvider();
|
||||
$context = new InstrumentationContext($tracerProvider, $meterProvider, $loggerProvider);
|
||||
|
||||
foreach (ServiceLoader::load(Instrumentation::class) as $instrumentation) {
|
||||
/** @var Instrumentation $instrumentation */
|
||||
try {
|
||||
$instrumentation->register($hookManager, $configuration, $context);
|
||||
} catch (Throwable $t) {
|
||||
self::logError(sprintf('Unable to load instrumentation: %s', $instrumentation::class), ['exception' => $t]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static function createLateBindingTracerProvider(): TracerProviderInterface
|
||||
{
|
||||
return new LateBindingTracerProvider(static function (): TracerProviderInterface {
|
||||
$scope = Context::getRoot()->activate();
|
||||
|
||||
try {
|
||||
return Globals::tracerProvider();
|
||||
} finally {
|
||||
$scope->detach();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static function createLateBindingMeterProvider(): MeterProviderInterface
|
||||
{
|
||||
return new LateBindingMeterProvider(static function (): MeterProviderInterface {
|
||||
$scope = Context::getRoot()->activate();
|
||||
|
||||
try {
|
||||
return Globals::meterProvider();
|
||||
} finally {
|
||||
$scope->detach();
|
||||
}
|
||||
});
|
||||
}
|
||||
private static function createLateBindingLoggerProvider(): LoggerProviderInterface
|
||||
{
|
||||
return new LateBindingLoggerProvider(static function (): LoggerProviderInterface {
|
||||
$scope = Context::getRoot()->activate();
|
||||
|
||||
try {
|
||||
return Globals::loggerProvider();
|
||||
} finally {
|
||||
$scope->detach();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static function getHookManager(): HookManagerInterface
|
||||
{
|
||||
/** @var HookManagerInterface $hookManager */
|
||||
foreach (ServiceLoader::load(HookManagerInterface::class) as $hookManager) {
|
||||
return $hookManager;
|
||||
}
|
||||
|
||||
return new NoopHookManager();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -96,14 +202,7 @@ class SdkAutoloader
|
|||
*/
|
||||
public static function isEnabled(): bool
|
||||
{
|
||||
try {
|
||||
$enabled = Configuration::getBoolean(Variables::OTEL_PHP_AUTOLOAD_ENABLED);
|
||||
} catch (InvalidArgumentException) {
|
||||
//invalid setting, assume false
|
||||
return false;
|
||||
}
|
||||
|
||||
return $enabled;
|
||||
return Configuration::getBoolean(Variables::OTEL_PHP_AUTOLOAD_ENABLED);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@
|
|||
"psr/log": "^1.1|^2.0|^3.0",
|
||||
"ramsey/uuid": "^3.0 || ^4.0",
|
||||
"symfony/polyfill-mbstring": "^1.23",
|
||||
"symfony/polyfill-php82": "^1.26"
|
||||
"symfony/polyfill-php82": "^1.26",
|
||||
"tbachert/spi": ">= 0.2.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
|
@ -53,6 +54,11 @@
|
|||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.0.x-dev"
|
||||
},
|
||||
"spi": {
|
||||
"OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [
|
||||
"OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\ExtensionHookManager"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# anchors.yaml demonstrates anchor substitution to reuse OTLP exporter configuration across signals.
|
||||
|
||||
file_format: "0.1"
|
||||
file_format: "0.3"
|
||||
exporters:
|
||||
otlp: &otlp-exporter
|
||||
protocol: http/protobuf
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
# Configuration values are set to their defaults when default values are defined.
|
||||
|
||||
# The file format version
|
||||
file_format: "0.1"
|
||||
file_format: "0.3"
|
||||
|
||||
# Configure if the SDK is disabled or not. This is not required to be provided
|
||||
# to ensure the SDK isn't disabled, the default value when this is not provided
|
||||
|
|
@ -350,3 +350,107 @@ resource:
|
|||
service.name: !!str "unknown_service"
|
||||
# Configure the resource schema URL.
|
||||
schema_url: https://opentelemetry.io/schemas/1.26.0
|
||||
|
||||
# Configure instrumentation.
|
||||
instrumentation:
|
||||
# Configure general SemConv options that may apply to multiple languages and instrumentations.
|
||||
#
|
||||
# Instrumenation may merge general config options with the language specific configuration at .instrumentation.<language>.
|
||||
general:
|
||||
# Configure instrumentations following the peer semantic conventions.
|
||||
#
|
||||
# See peer semantic conventions: https://opentelemetry.io/docs/specs/semconv/attributes-registry/peer/
|
||||
peer:
|
||||
# Configure the service mapping for instrumentations following peer.service semantic conventions.
|
||||
#
|
||||
# Each entry is a key value pair where "peer" defines the IP address and "service" defines the corresponding logical name of the service.
|
||||
#
|
||||
# See peer.service semantic conventions: https://opentelemetry.io/docs/specs/semconv/general/attributes/#general-remote-service-attributes
|
||||
service_mapping:
|
||||
- peer: 1.2.3.4
|
||||
service: FooService
|
||||
- peer: 2.3.4.5
|
||||
service: BarService
|
||||
# Configure instrumentations following the http semantic conventions.
|
||||
#
|
||||
# See http semantic conventions: https://opentelemetry.io/docs/specs/semconv/http/
|
||||
http:
|
||||
# Configure instrumentations following the http client semantic conventions.
|
||||
client:
|
||||
# Configure headers to capture for outbound http requests.
|
||||
request_captured_headers:
|
||||
- Content-Type
|
||||
- Accept
|
||||
# Configure headers to capture for outbound http responses.
|
||||
response_captured_headers:
|
||||
- Content-Type
|
||||
- Content-Encoding
|
||||
# Configure instrumentations following the http server semantic conventions.
|
||||
server:
|
||||
# Configure headers to capture for inbound http requests.
|
||||
request_captured_headers:
|
||||
- Content-Type
|
||||
- Accept
|
||||
# Configure headers to capture for outbound http responses.
|
||||
response_captured_headers:
|
||||
- Content-Type
|
||||
- Content-Encoding
|
||||
# Configure language-specific instrumentation libraries.
|
||||
#
|
||||
# Keys may refer to instrumentation libraries or collections of related configuration. Because there is no central schema defining the keys or their contents, instrumentation must carefully document their schema and avoid key collisions with other instrumentations.
|
||||
#
|
||||
# Configure C++ language-specific instrumentation libraries.
|
||||
cpp:
|
||||
# Configure the instrumentation corresponding to key "example".
|
||||
example:
|
||||
property: "value"
|
||||
# Configure .NET language-specific instrumentation libraries.
|
||||
dotnet:
|
||||
# Configure the instrumentation corresponding to key "example".
|
||||
example:
|
||||
property: "value"
|
||||
# Configure Erlang language-specific instrumentation libraries.
|
||||
erlang:
|
||||
# Configure the instrumentation corresponding to key "example".
|
||||
example:
|
||||
property: "value"
|
||||
# Configure Go language-specific instrumentation libraries.
|
||||
go:
|
||||
# Configure the instrumentation corresponding to key "example".
|
||||
example:
|
||||
property: "value"
|
||||
# Configure Java language-specific instrumentation libraries.
|
||||
java:
|
||||
# Configure the instrumentation corresponding to key "example".
|
||||
example:
|
||||
property: "value"
|
||||
# Configure JavaScript language-specific instrumentation libraries.
|
||||
js:
|
||||
# Configure the instrumentation corresponding to key "example".
|
||||
example:
|
||||
property: "value"
|
||||
# Configure PHP language-specific instrumentation libraries.
|
||||
php:
|
||||
# Configure the instrumentation corresponding to key "example".
|
||||
example:
|
||||
property: "value"
|
||||
# Configure Python language-specific instrumentation libraries.
|
||||
python:
|
||||
# Configure the instrumentation corresponding to key "example".
|
||||
example:
|
||||
property: "value"
|
||||
# Configure Ruby language-specific instrumentation libraries.
|
||||
ruby:
|
||||
# Configure the instrumentation corresponding to key "example".
|
||||
example:
|
||||
property: "value"
|
||||
# Configure Rust language-specific instrumentation libraries.
|
||||
rust:
|
||||
# Configure the instrumentation corresponding to key "example".
|
||||
example:
|
||||
property: "value"
|
||||
# Configure Swift language-specific instrumentation libraries.
|
||||
swift:
|
||||
# Configure the instrumentation corresponding to key "example".
|
||||
example:
|
||||
property: "value"
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Unit\API\Instrumentation\AutoInstrumentation;
|
||||
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\ConfigurationRegistry;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\InstrumentationConfiguration;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
#[CoversClass(ConfigurationRegistry::class)]
|
||||
class ConfigurationRegistryTest extends TestCase
|
||||
{
|
||||
public function test_registry(): void
|
||||
{
|
||||
$registry = new ConfigurationRegistry();
|
||||
$config = new class() implements InstrumentationConfiguration {};
|
||||
$registry->add($config);
|
||||
|
||||
$this->assertSame($config, $registry->get($config::class));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Unit\API\Instrumentation\AutoInstrumentation;
|
||||
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Context;
|
||||
use OpenTelemetry\API\Logs\NoopLoggerProvider;
|
||||
use OpenTelemetry\API\Metrics\Noop\NoopMeterProvider;
|
||||
use OpenTelemetry\API\Trace\NoopTracerProvider;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
#[CoversClass(Context::class)]
|
||||
class ContextTest extends TestCase
|
||||
{
|
||||
public function test_default_noops(): void
|
||||
{
|
||||
$context = new Context();
|
||||
$this->assertInstanceOf(NoopTracerProvider::class, $context->tracerProvider);
|
||||
$this->assertInstanceOf(NoopMeterProvider::class, $context->meterProvider);
|
||||
$this->assertInstanceOf(NoopLoggerProvider::class, $context->loggerProvider);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Tests\Unit\API\Instrumentation\AutoInstrumentation;
|
||||
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\ConfigurationRegistry;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Context as InstrumentationContext;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\ExtensionHookManager;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\HookManager;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\HookManagerInterface;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Instrumentation;
|
||||
use OpenTelemetry\API\Instrumentation\Configurator;
|
||||
use OpenTelemetry\API\Logs\LoggerProviderInterface;
|
||||
use OpenTelemetry\API\Metrics\MeterProviderInterface;
|
||||
use OpenTelemetry\API\Trace\TracerProviderInterface;
|
||||
use OpenTelemetry\Context\Context;
|
||||
use OpenTelemetry\Context\ScopeInterface;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
#[CoversClass(ExtensionHookManager::class)]
|
||||
class ExtensionHookManagerTest extends TestCase
|
||||
{
|
||||
private ConfigurationRegistry $registry;
|
||||
private ScopeInterface $scope;
|
||||
private HookManagerInterface $hookManager;
|
||||
private InstrumentationContext $context;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
if (!extension_loaded('opentelemetry')) {
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
$tracerProvider = $this->createMock(TracerProviderInterface::class);
|
||||
$this->scope = Configurator::create()
|
||||
->withTracerProvider($tracerProvider)
|
||||
->activate();
|
||||
$this->registry = new ConfigurationRegistry();
|
||||
$this->hookManager = new ExtensionHookManager();
|
||||
$this->context = new InstrumentationContext(
|
||||
$tracerProvider,
|
||||
$this->createMock(MeterProviderInterface::class),
|
||||
$this->createMock(LoggerProviderInterface::class)
|
||||
);
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
$this->scope->detach();
|
||||
}
|
||||
|
||||
public function test_modify_return_value_from_post_hook(): void
|
||||
{
|
||||
$target = new class() {
|
||||
public function test(): int
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
$instrumentation = $this->createInstrumentation($target::class, 'test', function () {
|
||||
}, function (): int {
|
||||
return 99;
|
||||
});
|
||||
$instrumentation->register($this->hookManager, $this->registry, $this->context);
|
||||
|
||||
$returnVal = $target->test();
|
||||
$this->assertSame(99, $returnVal);
|
||||
}
|
||||
|
||||
public function test_hook_manager_disabled(): void
|
||||
{
|
||||
$target = new class() {
|
||||
public function test(): int
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
};
|
||||
$instrumentation = $this->createInstrumentation($target::class, 'test', function () {
|
||||
}, function (): int {
|
||||
$this->fail('post hook not expected to be called');
|
||||
});
|
||||
$instrumentation->register($this->hookManager, $this->registry, $this->context);
|
||||
|
||||
$scope = HookManager::disable(Context::getCurrent())->activate();
|
||||
|
||||
try {
|
||||
$returnVal = $target->test();
|
||||
} finally {
|
||||
$scope->detach();
|
||||
}
|
||||
$this->assertSame(2, $returnVal, 'original value, since hook did not run');
|
||||
}
|
||||
|
||||
public function test_disable_hook_manager_after_use(): void
|
||||
{
|
||||
$target = new class() {
|
||||
public function test(): int
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
};
|
||||
$instrumentation = $this->createInstrumentation($target::class, 'test', function () {
|
||||
}, function (): int {
|
||||
return 123;
|
||||
});
|
||||
$instrumentation->register($this->hookManager, $this->registry, $this->context);
|
||||
$this->assertSame(123, $target->test(), 'post hook function ran and modified return value');
|
||||
|
||||
$scope = HookManager::disable(Context::getCurrent())->activate();
|
||||
|
||||
try {
|
||||
$this->assertSame(3, $target->test(), 'post hook function did not run');
|
||||
} finally {
|
||||
$scope->detach();
|
||||
}
|
||||
}
|
||||
|
||||
private function createInstrumentation(string $class, string $method, $pre, $post): Instrumentation
|
||||
{
|
||||
return new class($class, $method, $pre, $post) implements Instrumentation {
|
||||
private $pre;
|
||||
private $post;
|
||||
|
||||
public function __construct(
|
||||
private readonly string $class,
|
||||
private readonly string $method,
|
||||
?callable $pre = null,
|
||||
?callable $post = null,
|
||||
) {
|
||||
$this->pre = $pre;
|
||||
$this->post = $post;
|
||||
}
|
||||
|
||||
public function register(HookManagerInterface $hookManager, ConfigurationRegistry $configuration, InstrumentationContext $context): void
|
||||
{
|
||||
$hookManager->hook($this->class, $this->method, $this->pre, $this->post);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Tests\Unit\API\Instrumentation\AutoInstrumentation;
|
||||
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\HookManager;
|
||||
use OpenTelemetry\Context\Context;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
#[CoversClass(HookManager::class)]
|
||||
class HookManagerTest extends TestCase
|
||||
{
|
||||
public function test_enable_disable(): void
|
||||
{
|
||||
$context = Context::getRoot();
|
||||
|
||||
$this->assertFalse(HookManager::disabled($context));
|
||||
|
||||
$context = HookManager::disable($context);
|
||||
$this->assertTrue(HookManager::disabled($context));
|
||||
|
||||
$context = HookManager::enable($context);
|
||||
$this->assertFalse(HookManager::disabled($context));
|
||||
}
|
||||
|
||||
public function test_global_disable(): void
|
||||
{
|
||||
$this->assertFalse(HookManager::disabled());
|
||||
$scope = HookManager::disable()->activate();
|
||||
|
||||
try {
|
||||
$this->assertTrue(HookManager::disabled());
|
||||
} finally {
|
||||
$scope->detach();
|
||||
}
|
||||
$this->assertFalse(HookManager::disabled());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OpenTelemetry\Tests\Unit\API\Instrumentation\AutoInstrumentation;
|
||||
|
||||
use Nevay\SPI\ServiceLoader;
|
||||
use OpenTelemetry\API\Behavior\Internal\Logging;
|
||||
use OpenTelemetry\API\Globals;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\ConfigurationRegistry;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Context;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\HookManagerInterface;
|
||||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Instrumentation;
|
||||
use OpenTelemetry\API\Instrumentation\Configurator;
|
||||
use OpenTelemetry\API\Logs\LateBindingLogger;
|
||||
use OpenTelemetry\API\Logs\LateBindingLoggerProvider;
|
||||
use OpenTelemetry\API\Logs\LoggerInterface;
|
||||
use OpenTelemetry\API\Logs\LoggerProviderInterface;
|
||||
use OpenTelemetry\API\Logs\LogRecord;
|
||||
use OpenTelemetry\API\Metrics\LateBindingMeter;
|
||||
use OpenTelemetry\API\Metrics\LateBindingMeterProvider;
|
||||
use OpenTelemetry\API\Metrics\MeterInterface;
|
||||
use OpenTelemetry\API\Metrics\MeterProviderInterface;
|
||||
use OpenTelemetry\API\Trace\LateBindingTracer;
|
||||
use OpenTelemetry\API\Trace\LateBindingTracerProvider;
|
||||
use OpenTelemetry\API\Trace\TracerInterface;
|
||||
use OpenTelemetry\API\Trace\TracerProviderInterface;
|
||||
use OpenTelemetry\SDK\Common\Configuration\Variables;
|
||||
use OpenTelemetry\SDK\SdkAutoloader;
|
||||
use OpenTelemetry\Tests\TestState;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
#[CoversClass(LateBindingLoggerProvider::class)]
|
||||
#[CoversClass(LateBindingLogger::class)]
|
||||
#[CoversClass(LateBindingMeterProvider::class)]
|
||||
#[CoversClass(LateBindingMeter::class)]
|
||||
#[CoversClass(LateBindingTracerProvider::class)]
|
||||
#[CoversClass(LateBindingTracer::class)]
|
||||
// @todo phpunit 11 (8.2+) only, replace CoversClass(SdkAutoloader::class)
|
||||
#[CoversClass(SdkAutoloader::class)]
|
||||
//#[CoversMethod(SdkAutoloader::class, 'createLateBindingLoggerProvider')]
|
||||
//#[CoversMethod(SdkAutoloader::class, 'createLateBindingMeterProvider')]
|
||||
//#[CoversMethod(SdkAutoloader::class, 'createLateBindingTracerProvider')]
|
||||
class LateBindingProviderTest extends TestCase
|
||||
{
|
||||
use TestState;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
Logging::disable();
|
||||
}
|
||||
|
||||
public function test_late_binding_providers(): void
|
||||
{
|
||||
$instrumentation = new class() implements Instrumentation {
|
||||
private static ?Context $context;
|
||||
public function register(HookManagerInterface $hookManager, ConfigurationRegistry $configuration, Context $context): void
|
||||
{
|
||||
self::$context = $context;
|
||||
}
|
||||
public function getTracer(): TracerInterface
|
||||
{
|
||||
assert(self::$context !== null);
|
||||
|
||||
return self::$context->tracerProvider->getTracer('test');
|
||||
}
|
||||
public function getMeter(): MeterInterface
|
||||
{
|
||||
assert(self::$context !== null);
|
||||
|
||||
return self::$context->meterProvider->getMeter('test');
|
||||
}
|
||||
public function getLogger(): LoggerInterface
|
||||
{
|
||||
assert(self::$context !== null);
|
||||
|
||||
return self::$context->loggerProvider->getLogger('test');
|
||||
}
|
||||
};
|
||||
$this->setEnvironmentVariable(Variables::OTEL_PHP_AUTOLOAD_ENABLED, 'true');
|
||||
$tracer_accessed = false;
|
||||
$logger_accessed = false;
|
||||
$meter_accessed = false;
|
||||
|
||||
$tracerProvider = $this->createMock(TracerProviderInterface::class);
|
||||
$tracerProvider->method('getTracer')->willReturnCallback(function () use (&$tracer_accessed): TracerInterface {
|
||||
$tracer_accessed = true;
|
||||
|
||||
return $this->createMock(TracerInterface::class);
|
||||
});
|
||||
$meterProvider = $this->createMock(MeterProviderInterface::class);
|
||||
$meterProvider->method('getMeter')->willReturnCallback(function () use (&$meter_accessed): MeterInterface {
|
||||
$meter_accessed = true;
|
||||
|
||||
return $this->createMock(MeterInterface::class);
|
||||
});
|
||||
$loggerProvider = $this->createMock(LoggerProviderInterface::class);
|
||||
$loggerProvider->method('getLogger')->willReturnCallback(function () use (&$logger_accessed): LoggerInterface {
|
||||
$logger_accessed = true;
|
||||
|
||||
return $this->createMock(LoggerInterface::class);
|
||||
});
|
||||
ServiceLoader::register(Instrumentation::class, $instrumentation::class);
|
||||
$this->assertTrue(SdkAutoloader::autoload());
|
||||
//initializer added _after_ autoloader has run and instrumentation registered
|
||||
Globals::registerInitializer(function (Configurator $configurator) use ($tracerProvider, $loggerProvider, $meterProvider): Configurator {
|
||||
return $configurator
|
||||
->withTracerProvider($tracerProvider)
|
||||
->withMeterProvider($meterProvider)
|
||||
->withLoggerProvider($loggerProvider)
|
||||
;
|
||||
});
|
||||
|
||||
$this->assertFalse($tracer_accessed);
|
||||
$tracer = $instrumentation->getTracer();
|
||||
$this->assertFalse($tracer_accessed);
|
||||
$tracer->spanBuilder('test-span'); /** @phpstan-ignore-next-line */
|
||||
$this->assertTrue($tracer_accessed);
|
||||
|
||||
$this->assertFalse($meter_accessed);
|
||||
$meter = $instrumentation->getMeter();
|
||||
$this->assertFalse($meter_accessed);
|
||||
$meter->createCounter('cnt'); /** @phpstan-ignore-next-line */
|
||||
$this->assertTrue($meter_accessed);
|
||||
|
||||
$this->assertFalse($logger_accessed);
|
||||
$logger = $instrumentation->getLogger();
|
||||
$this->assertFalse($logger_accessed);
|
||||
$logger->emit(new LogRecord()); /** @phpstan-ignore-next-line */
|
||||
$this->assertTrue($logger_accessed);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace OpenTelemetry\Tests\Unit\API\Instrumentation;
|
||||
|
||||
use OpenTelemetry\API\Behavior\Internal\Logging;
|
||||
use OpenTelemetry\API\Behavior\Internal\LogWriter\LogWriterInterface;
|
||||
use OpenTelemetry\API\Globals;
|
||||
use OpenTelemetry\API\Instrumentation\CachedInstrumentation;
|
||||
use OpenTelemetry\API\Instrumentation\Configurator;
|
||||
|
|
@ -25,7 +27,9 @@ use OpenTelemetry\API\Trace\TracerProviderInterface;
|
|||
use OpenTelemetry\Context\Propagation\NoopTextMapPropagator;
|
||||
use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LogLevel;
|
||||
|
||||
#[CoversClass(Globals::class)]
|
||||
#[CoversClass(CachedInstrumentation::class)]
|
||||
|
|
@ -33,7 +37,15 @@ use PHPUnit\Framework\TestCase;
|
|||
#[CoversClass(ContextKeys::class)]
|
||||
final class InstrumentationTest extends TestCase
|
||||
{
|
||||
private LogWriterInterface&MockObject $logWriter;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->logWriter = $this->createMock(LogWriterInterface::class);
|
||||
Logging::setLogWriter($this->logWriter);
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
Globals::reset();
|
||||
}
|
||||
|
|
@ -131,4 +143,18 @@ final class InstrumentationTest extends TestCase
|
|||
Globals::propagator();
|
||||
$this->assertTrue($called); //@phpstan-ignore-line
|
||||
}
|
||||
|
||||
public function test_initializer_error(): void
|
||||
{
|
||||
$closure = function (Configurator $configurator): Configurator {
|
||||
throw new \Exception('kaboom');
|
||||
};
|
||||
Globals::registerInitializer($closure);
|
||||
$this->logWriter->expects($this->once())->method('write')->with(
|
||||
$this->equalTo(LogLevel::WARNING),
|
||||
$this->anything(),
|
||||
$this->anything(),
|
||||
);
|
||||
Globals::propagator();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,8 +26,19 @@ use Symfony\Component\Yaml\Yaml;
|
|||
#[CoversClass(ConfigurationFactory::class)]
|
||||
final class ConfigurationFactoryTest extends TestCase
|
||||
{
|
||||
|
||||
public string $cacheDir;
|
||||
public $properties;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->cacheDir = __DIR__ . '/configurations';
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
array_map('unlink', array_filter((array) glob($this->cacheDir . '/*cache*')));
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-suppress MissingTemplateParam
|
||||
*/
|
||||
|
|
@ -238,4 +249,15 @@ final class ConfigurationFactoryTest extends TestCase
|
|||
]),
|
||||
);
|
||||
}
|
||||
|
||||
public function test_cache_configuration(): void
|
||||
{
|
||||
$file = $this->cacheDir . '/kitchen-sink.yaml';
|
||||
$cacheFile = $this->cacheDir . '/kitchen-sink.cache';
|
||||
$this->assertFalse(file_exists($cacheFile), 'cache does not initially exist');
|
||||
$plugin = self::factory()->parseFile($file, $cacheFile);
|
||||
$this->assertTrue(file_exists($cacheFile));
|
||||
$fromCache = self::factory()->parseFile($file, $cacheFile);
|
||||
$this->assertEquals($fromCache, $plugin);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ final class OpenTelemetryConfiguration implements ComponentProvider
|
|||
->end()
|
||||
->end()
|
||||
->append($registry->component('sampler', Sampler::class))
|
||||
->append($registry->componentList('processors', SpanProcessor::class))
|
||||
->append($registry->componentArrayList('processors', SpanProcessor::class))
|
||||
->end()
|
||||
;
|
||||
|
||||
|
|
@ -203,7 +203,7 @@ final class OpenTelemetryConfiguration implements ComponentProvider
|
|||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->append($registry->componentList('readers', MetricReader::class))
|
||||
->append($registry->componentArrayList('readers', MetricReader::class))
|
||||
->end()
|
||||
;
|
||||
|
||||
|
|
@ -223,7 +223,7 @@ final class OpenTelemetryConfiguration implements ComponentProvider
|
|||
->integerNode('attribute_count_limit')->min(0)->defaultNull()->end()
|
||||
->end()
|
||||
->end()
|
||||
->append($registry->componentList('processors', LogRecordProcessor::class))
|
||||
->append($registry->componentArrayList('processors', LogRecordProcessor::class))
|
||||
->end()
|
||||
;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
**cache**
|
||||
|
|
@ -385,4 +385,32 @@ resource:
|
|||
# Environment variable: OTEL_SERVICE_NAME
|
||||
service.name: !!str "unknown_service"
|
||||
# Configure the resource schema URL.
|
||||
schema_url: https://opentelemetry.io/schemas/1.16.0
|
||||
schema_url: https://opentelemetry.io/schemas/1.16.0
|
||||
|
||||
instrumentation:
|
||||
php:
|
||||
example_instrumentation:
|
||||
span_name: ${EXAMPLE_INSTRUMENTATION_SPAN_NAME:-example span}
|
||||
java:
|
||||
general:
|
||||
peer:
|
||||
service_mapping:
|
||||
- peer: 1.2.3.4
|
||||
service: FooService
|
||||
- peer: 2.3.4.5
|
||||
service: BarService
|
||||
http:
|
||||
client:
|
||||
request_captured_headers:
|
||||
- Content-Type
|
||||
- Accept
|
||||
response_captured_headers:
|
||||
- Content-Type
|
||||
- Content-Encoding
|
||||
server:
|
||||
request_captured_headers:
|
||||
- Content-Type
|
||||
- Accept
|
||||
response_captured_headers:
|
||||
- Content-Type
|
||||
- Content-Encoding
|
||||
|
|
|
|||
|
|
@ -4,28 +4,39 @@ declare(strict_types=1);
|
|||
|
||||
namespace OpenTelemetry\Tests\Unit\SDK;
|
||||
|
||||
use OpenTelemetry\API\Behavior\Internal\Logging;
|
||||
use OpenTelemetry\API\Globals;
|
||||
use OpenTelemetry\API\Instrumentation\Configurator;
|
||||
use OpenTelemetry\API\LoggerHolder;
|
||||
use OpenTelemetry\API\Logs\NoopEventLoggerProvider;
|
||||
use OpenTelemetry\API\Logs\NoopLoggerProvider;
|
||||
use OpenTelemetry\API\Metrics\Noop\NoopMeterProvider;
|
||||
use OpenTelemetry\API\Trace\NoopTracerProvider;
|
||||
use OpenTelemetry\Context\Propagation\NoopTextMapPropagator;
|
||||
use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface;
|
||||
use OpenTelemetry\SDK\Common\Configuration\Variables;
|
||||
use OpenTelemetry\SDK\SdkAutoloader;
|
||||
use OpenTelemetry\Tests\TestState;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
|
||||
#[CoversClass(SdkAutoloader::class)]
|
||||
class SdkAutoloaderTest extends TestCase
|
||||
{
|
||||
use TestState;
|
||||
|
||||
/**
|
||||
* @var LoggerInterface&MockObject
|
||||
*/
|
||||
private LoggerInterface $logger;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
Logging::disable();
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
LoggerHolder::set($this->logger);
|
||||
Globals::reset();
|
||||
}
|
||||
|
||||
|
|
@ -141,4 +152,32 @@ class SdkAutoloaderTest extends TestCase
|
|||
$_SERVER['REQUEST_URI'] = '/test';
|
||||
$this->assertFalse(SdkAutoloader::autoload());
|
||||
}
|
||||
|
||||
public function test_autoload_from_config_file(): void
|
||||
{
|
||||
$this->logger->expects($this->never())->method('log')->with($this->equalTo(LogLevel::ERROR));
|
||||
$this->setEnvironmentVariable(Variables::OTEL_PHP_AUTOLOAD_ENABLED, 'true');
|
||||
$this->setEnvironmentVariable(Variables::OTEL_EXPERIMENTAL_CONFIG_FILE, __DIR__ . '/fixtures/otel-sdk.yaml');
|
||||
|
||||
$this->assertTrue(SdkAutoloader::autoload());
|
||||
$this->assertNotInstanceOf(NoopTracerProvider::class, Globals::tracerProvider());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the scenario where the SDK is created from config file, but a custom component
|
||||
* uses composer's autoload->files to add its own initializer
|
||||
*/
|
||||
public function test_autoload_with_late_globals_initializer(): void
|
||||
{
|
||||
$this->setEnvironmentVariable(Variables::OTEL_PHP_AUTOLOAD_ENABLED, 'true');
|
||||
$this->setEnvironmentVariable(Variables::OTEL_EXPERIMENTAL_CONFIG_FILE, __DIR__ . '/fixtures/otel-sdk.yaml');
|
||||
$this->assertTrue(SdkAutoloader::autoload());
|
||||
//SDK is configured, but globals have not been initialized yet, so we can add more initializers
|
||||
$propagator = $this->createMock(TextMapPropagatorInterface::class);
|
||||
Globals::registerInitializer(function (Configurator $configurator) use ($propagator) {
|
||||
return $configurator->withPropagator($propagator);
|
||||
});
|
||||
|
||||
$this->assertSame($propagator, Globals::propagator());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,26 @@ class SdkTest extends TestCase
|
|||
{
|
||||
use TestState;
|
||||
|
||||
private TextMapPropagatorInterface $propagator;
|
||||
private MeterProviderInterface $meterProvider;
|
||||
private TracerProviderInterface $tracerProvider;
|
||||
private LoggerProviderInterface $loggerProvider;
|
||||
private EventLoggerProviderInterface $eventLoggerProvider;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->propagator = $this->createMock(TextMapPropagatorInterface::class);
|
||||
$this->meterProvider = $this->createMock(MeterProviderInterface::class);
|
||||
$this->tracerProvider = $this->createMock(TracerProviderInterface::class);
|
||||
$this->loggerProvider = $this->createMock(LoggerProviderInterface::class);
|
||||
$this->eventLoggerProvider = $this->createMock(EventLoggerProviderInterface::class);
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
self::restoreEnvironmentVariables();
|
||||
}
|
||||
|
||||
public function test_is_not_disabled_by_default(): void
|
||||
{
|
||||
$this->assertFalse(Sdk::isDisabled());
|
||||
|
|
@ -70,16 +90,11 @@ class SdkTest extends TestCase
|
|||
|
||||
public function test_getters(): void
|
||||
{
|
||||
$propagator = $this->createMock(TextMapPropagatorInterface::class);
|
||||
$meterProvider = $this->createMock(MeterProviderInterface::class);
|
||||
$tracerProvider = $this->createMock(TracerProviderInterface::class);
|
||||
$loggerProvider = $this->createMock(LoggerProviderInterface::class);
|
||||
$eventLoggerProvider = $this->createMock(EventLoggerProviderInterface::class);
|
||||
$sdk = new Sdk($tracerProvider, $meterProvider, $loggerProvider, $eventLoggerProvider, $propagator);
|
||||
$this->assertSame($propagator, $sdk->getPropagator());
|
||||
$this->assertSame($meterProvider, $sdk->getMeterProvider());
|
||||
$this->assertSame($tracerProvider, $sdk->getTracerProvider());
|
||||
$this->assertSame($loggerProvider, $sdk->getLoggerProvider());
|
||||
$this->assertSame($eventLoggerProvider, $sdk->getEventLoggerProvider());
|
||||
$sdk = new Sdk($this->tracerProvider, $this->meterProvider, $this->loggerProvider, $this->eventLoggerProvider, $this->propagator);
|
||||
$this->assertSame($this->propagator, $sdk->getPropagator());
|
||||
$this->assertSame($this->meterProvider, $sdk->getMeterProvider());
|
||||
$this->assertSame($this->tracerProvider, $sdk->getTracerProvider());
|
||||
$this->assertSame($this->loggerProvider, $sdk->getLoggerProvider());
|
||||
$this->assertSame($this->eventLoggerProvider, $sdk->getEventLoggerProvider());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
file_format: '0.3'
|
||||
|
||||
tracer_provider:
|
||||
processors:
|
||||
- simple:
|
||||
exporter:
|
||||
console: {}
|
||||
|
||||
instrumentation:
|
||||
php:
|
||||
example_instrumentation:
|
||||
span_name: my-span
|
||||
general:
|
||||
peer:
|
||||
service_mapping:
|
||||
- peer: 1.2.3.4
|
||||
service: FooService
|
||||
- peer: 2.3.4.5
|
||||
service: BarService
|
||||
http:
|
||||
client:
|
||||
request_captured_headers:
|
||||
- Content-Type
|
||||
- Accept
|
||||
response_captured_headers:
|
||||
- Content-Type
|
||||
- Content-Encoding
|
||||
server:
|
||||
request_captured_headers:
|
||||
- Content-Type
|
||||
- Accept
|
||||
response_captured_headers:
|
||||
- Content-Type
|
||||
- Content-Encoding
|
||||
Loading…
Reference in New Issue