Implement declarative config ConfigProvider / ConfigProperties API - #5486
Implement declarative config ConfigProvider / ConfigProperties API#5486ocelotl wants to merge 4 commits into
Conversation
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.
|
Updated the global
|
…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).
| _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.
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,get_scalar_list,keys(),and
__contains__. Getters returnNoneon a missing key or type mismatch,matching the spec and Java's
DeclarativeConfigPropertiessemantics.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(26 tests)covering scalar getters, type-mismatch/
Nonebehavior,bool/intdisambiguation, nested
get_config/get_config_list/get_scalar_list,dataclass-node normalization, and the global provider get/set.
opentelemetry-configurationsuite passes locally (406 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)