diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py index 602b105ca7..53c9cb79c2 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py @@ -491,6 +491,18 @@ def _import_id_generator(id_generator_name: str) -> IdGenerator: raise RuntimeError(f"{id_generator_name} is not an IdGenerator") +def _import_opamp() -> callable[[...], None]: + # this in development, at the moment we are looking for a callable that takes + # the resource and instantiate an OpAMP agent. + # Since configuration is not specified every implementors may have its own. + # Refer to opentelemetry-opamp-client package on how to setup the OpAMP agent. + _, opamp_init_func = _import_config_components( + ["init_function"], "_opentelemetry_opamp" + )[0] + + return opamp_init_func + + def _initialize_components( auto_instrumentation_version: str | None = None, trace_exporter_names: list[str] | None = None, @@ -543,6 +555,17 @@ def _initialize_components( # from the env variable else defaults to "unknown_service" resource = Resource.create(resource_attributes) + # OpAMP is a system created to configure OpenTelemetry SDKs with a remote config. + # This is different than other init helpers because setting up OpAMP requires distro + # provided code as it's not strictly specified. We call OpAMP init before other code + # because people may want to have it blocking to get an updated config before setting + # up the rest of the SDK. + try: + _init_opamp = _import_opamp() + _init_opamp(resource=resource) + except RuntimeError: + _logger.debug("No OpAMP init function found") + _init_tracing( exporters=span_exporters, id_generator=id_generator, diff --git a/opentelemetry-sdk/tests/test_configurator.py b/opentelemetry-sdk/tests/test_configurator.py index 333494df74..1dddc224f2 100644 --- a/opentelemetry-sdk/tests/test_configurator.py +++ b/opentelemetry-sdk/tests/test_configurator.py @@ -1438,3 +1438,33 @@ def f(x): self.assertEqual(logging.config.dictConfig.__name__, "dictConfig") self.assertEqual(logging.basicConfig.__name__, "basicConfig") self.assertEqual(logging.config.fileConfig.__name__, "fileConfig") + + +class TestOpAMPInit(TestCase): + @patch("opentelemetry.sdk._configuration.entry_points") + @patch("opentelemetry.sdk._configuration.Resource") + def test_init_function_found(self, mock_resource, mock_entry_points): + init_function = mock.Mock() + mock_entry_points.configure_mock( + return_value=[IterEntryPoint("init_function", init_function)] + ) + + _initialize_components(id_generator=1) + + mock_entry_points.assert_has_calls( + [mock.call(group="_opentelemetry_opamp", name="init_function")] + ) + init_function.assert_called_once_with( + resource=mock_resource.create.return_value + ) + + @patch("opentelemetry.sdk._configuration.entry_points") + def test_init_function_not_found(self, mock_entry_points): + mock_entry_points.configure_mock(return_value=[]) + + with self.assertLogs(level="DEBUG") as cm: + _initialize_components(id_generator=1) + self.assertIn( + "DEBUG:opentelemetry.sdk._configuration:No OpAMP init function found", + cm.output, + )