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
1 change: 1 addition & 0 deletions .changelog/5363.changed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
`opentelemetry-sdk`: wire id_generator from declarative configuration to TracerProvider
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
from opentelemetry.sdk._configuration.models import (
ExperimentalOtlpFileExporter as ExperimentalOtlpFileExporterConfig,
)
from opentelemetry.sdk._configuration.models import (
IdGenerator as IdGeneratorConfig,
)
from opentelemetry.sdk._configuration.models import (
OtlpGrpcExporter as OtlpGrpcExporterConfig,
)
Expand Down Expand Up @@ -84,6 +87,7 @@
SimpleSpanProcessor,
SpanExporter,
)
from opentelemetry.sdk.trace.id_generator import IdGenerator, RandomIdGenerator
from opentelemetry.sdk.trace.sampling import (
ALWAYS_OFF,
ALWAYS_ON,
Expand Down Expand Up @@ -348,6 +352,24 @@ def _create_sampler(config: SamplerConfig) -> Sampler:
)


def _create_id_generator(config: IdGeneratorConfig) -> IdGenerator:
"""Create an IdGenerator from config.

Built-in ``random`` resolves to ``RandomIdGenerator``; unknown names
load from the ``opentelemetry_id_generator`` entry point group (the
same group ``OTEL_PYTHON_ID_GENERATOR`` uses today).
"""
if config.random is not None:
return RandomIdGenerator()
if config.additional_properties:
name = next(iter(config.additional_properties))
return load_entry_point("opentelemetry_id_generator", name)()
raise ConfigurationError(
"No id_generator type specified in config. "
"Supported built-in types: random."
)


def _create_parent_based_sampler(config: ParentBasedSamplerConfig) -> Sampler:
"""Create a ParentBased sampler from config, applying SDK defaults for absent delegates."""
root = (
Expand Down Expand Up @@ -431,6 +453,11 @@ def create_tracer_provider(
if config is not None and config.sampler is not None
else _DEFAULT_SAMPLER
)
id_generator = (
_create_id_generator(config.id_generator)
if config is not None and config.id_generator is not None
else None
)
span_limits = (
_create_span_limits(config.limits)
if config is not None and config.limits is not None
Expand All @@ -447,6 +474,7 @@ def create_tracer_provider(
resource=resource,
sampler=sampler,
span_limits=span_limits,
id_generator=id_generator,
)

if config is not None:
Expand Down
55 changes: 55 additions & 0 deletions opentelemetry-sdk/tests/_configuration/test_tracer_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
from opentelemetry.sdk._configuration.models import (
ExperimentalSpanParent as SpanParentConfig,
)
from opentelemetry.sdk._configuration.models import (
IdGenerator as IdGeneratorConfig,
)
from opentelemetry.sdk._configuration.models import (
OtlpGrpcExporter as OtlpGrpcExporterConfig,
)
Expand Down Expand Up @@ -82,6 +85,7 @@
ConsoleSpanExporter,
SimpleSpanProcessor,
)
from opentelemetry.sdk.trace.id_generator import RandomIdGenerator
from opentelemetry.sdk.trace.sampling import (
ALWAYS_OFF,
ALWAYS_ON,
Expand Down Expand Up @@ -852,3 +856,54 @@ def test_absent_limits_do_not_read_env_vars(self):
provider = self._create_with_limits(SpanLimitsConfig())
self.assertEqual(provider._span_limits.max_span_attributes, 128)
self.assertEqual(provider._span_limits.max_events, 128)


class TestCreateIdGenerator(unittest.TestCase):
"""Tests for _create_id_generator and id_generator wiring in create_tracer_provider."""

@staticmethod
def _make_provider(id_generator_config):
return create_tracer_provider(
TracerProviderConfig(
processors=[], id_generator=id_generator_config
)
)

def test_absent_id_generator_uses_sdk_default(self):
"""When id_generator is omitted, the SDK's default RandomIdGenerator is used."""
provider = create_tracer_provider(TracerProviderConfig(processors=[]))
self.assertIsInstance(provider.id_generator, RandomIdGenerator)

def test_builtin_random_id_generator(self):
"""Built-in 'random' id_generator resolves to RandomIdGenerator."""
provider = self._make_provider(IdGeneratorConfig(random={}))
self.assertIsInstance(provider.id_generator, RandomIdGenerator)

def test_plugin_id_generator_loaded_via_entry_point(self):
"""Unknown id_generator name is loaded from opentelemetry_id_generator entry point group."""
mock_generator = MagicMock()
mock_class = MagicMock(return_value=mock_generator)
with patch(
"opentelemetry.sdk._configuration._common.entry_points",
return_value=[MagicMock(**{"load.return_value": mock_class})],
):
# pylint: disable=unexpected-keyword-arg
provider = self._make_provider(
IdGeneratorConfig(my_custom_generator={})
)
self.assertIs(provider.id_generator, mock_generator)

def test_unknown_id_generator_raises_configuration_error(self):
"""Unknown id_generator name with no matching entry point raises ConfigurationError."""
with patch(
"opentelemetry.sdk._configuration._common.entry_points",
return_value=[],
):
with self.assertRaises(ConfigurationError):
# pylint: disable=unexpected-keyword-arg
self._make_provider(IdGeneratorConfig(no_such_generator={}))

def test_empty_id_generator_raises_configuration_error(self):
"""Empty IdGenerator config (no type specified) raises ConfigurationError."""
with self.assertRaises(ConfigurationError):
self._make_provider(IdGeneratorConfig())
Loading