You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
DeclarativeConfigProperties: no way to coerce types without triggering warningsΒ #8101
YamlDeclarativeConfigProperties logs WARNING-level messages when a getter is called for a type that doesn't match the stored value:
WARNING: Ignoring value for key [foo] because it is Long instead of String
This means we can't build a type-coercing DeclarativeConfigProperties decorator without producing invalid warnings.
Use case
The Spring Boot starter populates OpenTelemetry's declarative config YAML from Spring's property system. Spring flattens its own YAML into key-value pairs and resolves ${} placeholders via Environment.getProperty(), which always returns String. These flat string properties are then converted back into a nested map and fed to Jackson's ObjectMapper.convertValue() to produce an OpenTelemetryConfigurationModel, which ultimately backs a YamlDeclarativeConfigProperties.
The problem is that the resulting types are inconsistent:
Values from plain YAML (e.g. int_key: 42) are stored as their native types (Integer, Boolean, Double)
Values resolved through Spring placeholders (e.g. int_key: ${INT_ENV:42}) arrive as String ("42")
We'd like to use a type-coercing decorator around DeclarativeConfigProperties to handle both cases transparently, but there is no way to check the actual stored type without triggering the WARNING-level logs, e.g.
Calling delegate.getString("int_key") on a natively-typed Integer logs: "Ignoring value for key [int_key] because it is Integer instead of String"
Are you talking about the spring function "Spring flattens its own YAML into key-value pairs and resolves ${} placeholders via Environment.getProperty(), which always returns String."?
We'd like to use a type-coercing decorator
What's a type-coercing decorator?
but there is no way to check the actual stored type without triggering the WARNING-level logs, e.g.
I'm supported of adding more introspection APIs. Currently we have Set<String> getPropertyKeys(). Some options to improve:
Class<?> getType(String key): return the type of a given key
boolean is<Type>(String key) (e.g. boolean isString(String key)): boolean checks for each type
Is there a preference from the maintainer team on which approach aligns better with the current DeclarativeConfigProperties design? Once there is consensus on the preferred method signature, I'm happy to open a PR adding it to the interface and implementing it across the backing classes!
@Tusharika725 are you still on this? Happy to take it over if not.
I hit the downstream symptom in the Spring starter: debug: true resolved through Spring arrives as a String, so getBoolean returned false and span logging could never be enabled (open-telemetry/opentelemetry-java-instrumentation#20034). The workaround there is a copied DeclarativeConfigProperties that coerces β the copy exists only because there is no way to ask the delegate what it actually holds.
Of Jack's two options I'd take boolean is<Type>(String key). It maps 1:1 onto the existing getters, so a decorator just asks "would getX succeed" before calling it, and it keeps the interface from committing to concrete classes (Integer vs Long vs Double). getType is one method instead of several, but callers then switch on types the interface doesn't otherwise expose.
I'll open a PR with that unless someone prefers getType.
@jack-berg concrete answer, since it landed downstream three days ago: yes, either option unblocks it.
open-telemetry/opentelemetry-java-instrumentation#20034 shipped SpringDeclarativeConfigProperties β not a decorator but a full copy of YamlDeclarativeConfigProperties with coercing getters, carrying a TODO: Revisit after #8101. That copy gets deleted when this lands.
I'd take is<Type>. In that copy getInt has to accept both Integer and Long; with getType() every caller re-derives that widening rule, whereas is<Type> just asks "would getX succeed". Cost is five methods against one.
@Tusharika725 I asked on 21 Sep if you were still on this β no reply, so unless you object I'll take it.
Unless there's an objection to the signature, I'll open a PR adding is<Type> to the interface plus EmptyDeclarativeConfigProperties and YamlDeclarativeConfigProperties.
I'd take is. In that copy getInt has to accept both Integer and Long; with getType() every caller re-derives that widening rule, whereas is just asks "would getX succeed". Cost is five methods against one.
Unless there's an objection to the signature, I'll open a PR adding is to the interface plus EmptyDeclarativeConfigProperties and YamlDeclarativeConfigProperties.
Activity
jack-berg commentedon Feb 18, 2026
I don't understand this. This should resolve as a
tag:yaml.org,2002:int:Are you talking about the spring function "Spring flattens its own YAML into key-value pairs and resolves ${} placeholders via Environment.getProperty(), which always returns String."?
What's a type-coercing decorator?
I'm supported of adding more introspection APIs. Currently we have
Set<String> getPropertyKeys(). Some options to improve:Class<?> getType(String key): return the type of a given keyboolean is<Type>(String key)(e.g.boolean isString(String key)): boolean checks for each typetrask commentedon Feb 18, 2026
sorry for confusion, above statement is about Spring YAML (not OpenTelemetry YAML)
e.g. we want to implement ConfigProvider so that calls to DeclarativeConfigProperties.getInt() automatically coerce Strings to Ints where possible
Tusharika725 commentedon Apr 2, 2026
@trask @jack-berg I would love to pick this up as my next issue!
To allow for safe type-coercion without triggering the WARNING logs, adding introspection APIs makes perfect sense. Between Jack's two proposals:
Class<?> getType(String key)
boolean is(String key) (e.g., isString(), isInteger())
Is there a preference from the maintainer team on which approach aligns better with the current DeclarativeConfigProperties design? Once there is consensus on the preferred method signature, I'm happy to open a PR adding it to the interface and implementing it across the backing classes!
colamong commentedon Sep 21, 2026
@Tusharika725 are you still on this? Happy to take it over if not.
I hit the downstream symptom in the Spring starter:
debug: trueresolved through Spring arrives as a String, sogetBooleanreturned false and span logging could never be enabled (open-telemetry/opentelemetry-java-instrumentation#20034). The workaround there is a copiedDeclarativeConfigPropertiesthat coerces β the copy exists only because there is no way to ask the delegate what it actually holds.Of Jack's two options I'd take
boolean is<Type>(String key). It maps 1:1 onto the existing getters, so a decorator just asks "would getX succeed" before calling it, and it keeps the interface from committing to concrete classes (Integer vs Long vs Double).getTypeis one method instead of several, but callers then switch on types the interface doesn't otherwise expose.I'll open a PR with that unless someone prefers
getType.jack-berg commentedon Sep 24, 2026
@trask Would the addition of one or both of these allow you to write the type-coercing decorator you need for spring?
colamong commentedon Sep 28, 2026
@jack-berg concrete answer, since it landed downstream three days ago: yes, either option unblocks it.
open-telemetry/opentelemetry-java-instrumentation#20034 shipped
SpringDeclarativeConfigPropertiesβ not a decorator but a full copy ofYamlDeclarativeConfigPropertieswith coercing getters, carrying aTODO: Revisit after #8101. That copy gets deleted when this lands.I'd take
is<Type>. In that copygetInthas to accept bothIntegerandLong; withgetType()every caller re-derives that widening rule, whereasis<Type>just asks "wouldgetXsucceed". Cost is five methods against one.@Tusharika725 I asked on 21 Sep if you were still on this β no reply, so unless you object I'll take it.
Unless there's an objection to the signature, I'll open a PR adding
is<Type>to the interface plusEmptyDeclarativeConfigPropertiesandYamlDeclarativeConfigProperties.jack-berg commentedon Sep 29, 2026
Let's do it.