Exporters not loaded when using auto root span
Describe your environment
- Run https://github.com/open-telemetry/opentelemetry-php/blob/main/examples/traces/features/auto_root_span.php with
OTEL_TRACES_EXPORTERset tootlp
Steps to reproduce
- Add the auto_root_span.php example script to the public dir of my project
- Run it with the
php -Scommand from the script - Change the putenv
OTEL_TRACES_EXPORTERtootlp - Then curl to the page I see the following logs in the console:
What is the expected behavior? What did you expect to see? It either:
- Working
- Giving errors about no OTLP exporter config being added
What is the actual behavior? It can't load the OLTP exporter as a whole
$ php -S localhost:8080 test_otel.php
[Thu Oct 17 08:56:52 2024] PHP 8.3.12 Development Server (http://localhost:8080) started
[Thu Oct 17 08:56:55 2024] [::1]:44470 Accepted
[Thu Oct 17 08:56:55 2024] OpenTelemetry: [warning] Error during opentelemetry initialization: Span exporter factory not defined for: otlp
#0 /my_project_location/vendor/open-telemetry/sdk/Trace/ExporterFactory.php(28): OpenTelemetry\SDK\Registry::spanExporterFactory()
#1 /my_project_location/vendor/open-telemetry/sdk/SdkAutoloader.php(87): OpenTelemetry\SDK\Trace\ExporterFactory->create()
#2 /my_project_location/vendor/open-telemetry/sdk/SdkAutoloader.php(62): OpenTelemetry\SDK\SdkAutoloader::environmentBasedInitializer()
#3 /my_project_location/vendor/open-telemetry/api/Globals.php(93): OpenTelemetry\SDK\SdkAutoloader::OpenTelemetry\SDK\{closure}()
#4 /my_project_location/vendor/open-telemetry/api/Globals.php(43): OpenTelemetry\API\Globals::globals()
#5 /my_project_location/vendor/open-telemetry/sdk/Trace/AutoRootSpan.php(39): OpenTelemetry\API\Globals::tracerProvider()
#6 /my_project_location/vendor/open-telemetry/sdk/SdkAutoloader.php(69): OpenTelemetry\SDK\Trace\AutoRootSpan::create()
#7 /my_project_location/vendor/open-telemetry/sdk/_autoload.php(5): OpenTelemetry\SDK\SdkAutoloader::autoload()
#8 /my_project_location/vendor/composer/autoload_real.php(43): require('...')
#9 /my_project_location/vendor/composer/autoload_real.php(47): {closure}()
#10 /my_project_location/vendor/autoload.php(25): ComposerAutoloaderInit075534cb7854c559e471b4339a4e7714::getLoader()
#11 /my_project_location/public/test_otel.php(26): require('...')
#12 {main} in /my_project_location/vendor/open-telemetry/api/Globals.php(95)
[Thu Oct 17 08:56:55 2024] [::1]:44470 Closing
Additional notes?
- Initial discussion: https://cloud-native.slack.com/archives/C01NFPCV44V/p1729091207010589
From my debugging this happens because: The auto root span _register.php is loader earlier in the chain then the _register.php of the oltp exporter, not sure if it's alphabetical or something else in composer? The auto root span kicks of everything in the sdk package, and then also tries to set up the tracer The tracer has an in memory list of registered exporters, which is usually filled through the _register.php scripts. As that didn't happen yet, the exporter does not exist (yet)
If it were to used as a class, e.g. a list of predefined FQCN's then it could work since it would kick in the class loader in the background.
So you either need to find a way to chain these dependencies (don't think there is a composer way), or, from the tracer factory stuffs, directly depend on a list of predefined classes as a fallback. That could work, but will give some overhead if no authoritative classmap is used
Hi @xvilo . Has there been any update/workaround regarding this issue?
Uhm, work is being done on this so it isn’t an issue anymore. (https://github.com/open-telemetry/opentelemetry-php/pull/1412) Another work around for now would be to change the order of the dependencies in your composer.json, and then to dump the autoloader again
This is how I solved this for Drupal:
https://github.com/LionsAd/opentelemetry-auto-drupal/blob/drupal-instrumentation--mega-pack/src/DrupalAutoRootSpan.php#L19-L25
as long as you have a class, which is constructed way early after the autoloaded classes, you can create the auto root span there.
My current workaround to avoid the race is this:
Add a file to the project with the following content (name it something like _autoload-otel.php):
<?php
declare(strict_types=1);
use OpenTelemetry\SDK\SdkAutoloader;
if (PHP_SAPI !== 'cli') {
$_SERVER['OTEL_PHP_AUTOLOAD_ENABLED'] = 'true';
SdkAutoloader::autoload();
}
Then add the path to this file to the projects composer.json as an autoload file:
{
[...]
"autoload": {
"files": [
"_autoload-otel.php"
]
},
[...]
}
Note that project autoload files are loaded after dependency autoload files. As a result, when this file is interpreted, all otel extensions had the chance to register themselves. The important thing here is that OTEL_PHP_AUTOLOAD_ENABLED must not be present in the process environment.
Projects which need a way to switch this mechanism on and off just may introduce their own environment variable and check that in the if condition.
The PHP_SAPI condition makes this work with the built-in PHP server. If nobody on the project team is using the built-in server, then that check could maybe left out.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
is there a way to disable the stale issue not on this?
This issue has been automatically closed because it has not had recent activity, but it can be reopened. Thank you for your contributions.