@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user