Skip to content
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

- `opentelemetry-sdk`: add sampler plugin loading to declarative file configuration via the `opentelemetry_sampler` entry point group, matching the spec's PluginComponentProvider mechanism
([#5095](https://github.com/open-telemetry/opentelemetry-python/pull/5095))
- `opentelemetry-sdk`: add `additional_properties` support to generated config models via custom `datamodel-codegen` template, enabling plugin/custom component names to flow through typed dataclasses
([#5131](https://github.com/open-telemetry/opentelemetry-python/pull/5131))
- Fix incorrect code example in `create_tracer()` docstring
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
from typing import Optional

from opentelemetry import trace
from opentelemetry.sdk._configuration._common import _parse_headers
from opentelemetry.sdk._configuration._common import (
_parse_headers,
load_entry_point,
)
from opentelemetry.sdk._configuration._exceptions import ConfigurationError
from opentelemetry.sdk._configuration.models import (
OtlpGrpcExporter as OtlpGrpcExporterConfig,
Expand Down Expand Up @@ -184,7 +187,13 @@ def _create_span_processor(


def _create_sampler(config: SamplerConfig) -> Sampler:
"""Create a sampler from config."""
"""Create a sampler from config.

Known sampler types are checked via typed fields on the Sampler
dataclass. Unknown sampler names captured in additional_properties
by the @_additional_properties decorator are loaded via the
``opentelemetry_sampler`` entry point group.
"""
if config.always_on is not None:
return ALWAYS_ON
if config.always_off is not None:
Expand All @@ -194,6 +203,9 @@ def _create_sampler(config: SamplerConfig) -> Sampler:
return TraceIdRatioBased(ratio if ratio is not None else 1.0)
if config.parent_based is not None:
return _create_parent_based_sampler(config.parent_based)
if config.additional_properties:
name = next(iter(config.additional_properties))
return load_entry_point("opentelemetry_sampler", name)()
raise ConfigurationError(
f"Unknown or unsupported sampler type in config: {config!r}. "
"Supported types: always_on, always_off, trace_id_ratio_based, parent_based."
Expand Down
25 changes: 22 additions & 3 deletions opentelemetry-sdk/tests/_configuration/test_tracer_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
ALWAYS_OFF,
ALWAYS_ON,
ParentBased,
Sampler,
TraceIdRatioBased,
)

Expand Down Expand Up @@ -219,9 +220,27 @@ def test_parent_based_with_delegate_samplers(self):

def test_unknown_sampler_raises_configuration_error(self):
with self.assertRaises(ConfigurationError):
create_tracer_provider(
TracerProviderConfig(processors=[], sampler=SamplerConfig())
)
self._make_provider(SamplerConfig())

def test_plugin_sampler_loaded_via_entry_point(self):
mock_sampler = MagicMock(spec=Sampler)
mock_class = MagicMock(return_value=mock_sampler)
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(SamplerConfig(my_custom_sampler={}))
self.assertIs(provider.sampler, mock_sampler)

def test_unknown_plugin_raises_configuration_error(self):
with patch(
"opentelemetry.sdk._configuration._common.entry_points",
return_value=[],
):
with self.assertRaises(ConfigurationError):
# pylint: disable=unexpected-keyword-arg
self._make_provider(SamplerConfig(no_such_sampler={}))


class TestCreateSpanExporterAndProcessor(unittest.TestCase):
Expand Down
Loading