Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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]:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

callable is a builtin function, not a valid type, right?

I think this should be Callable[..., None] using collections.abc. You'll need to add it to the imports.

Suggested change
def _import_opamp() -> callable[[...], None]:
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(
Copy link
Copy Markdown
Member

@pmcollins pmcollins Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to read and handle the entry point directly here rather than use _import_config_components? (it throws an exception if there's no match).

["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,
Expand Down Expand Up @@ -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
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my distro I initialize the OpAMP client after the sdk has been setup but can't exclude other scenarios

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add this a SIG topic to get the execution order?

# 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)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible for _init_opamp to raise a RuntimeError? If so, it could be slightly confusing as the logs would say no init function found instead of the actual error.
Also, is it possible for it to throw other types of exceptions? Would we want to catch more broadly to ensure we don't prevent the rest from initializing?

except RuntimeError:
_logger.debug("No OpAMP init function found")

_init_tracing(
exporters=span_exporters,
id_generator=id_generator,
Expand Down
30 changes: 30 additions & 0 deletions opentelemetry-sdk/tests/test_configurator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Loading