如何在bean实例化之前记录Spring Boot应用程序的所有活动属性?

问题描述 投票:0回答:2

已经有一个问题要求记录活动配置,有一个正确的答案,但问题是只有在所有bean都正确实例化时才会记录配置。我想记录所有属性,即使(主要是)应用程序在启动时崩溃。我的问题更具体:

如何在 Bean 实例化之前记录 Spring Boot 应用程序的所有活动属性?

java spring spring-boot logging startup
2个回答
36
投票
为此,您需要注册

ApplicationListener

。根据文档,要捕获的事件是 ApplicationPreparedEvent

ApplicationPreparedEvent 是 SpringApplication 启动时发布的事件, ApplicationContext 已完全准备好但未刷新。豆子 将加载定义并且环境已准备好使用 这个阶段。

主要方法如下所示:

public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(MyApplication.class); springApplication.addListeners(new PropertiesLogger()); springApplication.run(args); }
我已经重用了当前问题中引用的答案的代码,但我对其进行了修改,因为您获得的上下文尚未刷新,并且环境的结构与应用程序启动后的结构不完全相同。我还按属性源打印了属性:一个用于系统环境,一个用于系统属性,一个用于应用程序配置属性,等等...另请注意,

ApplicationPreparedEvent

可以被多次触发,并且属性仅在第一次打印。有关详细信息,请参阅 
Spring Boot 问题 #8899

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; } }
    

2
投票
📝 在应用程序准备好之前显示属性

    就我而言,我需要在加载上下文之前显示属性。在调试应用程序时,我想记录所有属性,以便我知道发生了什么......
☕ Kotlin 实现

https://www.baeldung.com/spring-boot-environmentpostprocessor所述,可以通过使用EnvironmentPostProcessor在加载上下文之前收集属性,EnvironmentPostProcessor通过调用ConfigFileApplicationListener实例化为Spring Factories的一部分.loadPostProcessors()。此时,您可以收集所有属性并以任何特定方式显示。

注意:在此事件期间加载属性时,上下文尚未准备好。伐木工也是如此。因此,可以在应用横幅(如果有)之前加载属性

    此外,弹簧工厂的条目必须存在,因此首先创建它
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]
    
© www.soinside.com 2019 - 2024. All rights reserved.