Commit 5fc58b45 authored by Phillip Webb's avatar Phillip Webb

Merge pull request #7086 from nebhale/logger-actuator

* pr/7086:
  Polish `/loggers` actuator endpoint
  Add `/loggers` actuator endpoint
parents ae4dd0d1 a4481836
...@@ -37,6 +37,7 @@ import org.springframework.boot.actuate.endpoint.FlywayEndpoint; ...@@ -37,6 +37,7 @@ import org.springframework.boot.actuate.endpoint.FlywayEndpoint;
import org.springframework.boot.actuate.endpoint.HealthEndpoint; import org.springframework.boot.actuate.endpoint.HealthEndpoint;
import org.springframework.boot.actuate.endpoint.InfoEndpoint; import org.springframework.boot.actuate.endpoint.InfoEndpoint;
import org.springframework.boot.actuate.endpoint.LiquibaseEndpoint; import org.springframework.boot.actuate.endpoint.LiquibaseEndpoint;
import org.springframework.boot.actuate.endpoint.LoggersEndpoint;
import org.springframework.boot.actuate.endpoint.MetricsEndpoint; import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.boot.actuate.endpoint.PublicMetrics; import org.springframework.boot.actuate.endpoint.PublicMetrics;
import org.springframework.boot.actuate.endpoint.RequestMappingEndpoint; import org.springframework.boot.actuate.endpoint.RequestMappingEndpoint;
...@@ -59,6 +60,7 @@ import org.springframework.boot.autoconfigure.condition.SearchStrategy; ...@@ -59,6 +60,7 @@ import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.logging.LoggingSystem;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.AnnotationAwareOrderComparator;
...@@ -75,7 +77,7 @@ import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping; ...@@ -75,7 +77,7 @@ import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Meang Akira Tanaka * @author Meang Akira Tanaka
* * @author Ben Hale
*/ */
@Configuration @Configuration
@AutoConfigureAfter({ FlywayAutoConfiguration.class, LiquibaseAutoConfiguration.class }) @AutoConfigureAfter({ FlywayAutoConfiguration.class, LiquibaseAutoConfiguration.class })
...@@ -135,6 +137,13 @@ public class EndpointAutoConfiguration { ...@@ -135,6 +137,13 @@ public class EndpointAutoConfiguration {
? Collections.<InfoContributor>emptyList() : this.infoContributors); ? Collections.<InfoContributor>emptyList() : this.infoContributors);
} }
@Bean
@ConditionalOnBean(LoggingSystem.class)
@ConditionalOnMissingBean
public LoggersEndpoint loggersEndpoint(LoggingSystem loggingSystem) {
return new LoggersEndpoint(loggingSystem);
}
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public MetricsEndpoint metricsEndpoint() { public MetricsEndpoint metricsEndpoint() {
......
...@@ -25,6 +25,7 @@ import org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint; ...@@ -25,6 +25,7 @@ import org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint; import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint;
import org.springframework.boot.actuate.endpoint.HealthEndpoint; import org.springframework.boot.actuate.endpoint.HealthEndpoint;
import org.springframework.boot.actuate.endpoint.LoggersEndpoint;
import org.springframework.boot.actuate.endpoint.MetricsEndpoint; import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint; import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping; import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
...@@ -33,6 +34,7 @@ import org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint; ...@@ -33,6 +34,7 @@ import org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HeapdumpMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.HeapdumpMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.LogFileMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.LogFileMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.LoggersMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints; import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
...@@ -57,6 +59,7 @@ import org.springframework.web.cors.CorsConfiguration; ...@@ -57,6 +59,7 @@ import org.springframework.web.cors.CorsConfiguration;
* Configuration to expose {@link Endpoint} instances over Spring MVC. * Configuration to expose {@link Endpoint} instances over Spring MVC.
* *
* @author Dave Syer * @author Dave Syer
* @author Ben Hale
* @since 1.3.0 * @since 1.3.0
*/ */
@ManagementContextConfiguration @ManagementContextConfiguration
...@@ -150,6 +153,13 @@ public class EndpointWebMvcManagementContextConfiguration { ...@@ -150,6 +153,13 @@ public class EndpointWebMvcManagementContextConfiguration {
return healthMvcEndpoint; return healthMvcEndpoint;
} }
@Bean
@ConditionalOnBean(LoggersEndpoint.class)
@ConditionalOnEnabledEndpoint("loggers")
public LoggersMvcEndpoint loggersMvcEndpoint(LoggersEndpoint delegate) {
return new LoggersMvcEndpoint(delegate);
}
@Bean @Bean
@ConditionalOnBean(MetricsEndpoint.class) @ConditionalOnBean(MetricsEndpoint.class)
@ConditionalOnEnabledEndpoint("metrics") @ConditionalOnEnabledEndpoint("metrics")
......
/*
* Copyright 2016-2016 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
*
* http://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.actuate.endpoint;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.logging.LoggerConfiguration;
import org.springframework.boot.logging.LoggingSystem;
import org.springframework.util.Assert;
/**
* {@link Endpoint} to expose a collection of {@link LoggerConfiguration}s.
*
* @author Ben Hale
* @author Phillip Webb
* @since 1.5.0
*/
@ConfigurationProperties(prefix = "endpoints.loggers")
public class LoggersEndpoint
extends AbstractEndpoint<Map<String, LoggersEndpoint.LoggerLevels>> {
private final LoggingSystem loggingSystem;
/**
* Create a new {@link LoggersEndpoint} instance.
* @param loggingSystem the logging system to expose
*/
public LoggersEndpoint(LoggingSystem loggingSystem) {
super("loggers");
Assert.notNull(loggingSystem, "LoggingSystem must not be null");
this.loggingSystem = loggingSystem;
}
@Override
public Map<String, LoggerLevels> invoke() {
Collection<LoggerConfiguration> configurations = this.loggingSystem
.getLoggerConfigurations();
if (configurations == null) {
return Collections.emptyMap();
}
Map<String, LoggerLevels> result = new LinkedHashMap<String, LoggerLevels>(
configurations.size());
for (LoggerConfiguration configuration : configurations) {
result.put(configuration.getName(), new LoggerLevels(configuration));
}
return result;
}
public LoggerLevels invoke(String name) {
Assert.notNull(name, "Name must not be null");
LoggerConfiguration configuration = this.loggingSystem
.getLoggerConfiguration(name);
return (configuration == null ? null : new LoggerLevels(configuration));
}
public void setLogLevel(String name, LogLevel level) {
Assert.notNull(name, "Name must not be empty");
this.loggingSystem.setLogLevel(name, level);
}
/**
* Levels configured for a given logger exposed in a JSON friendly way.
*/
public static class LoggerLevels {
private String configuredLevel;
private String effectiveLevel;
public LoggerLevels(LoggerConfiguration configuration) {
this.configuredLevel = getName(configuration.getConfiguredLevel());
this.effectiveLevel = getName(configuration.getEffectiveLevel());
}
private String getName(LogLevel level) {
return (level == null ? null : level.name());
}
public String getConfiguredLevel() {
return this.configuredLevel;
}
public String getEffectiveLevel() {
return this.effectiveLevel;
}
}
}
/*
* Copyright 2016-2016 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
*
* http://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.actuate.endpoint.mvc;
import java.util.Map;
import org.springframework.boot.actuate.endpoint.LoggersEndpoint;
import org.springframework.boot.actuate.endpoint.LoggersEndpoint.LoggerLevels;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.logging.LogLevel;
import org.springframework.http.HttpEntity;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* Adapter to expose {@link LoggersEndpoint} as an {@link MvcEndpoint}.
*
* @author Ben Hale
* @since 1.5.0
*/
@ConfigurationProperties(prefix = "endpoints.loggers")
public class LoggersMvcEndpoint extends EndpointMvcAdapter {
private final LoggersEndpoint delegate;
public LoggersMvcEndpoint(LoggersEndpoint delegate) {
super(delegate);
this.delegate = delegate;
}
@GetMapping(value = "/{name:.*}", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
@HypermediaDisabled
public Object get(@PathVariable String name) {
if (!this.delegate.isEnabled()) {
// Shouldn't happen - MVC endpoint shouldn't be registered when delegate's
// disabled
return getDisabledResponse();
}
LoggerLevels levels = this.delegate.invoke(name);
return (levels == null ? ResponseEntity.notFound().build() : levels);
}
@PostMapping(value = "/{name:.*}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
@HypermediaDisabled
public Object set(@PathVariable String name,
@RequestBody Map<String, String> configuration) {
if (!this.delegate.isEnabled()) {
// Shouldn't happen - MVC endpoint shouldn't be registered when delegate's
// disabled
return getDisabledResponse();
}
String level = configuration.get("configuredLevel");
this.delegate.setLogLevel(name, level == null ? null : LogLevel.valueOf(level));
return HttpEntity.EMPTY;
}
}
...@@ -36,6 +36,8 @@ import org.springframework.boot.actuate.endpoint.FlywayEndpoint; ...@@ -36,6 +36,8 @@ import org.springframework.boot.actuate.endpoint.FlywayEndpoint;
import org.springframework.boot.actuate.endpoint.HealthEndpoint; import org.springframework.boot.actuate.endpoint.HealthEndpoint;
import org.springframework.boot.actuate.endpoint.InfoEndpoint; import org.springframework.boot.actuate.endpoint.InfoEndpoint;
import org.springframework.boot.actuate.endpoint.LiquibaseEndpoint; import org.springframework.boot.actuate.endpoint.LiquibaseEndpoint;
import org.springframework.boot.actuate.endpoint.LoggersEndpoint;
import org.springframework.boot.actuate.endpoint.LoggersEndpoint.LoggerLevels;
import org.springframework.boot.actuate.endpoint.MetricsEndpoint; import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.boot.actuate.endpoint.PublicMetrics; import org.springframework.boot.actuate.endpoint.PublicMetrics;
import org.springframework.boot.actuate.endpoint.RequestMappingEndpoint; import org.springframework.boot.actuate.endpoint.RequestMappingEndpoint;
...@@ -52,6 +54,7 @@ import org.springframework.boot.autoconfigure.info.ProjectInfoProperties; ...@@ -52,6 +54,7 @@ import org.springframework.boot.autoconfigure.info.ProjectInfoProperties;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
import org.springframework.boot.bind.PropertySourcesBinder; import org.springframework.boot.bind.PropertySourcesBinder;
import org.springframework.boot.logging.LoggingSystem;
import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
...@@ -75,6 +78,7 @@ import static org.mockito.Mockito.mock; ...@@ -75,6 +78,7 @@ import static org.mockito.Mockito.mock;
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Meang Akira Tanaka * @author Meang Akira Tanaka
* @author Ben Hale
*/ */
public class EndpointAutoConfigurationTests { public class EndpointAutoConfigurationTests {
...@@ -89,12 +93,13 @@ public class EndpointAutoConfigurationTests { ...@@ -89,12 +93,13 @@ public class EndpointAutoConfigurationTests {
@Test @Test
public void endpoints() throws Exception { public void endpoints() throws Exception {
load(EndpointAutoConfiguration.class); load(CustomLoggingConfig.class, EndpointAutoConfiguration.class);
assertThat(this.context.getBean(BeansEndpoint.class)).isNotNull(); assertThat(this.context.getBean(BeansEndpoint.class)).isNotNull();
assertThat(this.context.getBean(DumpEndpoint.class)).isNotNull(); assertThat(this.context.getBean(DumpEndpoint.class)).isNotNull();
assertThat(this.context.getBean(EnvironmentEndpoint.class)).isNotNull(); assertThat(this.context.getBean(EnvironmentEndpoint.class)).isNotNull();
assertThat(this.context.getBean(HealthEndpoint.class)).isNotNull(); assertThat(this.context.getBean(HealthEndpoint.class)).isNotNull();
assertThat(this.context.getBean(InfoEndpoint.class)).isNotNull(); assertThat(this.context.getBean(InfoEndpoint.class)).isNotNull();
assertThat(this.context.getBean(LoggersEndpoint.class)).isNotNull();
assertThat(this.context.getBean(MetricsEndpoint.class)).isNotNull(); assertThat(this.context.getBean(MetricsEndpoint.class)).isNotNull();
assertThat(this.context.getBean(ShutdownEndpoint.class)).isNotNull(); assertThat(this.context.getBean(ShutdownEndpoint.class)).isNotNull();
assertThat(this.context.getBean(TraceEndpoint.class)).isNotNull(); assertThat(this.context.getBean(TraceEndpoint.class)).isNotNull();
...@@ -121,6 +126,14 @@ public class EndpointAutoConfigurationTests { ...@@ -121,6 +126,14 @@ public class EndpointAutoConfigurationTests {
assertThat(result).isNotNull(); assertThat(result).isNotNull();
} }
@Test
public void loggersEndpointHasLoggers() throws Exception {
load(CustomLoggingConfig.class, EndpointAutoConfiguration.class);
LoggersEndpoint endpoint = this.context.getBean(LoggersEndpoint.class);
Map<String, LoggerLevels> loggers = endpoint.invoke();
assertThat(loggers.size()).isGreaterThan(0);
}
@Test @Test
public void metricEndpointsHasSystemMetricsByDefault() { public void metricEndpointsHasSystemMetricsByDefault() {
load(PublicMetricsAutoConfiguration.class, EndpointAutoConfiguration.class); load(PublicMetricsAutoConfiguration.class, EndpointAutoConfiguration.class);
...@@ -244,6 +257,16 @@ public class EndpointAutoConfigurationTests { ...@@ -244,6 +257,16 @@ public class EndpointAutoConfigurationTests {
this.context.refresh(); this.context.refresh();
} }
@Configuration
static class CustomLoggingConfig {
@Bean
LoggingSystem loggingSystem() {
return LoggingSystem.get(getClass().getClassLoader());
}
}
@Configuration @Configuration
static class CustomPublicMetricsConfig { static class CustomPublicMetricsConfig {
......
...@@ -49,6 +49,7 @@ import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMappingCusto ...@@ -49,6 +49,7 @@ import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMappingCusto
import org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.LoggersMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.ShutdownMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.ShutdownMvcEndpoint;
...@@ -71,6 +72,7 @@ import org.springframework.boot.context.embedded.ServerPortInfoApplicationContex ...@@ -71,6 +72,7 @@ import org.springframework.boot.context.embedded.ServerPortInfoApplicationContex
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
import org.springframework.boot.context.event.ApplicationFailedEvent; import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.boot.logging.LoggingSystem;
import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.boot.testutil.Matched; import org.springframework.boot.testutil.Matched;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
...@@ -108,6 +110,7 @@ import static org.mockito.Mockito.mock; ...@@ -108,6 +110,7 @@ import static org.mockito.Mockito.mock;
* @author Greg Turnquist * @author Greg Turnquist
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Ben Hale
*/ */
public class EndpointWebMvcAutoConfigurationTests { public class EndpointWebMvcAutoConfigurationTests {
...@@ -429,12 +432,13 @@ public class EndpointWebMvcAutoConfigurationTests { ...@@ -429,12 +432,13 @@ public class EndpointWebMvcAutoConfigurationTests {
@Test @Test
public void endpointsDefaultConfiguration() throws Exception { public void endpointsDefaultConfiguration() throws Exception {
this.applicationContext.register(RootConfig.class, BaseConfiguration.class, this.applicationContext.register(LoggingConfig.class, RootConfig.class,
ServerPortConfig.class, EndpointWebMvcAutoConfiguration.class); BaseConfiguration.class, ServerPortConfig.class,
EndpointWebMvcAutoConfiguration.class);
this.applicationContext.refresh(); this.applicationContext.refresh();
// /health, /metrics, /env, /actuator, /heapdump (/shutdown is disabled by // /health, /metrics, /loggers, /env, /actuator, /heapdump (/shutdown is disabled
// default) // by default)
assertThat(this.applicationContext.getBeansOfType(MvcEndpoint.class)).hasSize(5); assertThat(this.applicationContext.getBeansOfType(MvcEndpoint.class)).hasSize(6);
} }
@Test @Test
...@@ -457,6 +461,16 @@ public class EndpointWebMvcAutoConfigurationTests { ...@@ -457,6 +461,16 @@ public class EndpointWebMvcAutoConfigurationTests {
endpointEnabledOverride("env", EnvironmentMvcEndpoint.class); endpointEnabledOverride("env", EnvironmentMvcEndpoint.class);
} }
@Test
public void loggersEndpointDisabled() throws Exception {
endpointDisabled("loggers", LoggersMvcEndpoint.class);
}
@Test
public void loggersEndpointEnabledOverride() throws Exception {
endpointEnabledOverride("loggers", LoggersMvcEndpoint.class);
}
@Test @Test
public void metricsEndpointDisabled() throws Exception { public void metricsEndpointDisabled() throws Exception {
endpointDisabled("metrics", MetricsMvcEndpoint.class); endpointDisabled("metrics", MetricsMvcEndpoint.class);
...@@ -625,8 +639,9 @@ public class EndpointWebMvcAutoConfigurationTests { ...@@ -625,8 +639,9 @@ public class EndpointWebMvcAutoConfigurationTests {
private void endpointEnabledOverride(String name, Class<? extends MvcEndpoint> type) private void endpointEnabledOverride(String name, Class<? extends MvcEndpoint> type)
throws Exception { throws Exception {
this.applicationContext.register(RootConfig.class, BaseConfiguration.class, this.applicationContext.register(LoggingConfig.class, RootConfig.class,
ServerPortConfig.class, EndpointWebMvcAutoConfiguration.class); BaseConfiguration.class, ServerPortConfig.class,
EndpointWebMvcAutoConfiguration.class);
EnvironmentTestUtils.addEnvironment(this.applicationContext, EnvironmentTestUtils.addEnvironment(this.applicationContext,
"endpoints.enabled:false", "endpoints.enabled:false",
String.format("endpoints_%s_enabled:true", name)); String.format("endpoints_%s_enabled:true", name));
...@@ -740,6 +755,16 @@ public class EndpointWebMvcAutoConfigurationTests { ...@@ -740,6 +755,16 @@ public class EndpointWebMvcAutoConfigurationTests {
} }
@Configuration
public static class LoggingConfig {
@Bean
public LoggingSystem loggingSystem() {
return LoggingSystem.get(getClass().getClassLoader());
}
}
@Configuration @Configuration
public static class ServerPortConfig { public static class ServerPortConfig {
......
...@@ -42,6 +42,7 @@ import org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint; ...@@ -42,6 +42,7 @@ import org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.JolokiaMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.JolokiaMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.LogFileMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.LogFileMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.LoggersMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.ManagementServletContext; import org.springframework.boot.actuate.endpoint.mvc.ManagementServletContext;
import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
...@@ -50,6 +51,7 @@ import org.springframework.boot.autoconfigure.ImportAutoConfiguration; ...@@ -50,6 +51,7 @@ import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration; import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration;
import org.springframework.boot.logging.LoggingSystem;
import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
...@@ -62,6 +64,7 @@ import static org.assertj.core.api.Assertions.assertThat; ...@@ -62,6 +64,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for configuring the path of an MVC endpoint. * Tests for configuring the path of an MVC endpoint.
* *
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Ben Hale
*/ */
@RunWith(Parameterized.class) @RunWith(Parameterized.class)
public class MvcEndpointPathConfigurationTests { public class MvcEndpointPathConfigurationTests {
...@@ -95,6 +98,7 @@ public class MvcEndpointPathConfigurationTests { ...@@ -95,6 +98,7 @@ public class MvcEndpointPathConfigurationTests {
new Object[] { "jolokia", JolokiaMvcEndpoint.class }, new Object[] { "jolokia", JolokiaMvcEndpoint.class },
new Object[] { "liquibase", LiquibaseEndpoint.class }, new Object[] { "liquibase", LiquibaseEndpoint.class },
new Object[] { "logfile", LogFileMvcEndpoint.class }, new Object[] { "logfile", LogFileMvcEndpoint.class },
new Object[] { "loggers", LoggersMvcEndpoint.class },
new Object[] { "mappings", RequestMappingEndpoint.class }, new Object[] { "mappings", RequestMappingEndpoint.class },
new Object[] { "metrics", MetricsMvcEndpoint.class }, new Object[] { "metrics", MetricsMvcEndpoint.class },
new Object[] { "shutdown", ShutdownEndpoint.class }, new Object[] { "shutdown", ShutdownEndpoint.class },
...@@ -151,6 +155,11 @@ public class MvcEndpointPathConfigurationTests { ...@@ -151,6 +155,11 @@ public class MvcEndpointPathConfigurationTests {
return ConditionEvaluationReport.get(beanFactory); return ConditionEvaluationReport.get(beanFactory);
} }
@Bean
LoggingSystem loggingSystem() {
return LoggingSystem.get(getClass().getClassLoader());
}
@Bean @Bean
public FlywayEndpoint flyway() { public FlywayEndpoint flyway() {
return new FlywayEndpoint(new Flyway()); return new FlywayEndpoint(new Flyway());
......
/*
* Copyright 2016-2016 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
*
* http://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.actuate.endpoint;
import java.util.Collections;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.LoggersEndpoint.LoggerLevels;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.logging.LoggerConfiguration;
import org.springframework.boot.logging.LoggingSystem;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link LoggersEndpoint}.
*
* @author Ben Hale
*/
public class LoggersEndpointTests extends AbstractEndpointTests<LoggersEndpoint> {
public LoggersEndpointTests() {
super(Config.class, LoggersEndpoint.class, "loggers", true, "endpoints.loggers");
}
@Test
public void invokeShouldReturnConfigurations() throws Exception {
given(getLoggingSystem().getLoggerConfigurations()).willReturn(Collections
.singletonList(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG)));
LoggerLevels levels = getEndpointBean().invoke().get("ROOT");
assertThat(levels.getConfiguredLevel()).isNull();
assertThat(levels.getEffectiveLevel()).isEqualTo("DEBUG");
}
public void invokeWhenNameSpecifiedShouldReturnLevels() throws Exception {
given(getLoggingSystem().getLoggerConfiguration("ROOT"))
.willReturn(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG));
LoggerLevels levels = getEndpointBean().invoke("ROOT");
assertThat(levels.getConfiguredLevel()).isNull();
assertThat(levels.getEffectiveLevel()).isEqualTo("DEBUG");
}
public void setLogLevelShouldSetLevelOnLoggingSystem() throws Exception {
getEndpointBean().setLogLevel("ROOT", LogLevel.DEBUG);
verify(getLoggingSystem()).setLogLevel("ROOT", LogLevel.DEBUG);
}
private LoggingSystem getLoggingSystem() {
return this.context.getBean(LoggingSystem.class);
}
@Configuration
@EnableConfigurationProperties
public static class Config {
@Bean
public LoggingSystem loggingSystem() {
return mock(LoggingSystem.class);
}
@Bean
public LoggersEndpoint endpoint(LoggingSystem loggingSystem) {
return new LoggersEndpoint(loggingSystem);
}
}
}
...@@ -119,7 +119,7 @@ public class HalBrowserMvcEndpointVanillaIntegrationTests { ...@@ -119,7 +119,7 @@ public class HalBrowserMvcEndpointVanillaIntegrationTests {
@Test @Test
public void endpointsEachHaveSelf() throws Exception { public void endpointsEachHaveSelf() throws Exception {
Set<String> collections = new HashSet<String>( Set<String> collections = new HashSet<String>(
Arrays.asList("/trace", "/beans", "/dump", "/heapdump")); Arrays.asList("/trace", "/beans", "/dump", "/heapdump", "/loggers"));
for (MvcEndpoint endpoint : this.mvcEndpoints.getEndpoints()) { for (MvcEndpoint endpoint : this.mvcEndpoints.getEndpoints()) {
String path = endpoint.getPath(); String path = endpoint.getPath();
if (collections.contains(path)) { if (collections.contains(path)) {
......
/*
* Copyright 2016-2016 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
*
* http://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.actuate.endpoint.mvc;
import java.util.Collections;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration;
import org.springframework.boot.actuate.endpoint.LoggersEndpoint;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.logging.LoggerConfiguration;
import org.springframework.boot.logging.LoggingSystem;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Tests for {@link LoggersMvcEndpoint}.
*
* @author Ben Hale
* @author Phillip Webb
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class LoggersMvcEndpointTests {
@Autowired
private WebApplicationContext context;
@Autowired
private LoggingSystem loggingSystem;
private MockMvc mvc;
@Before
public void setUp() {
this.context.getBean(LoggersEndpoint.class).setEnabled(true);
this.mvc = MockMvcBuilders.webAppContextSetup(this.context)
.alwaysDo(MockMvcResultHandlers.print()).build();
}
@After
public void reset() {
Mockito.reset(this.loggingSystem);
}
@Test
public void getLoggerShouldReturnAllLoggerConfigurations() throws Exception {
given(this.loggingSystem.getLoggerConfigurations()).willReturn(Collections
.singletonList(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG)));
this.mvc.perform(get("/loggers")).andExpect(status().isOk())
.andExpect(content().string(equalTo("{\"ROOT\":{\"configuredLevel\":"
+ "null,\"effectiveLevel\":\"DEBUG\"}}")));
}
@Test
public void getLoggersWhenDisabledShouldReturnNotFound() throws Exception {
this.context.getBean(LoggersEndpoint.class).setEnabled(false);
this.mvc.perform(get("/loggers")).andExpect(status().isNotFound());
}
@Test
public void getLoggerShouldReturnLogLevels() throws Exception {
given(this.loggingSystem.getLoggerConfiguration("ROOT"))
.willReturn(new LoggerConfiguration("ROOT", null, LogLevel.DEBUG));
this.mvc.perform(get("/loggers/ROOT")).andExpect(status().isOk())
.andExpect(content().string(equalTo(
"{\"configuredLevel\":null," + "\"effectiveLevel\":\"DEBUG\"}")));
}
@Test
public void getLoggesWhenDisabledShouldReturnNotFound() throws Exception {
this.context.getBean(LoggersEndpoint.class).setEnabled(false);
this.mvc.perform(get("/loggers/ROOT")).andExpect(status().isNotFound());
}
@Test
public void getLoggersWhenLoggerNotFoundShouldReturnNotFound() throws Exception {
this.mvc.perform(get("/loggers/com.does.not.exist"))
.andExpect(status().isNotFound());
}
@Test
public void setLoggerShouldSetLogLevel() throws Exception {
this.mvc.perform(post("/loggers/ROOT").contentType(MediaType.APPLICATION_JSON)
.content("{\"configuredLevel\":\"DEBUG\"}")).andExpect(status().isOk());
verify(this.loggingSystem).setLogLevel("ROOT", LogLevel.DEBUG);
}
@Test
public void setLoggerWhenDisabledShouldReturnNotFound() throws Exception {
this.context.getBean(LoggersEndpoint.class).setEnabled(false);
this.mvc.perform(post("/loggers/ROOT").contentType(MediaType.APPLICATION_JSON)
.content("{\"configuredLevel\":\"DEBUG\"}"))
.andExpect(status().isNotFound());
verifyZeroInteractions(this.loggingSystem);
}
@Configuration
@Import({ JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class, WebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class })
public static class TestConfiguration {
@Bean
public LoggingSystem loggingSystem() {
return mock(LoggingSystem.class);
}
@Bean
public LoggersEndpoint endpoint(LoggingSystem loggingSystem) {
return new LoggersEndpoint(loggingSystem);
}
}
}
...@@ -16,6 +16,9 @@ ...@@ -16,6 +16,9 @@
package org.springframework.boot.logging; package org.springframework.boot.logging;
import java.util.HashMap;
import java.util.Map;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
...@@ -173,4 +176,34 @@ public abstract class AbstractLoggingSystem extends LoggingSystem { ...@@ -173,4 +176,34 @@ public abstract class AbstractLoggingSystem extends LoggingSystem {
new LoggingSystemProperties(environment).apply(logFile); new LoggingSystemProperties(environment).apply(logFile);
} }
/**
* Maintains a mapping between native levels and {@link LogLevel}.
* @param <T> The native level type
*/
protected static class LogLevels<T> {
private final Map<LogLevel, T> systemToNative;
private final Map<T, LogLevel> nativeToSystem;
public LogLevels() {
this.systemToNative = new HashMap<LogLevel, T>();
this.nativeToSystem = new HashMap<T, LogLevel>();
}
public void map(LogLevel system, T nativeLevel) {
this.systemToNative.put(system, nativeLevel);
this.nativeToSystem.put(nativeLevel, system);
}
public LogLevel convertNativeToSystem(T level) {
return this.nativeToSystem.get(level);
}
public T convertSystemToNative(LogLevel level) {
return this.systemToNative.get(level);
}
}
} }
/*
* Copyright 2016-2016 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
*
* http://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 org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Immutable class that represents the configuration of a {@link LoggingSystem}'s logger.
*
* @author Ben Hale
* @since 1.5.0
*/
public final class LoggerConfiguration {
private final String name;
private final LogLevel configuredLevel;
private final LogLevel effectiveLevel;
/**
* Create a new {@link LoggerConfiguration instance}.
* @param name the name of the logger
* @param configuredLevel the configured level of the logger
* @param effectiveLevel the effective level of the logger
*/
public LoggerConfiguration(String name, LogLevel configuredLevel,
LogLevel effectiveLevel) {
Assert.notNull(name, "Name must not be null");
Assert.notNull(effectiveLevel, "EffectiveLevel must not be null");
this.name = name;
this.configuredLevel = configuredLevel;
this.effectiveLevel = effectiveLevel;
}
/**
* Returns the configured level of the logger.
* @return the configured level of the logger
*/
public LogLevel getConfiguredLevel() {
return this.configuredLevel;
}
/**
* Returns the effective level of the logger.
* @return the effective level of the logger
*/
public LogLevel getEffectiveLevel() {
return this.effectiveLevel;
}
/**
* Returns the name of the logger.
* @return the name of the logger
*/
public String getName() {
return this.name;
}
@Override
public String toString() {
return "LoggerConfiguration [name=" + this.name + ", configuredLevel="
+ this.configuredLevel + ", effectiveLevel=" + this.effectiveLevel + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ObjectUtils.nullSafeHashCode(this.name);
result = prime * result + ObjectUtils.nullSafeHashCode(this.configuredLevel);
result = prime * result + ObjectUtils.nullSafeHashCode(this.effectiveLevel);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (obj instanceof LoggerConfiguration) {
LoggerConfiguration other = (LoggerConfiguration) obj;
boolean rtn = true;
rtn &= ObjectUtils.nullSafeEquals(this.name, other.name);
rtn &= ObjectUtils.nullSafeEquals(this.configuredLevel,
other.configuredLevel);
rtn &= ObjectUtils.nullSafeEquals(this.effectiveLevel, other.effectiveLevel);
return rtn;
}
return super.equals(obj);
}
}
/*
* Copyright 2016-2016 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
*
* http://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.Comparator;
import org.springframework.util.Assert;
/**
* An implementation of {@link Comparator} for comparing {@link LoggerConfiguration}s.
* Sorts the "root" logger as the first logger and then lexically by name after that.
*
* @author Ben Hale
* @since 1.5.0
*/
public class LoggerConfigurationComparator implements Comparator<LoggerConfiguration> {
private final String rootLoggerName;
/**
* Create a new {@link LoggerConfigurationComparator} instance.
* @param rootLoggerName the name of the "root" logger
*/
public LoggerConfigurationComparator(String rootLoggerName) {
Assert.notNull(rootLoggerName, "RootLoggerName must not be null");
this.rootLoggerName = rootLoggerName;
}
@Override
public int compare(LoggerConfiguration o1, LoggerConfiguration o2) {
if (this.rootLoggerName.equals(o1.getName())) {
return -1;
}
if (this.rootLoggerName.equals(o2.getName())) {
return 1;
}
return o1.getName().compareTo(o2.getName());
}
}
...@@ -18,6 +18,7 @@ package org.springframework.boot.logging; ...@@ -18,6 +18,7 @@ package org.springframework.boot.logging;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
...@@ -29,6 +30,7 @@ import org.springframework.util.StringUtils; ...@@ -29,6 +30,7 @@ import org.springframework.util.StringUtils;
* @author Phillip Webb * @author Phillip Webb
* @author Dave Syer * @author Dave Syer
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Ben Hale
*/ */
public abstract class LoggingSystem { public abstract class LoggingSystem {
...@@ -97,7 +99,29 @@ public abstract class LoggingSystem { ...@@ -97,7 +99,29 @@ public abstract class LoggingSystem {
* @param loggerName the name of the logger to set * @param loggerName the name of the logger to set
* @param level the log level * @param level the log level
*/ */
public abstract void setLogLevel(String loggerName, LogLevel level); public void setLogLevel(String loggerName, LogLevel level) {
throw new UnsupportedOperationException("Unable to set log level");
}
/**
* Returns a collection of the current configuration for all a {@link LoggingSystem}'s
* loggers.
* @return the current configurations
* @since 1.5.0
*/
public List<LoggerConfiguration> getLoggerConfigurations() {
throw new UnsupportedOperationException("Unable to get logger configurations");
}
/**
* Returns the current configuration for a {@link LoggingSystem}'s logger.
* @param loggerName the name of the logger
* @return the current configuration
* @since 1.5.0
*/
public LoggerConfiguration getLoggerConfiguration(String loggerName) {
throw new UnsupportedOperationException("Unable to get logger configuration");
}
/** /**
* Detect and return the logging system in use. Supports Logback and Java Logging. * Detect and return the logging system in use. Supports Logback and Java Logging.
...@@ -146,6 +170,16 @@ public abstract class LoggingSystem { ...@@ -146,6 +170,16 @@ public abstract class LoggingSystem {
} }
@Override
public List<LoggerConfiguration> getLoggerConfigurations() {
return Collections.emptyList();
}
@Override
public LoggerConfiguration getLoggerConfiguration(String loggerName) {
return null;
}
} }
} }
...@@ -18,9 +18,10 @@ package org.springframework.boot.logging.java; ...@@ -18,9 +18,10 @@ package org.springframework.boot.logging.java;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.Enumeration;
import java.util.Map; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.LogManager; import java.util.logging.LogManager;
import java.util.logging.Logger; import java.util.logging.Logger;
...@@ -28,6 +29,8 @@ import java.util.logging.Logger; ...@@ -28,6 +29,8 @@ import java.util.logging.Logger;
import org.springframework.boot.logging.AbstractLoggingSystem; 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.LoggerConfigurationComparator;
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.util.Assert; import org.springframework.util.Assert;
...@@ -41,21 +44,23 @@ import org.springframework.util.StringUtils; ...@@ -41,21 +44,23 @@ import org.springframework.util.StringUtils;
* @author Phillip Webb * @author Phillip Webb
* @author Dave Syer * @author Dave Syer
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Ben Hale
*/ */
public class JavaLoggingSystem extends AbstractLoggingSystem { public class JavaLoggingSystem extends AbstractLoggingSystem {
private static final Map<LogLevel, Level> LEVELS; private static final LoggerConfigurationComparator COMPARATOR = new LoggerConfigurationComparator(
"");
private static final LogLevels<Level> LEVELS = new LogLevels<Level>();
static { static {
Map<LogLevel, Level> levels = new HashMap<LogLevel, Level>(); LEVELS.map(LogLevel.TRACE, Level.FINEST);
levels.put(LogLevel.TRACE, Level.FINEST); LEVELS.map(LogLevel.DEBUG, Level.FINE);
levels.put(LogLevel.DEBUG, Level.FINE); LEVELS.map(LogLevel.INFO, Level.INFO);
levels.put(LogLevel.INFO, Level.INFO); LEVELS.map(LogLevel.WARN, Level.WARNING);
levels.put(LogLevel.WARN, Level.WARNING); LEVELS.map(LogLevel.ERROR, Level.SEVERE);
levels.put(LogLevel.ERROR, Level.SEVERE); LEVELS.map(LogLevel.FATAL, Level.SEVERE);
levels.put(LogLevel.FATAL, Level.SEVERE); LEVELS.map(LogLevel.OFF, Level.OFF);
levels.put(LogLevel.OFF, Level.OFF);
LEVELS = Collections.unmodifiableMap(levels);
} }
public JavaLoggingSystem(ClassLoader classLoader) { public JavaLoggingSystem(ClassLoader classLoader) {
...@@ -113,7 +118,39 @@ public class JavaLoggingSystem extends AbstractLoggingSystem { ...@@ -113,7 +118,39 @@ public class JavaLoggingSystem extends AbstractLoggingSystem {
Assert.notNull(level, "Level must not be null"); Assert.notNull(level, "Level must not be null");
String name = (StringUtils.hasText(loggerName) ? loggerName : ""); String name = (StringUtils.hasText(loggerName) ? loggerName : "");
Logger logger = Logger.getLogger(name); Logger logger = Logger.getLogger(name);
logger.setLevel(LEVELS.get(level)); if (logger != null) {
logger.setLevel(LEVELS.convertSystemToNative(level));
}
}
@Override
public List<LoggerConfiguration> getLoggerConfigurations() {
List<LoggerConfiguration> result = new ArrayList<LoggerConfiguration>();
Enumeration<String> names = LogManager.getLogManager().getLoggerNames();
while (names.hasMoreElements()) {
result.add(getLoggerConfiguration(names.nextElement()));
}
Collections.sort(result, COMPARATOR);
return Collections.unmodifiableList(result);
}
@Override
public LoggerConfiguration getLoggerConfiguration(String loggerName) {
Logger logger = Logger.getLogger(loggerName);
if (logger == null) {
return null;
}
LogLevel level = LEVELS.convertNativeToSystem(logger.getLevel());
LogLevel effectiveLevel = LEVELS.convertNativeToSystem(getEffectiveLevel(logger));
return new LoggerConfiguration(logger.getName(), level, effectiveLevel);
}
private Level getEffectiveLevel(Logger root) {
Logger logger = root;
while (logger.getLevel() == null) {
logger = logger.getParent();
}
return logger.getLevel();
} }
@Override @Override
......
...@@ -21,9 +21,7 @@ import java.io.InputStream; ...@@ -21,9 +21,7 @@ import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
...@@ -32,6 +30,7 @@ import org.apache.logging.log4j.core.Filter; ...@@ -32,6 +30,7 @@ import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.ConfigurationFactory; import org.apache.logging.log4j.core.config.ConfigurationFactory;
import org.apache.logging.log4j.core.config.ConfigurationSource; import org.apache.logging.log4j.core.config.ConfigurationSource;
import org.apache.logging.log4j.core.config.LoggerConfig; import org.apache.logging.log4j.core.config.LoggerConfig;
...@@ -40,6 +39,8 @@ import org.apache.logging.log4j.message.Message; ...@@ -40,6 +39,8 @@ import org.apache.logging.log4j.message.Message;
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.LoggerConfigurationComparator;
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.Slf4JLoggingSystem; import org.springframework.boot.logging.Slf4JLoggingSystem;
...@@ -54,24 +55,26 @@ import org.springframework.util.StringUtils; ...@@ -54,24 +55,26 @@ import org.springframework.util.StringUtils;
* @author Daniel Fullarton * @author Daniel Fullarton
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Alexander Heusingfeld * @author Alexander Heusingfeld
* @author Ben Hale
* @since 1.2.0 * @since 1.2.0
*/ */
public class Log4J2LoggingSystem extends Slf4JLoggingSystem { public class Log4J2LoggingSystem extends Slf4JLoggingSystem {
private static final LoggerConfigurationComparator COMPARATOR = new LoggerConfigurationComparator(
LogManager.ROOT_LOGGER_NAME);
private static final String FILE_PROTOCOL = "file"; private static final String FILE_PROTOCOL = "file";
private static final Map<LogLevel, Level> LEVELS; private static final LogLevels<Level> LEVELS = new LogLevels<Level>();
static { static {
Map<LogLevel, Level> levels = new HashMap<LogLevel, Level>(); LEVELS.map(LogLevel.TRACE, Level.TRACE);
levels.put(LogLevel.TRACE, Level.TRACE); LEVELS.map(LogLevel.DEBUG, Level.DEBUG);
levels.put(LogLevel.DEBUG, Level.DEBUG); LEVELS.map(LogLevel.INFO, Level.INFO);
levels.put(LogLevel.INFO, Level.INFO); LEVELS.map(LogLevel.WARN, Level.WARN);
levels.put(LogLevel.WARN, Level.WARN); LEVELS.map(LogLevel.ERROR, Level.ERROR);
levels.put(LogLevel.ERROR, Level.ERROR); LEVELS.map(LogLevel.FATAL, Level.FATAL);
levels.put(LogLevel.FATAL, Level.FATAL); LEVELS.map(LogLevel.OFF, Level.OFF);
levels.put(LogLevel.OFF, Level.OFF);
LEVELS = Collections.unmodifiableMap(levels);
} }
private static final Filter FILTER = new AbstractFilter() { private static final Filter FILTER = new AbstractFilter() {
...@@ -196,7 +199,7 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem { ...@@ -196,7 +199,7 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem {
@Override @Override
public void setLogLevel(String loggerName, LogLevel logLevel) { public void setLogLevel(String loggerName, LogLevel logLevel) {
Level level = LEVELS.get(logLevel); Level level = LEVELS.convertSystemToNative(logLevel);
LoggerConfig loggerConfig = getLoggerConfig(loggerName); LoggerConfig loggerConfig = getLoggerConfig(loggerName);
if (loggerConfig == null) { if (loggerConfig == null) {
loggerConfig = new LoggerConfig(loggerName, level, true); loggerConfig = new LoggerConfig(loggerName, level, true);
...@@ -208,6 +211,30 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem { ...@@ -208,6 +211,30 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem {
getLoggerContext().updateLoggers(); getLoggerContext().updateLoggers();
} }
@Override
public List<LoggerConfiguration> getLoggerConfigurations() {
List<LoggerConfiguration> result = new ArrayList<LoggerConfiguration>();
Configuration configuration = getLoggerContext().getConfiguration();
for (LoggerConfig loggerConfig : configuration.getLoggers().values()) {
result.add(convertLoggerConfiguration(loggerConfig));
}
Collections.sort(result, COMPARATOR);
return result;
}
@Override
public LoggerConfiguration getLoggerConfiguration(String loggerName) {
return convertLoggerConfiguration(getLoggerConfig(loggerName));
}
private LoggerConfiguration convertLoggerConfiguration(LoggerConfig loggerConfig) {
if (loggerConfig == null) {
return null;
}
LogLevel level = LEVELS.convertNativeToSystem(loggerConfig.getLevel());
return new LoggerConfiguration(loggerConfig.getName(), level, level);
}
@Override @Override
public Runnable getShutdownHandler() { public Runnable getShutdownHandler() {
return new ShutdownHandler(); return new ShutdownHandler();
......
...@@ -19,10 +19,9 @@ package org.springframework.boot.logging.logback; ...@@ -19,10 +19,9 @@ package org.springframework.boot.logging.logback;
import java.net.URL; import java.net.URL;
import java.security.CodeSource; import java.security.CodeSource;
import java.security.ProtectionDomain; import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.LoggerContext;
...@@ -40,6 +39,8 @@ import org.slf4j.impl.StaticLoggerBinder; ...@@ -40,6 +39,8 @@ import org.slf4j.impl.StaticLoggerBinder;
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.LoggerConfigurationComparator;
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.Slf4JLoggingSystem; import org.springframework.boot.logging.Slf4JLoggingSystem;
...@@ -53,23 +54,25 @@ import org.springframework.util.StringUtils; ...@@ -53,23 +54,25 @@ import org.springframework.util.StringUtils;
* @author Phillip Webb * @author Phillip Webb
* @author Dave Syer * @author Dave Syer
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Ben Hale
*/ */
public class LogbackLoggingSystem extends Slf4JLoggingSystem { public class LogbackLoggingSystem extends Slf4JLoggingSystem {
private static final LoggerConfigurationComparator COMPARATOR = new LoggerConfigurationComparator(
Logger.ROOT_LOGGER_NAME);
private static final String CONFIGURATION_FILE_PROPERTY = "logback.configurationFile"; private static final String CONFIGURATION_FILE_PROPERTY = "logback.configurationFile";
private static final Map<LogLevel, Level> LEVELS; private static final LogLevels<Level> LEVELS = new LogLevels<Level>();
static { static {
Map<LogLevel, Level> levels = new HashMap<LogLevel, Level>(); LEVELS.map(LogLevel.TRACE, Level.TRACE);
levels.put(LogLevel.TRACE, Level.TRACE); LEVELS.map(LogLevel.DEBUG, Level.DEBUG);
levels.put(LogLevel.DEBUG, Level.DEBUG); LEVELS.map(LogLevel.INFO, Level.INFO);
levels.put(LogLevel.INFO, Level.INFO); LEVELS.map(LogLevel.WARN, Level.WARN);
levels.put(LogLevel.WARN, Level.WARN); LEVELS.map(LogLevel.ERROR, Level.ERROR);
levels.put(LogLevel.ERROR, Level.ERROR); LEVELS.map(LogLevel.FATAL, Level.ERROR);
levels.put(LogLevel.FATAL, Level.ERROR); LEVELS.map(LogLevel.OFF, Level.OFF);
levels.put(LogLevel.OFF, Level.OFF);
LEVELS = Collections.unmodifiableMap(levels);
} }
private static final TurboFilter FILTER = new TurboFilter() { private static final TurboFilter FILTER = new TurboFilter() {
...@@ -209,9 +212,38 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { ...@@ -209,9 +212,38 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem {
System.setProperty("org.jboss.logging.provider", "slf4j"); System.setProperty("org.jboss.logging.provider", "slf4j");
} }
@Override
public List<LoggerConfiguration> getLoggerConfigurations() {
List<LoggerConfiguration> result = new ArrayList<LoggerConfiguration>();
for (ch.qos.logback.classic.Logger logger : getLoggerContext().getLoggerList()) {
result.add(getLoggerConfiguration(logger));
}
Collections.sort(result, COMPARATOR);
return result;
}
@Override
public LoggerConfiguration getLoggerConfiguration(String loggerName) {
return getLoggerConfiguration(getLogger(loggerName));
}
private LoggerConfiguration getLoggerConfiguration(
ch.qos.logback.classic.Logger logger) {
if (logger == null) {
return null;
}
LogLevel level = LEVELS.convertNativeToSystem(logger.getLevel());
LogLevel effectiveLevel = LEVELS
.convertNativeToSystem(logger.getEffectiveLevel());
return new LoggerConfiguration(logger.getName(), level, effectiveLevel);
}
@Override @Override
public void setLogLevel(String loggerName, LogLevel level) { public void setLogLevel(String loggerName, LogLevel level) {
getLogger(loggerName).setLevel(LEVELS.get(level)); ch.qos.logback.classic.Logger logger = getLogger(loggerName);
if (logger != null) {
logger.setLevel(LEVELS.convertSystemToNative(level));
}
} }
@Override @Override
...@@ -221,8 +253,8 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { ...@@ -221,8 +253,8 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem {
private ch.qos.logback.classic.Logger getLogger(String name) { private ch.qos.logback.classic.Logger getLogger(String name) {
LoggerContext factory = getLoggerContext(); LoggerContext factory = getLoggerContext();
return factory name = (StringUtils.isEmpty(name) ? Logger.ROOT_LOGGER_NAME : name);
.getLogger(StringUtils.isEmpty(name) ? Logger.ROOT_LOGGER_NAME : name); return factory.getLogger(name);
} }
......
/*
* Copyright 2016-2016 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
*
* http://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 org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link LoggerConfigurationComparator}.
*
* @author Ben Hale
*/
public class LoggerConfigurationComparatorTests {
private final LoggerConfigurationComparator comparator = new LoggerConfigurationComparator(
"ROOT");
@Test
public void rootLoggerFirst() {
LoggerConfiguration first = new LoggerConfiguration("ROOT", null, LogLevel.OFF);
LoggerConfiguration second = new LoggerConfiguration("alpha", null, LogLevel.OFF);
assertThat(this.comparator.compare(first, second)).isLessThan(0);
}
@Test
public void rootLoggerSecond() {
LoggerConfiguration first = new LoggerConfiguration("alpha", null, LogLevel.OFF);
LoggerConfiguration second = new LoggerConfiguration("ROOT", null, LogLevel.OFF);
assertThat(this.comparator.compare(first, second)).isGreaterThan(0);
}
@Test
public void rootLoggerFirstEmpty() {
LoggerConfiguration first = new LoggerConfiguration("ROOT", null, LogLevel.OFF);
LoggerConfiguration second = new LoggerConfiguration("", null, LogLevel.OFF);
assertThat(this.comparator.compare(first, second)).isLessThan(0);
}
@Test
public void rootLoggerSecondEmpty() {
LoggerConfiguration first = new LoggerConfiguration("", null, LogLevel.OFF);
LoggerConfiguration second = new LoggerConfiguration("ROOT", null, LogLevel.OFF);
assertThat(this.comparator.compare(first, second)).isGreaterThan(0);
}
@Test
public void lexicalFirst() {
LoggerConfiguration first = new LoggerConfiguration("alpha", null, LogLevel.OFF);
LoggerConfiguration second = new LoggerConfiguration("bravo", null, LogLevel.OFF);
assertThat(this.comparator.compare(first, second)).isLessThan(0);
}
@Test
public void lexicalSecond() {
LoggerConfiguration first = new LoggerConfiguration("bravo", null, LogLevel.OFF);
LoggerConfiguration second = new LoggerConfiguration("alpha", null, LogLevel.OFF);
assertThat(this.comparator.compare(first, second)).isGreaterThan(0);
}
@Test
public void lexicalEqual() {
LoggerConfiguration first = new LoggerConfiguration("alpha", null, LogLevel.OFF);
LoggerConfiguration second = new LoggerConfiguration("alpha", null, LogLevel.OFF);
assertThat(this.comparator.compare(first, second)).isEqualTo(0);
}
}
...@@ -18,6 +18,7 @@ package org.springframework.boot.logging; ...@@ -18,6 +18,7 @@ package org.springframework.boot.logging;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.logging.Handler; import java.util.logging.Handler;
...@@ -57,6 +58,7 @@ import static org.hamcrest.Matchers.not; ...@@ -57,6 +58,7 @@ import static org.hamcrest.Matchers.not;
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Ben Hale
*/ */
public class LoggingApplicationListenerTests { public class LoggingApplicationListenerTests {
...@@ -518,7 +520,16 @@ public class LoggingApplicationListenerTests { ...@@ -518,7 +520,16 @@ public class LoggingApplicationListenerTests {
@Override @Override
public void setLogLevel(String loggerName, LogLevel level) { public void setLogLevel(String loggerName, LogLevel level) {
}
@Override
public List<LoggerConfiguration> getLoggerConfigurations() {
return null;
}
@Override
public LoggerConfiguration getLoggerConfiguration(String loggerName) {
return null;
} }
@Override @Override
...@@ -552,17 +563,24 @@ public class LoggingApplicationListenerTests { ...@@ -552,17 +563,24 @@ public class LoggingApplicationListenerTests {
private boolean cleanedUp = false; private boolean cleanedUp = false;
public TestCleanupLoggingSystem(ClassLoader classLoader) { public TestCleanupLoggingSystem(ClassLoader classLoader) {
} }
@Override @Override
public void beforeInitialize() { public void beforeInitialize() {
} }
@Override @Override
public void setLogLevel(String loggerName, LogLevel level) { public void setLogLevel(String loggerName, LogLevel level) {
}
@Override
public List<LoggerConfiguration> getLoggerConfigurations() {
return null;
}
@Override
public LoggerConfiguration getLoggerConfiguration(String loggerName) {
return null;
} }
@Override @Override
......
...@@ -42,4 +42,28 @@ public class LoggingSystemTests { ...@@ -42,4 +42,28 @@ public class LoggingSystemTests {
assertThat(loggingSystem).isInstanceOf(NoOpLoggingSystem.class); assertThat(loggingSystem).isInstanceOf(NoOpLoggingSystem.class);
} }
@Test(expected = UnsupportedOperationException.class)
public void getLoggerConfigurationIsUnsupported() {
new StubLoggingSystem().getLoggerConfiguration("test-logger-name");
}
@Test(expected = UnsupportedOperationException.class)
public void listLoggerConfigurationsIsUnsupported() {
new StubLoggingSystem().getLoggerConfigurations();
}
private static final class StubLoggingSystem extends LoggingSystem {
@Override
public void beforeInitialize() {
// Stub implementation
}
@Override
public void setLogLevel(String loggerName, LogLevel level) {
// Stub implementation
}
}
} }
...@@ -19,7 +19,9 @@ package org.springframework.boot.logging.java; ...@@ -19,7 +19,9 @@ package org.springframework.boot.logging.java;
import java.io.File; import java.io.File;
import java.io.FileFilter; import java.io.FileFilter;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.logging.Level;
import org.apache.commons.logging.impl.Jdk14Logger; import org.apache.commons.logging.impl.Jdk14Logger;
import org.junit.After; import org.junit.After;
...@@ -29,6 +31,7 @@ import org.junit.Test; ...@@ -29,6 +31,7 @@ import org.junit.Test;
import org.springframework.boot.logging.AbstractLoggingSystemTests; import org.springframework.boot.logging.AbstractLoggingSystemTests;
import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.logging.LoggerConfiguration;
import org.springframework.boot.testutil.InternalOutputCapture; import org.springframework.boot.testutil.InternalOutputCapture;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
...@@ -40,6 +43,7 @@ import static org.assertj.core.api.Assertions.assertThat; ...@@ -40,6 +43,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* *
* @author Dave Syer * @author Dave Syer
* @author Phillip Webb * @author Phillip Webb
* @author Ben Hale
*/ */
public class JavaLoggingSystemTests extends AbstractLoggingSystemTests { public class JavaLoggingSystemTests extends AbstractLoggingSystemTests {
...@@ -74,6 +78,11 @@ public class JavaLoggingSystemTests extends AbstractLoggingSystemTests { ...@@ -74,6 +78,11 @@ public class JavaLoggingSystemTests extends AbstractLoggingSystemTests {
Locale.setDefault(this.defaultLocale); Locale.setDefault(this.defaultLocale);
} }
@After
public void resetLogger() {
this.logger.getLogger().setLevel(Level.OFF);
}
@Test @Test
public void noFile() throws Exception { public void noFile() throws Exception {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
...@@ -150,4 +159,26 @@ public class JavaLoggingSystemTests extends AbstractLoggingSystemTests { ...@@ -150,4 +159,26 @@ public class JavaLoggingSystemTests extends AbstractLoggingSystemTests {
.isEqualTo(1); .isEqualTo(1);
} }
@Test
public void getLoggingConfigurations() throws Exception {
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(null, null, null);
this.loggingSystem.setLogLevel(getClass().getName(), LogLevel.DEBUG);
List<LoggerConfiguration> configurations = this.loggingSystem
.getLoggerConfigurations();
assertThat(configurations).isNotEmpty();
assertThat(configurations.get(0).getName()).isEmpty();
}
@Test
public void getLoggingConfiguration() throws Exception {
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(null, null, null);
this.loggingSystem.setLogLevel(getClass().getName(), LogLevel.DEBUG);
LoggerConfiguration configuration = this.loggingSystem
.getLoggerConfiguration(getClass().getName());
assertThat(configuration).isEqualTo(new LoggerConfiguration(getClass().getName(),
LogLevel.DEBUG, LogLevel.DEBUG));
}
} }
...@@ -38,6 +38,7 @@ import org.junit.Test; ...@@ -38,6 +38,7 @@ import org.junit.Test;
import org.springframework.boot.logging.AbstractLoggingSystemTests; import org.springframework.boot.logging.AbstractLoggingSystemTests;
import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.logging.LoggerConfiguration;
import org.springframework.boot.testutil.InternalOutputCapture; import org.springframework.boot.testutil.InternalOutputCapture;
import org.springframework.boot.testutil.Matched; import org.springframework.boot.testutil.Matched;
import org.springframework.util.FileCopyUtils; import org.springframework.util.FileCopyUtils;
...@@ -57,6 +58,7 @@ import static org.mockito.Mockito.verify; ...@@ -57,6 +58,7 @@ import static org.mockito.Mockito.verify;
* @author Daniel Fullarton * @author Daniel Fullarton
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Ben Hale
*/ */
public class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { public class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
...@@ -131,6 +133,28 @@ public class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests { ...@@ -131,6 +133,28 @@ public class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
.isEqualTo(1); .isEqualTo(1);
} }
@Test
public void getLoggingConfigurations() throws Exception {
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(null, null, null);
this.loggingSystem.setLogLevel(getClass().getName(), LogLevel.DEBUG);
List<LoggerConfiguration> configurations = this.loggingSystem
.getLoggerConfigurations();
assertThat(configurations).isNotEmpty();
assertThat(configurations.get(0).getName()).isEmpty();
}
@Test
public void getLoggingConfiguration() throws Exception {
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(null, null, null);
this.loggingSystem.setLogLevel(getClass().getName(), LogLevel.DEBUG);
LoggerConfiguration configuration = this.loggingSystem
.getLoggerConfiguration(getClass().getName());
assertThat(configuration).isEqualTo(new LoggerConfiguration(getClass().getName(),
LogLevel.DEBUG, LogLevel.DEBUG));
}
@Test @Test
public void setLevelOfUnconfiguredLoggerDoesNotAffectRootConfiguration() public void setLevelOfUnconfiguredLoggerDoesNotAffectRootConfiguration()
throws Exception { throws Exception {
......
...@@ -18,6 +18,7 @@ package org.springframework.boot.logging.logback; ...@@ -18,6 +18,7 @@ package org.springframework.boot.logging.logback;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.util.List;
import java.util.logging.Handler; import java.util.logging.Handler;
import java.util.logging.LogManager; import java.util.logging.LogManager;
...@@ -39,6 +40,7 @@ import org.slf4j.impl.StaticLoggerBinder; ...@@ -39,6 +40,7 @@ import org.slf4j.impl.StaticLoggerBinder;
import org.springframework.boot.logging.AbstractLoggingSystemTests; import org.springframework.boot.logging.AbstractLoggingSystemTests;
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.LoggingInitializationContext; import org.springframework.boot.logging.LoggingInitializationContext;
import org.springframework.boot.testutil.InternalOutputCapture; import org.springframework.boot.testutil.InternalOutputCapture;
import org.springframework.boot.testutil.Matched; import org.springframework.boot.testutil.Matched;
...@@ -59,6 +61,7 @@ import static org.mockito.Mockito.verify; ...@@ -59,6 +61,7 @@ import static org.mockito.Mockito.verify;
* @author Dave Syer * @author Dave Syer
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Ben Hale
*/ */
public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests { public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
...@@ -170,6 +173,29 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests { ...@@ -170,6 +173,29 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests {
.isEqualTo(1); .isEqualTo(1);
} }
@Test
public void getLoggingConfigurations() throws Exception {
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
this.loggingSystem.setLogLevel(getClass().getName(), LogLevel.DEBUG);
List<LoggerConfiguration> configurations = this.loggingSystem
.getLoggerConfigurations();
assertThat(configurations).isNotEmpty();
assertThat(configurations.get(0).getName())
.isEqualTo(org.slf4j.Logger.ROOT_LOGGER_NAME);
}
@Test
public void getLoggingConfiguration() throws Exception {
this.loggingSystem.beforeInitialize();
this.loggingSystem.initialize(this.initializationContext, null, null);
this.loggingSystem.setLogLevel(getClass().getName(), LogLevel.DEBUG);
LoggerConfiguration configuration = this.loggingSystem
.getLoggerConfiguration(getClass().getName());
assertThat(configuration).isEqualTo(new LoggerConfiguration(getClass().getName(),
LogLevel.DEBUG, LogLevel.DEBUG));
}
@Test @Test
public void loggingThatUsesJulIsCaptured() { public void loggingThatUsesJulIsCaptured() {
this.loggingSystem.beforeInitialize(); this.loggingSystem.beforeInitialize();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment