Polish "Add support for configuring logging groups"

See gh-17515

Co-authored-by: Phillip Webb <pwebb@pivotal.io>
This commit is contained in:
Madhura Bhave 2019-07-30 11:20:15 -07:00
parent b9047c22e0
commit 61b86ff231
13 changed files with 446 additions and 374 deletions

View File

@ -57,6 +57,21 @@ include::{snippets}loggers/single/response-fields.adoc[]
[[loggers-group]]
== Retrieving a Single Group
To retrieve a single group, make a `GET` request to `/actuator/loggers/{group.name}`,
as shown in the following curl-based example:
include::{snippets}loggers/group/curl-request.adoc[]
The preceding example retrieves information about the logger group named `test`. The
resulting response is similar to the following:
include::{snippets}loggers/group/http-response.adoc[]
[[loggers-setting-level]] [[loggers-setting-level]]
== Setting a Log Level == Setting a Log Level
@ -81,6 +96,30 @@ include::{snippets}loggers/set/request-fields.adoc[]
[[loggers-setting-level]]
== Setting a Log Level for a Group
To set the level of a logger, make a `POST` request to
`/actuator/loggers/{group.name}` with a JSON body that specifies the configured level
for the logger group, as shown in the following curl-based example:
include::{snippets}loggers/setGroup/curl-request.adoc[]
The preceding example sets the `configuredLevel` of the `test` logger group to `DEBUG`.
[[loggers-setting-level-request-structure]]
=== Request Structure
The request specifies the desired level of the logger group. The following table describes the
structure of the request:
[cols="3,1,3"]
include::{snippets}loggers/set/request-fields.adoc[]
[[loggers-clearing-level]] [[loggers-clearing-level]]
== Clearing a Log Level == Clearing a Log Level

View File

