Repository: open-telemetry/opentelemetry-php (php)

Issue:
Add OTEL_PHP_DETECTORS environment variable
Add OTel PHP specific OTEL_PHP_DETECTORS environment variable to set up a given comma-separated list of detectors.

Pull request:
Added OTEL_PHP_DETECTORS environment variable
Resource detectors can be configured by an environment variable named `OTEL_PHP_DETECTORS`. By default, all resource detectors will be used to get resource info. The type of variable is List and known values are "all, env, host, os, process, process_runtime, sdk, sdk_provided, none". The order of priority is as follows:

1. If "all" is present, it will return resource info from all detectors and won't iterate the list of values.
2. If "all" is not present, it will iterate the list and add new instances of detectors(could raise multiple instances of the same detector but merge will handle duplicate resource info)
3. For "none", specific code is not written. If the list doesn't have "all" or any of the detectors in the list, it will return the equivalent of an empty resource.

"all" > any of the detectors > "none"

Fixes #584.

Patch:
diff --git a/examples/AirGappedTraceDebugging.php b/examples/AirGappedTraceDebugging.php
index 2f39a67c..d4592926 100644
--- a/examples/AirGappedTraceDebugging.php
+++ b/examples/AirGappedTraceDebugging.php
@@ -1,7 +1,6 @@
 <?php
 
 declare(strict_types=1);
-
 require __DIR__ . '/../vendor/autoload.php';
 
 /**
diff --git a/examples/LoggingOfSpanData.php b/examples/LoggingOfSpanData.php
index 3d2ffc83..4a195ca8 100644
--- a/examples/LoggingOfSpanData.php
+++ b/examples/LoggingOfSpanData.php
@@ -1,7 +1,6 @@
 <?php
 
 declare(strict_types=1);
-
 require __DIR__ . '/../vendor/autoload.php';
 
 /**
diff --git a/examples/ParentSpanExample.php b/examples/ParentSpanExample.php
index 8987bd5e..81dd9a0a 100644
--- a/examples/ParentSpanExample.php
+++ b/examples/ParentSpanExample.php
@@ -1,7 +1,6 @@
 <?php
 
 declare(strict_types=1);
-
 require __DIR__ . '/../vendor/autoload.php';
 
 use GuzzleHttp\Client;
diff --git a/examples/ResourceDetectors.php b/examples/ResourceDetectors.php
new file mode 100644
index 00000000..4c1cfa81
--- /dev/null
+++ b/examples/ResourceDetectors.php
@@ -0,0 +1,20 @@
+<?php
+
+declare(strict_types=1);
+require __DIR__ . '/../vendor/autoload.php';
+
+use OpenTelemetry\SDK\Trace\TracerProviderFactory;
+
+putenv('OTEL_PHP_DETECTORS=env,sdk,sdk_provided');
+
+echo 'Handling Resource Detectors From Environment' . PHP_EOL;
+
+$tracerProvider = (new TracerProviderFactory('example'))->create();
+
+$tracer = $tracerProvider->getTracer();
+
+echo 'Starting Tracer' . PHP_EOL;
+
+$rootSpan = $tracer->spanBuilder('root')->startSpan();
+$rootSpan->activate();
+$rootSpan->end();
diff --git a/src/SDK/Common/Environment/Defaults.php b/src/SDK/Common/Environment/Defaults.php
index da6034d9..fda1624f 100644
--- a/src/SDK/Common/Environment/Defaults.php
+++ b/src/SDK/Common/Environment/Defaults.php
@@ -100,4 +100,5 @@ interface Defaults
      * @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#language-specific-environment-variables
      */
     public const OTEL_PHP_TRACES_PROCESSOR = 'batch';
+    public const OTEL_PHP_DETECTORS = 'all';
 }
diff --git a/src/SDK/Common/Environment/KnownValues.php b/src/SDK/Common/Environment/KnownValues.php
index 1c83c7f0..eef33526 100644
--- a/src/SDK/Common/Environment/KnownValues.php
+++ b/src/SDK/Common/Environment/KnownValues.php
@@ -164,4 +164,22 @@ interface KnownValues
         self::VALUE_NOOP,
         self::VALUE_NONE,
     ];
