There is already a question asking for logging the active configuration, there is a correct answer but the problem is that the configuration is logged only if all beans are correctly instantiated. I would like to log all properties even (mainly) if the application crash at startup. My question is more specific:
How to log all active properties of a spring boot application before the beans instantiation?
Another very simple way to read application properties is to use @Value annotation. Simply annotation the class field with @Value annotation providing the name of the property you want to read from application. properties file and class field variable will be assigned that value.
To see all properties in your Spring Boot application, enable the Actuator endpoint called env . This enables an HTTP endpoint which shows all the properties of your application's environment. You can do this by setting the property management. endpoints.
hibernate = DEBUG will set logging level for classes of Spring framework web and Hibernate only.
React Full Stack Web Development With Spring BootSpring Boot uses Apache Commons logging for all internal logging. Spring Boot's default configurations provides a support for the use of Java Util Logging, Log4j2, and Logback. Using these, we can configure the console logging as well as file logging.
For doing this you need to register an ApplicationListener
. The event to catch is the ApplicationPreparedEvent
, according to the documentation:
ApplicationPreparedEvent is an event published when a SpringApplication is starting up and the ApplicationContext is fully prepared but not refreshed. The bean definitions will be loaded and the Environment is ready for use at this stage.
The main method would look like this:
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(MyApplication.class);
springApplication.addListeners(new PropertiesLogger());
springApplication.run(args);
}
I've reused the code of the answer cited in the current question but I've modified it because the context you get is not already refreshed and the structure of the environment is not exactly the same as after the startup of the application. I've also printed the properties by property sources: one for the the system environment, one for the system properties, one for the application configuration properties, etc... Note also that the ApplicationPreparedEvent
can be triggered multiple times, and that properties are printed only the first time. See Spring Boot issue #8899 for details.
package com.toto.myapp.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.PropertySource;
import java.util.LinkedList;
import java.util.List;
public class PropertiesLogger implements ApplicationListener<ApplicationPreparedEvent> {
private static final Logger log = LoggerFactory.getLogger(PropertiesLogger.class);
private ConfigurableEnvironment environment;
private boolean isFirstRun = true;
@Override
public void onApplicationEvent(ApplicationPreparedEvent event) {
if (isFirstRun) {
environment = event.getApplicationContext().getEnvironment();
printProperties();
}
isFirstRun = false;
}
public void printProperties() {
for (EnumerablePropertySource propertySource : findPropertiesPropertySources()) {
log.info("******* " + propertySource.getName() + " *******");
String[] propertyNames = propertySource.getPropertyNames();
Arrays.sort(propertyNames);
for (String propertyName : propertyNames) {
String resolvedProperty = environment.getProperty(propertyName);
String sourceProperty = propertySource.getProperty(propertyName).toString();
if(resolvedProperty.equals(sourceProperty)) {
log.info("{}={}", propertyName, resolvedProperty);
}else {
log.info("{}={} OVERRIDDEN to {}", propertyName, sourceProperty, resolvedProperty);
}
}
}
}
private List<EnumerablePropertySource> findPropertiesPropertySources() {
List<EnumerablePropertySource> propertiesPropertySources = new LinkedList<>();
for (PropertySource<?> propertySource : environment.getPropertySources()) {
if (propertySource instanceof EnumerablePropertySource) {
propertiesPropertySources.add((EnumerablePropertySource) propertySource);
}
}
return propertiesPropertySources;
}
}
As described at https://www.baeldung.com/spring-boot-environmentpostprocessor, the properties can be collected before the context is loaded through the use of EnvironmentPostProcessor, which is instantiated as part of Spring Factories from the call ConfigFileApplicationListener.loadPostProcessors(). At this point, you can collect all the properties and show in any specific way.
NOTE: While loading properties during this event, the context isn't ready. So, are the loggers. For this reason, the properties can be loaded before the App Banner (if any)
org.springframework.boot.env.EnvironmentPostProcessor=\
cash.app.PropertiesLoggerEnvironmentPostProcessor
package cash.app
import org.springframework.boot.SpringApplication
import org.springframework.boot.env.EnvironmentPostProcessor
import org.springframework.core.Ordered
import org.springframework.core.annotation.Order
import org.springframework.core.env.ConfigurableEnvironment
import org.springframework.core.env.EnumerablePropertySource
import java.util.*
/**
* This is to log the properties (config and system) before the app loads. This way, we know what will be loaded
* on the app.
* Note that we can't use the logger because the context hasn't built yet at the time it loads the properties twice.
*
* As an event consumer, the method ConfigFileApplicationListener.onApplicationEnvironmentPreparedEvent is called
* while the context is building. The process is described at https://www.baeldung.com/spring-boot-environmentpostprocessor
* and one important aspect is that this class is an EnvironmentPostProcessor, only loaded before the App is loaded
* with the assistance of the "src/main/resources/META-INF/spring.factories". It is loaded by the
* ConfigFileApplicationListener.loadPostProcessors(), which looks for the list of classses in the factories.
*
* https://www.baeldung.com/spring-boot-environmentpostprocessor explains how to create AutoConfiguration classes for
* shared libraries. For the case of config, the reload of properties is detailed and explained on the docs at
* https://www.baeldung.com/spring-reloading-properties
*
* TODO: We need to hide the secrets, if they are defined here.
*
* @author [email protected]
*/
@Order(Ordered.LOWEST_PRECEDENCE)
class PropertiesLoggerEnvironmentPostProcessor : EnvironmentPostProcessor {
companion object {
/**
* Sharing is started immediately and never stops.
*/
private var numberOfPasses: Int = 0
private var systemProperties: MutableMap<String, String> = mutableMapOf()
}
override fun postProcessEnvironment(environment: ConfigurableEnvironment, application: SpringApplication) {
for (propertySource in findPropertiesPropertySources(environment)) {
// Avoid printing the systemProperties twice
if (propertySource.name.equals("systemProperties")) {
numberOfPasses = numberOfPasses?.inc()
} else {
System.out.println("******* \" + ${propertySource.getName()} + \" *******" )
}
// Adaptation of https://stackoverflow.com/questions/48212761/how-to-log-all-active-properties-of-a-spring-boot-application-before-the-beans-i/48212783#48212783
val propertyNames = propertySource.propertyNames
Arrays.sort(propertyNames)
for (propertyName in propertyNames) {
val resolvedProperty = environment!!.getProperty(propertyName!!)
val sourceProperty = propertySource.getProperty(propertyName).toString()
if (resolvedProperty == sourceProperty) {
if (propertySource.name.equals("systemProperties")) {
systemProperties.put(propertyName, resolvedProperty)
} else {
System.out.println( "${propertyName}=${resolvedProperty}" )
}
} else {
if (propertySource.name.equals("systemProperties")) {
systemProperties.put(propertyName, resolvedProperty ?: "")
} else {
System.out.println( "${propertyName}=${sourceProperty} ----- OVERRIDDEN =>>>>>> ${propertyName}=${resolvedProperty}" )
}
}
}
}
// The system properties show up twice in the process... The class is called twice and we only print it in the end.
if (numberOfPasses == 2) {
System.out.println("******* \" System Properties \" *******")
val sysPropertyNames = systemProperties.keys.sorted()
for (sysPropertyName in sysPropertyNames) {
val sysPropertyValue = systemProperties!!.get(sysPropertyName!!)
System.out.println( "${sysPropertyName}=${sysPropertyValue}" )
}
}
}
private fun findPropertiesPropertySources(environment: ConfigurableEnvironment): List<EnumerablePropertySource<*>> {
val propertiesPropertySources: MutableList<EnumerablePropertySource<*>> = LinkedList()
for (propertySource in environment!!.propertySources) {
if (propertySource is EnumerablePropertySource<*>) {
if (propertySource.name.equals("systemProperties") || propertySource.name.contains("applicationConfig:")) {
propertiesPropertySources.add(propertySource)
}
}
}
return propertiesPropertySources.asReversed()
}
}
/Users/marcellodesales/.gradle/jdks/jdk-14.0.2+12/Contents/Home/bin/java -XX:TieredStopAtLevel=1 -noverify -Dspring.output.ansi.enabled=always
....
....
2022-02-22T21:24:39 INFO [app=springAppName_IS_UNDEFINED,prof=observed,db,ppd_dev][tid=,sid=,sxp=][uid=] 74720 --- [ restartedMain] o.s.b.devtools.restart.ChangeableUrls : The Class-Path manifest attribute in /Users/marcellodesales/.gradle/caches/modules-2/files-2.1/com.sun.xml.bind/jaxb-core/2.2.7s-codec-1.11.jar
2022-02-22T21:24:39 INFO [app=springAppName_IS_UNDEFINED,prof=observed,db,ppd_dev][tid=,sid=,sxp=][uid=] 74720 --- [ restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
******* " + applicationConfig: [classpath:/application.yaml] + " *******
management.endpoint.health.show-details=always
management.endpoints.web.base-path=/actuator ==========>>>>>> OVERRIDDEN =========>>>>>> management.endpoints.web.base-path=/orchestrator/actuator
management.endpoints.web.exposure.include=*
management.metrics.web.server.request.autotime.enabled=true
spring.application.name=orchestrator-service
spring.boot.admin.client.enabled=false ==========>>>>>> OVERRIDDEN =========>>>>>> spring.boot.admin.client.enabled=true
spring.cloud.discovery.client.composite-indicator.enabled=false
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
******* " + applicationConfig: [classpath:/application-ppd_dev.yaml] + " *******
spring.datasource.url=jdbc:postgresql://localhost:6433/supercash?createDatabaseIfNotExist=true ==========>>>>>> OVERRIDDEN
=========>>>>>> spring.datasource.url=jdbc:postgresql://localhost:6433/supercash?createDatabaseIfNotExist\=true
spring.devtools.livereload.enabled=true
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.test-connection=true
******* " System Properties " *******
LOG_LEVEL_PATTERN=%5p [,%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]
PID=74720
com.sun.management.jmxremote=
file.encoding=UTF-8
ftion
java.vm.specification.version=14
java.vm.vendor=AdoptOpenJDK
java.vm.version=14.0.2+12
jboss.modules.system.pkgs=com.intellij.rt
jdk.debug=release
line.separator=
os.arch=x86_64
os.name=Mac OS X
os.version=10.16
user.name=marcellodesales
user.timezone=America/Los_Angeles
2022-02-22T21:25:16 DEBUG [app=orchestrator-service,prof=observed,db,ppd_dev][tid=,sid=,sxp=][uid=] 74720 --- [ restartedMain] o.s.b.c.c.ConfigFileApplicationListener : Activated activeProfiles observed,db,ppd_dev
_____ _____ _
/ ____| / ____| | |
| (___ _ _ _ __ ___ _ __| | __ _ ___| |__
2022-02-22T20:41:08 INFO [app=orchestrator-service,prof=observed,db,ppd_dev][tid=,sid=,sxp=][uid=] 74181 --- [ restartedMain]
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