Enhance endpoints handling (#1201)
* Enhance endpoints handling * Add conditionalOnBean ServerProperties * Code review remarks from @shakuzen * Provide ignoreAutoConfiguredSkipPatterns flag. * Update documentation related to spring boot actuator and new flag spring.sleuth.web.ignore-auto-configured-skip-patterns.
This commit is contained in:
committed by
Marcin Grzejszczak
parent
1775ec1000
commit
5fb9177dec
@@ -1072,6 +1072,10 @@ You can configure which URIs you would like to skip by setting the `spring.sleut
|
||||
If you have `ManagementServerProperties` on classpath, its value of `contextPath` gets appended to the provided skip pattern.
|
||||
If you want to reuse the Sleuth's default skip patterns and just append your own, pass those patterns by using the `spring.sleuth.web.additionalSkipPattern`.
|
||||
|
||||
By default, all the spring boot actuator endpoints are automatically added to the skip pattern.
|
||||
If you want to disable this behaviour set `spring.sleuth.web.ignore-auto-configured-skip-patterns`
|
||||
to `true`.
|
||||
|
||||
To change the order of tracing filter registration, please set the
|
||||
`spring.sleuth.web.filter-order` property.
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ public class SleuthWebProperties {
|
||||
/**
|
||||
* Default set of skip patterns.
|
||||
*/
|
||||
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|/application/.*|/actuator.*|/cloudfoundryapplication";
|
||||
public static final String DEFAULT_SKIP_PATTERN = "/api-docs.*|/swagger.*|.*\\.png|.*\\.css|.*\\.js|.*\\.html|/favicon.ico|/hystrix.stream";
|
||||
|
||||
/**
|
||||
* When true enables instrumentation for web applications.
|
||||
@@ -67,6 +67,12 @@ public class SleuthWebProperties {
|
||||
*/
|
||||
private boolean exceptionLoggingFilterEnabled = true;
|
||||
|
||||
/**
|
||||
* If set to true, auto-configured skip patterns will be ignored.
|
||||
* @see TraceWebAutoConfiguration
|
||||
*/
|
||||
private boolean ignoreAutoConfiguredSkipPatterns = false;
|
||||
|
||||
/**
|
||||
* Properties related to HTTP clients.
|
||||
*/
|
||||
@@ -125,6 +131,15 @@ public class SleuthWebProperties {
|
||||
this.exceptionLoggingFilterEnabled = exceptionLoggingFilterEnabled;
|
||||
}
|
||||
|
||||
public boolean isIgnoreAutoConfiguredSkipPatterns() {
|
||||
return ignoreAutoConfiguredSkipPatterns;
|
||||
}
|
||||
|
||||
public void setIgnoreAutoConfiguredSkipPatterns(
|
||||
boolean ignoreAutoConfiguredSkipPatterns) {
|
||||
this.ignoreAutoConfiguredSkipPatterns = ignoreAutoConfiguredSkipPatterns;
|
||||
}
|
||||
|
||||
public Client getClient() {
|
||||
return this.client;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -25,6 +26,9 @@ import brave.Tracing;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointsSupplier;
|
||||
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoint;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
@@ -66,6 +70,7 @@ public class TraceWebAutoConfiguration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(ManagementServerProperties.class)
|
||||
@ConditionalOnProperty(value = "spring.sleuth.web.ignoreAutoConfiguredSkipPatterns", havingValue = "false", matchIfMissing = true)
|
||||
protected static class ManagementSkipPatternProviderConfig {
|
||||
|
||||
/**
|
||||
@@ -93,31 +98,53 @@ public class TraceWebAutoConfiguration {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass({ ServerProperties.class, WebEndpointProperties.class })
|
||||
protected static class ServerSkipPatternProviderConfig {
|
||||
@ConditionalOnClass({ ServerProperties.class, EndpointsSupplier.class,
|
||||
ExposableWebEndpoint.class })
|
||||
@ConditionalOnBean(ServerProperties.class)
|
||||
@ConditionalOnProperty(value = "spring.sleuth.web.ignoreAutoConfiguredSkipPatterns", havingValue = "false", matchIfMissing = true)
|
||||
protected static class ActuatorSkipPatternProviderConfig {
|
||||
|
||||
static Optional<Pattern> getEndpointsPatterns(ServerProperties serverProperties,
|
||||
WebEndpointProperties webEndpointProperties,
|
||||
EndpointsSupplier<ExposableWebEndpoint> endpointsSupplier) {
|
||||
Collection<ExposableWebEndpoint> endpoints = endpointsSupplier.getEndpoints();
|
||||
|
||||
if (endpoints.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses {@link ServerProperties#getServlet()#getContextPath()} and
|
||||
* {@link WebEndpointProperties#getBasePath()} to skip Actuator endpoints.
|
||||
*/
|
||||
static Optional<Pattern> getPatternForServerProperties(
|
||||
ServerProperties serverProperties,
|
||||
WebEndpointProperties webEndpointProperties) {
|
||||
String contextPath = serverProperties.getServlet().getContextPath();
|
||||
if (StringUtils.hasText(contextPath)) {
|
||||
return Optional.of(Pattern.compile(
|
||||
contextPath + webEndpointProperties.getBasePath() + ".*"));
|
||||
|
||||
String pattern = endpoints.stream().map(PathMappedEndpoint::getRootPath)
|
||||
.map(path -> path + "|" + path + "/.*").collect(
|
||||
Collectors.joining("|",
|
||||
getPathPrefix(contextPath,
|
||||
webEndpointProperties.getBasePath()) + "/(",
|
||||
")"));
|
||||
if (StringUtils.hasText(pattern)) {
|
||||
return Optional.of(Pattern.compile(pattern));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static String getPathPrefix(String contextPath, String actuatorBasePath) {
|
||||
String result = "";
|
||||
if (StringUtils.hasText(contextPath)) {
|
||||
result += contextPath;
|
||||
}
|
||||
if (!actuatorBasePath.equals("/")) {
|
||||
result += actuatorBasePath;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean({ ServerProperties.class, WebEndpointProperties.class })
|
||||
public SingleSkipPattern skipPatternForServerProperties(
|
||||
public SingleSkipPattern skipPatternForActuatorEndpoints(
|
||||
final ServerProperties serverProperties,
|
||||
final WebEndpointProperties webEndpointProperties) {
|
||||
return () -> getPatternForServerProperties(serverProperties,
|
||||
webEndpointProperties);
|
||||
final WebEndpointProperties webEndpointProperties,
|
||||
final EndpointsSupplier<ExposableWebEndpoint> endpointsSupplier) {
|
||||
return () -> getEndpointsPatterns(serverProperties, webEndpointProperties,
|
||||
endpointsSupplier);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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 org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.cloud.sleuth.DisableSecurity;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import brave.Tracer;
|
||||
import brave.sampler.Sampler;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = IgnoreAutoConfiguredSkipPatternsIntegrationTests.Config.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {
|
||||
"management.endpoints.web.exposure.include:*",
|
||||
"server.servlet.context-path:/context-path",
|
||||
"spring.sleuth.http.legacy.enabled:true",
|
||||
"spring.sleuth.web.ignoreAutoConfiguredSkipPatterns:true" })
|
||||
public class IgnoreAutoConfiguredSkipPatternsIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
ArrayListSpanReporter accumulator;
|
||||
|
||||
@Autowired
|
||||
Tracer tracer;
|
||||
|
||||
@LocalServerPort
|
||||
int port;
|
||||
|
||||
@Before
|
||||
@After
|
||||
public void clearSpans() {
|
||||
this.accumulator.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_sample_actuator_endpoint_when_override_pattern_is_true() {
|
||||
new RestTemplate().getForObject(
|
||||
"http://localhost:" + this.port + "/context-path/actuator/health",
|
||||
String.class);
|
||||
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
then(this.accumulator.getSpans()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_sample_non_actuator_endpoint_when_override_pattern_is_true() {
|
||||
new RestTemplate().getForObject(
|
||||
"http://localhost:" + this.port + "/context-path/something",
|
||||
String.class);
|
||||
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
then(this.accumulator.getSpans()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_sample_default_skip_patterns_when_override_pattern_is_true() {
|
||||
new RestTemplate().getForObject(
|
||||
"http://localhost:" + this.port + "/context-path/index.html",
|
||||
String.class);
|
||||
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
then(this.accumulator.getSpans()).hasSize(0);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration(exclude = RabbitAutoConfiguration.class)
|
||||
@Configuration
|
||||
@DisableSecurity
|
||||
@RestController
|
||||
public static class Config {
|
||||
|
||||
@GetMapping("something")
|
||||
void doNothing() {
|
||||
}
|
||||
|
||||
@GetMapping("index.html")
|
||||
void html() {
|
||||
}
|
||||
|
||||
@Bean
|
||||
ArrayListSpanReporter reporter() {
|
||||
return new ArrayListSpanReporter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Sampler sampler() {
|
||||
return Sampler.ALWAYS_SAMPLE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,10 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web.issues.issue971;
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import brave.Tracer;
|
||||
import brave.sampler.Sampler;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -37,11 +39,11 @@ import org.springframework.web.client.RestTemplate;
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = DemoSleuthSkipApplicationTests.Config.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {
|
||||
@SpringBootTest(classes = SkipEndPointsIntegrationTestsWithContextPathWithBasePath.Config.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {
|
||||
"management.endpoints.web.exposure.include:*",
|
||||
"server.servlet.context-path:/context-path",
|
||||
"spring.sleuth.http.legacy.enabled:true" })
|
||||
public class DemoSleuthSkipApplicationTests {
|
||||
public class SkipEndPointsIntegrationTestsWithContextPathWithBasePath {
|
||||
|
||||
@Autowired
|
||||
ArrayListSpanReporter accumulator;
|
||||
@@ -52,6 +54,12 @@ public class DemoSleuthSkipApplicationTests {
|
||||
@LocalServerPort
|
||||
int port;
|
||||
|
||||
@Before
|
||||
@After
|
||||
public void clearSpans() {
|
||||
this.accumulator.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_sample_skipped_endpoint_with_context_path() {
|
||||
new RestTemplate().getForObject(
|
||||
@@ -0,0 +1,118 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.cloud.sleuth.DisableSecurity;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import brave.Tracer;
|
||||
import brave.sampler.Sampler;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath.Config.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {
|
||||
"management.endpoints.web.exposure.include:*",
|
||||
"server.servlet.context-path:/context-path",
|
||||
"spring.sleuth.http.legacy.enabled:true",
|
||||
"management.endpoints.web.base-path:/" })
|
||||
public class SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath {
|
||||
|
||||
@Autowired
|
||||
private ArrayListSpanReporter spanReporter;
|
||||
|
||||
@Autowired
|
||||
private Tracer tracer;
|
||||
|
||||
@LocalServerPort
|
||||
int port;
|
||||
|
||||
@Before
|
||||
@After
|
||||
public void clearSpans() {
|
||||
this.spanReporter.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_sample_non_actuator_endpoint_with_context_path() {
|
||||
new RestTemplate().getForObject(
|
||||
"http://localhost:" + this.port + "/context-path/something",
|
||||
String.class);
|
||||
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
then(this.spanReporter.getSpans()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_sample_non_actuator_endpoint_with_context_path_and_health_in_path() {
|
||||
new RestTemplate().getForObject(
|
||||
"http://localhost:" + this.port + "/context-path/healthcare",
|
||||
String.class);
|
||||
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
then(this.spanReporter.getSpans()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_sample_actuator_endpoint_with_base_path_set_to_root() {
|
||||
new RestTemplate().getForObject(
|
||||
"http://localhost:" + this.port + "/context-path/health", String.class);
|
||||
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
then(this.spanReporter.getSpans()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_sample_actuator_endpoint_with_base_path_set_to_root_and_parameter() {
|
||||
new RestTemplate().getForObject(
|
||||
"http://localhost:" + this.port + "/context-path/metrics?xyz",
|
||||
String.class);
|
||||
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
then(this.spanReporter.getSpans()).hasSize(0);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration(exclude = RabbitAutoConfiguration.class)
|
||||
@Configuration
|
||||
@DisableSecurity
|
||||
@RestController
|
||||
public static class Config {
|
||||
|
||||
@GetMapping("something")
|
||||
void doNothing() {
|
||||
}
|
||||
|
||||
@GetMapping("healthcare")
|
||||
void healthCare() {
|
||||
}
|
||||
|
||||
@GetMapping("metrics")
|
||||
void metrics() {
|
||||
}
|
||||
|
||||
@Bean
|
||||
ArrayListSpanReporter reporter() {
|
||||
return new ArrayListSpanReporter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Sampler sampler() {
|
||||
return Sampler.ALWAYS_SAMPLE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.cloud.sleuth.DisableSecurity;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import brave.Tracer;
|
||||
import brave.sampler.Sampler;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath.Config.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {
|
||||
"management.endpoints.web.exposure.include:*",
|
||||
"spring.sleuth.http.legacy.enabled:true" })
|
||||
public class SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath {
|
||||
|
||||
@Autowired
|
||||
private ArrayListSpanReporter spanReporter;
|
||||
|
||||
@Autowired
|
||||
private Tracer tracer;
|
||||
|
||||
@LocalServerPort
|
||||
int port;
|
||||
|
||||
@Before
|
||||
@After
|
||||
public void clearSpans() {
|
||||
this.spanReporter.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_sample_non_actuator_endpoint() {
|
||||
new RestTemplate().getForObject("http://localhost:" + this.port + "/something",
|
||||
String.class);
|
||||
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
then(this.spanReporter.getSpans()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_sample_non_actuator_endpoint_and_healthcare_in_path() {
|
||||
new RestTemplate().getForObject("http://localhost:" + this.port + "/healthcare",
|
||||
String.class);
|
||||
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
then(this.spanReporter.getSpans()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_sample_actuator_endpoint() {
|
||||
new RestTemplate().getForObject(
|
||||
"http://localhost:" + this.port + "/actuator/health", String.class);
|
||||
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
then(this.spanReporter.getSpans()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_sample_actuator_endpoint_with_parameter() {
|
||||
new RestTemplate().getForObject(
|
||||
"http://localhost:" + this.port + "/actuator/metrics?xyz", String.class);
|
||||
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
then(this.spanReporter.getSpans()).hasSize(0);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration(exclude = RabbitAutoConfiguration.class)
|
||||
@Configuration
|
||||
@DisableSecurity
|
||||
@RestController
|
||||
public static class Config {
|
||||
|
||||
@GetMapping("something")
|
||||
void doNothing() {
|
||||
}
|
||||
|
||||
@GetMapping("healthcare")
|
||||
void healthCare() {
|
||||
}
|
||||
|
||||
@GetMapping("metrics")
|
||||
void metrics() {
|
||||
}
|
||||
|
||||
@Bean
|
||||
ArrayListSpanReporter reporter() {
|
||||
return new ArrayListSpanReporter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Sampler sampler() {
|
||||
return Sampler.ALWAYS_SAMPLE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.cloud.sleuth.DisableSecurity;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import brave.Tracer;
|
||||
import brave.sampler.Sampler;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath.Config.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {
|
||||
"management.endpoints.web.exposure.include:*",
|
||||
"spring.sleuth.http.legacy.enabled:true",
|
||||
"management.endpoints.web.base-path:/" })
|
||||
public class SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath {
|
||||
|
||||
@Autowired
|
||||
private ArrayListSpanReporter spanReporter;
|
||||
|
||||
@Autowired
|
||||
private Tracer tracer;
|
||||
|
||||
@LocalServerPort
|
||||
int port;
|
||||
|
||||
@Before
|
||||
@After
|
||||
public void clearSpans() {
|
||||
this.spanReporter.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_sample_non_actuator_endpoint() {
|
||||
new RestTemplate().getForObject("http://localhost:" + this.port + "/something",
|
||||
String.class);
|
||||
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
then(this.spanReporter.getSpans()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_sample_non_actuator_endpoint_with_healthcare_in_path() {
|
||||
new RestTemplate().getForObject("http://localhost:" + this.port + "/healthcare",
|
||||
String.class);
|
||||
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
then(this.spanReporter.getSpans()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_sample_actuator_endpoint() {
|
||||
new RestTemplate().getForObject("http://localhost:" + this.port + "/health",
|
||||
String.class);
|
||||
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
then(this.spanReporter.getSpans()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_sample_actuator_endpoint_with_parameter() {
|
||||
new RestTemplate().getForObject("http://localhost:" + this.port + "/metrics?xyz",
|
||||
String.class);
|
||||
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
then(this.spanReporter.getSpans()).hasSize(0);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration(exclude = RabbitAutoConfiguration.class)
|
||||
@Configuration
|
||||
@DisableSecurity
|
||||
@RestController
|
||||
public static class Config {
|
||||
|
||||
@GetMapping("something")
|
||||
void doNothing() {
|
||||
}
|
||||
|
||||
@GetMapping("healthcare")
|
||||
void healthCare() {
|
||||
}
|
||||
|
||||
@GetMapping("metrics")
|
||||
void metrics() {
|
||||
}
|
||||
|
||||
@Bean
|
||||
ArrayListSpanReporter reporter() {
|
||||
return new ArrayListSpanReporter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Sampler sampler() {
|
||||
return Sampler.ALWAYS_SAMPLE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,6 +17,8 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -24,6 +26,10 @@ import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointId;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointsSupplier;
|
||||
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebOperation;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
@@ -79,27 +85,103 @@ public class SkipPatternProviderConfigTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_return_empty_when_server_props_have_no_context_path()
|
||||
throws Exception {
|
||||
Optional<Pattern> pattern = new TraceWebAutoConfiguration.ServerSkipPatternProviderConfig()
|
||||
.skipPatternForServerProperties(new ServerProperties(),
|
||||
new WebEndpointProperties())
|
||||
public void should_return_empty_when_no_endpoints() {
|
||||
EndpointsSupplier<ExposableWebEndpoint> endpointsSupplier = Collections::emptyList;
|
||||
Optional<Pattern> pattern = new TraceWebAutoConfiguration.ActuatorSkipPatternProviderConfig()
|
||||
.skipPatternForActuatorEndpoints(new ServerProperties(),
|
||||
new WebEndpointProperties(), endpointsSupplier)
|
||||
.skipPattern();
|
||||
|
||||
then(pattern).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_return_server_props_with_context_path() throws Exception {
|
||||
public void should_return_endpoints_without_context_path() {
|
||||
ServerProperties properties = new ServerProperties();
|
||||
properties.getServlet().setContextPath("foo");
|
||||
WebEndpointProperties webEndpointProperties = new WebEndpointProperties();
|
||||
EndpointsSupplier<ExposableWebEndpoint> endpointsSupplier = () -> {
|
||||
ExposableWebEndpoint infoEndpoint = createEndpoint("info");
|
||||
ExposableWebEndpoint healthEndpoint = createEndpoint("health");
|
||||
|
||||
Optional<Pattern> pattern = new TraceWebAutoConfiguration.ServerSkipPatternProviderConfig()
|
||||
.skipPatternForServerProperties(properties, new WebEndpointProperties())
|
||||
return Arrays.asList(infoEndpoint, healthEndpoint);
|
||||
};
|
||||
|
||||
Optional<Pattern> pattern = new TraceWebAutoConfiguration.ActuatorSkipPatternProviderConfig()
|
||||
.skipPatternForActuatorEndpoints(properties, webEndpointProperties,
|
||||
endpointsSupplier)
|
||||
.skipPattern();
|
||||
|
||||
then(pattern).isNotEmpty();
|
||||
then(pattern.get().pattern()).isEqualTo("foo/actuator.*");
|
||||
then(pattern.get().pattern())
|
||||
.isEqualTo("/actuator/(info|info/.*|health|health/.*)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_return_endpoints_with_context_path() {
|
||||
WebEndpointProperties webEndpointProperties = new WebEndpointProperties();
|
||||
ServerProperties properties = new ServerProperties();
|
||||
properties.getServlet().setContextPath("foo");
|
||||
|
||||
EndpointsSupplier<ExposableWebEndpoint> endpointsSupplier = () -> {
|
||||
ExposableWebEndpoint infoEndpoint = createEndpoint("info");
|
||||
ExposableWebEndpoint healthEndpoint = createEndpoint("health");
|
||||
|
||||
return Arrays.asList(infoEndpoint, healthEndpoint);
|
||||
};
|
||||
|
||||
Optional<Pattern> pattern = new TraceWebAutoConfiguration.ActuatorSkipPatternProviderConfig()
|
||||
.skipPatternForActuatorEndpoints(properties, webEndpointProperties,
|
||||
endpointsSupplier)
|
||||
.skipPattern();
|
||||
|
||||
then(pattern).isNotEmpty();
|
||||
then(pattern.get().pattern())
|
||||
.isEqualTo("foo/actuator/(info|info/.*|health|health/.*)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_return_endpoints_without_context_path_and_base_path_set_to_root() {
|
||||
ServerProperties properties = new ServerProperties();
|
||||
WebEndpointProperties webEndpointProperties = new WebEndpointProperties();
|
||||
webEndpointProperties.setBasePath("/");
|
||||
|
||||
EndpointsSupplier<ExposableWebEndpoint> endpointsSupplier = () -> {
|
||||
ExposableWebEndpoint infoEndpoint = createEndpoint("info");
|
||||
ExposableWebEndpoint healthEndpoint = createEndpoint("health");
|
||||
|
||||
return Arrays.asList(infoEndpoint, healthEndpoint);
|
||||
};
|
||||
|
||||
Optional<Pattern> pattern = new TraceWebAutoConfiguration.ActuatorSkipPatternProviderConfig()
|
||||
.skipPatternForActuatorEndpoints(properties, webEndpointProperties,
|
||||
endpointsSupplier)
|
||||
.skipPattern();
|
||||
|
||||
then(pattern).isNotEmpty();
|
||||
then(pattern.get().pattern()).isEqualTo("/(info|info/.*|health|health/.*)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_return_endpoints_with_context_path_and_base_path_set_to_root() {
|
||||
WebEndpointProperties webEndpointProperties = new WebEndpointProperties();
|
||||
webEndpointProperties.setBasePath("/");
|
||||
ServerProperties properties = new ServerProperties();
|
||||
properties.getServlet().setContextPath("foo");
|
||||
|
||||
EndpointsSupplier<ExposableWebEndpoint> endpointsSupplier = () -> {
|
||||
ExposableWebEndpoint infoEndpoint = createEndpoint("info");
|
||||
ExposableWebEndpoint healthEndpoint = createEndpoint("health");
|
||||
|
||||
return Arrays.asList(infoEndpoint, healthEndpoint);
|
||||
};
|
||||
|
||||
Optional<Pattern> pattern = new TraceWebAutoConfiguration.ActuatorSkipPatternProviderConfig()
|
||||
.skipPatternForActuatorEndpoints(properties, webEndpointProperties,
|
||||
endpointsSupplier)
|
||||
.skipPattern();
|
||||
|
||||
then(pattern).isNotEmpty();
|
||||
then(pattern.get().pattern()).isEqualTo("foo/(info|info/.*|health|health/.*)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -120,4 +202,29 @@ public class SkipPatternProviderConfigTest {
|
||||
return () -> Optional.of(Pattern.compile("bar"));
|
||||
}
|
||||
|
||||
private ExposableWebEndpoint createEndpoint(final String name) {
|
||||
return new ExposableWebEndpoint() {
|
||||
|
||||
@Override
|
||||
public String getRootPath() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EndpointId getEndpointId() {
|
||||
return EndpointId.of(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnableByDefault() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<WebOperation> getOperations() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -48,8 +48,7 @@ public class TraceRequestHttpHeadersFilterTests {
|
||||
.isEqualTo(Collections.singletonList("World"));
|
||||
BDDAssertions.then(filteredHeaders.get("X-Hello-Request"))
|
||||
.isEqualTo(Collections.singletonList("Request World"));
|
||||
BDDAssertions.then(filteredHeaders.get("X-Auth-User"))
|
||||
.hasSize(1);
|
||||
BDDAssertions.then(filteredHeaders.get("X-Auth-User")).hasSize(1);
|
||||
BDDAssertions
|
||||
.then((Object) exchange
|
||||
.getAttribute(TraceRequestHttpHeadersFilter.SPAN_ATTRIBUTE))
|
||||
|
||||
Reference in New Issue
Block a user