+    public const VALUE_DETECTORS_ENVIRONMENT = 'env';
+    public const VALUE_DETECTORS_HOST = 'host';
+    public const VALUE_DETECTORS_OS = 'os';
+    public const VALUE_DETECTORS_PROCESS = 'process';
+    public const VALUE_DETECTORS_PROCESS_RUNTIME = 'process_runtime';
+    public const VALUE_DETECTORS_SDK = 'sdk';
+    public const VALUE_DETECTORS_SDK_PROVIDED = 'sdk_provided';
+    public const OTEL_PHP_DETECTORS = [
+        self::VALUE_ALL,
+        self::VALUE_DETECTORS_ENVIRONMENT,
+        self::VALUE_DETECTORS_HOST,
+        self::VALUE_DETECTORS_OS,
+        self::VALUE_DETECTORS_PROCESS,
+        self::VALUE_DETECTORS_PROCESS_RUNTIME,
+        self::VALUE_DETECTORS_SDK,
+        self::VALUE_DETECTORS_SDK_PROVIDED,
+        self::VALUE_NONE,
+    ];
 }
diff --git a/src/SDK/Common/Environment/ValueTypes.php b/src/SDK/Common/Environment/ValueTypes.php
index 6fdce678..b7184c25 100644
--- a/src/SDK/Common/Environment/ValueTypes.php
+++ b/src/SDK/Common/Environment/ValueTypes.php
@@ -118,4 +118,5 @@ interface ValueTypes
      * @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#language-specific-environment-variables
      */
     public const OTEL_PHP_TRACES_PROCESSOR = VariableTypes::ENUM;
+    public const OTEL_PHP_DETECTORS = VariableTypes::LIST;
 }
diff --git a/src/SDK/Common/Environment/Variables.php b/src/SDK/Common/Environment/Variables.php
index f80340ea..36bfed71 100644
--- a/src/SDK/Common/Environment/Variables.php
+++ b/src/SDK/Common/Environment/Variables.php
@@ -118,4 +118,5 @@ interface Variables
      * @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#language-specific-environment-variables
      */
     public const OTEL_PHP_TRACES_PROCESSOR = 'OTEL_PHP_TRACES_PROCESSOR';
+    public const OTEL_PHP_DETECTORS = 'OTEL_PHP_DETECTORS';
 }
diff --git a/src/SDK/Resource/ResourceInfo.php b/src/SDK/Resource/ResourceInfo.php
index ab48e5da..1c70f9e2 100644
--- a/src/SDK/Resource/ResourceInfo.php
+++ b/src/SDK/Resource/ResourceInfo.php
@@ -4,8 +4,12 @@ declare(strict_types=1);
 
 namespace OpenTelemetry\SDK\Resource;
 
+use function in_array;
 use OpenTelemetry\SDK\Attributes;
 use OpenTelemetry\SDK\AttributesInterface;
