forked from open-telemetry/opentelemetry-php
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[WIP] Add instrumentation configuration
- Loading branch information
Showing
13 changed files
with
357 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
<?php declare(strict_types=1); | ||
namespace _; | ||
|
||
use Nevay\SPI\ServiceLoader; | ||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\ConfigurationRegistry; | ||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\ExtensionHookManager; | ||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\HookManager; | ||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Instrumentation; | ||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\InstrumentationConfiguration; | ||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\NoopHookManager; | ||
use OpenTelemetry\Config\SDK\Configuration; | ||
use OpenTelemetry\Config\SDK\Configuration\ComponentPlugin; | ||
use OpenTelemetry\Config\SDK\Configuration\ComponentProvider; | ||
use OpenTelemetry\Config\SDK\Configuration\ComponentProviderRegistry; | ||
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; | ||
use OpenTelemetry\Example\Example; | ||
use OpenTelemetry\Example\ExampleConfigProvider; | ||
use OpenTelemetry\Example\ExampleInstrumentation; | ||
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; | ||
use const PHP_EOL; | ||
|
||
// EXAMPLE_INSTRUMENTATION_SPAN_NAME=test1234 php examples/instrumentation/configure_instrumentation.php | ||
|
||
require __DIR__ . '/../../vendor/autoload.php'; | ||
|
||
ServiceLoader::register(HookManager::class, ExtensionHookManager::class); | ||
ServiceLoader::register(ComponentProvider::class, ExampleConfigProvider::class); | ||
ServiceLoader::register(Instrumentation::class, ExampleInstrumentation::class); | ||
|
||
$sdk = Configuration::parseFile(__DIR__ . '/otel-sdk.yaml')->create(new Context())->setAutoShutdown(true)->build(); | ||
$configuration = parseInstrumentationConfig(__DIR__ . '/otel-instrumentation.yaml')->create(new Context()); | ||
|
||
$hookManager = hookManager(); | ||
$context = new Context($sdk->getTracerProvider()); | ||
$storage = \OpenTelemetry\Context\Context::storage(); | ||
|
||
foreach (ServiceLoader::load(Instrumentation::class) as $instrumentation) { | ||
$instrumentation->register($hookManager, $context, $configuration, $storage); | ||
} | ||
|
||
$scope = $storage->attach($hookManager->enable($storage->current())); | ||
|
||
try { | ||
echo (new Example())->test(), PHP_EOL; | ||
} finally { | ||
$scope->detach(); | ||
} | ||
|
||
|
||
function hookManager(): HookManager { | ||
foreach (ServiceLoader::load(HookManager::class) as $hookManager) { | ||
return $hookManager; | ||
} | ||
|
||
return new NoopHookManager(); | ||
} | ||
|
||
function parseInstrumentationConfig(string $file): ComponentPlugin { | ||
// TODO Include in SDK config? | ||
return (new ConfigurationFactory( | ||
ServiceLoader::load(ComponentProvider::class), | ||
new class implements ComponentProvider { | ||
|
||
/** | ||
* @param array{ | ||
* config: list<ComponentPlugin<InstrumentationConfiguration>>, | ||
* } $properties | ||
*/ | ||
public function createPlugin(array $properties, Context $context): ConfigurationRegistry { | ||
$configurationRegistry = new ConfigurationRegistry(); | ||
foreach ($properties['config'] as $configuration) { | ||
$configurationRegistry->add($configuration->create($context)); | ||
} | ||
|
||
return $configurationRegistry; | ||
} | ||
|
||
public function getConfig(ComponentProviderRegistry $registry): ArrayNodeDefinition { | ||
$root = new ArrayNodeDefinition('instrumentation'); | ||
$root | ||
->children() | ||
// TODO add disabled_instrumentations arrayNode to allow disabling specific instrumentation classes? | ||
->append($registry->componentList('config', InstrumentationConfiguration::class)) | ||
->end() | ||
; | ||
|
||
return $root; | ||
} | ||
}, | ||
new EnvSourceReader([ | ||
new ServerEnvSource(), | ||
new PhpIniEnvSource(), | ||
]), | ||
))->parseFile($file); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
config: | ||
- example_instrumentation: | ||
span_name: ${EXAMPLE_INSTRUMENTATION_SPAN_NAME:-example span} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
file_format: '0.1' | ||
|
||
tracer_provider: | ||
processors: | ||
- simple: | ||
exporter: | ||
console: {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<?php declare(strict_types=1); | ||
namespace OpenTelemetry\Example; | ||
|
||
final class Example { | ||
|
||
public function test(): int { | ||
return 42; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<?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, | ||
) {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<?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 { | ||
|
||
/** | ||
* @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'], | ||
); | ||
} | ||
|
||
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
<?php declare(strict_types=1); | ||
namespace OpenTelemetry\Example; | ||
|
||
use Exception; | ||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\ConfigurationRegistry; | ||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\HookManager; | ||
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Instrumentation; | ||
use OpenTelemetry\API\Trace\Span; | ||
use OpenTelemetry\Config\SDK\Configuration\Context; | ||
use OpenTelemetry\Context\ContextStorageInterface; | ||
|
||
final class ExampleInstrumentation implements Instrumentation { | ||
|
||
public function register(HookManager $hookManager, Context $context, ConfigurationRegistry $configuration, ContextStorageInterface $storage): 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, $storage): void { | ||
$context = $storage->current(); | ||
|
||
$span = $tracer | ||
->spanBuilder($config->spanName) | ||
->setParent($context) | ||
->startSpan(); | ||
|
||
$storage->attach($span->storeInContext($context)); | ||
}, | ||
static function() use ($storage): void { | ||
if (!$scope = $storage->scope()) { | ||
return; | ||
} | ||
|
||
$scope->detach(); | ||
|
||
$span = Span::fromContext($scope->context()); | ||
$span->end(); | ||
} | ||
); | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
src/API/Instrumentation/AutoInstrumentation/ConfigurationRegistry.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?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; | ||
} | ||
} |
63 changes: 63 additions & 0 deletions
63
src/API/Instrumentation/AutoInstrumentation/ExtensionHookManager.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
<?php declare(strict_types=1); | ||
namespace OpenTelemetry\API\Instrumentation\AutoInstrumentation; | ||
|
||
use Closure; | ||
use Nevay\SPI\ServiceProviderDependency\ExtensionDependency; | ||
use OpenTelemetry\Context\Context; | ||
use OpenTelemetry\Context\ContextKeyInterface; | ||
use ReflectionFunction; | ||
use function assert; | ||
use function extension_loaded; | ||
|
||
#[ExtensionDependency('opentelemetry', '^1.0')] | ||
final class ExtensionHookManager implements HookManager { | ||
|
||
private readonly ContextKeyInterface $contextKey; | ||
|
||
public function __construct() { | ||
$this->contextKey = Context::createKey(self::class); | ||
} | ||
|
||
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)); | ||
} | ||
|
||
public function enable(Context $context): Context { | ||
return $context->with($this->contextKey, true); | ||
} | ||
|
||
public function disable(Context $context): Context { | ||
return $context->with($this->contextKey, null); | ||
} | ||
|
||
private function bindHookScope(?Closure $closure): ?Closure { | ||
if (!$closure) { | ||
return null; | ||
} | ||
|
||
$contextKey = $this->contextKey; | ||
$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, $contextKey): void { | ||
if (!Context::getCurrent()->get($contextKey)) { | ||
return; | ||
} | ||
|
||
$closure(...$args); | ||
}; | ||
} | ||
|
||
return static function(mixed ...$args) use ($closure, $contextKey): mixed { | ||
if (!Context::getCurrent()->get($contextKey)) { | ||
return null; | ||
} | ||
|
||
return $closure(...$args); | ||
}; | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
src/API/Instrumentation/AutoInstrumentation/HookManager.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?php declare(strict_types=1); | ||
namespace OpenTelemetry\API\Instrumentation\AutoInstrumentation; | ||
|
||
use Closure; | ||
use OpenTelemetry\Context\Context; | ||
use Throwable; | ||
|
||
interface HookManager { | ||
|
||
/** | ||
* @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; | ||
|
||
public function enable(Context $context): Context; | ||
|
||
public function disable(Context $context): Context; | ||
} |
10 changes: 10 additions & 0 deletions
10
src/API/Instrumentation/AutoInstrumentation/Instrumentation.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?php declare(strict_types=1); | ||
namespace OpenTelemetry\API\Instrumentation\AutoInstrumentation; | ||
|
||
use OpenTelemetry\Config\SDK\Configuration\Context; | ||
use OpenTelemetry\Context\ContextStorageInterface; | ||
|
||
interface Instrumentation { | ||
|
||
public function register(HookManager $hookManager, Context $context, ConfigurationRegistry $configuration, ContextStorageInterface $storage): void; | ||
} |
6 changes: 6 additions & 0 deletions
6
src/API/Instrumentation/AutoInstrumentation/InstrumentationConfiguration.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
<?php declare(strict_types=1); | ||
namespace OpenTelemetry\API\Instrumentation\AutoInstrumentation; | ||
|
||
interface InstrumentationConfiguration { | ||
|
||
} |
20 changes: 20 additions & 0 deletions
20
src/API/Instrumentation/AutoInstrumentation/NoopHookManager.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?php declare(strict_types=1); | ||
namespace OpenTelemetry\API\Instrumentation\AutoInstrumentation; | ||
|
||
use Closure; | ||
use OpenTelemetry\Context\Context; | ||
|
||
final class NoopHookManager implements HookManager { | ||
|
||
public function hook(?string $class, string $function, ?Closure $preHook = null, ?Closure $postHook = null): void { | ||
// no-op | ||
} | ||
|
||
public function enable(Context $context): Context { | ||
return $context; | ||
} | ||
|
||
public function disable(Context $context): Context { | ||
return $context; | ||
} | ||
} |