web-dev-qa-db-ja.com

Spring Bootアプリケーションでアクティブな構成をログに記録する方法は?

Spring BootにYAML構成を使用したいのですが、さまざまなプロファイルでアクティブになっているプロパティを示す単一のファイルを用意するのは非常に読みやすく便利です。残念ながら、application.ymlはかなり壊れやすい場合があります。

スペースの代わりにタブを使用するようなものは、プロパティが存在しない原因となり(私が見る限り警告なしで)、YAMLの不明な問題が原因で、アクティブなプロファイルが設定されていないことがよくあります。

そのため、現在アクティブなプロファイルとプロパティを取得してログに記録できるフックがあるかどうか疑問に思っていました。

同様に、application.ymlにエラーがありますか?それか、私が自分でYAMLを検証して、起動プロセスを終了できるようにするための手段のどちらかです。

20
Steve

私も同じ問題を抱えていて、プロファイル処理システムにいくつかの有用なログを出力するように指示するデバッグフラグがあればよいのにと思います。これを行う1つの可能な方法は、アプリケーションコンテキストのイベントリスナーを登録し、環境からプロファイルを出力することです。私自身はこの方法を試したことがないので、走行距離が異なる場合があります。ここで概説されているようなものだと思います:

アプリケーションコンテキスト初期化イベントにフックを追加するには?

次に、リスナーで次のようなことを行います。

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

試してみる価値があるかもしれません。おそらくそれを行うことができるもう1つの方法は、プロファイルを印刷する必要があるコードに注入される環境を宣言することです。つまり:

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

他の回答に加えて:コンテキスト更新イベントでアクティブプロパティをログに記録します。

Java 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("******************************************************************************");
    }
}

コトリン

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("******************************************************************************")
    }
}

次のような出力:

************************* 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
...
******************************************************************************
11
fightlight

アクチュエータ/ envサービスはプロパティを表示しますが、実際にアクティブなプロパティ値は表示しません。たいていの場合、アプリケーションのプロパティを

  • プロファイル固有のアプリケーションプロパティ
  • コマンドライン引数
  • OS環境変数

したがって、複数のソースで同じプロパティと異なる値を持つことになります。

スニペットベローは、起動時にアクティブなアプリケーションプロパティ値を出力します。

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

}
7

_application.yml_にエラーが含まれていると、起動時にエラーが発生します。それはあなたが「エラー」によって何を意味するかによると思います。確かに、YAMLの形式が適切でない場合は失敗します。また、たとえば_@ConfigurationProperties_としてマークされている_ignoreInvalidFields=true_を設定している場合、または変換できない値を設定している場合。これはかなり広い範囲のエラーです。

アクティブプロファイルは、おそらくEnvironment実装によって起動時にログに記録されます(ただし、いずれの場合も、それを取得してランチャーコードに記録するのは簡単です。EnvironmenttoString()は、アクティブプロファイルをリストします。考える)。 Actuatorを追加すると、/ envエンドポイントでアクティブプロファイル(およびその他)も利用できます。

2
Dave Syer

Bean /アプリケーションを初期化する前にアクティブなプロファイルを取得したい場合は、SpringBootServletInitializer/SpringApplication(つまり、JHipsterアプリケーションのApplicationWebXml)にカスタムバナーを登録することが唯一の方法です。

例えば.

@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.");
    }
    ...
}
1
Flávio Etrusco