+use OpenTelemetry\SDK\Common\Environment\Accessor;
+use OpenTelemetry\SDK\Common\Environment\KnownValues as Values;
+use OpenTelemetry\SDK\Common\Environment\Variables as Env;
 
 /**
  * A Resource is an immutable representation of the entity producing telemetry. For example, a process producing telemetry
@@ -51,17 +55,57 @@ class ResourceInfo
 
     public static function defaultResource(): self
     {
-        return (new Detectors\Composite([
-            new Detectors\Environment(),
+        $detectors = Accessor::getList(Env::OTEL_PHP_DETECTORS);
 
-            new Detectors\Host(),
-            new Detectors\OperatingSystem(),
-            new Detectors\Process(),
-            new Detectors\ProcessRuntime(),
+        if (in_array(Values::VALUE_ALL, $detectors)) {
+            return (new Detectors\Composite([
+                new Detectors\Environment(),
+                new Detectors\Host(),
+                new Detectors\OperatingSystem(),
+                new Detectors\Process(),
+                new Detectors\ProcessRuntime(),
+                new Detectors\Sdk(),
+                new Detectors\SdkProvided(),
+            ]))->getResource();
+        }
+
+        $resourceDetectors = [];
+
+        foreach ($detectors as $detector) {
+            switch ($detector) {
+                case Values::VALUE_DETECTORS_ENVIRONMENT:
+                    $resourceDetectors[] = new Detectors\Environment();
+
+                    break;
+                case Values::VALUE_DETECTORS_HOST:
+                    $resourceDetectors[] = new Detectors\Host();
+
+                    break;
+                case Values::VALUE_DETECTORS_OS:
+                    $resourceDetectors[] = new Detectors\OperatingSystem();
+
+                    break;
+                case Values::VALUE_DETECTORS_PROCESS:
+                    $resourceDetectors[] = new Detectors\Process();
+
+                    break;
+                case Values::VALUE_DETECTORS_PROCESS_RUNTIME:
+                    $resourceDetectors[] = new Detectors\ProcessRuntime();
+
+                    break;
+                case Values::VALUE_DETECTORS_SDK:
+                    $resourceDetectors[] = new Detectors\Sdk();
+
+                    break;
+                case Values::VALUE_DETECTORS_SDK_PROVIDED:
+                    $resourceDetectors[] = new Detectors\SdkProvided();
+
+                    break;
+                default:
+            }
+        }
 
-            new Detectors\Sdk(),
-            new Detectors\SdkProvided(),
-        ]))->getResource();
+        return (new Detectors\Composite($resourceDetectors))->getResource();
     }
 
     public static function emptyResource(): self


Test patch:
diff --git a/tests/Unit/SDK/Resource/ResourceTest.php b/tests/Unit/SDK/Resource/ResourceTest.php
index 74a87a05..25211bab 100644
--- a/tests/Unit/SDK/Resource/ResourceTest.php
+++ b/tests/Unit/SDK/Resource/ResourceTest.php
@@ -61,7 +61,7 @@ class ResourceTest extends TestCase
         $this->assertSame('test', $name);
     }
 
-    public function test_default_resource(): void
+    public function test_all_default_resources(): void
     {
         $resource = ResourceInfo::defaultResource();
 
@@ -89,6 +89,142 @@ class ResourceTest extends TestCase
         $this->assertEquals('unknown_service', $resource->getAttributes()->get(ResourceAttributes::SERVICE_NAME));
     }
 
+    public function test_none_default_resources(): void
+    {
+        $this->setEnvironmentVariable('OTEL_PHP_DETECTORS', 'none');
+
+        $resource = ResourceInfo::defaultResource();
+
+        $this->assertNull($resource->getSchemaUrl());
+
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::HOST_NAME));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::HOST_ARCH));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::OS_TYPE));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::OS_DESCRIPTION));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::OS_NAME));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::OS_VERSION));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_PID));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_EXECUTABLE_PATH));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_COMMAND));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_COMMAND_ARGS));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_RUNTIME_NAME));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_RUNTIME_VERSION));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::TELEMETRY_SDK_NAME));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::TELEMETRY_SDK_LANGUAGE));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::TELEMETRY_SDK_VERSION));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::SERVICE_NAME));
+    }
+
+    public function test_env_default_resources(): void
+    {
+        $this->setEnvironmentVariable('OTEL_PHP_DETECTORS', 'env');
+        $this->setEnvironmentVariable('OTEL_SERVICE_NAME', 'test-service');
+
+        $resource = ResourceInfo::defaultResource();
+
+        $this->assertSame(ResourceAttributes::SCHEMA_URL, $resource->getSchemaUrl());
+
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::HOST_NAME));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::HOST_ARCH));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::OS_TYPE));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::OS_DESCRIPTION));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::OS_NAME));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::OS_VERSION));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_PID));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_EXECUTABLE_PATH));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_COMMAND));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_COMMAND_ARGS));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_RUNTIME_NAME));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_RUNTIME_VERSION));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::TELEMETRY_SDK_NAME));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::TELEMETRY_SDK_LANGUAGE));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::TELEMETRY_SDK_VERSION));
+
+        $this->assertEquals('test-service', $resource->getAttributes()->get(ResourceAttributes::SERVICE_NAME));
+    }
+
+    public function test_os_and_host_default_resources(): void
+    {
+        $this->setEnvironmentVariable('OTEL_PHP_DETECTORS', 'os,host');
+
+        $resource = ResourceInfo::defaultResource();
+
+        $this->assertSame(ResourceAttributes::SCHEMA_URL, $resource->getSchemaUrl());
+
+        $this->assertNotNull($resource->getAttributes()->get(ResourceAttributes::HOST_NAME));
+        $this->assertNotNull($resource->getAttributes()->get(ResourceAttributes::HOST_ARCH));
+        $this->assertNotNull($resource->getAttributes()->get(ResourceAttributes::OS_TYPE));
+        $this->assertNotNull($resource->getAttributes()->get(ResourceAttributes::OS_DESCRIPTION));
+        $this->assertNotNull($resource->getAttributes()->get(ResourceAttributes::OS_NAME));
+        $this->assertNotNull($resource->getAttributes()->get(ResourceAttributes::OS_VERSION));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_PID));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_EXECUTABLE_PATH));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_COMMAND));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_COMMAND_ARGS));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_RUNTIME_NAME));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_RUNTIME_VERSION));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::TELEMETRY_SDK_NAME));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::TELEMETRY_SDK_LANGUAGE));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::TELEMETRY_SDK_VERSION));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::SERVICE_NAME));
+    }
+
+    public function test_process_and_process_runtime_default_resources(): void
+    {
+        $this->setEnvironmentVariable('OTEL_PHP_DETECTORS', 'process,process_runtime');
+
+        $resource = ResourceInfo::defaultResource();
+
+        $this->assertSame(ResourceAttributes::SCHEMA_URL, $resource->getSchemaUrl());
+
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::HOST_NAME));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::HOST_ARCH));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::OS_TYPE));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::OS_DESCRIPTION));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::OS_NAME));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::OS_VERSION));
+        $this->assertNotNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_PID));
+        $this->assertNotNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_EXECUTABLE_PATH));
+        $this->assertNotNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_COMMAND));
+        $this->assertNotNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_COMMAND_ARGS));
+        $this->assertNotNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_RUNTIME_NAME));
+        $this->assertNotNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_RUNTIME_VERSION));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::TELEMETRY_SDK_NAME));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::TELEMETRY_SDK_LANGUAGE));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::TELEMETRY_SDK_VERSION));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::SERVICE_NAME));
+    }
+
+    public function test_sdk_and_sdk_provided_default_resources(): void
+    {
+        $this->setEnvironmentVariable('OTEL_PHP_DETECTORS', 'sdk,sdk_provided');
+
+        $resource = ResourceInfo::defaultResource();
+
+        $this->assertSame(ResourceAttributes::SCHEMA_URL, $resource->getSchemaUrl());
+
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::HOST_NAME));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::HOST_ARCH));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::OS_TYPE));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::OS_DESCRIPTION));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::OS_NAME));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::OS_VERSION));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_PID));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_EXECUTABLE_PATH));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_COMMAND));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_COMMAND_ARGS));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_RUNTIME_NAME));
+        $this->assertNull($resource->getAttributes()->get(ResourceAttributes::PROCESS_RUNTIME_VERSION));
+        $this->assertNotNull($resource->getAttributes()->get(ResourceAttributes::TELEMETRY_SDK_NAME));
+        $this->assertNotNull($resource->getAttributes()->get(ResourceAttributes::TELEMETRY_SDK_LANGUAGE));
+        $this->assertNotNull($resource->getAttributes()->get(ResourceAttributes::TELEMETRY_SDK_VERSION));
+        $this->assertNotNull($resource->getAttributes()->get(ResourceAttributes::SERVICE_NAME));
+
+        $this->assertEquals('opentelemetry', $resource->getAttributes()->get(ResourceAttributes::TELEMETRY_SDK_NAME));
+        $this->assertEquals('php', $resource->getAttributes()->get(ResourceAttributes::TELEMETRY_SDK_LANGUAGE));
+        $this->assertEquals('unknown_service', $resource->getAttributes()->get(ResourceAttributes::SERVICE_NAME));
+    }
+
     public function test_merge(): void
     {
         $primary = ResourceInfo::create(new Attributes(['name' => 'primary', 'empty' => '']));