Simple reproducing code:
@Test
fun test() {
val binder = Binder.get(MockEnvironment())
expectThrows<InvalidConfigurationPropertyNameException> {
binder.bind("logging.level.com.example.foo_bar", String::class.java)
}.message.isNotNull().isEqualTo("Configuration property name 'logging.level.com.example.foo_bar' is not valid")
}
com.example.foo_bar is a valid package name, even if underscores are discouraged.
Also happens w/ a real Environment with both v2.6.3 and v2.5.10.
Use case is that we are using GCP's Runtime Config to change log levels, set feature flags, etc. My GCP Runtime Config property source is checking the values returned from GCP using a Binder like above so I can publish a EnvironmentChangeEvent for all updated keys so that LoggingRebinder can change log levels on the fly.
I can work around it by catching, but this will log a message each time:
// check other property sources like systemProperties, systemEnvironment etc
val updatedConfig = config.filter { (key, value) ->
val currentValue = runCatching {
// this can throw InvalidConfigurationPropertyNameException
binder.bind(key, String::class.java).orElse(null)
}.onFailure { ex ->
log.warn(ex, "Exception checking current value of \"{key}\".", key)
}.getOrNull()
value != currentValue
}
Comment From: wilkinsona
This is to be expected as you cannot assume that the name of every entry in the environment will be a valid configuration property name. In the case of logging.level.com.example.foo_bar, it's logging.level that's the name of a property with a Map<String, LogLevel> value. com.example.foo_bar is the key in that map.
If you're specifically interested in logging.level, you should bind to a map as Boot itself does:
https://github.com/spring-projects/spring-boot/blob/4777dce03cef81f6db4ec9d5b7b9e16c6053602c/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/logging/LoggingApplicationListener.java#L376-L377
Comment From: efenderbosch
Looks like I can use
val name = ConfigurationPropertyName.adapt(key, '.')
binder.bind(name, Bindable.of(String::class.java)).orElse(null)