[#187] Adds ManagementProperties contextPath to skipPattern

Fixes #187
This commit is contained in:
Marcin Grzejszczak
2016-03-07 17:45:46 +01:00
parent 3e9a2252a3
commit 4f4aa25a5d
5 changed files with 197 additions and 20 deletions

View File

@@ -330,7 +330,8 @@ Features from this section can be disabled by providing the `spring.sleuth.web.e
Via the `TraceFilter` all sampled incoming requests result in creation of a Span. That Span's name is `http:` + the path to which
the request was sent. E.g. if the request was sent to `/foo/bar` then the name will be `http:/foo/bar`. You can configure which URIs you would
like to skip via the `spring.sleuth.instrument.web.skipPattern` property.
like to skip via the `spring.sleuth.instrument.web.skipPattern` property. If you have `ManagementServerProperties` on classpath then
its value of `contextPath` gets appended to the provided skip pattern.
==== Async Servlet support

View File

@@ -74,8 +74,8 @@ public class TraceFilter extends OncePerRequestFilter
protected static final String TRACE_REQUEST_ATTR = TraceFilter.class.getName()
+ ".TRACE";
public static final Pattern DEFAULT_SKIP_PATTERN = Pattern.compile(
"/api-docs.*|/autoconfig|/configprops|/dump|/health|/info|/metrics.*|/mappings|/trace|/swagger.*|.*\\.png|.*\\.css|.*\\.js|.*\\.html|/favicon.ico|/hystrix.stream");
public static final String DEFAULT_SKIP_PATTERN =
"/api-docs.*|/autoconfig|/configprops|/dump|/health|/info|/metrics.*|/mappings|/trace|/swagger.*|.*\\.png|.*\\.css|.*\\.js|.*\\.html|/favicon.ico|/hystrix.stream";
private final Tracer tracer;
private final TraceKeys traceKeys;
@@ -86,7 +86,7 @@ public class TraceFilter extends OncePerRequestFilter
private ApplicationEventPublisher publisher;
public TraceFilter(Tracer tracer, TraceKeys traceKeys) {
this(tracer, traceKeys, DEFAULT_SKIP_PATTERN, new Random());
this(tracer, traceKeys, Pattern.compile(DEFAULT_SKIP_PATTERN), new Random());
}
public TraceFilter(Tracer tracer, TraceKeys traceKeys, Pattern skipPattern,
@@ -105,7 +105,7 @@ public class TraceFilter extends OncePerRequestFilter
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
throws ServletException, IOException {
String uri = this.urlPathHelper.getPathWithinApplication(request);
boolean skip = this.skipPattern.matcher(uri).matches()

View File

@@ -20,17 +20,20 @@ import java.util.regex.Pattern;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.autoconfigure.ManagementServerProperties;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.sleuth.SpanAccessor;
import org.springframework.cloud.sleuth.SpanNamer;
import org.springframework.cloud.sleuth.TraceKeys;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.cloud.sleuth.TraceKeys;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -77,11 +80,82 @@ public class TraceWebAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public TraceFilter traceFilter(ApplicationEventPublisher publisher, Random random) {
Pattern pattern = StringUtils.hasText(this.skipPattern) ? Pattern.compile(this.skipPattern)
: TraceFilter.DEFAULT_SKIP_PATTERN;
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, pattern, random);
public TraceFilter traceFilter(ApplicationEventPublisher publisher, Random random, SkipPatternProvider skipPatternProvider) {
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, skipPatternProvider.skipPattern(), random);
filter.setApplicationEventPublisher(publisher);
return filter;
}
@Configuration
@ConditionalOnClass(ManagementServerProperties.class)
@ConditionalOnMissingBean(SkipPatternProvider.class)
protected static class SkipPatternProviderConfig {
/**
* Pattern for URLs that should be skipped in tracing
*/
@Value("${spring.sleuth.instrument.web.skipPattern:}")
private String skipPattern;
@Bean
@ConditionalOnBean(ManagementServerProperties.class)
public SkipPatternProvider skipPatternForManagementServerProperties(
final ManagementServerProperties managementServerProperties) {
return new SkipPatternProvider() {
@Override
public Pattern skipPattern() {
return getPatternForManagementServerProperties(
managementServerProperties, SkipPatternProviderConfig.this.skipPattern);
}
};
}
/**
* Sets or appends {@link ManagementServerProperties#getContextPath()} to the
* skip pattern. If neither is available then sets the default one
*/
static Pattern getPatternForManagementServerProperties(
ManagementServerProperties managementServerProperties, String skipPattern) {
if (StringUtils.hasText(skipPattern) &&
StringUtils.hasText(managementServerProperties.getContextPath())) {
return Pattern.compile(skipPattern + "|" +
managementServerProperties.getContextPath() + ".*");
} else if (StringUtils.hasText(managementServerProperties.getContextPath())) {
return Pattern.compile(managementServerProperties.getContextPath() + ".*");
}
return defaultSkipPattern(skipPattern);
}
@Bean
@ConditionalOnMissingBean(ManagementServerProperties.class)
public SkipPatternProvider defaultSkipPatternBeanIfManagementServerPropsArePresent() {
return defaultSkipPatternProvider(this.skipPattern);
}
}
@Bean
@ConditionalOnMissingClass("org.springframework.boot.actuate.autoconfigure.ManagementServerProperties")
@ConditionalOnMissingBean(SkipPatternProvider.class)
public SkipPatternProvider defaultSkipPatternBean() {
return defaultSkipPatternProvider(this.skipPattern);
}
private static SkipPatternProvider defaultSkipPatternProvider(final String skipPattern) {
return new SkipPatternProvider() {
@Override
public Pattern skipPattern() {
return defaultSkipPattern(skipPattern);
}
};
}
private static Pattern defaultSkipPattern(String skipPattern) {
return StringUtils.hasText(skipPattern) ?
Pattern.compile(skipPattern)
: Pattern.compile(TraceFilter.DEFAULT_SKIP_PATTERN);
}
interface SkipPatternProvider {
Pattern skipPattern();
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2013-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.cloud.sleuth.instrument.web;
import java.util.regex.Pattern;
import org.junit.Test;
import org.springframework.boot.actuate.autoconfigure.ManagementServerProperties;
import org.springframework.cloud.sleuth.instrument.web.TraceWebAutoConfiguration.SkipPatternProviderConfig;
import static org.assertj.core.api.BDDAssertions.then;
/**
* @author Marcin Grzejszczak
*/
public class SkipPatternProviderConfigTest {
@Test
public void should_combine_skip_pattern_and_management_context_when_they_are_both_not_empty() throws Exception {
String skipPattern = "foo.*|bar.*";
Pattern pattern = SkipPatternProviderConfig.getPatternForManagementServerProperties(
managementServerPropertiesWithContextPath(), skipPattern);
then(pattern.pattern()).isEqualTo("foo.*|bar.*|/management/context.*");
}
@Test
public void should_pick_skip_pattern_when_its_not_empty_and_management_context_is_empty() throws Exception {
String skipPattern = "foo.*|bar.*";
Pattern pattern = SkipPatternProviderConfig.getPatternForManagementServerProperties(new ManagementServerProperties(), skipPattern);
then(pattern.pattern()).isEqualTo("foo.*|bar.*");
}
@Test
public void should_pick_management_context_when_skip_patterns_is_empty_and_context_path_is_not() throws Exception {
String skipPattern = "";
Pattern pattern = SkipPatternProviderConfig.getPatternForManagementServerProperties(
managementServerPropertiesWithContextPath(), skipPattern);
then(pattern.pattern()).isEqualTo("/management/context.*");
}
@Test
public void should_pick_default_pattern_when_both_management_context_and_skip_patterns_are_empty() throws Exception {
String skipPattern = "";
Pattern pattern = SkipPatternProviderConfig.getPatternForManagementServerProperties(new ManagementServerProperties(), skipPattern);
then(pattern.pattern()).isEqualTo(TraceFilter.DEFAULT_SKIP_PATTERN);
}
private ManagementServerProperties managementServerPropertiesWithContextPath() {
ManagementServerProperties managementServerProperties = new ManagementServerProperties();
managementServerProperties.setContextPath("/management/context");
return managementServerProperties;
}
}

View File

@@ -1,9 +1,5 @@
package org.springframework.cloud.sleuth.instrument.web;
import static org.assertj.core.api.BDDAssertions.then;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
@@ -12,12 +8,15 @@ import org.apache.commons.logging.LogFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.ManagementServerProperties;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.TraceKeys;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.instrument.DefaultTestAutoConfiguration;
import org.springframework.cloud.sleuth.TraceKeys;
import org.springframework.cloud.sleuth.instrument.web.common.AbstractMvcIntegrationTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MvcResult;
@@ -26,6 +25,10 @@ import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static org.assertj.core.api.BDDAssertions.then;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(TraceFilterIntegrationTests.class)
@DefaultTestAutoConfiguration
@@ -34,10 +37,9 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest {
private static Log logger = LogFactory.getLog(TraceFilterIntegrationTests.class);
@Autowired
Tracer tracer;
@Autowired
TraceKeys traceKeys;
@Autowired Tracer tracer;
@Autowired TraceKeys traceKeys;
@Autowired TraceFilter traceFilter;
static Span span;
@@ -61,6 +63,13 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest {
then(tracingHeaderFrom(mvcResult)).isNotNull();
}
@Test
public void should_ignore_sampling_the_span_if_uri_matches_management_properties_context_path() throws Exception {
MvcResult mvcResult = whenSentInfoWithTraceId(new Random().nextLong());
then(notSampledHeaderIsPresent(mvcResult)).isEqualTo(true);
}
@Test
public void when_correlationId_is_sent_should_not_create_a_new_one_but_return_the_existing_one_instead()
throws Exception {
@@ -85,7 +94,7 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest {
@Override
protected void configureMockMvcBuilder(DefaultMockMvcBuilder mockMvcBuilder) {
mockMvcBuilder.addFilters(new TraceFilter(this.tracer, this.traceKeys));
mockMvcBuilder.addFilters(traceFilter);
}
private MvcResult whenSentPingWithoutTracingData() throws Exception {
@@ -98,6 +107,10 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest {
return sendPingWithTraceId(Span.TRACE_ID_NAME, passedTraceId);
}
private MvcResult whenSentInfoWithTraceId(Long passedTraceId) throws Exception {
return sendPingWithTraceId("/additionalContextPath/info", Span.TRACE_ID_NAME, passedTraceId);
}
private MvcResult whenSentFutureWithTraceId(Long passedTraceId) throws Exception {
return sendPingWithTraceId("/future", Span.TRACE_ID_NAME, passedTraceId);
}
@@ -119,4 +132,18 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest {
private Long tracingHeaderFrom(MvcResult mvcResult) {
return Span.hexToId(mvcResult.getResponse().getHeader(Span.TRACE_ID_NAME));
}
private boolean notSampledHeaderIsPresent(MvcResult mvcResult) {
return mvcResult.getResponse().containsHeader(Span.NOT_SAMPLED_NAME);
}
@Configuration
static class Config {
@Bean
ManagementServerProperties managementServerProperties() {
ManagementServerProperties managementServerProperties = new ManagementServerProperties();
managementServerProperties.setContextPath("/additionalContextPath");
return managementServerProperties;
}
}
}