Updates to latest Sampling infrastructure (2.1.x) (#1461)

* Updates to latest Sampling infrastructure (2.1.x)

This adapts #1456 for the 2.1.x branch
This commit is contained in:
Adrian Cole
2019-10-08 16:47:25 -07:00
committed by Marcin Grzejszczak
parent ac7dd52544
commit aa533bc4cb
26 changed files with 815 additions and 103 deletions

View File

@@ -34,7 +34,7 @@
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<spring-boot.version>2.1.7.RELEASE</spring-boot.version>
<brave.version>5.7.0</brave.version>
<brave.version>5.8.0</brave.version>
</properties>
<dependencyManagement>

View File

@@ -845,21 +845,23 @@ Sleuth will search for beans of those types and automatically apply customizatio
=== HTTP
If a customization of client / server parsing of the HTTP related spans is required,
just register a bean of type `brave.http.HttpClientParser` or
If a customization of client / server parsing of the HTTP related spans is
required, just register a bean of type `brave.http.HttpClientParser` or
`brave.http.HttpServerParser`. If client /server sampling is required, just
register a bean of type `brave.http.HttpSampler` and name the bean
`sleuthClientSampler` for client sampler and `sleuthServerSampler` for server sampler.
For your convenience the `@ClientSampler` and `@ServerSampler`
annotations can be used to inject the proper beans or to
reference the bean names via their static String `NAME` fields.
register a bean of type `brave.sampler.SamplerFunction<HttpRequest>` and name
the bean `sleuthHttpClientSampler` for client sampler and
`sleuthHttpServerSampler` for server sampler.
For your convenience the `@HttpClientSampler` and `@HttpServerSampler`
annotations can be used to inject the proper beans or to reference the bean
names via their static String `NAME` fields.
Check out Brave's code to see an example of how to make a path-based sampler
https://github.com/openzipkin/brave/tree/master/instrumentation/http#sampling-policy
If you want to completely rewrite the `HttpTracing` bean you can use the `SkipPatternProvider`
interface to retrieve the URL `Pattern` for spans that should be not sampled. Below you can see
an example of usage of `SkipPatternProvider` inside a server side, `HttpSampler`.
an example of usage of `SkipPatternProvider` inside a server side, `Sampler<HttpRequest>`.
[source,java]
----
@@ -881,6 +883,34 @@ In the following example, we register the `TracingFilter` bean, add the `ZIPKIN-
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterIntegrationTests.java[tags=response_headers,indent=0]
----
=== RPC
Sleuth automatically configures the `RpcTracing` bean which serves as a
foundation for RPC instrumentation such as gRPC or Dubbo.
If a customization of client / server sampling of the RPC traces is required,
just register a bean of type `brave.sampler.SamplerFunction<RpcRequest>` and
name the bean `sleuthRpcClientSampler` for client sampler and
`sleuthRpcServerSampler` for server sampler.
For your convenience the `@RpcClientSampler` and `@RpcServerSampler`
annotations can be used to inject the proper beans or to reference the bean
names via their static String `NAME` fields.
Ex. Here's a sampler that traces 100 "GetUserToken" server requests per second.
This doesn't start new traces for requests to the health check service. Other
requests will use the global sampling configuration.
[source,java]
----
@Configuration
class Config {
include::../../../../spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfigurationIntegrationTests.java[tags=custom_rpc_server_sampler,indent=2]
}
----
For more, see https://github.com/openzipkin/brave/tree/master/instrumentation/rpc#sampling-policy
=== Custom service name
By default, Sleuth assumes that, when you send a span to Zipkin, you want the span's service name to be equal to the value of the `spring.application.name` property.
@@ -1119,13 +1149,13 @@ To change the order of tracing filter registration, please set the
==== Dubbo RPC support
Via the integration with Brave, Spring Cloud Sleuth supports https://dubbo.apache.org/[Dubbo].
It's enough to add the `brave-instrumentation-dubbo-rpc` dependency:
It's enough to add the `brave-instrumentation-dubbo` dependency:
[source,xml,indent=0]
----
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-dubbo-rpc</artifactId>
<artifactId>brave-instrumentation-dubbo</artifactId>
</dependency>
----

View File

@@ -265,7 +265,7 @@
<spring-cloud-netflix.version>2.1.4.BUILD-SNAPSHOT</spring-cloud-netflix.version>
<spring-cloud-openfeign.version>2.1.4.BUILD-SNAPSHOT
</spring-cloud-openfeign.version>
<brave.version>5.7.0</brave.version>
<brave.version>5.8.0</brave.version>
<spring-security-boot-autoconfigure.version>2.1.2.RELEASE
</spring-security-boot-autoconfigure.version>

View File

@@ -197,6 +197,10 @@
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-context-log4j2</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-rpc</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-spring-web</artifactId>

View File

@@ -19,15 +19,17 @@ package org.springframework.cloud.sleuth.instrument.grpc;
import java.util.List;
import java.util.Optional;
import brave.Tracing;
import brave.grpc.GrpcTracing;
import brave.rpc.RpcTracing;
import io.grpc.ServerInterceptor;
import org.lognet.springboot.grpc.GRpcGlobalInterceptor;
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.ConditionalOnProperty;
import org.springframework.cloud.sleuth.instrument.rpc.TraceRpcAutoConfiguration;
import org.springframework.context.annotation.Bean;
/**
@@ -41,12 +43,13 @@ import org.springframework.context.annotation.Bean;
*/
@ConditionalOnClass({ GrpcTracing.class, GRpcGlobalInterceptor.class })
@ConditionalOnProperty(value = "spring.sleuth.grpc.enabled", matchIfMissing = true)
@ConditionalOnBean(Tracing.class)
@ConditionalOnBean(RpcTracing.class)
@AutoConfigureAfter(TraceRpcAutoConfiguration.class)
public class TraceGrpcAutoConfiguration {
@Bean
public GrpcTracing grpcTracing(Tracing tracing) {
return GrpcTracing.create(tracing);
public GrpcTracing grpcTracing(RpcTracing rpcTracing) {
return GrpcTracing.create(rpcTracing);
}
// Register a global interceptor for both the server

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2013-2019 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
*
* https://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.rpc;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import brave.sampler.SamplerFunction;
import org.springframework.beans.factory.annotation.Qualifier;
/**
* Annotate a client {@link brave.sampler.SamplerFunction} that should be injected to
* {@link brave.rpc.RpcTracing.Builder#clientSampler(SamplerFunction)}.
*
* @since 2.2.0
* @see Qualifier
*/
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Qualifier(RpcClientSampler.NAME)
public @interface RpcClientSampler {
/**
* Default name for RPC client sampler.
*/
String NAME = "sleuthRpcClientSampler";
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2013-2019 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
*
* https://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.rpc;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import brave.sampler.SamplerFunction;
import org.springframework.beans.factory.annotation.Qualifier;
/**
* Annotate a server {@link brave.sampler.SamplerFunction} that should be injected to
* {@link brave.rpc.RpcTracing.Builder#serverSampler(SamplerFunction)}.
*
* @since 2.2.0
* @see Qualifier
*/
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Qualifier(RpcServerSampler.NAME)
public @interface RpcServerSampler {
/**
* Default name for RPC server sampler.
*/
String NAME = "sleuthRpcServerSampler";
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2013-2019 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
*
* https://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.rpc;
import java.util.ArrayList;
import java.util.List;
import brave.Tracing;
import brave.rpc.RpcRequest;
import brave.rpc.RpcTracing;
import brave.rpc.RpcTracingCustomizer;
import brave.sampler.SamplerFunction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} related to RPC based communication.
*
* @since 2.2.0
*/
@Configuration
@ConditionalOnProperty(name = "spring.sleuth.rpc.enabled", havingValue = "true", matchIfMissing = true)
@ConditionalOnBean(Tracing.class)
@AutoConfigureAfter(TraceAutoConfiguration.class)
public class TraceRpcAutoConfiguration {
@Autowired(required = false)
List<RpcTracingCustomizer> rpcTracingCustomizers = new ArrayList<>();
@Bean
@ConditionalOnMissingBean
// NOTE: stable bean name as might be used outside sleuth
RpcTracing rpcTracing(Tracing tracing,
@Nullable @RpcClientSampler SamplerFunction<RpcRequest> clientSampler,
@Nullable @RpcServerSampler SamplerFunction<RpcRequest> serverSampler) {
RpcTracing.Builder builder = RpcTracing.newBuilder(tracing);
if (clientSampler != null) {
builder.clientSampler(clientSampler);
}
if (serverSampler != null) {
builder.serverSampler(serverSampler);
}
for (RpcTracingCustomizer customizer : this.rpcTracingCustomizers) {
customizer.customize(builder);
}
return builder.build();
}
}

View File

@@ -32,6 +32,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
* @author Marcin Grzejszczak
* @since 2.0.0
* @see Qualifier
* @deprecated Since 2.2.0, please use {@link HttpClientSampler}
*/
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
ElementType.ANNOTATION_TYPE })
@@ -39,6 +40,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
@Inherited
@Documented
@Qualifier(ClientSampler.NAME)
@Deprecated
public @interface ClientSampler {
/**

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2013-2019 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
*
* https://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.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import brave.sampler.SamplerFunction;
import org.springframework.beans.factory.annotation.Qualifier;
/**
* Annotate a client {@link brave.sampler.SamplerFunction} that should be injected to
* {@link brave.http.HttpTracing.Builder#clientSampler(SamplerFunction)}.
*
* @since 2.2.0
* @see Qualifier
*/
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Qualifier(HttpClientSampler.NAME)
public @interface HttpClientSampler {
/**
* Default name for Sleuth HTTP client sampler.
*/
String NAME = "sleuthHttpClientSampler";
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2013-2019 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
*
* https://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.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import brave.sampler.SamplerFunction;
import org.springframework.beans.factory.annotation.Qualifier;
/**
* Annotate a client {@link brave.sampler.SamplerFunction} that should be injected to
* {@link brave.http.HttpTracing.Builder#serverSampler(SamplerFunction)}.
*
* @since 2.2.0
* @see Qualifier
*/
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Qualifier(HttpServerSampler.NAME)
public @interface HttpServerSampler {
/**
* Default name for the Sleuth HTTP server sampler.
*/
String NAME = "sleuthHttpServerSampler";
}

View File

@@ -32,6 +32,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
* @author Marcin Grzejszczak
* @since 2.0.0
* @see Qualifier
* @deprecated Since 2.2.0, please use {@link HttpServerSampler}
*/
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
ElementType.ANNOTATION_TYPE })
@@ -39,6 +40,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
@Inherited
@Documented
@Qualifier(ServerSampler.NAME)
@Deprecated
public @interface ServerSampler {
/**

View File

@@ -18,8 +18,8 @@ package org.springframework.cloud.sleuth.instrument.web;
import java.util.regex.Pattern;
import brave.http.HttpAdapter;
import brave.http.HttpSampler;
import brave.http.HttpRequest;
import brave.sampler.SamplerFunction;
/**
* Doesn't sample a span if skip pattern is matched.
@@ -27,19 +27,19 @@ import brave.http.HttpSampler;
* @author Marcin Grzejszczak
* @since 2.0.0
*/
class SleuthHttpSampler extends HttpSampler {
class SkipPatternHttpServerSampler implements SamplerFunction<HttpRequest> {
private final SkipPatternProvider provider;
private Pattern pattern;
SleuthHttpSampler(SkipPatternProvider provider) {
SkipPatternHttpServerSampler(SkipPatternProvider provider) {
this.provider = provider;
}
@Override
public <Req> Boolean trySample(HttpAdapter<Req, ?> adapter, Req request) {
String url = adapter.path(request);
public Boolean trySample(HttpRequest request) {
String url = request.path();
boolean shouldSkip = pattern().matcher(url).matches();
if (shouldSkip) {
return false;

View File

@@ -21,12 +21,13 @@ import java.util.List;
import brave.ErrorParser;
import brave.Tracing;
import brave.http.HttpAdapter;
import brave.http.HttpClientParser;
import brave.http.HttpRequest;
import brave.http.HttpSampler;
import brave.http.HttpServerParser;
import brave.http.HttpTracing;
import brave.http.HttpTracingCustomizer;
import brave.sampler.SamplerFunction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
@@ -63,22 +64,28 @@ public class TraceHttpAutoConfiguration {
// NOTE: stable bean name as might be used outside sleuth
HttpTracing httpTracing(Tracing tracing, SkipPatternProvider provider,
HttpClientParser clientParser, HttpServerParser serverParser,
@ClientSampler HttpSampler clientSampler,
@Nullable @ServerSampler HttpSampler serverSampler) {
HttpSampler combinedSampler = combineUserProvidedSamplerWithSkipPatternSampler(
serverSampler, provider);
@HttpClientSampler SamplerFunction<HttpRequest> httpClientSampler,
@Nullable @ServerSampler HttpSampler serverSampler,
@Nullable @HttpServerSampler SamplerFunction<HttpRequest> httpServerSampler) {
if (httpServerSampler == null) {
httpServerSampler = serverSampler;
}
SamplerFunction<HttpRequest> combinedSampler = combineUserProvidedSamplerWithSkipPatternSampler(
httpServerSampler, provider);
HttpTracing.Builder builder = HttpTracing.newBuilder(tracing)
.clientParser(clientParser).serverParser(serverParser)
.clientSampler(clientSampler).serverSampler(combinedSampler);
.clientSampler(httpClientSampler).serverSampler(combinedSampler);
for (HttpTracingCustomizer customizer : this.httpTracingCustomizers) {
customizer.customize(builder);
}
return builder.build();
}
private HttpSampler combineUserProvidedSamplerWithSkipPatternSampler(
HttpSampler serverSampler, SkipPatternProvider provider) {
SleuthHttpSampler skipPatternSampler = new SleuthHttpSampler(provider);
private SamplerFunction<HttpRequest> combineUserProvidedSamplerWithSkipPatternSampler(
@Nullable SamplerFunction<HttpRequest> serverSampler,
SkipPatternProvider provider) {
SkipPatternHttpServerSampler skipPatternSampler = new SkipPatternHttpServerSampler(
provider);
if (serverSampler == null) {
return skipPatternSampler;
}
@@ -118,9 +125,14 @@ public class TraceHttpAutoConfiguration {
}
@Bean
@ConditionalOnMissingBean(name = ClientSampler.NAME)
HttpSampler sleuthClientSampler(SleuthWebProperties sleuthWebProperties) {
return new PathMatchingHttpSampler(sleuthWebProperties);
@ConditionalOnMissingBean(name = HttpClientSampler.NAME)
SamplerFunction<HttpRequest> sleuthHttpClientSampler(
@Nullable @ClientSampler HttpSampler sleuthClientSampler,
SleuthWebProperties sleuthWebProperties) {
if (sleuthClientSampler != null) {
return sleuthClientSampler;
}
return new SkipPatternHttpClientSampler(sleuthWebProperties);
}
}
@@ -130,25 +142,26 @@ public class TraceHttpAutoConfiguration {
*
* @author Adrian Cole
*/
class CompositeHttpSampler extends HttpSampler {
class CompositeHttpSampler implements SamplerFunction<HttpRequest> {
private final HttpSampler left;
final SamplerFunction<HttpRequest> left;
private final HttpSampler right;
final SamplerFunction<HttpRequest> right;
CompositeHttpSampler(HttpSampler left, HttpSampler right) {
CompositeHttpSampler(SamplerFunction<HttpRequest> left,
SamplerFunction<HttpRequest> right) {
this.left = left;
this.right = right;
}
@Override
public <Req> Boolean trySample(HttpAdapter<Req, ?> adapter, Req request) {
public Boolean trySample(HttpRequest request) {
// If either decision is false, return false
Boolean leftDecision = this.left.trySample(adapter, request);
Boolean leftDecision = this.left.trySample(request);
if (Boolean.FALSE.equals(leftDecision)) {
return false;
}
Boolean rightDecision = this.right.trySample(adapter, request);
Boolean rightDecision = this.right.trySample(request);
if (Boolean.FALSE.equals(rightDecision)) {
return false;
}
@@ -160,7 +173,7 @@ class CompositeHttpSampler extends HttpSampler {
return leftDecision;
}
// Neither are null and at least one is true
return leftDecision && rightDecision;
return rightDecision;
}
}
@@ -170,17 +183,17 @@ class CompositeHttpSampler extends HttpSampler {
*
* @author Marcin Grzejszczak
*/
class PathMatchingHttpSampler extends HttpSampler {
class SkipPatternHttpClientSampler implements SamplerFunction<HttpRequest> {
private final SleuthWebProperties properties;
PathMatchingHttpSampler(SleuthWebProperties properties) {
SkipPatternHttpClientSampler(SleuthWebProperties properties) {
this.properties = properties;
}
@Override
public <Req> Boolean trySample(HttpAdapter<Req, ?> adapter, Req request) {
String path = adapter.path(request);
public Boolean trySample(HttpRequest request) {
String path = request.path();
if (path == null) {
return null;
}

View File

@@ -237,7 +237,8 @@ class RestTemplateInterceptorInjector {
private boolean hasTraceInterceptor(RestTemplate restTemplate) {
for (ClientHttpRequestInterceptor interceptor : restTemplate.getInterceptors()) {
if (interceptor instanceof TracingClientHttpRequestInterceptor || interceptor instanceof LazyTracingClientHttpRequestInterceptor) {
if (interceptor instanceof TracingClientHttpRequestInterceptor
|| interceptor instanceof LazyTracingClientHttpRequestInterceptor) {
return true;
}
}

View File

@@ -20,6 +20,7 @@ org.springframework.cloud.sleuth.instrument.rxjava.RxJavaAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.reactor.TraceReactorAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.web.TraceWebFluxAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.zuul.TraceZuulAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.rpc.TraceRpcAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.grpc.TraceGrpcAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.messaging.TraceMessagingAutoConfiguration,\
org.springframework.cloud.sleuth.instrument.messaging.TraceSpringIntegrationAutoConfiguration,\

View File

@@ -20,12 +20,14 @@ import brave.TracingCustomizer;
import brave.http.HttpTracingCustomizer;
import brave.propagation.CurrentTraceContextCustomizer;
import brave.propagation.ExtraFieldCustomizer;
import brave.rpc.RpcTracingCustomizer;
import brave.sampler.Sampler;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.instrument.rpc.TraceRpcAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.web.TraceHttpAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.web.TraceWebAutoConfiguration;
import org.springframework.context.annotation.Bean;
@@ -38,7 +40,8 @@ public class TraceAutoConfigurationCustomizersTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withPropertyValues("spring.sleuth.baggage-keys=my-baggage")
.withConfiguration(AutoConfigurations.of(TraceAutoConfiguration.class,
TraceWebAutoConfiguration.class, TraceHttpAutoConfiguration.class))
TraceWebAutoConfiguration.class, TraceHttpAutoConfiguration.class,
TraceRpcAutoConfiguration.class))
.withUserConfiguration(Customizers.class);
@Test
@@ -60,6 +63,7 @@ public class TraceAutoConfigurationCustomizersTests {
then(bean.contextCustomizerApplied).isTrue();
then(bean.extraFieldCustomizerApplied).isTrue();
then(bean.httpCustomizerApplied).isTrue();
then(bean.rpcCustomizerApplied).isTrue();
}
@Configuration
@@ -73,6 +77,8 @@ public class TraceAutoConfigurationCustomizersTests {
boolean httpCustomizerApplied;
boolean rpcCustomizerApplied;
@Bean
TracingCustomizer sleuthTracingCustomizer() {
return builder -> tracingCustomizerApplied = true;
@@ -93,6 +99,11 @@ public class TraceAutoConfigurationCustomizersTests {
return builder -> httpCustomizerApplied = true;
}
@Bean
RpcTracingCustomizer sleuthRpcTracingCustomizer() {
return builder -> rpcCustomizerApplied = true;
}
@Bean
Sampler sampler() {
return Sampler.ALWAYS_SAMPLE;

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2013-2019 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
*
* https://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.rpc;
import brave.rpc.RpcRequest;
import brave.rpc.RpcRuleSampler;
import brave.sampler.Matcher;
import brave.sampler.RateLimitingSampler;
import brave.sampler.Sampler;
import brave.sampler.SamplerFunction;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
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 static brave.rpc.RpcRequestMatchers.methodEquals;
import static brave.rpc.RpcRequestMatchers.serviceEquals;
import static brave.sampler.Matchers.and;
import static org.assertj.core.api.BDDAssertions.then;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TraceRpcAutoConfigurationIntegrationTests.Config.class, webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class TraceRpcAutoConfigurationIntegrationTests {
@Autowired
@RpcServerSampler
SamplerFunction<RpcRequest> sampler;
@Test
public void should_inject_rpc_sampler() {
then(this.sampler).isNotNull();
}
@EnableAutoConfiguration
@Configuration
public static class Config {
@Bean
ArrayListSpanReporter reporter() {
return new ArrayListSpanReporter();
}
// tag::custom_rpc_server_sampler[]
@Bean(name = RpcServerSampler.NAME)
SamplerFunction<RpcRequest> myRpcSampler() {
Matcher<RpcRequest> userAuth = and(serviceEquals("users.UserService"),
methodEquals("GetUserToken"));
return RpcRuleSampler.newBuilder()
.putRule(serviceEquals("grpc.health.v1.Health"), Sampler.NEVER_SAMPLE)
.putRule(userAuth, RateLimitingSampler.create(100)).build();
}
// end::custom_rpc_server_sampler[]
}
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright 2013-2019 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
*
* https://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.rpc;
import brave.rpc.RpcRequest;
import brave.rpc.RpcTracing;
import brave.sampler.SamplerFunction;
import brave.sampler.SamplerFunctions;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.BDDAssertions.then;
public class TraceRpcAutoConfigurationTests {
@Test
public void defaultsToBraveRpcClientSampler() {
contextRunner().run((context) -> {
SamplerFunction<RpcRequest> clientSampler = context.getBean(RpcTracing.class)
.clientSampler();
then(clientSampler).isSameAs(SamplerFunctions.deferDecision());
});
}
@Test
public void configuresUserProvidedRpcClientSampler() {
contextRunner().withUserConfiguration(RpcClientSamplerConfig.class)
.run((context) -> {
SamplerFunction<RpcRequest> clientSampler = context
.getBean(RpcTracing.class).clientSampler();
then(clientSampler).isSameAs(RpcClientSamplerConfig.INSTANCE);
});
}
@Test
public void defaultsToBraveRpcServerSampler() {
contextRunner().run((context) -> {
SamplerFunction<RpcRequest> serverSampler = context.getBean(RpcTracing.class)
.serverSampler();
then(serverSampler).isSameAs(SamplerFunctions.deferDecision());
});
}
@Test
public void configuresUserProvidedRpcServerSampler() {
contextRunner().withUserConfiguration(RpcServerSamplerConfig.class)
.run((context) -> {
SamplerFunction<RpcRequest> serverSampler = context
.getBean(RpcTracing.class).serverSampler();
then(serverSampler).isSameAs(RpcServerSamplerConfig.INSTANCE);
});
}
private ApplicationContextRunner contextRunner(String... propertyValues) {
return new ApplicationContextRunner().withPropertyValues(propertyValues)
.withConfiguration(AutoConfigurations.of(TraceAutoConfiguration.class,
TraceRpcAutoConfiguration.class,
TraceRpcAutoConfiguration.class));
}
}
@Configuration
class RpcClientSamplerConfig {
static final SamplerFunction<RpcRequest> INSTANCE = request -> null;
@Bean(RpcClientSampler.NAME)
SamplerFunction<RpcRequest> sleuthRpcClientSampler() {
return INSTANCE;
}
}
@Configuration
class RpcServerSamplerConfig {
static final SamplerFunction<RpcRequest> INSTANCE = request -> null;
@Bean(RpcServerSampler.NAME)
SamplerFunction<RpcRequest> sleuthRpcServerSampler() {
return INSTANCE;
}
}

View File

@@ -16,8 +16,8 @@
package org.springframework.cloud.sleuth.instrument.web;
import brave.http.HttpAdapter;
import brave.http.HttpSampler;
import brave.http.HttpRequest;
import brave.sampler.SamplerFunction;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,17 +31,15 @@ import static org.mockito.BDDMockito.given;
public class CompositeHttpSamplerTests {
@Mock
HttpAdapter adapter;
SamplerFunction<HttpRequest> left;
@Mock
HttpSampler left;
SamplerFunction<HttpRequest> right;
@Mock
HttpSampler right;
HttpRequest request;
HttpSampler sampler;
Object request = new Object();
SamplerFunction<HttpRequest> sampler;
@Before
public void init() {
@@ -50,41 +48,41 @@ public class CompositeHttpSamplerTests {
@Test
public void should_return_null_on_both_null() {
given(this.left.trySample(this.adapter, this.request)).willReturn(null);
given(this.right.trySample(this.adapter, this.request)).willReturn(null);
given(this.left.trySample(this.request)).willReturn(null);
given(this.right.trySample(this.request)).willReturn(null);
then(this.sampler.trySample(this.adapter, this.request)).isNull();
then(this.sampler.trySample(this.request)).isNull();
}
@Test
public void should_return_false_on_any_false() {
given(this.left.trySample(this.adapter, this.request)).willReturn(false);
given(this.right.trySample(this.adapter, this.request)).willReturn(null);
given(this.left.trySample(this.request)).willReturn(false);
given(this.right.trySample(this.request)).willReturn(null);
then(this.sampler.trySample(this.adapter, this.request)).isFalse();
then(this.sampler.trySample(this.request)).isFalse();
given(this.left.trySample(this.adapter, this.request)).willReturn(null);
given(this.right.trySample(this.adapter, this.request)).willReturn(false);
given(this.left.trySample(this.request)).willReturn(null);
given(this.right.trySample(this.request)).willReturn(false);
then(this.sampler.trySample(this.adapter, this.request)).isFalse();
then(this.sampler.trySample(this.request)).isFalse();
given(this.left.trySample(this.adapter, this.request)).willReturn(false);
given(this.right.trySample(this.adapter, this.request)).willReturn(true);
given(this.left.trySample(this.request)).willReturn(false);
given(this.right.trySample(this.request)).willReturn(true);
then(this.sampler.trySample(this.adapter, this.request)).isFalse();
then(this.sampler.trySample(this.request)).isFalse();
given(this.left.trySample(this.adapter, this.request)).willReturn(true);
given(this.right.trySample(this.adapter, this.request)).willReturn(false);
given(this.left.trySample(this.request)).willReturn(true);
given(this.right.trySample(this.request)).willReturn(false);
then(this.sampler.trySample(this.adapter, this.request)).isFalse();
then(this.sampler.trySample(this.request)).isFalse();
}
@Test
public void should_return_true_on_both_true() {
given(this.left.trySample(this.adapter, this.request)).willReturn(true);
given(this.right.trySample(this.adapter, this.request)).willReturn(true);
given(this.left.trySample(this.request)).willReturn(true);
given(this.right.trySample(this.request)).willReturn(true);
then(this.sampler.trySample(this.adapter, this.request)).isTrue();
then(this.sampler.trySample(this.request)).isTrue();
}
}

View File

@@ -18,7 +18,7 @@ package org.springframework.cloud.sleuth.instrument.web;
import java.util.regex.Pattern;
import brave.http.HttpAdapter;
import brave.http.HttpRequest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.BDDMockito;
@@ -31,27 +31,27 @@ import static org.assertj.core.api.BDDAssertions.then;
* @author Marcin Grzejszczak
*/
@RunWith(MockitoJUnitRunner.class)
public class SleuthHttpSamplerTests {
public class SkipPatternHttpServerSamplerTests {
@Mock
HttpAdapter adapter;
HttpRequest request;
@Test
public void should_delegate_sampling_decision_if_pattern_is_not_matched() {
SkipPatternProvider provider = () -> Pattern.compile("foo");
BDDMockito.given(this.adapter.path(BDDMockito.any())).willReturn("url");
SleuthHttpSampler sampler = new SleuthHttpSampler(provider);
BDDMockito.given(this.request.path()).willReturn("url");
SkipPatternHttpServerSampler sampler = new SkipPatternHttpServerSampler(provider);
then(sampler.trySample(this.adapter, new Object())).isNull();
then(sampler.trySample(this.request)).isNull();
}
@Test
public void should_not_sample_if_pattern_is_matched() {
SkipPatternProvider provider = () -> Pattern.compile(".*");
BDDMockito.given(this.adapter.path(BDDMockito.any())).willReturn("url");
SleuthHttpSampler sampler = new SleuthHttpSampler(provider);
BDDMockito.given(this.request.path()).willReturn("url");
SkipPatternHttpServerSampler sampler = new SkipPatternHttpServerSampler(provider);
then(sampler.trySample(this.adapter, new Object())).isFalse();
then(sampler.trySample(this.request)).isFalse();
}
}

View File

@@ -75,7 +75,8 @@ public class TraceFilterTests {
HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing)
.clientParser(new SleuthHttpClientParser(this.traceKeys))
.serverParser(new SleuthHttpServerParser(this.traceKeys, new ErrorParser()))
.serverSampler(new SleuthHttpSampler(() -> Pattern.compile(""))).build();
.serverSampler(new SkipPatternHttpServerSampler(() -> Pattern.compile("")))
.build();
Filter filter = TracingFilter.create(this.httpTracing);
@@ -124,7 +125,9 @@ public class TraceFilterTests {
.clientParser(new SleuthHttpClientParser(this.traceKeys))
.serverParser(
new SleuthHttpServerParser(this.traceKeys, new ErrorParser()))
.serverSampler(new SleuthHttpSampler(() -> Pattern.compile(""))).build();
.serverSampler(
new SkipPatternHttpServerSampler(() -> Pattern.compile("")))
.build();
return TracingFilter.create(httpTracing);
}

View File

@@ -23,9 +23,9 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors;
import brave.Tracing;
import brave.http.HttpAdapter;
import brave.http.HttpSampler;
import brave.http.HttpRequest;
import brave.sampler.Sampler;
import brave.sampler.SamplerFunction;
import org.assertj.core.api.BDDAssertions;
import org.junit.After;
import org.junit.Before;
@@ -72,8 +72,8 @@ public class TraceFilterWebIntegrationTests {
ArrayListSpanReporter accumulator;
@Autowired
@ServerSampler
HttpSampler sampler;
@HttpServerSampler
SamplerFunction<HttpRequest> sampler;
@Autowired
Environment environment;
@@ -161,20 +161,16 @@ public class TraceFilterWebIntegrationTests {
}
// tag::custom_server_sampler[]
@Bean(name = ServerSampler.NAME)
HttpSampler myHttpSampler(SkipPatternProvider provider) {
@Bean(name = HttpServerSampler.NAME)
SamplerFunction<HttpRequest> myHttpSampler(SkipPatternProvider provider) {
Pattern pattern = provider.skipPattern();
return new HttpSampler() {
@Override
public <Req> Boolean trySample(HttpAdapter<Req, ?> adapter, Req request) {
String url = adapter.path(request);
boolean shouldSkip = pattern.matcher(url).matches();
if (shouldSkip) {
return false;
}
return null;
return request -> {
String url = request.path();
boolean shouldSkip = pattern.matcher(url).matches();
if (shouldSkip) {
return false;
}
return null;
};
}
// end::custom_server_sampler[]

View File

@@ -0,0 +1,190 @@
/*
* Copyright 2013-2019 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
*
* https://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 brave.http.HttpAdapter;
import brave.http.HttpRequest;
import brave.http.HttpSampler;
import brave.http.HttpTracing;
import brave.sampler.SamplerFunction;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.BDDAssertions.then;
public class TraceHttpAutoConfigurationTests {
@Test
public void defaultsToSkipPatternHttpClientSampler() {
contextRunner().run((context) -> {
SamplerFunction<HttpRequest> clientSampler = context
.getBean(HttpTracing.class).clientRequestSampler();
then(clientSampler).isInstanceOf(SkipPatternHttpClientSampler.class);
});
}
@Test
public void configuresUserProvidedDeprecatedClientSampler() {
contextRunner().withUserConfiguration(DeprecatedClientSamplerConfig.class)
.run((context) -> {
SamplerFunction<HttpRequest> clientSampler = context
.getBean(HttpTracing.class).clientRequestSampler();
then(clientSampler).isSameAs(DeprecatedClientSamplerConfig.INSTANCE);
});
}
@Test
public void configuresUserProvidedHttpClientSampler() {
contextRunner().withUserConfiguration(HttpClientSamplerConfig.class)
.run((context) -> {
SamplerFunction<HttpRequest> clientSampler = context
.getBean(HttpTracing.class).clientRequestSampler();
then(clientSampler).isSameAs(HttpClientSamplerConfig.INSTANCE);
});
}
@Test
public void prefersUserProvidedHttpClientSampler() {
contextRunner().withUserConfiguration(DeprecatedClientSamplerConfig.class)
.withUserConfiguration(HttpClientSamplerConfig.class).run((context) -> {
SamplerFunction<HttpRequest> clientSampler = context
.getBean(HttpTracing.class).clientRequestSampler();
then(clientSampler).isSameAs(HttpClientSamplerConfig.INSTANCE);
});
}
@Test
public void defaultsToSkipPatternHttpServerSampler() {
contextRunner().run((context) -> {
SamplerFunction<HttpRequest> serverSampler = context
.getBean(HttpTracing.class).serverRequestSampler();
then(serverSampler).isInstanceOf(SkipPatternHttpServerSampler.class);
});
}
@Test
public void wrapsUserProvidedDeprecatedServerSampler() {
contextRunner().withUserConfiguration(DeprecatedServerSamplerConfig.class).run(
thenCompositeHttpServerSamplerOf(DeprecatedServerSamplerConfig.INSTANCE));
}
@Test
public void wrapsUserProvidedHttpServerSampler() {
contextRunner().withUserConfiguration(HttpServerSamplerConfig.class)
.run(thenCompositeHttpServerSamplerOf(HttpServerSamplerConfig.INSTANCE));
}
@Test
public void prefersUserProvidedUserProvidedHttpServerSampler() {
contextRunner().withUserConfiguration(HttpServerSamplerConfig.class)
.withUserConfiguration(DeprecatedServerSamplerConfig.class)
.run(thenCompositeHttpServerSamplerOf(HttpServerSamplerConfig.INSTANCE));
}
private ContextConsumer<AssertableApplicationContext> thenCompositeHttpServerSamplerOf(
SamplerFunction<HttpRequest> instance) {
return (context) -> {
SamplerFunction<HttpRequest> serverSampler = context
.getBean(HttpTracing.class).serverRequestSampler();
then(serverSampler).isInstanceOf(CompositeHttpSampler.class);
then(((CompositeHttpSampler) serverSampler).left)
.isInstanceOf(SkipPatternHttpServerSampler.class);
then(((CompositeHttpSampler) serverSampler).right).isSameAs(instance);
};
}
private ApplicationContextRunner contextRunner(String... propertyValues) {
return new ApplicationContextRunner().withPropertyValues(propertyValues)
.withConfiguration(AutoConfigurations.of(TraceAutoConfiguration.class,
TraceHttpAutoConfiguration.class,
TraceWebAutoConfiguration.class));
}
}
@Configuration
class HttpClientSamplerConfig {
static final SamplerFunction<HttpRequest> INSTANCE = request -> null;
@Bean(HttpClientSampler.NAME)
SamplerFunction<HttpRequest> sleuthHttpClientSampler() {
return INSTANCE;
}
}
@Configuration
class DeprecatedClientSamplerConfig {
static final HttpSampler INSTANCE = new HttpSampler() {
@Override
public <Req> Boolean trySample(HttpAdapter<Req, ?> httpAdapter, Req req) {
return null;
}
};
@Bean(ClientSampler.NAME)
HttpSampler sleuthClientSampler() {
return INSTANCE;
}
}
@Configuration
class HttpServerSamplerConfig {
static final SamplerFunction<HttpRequest> INSTANCE = request -> null;
@Bean(HttpServerSampler.NAME)
SamplerFunction<HttpRequest> sleuthHttpServerSampler() {
return INSTANCE;
}
}
@Configuration
class DeprecatedServerSamplerConfig {
static final HttpSampler INSTANCE = new HttpSampler() {
@Override
public <Req> Boolean trySample(HttpAdapter<Req, ?> httpAdapter, Req req) {
return null;
}
};
@Bean(ServerSampler.NAME)
HttpSampler sleuthServerSampler() {
return INSTANCE;
}
}

View File

@@ -104,7 +104,8 @@ public class TraceWebClientAutoConfigurationTests {
.containsOnlyElementsOf(Collections.singletonList(1));
}
private void incrementNumberOfInstances(Map<Class, Integer> numberOfInstances, ClientHttpRequestInterceptor interceptor) {
private void incrementNumberOfInstances(Map<Class, Integer> numberOfInstances,
ClientHttpRequestInterceptor interceptor) {
Integer no = numberOfInstances.get(interceptor.getClass());
if (no == null) {
numberOfInstances.put(interceptor.getClass(), 1);

View File

@@ -73,7 +73,7 @@
<dependency>
<groupId>io.zipkin.zipkin2</groupId>
<artifactId>zipkin</artifactId>
<version>2.16.0</version>
<version>2.17.0</version>
</dependency>
</dependencies>
</dependencyManagement>