如何在 Spring Boot 应用程序中记录活动配置?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24448947/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 06:48:20  来源:igfitidea点击:

How to log the active configuration in a Spring Boot application?

springyamlspring-boot

提问by Steve

I would really like to use YAML config for Spring Boot, as I find it quite readable and useful to have a single file showing what properties are active in my different profiles. Unfortunately, I'm finding that setting properties in application.ymlcan be rather fragile.

我真的很想将 YAML 配置用于 Spring Boot,因为我发现让单个文件显示我的不同配置文件中哪些属性处于活动状态非常可读且有用。不幸的是,我发现设置属性application.yml可能相当脆弱。

Things like using a tab instead of spaces will cause properties to not exist (without warnings as far as I can see), and all too often I find that my active profiles are not being set, due to some unknown issue with my YAML.

使用制表符而不是空格之类的事情会导致属性不存在(据我所知没有警告),而且我经常发现我的活动配置文件没有被设置,因为我的 YAML 存在一些未知问题。

So I was wondering whether there are any hooks that would enable me to get hold of the currently active profiles and properties, so that I could log them.

所以我想知道是否有任何钩子可以让我掌握当前活动的配置文件和属性,以便我可以记录它们。

Similarly, is there a way to cause start-up to fail if the application.ymlcontains errors? Either that or a means for me to validate the YAML myself, so that I could kill the start-up process.

同样,如果application.yml包含错误,有没有办法导致启动失败?要么是这样,要么是我自己验证 YAML 的一种方式,这样我就可以终止启动过程。

采纳答案by user2337270

I had the same problem, and wish there was a debug flag that would tell the profile processing system to spit out some useful logging. One possible way of doing it would be to register an event listener for your application context, and print out the profiles from the environment. I haven't tried doing it this way myself, so your mileage may vary. I think maybe something like what's outlined here:

我遇到了同样的问题,希望有一个调试标志可以告诉配置文件处理系统吐出一些有用的日志记录。一种可能的方法是为您的应用程序上下文注册一个事件侦听器,并从环境中打印出配置文件。我自己没有尝试过这样做,所以你的里程可能会有所不同。我认为可能类似于此处概述的内容:

How to add a hook to the application context initialization event?

如何为应用程序上下文初始化事件添加一个钩子?

Then you'd do something like this in your listener:

然后你会在你的听众中做这样的事情:

System.out.println("Active profiles: " + Arrays.toString(ctxt.getEnvironment().getActiveProfiles()));

Might be worth a try. Another way you could probably do it would be to declare the Environment to be injected in the code where you need to print the profiles. I.e.:

也许值得尝试一下。另一种可能的方法是在需要打印配置文件的代码中声明要注入的环境。IE:

@Component
public class SomeClass {
  @Autowired
  private Environment env;
  ...
  private void dumpProfiles() {
    // Print whatever needed from env here
  }
}

回答by fightlight

In addition to other answers: logging active properties on context refreshed event.

除了其他答案:在上下文刷新事件上记录活动属性。

Java 8

爪哇 8

package mypackage;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@Slf4j
@Component
public class AppContextEventListener {

    @EventListener
    public void handleContextRefreshed(ContextRefreshedEvent event) {
        printActiveProperties((ConfigurableEnvironment) event.getApplicationContext().getEnvironment());
    }

    private void printActiveProperties(ConfigurableEnvironment env) {

        System.out.println("************************* ACTIVE APP PROPERTIES ******************************");

        List<MapPropertySource> propertySources = new ArrayList<>();

        env.getPropertySources().forEach(it -> {
            if (it instanceof MapPropertySource && it.getName().contains("applicationConfig")) {
                propertySources.add((MapPropertySource) it);
            }
        });

        propertySources.stream()
                .map(propertySource -> propertySource.getSource().keySet())
                .flatMap(Collection::stream)
                .distinct()
                .sorted()
                .forEach(key -> {
                    try {
                        System.out.println(key + "=" + env.getProperty(key));
                    } catch (Exception e) {
                        log.warn("{} -> {}", key, e.getMessage());
                    }
                });
        System.out.println("******************************************************************************");
    }
}

Kotlin

科特林

package mypackage

import mu.KLogging
import org.springframework.context.event.ContextRefreshedEvent
import org.springframework.context.event.EventListener
import org.springframework.core.env.ConfigurableEnvironment
import org.springframework.core.env.MapPropertySource
import org.springframework.stereotype.Component

@Component
class AppContextEventListener {

    companion object : KLogging()

    @EventListener
    fun handleContextRefreshed(event: ContextRefreshedEvent) {
        printActiveProperties(event.applicationContext.environment as ConfigurableEnvironment)
    }

    fun printActiveProperties(env: ConfigurableEnvironment) {
        println("************************* ACTIVE APP PROPERTIES ******************************")
        env.propertySources
                .filter { it.name.contains("applicationConfig") }
                .map { it as EnumerablePropertySource<*> }
                .map { it -> it.propertyNames.toList() }
                .flatMap { it }
                .distinctBy { it }
                .sortedBy { it }
                .forEach { it ->
                    try {
                        println("$it=${env.getProperty(it)}")
                    } catch (e: Exception) {
                        logger.warn("$it -> ${e.message}")
                    }
                }
        println("******************************************************************************")
    }
}

