6 changed files with 337 additions and 0 deletions
@ -0,0 +1,39 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012-present the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.boot.autoconfigure.context; |
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfiguration; |
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
|
||||||
|
/** |
||||||
|
* Auto-configuration for configuration snapshot logging. |
||||||
|
* |
||||||
|
* @author Your Name |
||||||
|
* @since 3.2.0 |
||||||
|
*/ |
||||||
|
@AutoConfiguration |
||||||
|
@ConditionalOnProperty(prefix = "spring.config-snapshot", name = "enabled", havingValue = "true") |
||||||
|
@EnableConfigurationProperties(ConfigurationSnapshotProperties.class) |
||||||
|
public class ConfigurationSnapshotAutoConfiguration { |
||||||
|
|
||||||
|
@Bean |
||||||
|
public ConfigurationSnapshotListener configurationSnapshotListener(ConfigurationSnapshotProperties properties) { |
||||||
|
return new ConfigurationSnapshotListener(properties); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,128 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012-present the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.boot.autoconfigure.context; |
||||||
|
|
||||||
|
import java.io.FileWriter; |
||||||
|
import java.io.IOException; |
||||||
|
import java.time.LocalDateTime; |
||||||
|
import java.time.format.DateTimeFormatter; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Properties; |
||||||
|
|
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
import org.springframework.context.ApplicationListener; |
||||||
|
import org.springframework.context.event.ContextRefreshedEvent; |
||||||
|
import org.springframework.core.env.ConfigurableEnvironment; |
||||||
|
import org.springframework.core.env.Environment; |
||||||
|
import org.springframework.core.env.PropertiesPropertySource; |
||||||
|
import org.springframework.core.env.PropertySource; |
||||||
|
|
||||||
|
/** |
||||||
|
* Application listener that logs a snapshot of the application configuration when the |
||||||
|
* context is refreshed. |
||||||
|
* |
||||||
|
* @author Your Name |
||||||
|
* @since 3.2.0 |
||||||
|
*/ |
||||||
|
public class ConfigurationSnapshotListener implements ApplicationListener<ContextRefreshedEvent> { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ConfigurationSnapshotListener.class); |
||||||
|
|
||||||
|
private final ConfigurationSnapshotProperties properties; |
||||||
|
|
||||||
|
public ConfigurationSnapshotListener(ConfigurationSnapshotProperties properties) { |
||||||
|
this.properties = properties; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onApplicationEvent(ContextRefreshedEvent event) { |
||||||
|
Environment environment = event.getApplicationContext().getEnvironment(); |
||||||
|
if (!(environment instanceof ConfigurableEnvironment configurableEnvironment)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Build configuration snapshot
|
||||||
|
Map<String, Object> snapshot = new HashMap<>(); |
||||||
|
snapshot.put("activeProfiles", environment.getActiveProfiles()); |
||||||
|
|
||||||
|
Map<String, String> includedProperties = new HashMap<>(); |
||||||
|
if (this.properties.getInclude() != null && !this.properties.getInclude().isEmpty()) { |
||||||
|
for (String propertyName : this.properties.getInclude()) { |
||||||
|
if (environment.containsProperty(propertyName)) { |
||||||
|
includedProperties.put(propertyName, environment.getProperty(propertyName)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
snapshot.put("properties", includedProperties); |
||||||
|
|
||||||
|
// Convert to JSON
|
||||||
|
String json = toJson(snapshot); |
||||||
|
|
||||||
|
// Log to console or file
|
||||||
|
if (this.properties.getLogTo() == ConfigurationSnapshotProperties.LogTo.CONSOLE) { |
||||||
|
logger.info("Configuration snapshot: {}", json); |
||||||
|
} else { |
||||||
|
writeToFile(json); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private String toJson(Map<String, Object> snapshot) { |
||||||
|
StringBuilder sb = new StringBuilder(); |
||||||
|
sb.append("{"); |
||||||
|
|
||||||
|
// Active profiles
|
||||||
|
sb.append("\"activeProfiles\": ["); |
||||||
|
String[] activeProfiles = (String[]) snapshot.get("activeProfiles"); |
||||||
|
for (int i = 0; i < activeProfiles.length; i++) { |
||||||
|
if (i > 0) { |
||||||
|
sb.append(","); |
||||||
|
} |
||||||
|
sb.append("\"").append(activeProfiles[i]).append("\""); |
||||||
|
} |
||||||
|
sb.append("]"); |
||||||
|
|
||||||
|
// Properties
|
||||||
|
sb.append(",\"properties\": {"); |
||||||
|
Map<String, String> properties = (Map<String, String>) snapshot.get("properties"); |
||||||
|
int i = 0; |
||||||
|
for (Map.Entry<String, String> entry : properties.entrySet()) { |
||||||
|
if (i > 0) { |
||||||
|
sb.append(","); |
||||||
|
} |
||||||
|
sb.append("\"").append(entry.getKey()).append("\":\"").append(entry.getValue()).append("\""); |
||||||
|
i++; |
||||||
|
} |
||||||
|
sb.append("}"); |
||||||
|
|
||||||
|
sb.append("}"); |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
private void writeToFile(String json) { |
||||||
|
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); |
||||||
|
String filename = "config-snapshot-" + timestamp + ".log"; |
||||||
|
|
||||||
|
try (FileWriter writer = new FileWriter(filename)) { |
||||||
|
writer.write(json); |
||||||
|
logger.info("Configuration snapshot written to file: {}", filename); |
||||||
|
} catch (IOException e) { |
||||||
|
logger.error("Failed to write configuration snapshot to file: {}", filename, e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,74 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012-present the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.boot.autoconfigure.context; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties; |
||||||
|
|
||||||
|
/** |
||||||
|
* Configuration properties for configuration snapshot logging. |
||||||
|
* |
||||||
|
* @author Your Name |
||||||
|
* @since 3.2.0 |
||||||
|
*/ |
||||||
|
@ConfigurationProperties(prefix = "spring.config-snapshot") |
||||||
|
public class ConfigurationSnapshotProperties { |
||||||
|
|
||||||
|
/** |
||||||
|
* Whether to enable configuration snapshot logging. |
||||||
|
*/ |
||||||
|
private boolean enabled = false; |
||||||
|
|
||||||
|
/** |
||||||
|
* Where to log the configuration snapshot. |
||||||
|
*/ |
||||||
|
private LogTo logTo = LogTo.CONSOLE; |
||||||
|
|
||||||
|
/** |
||||||
|
* List of properties to include in the snapshot. |
||||||
|
*/ |
||||||
|
private List<String> include; |
||||||
|
|
||||||
|
public boolean isEnabled() { |
||||||
|
return this.enabled; |
||||||
|
} |
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) { |
||||||
|
this.enabled = enabled; |
||||||
|
} |
||||||
|
|
||||||
|
public LogTo getLogTo() { |
||||||
|
return this.logTo; |
||||||
|
} |
||||||
|
|
||||||
|
public void setLogTo(LogTo logTo) { |
||||||
|
this.logTo = logTo; |
||||||
|
} |
||||||
|
|
||||||
|
public List<String> getInclude() { |
||||||
|
return this.include; |
||||||
|
} |
||||||
|
|
||||||
|
public void setInclude(List<String> include) { |
||||||
|
this.include = include; |
||||||
|
} |
||||||
|
|
||||||
|
public enum LogTo { |
||||||
|
CONSOLE, FILE |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,74 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012-present the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.boot.autoconfigure.context; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigurations; |
||||||
|
import org.springframework.boot.test.context.runner.ApplicationContextRunner; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for {@link ConfigurationSnapshotAutoConfiguration}. |
||||||
|
* |
||||||
|
* @author Your Name |
||||||
|
* @since 3.2.0 |
||||||
|
*/ |
||||||
|
class ConfigurationSnapshotAutoConfigurationTests { |
||||||
|
|
||||||
|
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration( |
||||||
|
AutoConfigurations.of(ConfigurationSnapshotAutoConfiguration.class)); |
||||||
|
|
||||||
|
@Test |
||||||
|
void configurationSnapshotAutoConfigurationIsDisabledByDefault() { |
||||||
|
this.contextRunner.run((context) -> { |
||||||
|
assertThat(context.getBeansOfType(ConfigurationSnapshotListener.class)).isEmpty(); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void configurationSnapshotAutoConfigurationIsEnabledWhenPropertyIsSetToTrue() { |
||||||
|
this.contextRunner.withPropertyValues("spring.config-snapshot.enabled=true") |
||||||
|
.run((context) -> { |
||||||
|
assertThat(context.getBeansOfType(ConfigurationSnapshotListener.class)).isNotEmpty(); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void configurationSnapshotAutoConfigurationIsDisabledWhenPropertyIsSetToFalse() { |
||||||
|
this.contextRunner.withPropertyValues("spring.config-snapshot.enabled=false") |
||||||
|
.run((context) -> { |
||||||
|
assertThat(context.getBeansOfType(ConfigurationSnapshotListener.class)).isEmpty(); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void configurationSnapshotPropertiesAreBoundCorrectly() { |
||||||
|
this.contextRunner.withPropertyValues( |
||||||
|
"spring.config-snapshot.enabled=true", |
||||||
|
"spring.config-snapshot.log-to=FILE", |
||||||
|
"spring.config-snapshot.include=spring.datasource.url,spring.cache.enabled") |
||||||
|
.run((context) -> { |
||||||
|
ConfigurationSnapshotProperties properties = context.getBean(ConfigurationSnapshotProperties.class); |
||||||
|
assertThat(properties.isEnabled()).isTrue(); |
||||||
|
assertThat(properties.getLogTo()).isEqualTo(ConfigurationSnapshotProperties.LogTo.FILE); |
||||||
|
assertThat(properties.getInclude()).containsExactly("spring.datasource.url", "spring.cache.enabled"); |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue