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

前端 未结 5 1495
野性不改
野性不改 2020-12-14 15:51

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 profi

相关标签:
5条回答
  • 2020-12-14 16:37

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

    0 讨论(0)
  • 2020-12-14 16:51

    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
      }
    }
    
    0 讨论(0)
  • 2020-12-14 16:54

    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));
            }
        }
    
    }
    
    0 讨论(0)
  • 2020-12-14 16:55

    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
    ...
    ******************************************************************************
    
    0 讨论(0)
  • 2020-12-14 16:55

    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.

    0 讨论(0)
提交回复
热议问题