I have created an EnvironmentPostProcessor in SpringBoot to fetch properties from database and attached it to the Spring's Environment
as a PropertySource.
This is the code I have:
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
Map<String, Object> propertySource = new HashMap<>();
// LOG SOMETHING HERE *******************
logger.error("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
String[] activeProfiles = environment.getActiveProfiles();
String[] defaultProfiles = environment.getDefaultProfiles();
// Do not pull db configuration when 'default' profile (used by Jenkins only) is run
if (activeProfiles.length == 0 && defaultProfiles[0] == "default") {
return;
}
// Load properties for Config schema
String dataSourceUrl = environment.getProperty("service.datasource.url");
String username = environment.getProperty("service.datasource.username");
String password = environment.getProperty("service.datasource.password");
String driver = environment.getProperty("service.datasource.driverClassName");
try {
// Build manually datasource to Config
DataSource ds = DataSourceBuilder
.create()
.username(username)
.password(password)
.url(dataSourceUrl)
.driverClassName(driver)
.build();
// Fetch all properties
PreparedStatement preparedStatement = ds.getConnection().prepareStatement("SELECT name, value FROM propertyConfig WHERE service = ?");
preparedStatement.setString(1, APP_NAME);
ResultSet rs = preparedStatement.executeQuery();
// Populate all properties into the property source
while (rs.next()) {
String propName = rs.getString("name");
propertySource.put(propName, rs.getString("value"));
}
// Create a custom property source with the highest precedence and add it to Spring Environment
environment.getPropertySources().addFirst(new MapPropertySource(PROPERTY_SOURCE_NAME, propertySource));
} catch (Exception e) {
throw new Exception("Error fetching properties from ServiceConfig");
}
}
And this is the main/META-INF/spring-factories
file had to be created:
# Environment Post Processor
org.springframework.boot.env.EnvironmentPostProcessor=com.blabla.config.ReadDbPropertiesPostProcessor
The code works well, it fetches from the db what I need. However, I would like to log information about this in case something wrong occurs, for instance if db is down I want to log an error and stop the app to start. My app is configured to use logger and not the console.
I have tried logging the error, throwing exceptions, also printing out something but my log is never logging this information.
How can I do to use the logger during this early spring stage? Is it possible to do this in anyway? Am I using EnvironmentPostProcessor wrongly?
The problem here is that logging system initialized only after spring context is initialized. When the log method is invoked the log system does not know what to do with the information and it does nothing.
There is no elegant way to solve this issue. You either get rid of spring-managed log system or use deferred log mechanisms (just like spring does internally).
To be able to use DeferredLog
you have to make sure that after context initialization the system will request to replay logs.
Here is one of the ways how it could be achieved:
@Component
public class MyEnvironmentPostProcessor implements
EnvironmentPostProcessor, ApplicationListener<ApplicationEvent> {
private static final DeferredLog log = new DeferredLog();
@Override
public void postProcessEnvironment(
ConfigurableEnvironment env, SpringApplication app) {
log.error("This should be printed");
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
log.replayTo(MyEnvironmentPostProcessor.class);
}
}
In this example every log message is cached in the DeferredLog
. And once the context initialized the system will call onApplicationEvent
. This method will replay all the cached log-events to the standard logger.
NOTE: I used ApplicationListener
here but you can use every convenient way. The idea is to call DeferredLog.replayTo()
once context initialized and it does not matter from which place you call it.
PS: The location of spring.factories
should be src/main/resources/META-INF
otherwise postProcessEnvironment
might not be invoked.
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