From bacf0878afa144fe271f1f4dcb9e526de1f35d5b Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 15 Nov 2016 10:40:21 -0800 Subject: [PATCH 1/8] Polish --- ...rPropertiesAutoConfigurationNoSecurityTests.java | 13 ++++++------- .../web/servlet/MockMvcAutoConfiguration.java | 1 + 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/ManagementServerPropertiesAutoConfigurationNoSecurityTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/ManagementServerPropertiesAutoConfigurationNoSecurityTests.java index 2f2aca4486..af7700e92f 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/ManagementServerPropertiesAutoConfigurationNoSecurityTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/ManagementServerPropertiesAutoConfigurationNoSecurityTests.java @@ -48,17 +48,16 @@ public class ManagementServerPropertiesAutoConfigurationNoSecurityTests { @Test public void securitySettingsIgnoredWithoutSpringSecurity() { - ManagementServerProperties properties = - load("management.security.enabled=false"); + ManagementServerProperties properties = load("management.security.enabled=false"); assertThat(properties.getSecurity().isEnabled()).isFalse(); } public ManagementServerProperties load(String... environment) { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); - EnvironmentTestUtils.addEnvironment(ctx, environment); - ctx.register(ManagementServerPropertiesAutoConfiguration.class); - ctx.refresh(); - this.context = ctx; + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(context, environment); + context.register(ManagementServerPropertiesAutoConfiguration.class); + context.refresh(); + this.context = context; return this.context.getBean(ManagementServerProperties.class); } diff --git a/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/MockMvcAutoConfiguration.java b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/MockMvcAutoConfiguration.java index 401096a8f4..6fbce1b698 100644 --- a/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/MockMvcAutoConfiguration.java +++ b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/MockMvcAutoConfiguration.java @@ -51,6 +51,7 @@ import org.springframework.web.servlet.DispatcherServlet; public class MockMvcAutoConfiguration { private final WebApplicationContext context; + private final WebMvcProperties webMvcProperties; MockMvcAutoConfiguration(WebApplicationContext context, From 0e3a3df6f495ed0539ef9535b9d95071d20e2fb1 Mon Sep 17 00:00:00 2001 From: Madhura Bhave Date: Tue, 15 Nov 2016 13:57:16 -0800 Subject: [PATCH 2/8] Return log levels in `/loggers` endpoint payload Update `LoggersEndpoint` to additionally return the log levels actually supported by the system. Fixes gh-7396 --- spring-boot-actuator/pom.xml | 5 ++++ .../actuate/endpoint/LoggersEndpoint.java | 27 ++++++++++++++----- .../EndpointAutoConfigurationTests.java | 4 ++- .../endpoint/LoggersEndpointTests.java | 18 ++++++++++--- .../endpoint/mvc/LoggersMvcEndpointTests.java | 12 ++++++--- .../boot/logging/AbstractLoggingSystem.java | 10 ++++++- .../boot/logging/LoggingSystem.java | 11 ++++++++ .../boot/logging/java/JavaLoggingSystem.java | 6 +++++ .../logging/log4j2/Log4J2LoggingSystem.java | 6 +++++ .../logging/logback/LogbackLoggingSystem.java | 6 +++++ .../logging/java/JavaLoggingSystemTests.java | 8 ++++++ .../log4j2/Log4J2LoggingSystemTests.java | 7 +++++ .../logback/LogbackLoggingSystemTests.java | 8 ++++++ 13 files changed, 114 insertions(+), 14 deletions(-) diff --git a/spring-boot-actuator/pom.xml b/spring-boot-actuator/pom.xml index 7434a07c05..984b8cbf5d 100644 --- a/spring-boot-actuator/pom.xml +++ b/spring-boot-actuator/pom.xml @@ -338,6 +338,11 @@ hsqldb test + + org.skyscreamer + jsonassert + test + org.springframework spring-test diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/LoggersEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/LoggersEndpoint.java index 334d578295..fbb999a608 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/LoggersEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/LoggersEndpoint.java @@ -20,6 +20,9 @@ import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; +import java.util.NavigableSet; +import java.util.Set; +import java.util.TreeSet; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.logging.LogLevel; @@ -35,8 +38,7 @@ import org.springframework.util.Assert; * @since 1.5.0 */ @ConfigurationProperties(prefix = "endpoints.loggers") -public class LoggersEndpoint - extends AbstractEndpoint> { +public class LoggersEndpoint extends AbstractEndpoint> { private final LoggingSystem loggingSystem; @@ -51,18 +53,31 @@ public class LoggersEndpoint } @Override - public Map invoke() { + public Map invoke() { Collection configurations = this.loggingSystem .getLoggerConfigurations(); if (configurations == null) { return Collections.emptyMap(); } - Map result = new LinkedHashMap( + Map result = new LinkedHashMap(); + result.put("levels", getLevels()); + result.put("loggers", getLoggers(configurations)); + return result; + } + + private NavigableSet getLevels() { + Set levels = this.loggingSystem.getSupportedLogLevels(); + return new TreeSet(levels).descendingSet(); + } + + private Map getLoggers( + Collection configurations) { + Map loggers = new LinkedHashMap( configurations.size()); for (LoggerConfiguration configuration : configurations) { - result.put(configuration.getName(), new LoggerLevels(configuration)); + loggers.put(configuration.getName(), new LoggerLevels(configuration)); } - return result; + return loggers; } public LoggerLevels invoke(String name) { diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfigurationTests.java index e41fc55a8c..444e014314 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfigurationTests.java @@ -130,7 +130,9 @@ public class EndpointAutoConfigurationTests { public void loggersEndpointHasLoggers() throws Exception { load(CustomLoggingConfig.class, EndpointAutoConfiguration.class); LoggersEndpoint endpoint = this.context.getBean(LoggersEndpoint.class); - Map loggers = endpoint.invoke(); + Map result = endpoint.invoke(); + Map loggers = (Map) result + .get("loggers"); assertThat(loggers.size()).isGreaterThan(0); } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/LoggersEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/LoggersEndpointTests.java index 21e27f7b8c..4d0aade680 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/LoggersEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/LoggersEndpointTests.java @@ -17,6 +17,9 @@ package org.springframework.boot.actuate.endpoint; import java.util.Collections; +import java.util.EnumSet; +import java.util.Map; +import java.util.Set; import org.junit.Test; @@ -45,12 +48,21 @@ public class LoggersEndpointTests extends AbstractEndpointTests } @Test + @SuppressWarnings("unchecked") public void invokeShouldReturnConfigurations() throws Exception { given(getLoggingSystem().getLoggerConfigurations()).willReturn(Collections .singletonList(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG))); - LoggerLevels levels = getEndpointBean().invoke().get("ROOT"); - assertThat(levels.getConfiguredLevel()).isNull(); - assertThat(levels.getEffectiveLevel()).isEqualTo("DEBUG"); + given(getLoggingSystem().getSupportedLogLevels()) + .willReturn(EnumSet.allOf(LogLevel.class)); + Map result = getEndpointBean().invoke(); + Map loggers = (Map) result + .get("loggers"); + Set levels = (Set) result.get("levels"); + LoggerLevels rootLevels = loggers.get("ROOT"); + assertThat(rootLevels.getConfiguredLevel()).isNull(); + assertThat(rootLevels.getEffectiveLevel()).isEqualTo("DEBUG"); + assertThat(levels).containsExactly(LogLevel.OFF, LogLevel.FATAL, LogLevel.ERROR, + LogLevel.WARN, LogLevel.INFO, LogLevel.DEBUG, LogLevel.TRACE); } public void invokeWhenNameSpecifiedShouldReturnLevels() throws Exception { diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/LoggersMvcEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/LoggersMvcEndpointTests.java index ab769ee67f..dbf81ed441 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/LoggersMvcEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/LoggersMvcEndpointTests.java @@ -17,6 +17,7 @@ package org.springframework.boot.actuate.endpoint.mvc; import java.util.Collections; +import java.util.EnumSet; import org.junit.After; import org.junit.Before; @@ -80,18 +81,23 @@ public class LoggersMvcEndpointTests { .alwaysDo(MockMvcResultHandlers.print()).build(); } + @Before @After - public void reset() { + public void resetMocks() { Mockito.reset(this.loggingSystem); + given(this.loggingSystem.getSupportedLogLevels()) + .willReturn(EnumSet.allOf(LogLevel.class)); } @Test public void getLoggerShouldReturnAllLoggerConfigurations() throws Exception { given(this.loggingSystem.getLoggerConfigurations()).willReturn(Collections .singletonList(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG))); + String expected = "{\"levels\":[\"OFF\",\"FATAL\",\"ERROR\",\"WARN\",\"INFO\",\"DEBUG\",\"TRACE\"]," + + "\"loggers\":{\"ROOT\":{\"configuredLevel\":null,\"effectiveLevel\":\"DEBUG\"}}}"; + System.out.println(expected); this.mvc.perform(get("/loggers")).andExpect(status().isOk()) - .andExpect(content().string(equalTo("{\"ROOT\":{\"configuredLevel\":" - + "null,\"effectiveLevel\":\"DEBUG\"}}"))); + .andExpect(content().json(expected)); } @Test diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/AbstractLoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/AbstractLoggingSystem.java index d3521e2ced..711e180e64 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/AbstractLoggingSystem.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/AbstractLoggingSystem.java @@ -17,7 +17,9 @@ package org.springframework.boot.logging; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.Map; +import java.util.Set; import org.springframework.core.env.Environment; import org.springframework.core.io.ClassPathResource; @@ -193,7 +195,9 @@ public abstract class AbstractLoggingSystem extends LoggingSystem { public void map(LogLevel system, T nativeLevel) { this.systemToNative.put(system, nativeLevel); - this.nativeToSystem.put(nativeLevel, system); + if (!this.nativeToSystem.containsKey(nativeLevel)) { + this.nativeToSystem.put(nativeLevel, system); + } } public LogLevel convertNativeToSystem(T level) { @@ -204,6 +208,10 @@ public abstract class AbstractLoggingSystem extends LoggingSystem { return this.systemToNative.get(level); } + public Set getSupported() { + return new LinkedHashSet(this.nativeToSystem.values()); + } + } } diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystem.java index a2e1758037..a8a53462b5 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystem.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystem.java @@ -17,9 +17,11 @@ package org.springframework.boot.logging; import java.util.Collections; +import java.util.EnumSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; @@ -94,6 +96,15 @@ public abstract class LoggingSystem { return null; } + /** + * Returns a set of the {@link LogLevel LogLevels} that are actually supported by the + * logging system. + * @return the supported levels + */ + public Set getSupportedLogLevels() { + return EnumSet.allOf(LogLevel.class); + } + /** * Sets the logging level for a given logger. * @param loggerName the name of the logger to set diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/java/JavaLoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/java/JavaLoggingSystem.java index d23a99d337..da05c83f7f 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/java/JavaLoggingSystem.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/java/JavaLoggingSystem.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; +import java.util.Set; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.Logger; @@ -113,6 +114,11 @@ public class JavaLoggingSystem extends AbstractLoggingSystem { } } + @Override + public Set getSupportedLogLevels() { + return LEVELS.getSupported(); + } + @Override public void setLogLevel(String loggerName, LogLevel level) { Assert.notNull(level, "Level must not be null"); diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java index bfca2884d1..3ccc3c62fe 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java @@ -22,6 +22,7 @@ import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Set; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; @@ -197,6 +198,11 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem { getLoggerContext().reconfigure(); } + @Override + public Set getSupportedLogLevels() { + return LEVELS.getSupported(); + } + @Override public void setLogLevel(String loggerName, LogLevel logLevel) { Level level = LEVELS.convertSystemToNative(logLevel); diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java index f58ba8f57e..c71971466e 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java @@ -22,6 +22,7 @@ import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Set; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.LoggerContext; @@ -238,6 +239,11 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { return new LoggerConfiguration(logger.getName(), level, effectiveLevel); } + @Override + public Set getSupportedLogLevels() { + return LEVELS.getSupported(); + } + @Override public void setLogLevel(String loggerName, LogLevel level) { ch.qos.logback.classic.Logger logger = getLogger(loggerName); diff --git a/spring-boot/src/test/java/org/springframework/boot/logging/java/JavaLoggingSystemTests.java b/spring-boot/src/test/java/org/springframework/boot/logging/java/JavaLoggingSystemTests.java index 12eb04eb9c..bc8a53b83f 100644 --- a/spring-boot/src/test/java/org/springframework/boot/logging/java/JavaLoggingSystemTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/logging/java/JavaLoggingSystemTests.java @@ -19,6 +19,7 @@ package org.springframework.boot.logging.java; import java.io.File; import java.io.FileFilter; import java.io.IOException; +import java.util.EnumSet; import java.util.List; import java.util.Locale; import java.util.logging.Level; @@ -148,6 +149,13 @@ public class JavaLoggingSystemTests extends AbstractLoggingSystemTests { null); } + @Test + public void getSupportedLevels() { + assertThat(this.loggingSystem.getSupportedLogLevels()) + .isEqualTo(EnumSet.of(LogLevel.TRACE, LogLevel.DEBUG, LogLevel.INFO, + LogLevel.WARN, LogLevel.ERROR, LogLevel.OFF)); + } + @Test public void setLevel() throws Exception { this.loggingSystem.beforeInitialize(); diff --git a/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java b/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java index 1e06c7c891..b2abdb969f 100644 --- a/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java @@ -22,6 +22,7 @@ import java.io.File; import java.io.FileReader; import java.util.ArrayList; import java.util.Collections; +import java.util.EnumSet; import java.util.List; import com.fasterxml.jackson.databind.ObjectMapper; @@ -122,6 +123,12 @@ public class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { this.loggingSystem.initialize(null, "classpath:log4j2-nonexistent.xml", null); } + @Test + public void getSupportedLevels() { + assertThat(this.loggingSystem.getSupportedLogLevels()) + .isEqualTo(EnumSet.allOf(LogLevel.class)); + } + @Test public void setLevel() throws Exception { this.loggingSystem.beforeInitialize(); diff --git a/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java b/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java index 072b9b8210..b7f2e117b0 100644 --- a/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java @@ -18,6 +18,7 @@ package org.springframework.boot.logging.logback; import java.io.File; import java.io.FileReader; +import java.util.EnumSet; import java.util.List; import java.util.logging.Handler; import java.util.logging.LogManager; @@ -162,6 +163,13 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests { "classpath:logback-nonexistent.xml", null); } + @Test + public void getSupportedLevels() { + assertThat(this.loggingSystem.getSupportedLogLevels()) + .isEqualTo(EnumSet.of(LogLevel.TRACE, LogLevel.DEBUG, LogLevel.INFO, + LogLevel.WARN, LogLevel.ERROR, LogLevel.OFF)); + } + @Test public void setLevel() throws Exception { this.loggingSystem.beforeInitialize(); From e7db7adfb81f47e466a9da06e3383dff0b34dd83 Mon Sep 17 00:00:00 2001 From: Madhura Bhave Date: Mon, 14 Nov 2016 16:35:36 -0800 Subject: [PATCH 3/8] Rename ApplicationStartedEvent Rename `ApplicationStartedEvent` to `ApplicationStartingEvent` to avoid confusion. Fixes gh-7381 --- .../restart/RestartApplicationListener.java | 8 ++-- .../RestartApplicationListenerTests.java | 4 +- .../boot/SpringApplication.java | 2 +- .../boot/SpringApplicationRunListener.java | 2 +- .../boot/SpringApplicationRunListeners.java | 4 +- .../event/ApplicationStartedEvent.java | 4 +- .../event/ApplicationStartingEvent.java | 47 +++++++++++++++++++ .../event/EventPublishingRunListener.java | 3 +- ...baseServiceLocatorApplicationListener.java | 6 +-- .../logging/LoggingApplicationListener.java | 10 ++-- .../boot/SpringApplicationTests.java | 7 ++- ...ngApplicationListenerIntegrationTests.java | 12 +++-- .../LoggingApplicationListenerTests.java | 16 +++---- .../system/ApplicationPidFileWriterTests.java | 6 +-- 14 files changed, 93 insertions(+), 38 deletions(-) create mode 100644 spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationStartingEvent.java diff --git a/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/RestartApplicationListener.java b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/RestartApplicationListener.java index 2ad0723fc5..8cd7d81229 100644 --- a/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/RestartApplicationListener.java +++ b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/RestartApplicationListener.java @@ -19,7 +19,7 @@ package org.springframework.boot.devtools.restart; import org.springframework.boot.context.event.ApplicationFailedEvent; import org.springframework.boot.context.event.ApplicationPreparedEvent; import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.boot.context.event.ApplicationStartingEvent; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.core.Ordered; @@ -41,8 +41,8 @@ public class RestartApplicationListener @Override public void onApplicationEvent(ApplicationEvent event) { - if (event instanceof ApplicationStartedEvent) { - onApplicationStartedEvent((ApplicationStartedEvent) event); + if (event instanceof ApplicationStartingEvent) { + onApplicationStartingEvent((ApplicationStartingEvent) event); } if (event instanceof ApplicationPreparedEvent) { Restarter.getInstance() @@ -57,7 +57,7 @@ public class RestartApplicationListener } } - private void onApplicationStartedEvent(ApplicationStartedEvent event) { + private void onApplicationStartingEvent(ApplicationStartingEvent event) { // It's too early to use the Spring environment but we should still allow // users to disable restart using a System property. String enabled = System.getProperty(ENABLED_PROPERTY); diff --git a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestartApplicationListenerTests.java b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestartApplicationListenerTests.java index e87b713e40..31acad0f0e 100644 --- a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestartApplicationListenerTests.java +++ b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/RestartApplicationListenerTests.java @@ -24,7 +24,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.context.event.ApplicationFailedEvent; import org.springframework.boot.context.event.ApplicationPreparedEvent; import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.boot.context.event.ApplicationStartingEvent; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.Ordered; import org.springframework.test.util.ReflectionTestUtils; @@ -92,7 +92,7 @@ public class RestartApplicationListenerTests { SpringApplication application = new SpringApplication(); ConfigurableApplicationContext context = mock( ConfigurableApplicationContext.class); - listener.onApplicationEvent(new ApplicationStartedEvent(application, ARGS)); + listener.onApplicationEvent(new ApplicationStartingEvent(application, ARGS)); assertThat(Restarter.getInstance()).isNotEqualTo(nullValue()); assertThat(Restarter.getInstance().isFinished()).isFalse(); listener.onApplicationEvent( diff --git a/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java b/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java index 8740f78b7b..b66b83f814 100644 --- a/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java +++ b/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java @@ -299,7 +299,7 @@ public class SpringApplication { FailureAnalyzers analyzers = null; configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); - listeners.started(); + listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); diff --git a/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListener.java b/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListener.java index 6369d2e9b3..ecaabfe392 100644 --- a/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListener.java +++ b/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListener.java @@ -37,7 +37,7 @@ public interface SpringApplicationRunListener { * Called immediately when the run method has first started. Can be used for very * early initialization. */ - void started(); + void starting(); /** * Called once the environment has been prepared, but before the diff --git a/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListeners.java b/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListeners.java index ec651f79b2..c177807551 100644 --- a/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListeners.java +++ b/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListeners.java @@ -43,9 +43,9 @@ class SpringApplicationRunListeners { this.listeners = new ArrayList(listeners); } - public void started() { + public void starting() { for (SpringApplicationRunListener listener : this.listeners) { - listener.started(); + listener.starting(); } } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationStartedEvent.java b/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationStartedEvent.java index 9973a4cc3c..f075302c2a 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationStartedEvent.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationStartedEvent.java @@ -29,9 +29,11 @@ import org.springframework.core.env.Environment; * state too much at this early stage since it might be modified later in the lifecycle. * * @author Dave Syer + * @deprecated since 1.5.0 in favor of {@link ApplicationStartingEvent} */ +@Deprecated @SuppressWarnings("serial") -public class ApplicationStartedEvent extends SpringApplicationEvent { +public class ApplicationStartedEvent extends ApplicationStartingEvent { /** * Create a new {@link ApplicationStartedEvent} instance. diff --git a/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationStartingEvent.java b/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationStartingEvent.java new file mode 100644 index 0000000000..d645fa6f3b --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/context/event/ApplicationStartingEvent.java @@ -0,0 +1,47 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.context.event; + +import org.springframework.boot.SpringApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationListener; +import org.springframework.core.env.Environment; + +/** + * Event published as early as conceivably possible as soon as a {@link SpringApplication} + * has been started - before the {@link Environment} or {@link ApplicationContext} is + * available, but after the {@link ApplicationListener}s have been registered. The source + * of the event is the {@link SpringApplication} itself, but beware of using its internal + * state too much at this early stage since it might be modified later in the lifecycle. + * + * @author Phillip Webb + * @author Madhura Bhave + * @since 1.5.0 + */ +@SuppressWarnings("serial") +public class ApplicationStartingEvent extends SpringApplicationEvent { + + /** + * Create a new {@link ApplicationStartingEvent} instance. + * @param application the current application + * @param args the arguments the application is running with + */ + public ApplicationStartingEvent(SpringApplication application, String[] args) { + super(application, args); + } + +} diff --git a/spring-boot/src/main/java/org/springframework/boot/context/event/EventPublishingRunListener.java b/spring-boot/src/main/java/org/springframework/boot/context/event/EventPublishingRunListener.java index de26352fb6..4e2f3d9e4b 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/event/EventPublishingRunListener.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/event/EventPublishingRunListener.java @@ -58,7 +58,8 @@ public class EventPublishingRunListener implements SpringApplicationRunListener, } @Override - public void started() { + @SuppressWarnings("deprecation") + public void starting() { this.initialMulticaster .multicastEvent(new ApplicationStartedEvent(this.application, this.args)); } diff --git a/spring-boot/src/main/java/org/springframework/boot/liquibase/LiquibaseServiceLocatorApplicationListener.java b/spring-boot/src/main/java/org/springframework/boot/liquibase/LiquibaseServiceLocatorApplicationListener.java index 6a32be7677..8cfc967d3e 100644 --- a/spring-boot/src/main/java/org/springframework/boot/liquibase/LiquibaseServiceLocatorApplicationListener.java +++ b/spring-boot/src/main/java/org/springframework/boot/liquibase/LiquibaseServiceLocatorApplicationListener.java @@ -21,7 +21,7 @@ import liquibase.servicelocator.ServiceLocator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.boot.context.event.ApplicationStartingEvent; import org.springframework.context.ApplicationListener; import org.springframework.util.ClassUtils; @@ -33,13 +33,13 @@ import org.springframework.util.ClassUtils; * @author Dave Syer */ public class LiquibaseServiceLocatorApplicationListener - implements ApplicationListener { + implements ApplicationListener { private static final Log logger = LogFactory .getLog(LiquibaseServiceLocatorApplicationListener.class); @Override - public void onApplicationEvent(ApplicationStartedEvent event) { + public void onApplicationEvent(ApplicationStartingEvent event) { if (ClassUtils.isPresent("liquibase.servicelocator.ServiceLocator", null)) { new LiquibasePresent().replaceServiceLocator(); } diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/LoggingApplicationListener.java b/spring-boot/src/main/java/org/springframework/boot/logging/LoggingApplicationListener.java index 01ac287e04..e3650be579 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/LoggingApplicationListener.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/LoggingApplicationListener.java @@ -30,7 +30,7 @@ import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.context.event.ApplicationFailedEvent; import org.springframework.boot.context.event.ApplicationPreparedEvent; -import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.boot.context.event.ApplicationStartingEvent; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; @@ -161,7 +161,7 @@ public class LoggingApplicationListener implements GenericApplicationListener { LOG_LEVEL_LOGGERS.add(LogLevel.DEBUG, "org.hibernate.SQL"); } - private static Class[] EVENT_TYPES = { ApplicationStartedEvent.class, + private static Class[] EVENT_TYPES = { ApplicationStartingEvent.class, ApplicationEnvironmentPreparedEvent.class, ApplicationPreparedEvent.class, ContextClosedEvent.class }; @@ -201,8 +201,8 @@ public class LoggingApplicationListener implements GenericApplicationListener { @Override public void onApplicationEvent(ApplicationEvent event) { - if (event instanceof ApplicationStartedEvent) { - onApplicationStartedEvent((ApplicationStartedEvent) event); + if (event instanceof ApplicationStartingEvent) { + onApplicationStartingEvent((ApplicationStartingEvent) event); } else if (event instanceof ApplicationEnvironmentPreparedEvent) { onApplicationEnvironmentPreparedEvent( @@ -220,7 +220,7 @@ public class LoggingApplicationListener implements GenericApplicationListener { } } - private void onApplicationStartedEvent(ApplicationStartedEvent event) { + private void onApplicationStartingEvent(ApplicationStartingEvent event) { this.loggingSystem = LoggingSystem .get(event.getSpringApplication().getClassLoader()); this.loggingSystem.beforeInitialize(); diff --git a/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java b/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java index d24a1afc93..d21326b900 100644 --- a/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java @@ -44,7 +44,7 @@ import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletCon import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.context.event.ApplicationPreparedEvent; import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.boot.context.event.ApplicationStartingEvent; import org.springframework.boot.testutil.InternalOutputCapture; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -302,6 +302,7 @@ public class SpringApplicationTests { } @Test + @SuppressWarnings("deprecation") public void eventsOrder() { SpringApplication application = new SpringApplication(ExampleConfig.class); application.setWebEnvironment(false); @@ -316,7 +317,9 @@ public class SpringApplicationTests { application.addListeners(new ApplicationRunningEventListener()); this.context = application.run(); assertThat(events).hasSize(5); - assertThat(events.get(0)).isInstanceOf(ApplicationStartedEvent.class); + assertThat(events.get(0)).isInstanceOf( + org.springframework.boot.context.event.ApplicationStartedEvent.class); + assertThat(events.get(0)).isInstanceOf(ApplicationStartingEvent.class); assertThat(events.get(1)).isInstanceOf(ApplicationEnvironmentPreparedEvent.class); assertThat(events.get(2)).isInstanceOf(ApplicationPreparedEvent.class); assertThat(events.get(3)).isInstanceOf(ContextRefreshedEvent.class); diff --git a/spring-boot/src/test/java/org/springframework/boot/logging/LoggingApplicationListenerIntegrationTests.java b/spring-boot/src/test/java/org/springframework/boot/logging/LoggingApplicationListenerIntegrationTests.java index 19bc4ba6e6..7fd4b9dcb2 100644 --- a/spring-boot/src/test/java/org/springframework/boot/logging/LoggingApplicationListenerIntegrationTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/logging/LoggingApplicationListenerIntegrationTests.java @@ -22,7 +22,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.boot.context.event.ApplicationStartingEvent; import org.springframework.boot.testutil.InternalOutputCapture; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; @@ -56,16 +56,18 @@ public class LoggingApplicationListenerIntegrationTests { @Test public void loggingPerformedDuringChildApplicationStartIsNotLost() { new SpringApplicationBuilder(Config.class).web(false).child(Config.class) - .web(false).listeners(new ApplicationListener() { + .web(false) + .listeners(new ApplicationListener() { private final Logger logger = LoggerFactory.getLogger(getClass()); @Override - public void onApplicationEvent(ApplicationStartedEvent event) { - this.logger.info("Child application started"); + public void onApplicationEvent(ApplicationStartingEvent event) { + this.logger.info("Child application starting"); } + }).run(); - assertThat(this.outputCapture.toString()).contains("Child application started"); + assertThat(this.outputCapture.toString()).contains("Child application starting"); } @Component diff --git a/spring-boot/src/test/java/org/springframework/boot/logging/LoggingApplicationListenerTests.java b/spring-boot/src/test/java/org/springframework/boot/logging/LoggingApplicationListenerTests.java index cbc2647c68..4ef3fb5305 100644 --- a/spring-boot/src/test/java/org/springframework/boot/logging/LoggingApplicationListenerTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/logging/LoggingApplicationListenerTests.java @@ -39,7 +39,7 @@ import org.slf4j.bridge.SLF4JBridgeHandler; import org.springframework.boot.ApplicationPid; import org.springframework.boot.SpringApplication; import org.springframework.boot.context.event.ApplicationFailedEvent; -import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.boot.context.event.ApplicationStartingEvent; import org.springframework.boot.logging.java.JavaLoggingSystem; import org.springframework.boot.testutil.InternalOutputCapture; import org.springframework.context.event.ContextClosedEvent; @@ -86,7 +86,7 @@ public class LoggingApplicationListenerTests { LogManager.getLogManager().readConfiguration( JavaLoggingSystem.class.getResourceAsStream("logging.properties")); this.initializer.onApplicationEvent( - new ApplicationStartedEvent(new SpringApplication(), NO_ARGS)); + new ApplicationStartingEvent(new SpringApplication(), NO_ARGS)); new File("target/foo.log").delete(); new File(tmpDir() + "/spring.log").delete(); } @@ -342,7 +342,7 @@ public class LoggingApplicationListenerTests { public void parseArgsDoesntReplace() throws Exception { this.initializer.setSpringBootLogging(LogLevel.ERROR); this.initializer.setParseArgs(false); - this.initializer.onApplicationEvent(new ApplicationStartedEvent( + this.initializer.onApplicationEvent(new ApplicationStartingEvent( this.springApplication, new String[] { "--debug" })); this.initializer.initialize(this.context.getEnvironment(), this.context.getClassLoader()); @@ -387,7 +387,7 @@ public class LoggingApplicationListenerTests { System.setProperty(LoggingSystem.class.getName(), TestShutdownHandlerLoggingSystem.class.getName()); listener.onApplicationEvent( - new ApplicationStartedEvent(new SpringApplication(), NO_ARGS)); + new ApplicationStartingEvent(new SpringApplication(), NO_ARGS)); listener.initialize(this.context.getEnvironment(), this.context.getClassLoader()); assertThat(listener.shutdownHook).isNull(); } @@ -400,7 +400,7 @@ public class LoggingApplicationListenerTests { TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, "logging.register_shutdown_hook=true"); listener.onApplicationEvent( - new ApplicationStartedEvent(new SpringApplication(), NO_ARGS)); + new ApplicationStartingEvent(new SpringApplication(), NO_ARGS)); listener.initialize(this.context.getEnvironment(), this.context.getClassLoader()); assertThat(listener.shutdownHook).isNotNull(); listener.shutdownHook.start(); @@ -413,7 +413,7 @@ public class LoggingApplicationListenerTests { System.setProperty(LoggingSystem.SYSTEM_PROPERTY, TestCleanupLoggingSystem.class.getName()); this.initializer.onApplicationEvent( - new ApplicationStartedEvent(this.springApplication, new String[0])); + new ApplicationStartingEvent(this.springApplication, new String[0])); TestCleanupLoggingSystem loggingSystem = (TestCleanupLoggingSystem) ReflectionTestUtils .getField(this.initializer, "loggingSystem"); assertThat(loggingSystem.cleanedUp).isFalse(); @@ -426,7 +426,7 @@ public class LoggingApplicationListenerTests { System.setProperty(LoggingSystem.SYSTEM_PROPERTY, TestCleanupLoggingSystem.class.getName()); this.initializer.onApplicationEvent( - new ApplicationStartedEvent(this.springApplication, new String[0])); + new ApplicationStartingEvent(this.springApplication, new String[0])); TestCleanupLoggingSystem loggingSystem = (TestCleanupLoggingSystem) ReflectionTestUtils .getField(this.initializer, "loggingSystem"); assertThat(loggingSystem.cleanedUp).isFalse(); @@ -472,7 +472,7 @@ public class LoggingApplicationListenerTests { System.setProperty(LoggingSystem.SYSTEM_PROPERTY, TestCleanupLoggingSystem.class.getName()); this.initializer.onApplicationEvent( - new ApplicationStartedEvent(this.springApplication, new String[0])); + new ApplicationStartingEvent(this.springApplication, new String[0])); TestCleanupLoggingSystem loggingSystem = (TestCleanupLoggingSystem) ReflectionTestUtils .getField(this.initializer, "loggingSystem"); assertThat(loggingSystem.cleanedUp).isFalse(); diff --git a/spring-boot/src/test/java/org/springframework/boot/system/ApplicationPidFileWriterTests.java b/spring-boot/src/test/java/org/springframework/boot/system/ApplicationPidFileWriterTests.java index 5b6ada3da3..698ec42a43 100644 --- a/spring-boot/src/test/java/org/springframework/boot/system/ApplicationPidFileWriterTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/system/ApplicationPidFileWriterTests.java @@ -30,7 +30,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.context.event.ApplicationPreparedEvent; import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.boot.context.event.ApplicationStartingEvent; import org.springframework.boot.context.event.SpringApplicationEvent; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.ConfigurableEnvironment; @@ -129,9 +129,9 @@ public class ApplicationPidFileWriterTests { public void withNoEnvironment() throws Exception { File file = this.temporaryFolder.newFile(); ApplicationPidFileWriter listener = new ApplicationPidFileWriter(file); - listener.setTriggerEventType(ApplicationStartedEvent.class); + listener.setTriggerEventType(ApplicationStartingEvent.class); listener.onApplicationEvent( - new ApplicationStartedEvent(new SpringApplication(), new String[] {})); + new ApplicationStartingEvent(new SpringApplication(), new String[] {})); assertThat(FileCopyUtils.copyToString(new FileReader(file))).isNotEmpty(); } From 01c381f2a9265d92cf8ac375b11dcf77986662bd Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 15 Nov 2016 15:46:19 -0800 Subject: [PATCH 4/8] Remove empty logger from default logback config Remove the empty logger as it was not also defined in `defaults.xml` and caused rendering issues with the new `/loggers` endpoint. Fixes gh-7386 --- .../boot/logging/logback/DefaultLogbackConfiguration.java | 1 - 1 file changed, 1 deletion(-) diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java b/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java index 0c5b831271..21728edf95 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java @@ -95,7 +95,6 @@ class DefaultLogbackConfiguration { "org.springframework.boot"); config.start(debugRemapAppender); config.appender("DEBUG_LEVEL_REMAPPER", debugRemapAppender); - config.logger("", Level.ERROR); config.logger("org.apache.catalina.startup.DigesterFactory", Level.ERROR); config.logger("org.apache.catalina.util.LifecycleBase", Level.ERROR); config.logger("org.apache.coyote.http11.Http11NioProtocol", Level.WARN); From 1d2f6d25fa49c5d26f363308c8218804c39ae70a Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 15 Nov 2016 17:27:15 -0800 Subject: [PATCH 5/8] Use consistent 'ROOT' logger name Ensure all LoggingSystem implementation provide a consistent ROOT logger name. Prior to this commit the `/loggers` endpoint could return '' for root loggers which could then not be set using a POST. Fixes gh-7372 --- .../boot/logging/AbstractLoggingSystem.java | 4 ++++ .../logging/LoggerConfigurationComparator.java | 5 ++--- .../boot/logging/LoggingApplicationListener.java | 2 +- .../boot/logging/LoggingSystem.java | 10 +++++++++- .../boot/logging/java/JavaLoggingSystem.java | 16 ++++++++-------- .../boot/logging/log4j2/Log4J2LoggingSystem.java | 16 +++++++++------- .../logging/logback/LogbackLoggingSystem.java | 16 +++++++++------- .../logging/java/JavaLoggingSystemTests.java | 3 ++- .../logging/log4j2/Log4J2LoggingSystemTests.java | 4 +++- .../logback/LogbackLoggingSystemTests.java | 3 ++- 10 files changed, 49 insertions(+), 30 deletions(-) diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/AbstractLoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/AbstractLoggingSystem.java index 711e180e64..e84f025026 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/AbstractLoggingSystem.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/AbstractLoggingSystem.java @@ -16,6 +16,7 @@ package org.springframework.boot.logging; +import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Map; @@ -35,6 +36,9 @@ import org.springframework.util.SystemPropertyUtils; */ public abstract class AbstractLoggingSystem extends LoggingSystem { + protected static final Comparator CONFIGURATION_COMPARATOR = new LoggerConfigurationComparator( + ROOT_LOGGER_NAME); + private final ClassLoader classLoader; public AbstractLoggingSystem(ClassLoader classLoader) { diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/LoggerConfigurationComparator.java b/spring-boot/src/main/java/org/springframework/boot/logging/LoggerConfigurationComparator.java index ae0cb44095..dee904a508 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/LoggerConfigurationComparator.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/LoggerConfigurationComparator.java @@ -25,9 +25,8 @@ import org.springframework.util.Assert; * Sorts the "root" logger as the first logger and then lexically by name after that. * * @author Ben Hale - * @since 1.5.0 */ -public class LoggerConfigurationComparator implements Comparator { +class LoggerConfigurationComparator implements Comparator { private final String rootLoggerName; @@ -35,7 +34,7 @@ public class LoggerConfigurationComparator implements Comparator SYSTEMS; static { @@ -107,7 +114,8 @@ public abstract class LoggingSystem { /** * Sets the logging level for a given logger. - * @param loggerName the name of the logger to set + * @param loggerName the name of the logger to set ({@code null} can be used for the + * root logger). * @param level the log level */ public void setLogLevel(String loggerName, LogLevel level) { diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/java/JavaLoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/java/JavaLoggingSystem.java index da05c83f7f..451afd5ff9 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/java/JavaLoggingSystem.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/java/JavaLoggingSystem.java @@ -31,7 +31,6 @@ import org.springframework.boot.logging.AbstractLoggingSystem; import org.springframework.boot.logging.LogFile; import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LoggerConfiguration; -import org.springframework.boot.logging.LoggerConfigurationComparator; import org.springframework.boot.logging.LoggingInitializationContext; import org.springframework.boot.logging.LoggingSystem; import org.springframework.util.Assert; @@ -49,9 +48,6 @@ import org.springframework.util.StringUtils; */ public class JavaLoggingSystem extends AbstractLoggingSystem { - private static final LoggerConfigurationComparator COMPARATOR = new LoggerConfigurationComparator( - ""); - private static final LogLevels LEVELS = new LogLevels(); static { @@ -122,8 +118,10 @@ public class JavaLoggingSystem extends AbstractLoggingSystem { @Override public void setLogLevel(String loggerName, LogLevel level) { Assert.notNull(level, "Level must not be null"); - String name = (StringUtils.hasText(loggerName) ? loggerName : ""); - Logger logger = Logger.getLogger(name); + if (loggerName == null || ROOT_LOGGER_NAME.equals(loggerName)) { + loggerName = ""; + } + Logger logger = Logger.getLogger(loggerName); if (logger != null) { logger.setLevel(LEVELS.convertSystemToNative(level)); } @@ -136,7 +134,7 @@ public class JavaLoggingSystem extends AbstractLoggingSystem { while (names.hasMoreElements()) { result.add(getLoggerConfiguration(names.nextElement())); } - Collections.sort(result, COMPARATOR); + Collections.sort(result, CONFIGURATION_COMPARATOR); return Collections.unmodifiableList(result); } @@ -148,7 +146,9 @@ public class JavaLoggingSystem extends AbstractLoggingSystem { } LogLevel level = LEVELS.convertNativeToSystem(logger.getLevel()); LogLevel effectiveLevel = LEVELS.convertNativeToSystem(getEffectiveLevel(logger)); - return new LoggerConfiguration(logger.getName(), level, effectiveLevel); + String name = (StringUtils.hasLength(logger.getName()) ? logger.getName() + : ROOT_LOGGER_NAME); + return new LoggerConfiguration(name, level, effectiveLevel); } private Level getEffectiveLevel(Logger root) { diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java index 3ccc3c62fe..41f966cc5d 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java @@ -41,7 +41,6 @@ import org.apache.logging.log4j.message.Message; import org.springframework.boot.logging.LogFile; import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LoggerConfiguration; -import org.springframework.boot.logging.LoggerConfigurationComparator; import org.springframework.boot.logging.LoggingInitializationContext; import org.springframework.boot.logging.LoggingSystem; import org.springframework.boot.logging.Slf4JLoggingSystem; @@ -61,9 +60,6 @@ import org.springframework.util.StringUtils; */ public class Log4J2LoggingSystem extends Slf4JLoggingSystem { - private static final LoggerConfigurationComparator COMPARATOR = new LoggerConfigurationComparator( - LogManager.ROOT_LOGGER_NAME); - private static final String FILE_PROTOCOL = "file"; private static final LogLevels LEVELS = new LogLevels(); @@ -224,7 +220,7 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem { for (LoggerConfig loggerConfig : configuration.getLoggers().values()) { result.add(convertLoggerConfiguration(loggerConfig)); } - Collections.sort(result, COMPARATOR); + Collections.sort(result, CONFIGURATION_COMPARATOR); return result; } @@ -238,7 +234,11 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem { return null; } LogLevel level = LEVELS.convertNativeToSystem(loggerConfig.getLevel()); - return new LoggerConfiguration(loggerConfig.getName(), level, level); + String name = loggerConfig.getName(); + if (!StringUtils.hasLength(name) || LogManager.ROOT_LOGGER_NAME.equals(name)) { + name = ROOT_LOGGER_NAME; + } + return new LoggerConfiguration(name, level, level); } @Override @@ -254,7 +254,9 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem { } private LoggerConfig getLoggerConfig(String name) { - name = (StringUtils.hasText(name) ? name : LogManager.ROOT_LOGGER_NAME); + if (!StringUtils.hasLength(name) || ROOT_LOGGER_NAME.equals(name)) { + name = LogManager.ROOT_LOGGER_NAME; + } return getLoggerContext().getConfiguration().getLoggers().get(name); } diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java index c71971466e..28a5d90543 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java @@ -41,7 +41,6 @@ import org.slf4j.impl.StaticLoggerBinder; import org.springframework.boot.logging.LogFile; import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LoggerConfiguration; -import org.springframework.boot.logging.LoggerConfigurationComparator; import org.springframework.boot.logging.LoggingInitializationContext; import org.springframework.boot.logging.LoggingSystem; import org.springframework.boot.logging.Slf4JLoggingSystem; @@ -59,9 +58,6 @@ import org.springframework.util.StringUtils; */ public class LogbackLoggingSystem extends Slf4JLoggingSystem { - private static final LoggerConfigurationComparator COMPARATOR = new LoggerConfigurationComparator( - Logger.ROOT_LOGGER_NAME); - private static final String CONFIGURATION_FILE_PROPERTY = "logback.configurationFile"; private static final LogLevels LEVELS = new LogLevels(); @@ -219,7 +215,7 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { for (ch.qos.logback.classic.Logger logger : getLoggerContext().getLoggerList()) { result.add(getLoggerConfiguration(logger)); } - Collections.sort(result, COMPARATOR); + Collections.sort(result, CONFIGURATION_COMPARATOR); return result; } @@ -236,7 +232,11 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { LogLevel level = LEVELS.convertNativeToSystem(logger.getLevel()); LogLevel effectiveLevel = LEVELS .convertNativeToSystem(logger.getEffectiveLevel()); - return new LoggerConfiguration(logger.getName(), level, effectiveLevel); + String name = logger.getName(); + if (!StringUtils.hasLength(name) || Logger.ROOT_LOGGER_NAME.equals(name)) { + name = ROOT_LOGGER_NAME; + } + return new LoggerConfiguration(name, level, effectiveLevel); } @Override @@ -259,7 +259,9 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { private ch.qos.logback.classic.Logger getLogger(String name) { LoggerContext factory = getLoggerContext(); - name = (StringUtils.isEmpty(name) ? Logger.ROOT_LOGGER_NAME : name); + if (StringUtils.isEmpty(name) || ROOT_LOGGER_NAME.equals(name)) { + name = Logger.ROOT_LOGGER_NAME; + } return factory.getLogger(name); } diff --git a/spring-boot/src/test/java/org/springframework/boot/logging/java/JavaLoggingSystemTests.java b/spring-boot/src/test/java/org/springframework/boot/logging/java/JavaLoggingSystemTests.java index bc8a53b83f..c6010bc8e9 100644 --- a/spring-boot/src/test/java/org/springframework/boot/logging/java/JavaLoggingSystemTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/logging/java/JavaLoggingSystemTests.java @@ -33,6 +33,7 @@ import org.junit.Test; import org.springframework.boot.logging.AbstractLoggingSystemTests; import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LoggerConfiguration; +import org.springframework.boot.logging.LoggingSystem; import org.springframework.boot.testutil.InternalOutputCapture; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; @@ -175,7 +176,7 @@ public class JavaLoggingSystemTests extends AbstractLoggingSystemTests { List configurations = this.loggingSystem .getLoggerConfigurations(); assertThat(configurations).isNotEmpty(); - assertThat(configurations.get(0).getName()).isEmpty(); + assertThat(configurations.get(0).getName()).isEqualTo(LoggingSystem.ROOT_LOGGER_NAME); } @Test diff --git a/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java b/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java index b2abdb969f..06743df6c3 100644 --- a/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java @@ -40,6 +40,7 @@ import org.junit.Test; import org.springframework.boot.logging.AbstractLoggingSystemTests; import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LoggerConfiguration; +import org.springframework.boot.logging.LoggingSystem; import org.springframework.boot.testutil.InternalOutputCapture; import org.springframework.boot.testutil.Matched; import org.springframework.util.FileCopyUtils; @@ -148,7 +149,8 @@ public class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { List configurations = this.loggingSystem .getLoggerConfigurations(); assertThat(configurations).isNotEmpty(); - assertThat(configurations.get(0).getName()).isEmpty(); + assertThat(configurations.get(0).getName()) + .isEqualTo(LoggingSystem.ROOT_LOGGER_NAME); } @Test diff --git a/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java b/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java index b7f2e117b0..730a2efb2b 100644 --- a/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java @@ -43,6 +43,7 @@ import org.springframework.boot.logging.LogFile; import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LoggerConfiguration; import org.springframework.boot.logging.LoggingInitializationContext; +import org.springframework.boot.logging.LoggingSystem; import org.springframework.boot.testutil.InternalOutputCapture; import org.springframework.boot.testutil.Matched; import org.springframework.mock.env.MockEnvironment; @@ -190,7 +191,7 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests { .getLoggerConfigurations(); assertThat(configurations).isNotEmpty(); assertThat(configurations.get(0).getName()) - .isEqualTo(org.slf4j.Logger.ROOT_LOGGER_NAME); + .isEqualTo(LoggingSystem.ROOT_LOGGER_NAME); } @Test From 00099552dbea50d3a25664a80e77429c322cd0ef Mon Sep 17 00:00:00 2001 From: Ben Hale Date: Mon, 14 Nov 2016 15:32:43 -0800 Subject: [PATCH 6/8] Add Logger actuator documentation Add Actuator and Reference documentation for the `/logger` endpoint. This documentation includes information on listing, reading, and modifying the configuration of loggers. Closes gh-7390 See gh-7086 --- .../src/main/asciidoc/loggers.adoc | 61 +++++++++++++++++++ .../hypermedia/EndpointDocumentation.java | 18 ++++++ .../asciidoc/production-ready-features.adoc | 37 +++++++++++ 3 files changed, 116 insertions(+) create mode 100644 spring-boot-actuator-docs/src/main/asciidoc/loggers.adoc diff --git a/spring-boot-actuator-docs/src/main/asciidoc/loggers.adoc b/spring-boot-actuator-docs/src/main/asciidoc/loggers.adoc new file mode 100644 index 0000000000..735fec1104 --- /dev/null +++ b/spring-boot-actuator-docs/src/main/asciidoc/loggers.adoc @@ -0,0 +1,61 @@ +=== /loggers +This endpoint allows you to view and modify the log levels for the loggers in your +application. It builds on top of the `LoggingSystem` abstraction and supports the same +logging frameworks. The logging levels are defined by the `LogLevel` enumeration and +consists of the following values (although not all logging systems support the full set): + +* `TRACE` +* `DEBUG` +* `INFO` +* `WARN` +* `ERROR` +* `FATAL` +* `OFF` +* `null` + +The `configuredLevel` property reflects an explicitly configured logger level, while the +`effectiveLevel` property reflects the logger level inherited from parent loggers. The +`effectiveLevel` is managed by each logging framework and reflects the propagation rules +inherent to and configured in that framework. `null` indicates that there is no explicit +configuration defined. + + + +==== Listing All Loggers +Example curl request: +include::{generated}/loggers/curl-request.adoc[] + +Example HTTP request: [small]##link:../health[icon:external-link[role="silver"]]## +include::{generated}/loggers/http-request.adoc[] + +Example HTTP response: +include::{generated}/loggers/http-response.adoc[] + + + +==== Getting a Single Logger +Example curl request: +include::{generated}/single-logger/curl-request.adoc[] + +Example HTTP request: [small]##link:../health[icon:external-link[role="silver"]]## +include::{generated}/single-logger/http-request.adoc[] + +Example HTTP response: +include::{generated}/single-logger/http-response.adoc[] + + + +==== Configuring a Logger +Setting the `configuredLevel` of a logger requires `POSTing` a partial payload to the +resource. The `configuredLevel` property must contain a string representation of the +enumeration described above. `null` indicates that the log level should be unset, +allowing it to inherit configuration from it's parent. + +Example curl request: +include::{generated}/set-logger/curl-request.adoc[] + +Example HTTP request: [small]##link:../health[icon:external-link[role="silver"]]## +include::{generated}/set-logger/http-request.adoc[] + +Example HTTP response: +include::{generated}/set-logger/http-response.adoc[] diff --git a/spring-boot-actuator-docs/src/restdoc/java/org/springframework/boot/actuate/hypermedia/EndpointDocumentation.java b/spring-boot-actuator-docs/src/restdoc/java/org/springframework/boot/actuate/hypermedia/EndpointDocumentation.java index 79a931a302..4f933ef598 100644 --- a/spring-boot-actuator-docs/src/restdoc/java/org/springframework/boot/actuate/hypermedia/EndpointDocumentation.java +++ b/spring-boot-actuator-docs/src/restdoc/java/org/springframework/boot/actuate/hypermedia/EndpointDocumentation.java @@ -58,6 +58,7 @@ import org.springframework.util.StringUtils; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @RunWith(SpringRunner.class) @@ -108,6 +109,23 @@ public class EndpointDocumentation { .andDo(document("partial-logfile")); } + @Test + public void singleLogger() throws Exception { + this.mockMvc + .perform(get("/loggers/org.springframework.boot") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()).andDo(document("single-logger")); + } + + @Test + public void setLogger() throws Exception { + this.mockMvc + .perform(post("/loggers/org.springframework.boot") + .contentType(MediaType.APPLICATION_JSON) + .content("{\"configuredLevel\": \"DEBUG\"}")) + .andExpect(status().isOk()).andDo(document("set-logger")); + } + @Test public void endpoints() throws Exception { final File docs = new File("src/main/asciidoc"); diff --git a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc index 8f2c11e704..1a78e186db 100644 --- a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc @@ -108,6 +108,10 @@ authenticated). |Displays arbitrary application info. |false +|`loggers` +|Shows and modifies the configuration of loggers in the application. +|true + |`liquibase` |Shows any Liquibase database migrations that have been applied. |true @@ -945,6 +949,39 @@ documentation]. +[[production-ready-loggers]] +== Loggers +Spring Boot Actuator includes the ability to view and configure the log levels of your +application at runtime. You can view either the entire list or an individual logger's +configuration which is made up of both the explictily configured logging level as well as +the effective logging level given to it by the logging framework. These levels can be: + +* `TRACE` +* `DEBUG` +* `INFO` +* `WARN` +* `ERROR` +* `FATAL` +* `OFF` +* `null` + +with `null` indicating that there is no explict configuration. + + + +[[production-ready-logger-configuration]] +=== Configure a Logger +In order to configure a given logger, you `POST` a partial entity to the resource's URI: + +[source,json,indent=0] +---- + { + "configuredLevel": "DEBUG" + } +---- + + + [[production-ready-metrics]] == Metrics Spring Boot Actuator includes a metrics service with '`gauge`' and '`counter`' support. From ada02232b933d011980041704db28d0a124e7ddf Mon Sep 17 00:00:00 2001 From: Madhura Bhave Date: Wed, 12 Oct 2016 13:55:34 -0700 Subject: [PATCH 7/8] Change LinksEnhancer to use endpoint name Update `LinksEnhancer` to use NamedEndpoint names as rel names. If the endpoint name is not available, fallback to endpoint path. Allow multiple hrefs per rel if path is different. Fixes gh-7132 Closes gh-7164 --- .../actuate/autoconfigure/LinksEnhancer.java | 33 ++-- .../autoconfigure/LinksEnhancerTests.java | 145 ++++++++++++++++++ 2 files changed, 167 insertions(+), 11 deletions(-) create mode 100644 spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/LinksEnhancerTests.java diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/LinksEnhancer.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/LinksEnhancer.java index 4fe5be0893..71d5a5b28e 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/LinksEnhancer.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/LinksEnhancer.java @@ -16,11 +16,14 @@ package org.springframework.boot.actuate.autoconfigure; -import java.util.HashSet; -import java.util.Set; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints; +import org.springframework.boot.actuate.endpoint.mvc.NamedMvcEndpoint; import org.springframework.hateoas.ResourceSupport; import org.springframework.util.StringUtils; @@ -47,23 +50,31 @@ class LinksEnhancer { resource.add(linkTo(LinksEnhancer.class).slash(this.rootPath + self) .withSelfRel()); } - Set added = new HashSet(); + Map> added = new HashMap>(); for (MvcEndpoint endpoint : this.endpoints.getEndpoints()) { - if (!endpoint.getPath().equals(self) && !added.contains(endpoint.getPath())) { - addEndpointLink(resource, endpoint); + + String rel = getRel(endpoint); + List pathsForRel = added.get(rel) == null ? new ArrayList() : added.get(rel); + + if (!endpoint.getPath().equals(self) && !pathsForRel.contains(endpoint.getPath())) { + addEndpointLink(resource, endpoint, rel); + pathsForRel.add(endpoint.getPath()); + added.put(rel, pathsForRel); } - added.add(endpoint.getPath()); } } - private void addEndpointLink(ResourceSupport resource, MvcEndpoint endpoint) { + private String getRel(MvcEndpoint endpoint) { + String name = endpoint instanceof NamedMvcEndpoint ? ((NamedMvcEndpoint) endpoint).getName() : endpoint.getPath(); + return (name.startsWith("/") ? name.substring(1) : name); + } + + private void addEndpointLink(ResourceSupport resource, MvcEndpoint endpoint, String rel) { Class type = endpoint.getEndpointType(); type = (type == null ? Object.class : type); - String path = endpoint.getPath(); - String rel = (path.startsWith("/") ? path.substring(1) : path); if (StringUtils.hasText(rel)) { - String fullPath = this.rootPath + endpoint.getPath(); - resource.add(linkTo(type).slash(fullPath).withRel(rel)); + String href = this.rootPath + endpoint.getPath(); + resource.add(linkTo(type).slash(href).withRel(rel)); } } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/LinksEnhancerTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/LinksEnhancerTests.java new file mode 100644 index 0000000000..19cebdecf7 --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/LinksEnhancerTests.java @@ -0,0 +1,145 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.autoconfigure; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.assertj.core.api.Condition; +import org.junit.Before; +import org.junit.Test; + +import org.springframework.boot.actuate.endpoint.AbstractEndpoint; +import org.springframework.boot.actuate.endpoint.mvc.AbstractMvcEndpoint; +import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter; +import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; +import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints; +import org.springframework.hateoas.Link; +import org.springframework.hateoas.ResourceSupport; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.context.support.StaticWebApplicationContext; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link LinksEnhancer}. + * + * @author Madhura Bhave + */ +public class LinksEnhancerTests { + + @Before + public void setup() { + MockHttpServletRequest request = new MockHttpServletRequest(); + RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); + } + + @Test + public void useNameAsRelIfAvailable() throws Exception { + TestMvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("a")); + endpoint.setPath("something-else"); + LinksEnhancer enhancer = getLinksEnhancer(Collections.singletonList((MvcEndpoint) endpoint)); + ResourceSupport support = new ResourceSupport(); + enhancer.addEndpointLinks(support, ""); + assertThat(support.getLink("a").getHref()).contains("/something-else"); + } + + @Test + public void usePathAsRelIfNameNotAvailable() throws Exception { + MvcEndpoint endpoint = new NoNameTestMvcEndpoint("/a", false); + LinksEnhancer enhancer = getLinksEnhancer(Collections.singletonList(endpoint)); + ResourceSupport support = new ResourceSupport(); + enhancer.addEndpointLinks(support, ""); + assertThat(support.getLink("a").getHref()).contains("/a"); + } + + @Test + public void hrefNotAddedToRelTwice() throws Exception { + MvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("a")); + MvcEndpoint otherEndpoint = new TestMvcEndpoint(new TestEndpoint("a")); + LinksEnhancer enhancer = getLinksEnhancer(Arrays.asList(endpoint, otherEndpoint)); + ResourceSupport support = new ResourceSupport(); + enhancer.addEndpointLinks(support, ""); + assertThat(support.getLinks()).haveExactly(1, getCondition("a", "a")); + } + + @Test + public void multipleHrefsForSameRelWhenPathIsDifferent() throws Exception { + TestMvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("a")); + endpoint.setPath("endpoint"); + TestMvcEndpoint otherEndpoint = new TestMvcEndpoint(new TestEndpoint("a")); + otherEndpoint.setPath("other-endpoint"); + LinksEnhancer enhancer = getLinksEnhancer(Arrays.asList((MvcEndpoint) endpoint, otherEndpoint)); + ResourceSupport support = new ResourceSupport(); + enhancer.addEndpointLinks(support, ""); + assertThat(support.getLinks()).haveExactly(1, getCondition("a", "endpoint")); + assertThat(support.getLinks()).haveExactly(1, getCondition("a", "other-endpoint")); + } + + private Condition getCondition(final String rel, final String href) { + return new Condition() { + @Override + public boolean matches(Link link) { + return link.getRel().equals(rel) && link.getHref().equals("http://localhost/" + href); + } + }; + } + + private LinksEnhancer getLinksEnhancer(List endpoints) throws Exception { + StaticWebApplicationContext context = new StaticWebApplicationContext(); + for (MvcEndpoint endpoint : endpoints) { + context.getDefaultListableBeanFactory().registerSingleton(endpoint.toString(), + endpoint); + } + MvcEndpoints mvcEndpoints = new MvcEndpoints(); + mvcEndpoints.setApplicationContext(context); + mvcEndpoints.afterPropertiesSet(); + return new LinksEnhancer("", mvcEndpoints); + } + + private static class TestEndpoint extends AbstractEndpoint { + + TestEndpoint(String id) { + super(id); + } + + @Override + public Object invoke() { + return null; + } + + } + + private static class TestMvcEndpoint extends EndpointMvcAdapter { + + TestMvcEndpoint(TestEndpoint delegate) { + super(delegate); + } + + } + + private static class NoNameTestMvcEndpoint extends AbstractMvcEndpoint { + + NoNameTestMvcEndpoint(String path, boolean sensitive) { + super(path, sensitive); + } + } + +} From 449b42ffa06b5ebfb953ff63a241683560ba6a1f Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 15 Nov 2016 18:02:57 -0800 Subject: [PATCH 8/8] Polish LinksEnhancer to use endpoint name See gh-7164 See gh-7132 --- .../actuate/autoconfigure/LinksEnhancer.java | 33 ++++++++++--------- .../autoconfigure/LinksEnhancerTests.java | 31 ++++++++++------- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/LinksEnhancer.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/LinksEnhancer.java index 71d5a5b28e..b9a97d4780 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/LinksEnhancer.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/LinksEnhancer.java @@ -16,15 +16,14 @@ package org.springframework.boot.actuate.autoconfigure; -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints; import org.springframework.boot.actuate.endpoint.mvc.NamedMvcEndpoint; import org.springframework.hateoas.ResourceSupport; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; @@ -33,6 +32,7 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; * Adds endpoint links to {@link ResourceSupport}. * * @author Dave Syer + * @author Madhura Bhave */ class LinksEnhancer { @@ -50,26 +50,29 @@ class LinksEnhancer { resource.add(linkTo(LinksEnhancer.class).slash(this.rootPath + self) .withSelfRel()); } - Map> added = new HashMap>(); + MultiValueMap added = new LinkedMultiValueMap(); for (MvcEndpoint endpoint : this.endpoints.getEndpoints()) { - - String rel = getRel(endpoint); - List pathsForRel = added.get(rel) == null ? new ArrayList() : added.get(rel); - - if (!endpoint.getPath().equals(self) && !pathsForRel.contains(endpoint.getPath())) { - addEndpointLink(resource, endpoint, rel); - pathsForRel.add(endpoint.getPath()); - added.put(rel, pathsForRel); + if (!endpoint.getPath().equals(self)) { + String rel = getRel(endpoint); + List paths = added.get(rel); + if (paths == null || !paths.contains(endpoint.getPath())) { + addEndpointLink(resource, endpoint, rel); + added.add(rel, endpoint.getPath()); + } } } } private String getRel(MvcEndpoint endpoint) { - String name = endpoint instanceof NamedMvcEndpoint ? ((NamedMvcEndpoint) endpoint).getName() : endpoint.getPath(); - return (name.startsWith("/") ? name.substring(1) : name); + if (endpoint instanceof NamedMvcEndpoint) { + return ((NamedMvcEndpoint) endpoint).getName(); + } + String path = endpoint.getPath(); + return (path.startsWith("/") ? path.substring(1) : path); } - private void addEndpointLink(ResourceSupport resource, MvcEndpoint endpoint, String rel) { + private void addEndpointLink(ResourceSupport resource, MvcEndpoint endpoint, + String rel) { Class type = endpoint.getEndpointType(); type = (type == null ? Object.class : type); if (StringUtils.hasText(rel)) { diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/LinksEnhancerTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/LinksEnhancerTests.java index 19cebdecf7..a443143b2b 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/LinksEnhancerTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/LinksEnhancerTests.java @@ -55,7 +55,8 @@ public class LinksEnhancerTests { public void useNameAsRelIfAvailable() throws Exception { TestMvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("a")); endpoint.setPath("something-else"); - LinksEnhancer enhancer = getLinksEnhancer(Collections.singletonList((MvcEndpoint) endpoint)); + LinksEnhancer enhancer = getLinksEnhancer( + Collections.singletonList((MvcEndpoint) endpoint)); ResourceSupport support = new ResourceSupport(); enhancer.addEndpointLinks(support, ""); assertThat(support.getLink("a").getHref()).contains("/something-else"); @@ -86,20 +87,13 @@ public class LinksEnhancerTests { endpoint.setPath("endpoint"); TestMvcEndpoint otherEndpoint = new TestMvcEndpoint(new TestEndpoint("a")); otherEndpoint.setPath("other-endpoint"); - LinksEnhancer enhancer = getLinksEnhancer(Arrays.asList((MvcEndpoint) endpoint, otherEndpoint)); + LinksEnhancer enhancer = getLinksEnhancer( + Arrays.asList((MvcEndpoint) endpoint, otherEndpoint)); ResourceSupport support = new ResourceSupport(); enhancer.addEndpointLinks(support, ""); assertThat(support.getLinks()).haveExactly(1, getCondition("a", "endpoint")); - assertThat(support.getLinks()).haveExactly(1, getCondition("a", "other-endpoint")); - } - - private Condition getCondition(final String rel, final String href) { - return new Condition() { - @Override - public boolean matches(Link link) { - return link.getRel().equals(rel) && link.getHref().equals("http://localhost/" + href); - } - }; + assertThat(support.getLinks()).haveExactly(1, + getCondition("a", "other-endpoint")); } private LinksEnhancer getLinksEnhancer(List endpoints) throws Exception { @@ -114,6 +108,18 @@ public class LinksEnhancerTests { return new LinksEnhancer("", mvcEndpoints); } + private Condition getCondition(final String rel, final String href) { + return new Condition() { + + @Override + public boolean matches(Link link) { + return link.getRel().equals(rel) + && link.getHref().equals("http://localhost/" + href); + } + + }; + } + private static class TestEndpoint extends AbstractEndpoint { TestEndpoint(String id) { @@ -140,6 +146,7 @@ public class LinksEnhancerTests { NoNameTestMvcEndpoint(String path, boolean sensitive) { super(path, sensitive); } + } }