Skip to content

Commit 605dee4

Browse files
committed
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
1 parent bdf2b2e commit 605dee4

File tree

9 files changed

+84
-6
lines changed

9 files changed

+84
-6
lines changed

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/LoggersEndpointMBean.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,15 @@
2424
import org.springframework.boot.logging.LogLevel;
2525
import org.springframework.jmx.export.annotation.ManagedAttribute;
2626
import org.springframework.jmx.export.annotation.ManagedOperation;
27-
import org.springframework.util.Assert;
27+
import org.springframework.jmx.export.annotation.ManagedOperationParameter;
28+
import org.springframework.jmx.export.annotation.ManagedOperationParameters;
29+
import org.springframework.util.StringUtils;
2830

2931
/**
3032
* Adapter to expose {@link LoggersEndpoint} as an {@link MvcEndpoint}.
3133
*
3234
* @author Vedran Pavic
35+
* @author Stephane Nicoll
3336
* @since 1.5.0
3437
*/
3538
public class LoggersEndpointMBean extends EndpointMBean {
@@ -45,15 +48,25 @@ public Object getLoggers() {
4548
}
4649

4750
@ManagedOperation(description = "Get log level for a given logger")
51+
@ManagedOperationParameters({
52+
@ManagedOperationParameter(name = "loggerName", description = "Name of the logger") })
4853
public Object getLogger(String loggerName) {
4954
return convert(getEndpoint().invoke(loggerName));
5055
}
5156

5257
@ManagedOperation(description = "Set log level for a given logger")
58+
@ManagedOperationParameters({
59+
@ManagedOperationParameter(name = "loggerName", description = "Name of the logger"),
60+
@ManagedOperationParameter(name = "logLevel", description = "New log level (can be null or empty String to remove the custom level)") })
5361
public void setLogLevel(String loggerName, String logLevel) {
54-
Assert.notNull(logLevel, "LogLevel must not be null");
55-
LogLevel level = LogLevel.valueOf(logLevel.toUpperCase());
56-
getEndpoint().setLogLevel(loggerName, level);
62+
getEndpoint().setLogLevel(loggerName, determineLogLevel(logLevel));
63+
}
64+
65+
private LogLevel determineLogLevel(String logLevel) {
66+
if (StringUtils.hasText(logLevel)) {
67+
return LogLevel.valueOf(logLevel.toUpperCase());
68+
}
69+
return null;
5770
}
5871

5972
@Override

spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/LoggersEndpointTests.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ public void setLogLevelShouldSetLevelOnLoggingSystem() throws Exception {
8080
verify(getLoggingSystem()).setLogLevel("ROOT", LogLevel.DEBUG);
8181
}
8282

83+
@Test
84+
public void setLogLevelToNull() {
85+
getEndpointBean().setLogLevel("ROOT", null);
86+
verify(getLoggingSystem()).setLogLevel("ROOT", null);
87+
}
88+
8389
private LoggingSystem getLoggingSystem() {
8490
return this.context.getBean(LoggingSystem.class);
8591
}

spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/LoggersMvcEndpointTests.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
* @author Ben Hale
6565
* @author Phillip Webb
6666
* @author Eddú Meléndez
67+
* @author Stephane Nicoll
6768
*/
6869
@RunWith(SpringRunner.class)
6970
@SpringBootTest
@@ -181,6 +182,22 @@ public void setLoggerWithWrongLogLevel() throws Exception {
181182
verifyZeroInteractions(this.loggingSystem);
182183
}
183184

185+
@Test
186+
public void setLoggerWithNullLogLevel() throws Exception {
187+
this.mvc.perform(post(PATH + "/ROOT").contentType(MediaType.APPLICATION_JSON)
188+
.content("{\"configuredLevel\": null}"))
189+
.andExpect(status().isNoContent());
190+
verify(this.loggingSystem).setLogLevel("ROOT", null);
191+
}
192+
193+
@Test
194+
public void setLoggerWithNoLogLevel() throws Exception {
195+
this.mvc.perform(post(PATH + "/ROOT").contentType(MediaType.APPLICATION_JSON)
196+
.content("{}"))
197+
.andExpect(status().isNoContent());
198+
verify(this.loggingSystem).setLogLevel("ROOT", null);
199+
}
200+
184201
@Test
185202
public void logLevelForLoggerWithNameThatCouldBeMistakenForAPathExtension()
186203
throws Exception {

spring-boot-docs/src/main/asciidoc/production-ready-features.adoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,9 @@ In order to configure a given logger, you `POST` a partial entity to the resourc
927927
}
928928
----
929929

930+
TIP: You can also pass a `null` `configuredLevel` to "reset" the specific level of the
931+
logger (and use the default configuration instead).
932+
930933

931934

932935
[[production-ready-metrics]]

spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystem.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ public Set<LogLevel> getSupportedLogLevels() {
116116
* Sets the logging level for a given logger.
117117
* @param loggerName the name of the logger to set ({@code null} can be used for the
118118
* root logger).
119-
* @param level the log level
119+
* @param level the log level ({@code null} can be used to remove any custom level
120+
* for the logger and use the default configuration instead)
120121
*/
121122
public void setLogLevel(String loggerName, LogLevel level) {
122123
throw new UnsupportedOperationException("Unable to set log level");

spring-boot/src/main/java/org/springframework/boot/logging/java/JavaLoggingSystem.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ public Set<LogLevel> getSupportedLogLevels() {
117117

118118
@Override
119119
public void setLogLevel(String loggerName, LogLevel level) {
120-
Assert.notNull(level, "Level must not be null");
121120
if (loggerName == null || ROOT_LOGGER_NAME.equals(loggerName)) {
122121
loggerName = "";
123122
}

spring-boot/src/test/java/org/springframework/boot/logging/java/JavaLoggingSystemTests.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,19 @@ public void setLevel() throws Exception {
168168
.isEqualTo(1);
169169
}
170170

171+
@Test
172+
public void setLevelToNull() throws Exception {
173+
this.loggingSystem.beforeInitialize();
174+
this.loggingSystem.initialize(null, null, null);
175+
this.logger.fine("Hello");
176+
this.loggingSystem.setLogLevel("org.springframework.boot", LogLevel.DEBUG);
177+
this.logger.fine("Hello");
178+
this.loggingSystem.setLogLevel("org.springframework.boot", null);
179+
this.logger.fine("Hello");
180+
assertThat(StringUtils.countOccurrencesOf(this.output.toString(), "Hello"))
181+
.isEqualTo(1);
182+
}
183+
171184
@Test
172185
public void getLoggingConfigurations() throws Exception {
173186
this.loggingSystem.beforeInitialize();

spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,19 @@ public void setLevel() throws Exception {
141141
.isEqualTo(1);
142142
}
143143

144+
@Test
145+
public void setLevelToNull() throws Exception {
146+
this.loggingSystem.beforeInitialize();
147+
this.loggingSystem.initialize(null, null, null);
148+
this.logger.debug("Hello");
149+
this.loggingSystem.setLogLevel("org.springframework.boot", LogLevel.DEBUG);
150+
this.logger.debug("Hello");
151+
this.loggingSystem.setLogLevel("org.springframework.boot", null);
152+
this.logger.debug("Hello");
153+
assertThat(StringUtils.countOccurrencesOf(this.output.toString(), "Hello"))
154+
.isEqualTo(1);
155+
}
156+
144157
@Test
145158
public void getLoggingConfigurations() throws Exception {
146159
this.loggingSystem.beforeInitialize();

spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,19 @@ public void setLevel() throws Exception {
184184
.isEqualTo(1);
185185
}
186186

187+
@Test
188+
public void setLevelToNull() throws Exception {
189+
this.loggingSystem.beforeInitialize();
190+
this.loggingSystem.initialize(this.initializationContext, null, null);
191+
this.logger.debug("Hello");
192+
this.loggingSystem.setLogLevel("org.springframework.boot", LogLevel.DEBUG);
193+
this.logger.debug("Hello");
194+
this.loggingSystem.setLogLevel("org.springframework.boot", null);
195+
this.logger.debug("Hello");
196+
assertThat(StringUtils.countOccurrencesOf(this.output.toString(), "Hello"))
197+
.isEqualTo(1);
198+
}
199+
187200
@Test
188201
public void getLoggingConfigurations() throws Exception {
189202
this.loggingSystem.beforeInitialize();

0 commit comments

Comments
 (0)