Output like:

输出如:

************************* ACTIVE APP PROPERTIES ******************************
server.port=3000
spring.application.name=my-app
...
2017-12-29 13:13:32.843  WARN 36252 --- [           main] m.AppContextEventListener        : spring.boot.admin.client.service-url -> Could not resolve placeholder 'management.address' in value "http://${management.address}:${server.port}"
...
spring.datasource.password=
spring.datasource.url=jdbc:postgresql://localhost/my_db?currentSchema=public
spring.datasource.username=db_user
...
******************************************************************************

回答by Krzysztof Ziomek

Actuator /env service displays properties, but it doesn't displays which property value is actually active. Very often you may want to override your application properties with

Actuator /env 服务显示属性,但不显示实际活动的属性值。很多时候你可能想用

  • profile-specific application properties
  • command line arguments
  • OS environment variables
  • 配置文件特定的应用程序属性
  • 命令行参数
  • 操作系统环境变量

Thus you will have the same property and different values in several sources.

因此,您将在多个来源中拥有相同的属性和不同的值。

Snippet bellow prints active application properties values on startup:

下面的代码段在启动时打印活动的应用程序属性值:

@Configuration
public class PropertiesLogger {
    private static final Logger log = LoggerFactory.getLogger(PropertiesLogger.class);

    @Autowired
    private AbstractEnvironment environment;

    @PostConstruct
    public void printProperties() {

        log.info("**** APPLICATION PROPERTIES SOURCES ****");

        Set<String> properties = new TreeSet<>();
        for (PropertiesPropertySource p : findPropertiesPropertySources()) {
            log.info(p.toString());
            properties.addAll(Arrays.asList(p.getPropertyNames()));
        }

        log.info("**** APPLICATION PROPERTIES VALUES ****");
        print(properties);

    }

    private List<PropertiesPropertySource> findPropertiesPropertySources() {
        List<PropertiesPropertySource> propertiesPropertySources = new LinkedList<>();
        for (PropertySource<?> propertySource : environment.getPropertySources()) {
            if (propertySource instanceof PropertiesPropertySource) {
                propertiesPropertySources.add((PropertiesPropertySource) propertySource);
            }
        }
        return propertiesPropertySources;
    }

    private void print(Set<String> properties) {
        for (String propertyName : properties) {
            log.info("{}={}", propertyName, environment.getProperty(propertyName));
        }
    }

}

回答by Dave Syer

If application.ymlcontains errors it will cause a failure on startup. I guess it depends what you mean by "error" though. Certainly it will fail if the YAML is not well formed. Also if you are setting @ConfigurationPropertiesthat are marked as ignoreInvalidFields=truefor instance, or if you set a value that cannot be converted. That's a pretty wide range of errors.

如果application.yml包含错误,则会导致启动失败。我想这取决于你所说的“错误”是什么意思。如果 YAML 格式不正确,它肯定会失败。此外,如果您正在设置@ConfigurationProperties标记ignoreInvalidFields=true为例如,或者如果您设置了一个无法转换的值。这是一个相当广泛的错误。

The active profiles will probably be logged on startup by the Environmentimplementation (but in any case it's easy for you to grab that and log it in your launcher code - the toString()of teh Environmentwill list the active profiles I think). Active profiles (and more) are also available in the /env endpoint if you add the Actuator.

活动配置文件可能会在启动时被Environment实现记录(但在任何情况下,您都可以轻松获取它并将其记录在您的启动器代码中 - toString()tehEnvironment将列出我认为的活动配置文件)。如果您添加执行器,则 /env 端点中也可以使用活动配置文件(以及更多)。

回答by Flávio Etrusco

In case you want to get the active profiles before initializing the beans/application, the only way I found is registering a custom Banner in your SpringBootServletInitializer/SpringApplication (i.e. ApplicationWebXml in a JHipster application).

如果您想在初始化 bean/应用程序之前获取活动配置文件,我发现的唯一方法是在您的 SpringBootServletInitializer/SpringApplication(即 JHipster 应用程序中的 ApplicationWebXml)中注册自定义横幅。

e.g.

例如

@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder)
{
    // set a default to use when no profile is configured.
    DefaultProfileUtil.addDefaultProfile(builder.application());
    return builder.sources(MyApp.class).banner(this::printBanner);
}

/** Custom 'banner' to obtain early access to the Spring configuration to validate and debug it. */
private void printBanner(Environment env, Class<?> sourceClass, PrintStream out)
{
    if (env.getProperty("spring.datasource.url") == null)
    {
        throw new RuntimeException(
            "'spring.datasource.url' is not configured! Check your configuration files and the value of 'spring.profiles.active' in your launcher.");
    }
    ...
}