Skip to content

Commit

Permalink
Register skipPattern for SB actuator endpoints dynamically. (#100)
Browse files Browse the repository at this point in the history
  • Loading branch information
ask4gilles authored and geoand committed Apr 17, 2019
1 parent e043077 commit 6cae6b8
Show file tree
Hide file tree
Showing 16 changed files with 1,132 additions and 66 deletions.
6 changes: 6 additions & 0 deletions opentracing-spring-web-starter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>io.opentracing</groupId>
<artifactId>opentracing-mock</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
Expand Down Expand Up @@ -47,22 +49,27 @@
/**
* @author Pavol Loffay
* @author Eddú Meléndez
* @author Gilles Robert
*/
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnBean(Tracer.class)
@AutoConfigureAfter(TracerAutoConfiguration.class)
@AutoConfigureAfter({TracerAutoConfiguration.class, SkipPatternAutoConfiguration.class})
@EnableConfigurationProperties(WebTracingProperties.class)
@ConditionalOnClass(WebMvcConfigurerAdapter.class)
@ConditionalOnProperty(name = "opentracing.spring.web.enabled", havingValue = "true", matchIfMissing = true)
public class ServerTracingAutoConfiguration {

private static final Log log = LogFactory.getLog(ServerTracingAutoConfiguration.class);

private final Pattern skipPattern;
private final ObjectProvider<List<ServletFilterSpanDecorator>> servletFilterSpanDecorator;
private final ObjectProvider<List<HandlerInterceptorSpanDecorator>> interceptorSpanDecorator;

public ServerTracingAutoConfiguration(ObjectProvider<List<ServletFilterSpanDecorator>> servletFilterSpanDecorator,
public ServerTracingAutoConfiguration(@Qualifier("skipPattern") Pattern skipPattern,
ObjectProvider<List<ServletFilterSpanDecorator>> servletFilterSpanDecorator,
ObjectProvider<List<HandlerInterceptorSpanDecorator>> interceptorSpanDecorator) {
this.skipPattern = skipPattern;
this.servletFilterSpanDecorator = servletFilterSpanDecorator;
this.interceptorSpanDecorator = interceptorSpanDecorator;
}
Expand All @@ -72,17 +79,16 @@ public ServerTracingAutoConfiguration(ObjectProvider<List<ServletFilterSpanDecor
public FilterRegistrationBean tracingFilter(Tracer tracer, WebTracingProperties tracingConfiguration) {
log.info(format("Creating %s bean with %s mapped to %s, skip pattern is \"%s\"",
FilterRegistrationBean.class.getSimpleName(), TracingFilter.class.getSimpleName(),
tracingConfiguration.getUrlPatterns().toString(),
tracingConfiguration.getSkipPattern()));
tracingConfiguration.getUrlPatterns().toString(), skipPattern));

List<ServletFilterSpanDecorator> decorators = servletFilterSpanDecorator.getIfAvailable();
if (CollectionUtils.isEmpty(decorators)) {
decorators = Collections.singletonList(ServletFilterSpanDecorator.STANDARD_TAGS);
}

TracingFilter tracingFilter = new TracingFilter(tracer, decorators, tracingConfiguration.getSkipPattern());
TracingFilter tracingFilter = new TracingFilter(tracer, decorators, skipPattern);

FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(tracingFilter);
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(tracingFilter);
filterRegistrationBean.setUrlPatterns(tracingConfiguration.getUrlPatterns());
filterRegistrationBean.setOrder(tracingConfiguration.getOrder());
filterRegistrationBean.setAsyncSupported(true);
Expand All @@ -106,8 +112,6 @@ public void addInterceptors(InterceptorRegistry registry) {
}

registry.addInterceptor(new TracingHandlerInterceptor(tracer, decorators));

super.addInterceptors(registry);
}
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/**
* Copyright 2016-2019 The OpenTracing 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 io.opentracing.contrib.spring.web.starter;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.web.server.ConditionalOnManagementPort;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
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.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

import io.opentracing.contrib.spring.web.webfilter.SkipPattern;

/**
* @author Gilles Robert
*/
@Configuration
@ConditionalOnProperty(name = "opentracing.spring.web.enabled", havingValue = "true", matchIfMissing = true)
public class SkipPatternAutoConfiguration {

@Autowired(required = false)
private List<SkipPattern> patterns = new ArrayList<>();

@Bean(name = "skipPattern")
public Pattern skipPattern() {
return Pattern.compile(this.patterns
.stream()
.map(SkipPattern::pattern)
.filter(Optional::isPresent).map(Optional::get)
.map(Pattern::pattern)
.collect(Collectors.joining("|")));
}

@Configuration
@ConditionalOnClass(ManagementServerProperties.class)
@ConditionalOnProperty(value = "opentracing.spring.web.ignoreAutoConfiguredSkipPatterns", havingValue = "false", matchIfMissing = true)
protected static class ManagementSkipPatternProviderConfig {

static Optional<Pattern> getPatternForManagementServerProperties(
ManagementServerProperties managementServerProperties) {
String contextPath = managementServerProperties.getServlet().getContextPath();
if (StringUtils.hasText(contextPath)) {
return Optional.of(Pattern.compile(contextPath + ".*"));
}
return Optional.empty();
}

@Bean
@ConditionalOnBean(ManagementServerProperties.class)
public SkipPattern skipPatternForManagementServerProperties(
final ManagementServerProperties managementServerProperties) {
return () -> getPatternForManagementServerProperties(managementServerProperties);
}
}

@Configuration
@ConditionalOnClass( {ServerProperties.class, EndpointsSupplier.class, ExposableWebEndpoint.class})
@ConditionalOnBean(ServerProperties.class)
@ConditionalOnProperty(value = "opentracing.spring.web.ignoreAutoConfiguredSkipPatterns", havingValue = "false", matchIfMissing = true)
protected static class ActuatorSkipPatternProviderConfig {

static Optional<Pattern> getEndpointsPatterns(String contextPath,
WebEndpointProperties webEndpointProperties,
EndpointsSupplier<ExposableWebEndpoint> endpointsSupplier) {
Collection<ExposableWebEndpoint> endpoints = endpointsSupplier.getEndpoints();

if (endpoints.isEmpty()) {
return Optional.empty();
}

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
@ConditionalOnManagementPort(ManagementPortType.SAME)
public SkipPattern skipPatternForActuatorEndpointsSamePort(
final ServerProperties serverProperties,
final WebEndpointProperties webEndpointProperties,
final EndpointsSupplier<ExposableWebEndpoint> endpointsSupplier) {
return () -> getEndpointsPatterns(
serverProperties.getServlet().getContextPath(), webEndpointProperties,
endpointsSupplier);
}

@Bean
@ConditionalOnManagementPort(ManagementPortType.DIFFERENT)
@ConditionalOnProperty(name = "management.server.servlet.context-path", havingValue = "/", matchIfMissing = true)
public SkipPattern skipPatternForActuatorEndpointsDifferentPort(
final WebEndpointProperties webEndpointProperties,
final EndpointsSupplier<ExposableWebEndpoint> endpointsSupplier) {
return () -> getEndpointsPatterns(null, webEndpointProperties,
endpointsSupplier);
}
}

@Configuration
protected static class DefaultSkipPatternConfig {

private static String combinedPatterns(String skipPattern) {
String pattern = skipPattern;
if (!StringUtils.hasText(skipPattern)) {
pattern = WebTracingProperties.DEFAULT_SKIP_PATTERN;
}
return pattern;
}

@Bean
SkipPattern defaultSkipPatternBean(WebTracingProperties webTracingProperties) {
return () -> Optional.of(Pattern.compile(combinedPatterns(webTracingProperties.getSkipPattern())));
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
*/
package io.opentracing.contrib.spring.web.starter;

import io.opentracing.Tracer;
import io.opentracing.contrib.spring.tracer.configuration.TracerAutoConfiguration;
import io.opentracing.contrib.spring.web.webfilter.TracingWebFilter;
import io.opentracing.contrib.spring.web.webfilter.WebFluxSpanDecorator;
import java.util.List;
import java.util.regex.Pattern;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
Expand All @@ -27,20 +27,25 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;
import io.opentracing.Tracer;
import io.opentracing.contrib.spring.tracer.configuration.TracerAutoConfiguration;
import io.opentracing.contrib.spring.web.webfilter.TracingWebFilter;
import io.opentracing.contrib.spring.web.webfilter.WebFluxSpanDecorator;

/**
* Instrumentation of WebFlux.
*
* @author Csaba Kos
* @author Gilles Robert
*/
@Configuration
@ConditionalOnBean(Tracer.class)
@AutoConfigureAfter(TracerAutoConfiguration.class)
@AutoConfigureAfter({TracerAutoConfiguration.class, SkipPatternAutoConfiguration.class})
@EnableConfigurationProperties(WebTracingProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@ConditionalOnProperty(name = WebTracingProperties.CONFIGURATION_PREFIX + ".enabled", havingValue = "true", matchIfMissing = true)
public class WebFluxTracingAutoConfiguration {

@ConditionalOnMissingBean(WebFluxSpanDecorator.class)
@Configuration
static class DefaultWebFluxSpanDecorators {
Expand All @@ -60,12 +65,13 @@ WebFluxSpanDecorator webFluxTagsWebFluxSpanDecorator() {
public TracingWebFilter traceFilter(
final Tracer tracer,
final WebTracingProperties webTracingProperties,
final ObjectProvider<List<WebFluxSpanDecorator>> webFilterSpanDecorators
final ObjectProvider<List<WebFluxSpanDecorator>> webFilterSpanDecorators,
final @Qualifier("skipPattern") Pattern skipPattern
) {
return new TracingWebFilter(
tracer,
webTracingProperties.getOrder(),
webTracingProperties.getSkipPattern(),
skipPattern,
webTracingProperties.getUrlPatterns(),
webFilterSpanDecorators.getObject()
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,24 @@
*/
package io.opentracing.contrib.spring.web.starter;

import java.util.*;
import java.util.regex.Pattern;
import java.util.Collections;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;

/**
* @author Pavol Loffay
* @author Gilles Robert
*/
@ConfigurationProperties(WebTracingProperties.CONFIGURATION_PREFIX)
public class WebTracingProperties {

public static final String CONFIGURATION_PREFIX = "opentracing.spring.web";

public static final Pattern DEFAULT_SKIP_PATTERN = Pattern.compile(
"/api-docs.*|/autoconfig|/configprops|/dump|/health|/info|/metrics.*|/actuator.*|" +
"/mappings|/swagger.*|.*\\.png|.*\\.css|.*\\.js|.*\\.html|/favicon.ico|/hystrix.stream");
static final String DEFAULT_SKIP_PATTERN = "/api-docs.*|/swagger.*|.*\\.png|.*\\.css|.*\\.js|.*\\.html|/favicon.ico|/hystrix.stream";

private boolean enabled = true;
private Pattern skipPattern = DEFAULT_SKIP_PATTERN;
private String skipPattern = DEFAULT_SKIP_PATTERN;
private int order = Integer.MIN_VALUE;

/**
Expand All @@ -54,11 +53,11 @@ public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

public Pattern getSkipPattern() {
public String getSkipPattern() {
return skipPattern;
}

public void setSkipPattern(Pattern skipPattern) {
public void setSkipPattern(String skipPattern) {
this.skipPattern = skipPattern;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
io.opentracing.contrib.spring.web.starter.ServerTracingAutoConfiguration,\
io.opentracing.contrib.spring.web.starter.SkipPatternAutoConfiguration,\
io.opentracing.contrib.spring.web.starter.RestTemplateTracingAutoConfiguration,\
io.opentracing.contrib.spring.web.starter.WebClientTracingAutoConfiguration,\
io.opentracing.contrib.spring.web.starter.WebFluxTracingAutoConfiguration
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.hamcrest.core.IsEqual;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -174,6 +175,7 @@ public void testAsyncRestClientCustomTracing() {
Assert.assertEquals("foo", mockTracer.finishedSpans().get(0).tags().get("custom-test"));
}

@Ignore("Fix me, I'm flaky!")
@Test
public void testWebClientCustomTracing() {
try {
Expand Down
Loading

0 comments on commit 6cae6b8

Please sign in to comment.