feat(config): Add TracerProvider support for declarative config#4985
feat(config): Add TracerProvider support for declarative config#4985MikeGoldsmith wants to merge 17 commits intoopen-telemetry:mainfrom
Conversation
Implements create_resource() and create_propagator()/configure_propagator() for the declarative file configuration. Resource creation does not read OTEL_RESOURCE_ATTRIBUTES or run any detectors (matches Java/JS SDK behavior). Propagator configuration always calls set_global_textmap to override Python's default tracecontext+baggage, setting a noop CompositePropagator when no propagator is configured. Assisted-by: Claude Sonnet 4.6
Assisted-by: Claude Sonnet 4.6
- _resource.py: refactor _coerce_attribute_value to dispatch table to avoid too-many-return-statements; fix short variable names k/v -> attr_key/attr_val; fix return type of _sdk_default_attributes to dict[str, str] to satisfy pyright - _propagator.py: rename short variable names e -> exc, p -> propagator - test_resource.py: move imports to top level; split TestCreateResource (25 methods) into three focused classes to satisfy too-many-public-methods - test_propagator.py: add pylint disable for protected-access Assisted-by: Claude Sonnet 4.6
- replace _sdk_default_attributes() with _DEFAULT_RESOURCE from resources module - move _coerce_bool into dispatch tables for both scalar and array bool types, fixing a bug where bool_array with string values like "false" would coerce incorrectly via plain bool() (non-empty string -> True) - add test for bool_array with string values to cover the bug Assisted-by: Claude Sonnet 4.6
… into mike/config-resource-propagator
Assisted-by: Claude Sonnet 4.6
…erge - collapse _SCALAR_COERCIONS and _ARRAY_COERCIONS into a single _COERCIONS dict using an _array() factory, reducing _coerce_attribute_value to two lines - process attributes_list before attributes so explicit attributes naturally overwrite list entries without needing an explicit guard Assisted-by: Claude Sonnet 4.6
| return CompositePropagator(propagators) | ||
|
|
||
|
|
||
| def configure_propagator(config: Optional[PropagatorConfig]) -> None: |
There was a problem hiding this comment.
Correct me if I'm wrong, but did we want to silently disable trace context, baggage propagation for an empty propagator section in the config file?
There was a problem hiding this comment.
The intention here is "what you see is what you get" (WYSIWYG) semantics as defined in the declarative config contributing guide:
implementations should minimize the amount of magic that occurs as a result of the absence of an optional property
So if propagators is absent or empty in the config file, the intent is that no propagators are configured — not that we fall back to the SDK defaults of tracecontext+baggage. WYSIWYG: there are no propagators in the config, you get no propagators.
This is intentional and consistent with how e.g. an absent meter_provider results in a noop meter provider rather than a default one with a periodic reader and OTLP exporter.
Assisted-by: Claude Sonnet 4.6
Adds _run_detectors() stub and _filter_attributes() to create_resource(), providing the shared scaffolding for detector PRs to build on. Detectors are opt-in: nothing runs unless explicitly listed under detection_development.detectors in the config. The include/exclude attribute filter mirrors other SDK behaviour. Assisted-by: Claude Sonnet 4.6
Merges service.name=unknown_service into base before running detectors, so detectors (e.g. service) can override it. Previously it was added to config_attrs and merged last, which would have silently overridden any detector-provided service.name. Assisted-by: Claude Sonnet 4.6
Assisted-by: Claude Sonnet 4.6
a54d5b1 to
9653d67
Compare
Assisted-by: Claude Sonnet 4.6
Implements create_tracer_provider() and configure_tracer_provider() for the declarative configuration pipeline (tracking issue open-telemetry#3631 step 5). Key behaviors: - Never reads OTEL_TRACES_SAMPLER or OTEL_SPAN_*_LIMIT env vars; absent config fields use OTel spec defaults (matching Java SDK behavior) - Default sampler is ParentBased(root=ALWAYS_ON) per the OTel spec - SpanLimits absent fields use hardcoded defaults (128) not env vars - configure_tracer_provider(None) is a no-op per spec/Java/JS behavior - OTLP exporter fields pass None through so the exporter reads its own env vars for unspecified values - Lazy imports for optional OTLP packages with ConfigurationError on missing - Supports all 4 ParentBased delegate samplers Assisted-by: Claude Sonnet 4.6
Assisted-by: Claude Sonnet 4.6
- add # noqa: PLC0415 to lazy OTLP imports (ruff also enforces this) - move SDK imports to top-level (BatchSpanProcessor, etc.) - convert test helper methods to @staticmethod to satisfy no-self-use - add pylint: disable=protected-access for private member access in tests - fix return type annotation on _create_span_processor Assisted-by: Claude Sonnet 4.6
Assisted-by: Claude Sonnet 4.6
Assisted-by: Claude Sonnet 4.6
9653d67 to
232b06f
Compare
| """ | ||
| if headers is None and headers_list is None: | ||
| return None | ||
| result: Dict[str, str] = {} |
There was a problem hiding this comment.
nit:
| result: Dict[str, str] = {} | |
| result: dict[str, str] = {} |
| if headers is None and headers_list is None: | ||
| return None | ||
| result: Dict[str, str] = {} | ||
| if headers_list: |
There was a problem hiding this comment.
This function is nearly identical to _parse_attributes_list, can we create a more general shared function that can ideally be used in both places?
Description
Implements
create_tracer_provider()andconfigure_tracer_provider()for the declarative configuration pipeline, as part of the ongoing work tracked in the following PR:Note
This PR is based on #4979 (Resource & Propagator creation) which must be merged first. The extra commits at the base of this branch will be dropped once that PR lands.
What's included
_tracer_provider.py: creates an SDKTracerProviderfrom declarative configalways_on,always_off,trace_id_ratio_based,parent_based(with all 4 delegate samplers)BatchSpanProcessorandSimpleSpanProcessorConfigurationErrorif not installedSpanLimitsand sampler use OTel spec defaults for absent fields — no env-var leakageconfigure_tracer_provider(None)is a no-op per spec/Java/JS behaviorEnv-var suppression
Python's
TracerProviderreadsOTEL_TRACES_SAMPLERwhensampler=None, andSpanLimitsreadsOTEL_SPAN_*_LIMITwhen fields areNone. To match the spec's "what you see is what you get" semantics (and Java SDK behavior), we always pass explicit values — defaulting to spec defaults (e.g.ParentBased(root=ALWAYS_ON), 128 for count limits) rather than falling through to env vars.Type of change
How Has This Been Tested?
tests/_configuration/test_tracer_provider.pyOTEL_TRACES_SAMPLER,OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT) are not read when config is usedDoes This PR Require a Contrib Repo Change?
Checklist:
Assisted-by: Claude Sonnet 4.6