Implement declarative config ConfigProvider / ConfigProperties API - #5486
Implement declarative config ConfigProvider / ConfigProperties API#5486ocelotl wants to merge 4 commits into
Conversation
|
Updated the global
|
| _logger.warning("Overriding of current ConfigProvider is not allowed") | ||
|
|
||
|
|
||
| def get_config_provider() -> ConfigProvider: |
There was a problem hiding this comment.
I'm not sure if making the ConfigProvider accessible before initialization in a similar manner to Tracers/Loggers/Meter is desirable in this case. Why not just forward it as an argument to the instrument function on instrumentor classes and provide a no-op config provider if not initialized? What exactly is the global state buying us here as opposed to threading it through regular function calls? I'd imagine that receiving a ProxyConfigProvider instance wouldn't be that useful since consumers will almost always read from the configuration immediately.
There was a problem hiding this comment.
I don't disagree, I'm following this approach for consistency. Even if we follow the approach suggested in this PR we would do no harm, so we get something that is consistent with the rest of the providers and we get a harmless possible scenario.
There was a problem hiding this comment.
Pull request overview
Adds the OpenTelemetry declarative Instrumentation Configuration API surface to the opentelemetry-configuration package, and wires it into configure_sdk so instrumentation libraries can read the parsed .instrumentation node via a global provider.
Changes:
- Introduces
ConfigProperties(typed read view over a mapping node) andConfigProvider(exposes instrumentation config), plus globalget_config_provider/set_config_providerwith proxy-default + set-once semantics. - Updates
configure_sdkto build aConfigProviderfrom the instrumentation config node and install it globally. - Adds a comprehensive unit test suite for scalar/structured accessors and global provider behavior.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| opentelemetry-configuration/src/opentelemetry/configuration/_config_provider.py | New ConfigProvider/ConfigProperties implementation and global provider registry. |
| opentelemetry-configuration/src/opentelemetry/configuration/_sdk.py | Wires configure_sdk to set the global ConfigProvider based on instrumentation config. |
| opentelemetry-configuration/src/opentelemetry/configuration/init.py | Exports the new public API symbols from opentelemetry.configuration. |
| opentelemetry-configuration/tests/test_config_provider.py | Adds unit tests for getters, type-mismatch behavior, node normalization, and global get/set semantics. |
| .changelog/5486.added | Documents the new configuration API surface and configure_sdk wiring. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| result: list[ConfigProperties] = [] | ||
| for item in value: | ||
| mapping = _node_to_mapping(item) | ||
| if not mapping and item is not None: | ||
| self._log_type_mismatch(name, value, "list of mappings") | ||
| return None | ||
| result.append(ConfigProperties(mapping)) | ||
| return result |
| sections. Sections absent from the config (``None``) leave the | ||
| corresponding global untouched — matching the spec's "noop default" | ||
| behavior. | ||
| behavior. The global :class:`ConfigProvider` is always set, exposing the | ||
| ``instrumentation`` config node as a read view (empty when absent) for | ||
| instrumentation libraries to consume. |
Pull request dashboard statusWaiting on the author · refreshed 2026-08-24 14:39 UTC Two things need attention:
Status above doesn't look right?
|
xrmx
left a comment
There was a problem hiding this comment.
This API is going to be called by instrumentations but this API sits in opentelemetry-configuration. So are instrumentations going to add a dependency on it?
| """Read view over declarative instrumentation configuration. | ||
|
|
||
| Implements the spec's ``ConfigProvider`` / ``ConfigProperties`` API | ||
| (``configuration/api.md``): a stateless, typed read view over the parsed |
There was a problem hiding this comment.
Should this be a full url instead?
| """ | ||
| if node is None: | ||
| return {} | ||
| if is_dataclass(node) and not isinstance(node, type): |
There was a problem hiding this comment.
I think we lose the distinction between the key being absent and then key being None. Would this be an issue?
|
Hi @ocelotl — just a friendly reminder that this pull request is waiting on you. The dashboard status comment has the open items and is kept current.
|
Adds the Instrumentation Configuration API from the declarative configuration spec (configuration/api.md), which was previously unimplemented in Python: - ConfigProperties: a typed, schemaless read view over a parsed configuration mapping node, with get_string/get_bool/get_int/ get_float, get_config, get_config_list, get_scalar_list, and key introspection. Getters return None on a missing key or type mismatch, matching the spec and Java's DeclarativeConfigProperties semantics. - ConfigProvider: holds the ConfigProperties for the instrumentation config node and exposes get_instrumentation_config(). - A global default ConfigProvider with get_config_provider() / set_config_provider(), so instrumentation libraries can access it. - configure_sdk now builds a ConfigProvider from the .instrumentation node and sets it as the global. Fixes open-telemetry#5485.
Make the global ConfigProvider consistent with the other OpenTelemetry globals (tracer/meter/logger): - set_config_provider is now set-once, guarded by a thread-safe Once. A second call logs a warning and keeps the existing provider instead of overwriting it (the previous version warned but overwrote, and was not thread-safe). - get_config_provider returns a NoOpConfigProvider (empty instrumentation config) when none is set, rather than None, so callers can traverse the config tree without None checks — mirroring ProxyTracerProvider and Java's ConfigProvider.noop(). Its return type is now non-optional.
…list getters Address further consistency gaps with existing opentelemetry-python APIs: - get_config_provider now returns a forwarding ProxyConfigProvider (reads the global lazily) instead of a frozen no-op, so a provider obtained before set_config_provider still resolves to the one installed later — matching ProxyTracerProvider / ProxyLoggerProvider. NoOpConfigProvider is kept as an explicit no-op (mirroring NoOpTracerProvider). - ConfigProperties getters now log a warning when a key is present with an incompatible type, instead of silently returning None (matching the package's own logging habits and Java's DeclarativeConfigProperties). - Replaced get_scalar_list(name, type) with typed getters get_string_list, get_bool_list, get_int_list, get_float_list, consistent with the scalar getters and avoiding a runtime type argument. - keys() now returns a set (matching the spec's "set of property keys").
…f import order - Rename changelog fragment 5485.added -> 5486.added to match the PR number (the changelog check requires <PR_NUMBER>.<type>). - Make ConfigProperties._log_type_mismatch a staticmethod (it does not use self) to satisfy pylint R6301 (no-self-use). - Disable protected-access in the test setUp that resets the module globals (pylint W0212). - Sort imports in the test module (ruff I001).
3d26221 to
00c93f6
Compare
Description
Implements the declarative configuration Instrumentation Configuration API
(
configuration/api.md),which was previously not implemented in Python.
ConfigProperties— a typed, schemaless read view over a parsedconfiguration mapping node:
get_string,get_bool,get_int,get_float,get_config(nested view),get_config_list, the scalar-sequence gettersget_string_list,get_bool_list,get_int_listandget_float_list,keys(), and__contains__. Getters returnNoneon a missing key or typemismatch, matching the spec and Java's
DeclarativeConfigPropertiessemantics. The spec requires accessors for "sequences of scalars" without
naming them, and asks for type safety "based on what is idiomatic in the
language", so Python uses one typed getter per scalar type rather than
Java's
getScalarList(name, Class<T>), which only takes a type tokenbecause of type erasure.
ConfigProvider— holds theConfigPropertiesfor the.instrumentationconfig node and exposes
get_instrumentation_config().ConfigProviderwithget_config_provider()/set_config_provider(), so instrumentation libraries can access configurationduring initialization.
configure_sdknow builds aConfigProviderfrom the.instrumentationnode and installs it as the global (empty when the node is absent).
All symbols are exported from
opentelemetry.configuration.Scope
This PR is intentionally scoped to the API surface (
ConfigProvider/ConfigProperties) and its wiring intoconfigure_sdk. The related but separateconcern of making
Create/configure_sdkreturn the top-level SDK components asa pure builder is not included here and can be tracked separately.
Fixes #5485.
Type of change
ConfigProvideris Development stability in the spec; the package is alreadymarked experimental.
ConfigPropertiesis Stable in the spec.How Has This Been Tested?
opentelemetry-configuration/tests/test_config_provider.py(40 tests)covering scalar getters, type-mismatch/
Nonebehavior,bool/intdisambiguation, nested
get_config/get_config_list, the typed scalar-listgetters, a key present with a null value versus an absent key,
dataclass-node normalization, and the global provider get/set.
opentelemetry-configurationsuite passes locally (421 passed, 29subtests) — no regressions from the
configure_sdkwiring.Does This PR Require a Contrib Repo Change?
No.
Checklist
.changelog/5485.added; will rename to the PRnumber if preferred)