Commit 605dee47 authored by Stephane Nicoll's avatar Stephane Nicoll

Allow to reset a log level

This commit ensures that `setLogLevel` on the `LoggingSystem` accepts
a `null` level. A `null` level means any customization sets on that
level should be removed and the default configuration should be used
instead.

Effectively, the level of the parent logger is going to be used when
`setLevel` is called with `null` for a given logger.

Most JMX clients do not accept to pass `null` for an argument so an
empty String is translated to null in that specific case.

Closes gh-8776
parent bdf2b2e8
......@@ -24,12 +24,15 @@ import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.logging.LogLevel;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.util.Assert;
import org.springframework.jmx.export.annotation.ManagedOperationParameter;
import org.springframework.jmx.export.annotation.ManagedOperationParameters;
import org.springframework.util.StringUtils;
/**
* Adapter to expose {@link LoggersEndpoint} as an {@link MvcEndpoint}.
*
* @author Vedran Pavic
* @author Stephane Nicoll
* @since 1.5.0
*/
public class LoggersEndpointMBean extends EndpointMBean {
......@@ -45,15 +48,25 @@ public class LoggersEndpointMBean extends EndpointMBean {
}
@ManagedOperation(description = "Get log level for a given logger")
@ManagedOperationParameters({
@ManagedOperationParameter(name = "loggerName", description = "Name of the logger") })
public Object getLogger(String loggerName) {
return convert(getEndpoint().invoke(loggerName));
}
@ManagedOperation(description = "Set log level for a given logger")
@ManagedOperationParameters({
@ManagedOperationParameter(name = "loggerName", description = "Name of the logger"),
@ManagedOperationParameter(name = "logLevel", description = "New log level (can be null or empty String to remove the custom level)") })
public void setLogLevel(String loggerName, String logLevel) {
Assert.notNull(logLevel, "LogLevel must not be null");
LogLevel level = LogLevel.valueOf(logLevel.toUpperCase());
getEndpoint().setLogLevel(loggerName, level);
getEndpoint().setLogLevel(loggerName, determineLogLevel(logLevel));
}
private LogLevel determineLogLevel(String logLevel) {
if (StringUtils.hasText(logLevel)) {
return LogLevel.valueOf(logLevel.toUpperCase());
}
return null;
}
@Override
......
......@@ -80,6 +80,12 @@ public class LoggersEndpointTests extends AbstractEndpointTests<LoggersEndpoint>
verify(getLoggingSystem()).setLogLevel("ROOT", LogLevel.DEBUG);
}
@Test
public void setLogLevelToNull() {
getEndpointBean().setLogLevel("ROOT", null);
verify(getLoggingSystem()).setLogLevel("ROOT", null);
}
private LoggingSystem getLoggingSystem() {
return this.context.getBean(LoggingSystem.class);
}
......
......@@ -64,6 +64,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @author Ben Hale
* @author Phillip Webb
* @author Eddú Meléndez
* @author Stephane Nicoll
*/
@RunWith(SpringRunner.class)
@SpringBootTest
......@@ -181,6 +182,22 @@ public class LoggersMvcEndpointTests {
verifyZeroInteractions(this.loggingSystem);
}
@Test
public void setLoggerWithNullLogLevel() throws Exception {
this.mvc.perform(post(PATH + "/ROOT").contentType(MediaType.APPLICATION_JSON)
.content("{\"configuredLevel\": null}"))
.andExpect(status().isNoContent());
verify(this.loggingSystem).setLogLevel("ROOT", null);
}
@Test
public void setLoggerWithNoLogLevel() throws Exception {
this.mvc.perform(post(PATH + "/ROOT").contentType(MediaType.APPLICATION_JSON)
.content("{}"))
.andExpect(status().isNoContent());
verify(this.loggingSystem).setLogLevel("ROOT", null);
}
@Test
public void logLevelForLoggerWithNameThatCouldBeMistakenForAPathExtension()
throws Exception {
......
......@@ -927,6 +927,9 @@ In order to configure a given logger, you `POST` a partial entity to the resourc
}
----
TIP: You can also pass a `null` `configuredLevel` to "reset" the specific level of the
logger (and use the default configuration instead).
[[production-ready-metrics]]
......
......@@ -116,7 +116,8 @@ public abstract class LoggingSystem {
* Sets the logging level for a given logger.
* @param loggerName the name of the logger to set ({@code null} can be used for the
* root logger).
* @param level the log level
* @param level the log level ({@code null} can be used to remove any custom level
* for the logger and use the default configuration instead)
*/
public void setLogLevel(String loggerName, LogLevel level) {
throw new UnsupportedOperationException("Unable to set log level");
......
......@@ -117,7 +117,6 @@ public class JavaLoggingSystem extends AbstractLoggingSystem {
@Override
public void setLogLevel(String loggerName, LogLevel level) {
Assert.notNull(level, "Level must not be null");
if (loggerName == null || ROOT_LOGGER_NAME.equals(loggerName)) {
loggerName = "";
}
......
......@@ -168,6 +168,19 @@ public class JavaLoggingSystemTests extends AbstractLoggingSystemTests {
.isEqualTo(1);
}
@Test
public void setLevelToNull() throws Exception {
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(null, null, null);
this.logger.fine("Hello");
this.loggingSystem.setLogLevel("org.springframework.boot", LogLevel.DEBUG);
this.logger.fine("Hello");
this.loggingSystem.setLogLevel("org.springframework.boot", null);
this.logger.fine("Hello");
assertThat(StringUtils.countOccurrencesOf(this.output.toString(), "Hello"))
.isEqualTo(1);
}
@Test
public void getLoggingConfigurations() throws Exception {
this.loggingSystem.beforeInitialize();
......
......@@ -141,6 +141,19 @@ public class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
.isEqualTo(1);
}
@Test
public void setLevelToNull() throws Exception {
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(null, null, null);
this.logger.debug("Hello");
this.loggingSystem.setLogLevel("org.springframework.boot", LogLevel.DEBUG);
this.logger.debug("Hello");
this.loggingSystem.setLogLevel("org.springframework.boot", null);
this.logger.debug("Hello");
assertThat(StringUtils.countOccurrencesOf(this.output.toString(), "Hello"))
.isEqualTo(1);
}
@Test
public void getLoggingConfigurations() throws Exception {
this.loggingSystem.beforeInitialize();
......
......@@ -184,6 +184,19 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
.isEqualTo(1);
}
@Test
public void setLevelToNull() throws Exception {
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
this.logger.debug("Hello");
this.loggingSystem.setLogLevel("org.springframework.boot", LogLevel.DEBUG);
this.logger.debug("Hello");
this.loggingSystem.setLogLevel("org.springframework.boot", null);
this.logger.debug("Hello");
assertThat(StringUtils.countOccurrencesOf(this.output.toString(), "Hello"))
.isEqualTo(1);
}
@Test
public void getLoggingConfigurations() throws Exception {
this.loggingSystem.beforeInitialize();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment