Skip to content

Commit 33b0fda

Browse files
committed
fix to gh-16680
1 parent 741d873 commit 33b0fda

File tree

10 files changed

+359
-14
lines changed

10 files changed

+359
-14
lines changed

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/logging/LoggersEndpoint.java

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@
1919
import java.util.Collection;
2020
import java.util.Collections;
2121
import java.util.LinkedHashMap;
22+
import java.util.List;
2223
import java.util.Map;
2324
import java.util.NavigableSet;
2425
import java.util.Set;
2526
import java.util.TreeSet;
2627

28+
import com.fasterxml.jackson.annotation.JsonInclude;
29+
2730
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
2831
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
2932
import org.springframework.boot.actuate.endpoint.annotation.Selector;
@@ -58,25 +61,39 @@ public LoggersEndpoint(LoggingSystem loggingSystem) {
5861
@ReadOperation
5962
public Map<String, Object> loggers() {
6063
Collection<LoggerConfiguration> configurations = this.loggingSystem.getLoggerConfigurations();
64+
Set<String> groups = this.loggingSystem.getLoggerGroups();
6165
if (configurations == null) {
6266
return Collections.emptyMap();
6367
}
6468
Map<String, Object> result = new LinkedHashMap<>();
6569
result.put("levels", getLevels());
6670
result.put("loggers", getLoggers(configurations));
71+
if (groups != null) {
72+
result.put("groups", getLoggerGroups(groups));
73+
}
6774
return result;
6875
}
6976

7077
@ReadOperation
7178
public LoggerLevels loggerLevels(@Selector String name) {
7279
Assert.notNull(name, "Name must not be null");
80+
LogLevel groupConfiguredLevel = this.loggingSystem.getLoggerGroupConfiguredLevel(name);
81+
if (groupConfiguredLevel != null) {
82+
List<String> members = this.loggingSystem.getLoggerGroup(name);
83+
return (groupConfiguredLevel != null) ? new LoggerLevels(groupConfiguredLevel, members) : null;
84+
}
7385
LoggerConfiguration configuration = this.loggingSystem.getLoggerConfiguration(name);
74-
return (configuration != null) ? new LoggerLevels(configuration) : null;
86+
return new LoggerLevels(configuration);
7587
}
7688

7789
@WriteOperation
7890
public void configureLogLevel(@Selector String name, @Nullable LogLevel configuredLevel) {
7991
Assert.notNull(name, "Name must not be empty");
92+
List<String> members = this.loggingSystem.getLoggerGroup(name);
93+
if (members != null) {
94+
this.loggingSystem.setLoggerGroupLevel(name, configuredLevel);
95+
return;
96+
}
8097
this.loggingSystem.setLogLevel(name, configuredLevel);
8198
}
8299

@@ -93,18 +110,41 @@ private Map<String, LoggerLevels> getLoggers(Collection<LoggerConfiguration> con
93110
return loggers;
94111
}
95112

113+
private Map<String, LoggerLevels> getLoggerGroups(Set<String> groups) {
114+
Map<String, LoggerLevels> loggerGroups = new LinkedHashMap<>(groups.size());
115+
for (String name : groups) {
116+
List<String> members = this.loggingSystem.getLoggerGroup(name);
117+
LogLevel groupConfiguredLevel = this.loggingSystem.getLoggerGroupConfiguredLevel(name);
118+
loggerGroups.put(name, new LoggerLevels(groupConfiguredLevel, members));
119+
}
120+
return loggerGroups;
121+
}
122+
96123
/**
97124
* Levels configured for a given logger exposed in a JSON friendly way.
98125
*/
99126
public static class LoggerLevels {
100127

101128
private String configuredLevel;
102129

130+
@JsonInclude(JsonInclude.Include.NON_NULL)
103131
private String effectiveLevel;
104132

133+
@JsonInclude(JsonInclude.Include.NON_NULL)
134+
private List<String> members;
135+
105136
public LoggerLevels(LoggerConfiguration configuration) {
106-
this.configuredLevel = getName(configuration.getConfiguredLevel());
107-
this.effectiveLevel = getName(configuration.getEffectiveLevel());
137+
this(configuration.getConfiguredLevel(), configuration.getEffectiveLevel(), null);
138+
}
139+
140+
public LoggerLevels(LogLevel level, List<String> members) {
141+
this(level, null, members);
142+
}
143+
144+
public LoggerLevels(LogLevel configuredLevel, LogLevel effectiveLevel, List<String> members) {
145+
this.configuredLevel = getName(configuredLevel);
146+
this.effectiveLevel = getName(effectiveLevel);
147+
this.members = members;
108148
}
109149

110150
private String getName(LogLevel level) {
@@ -119,6 +159,10 @@ public String getEffectiveLevel() {
119159
return this.effectiveLevel;
120160
}
121161

162+
public List<String> getMembers() {
163+
return this.members;
164+
}
165+
122166
}
123167

124168
}

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

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,11 @@ class LoggersEndpointTests {
4545

4646
@Test
4747
@SuppressWarnings("unchecked")
48-
void loggersShouldReturnLoggerConfigurations() {
48+
void loggersShouldReturnLoggerConfigurationsWithNoLoggerGroups() {
4949
given(this.loggingSystem.getLoggerConfigurations())
5050
.willReturn(Collections.singletonList(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG)));
5151
given(this.loggingSystem.getSupportedLogLevels()).willReturn(EnumSet.allOf(LogLevel.class));
52+
given(this.loggingSystem.getLoggerGroups()).willReturn(null);
5253
Map<String, Object> result = new LoggersEndpoint(this.loggingSystem).loggers();
5354
Map<String, LoggerLevels> loggers = (Map<String, LoggerLevels>) result.get("loggers");
5455
Set<LogLevel> levels = (Set<LogLevel>) result.get("levels");
@@ -57,6 +58,33 @@ void loggersShouldReturnLoggerConfigurations() {
5758
assertThat(rootLevels.getEffectiveLevel()).isEqualTo("DEBUG");
5859
assertThat(levels).containsExactly(LogLevel.OFF, LogLevel.FATAL, LogLevel.ERROR, LogLevel.WARN, LogLevel.INFO,
5960
LogLevel.DEBUG, LogLevel.TRACE);
61+
assertThat(result.get("groups")).isNull();
62+
}
63+
64+
@Test
65+
@SuppressWarnings("unchecked")
66+
void loggersShouldReturnLoggerConfigurationsWithLoggerGroups() {
67+
given(this.loggingSystem.getLoggerConfigurations())
68+
.willReturn(Collections.singletonList(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG)));
69+
given(this.loggingSystem.getSupportedLogLevels()).willReturn(EnumSet.allOf(LogLevel.class));
70+
given(this.loggingSystem.getLoggerGroup("test")).willReturn(Collections.singletonList("test.member"));
71+
given(this.loggingSystem.getLoggerGroups()).willReturn(Collections.singleton("test"));
72+
given(this.loggingSystem.getLoggerGroupConfiguredLevel("test")).willReturn(LogLevel.DEBUG);
73+
Map<String, Object> result = new LoggersEndpoint(this.loggingSystem).loggers();
74+
Map<String, LoggerLevels> loggerGroups = (Map<String, LoggerLevels>) result.get("groups");
75+
LoggerLevels testLoggerLevel = loggerGroups.get("test");
76+
Map<String, LoggerLevels> loggers = (Map<String, LoggerLevels>) result.get("loggers");
77+
Set<LogLevel> levels = (Set<LogLevel>) result.get("levels");
78+
LoggerLevels rootLevels = loggers.get("ROOT");
79+
assertThat(rootLevels.getConfiguredLevel()).isNull();
80+
assertThat(rootLevels.getEffectiveLevel()).isEqualTo("DEBUG");
81+
assertThat(levels).containsExactly(LogLevel.OFF, LogLevel.FATAL, LogLevel.ERROR, LogLevel.WARN, LogLevel.INFO,
82+
LogLevel.DEBUG, LogLevel.TRACE);
83+
assertThat(loggerGroups).isNotNull();
84+
assertThat(testLoggerLevel).isNotNull();
85+
assertThat(testLoggerLevel.getEffectiveLevel()).isNull();
86+
assertThat(testLoggerLevel.getConfiguredLevel()).isEqualTo("DEBUG");
87+
assertThat(testLoggerLevel.getMembers()).isEqualTo(Collections.singletonList("test.member"));
6088
}
6189

6290
@Test
@@ -68,16 +96,45 @@ void loggerLevelsWhenNameSpecifiedShouldReturnLevels() {
6896
assertThat(levels.getEffectiveLevel()).isEqualTo("DEBUG");
6997
}
7098

99+
@Test
100+
void groupNameSpecifiedShouldReturnConfiguredLevelAndMembers() {
101+
given(this.loggingSystem.getLoggerGroup("test")).willReturn(Collections.singletonList("test.member"));
102+
given(this.loggingSystem.getLoggerGroupConfiguredLevel("test")).willReturn(LogLevel.DEBUG);
103+
LoggerLevels levels = new LoggersEndpoint(this.loggingSystem).loggerLevels("test");
104+
assertThat(levels.getEffectiveLevel()).isNull();
105+
assertThat(levels.getConfiguredLevel()).isEqualTo("DEBUG");
106+
assertThat(levels.getMembers()).isEqualTo(Collections.singletonList("test.member"));
107+
}
108+
71109
@Test
72110
void configureLogLevelShouldSetLevelOnLoggingSystem() {
111+
given(this.loggingSystem.getLoggerGroup("ROOT")).willReturn(null);
73112
new LoggersEndpoint(this.loggingSystem).configureLogLevel("ROOT", LogLevel.DEBUG);
74113
verify(this.loggingSystem).setLogLevel("ROOT", LogLevel.DEBUG);
75114
}
76115

77116
@Test
78117
void configureLogLevelWithNullSetsLevelOnLoggingSystemToNull() {
118+
given(this.loggingSystem.getLoggerGroup("ROOT")).willReturn(null);
79119
new LoggersEndpoint(this.loggingSystem).configureLogLevel("ROOT", null);
80120
verify(this.loggingSystem).setLogLevel("ROOT", null);
81121
}
82122

123+
@Test
124+
void configureLogLevelInLoggerGroupShouldSetLevelOnLoggingSystem() {
125+
given(this.loggingSystem.getLoggerGroup("test")).willReturn(Collections.singletonList("test.member"));
126+
new LoggersEndpoint(this.loggingSystem).configureLogLevel("test", LogLevel.DEBUG);
127+
verify(this.loggingSystem).setLoggerGroupLevel("test", LogLevel.DEBUG);
128+
}
129+
130+
@Test
131+
void configureLogLevelWithNullInLoggerGroupShouldSetLevelOnLoggingSystem() {
132+
given(this.loggingSystem.getLoggerGroup("test")).willReturn(Collections.singletonList("test.member"));
133+
new LoggersEndpoint(this.loggingSystem).configureLogLevel("test", null);
134+
verify(this.loggingSystem).setLoggerGroupLevel("test", null);
135+
}
136+
137+
// @Test
138+
// void
139+
83140
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/logging/LoggingApplicationListener.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.boot.context.logging;
1818

19+
import java.util.Arrays;
1920
import java.util.Collections;
2021
import java.util.LinkedHashMap;
2122
import java.util.List;
@@ -325,7 +326,8 @@ private void initializeLogLevel(LoggingSystem system, LogLevel level, String log
325326
system.setLogLevel(logger, level);
326327
return;
327328
}
328-
groupLoggers.forEach((groupLogger) -> system.setLogLevel(groupLogger, level));
329+
system.setLoggerGroup(logger, groupLoggers);
330+
system.setLoggerGroupLevel(logger, level);
329331
}
330332

331333
protected void setLogLevels(LoggingSystem system, Environment environment) {
@@ -342,7 +344,7 @@ protected void setLogLevels(LoggingSystem system, Environment environment) {
342344
setLogLevel(system, name, level);
343345
}
344346
else {
345-
setLogLevel(system, groupedNames, level);
347+
setLogLevel(system, groupedNames, level, name);
346348
}
347349
});
348350
}
@@ -353,15 +355,18 @@ private Map<String, String[]> getGroups() {
353355
return groups;
354356
}
355357

356-
private void setLogLevel(LoggingSystem system, String[] names, LogLevel level) {
357-
for (String name : names) {
358-
setLogLevel(system, name, level);
358+
private void setLogLevel(LoggingSystem system, String[] names, LogLevel level, String groupName) {
359+
try {
360+
system.setLoggerGroup(groupName, Arrays.asList(names));
361+
system.setLoggerGroupLevel(groupName, level);
362+
}
363+
catch (RuntimeException ex) {
364+
this.logger.error("Cannot set level '" + level + "' for '" + groupName + "'");
359365
}
360366
}
361367

362368
private void setLogLevel(LoggingSystem system, String name, LogLevel level) {
363369
try {
364-
name = name.equalsIgnoreCase(LoggingSystem.ROOT_LOGGER_NAME) ? null : name;
365370
system.setLogLevel(name, level);
366371
}
367372
catch (RuntimeException ex) {

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

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,16 @@ public void setLogLevel(String loggerName, LogLevel level) {
122122
throw new UnsupportedOperationException("Unable to set log level");
123123
}
124124

125+
/**
126+
* Set the logging level for a given logger group.
127+
* @param groupName the name of the group to set
128+
* @param level the log level ({@code null}) can be used to remove any custom level
129+
* for the logger group and use the default configuration instead.
130+
*/
131+
public void setLoggerGroupLevel(String groupName, LogLevel level) {
132+
throw new UnsupportedOperationException("Unable to set log level");
133+
}
134+
125135
/**
126136
* Returns a collection of the current configuration for all a {@link LoggingSystem}'s
127137
* loggers.
@@ -142,6 +152,41 @@ public LoggerConfiguration getLoggerConfiguration(String loggerName) {
142152
throw new UnsupportedOperationException("Unable to get logger configuration");
143153
}
144154

155+
/**
156+
* Get the all registered logger groups.
157+
* @return a Set of the names of the logger groups
158+
*/
159+
public Set<String> getLoggerGroups() {
160+
throw new UnsupportedOperationException("Unable to get logger group configurations");
161+
}
162+
163+
/**
164+
* Get a logger group's configured level.
165+
* @param groupName name of the logger group
166+
* @return the logger groups configured level
167+
*/
168+
public LogLevel getLoggerGroupConfiguredLevel(String groupName) {
169+
throw new UnsupportedOperationException("Unable to get logger group configurations");
170+
}
171+
172+
/**
173+
* Associate a name to a list of logger's name to create a logger group.
174+
* @param groupName name of the logger group
175+
* @param members list of the members names
176+
*/
177+
public void setLoggerGroup(String groupName, List<String> members) {
178+
throw new UnsupportedOperationException("Unable to set logger group");
179+
}
180+
181+
/**
182+
* Get a logger group's members.
183+
* @param groupName name of the logger group
184+
* @return list of the members names associated with this group
185+
*/
186+
public List<String> getLoggerGroup(String groupName) {
187+
throw new UnsupportedOperationException("Unable to get logger group");
188+
}
189+
145190
/**
146191
* Detect and return the logging system in use. Supports Logback and Java Logging.
147192
* @param classLoader the classloader

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

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
import java.util.Collections;
2323
import java.util.Enumeration;
2424
import java.util.HashSet;
25+
import java.util.LinkedHashMap;
2526
import java.util.List;
27+
import java.util.Map;
2628
import java.util.Set;
2729
import java.util.logging.Level;
2830
import java.util.logging.LogManager;
@@ -54,6 +56,10 @@ public class JavaLoggingSystem extends AbstractLoggingSystem {
5456

5557
private final Set<Logger> configuredLoggers = Collections.synchronizedSet(new HashSet<>());
5658

59+
private Map<String, List<String>> loggerGroups = new LinkedHashMap<>();
60+
61+
private Map<String, LogLevel> loggerGroupConfigurations = new LinkedHashMap<>();
62+
5763
static {
5864
LEVELS.map(LogLevel.TRACE, Level.FINEST);
5965
LEVELS.map(LogLevel.DEBUG, Level.FINE);
@@ -117,7 +123,7 @@ public Set<LogLevel> getSupportedLogLevels() {
117123

118124
@Override
119125
public void setLogLevel(String loggerName, LogLevel level) {
120-
if (loggerName == null || ROOT_LOGGER_NAME.equals(loggerName)) {
126+
if (loggerName == null || ROOT_LOGGER_NAME.equalsIgnoreCase(loggerName)) {
121127
loggerName = "";
122128
}
123129
Logger logger = Logger.getLogger(loggerName);
@@ -150,6 +156,36 @@ public LoggerConfiguration getLoggerConfiguration(String loggerName) {
150156
return new LoggerConfiguration(name, level, effectiveLevel);
151157
}
152158

159+
@Override
160+
public void setLoggerGroupLevel(String groupName, LogLevel level) {
161+
if (!this.loggerGroups.containsKey(groupName)) {
162+
throw new IllegalArgumentException("Group does not exist");
163+
}
164+
this.loggerGroupConfigurations.put(groupName, level);
165+
List<String> members = this.loggerGroups.get(groupName);
166+
members.forEach((member) -> setLogLevel(member, level));
167+
}
168+
169+
@Override
170+
public Set<String> getLoggerGroups() {
171+
return this.loggerGroupConfigurations.isEmpty() ? null : this.loggerGroupConfigurations.keySet();
172+
}
173+
174+
@Override
175+
public LogLevel getLoggerGroupConfiguredLevel(String name) {
176+
return this.loggerGroupConfigurations.get(name);
177+
}
178+
179+
@Override
180+
public void setLoggerGroup(String groupName, List<String> members) {
181+
this.loggerGroups.put(groupName, members);
182+
}
183+
184+
@Override
185+
public List<String> getLoggerGroup(String groupName) {
186+
return this.loggerGroups.get(groupName);
187+
}
188+
153189
private Level getEffectiveLevel(Logger root) {
154190
Logger logger = root;
155191
while (logger.getLevel() == null) {

0 commit comments

Comments
 (0)