Skip to content

feat(config): sampler plugin loading via entry points for declarative config#5095

Open
MikeGoldsmith wants to merge 4 commits intoopen-telemetry:mainfrom
MikeGoldsmith:mike/config-sampler-plugin-loading
Open

feat(config): sampler plugin loading via entry points for declarative config#5095
MikeGoldsmith wants to merge 4 commits intoopen-telemetry:mainfrom
MikeGoldsmith:mike/config-sampler-plugin-loading

Conversation

@MikeGoldsmith
Copy link
Copy Markdown
Member

@MikeGoldsmith MikeGoldsmith commented Apr 14, 2026

Depends on #5093 (shared load_entry_point utility). Best reviewed after that merges.

Description

Extends sampler support in declarative file configuration to handle custom (plugin) samplers, matching the spec's PluginComponentProvider mechanism.

Background

Python's typed dataclass models (generated from JSON Schema) drop unknown keys. A YAML key like my_custom_sampler: {} would be silently lost before reaching the factory. Other SDKs avoid this by working with non-typed structs/maps, which preserve the unknown key as the plugin name.

Solution

Sampler and ParentBasedSampler are changed from @dataclass to TypeAlias = dict[str, Any] in models.py. This reflects how the loader already stores nested fields (raw dicts via OpenTelemetryConfiguration(**data) with no recursive conversion), and means the plugin name is preserved as a dict key through the pipeline.

_create_sampler() now has a single code path: extract the one key as the sampler name, look it up in _SAMPLER_REGISTRY (known names: always_on, always_off, trace_id_ratio_based, parent_based), then fall back to load_entry_point("opentelemetry_sampler", name) for unknown names.

Custom sampler example

tracer_provider:
  sampler:
    my_custom_sampler: {}
[project.entry-points."opentelemetry_sampler"]
my_custom_sampler = "my_package:MySamplerClass"

Closes #5071

Extracts a generic `load_entry_point(group, name)` helper into `_common`
so that resource detector, exporter, propagator, and sampler plugin loading
in declarative file config can all use the same entry point lookup pattern
rather than duplicating it.

Refactors `_propagator.py` to use the new util, removing its inline
entry point lookup.

Assisted-by: Claude Sonnet 4.6
Extends _create_sampler() to accept raw dicts (from the YAML path) in
addition to typed dataclasses (from the direct API path). Unknown sampler
names fall back to load_entry_point("opentelemetry_sampler", name),
matching the spec's PluginComponentProvider mechanism and Java SDK
behaviour.

_create_parent_based_sampler() gets the same dict-path treatment so
custom samplers can be used as delegate samplers inside parent_based.

Assisted-by: Claude Sonnet 4.6
Sampler and ParentBasedSampler are changed from @DataClass to
TypeAlias = dict[str, Any] in models.py. The generated dataclass
representation dropped unknown keys, making plugin sampler names
unrecoverable before reaching the factory. The dict type preserves
the raw YAML key, which is the plugin name.

_create_sampler() now has a single code path: extract the single key
as the sampler name, look it up in _SAMPLER_REGISTRY (always_on,
always_off, trace_id_ratio_based, parent_based), and fall back to
load_entry_point("opentelemetry_sampler", name) for unknown names.
This matches the spec's PluginComponentProvider mechanism.

Assisted-by: Claude Sonnet 4.6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Ready for review

Development

Successfully merging this pull request may close these issues.

feat(config): generic plugin loading for samplers in declarative config

1 participant