From be5fa041180762d4f2f782fcccadc04d1b97c47d Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Thu, 3 Oct 2019 18:21:56 +0800 Subject: [PATCH] Updates to latest Sampling infrastructure (#1456) * Updates to latest Sampling infrastructure Brave recently switched to an interface model for higher level sampling like HTTP. So, `HttpSampler` -> `SamplingFunction`. Don't worry because `HttpSampler` was retrofitted as a `SamplingFunction`. This change moves to the higher interface, avoiding deprecated methods and such. More interestingly, this weaves in support for `RpcTracing`, which *finally* introduces RPC sampling the same way. Specifically, this adds `SamplingFunction` under the same conventions as HTTP. Most immediately, this can be used here in gRPC and Dubbo, as autoconfiguration exists. It also works with any autoconfiguration that isn't here, such as Armeria. Ex. Here's a sampler that traces 100 "GetUserToken" requests per second. This doesn't start new traces for requests to the health check service. Other requests will use a global rate provided by the tracing component. ```java import static brave.rpc.RpcRequestMatchers.methodEquals; import static brave.rpc.RpcRequestMatchers.serviceEquals; import static brave.sampler.Matchers.and; --snip-- @Bean(name = ServerSampler.NAME) SamplerFunction myRpcSampler() { Matcher 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(); } ``` --- benchmarks/pom.xml | 2 +- .../main/asciidoc/spring-cloud-sleuth.adoc | 50 ++++- spring-cloud-sleuth-core/pom.xml | 4 + .../grpc/TraceGrpcAutoConfiguration.java | 11 +- .../instrument/rpc/RpcClientSampler.java | 50 +++++ .../instrument/rpc/RpcServerSampler.java | 50 +++++ .../rpc/TraceRpcAutoConfiguration.java | 74 +++++++ .../sleuth/instrument/web/ClientSampler.java | 2 + .../instrument/web/HttpClientSampler.java | 50 +++++ .../instrument/web/HttpServerSampler.java | 50 +++++ .../sleuth/instrument/web/ServerSampler.java | 2 + ...java => SkipPatternHttpServerSampler.java} | 12 +- .../web/TraceHttpAutoConfiguration.java | 61 +++--- .../main/resources/META-INF/spring.factories | 1 + ...raceAutoConfigurationCustomizersTests.java | 13 +- .../rpc/TraceRpcAutoConfigurationTests.java | 108 ++++++++++ .../web/CompositeHttpSamplerTests.java | 50 +++-- ...=> SkipPatternHttpServerSamplerTests.java} | 18 +- .../instrument/web/TraceFilterTests.java | 7 +- .../web/TraceHttpAutoConfigurationTests.java | 190 ++++++++++++++++++ spring-cloud-sleuth-dependencies/pom.xml | 2 +- spring-cloud-sleuth-samples/pom.xml | 2 +- tests/pom.xml | 1 + .../web/TraceFilterWebIntegrationTests.java | 28 ++- .../pom.xml | 74 +++++++ ...eRpcAutoConfigurationIntegrationTests.java | 77 +++++++ 26 files changed, 888 insertions(+), 101 deletions(-) create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rpc/RpcClientSampler.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rpc/RpcServerSampler.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfiguration.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpClientSampler.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerSampler.java rename spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/{SleuthHttpSampler.java => SkipPatternHttpServerSampler.java} (80%) create mode 100644 spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfigurationTests.java rename spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/{SleuthHttpSamplerTests.java => SkipPatternHttpServerSamplerTests.java} (72%) create mode 100644 spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceHttpAutoConfigurationTests.java create mode 100644 tests/spring-cloud-sleuth-instrumentation-rpc-tests/pom.xml create mode 100644 tests/spring-cloud-sleuth-instrumentation-rpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfigurationIntegrationTests.java diff --git a/benchmarks/pom.xml b/benchmarks/pom.xml index 78f0055f2..01b9c303a 100644 --- a/benchmarks/pom.xml +++ b/benchmarks/pom.xml @@ -34,7 +34,7 @@ 1.8 1.8 2.2.0.BUILD-SNAPSHOT - 5.7.0 + 5.8.0 diff --git a/docs/src/main/asciidoc/spring-cloud-sleuth.adoc b/docs/src/main/asciidoc/spring-cloud-sleuth.adoc index d54c01ed0..22eb7f8eb 100644 --- a/docs/src/main/asciidoc/spring-cloud-sleuth.adoc +++ b/docs/src/main/asciidoc/spring-cloud-sleuth.adoc @@ -838,21 +838,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` 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`. [source,java] ---- @@ -874,6 +876,34 @@ In the following example, we register the `TracingFilter` bean, add the `ZIPKIN- include::{project-root}/tests/spring-cloud-sleuth-instrumentation-mvc-tests/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` 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::{project-root}/tests/spring-cloud-sleuth-instrumentation-rpc-tests/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. @@ -1113,13 +1143,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] ---- io.zipkin.brave - brave-instrumentation-dubbo-rpc + brave-instrumentation-dubbo ---- diff --git a/spring-cloud-sleuth-core/pom.xml b/spring-cloud-sleuth-core/pom.xml index 36d965bf9..5227c9203 100644 --- a/spring-cloud-sleuth-core/pom.xml +++ b/spring-cloud-sleuth-core/pom.xml @@ -208,6 +208,10 @@ io.zipkin.brave brave-context-log4j2 + + io.zipkin.brave + brave-instrumentation-rpc + io.zipkin.brave brave-instrumentation-spring-web diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/grpc/TraceGrpcAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/grpc/TraceGrpcAutoConfiguration.java index 9cf02eb5d..4b333dee1 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/grpc/TraceGrpcAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/grpc/TraceGrpcAutoConfiguration.java @@ -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 diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rpc/RpcClientSampler.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rpc/RpcClientSampler.java new file mode 100644 index 000000000..689eef752 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rpc/RpcClientSampler.java @@ -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"; + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rpc/RpcServerSampler.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rpc/RpcServerSampler.java new file mode 100644 index 000000000..195116410 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rpc/RpcServerSampler.java @@ -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"; + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfiguration.java new file mode 100644 index 000000000..24efddb9c --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfiguration.java @@ -0,0 +1,74 @@ +/* + * 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 rpcTracingCustomizers = new ArrayList<>(); + + @Bean + @ConditionalOnMissingBean + // NOTE: stable bean name as might be used outside sleuth + RpcTracing rpcTracing(Tracing tracing, + @Nullable @RpcClientSampler SamplerFunction clientSampler, + @Nullable @RpcServerSampler SamplerFunction 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(); + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ClientSampler.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ClientSampler.java index 7b58cc5df..21bb50cd5 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ClientSampler.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ClientSampler.java @@ -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 { /** diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpClientSampler.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpClientSampler.java new file mode 100644 index 000000000..fbf748196 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpClientSampler.java @@ -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"; + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerSampler.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerSampler.java new file mode 100644 index 000000000..a725de02d --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerSampler.java @@ -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"; + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ServerSampler.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ServerSampler.java index f0a807f4f..fccf26a3b 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ServerSampler.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ServerSampler.java @@ -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 { /** diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/SleuthHttpSampler.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipPatternHttpServerSampler.java similarity index 80% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/SleuthHttpSampler.java rename to spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipPatternHttpServerSampler.java index 35e5f736c..4e00022f0 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/SleuthHttpSampler.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipPatternHttpServerSampler.java @@ -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 { private final SkipPatternProvider provider; private Pattern pattern; - SleuthHttpSampler(SkipPatternProvider provider) { + SkipPatternHttpServerSampler(SkipPatternProvider provider) { this.provider = provider; } @Override - public Boolean trySample(HttpAdapter 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; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceHttpAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceHttpAutoConfiguration.java index 12641da9d..0bbff7249 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceHttpAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceHttpAutoConfiguration.java @@ -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; @@ -64,22 +65,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 httpClientSampler, + @Nullable @ServerSampler HttpSampler serverSampler, + @Nullable @HttpServerSampler SamplerFunction httpServerSampler) { + if (httpServerSampler == null) { + httpServerSampler = serverSampler; + } + SamplerFunction 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 combineUserProvidedSamplerWithSkipPatternSampler( + @Nullable SamplerFunction serverSampler, + SkipPatternProvider provider) { + SkipPatternHttpServerSampler skipPatternSampler = new SkipPatternHttpServerSampler( + provider); if (serverSampler == null) { return skipPatternSampler; } @@ -123,9 +130,14 @@ public class TraceHttpAutoConfiguration { } @Bean - @ConditionalOnMissingBean(name = ClientSampler.NAME) - HttpSampler sleuthClientSampler(SleuthWebProperties sleuthWebProperties) { - return new PathMatchingHttpSampler(sleuthWebProperties); + @ConditionalOnMissingBean(name = HttpClientSampler.NAME) + SamplerFunction sleuthHttpClientSampler( + @Nullable @ClientSampler HttpSampler sleuthClientSampler, + SleuthWebProperties sleuthWebProperties) { + if (sleuthClientSampler != null) { + return sleuthClientSampler; + } + return new SkipPatternHttpClientSampler(sleuthWebProperties); } } @@ -135,25 +147,26 @@ public class TraceHttpAutoConfiguration { * * @author Adrian Cole */ -class CompositeHttpSampler extends HttpSampler { +class CompositeHttpSampler implements SamplerFunction { - private final HttpSampler left; + final SamplerFunction left; - private final HttpSampler right; + final SamplerFunction right; - CompositeHttpSampler(HttpSampler left, HttpSampler right) { + CompositeHttpSampler(SamplerFunction left, + SamplerFunction right) { this.left = left; this.right = right; } @Override - public Boolean trySample(HttpAdapter 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; } @@ -165,7 +178,7 @@ class CompositeHttpSampler extends HttpSampler { return leftDecision; } // Neither are null and at least one is true - return leftDecision && rightDecision; + return rightDecision; } } @@ -175,17 +188,17 @@ class CompositeHttpSampler extends HttpSampler { * * @author Marcin Grzejszczak */ -class PathMatchingHttpSampler extends HttpSampler { +class SkipPatternHttpClientSampler implements SamplerFunction { private final SleuthWebProperties properties; - PathMatchingHttpSampler(SleuthWebProperties properties) { + SkipPatternHttpClientSampler(SleuthWebProperties properties) { this.properties = properties; } @Override - public Boolean trySample(HttpAdapter adapter, Req request) { - String path = adapter.path(request); + public Boolean trySample(HttpRequest request) { + String path = request.path(); if (path == null) { return null; } diff --git a/spring-cloud-sleuth-core/src/main/resources/META-INF/spring.factories b/spring-cloud-sleuth-core/src/main/resources/META-INF/spring.factories index 725e2e30e..9290a35c7 100644 --- a/spring-cloud-sleuth-core/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-sleuth-core/src/main/resources/META-INF/spring.factories @@ -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.SleuthKafkaStreamsConfiguration,\ org.springframework.cloud.sleuth.instrument.messaging.TraceMessagingAutoConfiguration,\ diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationCustomizersTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationCustomizersTests.java index f449363ea..63799ff98 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationCustomizersTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationCustomizersTests.java @@ -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; @@ -37,7 +39,8 @@ public class TraceAutoConfigurationCustomizersTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(TraceAutoConfiguration.class, - TraceWebAutoConfiguration.class, TraceHttpAutoConfiguration.class)) + TraceWebAutoConfiguration.class, TraceHttpAutoConfiguration.class, + TraceRpcAutoConfiguration.class)) .withUserConfiguration(Customizers.class); @Test @@ -69,6 +72,7 @@ public class TraceAutoConfigurationCustomizersTests { then(bean.contextCustomizerApplied).isTrue(); then(bean.extraFieldCustomizerApplied).isTrue(); then(bean.httpCustomizerApplied).isTrue(); + then(bean.rpcCustomizerApplied).isTrue(); } @Configuration @@ -82,6 +86,8 @@ public class TraceAutoConfigurationCustomizersTests { boolean httpCustomizerApplied; + boolean rpcCustomizerApplied; + @Bean TracingCustomizer sleuthTracingCustomizer() { return builder -> tracingCustomizerApplied = true; @@ -102,6 +108,11 @@ public class TraceAutoConfigurationCustomizersTests { return builder -> httpCustomizerApplied = true; } + @Bean + RpcTracingCustomizer sleuthRpcTracingCustomizer() { + return builder -> rpcCustomizerApplied = true; + } + @Bean Sampler sampler() { return Sampler.ALWAYS_SAMPLE; diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfigurationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfigurationTests.java new file mode 100644 index 000000000..ba71b65ae --- /dev/null +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfigurationTests.java @@ -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 clientSampler = context.getBean(RpcTracing.class) + .clientSampler(); + + then(clientSampler).isSameAs(SamplerFunctions.deferDecision()); + }); + } + + @Test + public void configuresUserProvidedRpcClientSampler() { + contextRunner().withUserConfiguration(RpcClientSamplerConfig.class) + .run((context) -> { + SamplerFunction clientSampler = context + .getBean(RpcTracing.class).clientSampler(); + + then(clientSampler).isSameAs(RpcClientSamplerConfig.INSTANCE); + }); + } + + @Test + public void defaultsToBraveRpcServerSampler() { + contextRunner().run((context) -> { + SamplerFunction serverSampler = context.getBean(RpcTracing.class) + .serverSampler(); + + then(serverSampler).isSameAs(SamplerFunctions.deferDecision()); + }); + } + + @Test + public void configuresUserProvidedRpcServerSampler() { + contextRunner().withUserConfiguration(RpcServerSamplerConfig.class) + .run((context) -> { + SamplerFunction 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 INSTANCE = request -> null; + + @Bean(RpcClientSampler.NAME) + SamplerFunction sleuthRpcClientSampler() { + return INSTANCE; + } + +} + +@Configuration +class RpcServerSamplerConfig { + + static final SamplerFunction INSTANCE = request -> null; + + @Bean(RpcServerSampler.NAME) + SamplerFunction sleuthRpcServerSampler() { + return INSTANCE; + } + +} diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/CompositeHttpSamplerTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/CompositeHttpSamplerTests.java index 66bf56f65..51ce2c4cf 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/CompositeHttpSamplerTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/CompositeHttpSamplerTests.java @@ -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 left; @Mock - HttpSampler left; + SamplerFunction right; @Mock - HttpSampler right; + HttpRequest request; - HttpSampler sampler; - - Object request = new Object(); + SamplerFunction 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(); } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SleuthHttpSamplerTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipPatternHttpServerSamplerTests.java similarity index 72% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SleuthHttpSamplerTests.java rename to spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipPatternHttpServerSamplerTests.java index decbded42..49389a872 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SleuthHttpSamplerTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipPatternHttpServerSamplerTests.java @@ -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(); } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java index 5447472a6..09498b586 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java @@ -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); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceHttpAutoConfigurationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceHttpAutoConfigurationTests.java new file mode 100644 index 000000000..a5206db89 --- /dev/null +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceHttpAutoConfigurationTests.java @@ -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 clientSampler = context + .getBean(HttpTracing.class).clientRequestSampler(); + + then(clientSampler).isInstanceOf(SkipPatternHttpClientSampler.class); + }); + } + + @Test + public void configuresUserProvidedDeprecatedClientSampler() { + contextRunner().withUserConfiguration(DeprecatedClientSamplerConfig.class) + .run((context) -> { + SamplerFunction clientSampler = context + .getBean(HttpTracing.class).clientRequestSampler(); + + then(clientSampler).isSameAs(DeprecatedClientSamplerConfig.INSTANCE); + }); + } + + @Test + public void configuresUserProvidedHttpClientSampler() { + contextRunner().withUserConfiguration(HttpClientSamplerConfig.class) + .run((context) -> { + SamplerFunction 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 clientSampler = context + .getBean(HttpTracing.class).clientRequestSampler(); + + then(clientSampler).isSameAs(HttpClientSamplerConfig.INSTANCE); + }); + } + + @Test + public void defaultsToSkipPatternHttpServerSampler() { + contextRunner().run((context) -> { + SamplerFunction 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 thenCompositeHttpServerSamplerOf( + SamplerFunction instance) { + return (context) -> { + + SamplerFunction 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 INSTANCE = request -> null; + + @Bean(HttpClientSampler.NAME) + SamplerFunction sleuthHttpClientSampler() { + return INSTANCE; + } + +} + +@Configuration +class DeprecatedClientSamplerConfig { + + static final HttpSampler INSTANCE = new HttpSampler() { + @Override + public Boolean trySample(HttpAdapter httpAdapter, Req req) { + return null; + } + }; + + @Bean(ClientSampler.NAME) + HttpSampler sleuthClientSampler() { + return INSTANCE; + } + +} + +@Configuration +class HttpServerSamplerConfig { + + static final SamplerFunction INSTANCE = request -> null; + + @Bean(HttpServerSampler.NAME) + SamplerFunction sleuthHttpServerSampler() { + return INSTANCE; + } + +} + +@Configuration +class DeprecatedServerSamplerConfig { + + static final HttpSampler INSTANCE = new HttpSampler() { + @Override + public Boolean trySample(HttpAdapter httpAdapter, Req req) { + return null; + } + }; + + @Bean(ServerSampler.NAME) + HttpSampler sleuthServerSampler() { + return INSTANCE; + } + +} diff --git a/spring-cloud-sleuth-dependencies/pom.xml b/spring-cloud-sleuth-dependencies/pom.xml index 3daa58846..ee08eec5d 100644 --- a/spring-cloud-sleuth-dependencies/pom.xml +++ b/spring-cloud-sleuth-dependencies/pom.xml @@ -31,7 +31,7 @@ spring-cloud-sleuth-dependencies Spring Cloud Sleuth Dependencies - 5.7.0 + 5.8.0 0.34.1 3.4.1 diff --git a/spring-cloud-sleuth-samples/pom.xml b/spring-cloud-sleuth-samples/pom.xml index 3cfe48065..8ac5d5567 100644 --- a/spring-cloud-sleuth-samples/pom.xml +++ b/spring-cloud-sleuth-samples/pom.xml @@ -73,7 +73,7 @@ io.zipkin.zipkin2 zipkin - 2.16.0 + 2.17.0 diff --git a/tests/pom.xml b/tests/pom.xml index 2a698dd2c..91f080d1c 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -43,6 +43,7 @@ spring-cloud-sleuth-instrumentation-lettuce-tests spring-cloud-sleuth-instrumentation-rxjava-tests spring-cloud-sleuth-instrumentation-scheduling-tests + spring-cloud-sleuth-instrumentation-rpc-tests spring-cloud-sleuth-instrumentation-mvc-tests spring-cloud-sleuth-instrumentation-webflux-tests spring-cloud-sleuth-instrumentation-feign-tests diff --git a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterWebIntegrationTests.java b/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterWebIntegrationTests.java index 5e1beeb41..86737ef1d 100644 --- a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterWebIntegrationTests.java +++ b/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterWebIntegrationTests.java @@ -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; @@ -74,8 +74,8 @@ public class TraceFilterWebIntegrationTests { ArrayListSpanReporter accumulator; @Autowired - @ServerSampler - HttpSampler sampler; + @HttpServerSampler + SamplerFunction sampler; @Autowired Environment environment; @@ -163,20 +163,16 @@ public class TraceFilterWebIntegrationTests { } // tag::custom_server_sampler[] - @Bean(name = ServerSampler.NAME) - HttpSampler myHttpSampler(SkipPatternProvider provider) { + @Bean(name = HttpServerSampler.NAME) + SamplerFunction myHttpSampler(SkipPatternProvider provider) { Pattern pattern = provider.skipPattern(); - return new HttpSampler() { - - @Override - public Boolean trySample(HttpAdapter 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[] diff --git a/tests/spring-cloud-sleuth-instrumentation-rpc-tests/pom.xml b/tests/spring-cloud-sleuth-instrumentation-rpc-tests/pom.xml new file mode 100644 index 000000000..4270380c7 --- /dev/null +++ b/tests/spring-cloud-sleuth-instrumentation-rpc-tests/pom.xml @@ -0,0 +1,74 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-instrumentation-rpc-tests + jar + Spring Cloud Sleuth RPC Instrumentation Tests + Spring Cloud Sleuth RPC Instrumentation Tests + + + org.springframework.cloud + spring-cloud-sleuth-tests + 2.2.0.BUILD-SNAPSHOT + .. + + + + true + + + + + + + maven-deploy-plugin + + true + + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.boot + spring-boot-starter-test + test + + + org.awaitility + awaitility + test + + + + diff --git a/tests/spring-cloud-sleuth-instrumentation-rpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfigurationIntegrationTests.java b/tests/spring-cloud-sleuth-instrumentation-rpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfigurationIntegrationTests.java new file mode 100644 index 000000000..9c52f4b7b --- /dev/null +++ b/tests/spring-cloud-sleuth-instrumentation-rpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfigurationIntegrationTests.java @@ -0,0 +1,77 @@ +/* + * 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 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 myRpcSampler() { + Matcher 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[] + + } + +}