I'm trying to find a way to log my application properties (injected into my beans via @ConfigurationProperties) prior to the context startup so that I can see what the exact properties are before all the beans are initialized.
I've tried to create a Listener on an ApplicationEnvironmentPreparedEvent, but there is no way to retrieve all the properties defined short of retrieving individual properties one at a time.
Is there an easy way to do this? Some way to initialize @ConfigurationProperties
first and log their contents, or retrieve all the application startup properties prior to the context creation?
You can see application properties simply with the help of a custom class implementing the ApplicationListener and defining it as one of the startupup class in spring-factories entry so they will execute before the application load. Steps are as follows :-
a) Create a file called spring.factories in resource classpath i.e. src\main\resources\META-INF\spring.factories with content -
# Application Listeners
org.springframework.context.ApplicationListener=demo.CustomConfigListener
b) Create a custom listener class in your proj like here CustomConfigListener
package demo;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.PropertySource;
public class CustomConfigListener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
for(PropertySource<?> source : ((ApplicationEnvironmentPreparedEvent) event).getEnvironment().getPropertySources()){
if(source.getName().equals("applicationConfigurationProperties")){
if (source instanceof EnumerablePropertySource) {
for(String name : ((EnumerablePropertySource) source).getPropertyNames()){
System.out.println(name+" :: "+ ((EnumerablePropertySource) source).getProperty(name));
}
}
}
}
}
}
}
c) Your custom ConfigurationProperties class
package demo;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(ignoreUnknownFields = false, prefix = "mail")
public class MailProperties {
private String host;
private int port;
private Smtp smtp;
//Getters & Setters
public static class Smtp {
private boolean auth;
private boolean starttlsEnable;
//Getters & Setters
}
}
d) Finally your application.properties
mail.host=localhost
mail.port=25
mail.smtp.auth=false
mail.smtp.starttls-enable=false
As a followup to @Avis' answer, I realized that the snippet didn't include any command line args, etc, so I have updated his concept a little. I am attaching my logger class in case it has value for anyone who lands on this question in the future.
public class ConfigurationLogger implements ApplicationListener<ApplicationEvent> {
// slf4j logger
private static final Logger logger = LoggerFactory.getLogger(ConfigurationLogger.class);
// used to sanitize any password sensitive keys (copied from Spring Boot's Sanitizer() class
private Sanitizer sanitizer = new Sanitizer();
// store the config keys in a sorted map
private Map<String, Object> configurationProperties = new TreeMap<>();
/**
* Trigger upon all events during startup. Both ApplicatoinEnvironmentPrepareEvent and
* ApplicationPreparedEvent need access to the same configurationProperties object. Could
* have done this through separate events, both extending an abstract base class with a static
* hash map, but not worth the effort. Instead have the same class listen for all events, and
* delegate to the appropriate method.
*/
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
// store the values
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}else if( event instanceof ApplicationPreparedEvent){
// display the values
logConfigurationProperties( (ApplicationPreparedEvent)event);
}
}
/**
* Store the properties in the hash map for logging once all property sources have been read
*
* @param event
*/
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
for (PropertySource<?> source : event.getEnvironment().getPropertySources()) {
if (source instanceof EnumerablePropertySource) {
for (String key : ((EnumerablePropertySource) source).getPropertyNames()) {
Object value = ((EnumerablePropertySource) source).getProperty(key);
if (!configurationProperties.containsKey(key)) {
configurationProperties.put(key, sanitizer.sanitize(key, value));
}
}
}
}
}
/**
* Print all the config properties to the logger
*/
private void logConfigurationProperties( ApplicationPreparedEvent event) {
logger.debug("Application started with following parameters: ");
for( Map.Entry<String, Object> entry : configurationProperties.entrySet()){
logger.debug("{} :: {}", entry.getKey(), entry.getValue());
}
}
}
And the listener is initialized in the SpringApplication main class:
@SpringBootApplication
public class Application{
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application.class);
app.setShowBanner(false);
// add configuration properties logger
app.addListeners(new ConfigurationLogger());
app.run(args);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With