我已经在SpringBoot中创建了一个EnvironmentPostProcessor来从数据库中获取属性,并将其作为PropertySource附加到Spring的Environment
。
这是我的代码:
@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");
}
}
这是必须创建的main/META-INF/spring-factories
文件:
# Environment Post Processor
org.springframework.boot.env.EnvironmentPostProcessor=com.blabla.config.ReadDbPropertiesPostProcessor
代码运行良好,它从数据库中获取我需要的东西。但是,我想记录信息,以防出现错误,例如,如果数据库关闭,我想记录错误并停止应用程序启动。我的应用程序配置为使用日志记录器,而不是控制台。
我试过记录错误,抛出异常,也打印出一些东西,但我的日志从来没有记录这些信息。
在这个早春的阶段,我该如何使用日志记录器呢?无论如何都有可能做到这一点吗?我是否错误地使用了EnvironmentPostProcessor?
5条答案
按热度按时间vxbzzdmp1#
这里的问题是日志系统只有在spring上下文初始化之后才初始化,当log方法被调用时,日志系统不知道该如何处理这些信息,它什么也不做。
没有优雅的方法来解决这个问题,要么摆脱spring管理的日志系统,要么使用延迟日志机制(就像spring内部做的那样)。
为了能够使用
DeferredLog
,您必须确保在上下文初始化之后系统将请求重放日志。以下是实现这一目标的方法之一:
在这个例子中,每个日志消息都缓存在
DeferredLog
中。一旦上下文初始化,系统将调用onApplicationEvent
。这个方法将重放所有缓存的日志事件到标准日志记录器。注意:我在这里使用了
ApplicationListener
,但是你可以使用任何方便的方法。这个想法是在上下文初始化后调用DeferredLog.replayTo()
,从哪里调用它并不重要。PS:
spring.factories
的位置应该是src/main/resources/META-INF
,否则可能不会调用postProcessEnvironment
。llew8vvj2#
正如接受的答案中所指出的,问题在于运行
EnvironmentPostProcessor
时日志记录系统尚未初始化。然而,使用类似
EnvironmentPostProcessor
中的静态DeferredLog
的机制来临时存储日志,然后在ApplicationListener<ApplicationPreparedEvent>
中重放它们(一旦日志记录系统初始化)也不起作用,因为EnvironmentPostProcessor
和ApplicationListener
是由不同的类加载器加载和初始化的。因此,用于
ApplicationListener
的Class的示例对用作EnvironmentPostProcessor
的Class的示例不可见(即使它们实际上是同一个类)。一种方法是在
EnvironmentPostProcessor
中使用System.setProperty(...)
设置要注销的内容,在ApplicationListener
中使用System.getProperty(...)
设置要注销的内容。这避免了Spring类加载器的问题。我强烈建议不要使用这种方法,但它确实有效。YMMV,但在我的例子中,我发现将自定义环境设置逻辑从
EnvironmentPostProcessor
移动到ApplicationListener<ApplicationPreparedEvent>
对我来说工作得很好,包括日志记录。Spring应用程序事件参考:https://docs.spring.io/spring-boot/docs/2.2.6.RELEASE/reference/html/spring-boot-features.html#boot-features-application-events-and-listeners
ApplicationContextInitializer
重放DeferredLog
:日志:
bqf10yzr3#
我发现!在执行postProcessEnvironment时调用addInitializers。
显示日志
c9qzyr3d4#
对于Kotlin中的编码:
dced5bon5#
我知道这个问题最初是在几年前 * 在 * Spring 2.4之前问到的,但是对于现在看到这个帖子的人来说,我希望这能有所帮助。
在环境后处理器的JavaDocs中,您可以看到:
“从Sping Boot 2.4开始,EnvironmentPostProcessor实现可以选择采用以下构造函数参数: