Commit 61b86ff2 authored by Madhura Bhave's avatar Madhura Bhave

Polish "Add support for configuring logging groups"

See gh-17515
Co-authored-by: 's avatarPhillip Webb <pwebb@pivotal.io>
parent b9047c22
......@@ -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]]
== 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]]
== Clearing a Log Level
......
......@@ -25,7 +25,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
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.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext;
......@@ -48,8 +48,8 @@ public class LoggersEndpointAutoConfiguration {
@Conditional(OnEnabledLoggingSystemCondition.class)
@ConditionalOnMissingBean
public LoggersEndpoint loggersEndpoint(LoggingSystem loggingSystem,
ObjectProvider<LoggingGroups> loggingGroupsObjectProvider) {
return new LoggersEndpoint(loggingSystem, loggingGroupsObjectProvider.getIfAvailable());
ObjectProvider<LoggerGroups> springBootLoggerGroups) {
return new LoggersEndpoint(loggingSystem, springBootLoggerGroups.getIfAvailable(LoggerGroups::new));
}
static class OnEnabledLoggingSystemCondition extends SpringBootCondition {
......
......@@ -20,14 +20,15 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
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.logging.LogLevel;
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.test.mock.mockito.MockBean;
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("effectiveLevel").description("Effective level of the logger."));
private static final List<FieldDescriptor> groupLevelFields = Arrays.asList(
fieldWithPath("configuredLevel").description("Configured level of the logger group"),
fieldWithPath("members").description("Loggers that are part of this group").optional());
private static final List<FieldDescriptor> groupLevelFields;
@MockBean
private LoggingSystem loggingSystem;
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
private ObjectProvider<LoggingGroups> loggingGroupsObjectProvider;
private LoggingSystem loggingSystem;
@MockBean
LoggingGroups loggingGroups;
@Autowired
private LoggerGroups loggerGroups;
@Test
void allLoggers() throws Exception {
......@@ -76,10 +79,6 @@ class LoggersEndpointDocumentationTests extends MockMvcEndpointDocumentationTest
given(this.loggingSystem.getLoggerConfigurations())
.willReturn(Arrays.asList(new LoggerConfiguration("ROOT", LogLevel.INFO, LogLevel.INFO),
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())
.andDo(MockMvcRestDocumentation.document("loggers/all",
responseFields(fieldWithPath("levels").description("Levels support by the logging system."),
......@@ -91,7 +90,6 @@ class LoggersEndpointDocumentationTests extends MockMvcEndpointDocumentationTest
@Test
void logger() throws Exception {
given(this.loggingGroupsObjectProvider.getIfAvailable()).willReturn(this.loggingGroups);
given(this.loggingSystem.getLoggerConfiguration("com.example"))
.willReturn(new LoggerConfiguration("com.example", LogLevel.INFO, LogLevel.INFO));
this.mockMvc.perform(get("/actuator/loggers/com.example")).andExpect(status().isOk())
......@@ -100,17 +98,12 @@ class LoggersEndpointDocumentationTests extends MockMvcEndpointDocumentationTest
@Test
void loggerGroups() throws Exception {
given(this.loggingGroupsObjectProvider.getIfAvailable()).willReturn(this.loggingGroups);
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())
this.mockMvc.perform(get("/actuator/loggers/test")).andExpect(status().isOk())
.andDo(MockMvcRestDocumentation.document("loggers/group", responseFields(groupLevelFields)));
}
@Test
void setLogLevel() throws Exception {
given(this.loggingGroupsObjectProvider.getIfAvailable()).willReturn(this.loggingGroups);
this.mockMvc
.perform(post("/actuator/loggers/com.example").content("{\"configuredLevel\":\"debug\"}")
.contentType(MediaType.APPLICATION_JSON))
......@@ -122,22 +115,26 @@ class LoggersEndpointDocumentationTests extends MockMvcEndpointDocumentationTest
@Test
void setLogLevelOfLoggerGroup() throws Exception {
given(this.loggingGroupsObjectProvider.getIfAvailable()).willReturn(this.loggingGroups);
given(this.loggingGroups.isGroup("com.example")).willReturn(true);
this.mockMvc
.perform(post("/actuator/loggers/com.example")
.perform(post("/actuator/loggers/test")
.content("{\"configuredLevel\":\"debug\"}").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isNoContent()).andDo(
MockMvcRestDocumentation.document("loggers/setGroup",
requestFields(fieldWithPath("configuredLevel").description(
"Level for the logger group. May be omitted to clear the level of the loggers.")
.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
void clearLogLevel() throws Exception {
given(this.loggingGroupsObjectProvider.getIfAvailable()).willReturn(this.loggingGroups);
this.mockMvc
.perform(post("/actuator/loggers/com.example").content("{}").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isNoContent()).andDo(MockMvcRestDocumentation.document("loggers/clear"));
......@@ -149,9 +146,13 @@ class LoggersEndpointDocumentationTests extends MockMvcEndpointDocumentationTest
static class TestConfiguration {
@Bean
LoggersEndpoint endpoint(LoggingSystem loggingSystem,
ObjectProvider<LoggingGroups> loggingGroupsObjectProvider) {
return new LoggersEndpoint(loggingSystem, loggingGroupsObjectProvider.getIfAvailable());
LoggersEndpoint endpoint(LoggingSystem loggingSystem, LoggerGroups groups) {
groups.putAll(getLoggerGroups());
return new LoggersEndpoint(loggingSystem, groups);
}
private Map<String, List<String>> getLoggerGroups() {
return Collections.singletonMap("test", Arrays.asList("test.member1", "test.member2"));
}
}
......
......@@ -31,7 +31,8 @@ import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
import org.springframework.boot.logging.LogLevel;
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.lang.Nullable;
import org.springframework.util.Assert;
......@@ -49,17 +50,18 @@ public class LoggersEndpoint {
private final LoggingSystem loggingSystem;
private final LoggingGroups loggingGroups;
private final LoggerGroups loggerGroups;
/**
* Create a new {@link LoggersEndpoint} instance.
* @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(loggerGroups, "LoggerGroups must not be null");
this.loggingSystem = loggingSystem;
this.loggingGroups = loggingGroups;
this.loggerGroups = loggerGroups;
}
@ReadOperation
......@@ -71,20 +73,23 @@ public class LoggersEndpoint {
Map<String, Object> result = new LinkedHashMap<>();
result.put("levels", getLevels());
result.put("loggers", getLoggers(configurations));
if (this.loggingGroups != null && this.loggingGroups.getLoggerGroupNames() != null) {
Set<String> groups = this.loggingGroups.getLoggerGroupNames();
result.put("groups", getLoggerGroups(groups));
}
result.put("groups", getGroups());
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
public LoggerLevels loggerLevels(@Selector String name) {
Assert.notNull(name, "Name must not be null");
if (this.loggingGroups != null && this.loggingGroups.isGroup(name)) {
List<String> members = this.loggingGroups.getLoggerGroup(name);
LogLevel groupConfiguredLevel = this.loggingGroups.getLoggerGroupConfiguredLevel(name);
return new GroupLoggerLevels(groupConfiguredLevel, members);
LoggerGroup group = this.loggerGroups.get(name);
if (group != null) {
return new GroupLoggerLevels(group.getConfiguredLevel(), group.getMembers());
}
LoggerConfiguration configuration = this.loggingSystem.getLoggerConfiguration(name);
return (configuration != null) ? new SingleLoggerLevels(configuration) : null;
......@@ -93,8 +98,9 @@ public class LoggersEndpoint {
@WriteOperation
public void configureLogLevel(@Selector String name, @Nullable LogLevel configuredLevel) {
Assert.notNull(name, "Name must not be empty");
if (this.loggingGroups != null && this.loggingGroups.isGroup(name)) {
this.loggingGroups.setLoggerGroupLevel(name, configuredLevel);
LoggerGroup group = this.loggerGroups.get(name);
if (group != null && group.hasMembers()) {
group.configureLogLevel(configuredLevel, this.loggingSystem::setLogLevel);
return;
}
this.loggingSystem.setLogLevel(name, configuredLevel);
......@@ -113,16 +119,6 @@ public class LoggersEndpoint {
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.
*/
......@@ -134,13 +130,12 @@ public class LoggersEndpoint {
this.configuredLevel = getName(configuredLevel);
}
private String getName(LogLevel level) {
protected final String getName(LogLevel level) {
return (level != null) ? level.name() : null;
}
public String getConfiguredLevel() {
return this.configuredLevel;
}
}
......@@ -166,7 +161,7 @@ public class LoggersEndpoint {
public SingleLoggerLevels(LoggerConfiguration configuration) {
super(configuration.getConfiguredLevel());
this.effectiveLevel = super.getName(configuration.getEffectiveLevel());
this.effectiveLevel = getName(configuration.getEffectiveLevel());
}
public String getEffectiveLevel() {
......
......@@ -18,9 +18,11 @@ package org.springframework.boot.actuate.logging;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
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.logging.LogLevel;
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 static org.assertj.core.api.Assertions.assertThat;
......@@ -42,12 +44,21 @@ import static org.mockito.Mockito.verify;
* @author Ben Hale
* @author Andy Wilkinson
* @author HaiTao Zhang
* @author Madhura Bhave
*/
class LoggersEndpointTests {
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
@SuppressWarnings("unchecked")
......@@ -55,8 +66,7 @@ class LoggersEndpointTests {
given(this.loggingSystem.getLoggerConfigurations())
.willReturn(Collections.singletonList(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG)));
given(this.loggingSystem.getSupportedLogLevels()).willReturn(EnumSet.allOf(LogLevel.class));
given(this.loggingGroups.getLoggerGroupNames()).willReturn(null);
Map<String, Object> result = new LoggersEndpoint(this.loggingSystem, this.loggingGroups).loggers();
Map<String, Object> result = new LoggersEndpoint(this.loggingSystem, new LoggerGroups()).loggers();
Map<String, LoggerLevels> loggers = (Map<String, LoggerLevels>) result.get("loggers");
Set<LogLevel> levels = (Set<LogLevel>) result.get("levels");
SingleLoggerLevels rootLevels = (SingleLoggerLevels) loggers.get("ROOT");
......@@ -64,7 +74,8 @@ class LoggersEndpointTests {
assertThat(rootLevels.getEffectiveLevel()).isEqualTo("DEBUG");
assertThat(levels).containsExactly(LogLevel.OFF, LogLevel.FATAL, LogLevel.ERROR, LogLevel.WARN, LogLevel.INFO,
LogLevel.DEBUG, LogLevel.TRACE);
assertThat(result.get("groups")).isNull();
Map<String, LoggerGroups> groups = (Map<String, LoggerGroups>) result.get("groups");
assertThat(groups).isEmpty();
}
@Test
......@@ -73,12 +84,9 @@ class LoggersEndpointTests {
given(this.loggingSystem.getLoggerConfigurations())
.willReturn(Collections.singletonList(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG)));
given(this.loggingSystem.getSupportedLogLevels()).willReturn(EnumSet.allOf(LogLevel.class));
given(this.loggingGroups.getLoggerGroup("test")).willReturn(Collections.singletonList("test.member"));
given(this.loggingGroups.getLoggerGroupNames()).willReturn(Collections.singleton("test"));
given(this.loggingGroups.getLoggerGroupConfiguredLevel("test")).willReturn(LogLevel.DEBUG);
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, Object> result = new LoggersEndpoint(this.loggingSystem, this.loggerGroups).loggers();
Map<String, GroupLoggerLevels> loggerGroups = (Map<String, GroupLoggerLevels>) result.get("groups");
GroupLoggerLevels groupLevel = loggerGroups.get("test");
Map<String, LoggerLevels> loggers = (Map<String, LoggerLevels>) result.get("loggers");
Set<LogLevel> levels = (Set<LogLevel>) result.get("levels");
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,
LogLevel.DEBUG, LogLevel.TRACE);
assertThat(loggerGroups).isNotNull();
assertThat(testLoggerLevel).isNotNull();
assertThat(testLoggerLevel.getConfiguredLevel()).isEqualTo("DEBUG");
assertThat(testLoggerLevel.getMembers()).isEqualTo(Collections.singletonList("test.member"));
assertThat(groupLevel.getConfiguredLevel()).isEqualTo("DEBUG");
assertThat(groupLevel.getMembers()).containsExactly("test.member");
}
@Test
void loggerLevelsWhenNameSpecifiedShouldReturnLevels() {
given(this.loggingGroups.isGroup("ROOT")).willReturn(false);
given(this.loggingSystem.getLoggerConfiguration("ROOT"))
.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");
assertThat(levels.getConfiguredLevel()).isNull();
assertThat(levels.getEffectiveLevel()).isEqualTo("DEBUG");
......@@ -105,10 +111,7 @@ class LoggersEndpointTests {
@Test
void groupNameSpecifiedShouldReturnConfiguredLevelAndMembers() {
given(this.loggingGroups.isGroup("test")).willReturn(true);
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)
GroupLoggerLevels levels = (GroupLoggerLevels) new LoggersEndpoint(this.loggingSystem, this.loggerGroups)
.loggerLevels("test");
assertThat(levels.getConfiguredLevel()).isEqualTo("DEBUG");
assertThat(levels.getMembers()).isEqualTo(Collections.singletonList("test.member"));
......@@ -116,35 +119,26 @@ class LoggersEndpointTests {
@Test
void configureLogLevelShouldSetLevelOnLoggingSystem() {
given(this.loggingGroups.getLoggerGroup("ROOT")).willReturn(null);
new LoggersEndpoint(this.loggingSystem, this.loggingGroups).configureLogLevel("ROOT", LogLevel.DEBUG);
new LoggersEndpoint(this.loggingSystem, this.loggerGroups).configureLogLevel("ROOT", LogLevel.DEBUG);
verify(this.loggingSystem).setLogLevel("ROOT", LogLevel.DEBUG);
}
@Test
void configureLogLevelWithNullSetsLevelOnLoggingSystemToNull() {
given(this.loggingGroups.getLoggerGroup("ROOT")).willReturn(null);
new LoggersEndpoint(this.loggingSystem, this.loggingGroups).configureLogLevel("ROOT", null);
new LoggersEndpoint(this.loggingSystem, this.loggerGroups).configureLogLevel("ROOT", null);
verify(this.loggingSystem).setLogLevel("ROOT", null);
}
@Test
void configureLogLevelInLoggerGroupShouldSetLevelOnLoggingSystem() {
given(this.loggingGroups.isGroup("test")).willReturn(true);
given(this.loggingGroups.getLoggerGroup("test")).willReturn(Collections.singletonList("test.member"));
new LoggersEndpoint(this.loggingSystem, this.loggingGroups).configureLogLevel("test", LogLevel.DEBUG);
verify(this.loggingGroups).setLoggerGroupLevel("test", LogLevel.DEBUG);
new LoggersEndpoint(this.loggingSystem, this.loggerGroups).configureLogLevel("test", LogLevel.DEBUG);
verify(this.loggingSystem).setLogLevel("test.member", LogLevel.DEBUG);
}
@Test
void configureLogLevelWithNullInLoggerGroupShouldSetLevelOnLoggingSystem() {
given(this.loggingGroups.isGroup("test")).willReturn(true);
given(this.loggingGroups.getLoggerGroup("test")).willReturn(Collections.singletonList("test.member"));
new LoggersEndpoint(this.loggingSystem, this.loggingGroups).configureLogLevel("test", null);
verify(this.loggingGroups).setLoggerGroupLevel("test", null);
new LoggersEndpoint(this.loggingSystem, this.loggerGroups).configureLogLevel("test", null);
verify(this.loggingSystem).setLogLevel("test.member", null);
}
// @Test
// void
}
......@@ -16,29 +16,50 @@
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;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
/**
* Tests for {@link LoggingGroups}
* A single logger group.
*
* @author HaiTao Zhang
* @author Madhura Bhave
* @author Phillip Webb
* @since 2.2.0
*/
public class LoggingGroupsTests {
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));
}
private LoggingSystem loggingSystem = mock(LoggingSystem.class);
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;
}
@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);
public void configureLogLevel(LogLevel level, BiConsumer<String, LogLevel> configurer) {
this.configuredLevel = level;
this.members.forEach((name) -> configurer.accept(name, level));
}
}
/*
* 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();
}
}
/*
* 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);
}
}
......@@ -50,6 +50,7 @@ import org.springframework.boot.logging.AbstractLoggingSystem;
import org.springframework.boot.logging.LogFile;
import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.logging.LoggerConfiguration;
import org.springframework.boot.logging.LoggerGroups;
import org.springframework.boot.logging.LoggingInitializationContext;
import org.springframework.boot.logging.LoggingSystem;
import org.springframework.boot.logging.LoggingSystemProperties;
......@@ -284,6 +285,8 @@ class LoggingApplicationListenerTests {
this.loggerContext.getLogger("org.hibernate.SQL").debug("testdebugsqlgroup");
assertThat(this.output).contains("testdebugwebgroup");
assertThat(this.output).contains("testdebugsqlgroup");
LoggerGroups loggerGroups = (LoggerGroups) ReflectionTestUtils.getField(this.initializer, "loggerGroups");
assertThat(loggerGroups.get("web").getConfiguredLevel()).isEqualTo(LogLevel.DEBUG);
}
@Test
......
/*
* 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);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment