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

47,929

Solution 1

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.:

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

Solution 2

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

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

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

Solution 3

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

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

}

Solution 4

If application.yml contains 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 @ConfigurationProperties that are marked as ignoreInvalidFields=true for instance, or if you set a value that cannot be converted. That's a pretty wide range of errors.

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

Solution 5

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).

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.");
    }
    ...
}

Share:
47,929
Steve
Author by

Steve

Having developed web applications since the late 90s, these days, I operate as a solutions architect and developer, specialising in web applications, systems integration and service oriented architecture. My recent experience has involved working with the Java, Spring &amp; Hibernate stack and building business rules systems using Drools. The majority of this has been within banking, developing foreign exchange pricing systems and compliance systems, however for the past few years, the focus has been on designing and building API platforms to support web portals, which help the long term unemployed in their journey back to work. These days my non-coding time is mostly taken up by road cycling. In addition to this, I also enjoy mountain biking, track cycling, scuba diving and skiing. Given all the time I spend coding, I really feel the need to be physically active in my spare time!

Updated on July 09, 2022

Comments

  • Steve
    Steve almost 2 years

    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.yml can be rather fragile.

    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.

    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.yml contains errors? Either that or a means for me to validate the YAML myself, so that I could kill the start-up process.

  • Steve
    Steve over 9 years
    I adopted the approach of logging the results of getEnvironment().getActiveProfiles(), as part of start-up logging in my application main method.
  • pacoverflow
    pacoverflow over 7 years
    This printed out nothing for me.
  • Krzysztof Ziomek
    Krzysztof Ziomek over 7 years
    Nothing? Do you mean not even "**** APPLICATION PROPERTIES SOURCES ****" is printed on @PostConstruct? First, I woud make sure PropertiesLogger object is created at all in your application. Maybe some digging around @EnableAutoConfiguration may help.
  • pacoverflow
    pacoverflow over 7 years
    I mean it printed out "**** APPLICATION PROPERTIES SOURCES ****" followed by nothing followed by "**** APPLICATION PROPERTIES VALUES ****" followed by nothing.
  • Daniel Hári
    Daniel Hári about 7 years
    Works, but one problem with this is this only works after all beans constructed. If some bean throws exception on construct, property logs are not available.
  • Ortomala Lokni
    Ortomala Lokni over 6 years
    @DanielHári I've asked and answerd a question about this problem: stackoverflow.com/questions/48212761/…
  • KC Baltz
    KC Baltz almost 6 years
    I also only got the output pacoverflow got. I found this answer to be what I wanted: stackoverflow.com/a/48212783/9910
  • Flávio Etrusco
    Flávio Etrusco almost 6 years
    Edited my response and added the sample code. In case you are wondering, for my app I don't enumerate all properties there, just check the critical ones. But the Environment you receive in the Banner is a ConfigurableEnvironment, so you can iterate over getPropertySources() and enumerate from the sources that implement EnumerablePropertySource.
  • berlinguyinca
    berlinguyinca almost 5 years
    none of these solutions work for me to get anything with @ConfigurationProperties
  • fightlight
    fightlight almost 5 years
    @berlinguyinca Hi! What version of spring boot do you use? And why are you talking about @ConfigurationProperties? This method is for showing applied configuration properties from merged application.yml <- application-some-profile.yml <- etc or same with application.properties files at runtime. And I don't get how it is connected to @ConfigurationProperties.
  • berlinguyinca
    berlinguyinca almost 5 years
    version 2.x and we basically want to see the default values of all defined ConfigurationPropertie annotations.
  • fightlight
    fightlight almost 5 years
    @berlinguyinca just tried on Spring Boot 2.1.5.RELEASE project - works as expected ¯_(ツ)_/¯
  • berlinguyinca
    berlinguyinca almost 5 years
    yes the issue was, its not printing default values, only values which are explicitly set in the yaml file or set on the command line. But I wanted to print all possible configuration properties. Including defaults and not just explicitly specified ones.
  • ratijas
    ratijas over 4 years
    it seems like spring is doing it by default nowadays: INFO 22067 --- [ main] com.example.MainApplication : The following profiles are active: dev