Skip to content

feat(otel-hook): autoload registration of otel hook #25

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Dec 28, 2022
6 changes: 6 additions & 0 deletions hooks/OpenTelemetry/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ OpenTelemetry is an open specification for distributed tracing, metrics, and log

This package also builds on various PSRs (PHP Standards Recommendations) such as the Logger interfaces (PSR-3) and the Basic and Extended Coding Standards (PSR-1 and PSR-12).

### Autoloading

This package supports Composer autoloading. Thus, simply installing the package is all you need in order to immediately get started with OpenTracing for OpenFeature! Examples are provided that showcase the simple setup as well. Check out the Usage section for more info.

### OpenTelemetry Package Status

The OpenTelemetry package for PHP is still in beta, so there could be changes required. However, it exposes global primitives for span retrieval that should not require any configuration upfront for the provider to just work.
Expand All @@ -37,6 +41,8 @@ OpenTelemetryHook::register();

For more information on OpenTelemetry, check out [their documentation](https://opentelemetry.io/docs/instrumentation/php/).

For more examples, see the [examples](./examples/).

## Development

### PHP Versioning
Expand Down
9 changes: 6 additions & 3 deletions hooks/OpenTelemetry/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
],
"require": {
"php": "^7.4 || ^8",
"open-feature/sdk": "^1.1.0",
"open-feature/sdk": "^1.2.0",
"open-telemetry/api": "^0.0.17"
},
"require-dev": {
Expand Down Expand Up @@ -54,7 +54,10 @@
"autoload": {
"psr-4": {
"OpenFeature\\Hooks\\OpenTelemetry\\": "src"
}
},
"files": [
"src/_autoload.php"
]
},
"autoload-dev": {
"psr-4": {
Expand Down Expand Up @@ -109,7 +112,7 @@
"dev:test:unit:teardown": "echo 'Tore down for unit tests...'",
"dev:test:integration": [
"@dev:test:integration:setup",
"echo 'No integration tests to run'",
"phpunit --colors=always --testdox --testsuite=integration",
"@dev:test:integration:teardown"
],
"dev:test:integration:debug": "phpunit --colors=always --testdox -d xdebug.profiler_enable=on",
Expand Down
5 changes: 5 additions & 0 deletions hooks/OpenTelemetry/examples/AutoloadOTelSDK/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# OpenFeature OpenTelemetry Hook example

This example provides an example of bootstrapping and using the OpenTelemetry hook for OpenFeature.

It showcases the auto-instrumentation functionality of OpenFeature and OpenTelemetry, neither of which requiring imperative interactions with OTel to bootstrap, configure, and utilize it.
16 changes: 16 additions & 0 deletions hooks/OpenTelemetry/examples/AutoloadOTelSDK/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "open-feature/otel-manual-instrumentation-example",
"description": "An example of using the OpenTelemetry hook for OpenFeature with manual instrumentation",
"type": "project",
"license": "Apache-2.0",
"authors": [
{
"name": "Tom Carrio",
"email": "[email protected]"
}
],
"require": {
"open-telemetry/api": "0.0.17",
"open-feature/sdk": "^1.2.0"
}
}
25 changes: 25 additions & 0 deletions hooks/OpenTelemetry/examples/AutoloadOTelSDK/src/main.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

use OpenFeature\OpenFeatureAPI;

putenv('OTEL_PHP_AUTOLOAD_ENABLED=true');
putenv('OTEL_TRACES_EXPORTER=otlp');
putenv('OTEL_EXPORTER_OTLP_PROTOCOL=grpc');
putenv('OTEL_METRICS_EXPORTER=otlp');
putenv('OTEL_EXPORTER_OTLP_METRICS_PROTOCOL=grpc');
putenv('OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4317');
putenv('OTEL_PHP_TRACES_PROCESSOR=batch');
putenv('OTEL_PROPAGATORS=b3,baggage,tracecontext');

echo 'autoloading SDK example starting...' . PHP_EOL;

// Composer autoloader will execute SDK/_autoload.php which will register global instrumentation from environment configuration
require dirname(__DIR__) . '/vendor/autoload.php';

$client = OpenFeatureAPI::getInstance()->getClient('dev.openfeature.contrib.php.demo', '1.0.0');

$version = $client->getStringValue('dev.openfeature.contrib.php.version-value', 'unknown');

echo 'Version is ' . $version;
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# OpenFeature OpenTelemetry Hook example

This example provides an example of bootstrapping and using the OpenTelemetry hook for OpenFeature.

It showcases how you can manually configure the OTel SDK for metrics, tracing, and more. This is then utilized under the hood by OpenFeature within its hook lifecycles to report the feature flag semantic events via the tracer.
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@
"description": "An example of using the OpenTelemetry hook for OpenFeature with manual instrumentation",
"type": "project",
"license": "Apache-2.0",
"autoload": {
"psr-4": {
"OpenFeature\\Providers\\Examples\\OTelManualInstrumentation\\": "src/"
}
},
"authors": [
{
"name": "Tom Carrio",
Expand All @@ -22,6 +17,6 @@
"php-di/slim-bridge": "^3.2",
"open-telemetry/api": "0.0.17",
"open-telemetry/sdk": "0.0.17",
"open-feature/sdk": "^1.1"
"open-feature/sdk": "^1.2.0"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Slim\Routing\RouteContext;

// Registering the OTel hook requires only the following!
// Manually registering the OTel hook requires only the following!
// The rest of the work is simply using OpenFeature as you normally would
// The current context span will be used to emit trace events.
OpenTelemetryHook::register();
Expand Down
19 changes: 18 additions & 1 deletion hooks/OpenTelemetry/src/OpenTelemetryHook.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public static function register(): void
self::$instance = new OpenTelemetryHook();
}

if (self::$registeredHook) {
if (self::$registeredHook && self::isRegisteredInHooks()) {
return;
}

Expand Down Expand Up @@ -88,4 +88,21 @@ public function supportsFlagValueType(string $flagValueType): bool
{
return true;
}

/**
* Hooks can be cleared by other means so we can't simply memoize whether a registration has occurred
*
* However if no registration has yet happened then we can absolutely determine that the hook will
* not be registered yet.
*/
private static function isRegisteredInHooks(): bool
{
foreach (OpenFeatureAPI::getInstance()->getHooks() as $hook) {
if ($hook instanceof OpenTelemetryHook) {
return true;
}
}

return false;
}
}
6 changes: 6 additions & 0 deletions hooks/OpenTelemetry/src/_autoload.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php

declare(strict_types=1);

// automatically registers the OTel hook for OpenFeature
OpenFeature\Hooks\OpenTelemetry\OpenTelemetryHook::register();
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,37 @@

class OpenTelemetryHookTest extends TestCase
{
public function testAutoload(): void
{
// Given
$api = OpenFeatureAPI::getInstance();
$api->clearHooks();

// When
$this->simulateAutoload();

// Then

$this->assertCount(1, $api->getHooks());
$this->assertInstanceOf(Hook::class, $api->getHooks()[0]);
}

public function testCanBeRegistered(): void
{
// Given
$api = OpenFeatureAPI::getInstance();
$api->clearHooks();

// When
OpenTelemetryHook::register();

// Then
$this->assertNotEmpty($api->getHooks());
$this->assertCount(1, $api->getHooks());
$this->assertInstanceOf(Hook::class, $api->getHooks()[0]);
}

private function simulateAutoload(): void
{
require __DIR__ . '/../../src/_autoload.php';
}
}
2 changes: 1 addition & 1 deletion providers/CloudBees/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
],
"require": {
"php": "^7.4 || ^8",
"open-feature/sdk": "^1.0.0",
"open-feature/sdk": "^1.2.0",
"rollout/rox": "^5.0"
},
"require-dev": {
Expand Down
2 changes: 1 addition & 1 deletion providers/CloudBees/examples/CloudBees/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
}
],
"require": {
"open-feature/sdk": "^0.0.5",
"open-feature/sdk": "^1.2.0",
"open-feature/cloudbees-provider": "^0.0.1"
}
}
2 changes: 1 addition & 1 deletion providers/Flagd/examples/Grpc/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
}
],
"require": {
"open-feature/sdk": "^0.0.5",
"open-feature/sdk": "^1.2.0",
"monolog/monolog": "^2.8"
}
}
2 changes: 1 addition & 1 deletion providers/Flagd/examples/Http/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@
}
],
"require": {
"open-feature/sdk": "^0.0.5"
"open-feature/sdk": "^1.2.0"
}
}
2 changes: 1 addition & 1 deletion providers/Split/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
],
"require": {
"php": "^7.4 || ^8",
"open-feature/sdk": "^1.1.0",
"open-feature/sdk": "^1.2.0",
"splitsoftware/split-sdk-php": "^7.1"
},
"require-dev": {
Expand Down
2 changes: 1 addition & 1 deletion providers/Split/examples/SplitSDK/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
}
],
"require": {
"open-feature/sdk": "^0.0.5",
"open-feature/sdk": "^1.2.0",
"open-feature/split-provider": "^0.0.1"
}
}