@ -25,7 +25,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.logging.LoggingGroups; import org.springframework.boot.logging.LoggerGroups;
import org.springframework.boot.logging.LoggingSystem; import org.springframework.boot.logging.LoggingSystem;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.ConditionContext;
@ -48,8 +48,8 @@ public class LoggersEndpointAutoConfiguration {
@Conditional(OnEnabledLoggingSystemCondition.class) @Conditional(OnEnabledLoggingSystemCondition.class)
@ConditionalOnMissingBean @ConditionalOnMissingBean
public LoggersEndpoint loggersEndpoint(LoggingSystem loggingSystem, public LoggersEndpoint loggersEndpoint(LoggingSystem loggingSystem,
ObjectProvider<LoggingGroups> loggingGroupsObjectProvider) { ObjectProvider<LoggerGroups> springBootLoggerGroups) {
return new LoggersEndpoint(loggingSystem, loggingGroupsObjectProvider.getIfAvailable()); return new LoggersEndpoint(loggingSystem, springBootLoggerGroups.getIfAvailable(LoggerGroups::new));
} }
static class OnEnabledLoggingSystemCondition extends SpringBootCondition { static class OnEnabledLoggingSystemCondition extends SpringBootCondition {

View File

@ -20,14 +20,15 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.logging.LoggersEndpoint; import org.springframework.boot.actuate.logging.LoggersEndpoint;
import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.logging.LoggerConfiguration; import org.springframework.boot.logging.LoggerConfiguration;
import org.springframework.boot.logging.LoggingGroups; import org.springframework.boot.logging.LoggerGroups;
import org.springframework.boot.logging.LoggingSystem; import org.springframework.boot.logging.LoggingSystem;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -57,18 +58,20 @@ class LoggersEndpointDocumentationTests extends MockMvcEndpointDocumentationTest
fieldWithPath("configuredLevel").description("Configured level of the logger, if any.").optional(), fieldWithPath("configuredLevel").description("Configured level of the logger, if any.").optional(),
fieldWithPath("effectiveLevel").description("Effective level of the logger.")); fieldWithPath("effectiveLevel").description("Effective level of the logger."));
private static final List<FieldDescriptor> groupLevelFields = Arrays.asList( private static final List<FieldDescriptor> groupLevelFields;
fieldWithPath("configuredLevel").description("Configured level of the logger group"),
fieldWithPath("members").description("Loggers that are part of this group").optional()); static {
groupLevelFields = Arrays.asList(
fieldWithPath("configuredLevel").description("Configured level of the logger group")
.type(LogLevel.class).optional(),
fieldWithPath("members").description("Loggers that are part of this group").optional());
}
@MockBean @MockBean
private LoggingSystem loggingSystem; private LoggingSystem loggingSystem;
@MockBean @Autowired
private ObjectProvider<LoggingGroups> loggingGroupsObjectProvider; private LoggerGroups loggerGroups;
@MockBean
LoggingGroups loggingGroups;
@Test @Test
void allLoggers() throws Exception { void allLoggers() throws Exception {
@ -76,10 +79,6 @@ class LoggersEndpointDocumentationTests extends MockMvcEndpointDocumentationTest
given(this.loggingSystem.getLoggerConfigurations()) given(this.loggingSystem.getLoggerConfigurations())
.willReturn(Arrays.asList(new LoggerConfiguration("ROOT", LogLevel.INFO, LogLevel.INFO), .willReturn(Arrays.asList(new LoggerConfiguration("ROOT", LogLevel.INFO, LogLevel.INFO),
new LoggerConfiguration("com.example", LogLevel.DEBUG, LogLevel.DEBUG))); new LoggerConfiguration("com.example", LogLevel.DEBUG, LogLevel.DEBUG)));
given(this.loggingGroupsObjectProvider.getIfAvailable()).willReturn(this.loggingGroups);
given(this.loggingGroups.getLoggerGroupNames()).willReturn(Collections.singleton("test"));
given(this.loggingGroups.getLoggerGroup("test")).willReturn(Arrays.asList("test.member"));
given(this.loggingGroups.getLoggerGroupConfiguredLevel("test")).willReturn(LogLevel.INFO);
this.mockMvc.perform(get("/actuator/loggers")).andExpect(status().isOk()) this.mockMvc.perform(get("/actuator/loggers")).andExpect(status().isOk())
.andDo(MockMvcRestDocumentation.document("loggers/all", .andDo(MockMvcRestDocumentation.document("loggers/all",
responseFields(fieldWithPath("levels").description("Levels support by the logging system."), responseFields(fieldWithPath("levels").description("Levels support by the logging system."),
@ -91,7 +90,6 @@ class LoggersEndpointDocumentationTests extends MockMvcEndpointDocumentationTest
@Test @Test
void logger() throws Exception { void logger() throws Exception {
given(this.loggingGroupsObjectProvider.getIfAvailable()).willReturn(this.loggingGroups);
given(this.loggingSystem.getLoggerConfiguration("com.example")) given(this.loggingSystem.getLoggerConfiguration("com.example"))
.willReturn(new LoggerConfiguration("com.example", LogLevel.INFO, LogLevel.INFO)); .willReturn(new LoggerConfiguration("com.example", LogLevel.INFO, LogLevel.INFO));
this.mockMvc.perform(get("/actuator/loggers/com.example")).andExpect(status().isOk()) this.mockMvc.perform(get("/actuator/loggers/com.example")).andExpect(status().isOk())
@ -100,17 +98,12 @@ class LoggersEndpointDocumentationTests extends MockMvcEndpointDocumentationTest
@Test @Test
void loggerGroups() throws Exception { void loggerGroups() throws Exception {
given(this.loggingGroupsObjectProvider.getIfAvailable()).willReturn(this.loggingGroups); this.mockMvc.perform(get("/actuator/loggers/test")).andExpect(status().isOk())
given(this.loggingGroups.isGroup("com.example")).willReturn(true);
given(this.loggingGroups.getLoggerGroup("com.example")).willReturn(Arrays.asList("com.member", "com.member2"));
given(this.loggingGroups.getLoggerGroupConfiguredLevel("com.example")).willReturn(LogLevel.INFO);
this.mockMvc.perform(get("/actuator/loggers/com.example")).andExpect(status().isOk())
.andDo(MockMvcRestDocumentation.document("loggers/group", responseFields(groupLevelFields))); .andDo(MockMvcRestDocumentation.document("loggers/group", responseFields(groupLevelFields)));
} }
@Test @Test
void setLogLevel() throws Exception { void setLogLevel() throws Exception {
given(this.loggingGroupsObjectProvider.getIfAvailable()).willReturn(this.loggingGroups);
this.mockMvc this.mockMvc
.perform(post("/actuator/loggers/com.example").content("{\"configuredLevel\":\"debug\"}") .perform(post("/actuator/loggers/com.example").content("{\"configuredLevel\":\"debug\"}")
.contentType(MediaType.APPLICATION_JSON)) .contentType(MediaType.APPLICATION_JSON))
@ -122,22 +115,26 @@ class LoggersEndpointDocumentationTests extends MockMvcEndpointDocumentationTest
@Test @Test
void setLogLevelOfLoggerGroup() throws Exception { void setLogLevelOfLoggerGroup() throws Exception {
given(this.loggingGroupsObjectProvider.getIfAvailable()).willReturn(this.loggingGroups);
given(this.loggingGroups.isGroup("com.example")).willReturn(true);
this.mockMvc this.mockMvc
.perform(post("/actuator/loggers/com.example") .perform(post("/actuator/loggers/test")
.content("{\"configuredLevel\":\"debug\"}").contentType(MediaType.APPLICATION_JSON)) .content("{\"configuredLevel\":\"debug\"}").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isNoContent()).andDo( .andExpect(status().isNoContent()).andDo(
MockMvcRestDocumentation.document("loggers/setGroup", MockMvcRestDocumentation.document("loggers/setGroup",
requestFields(fieldWithPath("configuredLevel").description( requestFields(fieldWithPath("configuredLevel").description(
"Level for the logger group. May be omitted to clear the level of the loggers.") "Level for the logger group. May be omitted to clear the level of the loggers.")
.optional()))); .optional())));
verify(this.loggingGroups).setLoggerGroupLevel("com.example", LogLevel.DEBUG); verify(this.loggingSystem).setLogLevel("test.member1", LogLevel.DEBUG);
verify(this.loggingSystem).setLogLevel("test.member2", LogLevel.DEBUG);
resetLogger();
}
private void resetLogger() {
this.loggerGroups.get("test").configureLogLevel(null, (a, b) -> {
});
} }
@Test @Test
void clearLogLevel() throws Exception { void clearLogLevel() throws Exception {
given(this.loggingGroupsObjectProvider.getIfAvailable()).willReturn(this.loggingGroups);
this.mockMvc this.mockMvc
.perform(post("/actuator/loggers/com.example").content("{}").contentType(MediaType.APPLICATION_JSON)) .perform(post("/actuator/loggers/com.example").content("{}").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isNoContent()).andDo(MockMvcRestDocumentation.document("loggers/clear")); .andExpect(status().isNoContent()).andDo(MockMvcRestDocumentation.document("loggers/clear"));
@ -149,9 +146,13 @@ class LoggersEndpointDocumentationTests extends MockMvcEndpointDocumentationTest
static class TestConfiguration { static class TestConfiguration {
@Bean @Bean
LoggersEndpoint endpoint(LoggingSystem loggingSystem, LoggersEndpoint endpoint(LoggingSystem loggingSystem, LoggerGroups groups) {
ObjectProvider<LoggingGroups> loggingGroupsObjectProvider) { groups.putAll(getLoggerGroups());
return new LoggersEndpoint(loggingSystem, loggingGroupsObjectProvider.getIfAvailable()); return new LoggersEndpoint(loggingSystem, groups);
}
private Map<String, List<String>> getLoggerGroups() {
return Collections.singletonMap("test", Arrays.asList("test.member1", "test.member2"));
} }
} }

View File

@ -31,7 +31,8 @@ import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.logging.LoggerConfiguration; import org.springframework.boot.logging.LoggerConfiguration;
import org.springframework.boot.logging.LoggingGroups; import org.springframework.boot.logging.LoggerGroup;
import org.springframework.boot.logging.LoggerGroups;
import org.springframework.boot.logging.LoggingSystem; import org.springframework.boot.logging.LoggingSystem;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -49,17 +50,18 @@ public class LoggersEndpoint {
private final LoggingSystem loggingSystem; private final LoggingSystem loggingSystem;
private final LoggingGroups loggingGroups; private final LoggerGroups loggerGroups;
/** /**
* Create a new {@link LoggersEndpoint} instance. * Create a new {@link LoggersEndpoint} instance.
* @param loggingSystem the logging system to expose * @param loggingSystem the logging system to expose
* @param loggingGroups the logging group to expose if it exists * @param loggerGroups the logger group to expose
*/ */
public LoggersEndpoint(LoggingSystem loggingSystem, LoggingGroups loggingGroups) { public LoggersEndpoint(LoggingSystem loggingSystem, LoggerGroups loggerGroups) {
Assert.notNull(loggingSystem, "LoggingSystem must not be null"); Assert.notNull(loggingSystem, "LoggingSystem must not be null");
Assert.notNull(loggerGroups, "LoggerGroups must not be null");
this.loggingSystem = loggingSystem; this.loggingSystem = loggingSystem;
this.loggingGroups = loggingGroups; this.loggerGroups = loggerGroups;
} }
@ReadOperation @ReadOperation
@ -71,20 +73,23 @@ public class LoggersEndpoint {
Map<String, Object> result = new LinkedHashMap<>(); Map<String, Object> result = new LinkedHashMap<>();
result.put("levels", getLevels()); result.put("levels", getLevels());
result.put("loggers", getLoggers(configurations)); result.put("loggers", getLoggers(configurations));
if (this.loggingGroups != null && this.loggingGroups.getLoggerGroupNames() != null) { result.put("groups", getGroups());
Set<String> groups = this.loggingGroups.getLoggerGroupNames();
result.put("groups", getLoggerGroups(groups));
}
return result; return result;
} }
private Map<String, LoggerLevels> getGroups() {
Map<String, LoggerLevels> groups = new LinkedHashMap<>();
this.loggerGroups.forEach((group) -> groups.put(group.getName(),
new GroupLoggerLevels(group.getConfiguredLevel(), group.getMembers())));
return groups;
}
@ReadOperation @ReadOperation
public LoggerLevels loggerLevels(@Selector String name) { public LoggerLevels loggerLevels(@Selector String name) {
Assert.notNull(name, "Name must not be null"); Assert.notNull(name, "Name must not be null");
if (this.loggingGroups != null && this.loggingGroups.isGroup(name)) { LoggerGroup group = this.loggerGroups.get(name);
List<String> members = this.loggingGroups.getLoggerGroup(name); if (group != null) {
LogLevel groupConfiguredLevel = this.loggingGroups.getLoggerGroupConfiguredLevel(name); return new GroupLoggerLevels(group.getConfiguredLevel(), group.getMembers());
return new GroupLoggerLevels(groupConfiguredLevel, members);
} }
LoggerConfiguration configuration = this.loggingSystem.getLoggerConfiguration(name); LoggerConfiguration configuration = this.loggingSystem.getLoggerConfiguration(name);
return (configuration != null) ? new SingleLoggerLevels(configuration) : null; return (configuration != null) ? new SingleLoggerLevels(configuration) : null;
@ -93,8 +98,9 @@ public class LoggersEndpoint {
@WriteOperation @WriteOperation
public void configureLogLevel(@Selector String name, @Nullable LogLevel configuredLevel) { public void configureLogLevel(@Selector String name, @Nullable LogLevel configuredLevel) {
Assert.notNull(name, "Name must not be empty"); Assert.notNull(name, "Name must not be empty");
if (this.loggingGroups != null && this.loggingGroups.isGroup(name)) { LoggerGroup group = this.loggerGroups.get(name);
this.loggingGroups.setLoggerGroupLevel(name, configuredLevel); if (group != null && group.hasMembers()) {
group.configureLogLevel(configuredLevel, this.loggingSystem::setLogLevel);
return; return;
} }
this.loggingSystem.setLogLevel(name, configuredLevel); this.loggingSystem.setLogLevel(name, configuredLevel);
@ -113,16 +119,6 @@ public class LoggersEndpoint {
return loggers; return loggers;
} }
private Map<String, LoggerLevels> getLoggerGroups(Set<String> groups) {
Map<String, LoggerLevels> loggerGroups = new LinkedHashMap<>(groups.size());
for (String name : groups) {
List<String> members = this.loggingGroups.getLoggerGroup(name);
LogLevel groupConfiguredLevel = this.loggingGroups.getLoggerGroupConfiguredLevel(name);
loggerGroups.put(name, new GroupLoggerLevels(groupConfiguredLevel, members));
}
return loggerGroups;
}
/** /**
* Levels configured for a given logger exposed in a JSON friendly way. * Levels configured for a given logger exposed in a JSON friendly way.
*/ */
@ -134,13 +130,12 @@ public class LoggersEndpoint {
this.configuredLevel = getName(configuredLevel); this.configuredLevel = getName(configuredLevel);
} }
private String getName(LogLevel level) { protected final String getName(LogLevel level) {
return (level != null) ? level.name() : null; return (level != null) ? level.name() : null;
} }
public String getConfiguredLevel() { public String getConfiguredLevel() {
return this.configuredLevel; return this.configuredLevel;
} }
} }
@ -166,7 +161,7 @@ public class LoggersEndpoint {
public SingleLoggerLevels(LoggerConfiguration configuration) { public SingleLoggerLevels(LoggerConfiguration configuration) {
super(configuration.getConfiguredLevel()); super(configuration.getConfiguredLevel());
this.effectiveLevel = super.getName(configuration.getEffectiveLevel()); this.effectiveLevel = getName(configuration.getEffectiveLevel());
} }
public String getEffectiveLevel() { public String getEffectiveLevel() {

View File

@ -18,9 +18,11 @@ package org.springframework.boot.actuate.logging;
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.logging.LoggersEndpoint.GroupLoggerLevels; import org.springframework.boot.actuate.logging.LoggersEndpoint.GroupLoggerLevels;
@ -28,7 +30,7 @@ import org.springframework.boot.actuate.logging.LoggersEndpoint.LoggerLevels;
import org.springframework.boot.actuate.logging.LoggersEndpoint.SingleLoggerLevels; import org.springframework.boot.actuate.logging.LoggersEndpoint.SingleLoggerLevels;
import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.logging.LoggerConfiguration; import org.springframework.boot.logging.LoggerConfiguration;
import org.springframework.boot.logging.LoggingGroups; import org.springframework.boot.logging.LoggerGroups;
import org.springframework.boot.logging.LoggingSystem; import org.springframework.boot.logging.LoggingSystem;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -42,12 +44,21 @@ import static org.mockito.Mockito.verify;
* @author Ben Hale * @author Ben Hale
* @author Andy Wilkinson * @author Andy Wilkinson
* @author HaiTao Zhang * @author HaiTao Zhang
* @author Madhura Bhave
*/ */
class LoggersEndpointTests { class LoggersEndpointTests {
private final LoggingSystem loggingSystem = mock(LoggingSystem.class); private final LoggingSystem loggingSystem = mock(LoggingSystem.class);
private final LoggingGroups loggingGroups = mock(LoggingGroups.class); private LoggerGroups loggerGroups;
@BeforeEach
void setup() {
Map<String, List<String>> groups = Collections.singletonMap("test", Collections.singletonList("test.member"));
this.loggerGroups = new LoggerGroups(groups);
this.loggerGroups.get("test").configureLogLevel(LogLevel.DEBUG, (a, b) -> {
});
}
@Test @Test
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -55,8 +66,7 @@ class LoggersEndpointTests {
given(this.loggingSystem.getLoggerConfigurations()) given(this.loggingSystem.getLoggerConfigurations())
.willReturn(Collections.singletonList(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG))); .willReturn(Collections.singletonList(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG)));
given(this.loggingSystem.getSupportedLogLevels()).willReturn(EnumSet.allOf(LogLevel.class)); given(this.loggingSystem.getSupportedLogLevels()).willReturn(EnumSet.allOf(LogLevel.class));
given(this.loggingGroups.getLoggerGroupNames()).willReturn(null); Map<String, Object> result = new LoggersEndpoint(this.loggingSystem, new LoggerGroups()).loggers();
Map<String, Object> result = new LoggersEndpoint(this.loggingSystem, this.loggingGroups).loggers();
Map<String, LoggerLevels> loggers = (Map<String, LoggerLevels>) result.get("loggers"); Map<String, LoggerLevels> loggers = (Map<String, LoggerLevels>) result.get("loggers");
Set<LogLevel> levels = (Set<LogLevel>) result.get("levels"); Set<LogLevel> levels = (Set<LogLevel>) result.get("levels");
SingleLoggerLevels rootLevels = (SingleLoggerLevels) loggers.get("ROOT"); SingleLoggerLevels rootLevels = (SingleLoggerLevels) loggers.get("ROOT");
@ -64,7 +74,8 @@ class LoggersEndpointTests {
assertThat(rootLevels.getEffectiveLevel()).isEqualTo("DEBUG"); assertThat(rootLevels.getEffectiveLevel()).isEqualTo("DEBUG");
assertThat(levels).containsExactly(LogLevel.OFF, LogLevel.FATAL, LogLevel.ERROR, LogLevel.WARN, LogLevel.INFO, assertThat(levels).containsExactly(LogLevel.OFF, LogLevel.FATAL, LogLevel.ERROR, LogLevel.WARN, LogLevel.INFO,
LogLevel.DEBUG, LogLevel.TRACE); LogLevel.DEBUG, LogLevel.TRACE);
assertThat(result.get("groups")).isNull(); Map<String, LoggerGroups> groups = (Map<String, LoggerGroups>) result.get("groups");
assertThat(groups).isEmpty();
} }
@Test @Test
@ -73,12 +84,9 @@ class LoggersEndpointTests {
given(this.loggingSystem.getLoggerConfigurations()) given(this.loggingSystem.getLoggerConfigurations())
.willReturn(Collections.singletonList(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG))); .willReturn(Collections.singletonList(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG)));
given(this.loggingSystem.getSupportedLogLevels()).willReturn(EnumSet.allOf(LogLevel.class)); given(this.loggingSystem.getSupportedLogLevels()).willReturn(EnumSet.allOf(LogLevel.class));
given(this.loggingGroups.getLoggerGroup("test")).willReturn(Collections.singletonList("test.member")); Map<String, Object> result = new LoggersEndpoint(this.loggingSystem, this.loggerGroups).loggers();
given(this.loggingGroups.getLoggerGroupNames()).willReturn(Collections.singleton("test")); Map<String, GroupLoggerLevels> loggerGroups = (Map<String, GroupLoggerLevels>) result.get("groups");
given(this.loggingGroups.getLoggerGroupConfiguredLevel("test")).willReturn(LogLevel.DEBUG); GroupLoggerLevels groupLevel = loggerGroups.get("test");
Map<String, Object> result = new LoggersEndpoint(this.loggingSystem, this.loggingGroups).loggers();
Map<String, LoggerLevels> loggerGroups = (Map<String, LoggerLevels>) result.get("groups");
GroupLoggerLevels testLoggerLevel = (GroupLoggerLevels) loggerGroups.get("test");
Map<String, LoggerLevels> loggers = (Map<String, LoggerLevels>) result.get("loggers"); Map<String, LoggerLevels> loggers = (Map<String, LoggerLevels>) result.get("loggers");
Set<LogLevel> levels = (Set<LogLevel>) result.get("levels"); Set<LogLevel> levels = (Set<LogLevel>) result.get("levels");
SingleLoggerLevels rootLevels = (SingleLoggerLevels) loggers.get("ROOT"); SingleLoggerLevels rootLevels = (SingleLoggerLevels) loggers.get("ROOT");
@ -87,17 +95,15 @@ class LoggersEndpointTests {
assertThat(levels).containsExactly(LogLevel.OFF, LogLevel.FATAL, LogLevel.ERROR, LogLevel.WARN, LogLevel.INFO, assertThat(levels).containsExactly(LogLevel.OFF, LogLevel.FATAL, LogLevel.ERROR, LogLevel.WARN, LogLevel.INFO,
LogLevel.DEBUG, LogLevel.TRACE); LogLevel.DEBUG, LogLevel.TRACE);
assertThat(loggerGroups).isNotNull(); assertThat(loggerGroups).isNotNull();
assertThat(testLoggerLevel).isNotNull(); assertThat(groupLevel.getConfiguredLevel()).isEqualTo("DEBUG");
assertThat(testLoggerLevel.getConfiguredLevel()).isEqualTo("DEBUG"); assertThat(groupLevel.getMembers()).containsExactly("test.member");
assertThat(testLoggerLevel.getMembers()).isEqualTo(Collections.singletonList("test.member"));
} }
@Test @Test
void loggerLevelsWhenNameSpecifiedShouldReturnLevels() { void loggerLevelsWhenNameSpecifiedShouldReturnLevels() {
given(this.loggingGroups.isGroup("ROOT")).willReturn(false);
given(this.loggingSystem.getLoggerConfiguration("ROOT")) given(this.loggingSystem.getLoggerConfiguration("ROOT"))
.willReturn(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG)); .willReturn(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG));
SingleLoggerLevels levels = (SingleLoggerLevels) new LoggersEndpoint(this.loggingSystem, this.loggingGroups) SingleLoggerLevels levels = (SingleLoggerLevels) new LoggersEndpoint(this.loggingSystem, this.loggerGroups)
.loggerLevels("ROOT"); .loggerLevels("ROOT");
assertThat(levels.getConfiguredLevel()).isNull(); assertThat(levels.getConfiguredLevel()).isNull();
assertThat(levels.getEffectiveLevel()).isEqualTo("DEBUG"); assertThat(levels.getEffectiveLevel()).isEqualTo("DEBUG");
@ -105,10 +111,7 @@ class LoggersEndpointTests {
@Test @Test
void groupNameSpecifiedShouldReturnConfiguredLevelAndMembers() { void groupNameSpecifiedShouldReturnConfiguredLevelAndMembers() {
given(this.loggingGroups.isGroup("test")).willReturn(true); GroupLoggerLevels levels = (GroupLoggerLevels) new LoggersEndpoint(this.loggingSystem, this.loggerGroups)
given(this.loggingGroups.getLoggerGroup("test")).willReturn(Collections.singletonList("test.member"));
given(this.loggingGroups.getLoggerGroupConfiguredLevel("test")).willReturn(LogLevel.DEBUG);
GroupLoggerLevels levels = (GroupLoggerLevels) new LoggersEndpoint(this.loggingSystem, this.loggingGroups)
.loggerLevels("test"); .loggerLevels("test");
assertThat(levels.getConfiguredLevel()).isEqualTo("DEBUG"); assertThat(levels.getConfiguredLevel()).isEqualTo("DEBUG");
assertThat(levels.getMembers()).isEqualTo(Collections.singletonList("test.member")); assertThat(levels.getMembers()).isEqualTo(Collections.singletonList("test.member"));
@ -116,35 +119,26 @@ class LoggersEndpointTests {
@Test @Test
void configureLogLevelShouldSetLevelOnLoggingSystem() { void configureLogLevelShouldSetLevelOnLoggingSystem() {
given(this.loggingGroups.getLoggerGroup("ROOT")).willReturn(null); new LoggersEndpoint(this.loggingSystem, this.loggerGroups).configureLogLevel("ROOT", LogLevel.DEBUG);
new LoggersEndpoint(this.loggingSystem, this.loggingGroups).configureLogLevel("ROOT", LogLevel.DEBUG);
verify(this.loggingSystem).setLogLevel("ROOT", LogLevel.DEBUG); verify(this.loggingSystem).setLogLevel("ROOT", LogLevel.DEBUG);
} }
@Test @Test
void configureLogLevelWithNullSetsLevelOnLoggingSystemToNull() { void configureLogLevelWithNullSetsLevelOnLoggingSystemToNull() {
given(this.loggingGroups.getLoggerGroup("ROOT")).willReturn(null); new LoggersEndpoint(this.loggingSystem, this.loggerGroups).configureLogLevel("ROOT", null);
new LoggersEndpoint(this.loggingSystem, this.loggingGroups).configureLogLevel("ROOT", null);
verify(this.loggingSystem).setLogLevel("ROOT", null); verify(this.loggingSystem).setLogLevel("ROOT", null);
} }
@Test @Test
void configureLogLevelInLoggerGroupShouldSetLevelOnLoggingSystem() { void configureLogLevelInLoggerGroupShouldSetLevelOnLoggingSystem() {
given(this.loggingGroups.isGroup("test")).willReturn(true); new LoggersEndpoint(this.loggingSystem, this.loggerGroups).configureLogLevel("test", LogLevel.DEBUG);
given(this.loggingGroups.getLoggerGroup("test")).willReturn(Collections.singletonList("test.member")); verify(this.loggingSystem).setLogLevel("test.member", LogLevel.DEBUG);
new LoggersEndpoint(this.loggingSystem, this.loggingGroups).configureLogLevel("test", LogLevel.DEBUG);
verify(this.loggingGroups).setLoggerGroupLevel("test", LogLevel.DEBUG);
} }
@Test @Test
void configureLogLevelWithNullInLoggerGroupShouldSetLevelOnLoggingSystem() { void configureLogLevelWithNullInLoggerGroupShouldSetLevelOnLoggingSystem() {
given(this.loggingGroups.isGroup("test")).willReturn(true); new LoggersEndpoint(this.loggingSystem, this.loggerGroups).configureLogLevel("test", null);
given(this.loggingGroups.getLoggerGroup("test")).willReturn(Collections.singletonList("test.member")); verify(this.loggingSystem).setLogLevel("test.member", null);
new LoggersEndpoint(this.loggingSystem, this.loggingGroups).configureLogLevel("test", null);
verify(this.loggingGroups).setLoggerGroupLevel("test", null);
} }
// @Test
// void
} }

View File

@ -19,6 +19,9 @@ package org.springframework.boot.actuate.logging;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import net.minidev.json.JSONArray; import net.minidev.json.JSONArray;
import org.hamcrest.collection.IsIterableContainingInAnyOrder; import org.hamcrest.collection.IsIterableContainingInAnyOrder;
@ -31,7 +34,7 @@ import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
import org.springframework.boot.actuate.endpoint.web.test.WebEndpointTest; import org.springframework.boot.actuate.endpoint.web.test.WebEndpointTest;
import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.logging.LoggerConfiguration; import org.springframework.boot.logging.LoggerConfiguration;
import org.springframework.boot.logging.LoggingGroups; import org.springframework.boot.logging.LoggerGroups;
import org.springframework.boot.logging.LoggingSystem; import org.springframework.boot.logging.LoggingSystem;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -54,6 +57,7 @@ import static org.mockito.Mockito.verifyZeroInteractions;
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Andy Wilkinson * @author Andy Wilkinson
* @author HaiTao Zhang * @author HaiTao Zhang
* @author Madhura Bhave
*/ */
class LoggersEndpointWebIntegrationTests { class LoggersEndpointWebIntegrationTests {
@ -61,41 +65,21 @@ class LoggersEndpointWebIntegrationTests {
private LoggingSystem loggingSystem; private LoggingSystem loggingSystem;
private LoggingGroups loggingGroups; private LoggerGroups loggerGroups;
private ObjectProvider<LoggingGroups> loggingGroupsObjectProvider;
@BeforeEach @BeforeEach
@AfterEach @AfterEach
void resetMocks(ConfigurableApplicationContext context, WebTestClient client) { void resetMocks(ConfigurableApplicationContext context, WebTestClient client) {
this.client = client; this.client = client;
this.loggingSystem = context.getBean(LoggingSystem.class); this.loggingSystem = context.getBean(LoggingSystem.class);
this.loggingGroups = context.getBean(LoggingGroups.class); this.loggerGroups = context.getBean(LoggerGroups.class);
this.loggingGroupsObjectProvider = context.getBean(ObjectProvider.class);
Mockito.reset(this.loggingSystem); Mockito.reset(this.loggingSystem);
Mockito.reset(this.loggingGroups);
given(this.loggingSystem.getSupportedLogLevels()).willReturn(EnumSet.allOf(LogLevel.class)); given(this.loggingSystem.getSupportedLogLevels()).willReturn(EnumSet.allOf(LogLevel.class));
given(this.loggingGroupsObjectProvider.getIfAvailable()).willReturn(this.loggingGroups);
}
@WebEndpointTest
void getLoggerShouldReturnAllLoggerConfigurations() {
given(this.loggingGroups.getLoggerGroupNames()).willReturn(null);
given(this.loggingSystem.getLoggerConfigurations())
.willReturn(Collections.singletonList(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG)));
this.client.get().uri("/actuator/loggers").exchange().expectStatus().isOk().expectBody().jsonPath("$.length()")
.isEqualTo(2).jsonPath("levels")
.isEqualTo(jsonArrayOf("OFF", "FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"))
.jsonPath("loggers.length()").isEqualTo(1).jsonPath("loggers.ROOT.length()").isEqualTo(2)
.jsonPath("loggers.ROOT.configuredLevel").isEqualTo(null).jsonPath("loggers.ROOT.effectiveLevel")
.isEqualTo("DEBUG");
} }
@WebEndpointTest @WebEndpointTest
void getLoggerShouldReturnAllLoggerConfigurationsWithLoggerGroups() { void getLoggerShouldReturnAllLoggerConfigurationsWithLoggerGroups() {
given(this.loggingGroups.getLoggerGroupNames()).willReturn(Collections.singleton("test")); setLogLevelToDebug("test");
given(this.loggingGroups.getLoggerGroup("test")).willReturn(Arrays.asList("test.member1", "test.member2"));
given(this.loggingGroups.getLoggerGroupConfiguredLevel("test")).willReturn(LogLevel.DEBUG);
given(this.loggingSystem.getLoggerConfigurations()) given(this.loggingSystem.getLoggerConfigurations())
.willReturn(Collections.singletonList(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG))); .willReturn(Collections.singletonList(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG)));
this.client.get().uri("/actuator/loggers").exchange().expectStatus().isOk().expectBody().jsonPath("$.length()") this.client.get().uri("/actuator/loggers").exchange().expectStatus().isOk().expectBody().jsonPath("$.length()")
@ -103,15 +87,13 @@ class LoggersEndpointWebIntegrationTests {
.isEqualTo(jsonArrayOf("OFF", "FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE")) .isEqualTo(jsonArrayOf("OFF", "FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"))
.jsonPath("loggers.length()").isEqualTo(1).jsonPath("loggers.ROOT.length()").isEqualTo(2) .jsonPath("loggers.length()").isEqualTo(1).jsonPath("loggers.ROOT.length()").isEqualTo(2)
.jsonPath("loggers.ROOT.configuredLevel").isEqualTo(null).jsonPath("loggers.ROOT.effectiveLevel") .jsonPath("loggers.ROOT.configuredLevel").isEqualTo(null).jsonPath("loggers.ROOT.effectiveLevel")
.isEqualTo("DEBUG").jsonPath("groups.length()").isEqualTo(1).jsonPath("groups.test.length()") .isEqualTo("DEBUG").jsonPath("groups.length()").isEqualTo(2).jsonPath("groups.test.configuredLevel")
.isEqualTo(2).jsonPath("groups.test.configuredLevel").isEqualTo("DEBUG") .isEqualTo("DEBUG");
.jsonPath("groups.test.members.length()").isEqualTo(2).jsonPath("groups.test.members")
.value(IsIterableContainingInAnyOrder.containsInAnyOrder("test.member1", "test.member2"));
} }
@WebEndpointTest @WebEndpointTest
void getLoggerShouldReturnLogLevels() { void getLoggerShouldReturnLogLevels() {
given(this.loggingGroups.isGroup("ROOT")).willReturn(false); setLogLevelToDebug("test");
given(this.loggingSystem.getLoggerConfiguration("ROOT")) given(this.loggingSystem.getLoggerConfiguration("ROOT"))
.willReturn(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG)); .willReturn(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG));
this.client.get().uri("/actuator/loggers/ROOT").exchange().expectStatus().isOk().expectBody() this.client.get().uri("/actuator/loggers/ROOT").exchange().expectStatus().isOk().expectBody()
@ -126,9 +108,7 @@ class LoggersEndpointWebIntegrationTests {
@WebEndpointTest @WebEndpointTest
void getLoggerGroupShouldReturnConfiguredLogLevelAndMembers() { void getLoggerGroupShouldReturnConfiguredLogLevelAndMembers() {
given(this.loggingGroups.isGroup("test")).willReturn(true); setLogLevelToDebug("test");
given(this.loggingGroups.getLoggerGroupConfiguredLevel("test")).willReturn(LogLevel.DEBUG);
given(this.loggingGroups.getLoggerGroup("test")).willReturn(Arrays.asList("test.member1", "test.member2"));
this.client.get().uri("actuator/loggers/test").exchange().expectStatus().isOk().expectBody() this.client.get().uri("actuator/loggers/test").exchange().expectStatus().isOk().expectBody()
.jsonPath("$.length()").isEqualTo(2).jsonPath("members") .jsonPath("$.length()").isEqualTo(2).jsonPath("members")
.value(IsIterableContainingInAnyOrder.containsInAnyOrder("test.member1", "test.member2")) .value(IsIterableContainingInAnyOrder.containsInAnyOrder("test.member1", "test.member2"))
@ -137,7 +117,6 @@ class LoggersEndpointWebIntegrationTests {
@WebEndpointTest @WebEndpointTest
void setLoggerUsingApplicationJsonShouldSetLogLevel() { void setLoggerUsingApplicationJsonShouldSetLogLevel() {
given(this.loggingGroups.isGroup("ROOT")).willReturn(false);
this.client.post().uri("/actuator/loggers/ROOT").contentType(MediaType.APPLICATION_JSON) this.client.post().uri("/actuator/loggers/ROOT").contentType(MediaType.APPLICATION_JSON)
.body(Collections.singletonMap("configuredLevel", "debug")).exchange().expectStatus().isNoContent(); .body(Collections.singletonMap("configuredLevel", "debug")).exchange().expectStatus().isNoContent();
verify(this.loggingSystem).setLogLevel("ROOT", LogLevel.DEBUG); verify(this.loggingSystem).setLogLevel("ROOT", LogLevel.DEBUG);
@ -145,7 +124,6 @@ class LoggersEndpointWebIntegrationTests {
@WebEndpointTest @WebEndpointTest
void setLoggerUsingActuatorV2JsonShouldSetLogLevel() { void setLoggerUsingActuatorV2JsonShouldSetLogLevel() {
given(this.loggingGroups.isGroup("ROOT")).willReturn(false);
this.client.post().uri("/actuator/loggers/ROOT") this.client.post().uri("/actuator/loggers/ROOT")
.contentType(MediaType.parseMediaType(ActuatorMediaType.V2_JSON)) .contentType(MediaType.parseMediaType(ActuatorMediaType.V2_JSON))
.body(Collections.singletonMap("configuredLevel", "debug")).exchange().expectStatus().isNoContent(); .body(Collections.singletonMap("configuredLevel", "debug")).exchange().expectStatus().isNoContent();
@ -154,21 +132,19 @@ class LoggersEndpointWebIntegrationTests {
@WebEndpointTest @WebEndpointTest
void setLoggerGroupUsingActuatorV2JsonShouldSetLogLevel() { void setLoggerGroupUsingActuatorV2JsonShouldSetLogLevel() {
given(this.loggingGroups.isGroup("test")).willReturn(true);
given(this.loggingGroups.getLoggerGroup("test")).willReturn(Arrays.asList("test.member1", "test.member2"));
this.client.post().uri("/actuator/loggers/test") this.client.post().uri("/actuator/loggers/test")
.contentType(MediaType.parseMediaType(ActuatorMediaType.V2_JSON)) .contentType(MediaType.parseMediaType(ActuatorMediaType.V2_JSON))
.body(Collections.singletonMap("configuredLevel", "debug")).exchange().expectStatus().isNoContent(); .body(Collections.singletonMap("configuredLevel", "debug")).exchange().expectStatus().isNoContent();
verify(this.loggingGroups).setLoggerGroupLevel("test", LogLevel.DEBUG); verify(this.loggingSystem).setLogLevel("test.member1", LogLevel.DEBUG);
verify(this.loggingSystem).setLogLevel("test.member2", LogLevel.DEBUG);
} }
@WebEndpointTest @WebEndpointTest
void setLoggerGroupUsingApplicationJsonShouldSetLogLevel() { void setLoggerGroupUsingApplicationJsonShouldSetLogLevel() {
given(this.loggingGroups.isGroup("test")).willReturn(true);
given(this.loggingGroups.getLoggerGroup("test")).willReturn(Arrays.asList("test.member1", "test.member2"));
this.client.post().uri("/actuator/loggers/test").contentType(MediaType.APPLICATION_JSON) this.client.post().uri("/actuator/loggers/test").contentType(MediaType.APPLICATION_JSON)
.body(Collections.singletonMap("configuredLevel", "debug")).exchange().expectStatus().isNoContent(); .body(Collections.singletonMap("configuredLevel", "debug")).exchange().expectStatus().isNoContent();
verify(this.loggingGroups).setLoggerGroupLevel("test", LogLevel.DEBUG); verify(this.loggingSystem).setLogLevel("test.member1", LogLevel.DEBUG);
verify(this.loggingSystem).setLogLevel("test.member2", LogLevel.DEBUG);
} }
@WebEndpointTest @WebEndpointTest
@ -180,7 +156,6 @@ class LoggersEndpointWebIntegrationTests {
@WebEndpointTest @WebEndpointTest
void setLoggerWithNullLogLevel() { void setLoggerWithNullLogLevel() {
given(this.loggingGroups.isGroup("ROOT")).willReturn(false);
this.client.post().uri("/actuator/loggers/ROOT") this.client.post().uri("/actuator/loggers/ROOT")
.contentType(MediaType.parseMediaType(ActuatorMediaType.V2_JSON)) .contentType(MediaType.parseMediaType(ActuatorMediaType.V2_JSON))
.body(Collections.singletonMap("configuredLevel", null)).exchange().expectStatus().isNoContent(); .body(Collections.singletonMap("configuredLevel", null)).exchange().expectStatus().isNoContent();
@ -189,7 +164,6 @@ class LoggersEndpointWebIntegrationTests {
@WebEndpointTest @WebEndpointTest
void setLoggerWithNoLogLevel() { void setLoggerWithNoLogLevel() {
given(this.loggingGroups.isGroup("ROOT")).willReturn(false);
this.client.post().uri("/actuator/loggers/ROOT") this.client.post().uri("/actuator/loggers/ROOT")
.contentType(MediaType.parseMediaType(ActuatorMediaType.V2_JSON)).body(Collections.emptyMap()) .contentType(MediaType.parseMediaType(ActuatorMediaType.V2_JSON)).body(Collections.emptyMap())
.exchange().expectStatus().isNoContent(); .exchange().expectStatus().isNoContent();
@ -198,22 +172,20 @@ class LoggersEndpointWebIntegrationTests {
@WebEndpointTest @WebEndpointTest
void setLoggerGroupWithNullLogLevel() { void setLoggerGroupWithNullLogLevel() {
given(this.loggingGroups.isGroup("test")).willReturn(true);
given(this.loggingGroups.getLoggerGroup("test")).willReturn(Arrays.asList("test.member1", "test.member2"));
this.client.post().uri("/actuator/loggers/test") this.client.post().uri("/actuator/loggers/test")
.contentType(MediaType.parseMediaType(ActuatorMediaType.V2_JSON)) .contentType(MediaType.parseMediaType(ActuatorMediaType.V2_JSON))
.body(Collections.singletonMap("configuredLevel", null)).exchange().expectStatus().isNoContent(); .body(Collections.singletonMap("configuredLevel", null)).exchange().expectStatus().isNoContent();
verify(this.loggingGroups).setLoggerGroupLevel("test", null); verify(this.loggingSystem).setLogLevel("test.member1", null);
verify(this.loggingSystem).setLogLevel("test.member2", null);
} }
@WebEndpointTest @WebEndpointTest
void setLoggerGroupWithNoLogLevel() { void setLoggerGroupWithNoLogLevel() {
given(this.loggingGroups.isGroup("test")).willReturn(true);
given(this.loggingGroups.getLoggerGroup("test")).willReturn(Arrays.asList("test.member1", "test.member2"));
this.client.post().uri("/actuator/loggers/test") this.client.post().uri("/actuator/loggers/test")
.contentType(MediaType.parseMediaType(ActuatorMediaType.V2_JSON)).body(Collections.emptyMap()) .contentType(MediaType.parseMediaType(ActuatorMediaType.V2_JSON)).body(Collections.emptyMap())
.exchange().expectStatus().isNoContent(); .exchange().expectStatus().isNoContent();
verify(this.loggingGroups).setLoggerGroupLevel("test", null); verify(this.loggingSystem).setLogLevel("test.member1", null);
verify(this.loggingSystem).setLogLevel("test.member2", null);
} }
@WebEndpointTest @WebEndpointTest
@ -227,12 +199,15 @@ class LoggersEndpointWebIntegrationTests {
@WebEndpointTest @WebEndpointTest
void logLevelForLoggerGroupWithNameThatCouldBeMistakenForAPathExtension() { void logLevelForLoggerGroupWithNameThatCouldBeMistakenForAPathExtension() {
given(this.loggingGroups.isGroup("com.png")).willReturn(true); setLogLevelToDebug("group.png");
given(this.loggingGroups.getLoggerGroupConfiguredLevel("com.png")).willReturn(LogLevel.DEBUG); this.client.get().uri("/actuator/loggers/group.png").exchange().expectStatus().isOk().expectBody()
given(this.loggingGroups.getLoggerGroup("com.png")).willReturn(Arrays.asList("test.member1", "test.member2"));
this.client.get().uri("/actuator/loggers/com.png").exchange().expectStatus().isOk().expectBody()
.jsonPath("$.length()").isEqualTo(2).jsonPath("configuredLevel").isEqualTo("DEBUG").jsonPath("members") .jsonPath("$.length()").isEqualTo(2).jsonPath("configuredLevel").isEqualTo("DEBUG").jsonPath("members")
.value(IsIterableContainingInAnyOrder.containsInAnyOrder("test.member1", "test.member2")); .value(IsIterableContainingInAnyOrder.containsInAnyOrder("png.member1", "png.member2"));
}
private void setLogLevelToDebug(String name) {
this.loggerGroups.get(name).configureLogLevel(LogLevel.DEBUG, (a, b) -> {
});
} }
private JSONArray jsonArrayOf(Object... entries) { private JSONArray jsonArrayOf(Object... entries) {
@ -250,18 +225,20 @@ class LoggersEndpointWebIntegrationTests {
} }
@Bean @Bean
ObjectProvider<LoggingGroups> loggingGroupsObjectProvider() { LoggerGroups loggingGroups() {
return mock(ObjectProvider.class); return getLoggerGroups();
} }
@Bean private LoggerGroups getLoggerGroups() {
LoggingGroups loggingGroups() { Map<String, List<String>> groups = new LinkedHashMap<>();
return mock(LoggingGroups.class); groups.put("test", Arrays.asList("test.member1", "test.member2"));
groups.put("group.png", Arrays.asList("png.member1", "png.member2"));
return new LoggerGroups(groups);
} }
@Bean @Bean
LoggersEndpoint endpoint(LoggingSystem loggingSystem, LoggersEndpoint endpoint(LoggingSystem loggingSystem,
ObjectProvider<LoggingGroups> loggingGroupsObjectProvider) { ObjectProvider<LoggerGroups> loggingGroupsObjectProvider) {
return new LoggersEndpoint(loggingSystem, loggingGroupsObjectProvider.getIfAvailable()); return new LoggersEndpoint(loggingSystem, loggingGroupsObjectProvider.getIfAvailable());
} }

View File

@ -16,12 +16,11 @@
package org.springframework.boot.context.logging; package org.springframework.boot.context.logging;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@ -37,7 +36,8 @@ import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName; import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.logging.LogFile; import org.springframework.boot.logging.LogFile;
import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.logging.LoggingGroups; import org.springframework.boot.logging.LoggerGroup;
import org.springframework.boot.logging.LoggerGroups;
import org.springframework.boot.logging.LoggingInitializationContext; import org.springframework.boot.logging.LoggingInitializationContext;
import org.springframework.boot.logging.LoggingSystem; import org.springframework.boot.logging.LoggingSystem;
import org.springframework.boot.logging.LoggingSystemProperties; import org.springframework.boot.logging.LoggingSystemProperties;
@ -52,7 +52,6 @@ import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ResourceUtils; import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -85,6 +84,7 @@ import org.springframework.util.StringUtils;
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Madhura Bhave * @author Madhura Bhave
* @author HaiTao Zhang
* @since 2.0.0 * @since 2.0.0
* @see LoggingSystem#get(ClassLoader) * @see LoggingSystem#get(ClassLoader)
*/ */
@ -97,8 +97,8 @@ public class LoggingApplicationListener implements GenericApplicationListener {
private static final Bindable<Map<String, LogLevel>> STRING_LOGLEVEL_MAP = Bindable.mapOf(String.class, private static final Bindable<Map<String, LogLevel>> STRING_LOGLEVEL_MAP = Bindable.mapOf(String.class,
LogLevel.class); LogLevel.class);
private static final Bindable<Map<String, String[]>> STRING_STRINGS_MAP = Bindable.mapOf(String.class, private static final Bindable<Map<String, List<String>>> STRING_STRINGS_MAP = Bindable
String[].class); .of(ResolvableType.forClassWithGenerics(MultiValueMap.class, String.class, String.class).asMap());
/** /**
* The default order for the LoggingApplicationListener. * The default order for the LoggingApplicationListener.
@ -129,9 +129,10 @@ public class LoggingApplicationListener implements GenericApplicationListener {
public static final String LOGFILE_BEAN_NAME = "springBootLogFile"; public static final String LOGFILE_BEAN_NAME = "springBootLogFile";
/** /**
* The name of the{@link LoggingGroups} bean. * The name of the{@link LoggerGroups} bean.
* @since 2.2.0
*/ */
public static final String LOGGING_GROUPS_BEAN_NAME = "springBootLoggingGroups"; public static final String LOGGER_GROUPS_BEAN_NAME = "springBootLoggerGroups";
private static final Map<String, List<String>> DEFAULT_GROUP_LOGGERS; private static final Map<String, List<String>> DEFAULT_GROUP_LOGGERS;
static { static {
@ -147,7 +148,7 @@ public class LoggingApplicationListener implements GenericApplicationListener {
DEFAULT_GROUP_LOGGERS = Collections.unmodifiableMap(loggers); DEFAULT_GROUP_LOGGERS = Collections.unmodifiableMap(loggers);
} }
private static final Map<LogLevel, List<String>> LOG_LEVEL_LOGGERS; private static final Map<LogLevel, List<String>> SPRING_BOOT_LOGGING_LOGGERS;
static { static {
MultiValueMap<LogLevel, String> loggers = new LinkedMultiValueMap<>(); MultiValueMap<LogLevel, String> loggers = new LinkedMultiValueMap<>();
loggers.add(LogLevel.DEBUG, "sql"); loggers.add(LogLevel.DEBUG, "sql");
@ -158,7 +159,7 @@ public class LoggingApplicationListener implements GenericApplicationListener {
loggers.add(LogLevel.TRACE, "org.apache.catalina"); loggers.add(LogLevel.TRACE, "org.apache.catalina");
loggers.add(LogLevel.TRACE, "org.eclipse.jetty"); loggers.add(LogLevel.TRACE, "org.eclipse.jetty");
loggers.add(LogLevel.TRACE, "org.hibernate.tool.hbm2ddl"); loggers.add(LogLevel.TRACE, "org.hibernate.tool.hbm2ddl");
LOG_LEVEL_LOGGERS = Collections.unmodifiableMap(loggers); SPRING_BOOT_LOGGING_LOGGERS = Collections.unmodifiableMap(loggers);
} }
private static final Class<?>[] EVENT_TYPES = { ApplicationStartingEvent.class, private static final Class<?>[] EVENT_TYPES = { ApplicationStartingEvent.class,
@ -173,10 +174,10 @@ public class LoggingApplicationListener implements GenericApplicationListener {
private LoggingSystem loggingSystem; private LoggingSystem loggingSystem;
private LoggingGroups loggingGroups;
private LogFile logFile; private LogFile logFile;
private LoggerGroups loggerGroups;
private int order = DEFAULT_ORDER; private int order = DEFAULT_ORDER;
private boolean parseArgs = true; private boolean parseArgs = true;
@ -244,8 +245,8 @@ public class LoggingApplicationListener implements GenericApplicationListener {
if (this.logFile != null && !beanFactory.containsBean(LOGFILE_BEAN_NAME)) { if (this.logFile != null && !beanFactory.containsBean(LOGFILE_BEAN_NAME)) {
beanFactory.registerSingleton(LOGFILE_BEAN_NAME, this.logFile); beanFactory.registerSingleton(LOGFILE_BEAN_NAME, this.logFile);
} }
if (this.loggingGroups != null && !beanFactory.containsBean(LOGGING_GROUPS_BEAN_NAME)) { if (this.loggerGroups != null && !beanFactory.containsBean(LOGGER_GROUPS_BEAN_NAME)) {
beanFactory.registerSingleton(LOGGING_GROUPS_BEAN_NAME, this.loggingGroups); beanFactory.registerSingleton(LOGGER_GROUPS_BEAN_NAME, this.loggerGroups);
} }
} }
@ -269,11 +270,11 @@ public class LoggingApplicationListener implements GenericApplicationListener {
*/ */
protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) { protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
new LoggingSystemProperties(environment).apply(); new LoggingSystemProperties(environment).apply();
this.loggingGroups = new LoggingGroups(this.loggingSystem);
this.logFile = LogFile.get(environment); this.logFile = LogFile.get(environment);
if (this.logFile != null) { if (this.logFile != null) {
this.logFile.applyToSystemProperties(); this.logFile.applyToSystemProperties();
} }
this.loggerGroups = new LoggerGroups(DEFAULT_GROUP_LOGGERS);
initializeEarlyLoggingLevel(environment); initializeEarlyLoggingLevel(environment);
initializeSystem(environment, this.loggingSystem, this.logFile); initializeSystem(environment, this.loggingSystem, this.logFile);
initializeFinalLoggingLevels(environment, this.loggingSystem); initializeFinalLoggingLevels(environment, this.loggingSystem);
@ -321,70 +322,95 @@ public class LoggingApplicationListener implements GenericApplicationListener {
} }
private void initializeFinalLoggingLevels(ConfigurableEnvironment environment, LoggingSystem system) { private void initializeFinalLoggingLevels(ConfigurableEnvironment environment, LoggingSystem system) {
bindLoggerGroups(environment);
if (this.springBootLogging != null) { if (this.springBootLogging != null) {
initializeLogLevel(system, this.springBootLogging); initializeLogLevel(system, this.springBootLogging);
} }
setLogLevels(system, environment); setLogLevels(system, environment);
} }
protected void initializeLogLevel(LoggingSystem system, LogLevel level) { private void bindLoggerGroups(ConfigurableEnvironment environment) {
LOG_LEVEL_LOGGERS.getOrDefault(level, Collections.emptyList()) if (this.loggerGroups != null) {
.forEach((logger) -> initializeLogLevel(system, level, logger)); Binder binder = Binder.get(environment);
} binder.bind(LOGGING_GROUP, STRING_STRINGS_MAP).ifBound(this.loggerGroups::putAll);
private void initializeLogLevel(LoggingSystem system, LogLevel level, String logger) {
List<String> groupLoggers = DEFAULT_GROUP_LOGGERS.get(logger);
if (groupLoggers == null) {
system.setLogLevel(logger, level);
return;
} }
this.loggingGroups.setLoggerGroup(logger, groupLoggers);
this.loggingGroups.setLoggerGroupLevel(logger, level);
} }
/**
* Initialize loggers based on the {@link #setSpringBootLogging(LogLevel)
* springBootLogging} setting.
* @param system the logging system
* @param springBootLogging the spring boot logging level requested
* @deprecated since 2.2.0 in favor of
* {@link #initializeSpringBootLogging(LoggingSystem, LogLevel)}
*/
@Deprecated
protected void initializeLogLevel(LoggingSystem system, LogLevel springBootLogging) {
initializeSpringBootLogging(system, springBootLogging);
}
/**
* Initialize loggers based on the {@link #setSpringBootLogging(LogLevel)
* springBootLogging} setting. By default this implementation will pick an appropriate
* set of loggers to configure based on the level.
* @param system the logging system
* @param springBootLogging the spring boot logging level requested
* @since 2.2.0
*/
protected void initializeSpringBootLogging(LoggingSystem system, LogLevel springBootLogging) {
BiConsumer<String, LogLevel> configurer = getLogLevelConfigurer(system);
SPRING_BOOT_LOGGING_LOGGERS.getOrDefault(springBootLogging, Collections.emptyList())
.forEach((name) -> configureLogLevel(name, springBootLogging, configurer));
}
/**
* Set logging levels based on relevant {@link Environment} properties.
* @param system the logging system
* @param environment the environment
* @deprecated since 2.2.0 in favor of
* {@link #setLogLevels(LoggingSystem, ConfigurableEnvironment)}
*/
@Deprecated
protected void setLogLevels(LoggingSystem system, Environment environment) { protected void setLogLevels(LoggingSystem system, Environment environment) {
if (!(environment instanceof ConfigurableEnvironment)) { if (environment instanceof ConfigurableEnvironment) {
return; setLogLevels(system, (ConfigurableEnvironment) environment);
} }
}
/**
* Set logging levels based on relevant {@link Environment} properties.
* @param system the logging system
* @param environment the environment
* @since 2.2.0
*/
protected void setLogLevels(LoggingSystem system, ConfigurableEnvironment environment) {
BiConsumer<String, LogLevel> customizer = getLogLevelConfigurer(system);
Binder binder = Binder.get(environment); Binder binder = Binder.get(environment);
Map<String, String[]> groups = getGroups();
binder.bind(LOGGING_GROUP, STRING_STRINGS_MAP.withExistingValue(groups));
Map<String, LogLevel> levels = binder.bind(LOGGING_LEVEL, STRING_LOGLEVEL_MAP).orElseGet(Collections::emptyMap); Map<String, LogLevel> levels = binder.bind(LOGGING_LEVEL, STRING_LOGLEVEL_MAP).orElseGet(Collections::emptyMap);
levels.forEach((name, level) -> { levels.forEach((name, level) -> configureLogLevel(name, level, customizer));
String[] groupedNames = groups.get(name); }
if (ObjectUtils.isEmpty(groupedNames)) {
setLogLevel(system, name, level); private void configureLogLevel(String name, LogLevel level, BiConsumer<String, LogLevel> configurer) {
if (this.loggerGroups != null) {
LoggerGroup group = this.loggerGroups.get(name);
if (group != null && group.hasMembers()) {
group.configureLogLevel(level, configurer);
return;
} }
else { }
setLogLevel(groupedNames, level, name); configurer.accept(name, level);
}
private BiConsumer<String, LogLevel> getLogLevelConfigurer(LoggingSystem system) {
return (name, level) -> {
try {
name = name.equalsIgnoreCase(LoggingSystem.ROOT_LOGGER_NAME) ? null : name;
system.setLogLevel(name, level);
} }
}); catch (RuntimeException ex) {
} this.logger.error("Cannot set level '" + level + "' for '" + name + "'");
}
private Map<String, String[]> getGroups() { };
Map<String, String[]> groups = new LinkedHashMap<>();
DEFAULT_GROUP_LOGGERS.forEach((name, loggers) -> groups.put(name, StringUtils.toStringArray(loggers)));
return groups;
}
private void setLogLevel(String[] names, LogLevel level, String groupName) {
try {
this.loggingGroups.setLoggerGroup(groupName, Arrays.asList(names));
this.loggingGroups.setLoggerGroupLevel(groupName, level);
}
catch (RuntimeException ex) {
this.logger.error("Cannot set level '" + level + "' for '" + groupName + "'");
}
}
private void setLogLevel(LoggingSystem system, String name, LogLevel level) {
try {
name = name.equalsIgnoreCase(LoggingSystem.ROOT_LOGGER_NAME) ? null : name;
system.setLogLevel(name, level);
}
catch (RuntimeException ex) {
this.logger.error("Cannot set level '" + level + "' for '" + name + "'");
}
} }
private void registerShutdownHookIfNecessary(Environment environment, LoggingSystem loggingSystem) { private void registerShutdownHookIfNecessary(Environment environment, LoggingSystem loggingSystem) {

View File

@ -0,0 +1,65 @@
/*
* Copyright 2012-2019 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
*
* https://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.logging;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
/**
* A single logger group.
*
* @author Madhura Bhave
* @author Phillip Webb
* @since 2.2.0
*/
public final class LoggerGroup {
private final String name;
private final List<String> members;
private LogLevel configuredLevel;
LoggerGroup(String name, List<String> members) {
this.name = name;
this.members = Collections.unmodifiableList(new ArrayList<>(members));
}
public String getName() {
return this.name;
}
public List<String> getMembers() {
return this.members;
}
public boolean hasMembers() {
return !this.members.isEmpty();
}
public LogLevel getConfiguredLevel() {
return this.configuredLevel;
}
public void configureLogLevel(LogLevel level, BiConsumer<String, LogLevel> configurer) {
this.configuredLevel = level;
this.members.forEach((name) -> configurer.accept(name, level));
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2012-2019 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
*
* https://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.logging;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Logger groups configured via the Spring Environment.
*
* @author HaiTao Zhang
* @author Phillip Webb
* @since 2.2.0 #see {@link LoggerGroup}
*/
public final class LoggerGroups implements Iterable<LoggerGroup> {
private final Map<String, LoggerGroup> groups = new ConcurrentHashMap<>();
public LoggerGroups() {
}
public LoggerGroups(Map<String, List<String>> namesAndMembers) {
putAll(namesAndMembers);
}
public void putAll(Map<String, List<String>> namesAndMembers) {
namesAndMembers.forEach(this::put);
}
private void put(String name, List<String> members) {
put(new LoggerGroup(name, members));
}
private void put(LoggerGroup loggerGroup) {
this.groups.put(loggerGroup.getName(), loggerGroup);
}
public LoggerGroup get(String name) {
return this.groups.get(name);
}
@Override
public Iterator<LoggerGroup> iterator() {
return this.groups.values().iterator();
}
}

View File

@ -1,112 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.logging;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.util.Assert;
/**
* Manage logger groups.
*
* @author HaiTao Zhang
* @since 2.2.0
*/
public class LoggingGroups {
private Map<String, LogLevel> loggerGroupConfigurations;
private Map<String, List<String>> loggerGroups;
private LoggingSystem loggingSystem;
public LoggingGroups(LoggingSystem loggingSystem) {
this.loggerGroupConfigurations = new ConcurrentHashMap<>();
this.loggerGroups = new ConcurrentHashMap<>();
this.loggingSystem = loggingSystem;
}
/**
* Associate a name to a list of logger's name to create a logger group.
* @param groupName name of the logger group
* @param members list of the members names
*/
public void setLoggerGroup(String groupName, List<String> members) {
Assert.notNull(groupName, "Group name can not be null");
Assert.notNull(members, "Members can not be null");
this.loggerGroups.put(groupName, members);
}
/**
* Set the logging level for a given logger group.
* @param groupName the name of the group to set
* @param level the log level ({@code null}) can be used to remove any custom level
* for the logger group and use the default configuration instead.
*/
public void setLoggerGroupLevel(String groupName, LogLevel level) {
Assert.notNull(groupName, "Group name can not be null");
List<String> members = this.loggerGroups.get(groupName);
members.forEach((member) -> this.loggingSystem
.setLogLevel(member.equalsIgnoreCase(LoggingSystem.ROOT_LOGGER_NAME) ? null : member, level));
this.loggerGroupConfigurations.put(groupName, level);
}
/**
* Checks whether a groupName is associated to a logger group.
* @param groupName name of the logger group
* @return a boolean stating true when groupName is associated with a group of loggers
*/
public boolean isGroup(String groupName) {
Assert.notNull(groupName, "Group name can not be null");
return this.loggerGroups.containsKey(groupName);
}
/**
* Get the all registered logger groups.
* @return a Set of the names of the logger groups
*/
public Set<String> getLoggerGroupNames() {
synchronized (this) {
return this.loggerGroups.isEmpty() ? null : Collections.unmodifiableSet(this.loggerGroups.keySet());
}
}
/**
* Get a logger group's members.
* @param groupName name of the logger group
* @return list of the members names associated with this group
*/
public List<String> getLoggerGroup(String groupName) {
Assert.notNull(groupName, "Group name can not be null");
return Collections.unmodifiableList(this.loggerGroups.get(groupName));
}
/**
* Get a logger group's configured level.
* @param groupName name of the logger group
* @return the logger groups configured level
*/
public LogLevel getLoggerGroupConfiguredLevel(String groupName) {
Assert.notNull(groupName, "Group name can not be null");
return this.loggerGroupConfigurations.get(groupName);
}
}

View File

@ -50,6 +50,7 @@ import org.springframework.boot.logging.AbstractLoggingSystem;
import org.springframework.boot.logging.LogFile; import org.springframework.boot.logging.LogFile;
import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.logging.LoggerConfiguration; import org.springframework.boot.logging.LoggerConfiguration;
import org.springframework.boot.logging.LoggerGroups;
import org.springframework.boot.logging.LoggingInitializationContext; import org.springframework.boot.logging.LoggingInitializationContext;
import org.springframework.boot.logging.LoggingSystem; import org.springframework.boot.logging.LoggingSystem;
import org.springframework.boot.logging.LoggingSystemProperties; import org.springframework.boot.logging.LoggingSystemProperties;
@ -284,6 +285,8 @@ class LoggingApplicationListenerTests {
this.loggerContext.getLogger("org.hibernate.SQL").debug("testdebugsqlgroup"); this.loggerContext.getLogger("org.hibernate.SQL").debug("testdebugsqlgroup");
assertThat(this.output).contains("testdebugwebgroup"); assertThat(this.output).contains("testdebugwebgroup");
assertThat(this.output).contains("testdebugsqlgroup"); assertThat(this.output).contains("testdebugsqlgroup");
LoggerGroups loggerGroups = (LoggerGroups) ReflectionTestUtils.getField(this.initializer, "loggerGroups");
assertThat(loggerGroups.get("web").getConfiguredLevel()).isEqualTo(LogLevel.DEBUG);
} }
@Test @Test

View File

@ -0,0 +1,65 @@
/*
* Copyright 2012-2019 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
*
* https://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.logging;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link LoggerGroups}
*
* @author HaiTao Zhang
* @author Madhura Bhave
*/
class LoggerGroupsTests {
private LoggingSystem loggingSystem = mock(LoggingSystem.class);
@Test
void putAllShouldAddLoggerGroups() {
Map<String, List<String>> groups = Collections.singletonMap("test",
Arrays.asList("test.member", "test.member2"));
LoggerGroups loggerGroups = new LoggerGroups();
loggerGroups.putAll(groups);
LoggerGroup group = loggerGroups.get("test");
assertThat(group.getMembers()).containsExactly("test.member", "test.member2");
}
@Test
void iteratorShouldReturnLoggerGroups() {
LoggerGroups groups = createLoggerGroups();
assertThat(groups).hasSize(3);
assertThat(groups).extracting("name").containsExactlyInAnyOrder("test0", "test1", "test2");
}
private LoggerGroups createLoggerGroups() {
Map<String, List<String>> groups = new LinkedHashMap<>();
groups.put("test0", Arrays.asList("test0.member", "test0.member2"));
groups.put("test1", Arrays.asList("test1.member", "test1.member2"));
groups.put("test2", Arrays.asList("test2.member", "test2.member2"));
return new LoggerGroups(groups);
}
}

View File

@ -1,44 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.logging;
import java.util.Arrays;
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link LoggingGroups}
*
* @author HaiTao Zhang
*/
public class LoggingGroupsTests {
private LoggingSystem loggingSystem = mock(LoggingSystem.class);
@Test
void setLoggerGroupWithTheConfiguredLevelToAllMembers() {
LoggingGroups loggingGroups = new LoggingGroups(this.loggingSystem);
loggingGroups.setLoggerGroup("test", Arrays.asList("test.member", "test.member2"));
loggingGroups.setLoggerGroupLevel("test", LogLevel.DEBUG);
verify(this.loggingSystem).setLogLevel("test.member2", LogLevel.DEBUG);
verify(this.loggingSystem).setLogLevel("test.member", LogLevel.DEBUG);
}
}