Return log levels in `/loggers` endpoint payload

Update `LoggersEndpoint` to additionally return the log levels actually
supported by the system.

Fixes gh-7396
pull/7164/merge
Madhura Bhave 8 years ago committed by Phillip Webb
parent 764f13453a
commit 0e3a3df6f4

@ -338,6 +338,11 @@
<artifactId>hsqldb</artifactId> <artifactId>hsqldb</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.skyscreamer</groupId>
<artifactId>jsonassert</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId> <artifactId>spring-test</artifactId>

@ -20,6 +20,9 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; 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.context.properties.ConfigurationProperties;
import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LogLevel;
@ -35,8 +38,7 @@ import org.springframework.util.Assert;
* @since 1.5.0 * @since 1.5.0
*/ */
@ConfigurationProperties(prefix = "endpoints.loggers") @ConfigurationProperties(prefix = "endpoints.loggers")
public class LoggersEndpoint public class LoggersEndpoint extends AbstractEndpoint<Map<String, Object>> {
extends AbstractEndpoint<Map<String, LoggersEndpoint.LoggerLevels>> {
private final LoggingSystem loggingSystem; private final LoggingSystem loggingSystem;
@ -51,18 +53,31 @@ public class LoggersEndpoint
} }
@Override @Override
public Map<String, LoggerLevels> invoke() { public Map<String, Object> invoke() {
Collection<LoggerConfiguration> configurations = this.loggingSystem Collection<LoggerConfiguration> configurations = this.loggingSystem
.getLoggerConfigurations(); .getLoggerConfigurations();
if (configurations == null) { if (configurations == null) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
Map<String, LoggerLevels> result = new LinkedHashMap<String, LoggerLevels>( Map<String, Object> result = new LinkedHashMap<String, Object>();
result.put("levels", getLevels());
result.put("loggers", getLoggers(configurations));
return result;
}
private NavigableSet<LogLevel> getLevels() {
Set<LogLevel> levels = this.loggingSystem.getSupportedLogLevels();
return new TreeSet<LogLevel>(levels).descendingSet();
}
private Map<String, LoggerLevels> getLoggers(
Collection<LoggerConfiguration> configurations) {
Map<String, LoggerLevels> loggers = new LinkedHashMap<String, LoggerLevels>(
configurations.size()); configurations.size());
for (LoggerConfiguration configuration : configurations) { 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) { public LoggerLevels invoke(String name) {

@ -130,7 +130,9 @@ public class EndpointAutoConfigurationTests {
public void loggersEndpointHasLoggers() throws Exception { public void loggersEndpointHasLoggers() throws Exception {
load(CustomLoggingConfig.class, EndpointAutoConfiguration.class); load(CustomLoggingConfig.class, EndpointAutoConfiguration.class);
LoggersEndpoint endpoint = this.context.getBean(LoggersEndpoint.class); LoggersEndpoint endpoint = this.context.getBean(LoggersEndpoint.class);
Map<String, LoggerLevels> loggers = endpoint.invoke(); Map<String, Object> result = endpoint.invoke();
Map<String, LoggerLevels> loggers = (Map<String, LoggerLevels>) result
.get("loggers");
assertThat(loggers.size()).isGreaterThan(0); assertThat(loggers.size()).isGreaterThan(0);
} }

@ -17,6 +17,9 @@
package org.springframework.boot.actuate.endpoint; package org.springframework.boot.actuate.endpoint;
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
import org.junit.Test; import org.junit.Test;
@ -45,12 +48,21 @@ public class LoggersEndpointTests extends AbstractEndpointTests<LoggersEndpoint>
} }
@Test @Test
@SuppressWarnings("unchecked")
public void invokeShouldReturnConfigurations() throws Exception { public void invokeShouldReturnConfigurations() throws Exception {
given(getLoggingSystem().getLoggerConfigurations()).willReturn(Collections given(getLoggingSystem().getLoggerConfigurations()).willReturn(Collections
.singletonList(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG))); .singletonList(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG)));
LoggerLevels levels = getEndpointBean().invoke().get("ROOT"); given(getLoggingSystem().getSupportedLogLevels())
assertThat(levels.getConfiguredLevel()).isNull(); .willReturn(EnumSet.allOf(LogLevel.class));
assertThat(levels.getEffectiveLevel()).isEqualTo("DEBUG"); Map<String, Object> result = getEndpointBean().invoke();
Map<String, LoggerLevels> loggers = (Map<String, LoggerLevels>) result
.get("loggers");
Set<LogLevel> levels = (Set<LogLevel>) 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 { public void invokeWhenNameSpecifiedShouldReturnLevels() throws Exception {

@ -17,6 +17,7 @@
package org.springframework.boot.actuate.endpoint.mvc; package org.springframework.boot.actuate.endpoint.mvc;
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
@ -80,18 +81,23 @@ public class LoggersMvcEndpointTests {
.alwaysDo(MockMvcResultHandlers.print()).build(); .alwaysDo(MockMvcResultHandlers.print()).build();
} }
@Before
@After @After
public void reset() { public void resetMocks() {
Mockito.reset(this.loggingSystem); Mockito.reset(this.loggingSystem);
given(this.loggingSystem.getSupportedLogLevels())
.willReturn(EnumSet.allOf(LogLevel.class));
} }
@Test @Test
public void getLoggerShouldReturnAllLoggerConfigurations() throws Exception { public void getLoggerShouldReturnAllLoggerConfigurations() throws Exception {
given(this.loggingSystem.getLoggerConfigurations()).willReturn(Collections given(this.loggingSystem.getLoggerConfigurations()).willReturn(Collections
.singletonList(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG))); .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()) this.mvc.perform(get("/loggers")).andExpect(status().isOk())
.andExpect(content().string(equalTo("{\"ROOT\":{\"configuredLevel\":" .andExpect(content().json(expected));
+ "null,\"effectiveLevel\":\"DEBUG\"}}")));
} }
@Test @Test

@ -17,7 +17,9 @@
package org.springframework.boot.logging; package org.springframework.boot.logging;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
@ -193,7 +195,9 @@ public abstract class AbstractLoggingSystem extends LoggingSystem {
public void map(LogLevel system, T nativeLevel) { public void map(LogLevel system, T nativeLevel) {
this.systemToNative.put(system, 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) { public LogLevel convertNativeToSystem(T level) {
@ -204,6 +208,10 @@ public abstract class AbstractLoggingSystem extends LoggingSystem {
return this.systemToNative.get(level); return this.systemToNative.get(level);
} }
public Set<LogLevel> getSupported() {
return new LinkedHashSet<LogLevel>(this.nativeToSystem.values());
}
} }
} }

@ -17,9 +17,11 @@
package org.springframework.boot.logging; package org.springframework.boot.logging;
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -94,6 +96,15 @@ public abstract class LoggingSystem {
return null; return null;
} }
/**
* Returns a set of the {@link LogLevel LogLevels} that are actually supported by the
* logging system.
* @return the supported levels
*/
public Set<LogLevel> getSupportedLogLevels() {
return EnumSet.allOf(LogLevel.class);
}
/** /**
* Sets the logging level for a given logger. * 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

@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.LogManager; import java.util.logging.LogManager;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -113,6 +114,11 @@ public class JavaLoggingSystem extends AbstractLoggingSystem {
} }
} }
@Override
public Set<LogLevel> getSupportedLogLevels() {
return LEVELS.getSupported();
}
@Override @Override
public void setLogLevel(String loggerName, LogLevel level) { public void setLogLevel(String loggerName, LogLevel level) {
Assert.notNull(level, "Level must not be null"); Assert.notNull(level, "Level must not be null");

@ -22,6 +22,7 @@ import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set;
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@ -197,6 +198,11 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem {
getLoggerContext().reconfigure(); getLoggerContext().reconfigure();
} }
@Override
public Set<LogLevel> getSupportedLogLevels() {
return LEVELS.getSupported();
}
@Override @Override
public void setLogLevel(String loggerName, LogLevel logLevel) { public void setLogLevel(String loggerName, LogLevel logLevel) {
Level level = LEVELS.convertSystemToNative(logLevel); Level level = LEVELS.convertSystemToNative(logLevel);

@ -22,6 +22,7 @@ import java.security.ProtectionDomain;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set;
import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.LoggerContext;
@ -238,6 +239,11 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem {
return new LoggerConfiguration(logger.getName(), level, effectiveLevel); return new LoggerConfiguration(logger.getName(), level, effectiveLevel);
} }
@Override
public Set<LogLevel> getSupportedLogLevels() {
return LEVELS.getSupported();
}
@Override @Override
public void setLogLevel(String loggerName, LogLevel level) { public void setLogLevel(String loggerName, LogLevel level) {
ch.qos.logback.classic.Logger logger = getLogger(loggerName); ch.qos.logback.classic.Logger logger = getLogger(loggerName);

@ -19,6 +19,7 @@ package org.springframework.boot.logging.java;
import java.io.File; import java.io.File;
import java.io.FileFilter; import java.io.FileFilter;
import java.io.IOException; import java.io.IOException;
import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.logging.Level; import java.util.logging.Level;
@ -148,6 +149,13 @@ public class JavaLoggingSystemTests extends AbstractLoggingSystemTests {
null); 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 @Test
public void setLevel() throws Exception { public void setLevel() throws Exception {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();

@ -22,6 +22,7 @@ import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet;
import java.util.List; import java.util.List;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
@ -122,6 +123,12 @@ public class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
this.loggingSystem.initialize(null, "classpath:log4j2-nonexistent.xml", null); this.loggingSystem.initialize(null, "classpath:log4j2-nonexistent.xml", null);
} }
@Test
public void getSupportedLevels() {
assertThat(this.loggingSystem.getSupportedLogLevels())
.isEqualTo(EnumSet.allOf(LogLevel.class));
}
@Test @Test
public void setLevel() throws Exception { public void setLevel() throws Exception {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();

@ -18,6 +18,7 @@ package org.springframework.boot.logging.logback;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.logging.Handler; import java.util.logging.Handler;
import java.util.logging.LogManager; import java.util.logging.LogManager;
@ -162,6 +163,13 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
"classpath:logback-nonexistent.xml", null); "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 @Test
public void setLevel() throws Exception { public void setLevel() throws Exception {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();

Loading…
Cancel
Save