diff --git a/README.adoc b/README.adoc index 1f506114d..d186fda16 100644 --- a/README.adoc +++ b/README.adoc @@ -6,443 +6,91 @@ Edit the files in the src/main/asciidoc/ directory instead. :jdkversion: 1.8 -:github-tag: master -:github-repo: spring-cloud/spring-cloud-sleuth -:github-raw: https://raw.githubusercontent.com/{github-repo}/{github-tag} -:github-code: https://github.com/{github-repo}/tree/{github-tag} - -image::https://circleci.com/gh/spring-cloud/spring-cloud-sleuth.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-sleuth"] -image::https://codecov.io/gh/spring-cloud/spring-cloud-sleuth/branch/{github-tag}/graph/badge.svg["codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-sleuth"] -image::https://badges.gitter.im/spring-cloud/spring-cloud-sleuth.svg[Gitter, link="https://gitter.im/spring-cloud/spring-cloud-sleuth?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"] +image::https://circleci.com/gh/spring-cloud/spring-cloud-sleuth.svg?style=svg["CircleCI",link="https://circleci.com/gh/spring-cloud/spring-cloud-sleuth"] +image::https://codecov.io/gh/spring-cloud/spring-cloud-sleuth/branch/{branch}/graph/badge.svg["codecov",link="https://codecov.io/gh/spring-cloud/spring-cloud-sleuth"] +image::https://badges.gitter.im/spring-cloud/spring-cloud-sleuth.svg[Gitter,link="https://gitter.im/spring-cloud/spring-cloud-sleuth?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"] == Spring Cloud Sleuth -Spring Cloud Sleuth provides Spring Boot auto-configuration for distributed -tracing. Underneath, Spring Cloud Sleuth is a layer over a tracer library named -https://github.com/openzipkin/brave[Brave]. +Spring Cloud Sleuth provides Spring Boot auto-configuration for distributed tracing. -Sleuth configures everything you need to get started. This includes where trace -data (spans) are reported to, how many traces to keep (sampling), if remote -fields (baggage) are sent, and which libraries are traced. +Sleuth configures everything you need to get started. +This includes where trace data (spans) are reported to, how many traces to keep (sampling), if remote fields (baggage) are sent, and which libraries are traced. === Quick Start -Add sleuth to the classpath of a Spring Boot application -(see "`<>`" for Maven and Gradle examples), and you will -see trace IDs in logs. +Add Spring Cloud Sleuth to the classpath of a Spring Boot application (together with a Tracer implementation) and you will see trace IDs in logs. +Example of Sleuth with Brave tracer: -For example, consider the following HTTP handler: +[source,xml,indent=0,subs="verbatim,quotes,attributes"] +---- + + + + + org.springframework.cloud + spring-cloud-dependencies + + ${release.train.version} + pom + import + + + -[source,java] + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + + +---- + +Consider the following HTTP handler: + +[source,java,indent=0] ---- @RestController public class DemoController { - private static Logger log = LoggerFactory.getLogger(DemoController.class); + private static Logger log = LoggerFactory.getLogger(DemoController.class); - @RequestMapping("/") - public String home() { - log.info("Handling home"); - ... - return "Hello World"; - } + @RequestMapping("/") + public String home() { + log.info("Handling home"); + return "Hello World"; + } } ---- If you add that handler to a controller, you can see the calls to `home()` -being traced in the logs as well in https://zipkin.io/[Zipkin], if configured. +being traced in the logs (notice the `0b6aaf642574edd3` ids). -NOTE: Instead of logging the request in the handler explicitly, you -could set `logging.level.org.springframework.web.servlet.DispatcherServlet=DEBUG`. - -NOTE: Set `spring.application.name=myService` (for instance) to see the service -name as well as the trace and span IDs. - -:branch: master - -== Overview -Spring Cloud Sleuth provides Spring Boot auto-configuration for distributed -tracing. Underneath, Spring Cloud Sleuth is a layer over a Tracer library named -https://github.com/openzipkin/brave[Brave]. - -Sleuth configures everything you need to get started. This includes where trace -data (spans) are reported to, how many traces to keep (sampling), if remote -fields (baggage) are sent, and which libraries are traced. - -We maintain an https://github.com/openzipkin/sleuth-webmvc-example[example app] where two Spring Boot services collaborate on an -HTTP request. Sleuth configures these apps, so that timing of these requests are -recorded into https://zipkin.io[Zipkin], a distributed tracing system. Tracing -UIs visualize latency, such as time in one service vs waiting for other -services. - -Here's an example of what it looks like: - -image::{github-raw}/src/main/asciidoc/images/zipkin-trace-screenshot.png[Zipkin Trace] - -The https://github.com/openzipkin/sleuth-webmvc-example[source repository] of this -example includes demonstrations of many things, including WebFlux and messaging. -Most features require only a property or dependency change to work. These -snippets showcase the value of Spring Cloud Sleuth: Through auto-configuration, -Sleuth make getting started with distributed tracing easy! - -To keep things simple, the same example is used throughout documentation using -basic HTTP communication. - -:branch: master - -:doctype: book -:idprefix: -:idseparator: - -:toc: left -:toclevels: 4 -:tabsize: 4 -:numbered: -:sectanchors: -:sectnums: -:icons: font -:hide-uri-scheme: -:docinfo: shared,private - -:sc-ext: java -:project-full-name: Spring Cloud Sleuth - -== Features -Sleuth sets up instrumentation not only to track timing, but also to catch -errors so that they can be analyzed or correlated with logs. This works the -same way regardless of if the error came from a common instrumented library, -such as `RestTemplate`, or your own code annotated with `@NewSpan` or similar. - -Below, we'll use the word Zipkin to describe the tracing system, and include -Zipkin screenshots. However, most services accepting https://zipkin.io/zipkin-api/#/default/post_spans[Zipkin format] -have similar base features. Sleuth can also be configured to send data in other -formats, something detailed later. - -=== Contextualizing errors -Without distributed tracing, it can be difficult to understand the impact of a -an exception. For example, it can be hard to know if a specific request caused -the caller to fail or not. - -Zipkin reduces time in triage by contextualizing errors and delays. - -Requests colored red in the search screen failed: - -image::{github-raw}/src/main/asciidoc/images/zipkin-error-traces.png[Error Traces] - -If you then click on one of the traces, you can understand if the failure -happened before the request hit another service or not: - -image::{github-raw}/src/main/asciidoc/images/zipkin-error-trace-screenshot.png[Error Traces Info propagation] - -For example, the above error happened in the "backend" service, and caused the -"frontend" service to fail. - -=== Log correlation -Sleuth configures the logging context with variables including the service name -(`%{spring.zipkin.service.name}`) and the trace ID (`%{traceId}`). These help -you connect logs with distributed traces and allow you choice in what tools you -use to troubleshoot your services. - -Once you find any log with an error, you can look for the trace ID in the -message. Paste that into Zipkin to visualize the entire trace, regardless of -how many services the first request ended up hitting. - -[source] +[indent=0] ---- -backend.log: 2020-04-09 17:45:40.516 ERROR [backend,5e8eeec48b08e26882aba313eb08f0a4,dcc1df555b5777b3,true] 97203 --- [nio-9000-exec-1] o.s.c.s.i.web.ExceptionLoggingFilter : Uncaught exception thrown -frontend.log:2020-04-09 17:45:40.574 ERROR [frontend,5e8eeec48b08e26882aba313eb08f0a4,82aba313eb08f0a4,true] 97192 --- [nio-8081-exec-2] o.s.c.s.i.web.ExceptionLoggingFilter : Uncaught exception thrown +2020-10-21 12:01:16.285 INFO [,0b6aaf642574edd3,0b6aaf642574edd3,true] 289589 --- [nio-9000-exec-1] DemoController : Handling home! ---- -Above, you'll notice the trace ID is `5e8eeec48b08e26882aba313eb08f0a4`, for -example. This log configuration was automatically setup by Sleuth. +NOTE: Instead of logging the request in the handler explicitly, you could set `logging.level.org.springframework.web.servlet.DispatcherServlet=DEBUG`. -=== Service Dependency Graph -When you consider distributed tracing tracks requests, it makes sense that -trace data can paint a picture of your architecture. +NOTE: Set `spring.application.name=myService` (for instance) to see the service name as well as the trace and span IDs. -Zipkin includes a tool to build service dependency diagrams from traces, -including the count of calls and how many errors exist. +== Documentation -The example application will make a simple diagram like this, but your real -environment diagram may be more complex. - -image::{github-raw}/docs/src/main/asciidoc/images/zipkin-dependencies.png[Zipkin Dependencies] - -*Note*: Production environments will generate a lot of data. You will likely -need to run a separate service to aggregate the dependency graph. You can learn -more https://github.com/openzipkin/zipkin-dependencies/[here]. - -=== Request scoped properties (Baggage) -Distributed tracing works by propagating fields inside and across services that -connect the trace together: traceId and spanId notably. The context that holds -these fields can optionally push other fields that need to be consistent -regardless of many services are touched. The simple name for these extra fields -is "Baggage". - -Sleuth allows you to define which baggage are permitted to exist in the trace -context, including what header names are used. - -The following example shows setting baggage values: - -[source,java] ----- -Span initialSpan = this.tracer.nextSpan().name("span").start(); -BUSINESS_PROCESS.updateValue(initialSpan.context(), "ALM"); -COUNTRY_CODE.updateValue(initialSpan.context(), "FO"); ----- - -IMPORTANT: There is currently no limitation of the count or size of baggage -items. Keep in mind that too many can decrease system throughput or increase -RPC latency. In extreme cases, too much baggage can crash the application, due -to exceeding transport-level message or header capacity. - - -==== Baggage versus Tags - -Like trace IDs, Baggage is attached to messages or requests, usually as -headers. Tags are key value pairs sent in a Span to Zipkin. Baggage values are -not added spans by default, which means you can't search based on Baggage -unless you opt-in. - -To make baggage also tags, use the property `spring.sleuth.baggage.tag-fields` -like so: -[source,yml] ----- -spring: - sleuth: - baggage: - foo: bar - remoteFields: - - country-code - - x-vcap-request-id - tagFields: - - country-code ----- - -:branch: master - -[[sleuth-adding-project]] -== Adding Sleuth to your Project - -This section addresses how to add Sleuth to your project with either Maven or Gradle. - -IMPORTANT: To ensure that your application name is properly displayed in Zipkin, set the `spring.application.name` property in `bootstrap.yml`. - -=== Sleuth with Zipkin via HTTP - -If you want both Sleuth and Zipkin, add the `spring-cloud-starter-zipkin` dependency. - -The following example shows how to do so for Maven: - -.Maven -[source,xml,indent=0,subs="verbatim,attributes",role="primary"] ----- - <1> - - - org.springframework.cloud - spring-cloud-dependencies - ${release.train.version} - pom - import - - - - - <2> - org.springframework.cloud - spring-cloud-starter-zipkin - ----- -<1> We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself. -<2> Add the dependency to `spring-cloud-starter-zipkin`. - -The following example shows how to do so for Gradle: - -.Gradle -[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] ----- -dependencyManagement { <1> - imports { - mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}" - } -} - -dependencies { <2> - compile "org.springframework.cloud:spring-cloud-starter-zipkin" -} ----- -<1> We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself. -<2> Add the dependency to `spring-cloud-starter-zipkin`. - -=== Sleuth with Zipkin over RabbitMQ or Kafka - -If you want to use RabbitMQ or Kafka instead of HTTP, add the `spring-rabbit` or `spring-kafka` dependency. -The default destination name is `zipkin`. - -If using Kafka, you must set the property `spring.zipkin.sender.type` property accordingly: - -[source,yaml] ----- -spring.zipkin.sender.type: kafka ----- - -CAUTION: `spring-cloud-sleuth-stream` is deprecated and incompatible with these destinations. - -If you want Sleuth over RabbitMQ, add the `spring-cloud-starter-zipkin` and `spring-rabbit` -dependencies. - -The following example shows how to do so for Gradle: - -.Maven -[source,xml,indent=0,subs="verbatim,attributes",role="primary"] ----- - <1> - - - org.springframework.cloud - spring-cloud-dependencies - ${release.train.version} - pom - import - - - - - <2> - org.springframework.cloud - spring-cloud-starter-zipkin - - <3> - org.springframework.amqp - spring-rabbit - ----- -<1> We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself. -<2> Add the dependency to `spring-cloud-starter-zipkin`. That way, all nested dependencies get downloaded. -<3> To automatically configure RabbitMQ, add the `spring-rabbit` dependency. - -.Gradle -[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] ----- -dependencyManagement { <1> - imports { - mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}" - } -} - -dependencies { - compile "org.springframework.cloud:spring-cloud-starter-zipkin" <2> - compile "org.springframework.amqp:spring-rabbit" <3> -} ----- -<1> We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself. -<2> Add the dependency to `spring-cloud-starter-zipkin`. That way, all nested dependencies get downloaded. -<3> To automatically configure RabbitMQ, add the `spring-rabbit` dependency. - -=== Overriding the auto-configuration of Zipkin - -Spring Cloud Sleuth supports sending traces to multiple tracing systems as of version 2.1.0. -In order to get this to work, every tracing system needs to have a `Reporter` and `Sender`. -If you want to override the provided beans you need to give them a specific name. -To do this you can use respectively `ZipkinAutoConfiguration.REPORTER_BEAN_NAME` and `ZipkinAutoConfiguration.SENDER_BEAN_NAME`. - -[source,java] ----- - -@Configuration -protected static class MyConfig { - - @Bean(ZipkinAutoConfiguration.REPORTER_BEAN_NAME) - Reporter myReporter() { - return AsyncReporter.create(mySender()); - } - - @Bean(ZipkinAutoConfiguration.SENDER_BEAN_NAME) - MySender mySender() { - return new MySender(); - } - - static class MySender extends Sender { - - private boolean spanSent = false; - - boolean isSpanSent() { - return this.spanSent; - } - - @Override - public Encoding encoding() { - return Encoding.JSON; - } - - @Override - public int messageMaxBytes() { - return Integer.MAX_VALUE; - } - - @Override - public int messageSizeInBytes(List encodedSpans) { - return encoding().listSizeInBytes(encodedSpans); - } - - @Override - public Call sendSpans(List encodedSpans) { - this.spanSent = true; - return Call.create(null); - } - - } - -} - ----- - -=== Only Sleuth (log correlation) - -If you want to use only Spring Cloud Sleuth without the Zipkin integration, add the `spring-cloud-starter-sleuth` module to your project. - -The following example shows how to add Sleuth with Maven: - -[source,xml,indent=0,subs="verbatim,attributes",role="primary"] -.Maven ----- - <1> - - - org.springframework.cloud - spring-cloud-dependencies - ${release.train.version} - pom - import - - - - - <2> - org.springframework.cloud - spring-cloud-starter-sleuth - ----- -<1> We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself. -<2> Add the dependency to `spring-cloud-starter-sleuth`. - -The following example shows how to add Sleuth with Gradle: - -.Gradle -[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] ----- -dependencyManagement { <1> - imports { - mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}" - } -} - -dependencies { <2> - compile "org.springframework.cloud:spring-cloud-starter-sleuth" -} ----- -<1> We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself. -<2> Add the dependency to `spring-cloud-starter-sleuth`. +Please visit the https://docs.spring.io/spring-cloud-sleuth/docs/[documentation page] to read more about the project. == Building @@ -540,11 +188,6 @@ The generated eclipse projects can be imported by selecting `import existing pro from the `file` menu. -IMPORTANT: Spring Cloud Sleuth uses two different versions of language level. Java 1.7 is used for main sources, and -Java 1.8 is used for tests. When importing your project to an IDE, you should activate the `ide` Maven profile to turn on -Java 1.8 for both main and test sources. You MUST NOT use Java 1.8 features in the main sources. If you do -so, your app breaks during the Maven build. - == Contributing :spring-cloud-build-branch: master diff --git a/benchmarks/pom.xml b/benchmarks/pom.xml index 9ba9847d0..2d5887f23 100644 --- a/benchmarks/pom.xml +++ b/benchmarks/pom.xml @@ -68,6 +68,10 @@ ${project.groupId} spring-cloud-starter-sleuth + + ${project.groupId} + spring-cloud-starter-sleuth-otel + org.springframework.boot spring-boot-starter-web diff --git a/benchmarks/src/main/java/org/springframework/cloud/sleuth/benchmarks/app/mvc/SleuthBenchmarkingSpringApp.java b/benchmarks/src/main/java/org/springframework/cloud/sleuth/benchmarks/app/mvc/SleuthBenchmarkingSpringApp.java index dd4481e1c..41494a6c9 100644 --- a/benchmarks/src/main/java/org/springframework/cloud/sleuth/benchmarks/app/mvc/SleuthBenchmarkingSpringApp.java +++ b/benchmarks/src/main/java/org/springframework/cloud/sleuth/benchmarks/app/mvc/SleuthBenchmarkingSpringApp.java @@ -25,9 +25,6 @@ import java.util.regex.Pattern; import javax.annotation.PreDestroy; -import brave.Span; -import brave.Tracer; -import brave.sampler.Sampler; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -41,6 +38,8 @@ import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.cloud.sleuth.annotation.ContinueSpan; import org.springframework.cloud.sleuth.annotation.NewSpan; import org.springframework.cloud.sleuth.annotation.SpanTag; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.instrument.web.SkipPatternProvider; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Bean; @@ -121,11 +120,6 @@ public class SleuthBenchmarkingSpringApp implements ApplicationListener, Message> simpleManual(Tracing tracing) { + public Function, Message> simpleManual(BeanFactory beanFactory) { System.out.println("simple_manual_function"); - return new SimpleManualFunction(tracing); + return new SimpleManualFunction(beanFactory); } @Bean(name = "myFlux") @ConditionalOnProperty(value = "spring.sleuth.function.type", havingValue = "reactive_simple_manual") - public Function>, Flux>> reactiveSimpleManual(Tracing tracing) { + public Function>, Flux>> reactiveSimpleManual(BeanFactory beanFactory) { System.out.println("simple_reactive_manual_function"); - return new SimpleReactiveManualFunction(tracing); + return new SimpleReactiveManualFunction(beanFactory); } @Bean(name = "myFlux") @@ -170,22 +170,22 @@ class SimpleManualFunction implements Function, Message> private static final Logger log = LoggerFactory.getLogger(SimpleFunction.class); - private final Tracing tracing; + private final BeanFactory beanFactory; - SimpleManualFunction(Tracing tracing) { - this.tracing = tracing; + SimpleManualFunction(BeanFactory beanFactory) { + this.beanFactory = beanFactory; } @Override public Message apply(Message input) { - return (MessagingSleuthOperators.asFunction(this.tracing, input) - .andThen(msg -> MessagingSleuthOperators.withSpanInScope(this.tracing, msg, stringMessage -> { + return (MessagingSleuthOperators.asFunction(this.beanFactory, input) + .andThen(msg -> MessagingSleuthOperators.withSpanInScope(this.beanFactory, msg, stringMessage -> { log.info("Hello from simple manual [{}]", stringMessage.getPayload()); return stringMessage; - })).andThen(msg -> MessagingSleuthOperators.afterMessageHandled(this.tracing, msg, null)) - .andThen(msg -> MessagingSleuthOperators.handleOutputMessage(this.tracing, msg)) + })).andThen(msg -> MessagingSleuthOperators.afterMessageHandled(this.beanFactory, msg, null)) + .andThen(msg -> MessagingSleuthOperators.handleOutputMessage(this.beanFactory, msg)) .andThen(msg -> MessageBuilder.createMessage(msg.getPayload().toUpperCase(), msg.getHeaders())) - .andThen(msg -> MessagingSleuthOperators.afterMessageHandled(this.tracing, msg, null)).apply(input)); + .andThen(msg -> MessagingSleuthOperators.afterMessageHandled(this.beanFactory, msg, null)).apply(input)); } } @@ -207,21 +207,21 @@ class SimpleReactiveManualFunction implements Function>, Fl private static final Logger log = LoggerFactory.getLogger(SimpleReactiveFunction.class); - private final Tracing tracing; + private final BeanFactory beanFactory; - SimpleReactiveManualFunction(Tracing tracing) { - this.tracing = tracing; + SimpleReactiveManualFunction(BeanFactory beanFactory) { + this.beanFactory = beanFactory; } @Override public Flux> apply(Flux> input) { - return input.map(message -> (MessagingSleuthOperators.asFunction(this.tracing, message)) - .andThen(msg -> MessagingSleuthOperators.withSpanInScope(this.tracing, msg, stringMessage -> { + return input.map(message -> (MessagingSleuthOperators.asFunction(this.beanFactory, message)) + .andThen(msg -> MessagingSleuthOperators.withSpanInScope(this.beanFactory, msg, stringMessage -> { log.info("Hello from simple manual [{}]", stringMessage.getPayload()); return stringMessage; - })).andThen(msg -> MessagingSleuthOperators.afterMessageHandled(this.tracing, msg, null)) + })).andThen(msg -> MessagingSleuthOperators.afterMessageHandled(this.beanFactory, msg, null)) .andThen(msg -> MessageBuilder.createMessage(msg.getPayload().toUpperCase(), msg.getHeaders())) - .andThen(msg -> MessagingSleuthOperators.handleOutputMessage(this.tracing, msg)).apply(message)); + .andThen(msg -> MessagingSleuthOperators.handleOutputMessage(this.beanFactory, msg)).apply(message)); } } diff --git a/benchmarks/src/main/java/org/springframework/cloud/sleuth/benchmarks/app/webflux/SleuthBenchmarkingSpringWebFluxApp.java b/benchmarks/src/main/java/org/springframework/cloud/sleuth/benchmarks/app/webflux/SleuthBenchmarkingSpringWebFluxApp.java index 062f68403..758c993ff 100644 --- a/benchmarks/src/main/java/org/springframework/cloud/sleuth/benchmarks/app/webflux/SleuthBenchmarkingSpringWebFluxApp.java +++ b/benchmarks/src/main/java/org/springframework/cloud/sleuth/benchmarks/app/webflux/SleuthBenchmarkingSpringWebFluxApp.java @@ -20,9 +20,6 @@ import java.time.Duration; import java.util.regex.Pattern; import java.util.stream.Collectors; -import brave.handler.SpanHandler; -import brave.propagation.TraceContext; -import brave.sampler.Sampler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import reactor.core.publisher.Flux; @@ -37,6 +34,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory; import org.springframework.boot.web.reactive.context.ReactiveWebServerInitializedEvent; +import org.springframework.cloud.sleuth.api.TraceContext; import org.springframework.cloud.sleuth.instrument.web.SkipPatternProvider; import org.springframework.cloud.sleuth.instrument.web.WebFluxSleuthOperators; import org.springframework.context.ApplicationListener; @@ -73,11 +71,6 @@ public class SleuthBenchmarkingSpringWebFluxApp implements ApplicationListener Pattern.compile(""); @@ -89,13 +82,6 @@ public class SleuthBenchmarkingSpringWebFluxApp implements ApplicationListener log.info("Doing assertions")); TraceContext traceContext = signal.getContext().get(TraceContext.class); Assert.notNull(traceContext, "Context must be set by Sleuth instrumentation"); - Assert.state(traceContext.traceIdString().equals("4883117762eb9420"), "TraceId must be propagated"); + Assert.state(traceContext.traceId().equals("4883117762eb9420"), "TraceId must be propagated"); log.info("Assertions passed"); }); } diff --git a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/SampleTests.java b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/SampleTests.java index 086403988..1dcb57e7d 100644 --- a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/SampleTests.java +++ b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/SampleTests.java @@ -96,6 +96,7 @@ public class SampleTests { protected String[] runArgs() { List strings = new ArrayList<>(); strings.addAll(Arrays.asList("--spring.jmx.enabled=false", + "--spring.autoconfigure.exclude=org.springframework.cloud.sleuth.otel.autoconfig.TraceOtelAutoConfiguration", "--spring.application.name=defaultTraceContextForStream" + instrumentation.name())); strings.addAll(instrumentation.entires.stream().map(s -> "--" + s).collect(Collectors.toList())); return strings.toArray(new String[0]); @@ -151,7 +152,7 @@ public class SampleTests { } - @Configuration + @Configuration(proxyBeanMethods = false) @Import(TestChannelBinderConfiguration.class) static class TestConfiguration { diff --git a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/TracerImplementation.java b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/TracerImplementation.java new file mode 100644 index 000000000..07915b422 --- /dev/null +++ b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/TracerImplementation.java @@ -0,0 +1,44 @@ +/* + * Copyright 2016-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.benchmarks.jmh; + +import org.springframework.cloud.sleuth.brave.autoconfig.TraceBraveAutoConfiguration; +import org.springframework.cloud.sleuth.otel.autoconfig.TraceOtelAutoConfiguration; + +public enum TracerImplementation { + + otel(TraceBraveAutoConfiguration.class.getCanonicalName()), brave( + TraceOtelAutoConfiguration.class.getCanonicalName()); + + private String key = "spring.autoconfigure.exclude"; + + private String value; + + TracerImplementation(String value) { + this.value = value; + } + + public String property() { + return "--" + this.key + "=" + this.value; + } + + @Override + public String toString() { + return this.name(); + } + +} \ No newline at end of file diff --git a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/bridge/BridgeTests.java b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/bridge/BridgeTests.java new file mode 100644 index 000000000..1b4735012 --- /dev/null +++ b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/bridge/BridgeTests.java @@ -0,0 +1,135 @@ +/* + * Copyright 2016-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.benchmarks.jmh.bridge; + +import jmh.mbr.junit5.Microbenchmark; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.cloud.sleuth.benchmarks.jmh.TracerImplementation; +import org.springframework.cloud.sleuth.brave.autoconfig.TraceBraveAutoConfiguration; +import org.springframework.cloud.sleuth.brave.bridge.TraceBraveBridgeAutoConfiguation; +import org.springframework.cloud.sleuth.brave.propagation.TraceBravePropagationAutoConfiguration; +import org.springframework.cloud.sleuth.otel.autoconfig.TraceOtelAutoConfiguration; +import org.springframework.cloud.sleuth.otel.bridge.TraceOtelBridgeAutoConfiguation; +import org.springframework.cloud.sleuth.otel.log.TraceOtelLogAutoConfiguration; +import org.springframework.cloud.sleuth.otel.propagation.TraceOtelPropagationAutoConfiguration; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.BDDAssertions.then; + +@Measurement(iterations = 5) +@Warmup(iterations = 1) +@Fork(value = 2, warmups = 0) +@BenchmarkMode(Mode.AverageTime) +@Microbenchmark +public class BridgeTests { + + @Benchmark + public void should_create_next_span(BenchmarkContext context) throws Exception { + Tracer tracer = context.tracer; + Span span = tracer.nextSpan().start(); + try { + then(span).isNotNull(); + } + finally { + if (span != null) { + span.end(); + } + } + } + + @Benchmark + public void should_create_next_span_with_parent(BenchmarkContext context) throws Exception { + Tracer tracer = context.tracer; + Span span = tracer.nextSpan(context.parent).start(); + try { + then(span).isNotNull(); + } + finally { + if (span != null) { + span.end(); + } + } + } + + @Benchmark + public void should_retrieve_current_span_from_scope(BenchmarkContext context) throws Exception { + Tracer tracer = context.tracer; + Span span = context.parent; + try (Tracer.SpanInScope ws = tracer.withSpan(span)) { + then(tracer.currentSpan().context().spanId()).isEqualTo(span.context().spanId()); + } + } + + @State(Scope.Benchmark) + public static class BenchmarkContext { + + volatile ConfigurableApplicationContext withSleuth; + + volatile Tracer tracer; + + volatile Span parent; + + @Param + private TracerImplementation tracerImplementation; + + @Setup + public void setup() { + SpringApplication application = new SpringApplication(TestConfiguration.class); + application.setWebApplicationType(WebApplicationType.NONE); + this.withSleuth = application.run("--spring.jmx.enabled=false", this.tracerImplementation.property(), + "--spring.application.name=withSleuth_" + this.tracerImplementation.name()); + this.tracer = this.withSleuth.getBean(Tracer.class); + this.parent = this.tracer.nextSpan().name("name").start(); + } + + @TearDown + public void clean() { + this.withSleuth.close(); + this.parent.end(); + } + + @Configuration(proxyBeanMethods = false) + @ImportAutoConfiguration({ TraceAutoConfiguration.class, TraceBraveAutoConfiguration.class, + TraceBraveBridgeAutoConfiguation.class, TraceBravePropagationAutoConfiguration.class, + TraceOtelAutoConfiguration.class, TraceOtelBridgeAutoConfiguation.class, + TraceOtelPropagationAutoConfiguration.class, TraceOtelLogAutoConfiguration.class, + TraceOtelLogAutoConfiguration.class }) + static class TestConfiguration { + + } + + } + +} diff --git a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/mvc/AnnotationBenchmarksTests.java b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/mvc/AnnotationBenchmarksTests.java index 5b3f52fb3..f6b85f006 100644 --- a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/mvc/AnnotationBenchmarksTests.java +++ b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/mvc/AnnotationBenchmarksTests.java @@ -25,6 +25,7 @@ import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; @@ -34,6 +35,7 @@ import org.openjdk.jmh.annotations.Warmup; import org.springframework.boot.SpringApplication; import org.springframework.cloud.sleuth.benchmarks.app.mvc.SleuthBenchmarkingSpringApp; +import org.springframework.cloud.sleuth.benchmarks.jmh.TracerImplementation; import org.springframework.context.ConfigurableApplicationContext; import static org.assertj.core.api.BDDAssertions.then; @@ -64,10 +66,14 @@ public class AnnotationBenchmarksTests { volatile SleuthBenchmarkingSpringApp sleuth; + @Param + private TracerImplementation tracerImplementation; + @Setup public void setup() { this.withSleuth = new SpringApplication(SleuthBenchmarkingSpringApp.class).run("--spring.jmx.enabled=false", - "--spring.application.name=withSleuth"); + this.tracerImplementation.property(), + "--spring.application.name=withSleuth_" + this.tracerImplementation.name()); this.sleuth = this.withSleuth.getBean(SleuthBenchmarkingSpringApp.class); } diff --git a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/mvc/AsyncBenchmarksTests.java b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/mvc/AsyncBenchmarksTests.java index fac91d1e7..c8c970be3 100644 --- a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/mvc/AsyncBenchmarksTests.java +++ b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/mvc/AsyncBenchmarksTests.java @@ -25,6 +25,7 @@ import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; @@ -34,6 +35,7 @@ import org.openjdk.jmh.annotations.Warmup; import org.springframework.boot.SpringApplication; import org.springframework.cloud.sleuth.benchmarks.app.mvc.SleuthBenchmarkingSpringApp; +import org.springframework.cloud.sleuth.benchmarks.jmh.TracerImplementation; import org.springframework.context.ConfigurableApplicationContext; import static org.assertj.core.api.BDDAssertions.then; @@ -68,10 +70,14 @@ public class AsyncBenchmarksTests { volatile SleuthBenchmarkingSpringApp untracedAsyncMethodHavingBean; + @Param + private TracerImplementation tracerImplementation; + @Setup public void setup() { this.withSleuth = new SpringApplication(SleuthBenchmarkingSpringApp.class).run("--spring.jmx.enabled=false", - "--spring.application.name=withSleuth"); + this.tracerImplementation.property(), + "--spring.application.name=withSleuth_" + this.tracerImplementation.name()); this.withoutSleuth = new SpringApplication(SleuthBenchmarkingSpringApp.class).run( "--spring.jmx.enabled=false", "--spring.application.name=withoutSleuth", "--spring.sleuth.enabled=false", "--spring.sleuth.async.enabled=false"); diff --git a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/mvc/HttpFilterBenchmarksTests.java b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/mvc/HttpFilterBenchmarksTests.java index 54308a7bd..89eb3c9f9 100644 --- a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/mvc/HttpFilterBenchmarksTests.java +++ b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/mvc/HttpFilterBenchmarksTests.java @@ -27,7 +27,6 @@ import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; -import brave.servlet.TracingFilter; import jmh.mbr.junit5.Microbenchmark; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -35,6 +34,7 @@ import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; @@ -44,6 +44,8 @@ import org.openjdk.jmh.annotations.Warmup; import org.springframework.boot.SpringApplication; import org.springframework.cloud.sleuth.benchmarks.app.mvc.SleuthBenchmarkingSpringApp; +import org.springframework.cloud.sleuth.benchmarks.jmh.TracerImplementation; +import org.springframework.cloud.sleuth.instrument.web.servlet.TracingFilter; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.MediaType; import org.springframework.mock.web.MockFilterChain; @@ -131,10 +133,14 @@ public class HttpFilterBenchmarksTests { volatile MockMvc mockMvcForUntracedController; + @Param + private TracerImplementation tracerImplementation; + @Setup public void setup() { this.withSleuth = new SpringApplication(SleuthBenchmarkingSpringApp.class).run("--spring.jmx.enabled=false", - "--spring.application.name=withSleuth"); + this.tracerImplementation.property(), + "--spring.application.name=withSleuth_" + this.tracerImplementation.name()); this.tracingFilter = this.withSleuth.getBean(TracingFilter.class); this.mockMvcForTracedController = MockMvcBuilders .standaloneSetup(this.withSleuth.getBean(SleuthBenchmarkingSpringApp.class)).build(); diff --git a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/mvc/RestTemplateBenchmarkTests.java b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/mvc/RestTemplateBenchmarkTests.java index a4a6b3ca3..64e6f0b20 100644 --- a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/mvc/RestTemplateBenchmarkTests.java +++ b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/mvc/RestTemplateBenchmarkTests.java @@ -22,7 +22,6 @@ import java.util.concurrent.TimeUnit; import javax.servlet.ServletException; -import brave.spring.web.TracingClientHttpRequestInterceptor; import jmh.mbr.junit5.Microbenchmark; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -30,6 +29,7 @@ import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; @@ -39,6 +39,8 @@ import org.openjdk.jmh.annotations.Warmup; import org.springframework.boot.SpringApplication; import org.springframework.cloud.sleuth.benchmarks.app.mvc.SleuthBenchmarkingSpringApp; +import org.springframework.cloud.sleuth.benchmarks.jmh.TracerImplementation; +import org.springframework.cloud.sleuth.instrument.web.mvc.TracingClientHttpRequestInterceptor; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.test.web.client.MockMvcClientHttpRequestFactory; import org.springframework.test.web.servlet.MockMvc; @@ -80,10 +82,14 @@ public class RestTemplateBenchmarkTests { volatile RestTemplate untracedTemplate; + @Param + private TracerImplementation tracerImplementation; + @Setup public void setup() { this.withSleuth = new SpringApplication(SleuthBenchmarkingSpringApp.class).run("--spring.jmx.enabled=false", - "--spring.application.name=withSleuth"); + this.tracerImplementation.property(), + "--spring.application.name=withSleuth_" + this.tracerImplementation.name()); this.mockMvc = MockMvcBuilders.standaloneSetup(this.withSleuth.getBean(SleuthBenchmarkingSpringApp.class)) .build(); this.tracedTemplate = new RestTemplate(new MockMvcClientHttpRequestFactory(this.mockMvc)); diff --git a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/mvc/StartupBenchmarkTests.java b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/mvc/StartupBenchmarkTests.java index 24f454bc5..b55be9638 100644 --- a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/mvc/StartupBenchmarkTests.java +++ b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/mvc/StartupBenchmarkTests.java @@ -24,12 +24,14 @@ import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Level; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.annotations.Warmup; import org.springframework.cloud.sleuth.benchmarks.jmh.ProcessLauncherState; +import org.springframework.cloud.sleuth.benchmarks.jmh.TracerImplementation; @Measurement(iterations = 5) @Warmup(iterations = 1) @@ -46,33 +48,36 @@ public class StartupBenchmarkTests { @Benchmark public void withoutAnnotations(ApplicationState state) throws Exception { - state.setExtraArgs("--spring.sleuth.annotation.enabled=false"); + state.setExtraArgs("--spring.sleuth.annotation.enabled=false", state.tracerImplementation.property()); state.run(); } @Benchmark public void withoutAsync(ApplicationState state) throws Exception { - state.setExtraArgs("--spring.sleuth.async.enabled=false", "--spring.sleuth.annotation.enabled=false"); + state.setExtraArgs("--spring.sleuth.async.enabled=false", "--spring.sleuth.annotation.enabled=false", state.tracerImplementation.property()); state.run(); } @Benchmark public void withoutScheduled(ApplicationState state) throws Exception { state.setExtraArgs("--spring.sleuth.scheduled.enabled=false", "--spring.sleuth.async.enabled=false", - "--spring.sleuth.annotation.enabled=false"); + "--spring.sleuth.annotation.enabled=false", state.tracerImplementation.property()); state.run(); } @Benchmark public void withoutWeb(ApplicationState state) throws Exception { state.setExtraArgs("--spring.sleuth.web.enabled=false", "--spring.sleuth.scheduled.enabled=false", - "--spring.sleuth.async.enabled=false", "--spring.sleuth.annotation.enabled=false"); + "--spring.sleuth.async.enabled=false", "--spring.sleuth.annotation.enabled=false", state.tracerImplementation.property()); state.run(); } @State(Scope.Benchmark) public static class ApplicationState extends ProcessLauncherState { + @Param + private TracerImplementation tracerImplementation; + public ApplicationState() { super("target", "--server.port=0"); } diff --git a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/stream/MicroBenchmarkStreamTests.java b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/stream/MicroBenchmarkStreamTests.java index 1717e8a1f..c81f7a1bc 100644 --- a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/stream/MicroBenchmarkStreamTests.java +++ b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/stream/MicroBenchmarkStreamTests.java @@ -44,6 +44,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.sleuth.benchmarks.app.stream.SleuthBenchmarkingStreamApplication; +import org.springframework.cloud.sleuth.benchmarks.jmh.TracerImplementation; import org.springframework.cloud.stream.binder.test.InputDestination; import org.springframework.cloud.stream.binder.test.OutputDestination; import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration; @@ -82,6 +83,9 @@ public class MicroBenchmarkStreamTests { @Param private Instrumentation instrumentation; + @Param + private TracerImplementation tracerImplementation; + @Setup public void setup() { this.applicationContext = initContext(); @@ -104,7 +108,8 @@ public class MicroBenchmarkStreamTests { protected String[] runArgs() { List strings = new ArrayList<>(); strings.addAll(Arrays.asList("--spring.jmx.enabled=false", - "--spring.application.name=defaultTraceContextForStream" + instrumentation.name())); + this.tracerImplementation.property(), + "--spring.application.name=defaultTraceContextForStream" + instrumentation.name() + "_" + tracerImplementation.name())); strings.addAll(instrumentation.entires.stream().map(s -> "--" + s).collect(Collectors.toList())); return strings.toArray(new String[0]); } @@ -173,7 +178,7 @@ public class MicroBenchmarkStreamTests { } - @Configuration + @Configuration(proxyBeanMethods = false) @Import(TestChannelBinderConfiguration.class) static class TestConfiguration { diff --git a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/webflux/MicroBenchmarkHttpTests.java b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/webflux/MicroBenchmarkHttpTests.java index 360b8baf7..296eb335f 100644 --- a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/webflux/MicroBenchmarkHttpTests.java +++ b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/webflux/MicroBenchmarkHttpTests.java @@ -38,6 +38,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.sleuth.benchmarks.app.webflux.SleuthBenchmarkingSpringWebFluxApp; +import org.springframework.cloud.sleuth.benchmarks.jmh.TracerImplementation; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.test.web.reactive.server.WebTestClient; @@ -65,6 +66,9 @@ public class MicroBenchmarkHttpTests { @Param private Instrumentation instrumentation; + @Param + private TracerImplementation tracerImplementation; + @Setup public void setup() { this.applicationContext = initContext(); @@ -79,12 +83,13 @@ public class MicroBenchmarkHttpTests { protected String[] runArgs() { return new String[] { "--spring.jmx.enabled=false", - "--spring.application.name=defaultTraceContext" + instrumentation.name(), + tracerImplementation.property(), + "--spring.application.name=defaultTraceContext" + instrumentation.name() + "_" + tracerImplementation.name(), "--" + instrumentation.key + "=" + instrumentation.value }; } void run() { - this.webTestClient.get().uri(this.instrumentation.url).header("X-B3-TraceId", "4883117762eb9420") + this.webTestClient.get().uri(instrumentation.url).header("X-B3-TraceId", "4883117762eb9420") .header("X-B3-SpanId", "4883117762eb9420").exchange().expectStatus().isOk(); } diff --git a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/webflux/SpringWebFluxBenchmarksTests.java b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/webflux/SpringWebFluxBenchmarksTests.java index e9d65b4fe..6e3a1d2b1 100644 --- a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/webflux/SpringWebFluxBenchmarksTests.java +++ b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/webflux/SpringWebFluxBenchmarksTests.java @@ -52,6 +52,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.sleuth.benchmarks.app.webflux.SleuthBenchmarkingSpringWebFluxApp; +import org.springframework.cloud.sleuth.benchmarks.jmh.TracerImplementation; import org.springframework.context.ConfigurableApplicationContext; @Measurement(iterations = 5, time = 1) @@ -137,7 +138,7 @@ public class SpringWebFluxBenchmarksTests { } protected String[] runArgs() { - return new String[] { "--spring.jmx.enabled=false", "--spring.application.name=defaultTraceContext", + return new String[] { "--spring.jmx.enabled=false", "--spring.application.name=defaultTraceContext", TracerImplementation.brave.toString(), "--spring.sleuth.enabled=true" }; } diff --git a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/webflux/WithOutReactorSleuthSpringWebFluxBenchmarksTests.java b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/webflux/WithOutReactorSleuthSpringWebFluxBenchmarksTests.java index ee2bcd1f3..a2ae976c0 100644 --- a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/webflux/WithOutReactorSleuthSpringWebFluxBenchmarksTests.java +++ b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/webflux/WithOutReactorSleuthSpringWebFluxBenchmarksTests.java @@ -22,6 +22,8 @@ import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.springframework.cloud.sleuth.benchmarks.jmh.TracerImplementation; + /** * @author alvin */ @@ -38,6 +40,7 @@ public class WithOutReactorSleuthSpringWebFluxBenchmarksTests extends SpringWebF @Override protected String[] runArgs() { return new String[] { "--spring.jmx.enabled=false", "--spring.application.name=defaultTraceContext", + TracerImplementation.brave.toString(), "--spring.sleuth.enabled=true", "--spring.sleuth.reactor.enabled=false" }; diff --git a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/webflux/WithOutSleuthSpringWebFluxBenchmarksTests.java b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/webflux/WithOutSleuthSpringWebFluxBenchmarksTests.java index d4477d05d..7ea130c2e 100644 --- a/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/webflux/WithOutSleuthSpringWebFluxBenchmarksTests.java +++ b/benchmarks/src/test/java/org/springframework/cloud/sleuth/benchmarks/jmh/webflux/WithOutSleuthSpringWebFluxBenchmarksTests.java @@ -22,6 +22,8 @@ import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.springframework.cloud.sleuth.benchmarks.jmh.TracerImplementation; + /** * @author alvin */ @@ -37,7 +39,7 @@ public class WithOutSleuthSpringWebFluxBenchmarksTests extends SpringWebFluxBenc @Override protected String[] runArgs() { - return new String[] { "--spring.jmx.enabled=false", "--spring.application.name=defaultTraceContext", + return new String[] { "--spring.jmx.enabled=false", "--spring.application.name=defaultTraceContext", TracerImplementation.brave.toString(), "--spring.sleuth.enabled=false" }; } diff --git a/docs/src/main/asciidoc/README.adoc b/docs/src/main/asciidoc/README.adoc index b5c47cea1..63c63d560 100644 --- a/docs/src/main/asciidoc/README.adoc +++ b/docs/src/main/asciidoc/README.adoc @@ -1,71 +1,94 @@ :jdkversion: 1.8 -:github-tag: master -:github-repo: spring-cloud/spring-cloud-sleuth -:github-raw: https://raw.githubusercontent.com/{github-repo}/{github-tag} -:github-code: https://github.com/{github-repo}/tree/{github-tag} - -image::https://circleci.com/gh/spring-cloud/spring-cloud-sleuth.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-sleuth"] -image::https://codecov.io/gh/spring-cloud/spring-cloud-sleuth/branch/{github-tag}/graph/badge.svg["codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-sleuth"] -image::https://badges.gitter.im/spring-cloud/spring-cloud-sleuth.svg[Gitter, link="https://gitter.im/spring-cloud/spring-cloud-sleuth?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"] +image::https://circleci.com/gh/spring-cloud/spring-cloud-sleuth.svg?style=svg["CircleCI",link="https://circleci.com/gh/spring-cloud/spring-cloud-sleuth"] +image::https://codecov.io/gh/spring-cloud/spring-cloud-sleuth/branch/{branch}/graph/badge.svg["codecov",link="https://codecov.io/gh/spring-cloud/spring-cloud-sleuth"] +image::https://badges.gitter.im/spring-cloud/spring-cloud-sleuth.svg[Gitter,link="https://gitter.im/spring-cloud/spring-cloud-sleuth?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"] == Spring Cloud Sleuth -Spring Cloud Sleuth provides Spring Boot auto-configuration for distributed -tracing. Underneath, Spring Cloud Sleuth is a layer over a tracer library named -https://github.com/openzipkin/brave[Brave]. +Spring Cloud Sleuth provides Spring Boot auto-configuration for distributed tracing. -Sleuth configures everything you need to get started. This includes where trace -data (spans) are reported to, how many traces to keep (sampling), if remote -fields (baggage) are sent, and which libraries are traced. +Sleuth configures everything you need to get started. +This includes where trace data (spans) are reported to, how many traces to keep (sampling), if remote fields (baggage) are sent, and which libraries are traced. === Quick Start -Add sleuth to the classpath of a Spring Boot application -(see "`<>`" for Maven and Gradle examples), and you will -see trace IDs in logs. +Add Spring Cloud Sleuth to the classpath of a Spring Boot application (together with a Tracer implementation) and you will see trace IDs in logs. +Example of Sleuth with Brave tracer: -For example, consider the following HTTP handler: +[source,xml,indent=0,subs="verbatim,quotes,attributes"] +---- + + + + + org.springframework.cloud + spring-cloud-dependencies + + ${release.train.version} + pom + import + + + -[source,java] + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + + +---- + +Consider the following HTTP handler: + +[source,java,indent=0] ---- @RestController public class DemoController { - private static Logger log = LoggerFactory.getLogger(DemoController.class); + private static Logger log = LoggerFactory.getLogger(DemoController.class); - @RequestMapping("/") - public String home() { - log.info("Handling home"); - ... - return "Hello World"; - } + @RequestMapping("/") + public String home() { + log.info("Handling home"); + return "Hello World"; + } } ---- If you add that handler to a controller, you can see the calls to `home()` -being traced in the logs as well in https://zipkin.io/[Zipkin], if configured. +being traced in the logs (notice the `0b6aaf642574edd3` ids). -NOTE: Instead of logging the request in the handler explicitly, you -could set `logging.level.org.springframework.web.servlet.DispatcherServlet=DEBUG`. +[indent=0] +---- +2020-10-21 12:01:16.285 INFO [,0b6aaf642574edd3,0b6aaf642574edd3,true] 289589 --- [nio-9000-exec-1] DemoController : Handling home! +---- -NOTE: Set `spring.application.name=myService` (for instance) to see the service -name as well as the trace and span IDs. +NOTE: Instead of logging the request in the handler explicitly, you could set `logging.level.org.springframework.web.servlet.DispatcherServlet=DEBUG`. -include::overview.adoc[] +NOTE: Set `spring.application.name=myService` (for instance) to see the service name as well as the trace and span IDs. -include::features.adoc[] +== Documentation -include::setup.adoc[] +Please visit the https://docs.spring.io/spring-cloud-sleuth/docs/[documentation page] to read more about the project. == Building include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/building.adoc[] -IMPORTANT: Spring Cloud Sleuth uses two different versions of language level. Java 1.7 is used for main sources, and -Java 1.8 is used for tests. When importing your project to an IDE, you should activate the `ide` Maven profile to turn on -Java 1.8 for both main and test sources. You MUST NOT use Java 1.8 features in the main sources. If you do -so, your app breaks during the Maven build. - == Contributing include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/contributing.adoc[] diff --git a/docs/src/main/asciidoc/_attributes.adoc b/docs/src/main/asciidoc/_attributes.adoc index a55fa4df7..ed836ba7d 100644 --- a/docs/src/main/asciidoc/_attributes.adoc +++ b/docs/src/main/asciidoc/_attributes.adoc @@ -12,4 +12,14 @@ :docinfo: shared,private :sc-ext: java -:project-full-name: Spring Cloud Sleuth \ No newline at end of file +:project-full-name: Spring Cloud Sleuth + +// project-specific attributes +:core_path: {project-root} +:docs_path: {project-root}/docs +:brave_path: {project-root}/spring-cloud-sleuth-brave +:otel_path: {project-root}/spring-cloud-sleuth-otel +:tests_path: {core_path}/tests +:common_tests_path: {tests_path}/common +:brave_tests_path: {tests_path}/brave +:otel_tests_path: {tests_path}/otel diff --git a/docs/src/main/asciidoc/_configprops.adoc b/docs/src/main/asciidoc/_configprops.adoc index 804095f5c..9ca3fbc2d 100644 --- a/docs/src/main/asciidoc/_configprops.adoc +++ b/docs/src/main/asciidoc/_configprops.adoc @@ -4,31 +4,34 @@ |spring.sleuth.async.configurer.enabled | true | Enable default AsyncConfigurer. |spring.sleuth.async.enabled | true | Enable instrumenting async related components so that the tracing information is passed between threads. |spring.sleuth.async.ignored-beans | | List of {@link java.util.concurrent.Executor} bean names that should be ignored and not wrapped in a trace representation. -|spring.sleuth.baggage.correlation-enabled | true | Adds a {@link CorrelationScopeDecorator} to put baggage values into the correlation context. -|spring.sleuth.baggage.correlation-fields | | A list of {@link BaggageField#name() fields} to add to correlation (MDC) context. @see CorrelationScopeConfig.SingleCorrelationField#create(BaggageField) -|spring.sleuth.baggage.local-fields | | Same as {@link #remoteFields} except that this field is not propagated to remote services. @see BaggagePropagationConfig.SingleBaggageField#local(BaggageField) -|spring.sleuth.baggage.remote-fields | | List of fields that are referenced the same in-process as it is on the wire. For example, the field "x-vcap-request-id" would be set as-is including the prefix. @see BaggagePropagationConfig.SingleBaggageField#remote(BaggageField) @see BaggagePropagationConfig.SingleBaggageField.Builder#addKeyName(String) -|spring.sleuth.baggage.tag-fields | | A list of {@link BaggageField#name() fields} to tag into the span. @see Tags#BAGGAGE_FIELD +|spring.sleuth.baggage.correlation-enabled | true | context. +|spring.sleuth.baggage.correlation-fields | | +|spring.sleuth.baggage.local-fields | | +|spring.sleuth.baggage.remote-fields | | List of fields that are referenced the same in-process as it is on the wire. For example, the field "x-vcap-request-id" would be set as-is including the prefix. +|spring.sleuth.baggage.tag-fields | | |spring.sleuth.circuitbreaker.enabled | true | Enable Spring Cloud CircuitBreaker instrumentation. |spring.sleuth.enabled | true | |spring.sleuth.feign.enabled | true | Enable span information propagation when using Feign. |spring.sleuth.feign.processor.enabled | true | Enable post processor that wraps Feign Context in its tracing representations. |spring.sleuth.grpc.enabled | true | Enable span information propagation when using GRPC. |spring.sleuth.http.enabled | true | -|spring.sleuth.http.legacy.enabled | false | |spring.sleuth.integration.enabled | true | Enable Spring Integration sleuth instrumentation. |spring.sleuth.integration.patterns | [!hystrixStreamOutput*, *, !channel*] | An array of patterns against which channel names will be matched. @see org.springframework.integration.config.GlobalChannelInterceptor#patterns() Defaults to any channel name not matching the Hystrix Stream and functional Stream channel names. |spring.sleuth.integration.websockets.enabled | true | Enable tracing for WebSockets. -|spring.sleuth.messaging.enabled | false | Should messaging be turned on. |spring.sleuth.messaging.jms.enabled | true | Enable tracing of JMS. -|spring.sleuth.messaging.jms.remote-service-name | jms | |spring.sleuth.messaging.kafka.enabled | true | Enable tracing of Kafka. |spring.sleuth.messaging.kafka.mapper.enabled | true | Enable DefaultKafkaHeaderMapper tracing for Kafka. -|spring.sleuth.messaging.kafka.remote-service-name | kafka | +|spring.sleuth.messaging.messaging.enabled | false | Should messaging be turned on. +|spring.sleuth.messaging.messaging.jms.enabled | false | +|spring.sleuth.messaging.messaging.jms.remote-service-name | jms | +|spring.sleuth.messaging.messaging.kafka.enabled | false | +|spring.sleuth.messaging.messaging.kafka.remote-service-name | kafka | +|spring.sleuth.messaging.messaging.rabbit.enabled | false | +|spring.sleuth.messaging.messaging.rabbit.remote-service-name | rabbitmq | |spring.sleuth.messaging.rabbit.enabled | true | Enable tracing of RabbitMQ. -|spring.sleuth.messaging.rabbit.remote-service-name | rabbitmq | |spring.sleuth.mongodb.enabled | true | Enable tracing for MongoDb. |spring.sleuth.opentracing.enabled | true | +|spring.sleuth.propagation.type | | Type of propagation. |spring.sleuth.quartz.enabled | true | Enable tracing for Quartz. |spring.sleuth.reactor.decorate-on-each | true | When true decorates on each operator, will be less performing, but logging will always contain the tracing entries in each operator. When false decorates on last operator, will be more performing, but logging might not always contain the tracing entries. @deprecated use explicit value via {@link SleuthReactorProperties#instrumentationType} |spring.sleuth.reactor.enabled | true | When true enables instrumentation for reactor. @@ -43,16 +46,16 @@ |spring.sleuth.sampler.refresh.enabled | true | Enable refresh scope for sampler. |spring.sleuth.scheduled.enabled | true | Enable tracing for {@link org.springframework.scheduling.annotation.Scheduled}. |spring.sleuth.scheduled.skip-pattern | | Pattern for the fully qualified name of a class that should be skipped. -|spring.sleuth.span-handler.additional-span-name-patterns-to-ignore | | Additional list of span names to ignore. Will be appended to {@link #spanNamePatternsToSkip}. -|spring.sleuth.span-handler.enabled | false | Will turn on the default Sleuth handler mechanism. Might ignore exporting of certain spans; -|spring.sleuth.span-handler.span-name-patterns-to-skip | ^catalogWatchTaskScheduler$ | List of span names to ignore. They will not be sent to external systems. +|spring.sleuth.span-filter.additional-span-name-patterns-to-ignore | | Additional list of span names to ignore. Will be appended to {@link #spanNamePatternsToSkip}. +|spring.sleuth.span-filter.enabled | false | Will turn on the default Sleuth handler mechanism. Might ignore exporting of certain spans; +|spring.sleuth.span-filter.span-name-patterns-to-skip | ^catalogWatchTaskScheduler$ | List of span names to ignore. They will not be sent to external systems. |spring.sleuth.supports-join | true | True means the tracing system supports sharing a span ID between a client and server. |spring.sleuth.trace-id128 | false | When true, generate 128-bit trace IDs instead of 64-bit ones. |spring.sleuth.web.additional-skip-pattern | | Additional pattern for URLs that should be skipped in tracing. This will be appended to the {@link SleuthWebProperties#skipPattern}. |spring.sleuth.web.client.enabled | true | Enable interceptor injecting into {@link org.springframework.web.client.RestTemplate}. |spring.sleuth.web.client.skip-pattern | | Pattern for URLs that should be skipped in client side tracing. |spring.sleuth.web.enabled | true | When true enables instrumentation for web applications. -|spring.sleuth.web.filter-order | | Order in which the tracing filters should be registered. Defaults to {@link TraceHttpAutoConfiguration#TRACING_FILTER_ORDER}. +|spring.sleuth.web.filter-order | | Order in which the tracing filters should be registered. Defaults to {@link TraceWebServletAutoConfiguration#TRACING_FILTER_ORDER}. |spring.sleuth.web.ignore-auto-configured-skip-patterns | false | If set to true, auto-configured skip patterns will be ignored. @see SkipPatternConfiguration |spring.sleuth.web.skip-pattern | /api-docs.*\|/swagger.*\|.*\.png\|.*\.css\|.*\.js\|.*\.html\|/favicon.ico\|/hystrix.stream | Pattern for URLs that should be skipped in tracing. |spring.sleuth.web.webclient.enabled | true | Enable tracing instrumentation for WebClient. diff --git a/docs/src/main/asciidoc/_index.adoc b/docs/src/main/asciidoc/_index.adoc new file mode 100644 index 000000000..bd53a447d --- /dev/null +++ b/docs/src/main/asciidoc/_index.adoc @@ -0,0 +1,18 @@ +[[spring-cloud-sleuth-reference-documentation]] += Spring Cloud Sleuth Reference Documentation +Adrian Cole, Spencer Gibb, Marcin Grzejszczak, Dave Syer, Jay Bryant + +:docinfo: shared +include::_attributes.adoc[] + +The reference documentation consists of the following sections: + +[horizontal] +<> :: Legal information. +<> :: About the Documentation, Getting Help, First Steps, and more. +<> :: Introducing {project-full-name}, Developing Your First {project-full-name}-based Application +<> :: {project-full-name} usage examples and workflows. +<> :: Span creation, context propagation, and more. +<> :: Add sampling, propagate remote tags, and more. +<> :: Instrumentation configuration, context propagation, and more. +<> :: Configuration propertie. diff --git a/docs/src/main/asciidoc/_index_pdf.adoc b/docs/src/main/asciidoc/_index_pdf.adoc new file mode 100644 index 000000000..e1edcb891 --- /dev/null +++ b/docs/src/main/asciidoc/_index_pdf.adoc @@ -0,0 +1,13 @@ +[[spring-cloud-sleuth-reference-documentation]] += Spring Cloud Sleuth Reference Documentation +Adrian Cole, Spencer Gibb, Marcin Grzejszczak, Dave Syer, Jay Bryant + +include::_attributes.adoc[] + +include::legal.adoc[leveloffset=+1] +include::getting-started.adoc[leveloffset=+1] +include::using.adoc[leveloffset=+1] +include::project-features.adoc[leveloffset=+1] +include::howto.adoc[leveloffset=+1] +include::integrations.adoc[leveloffset=+1] +include::appendix.adoc[leveloffset=+1] diff --git a/docs/src/main/asciidoc/_index_single.adoc b/docs/src/main/asciidoc/_index_single.adoc new file mode 100644 index 000000000..741b91da1 --- /dev/null +++ b/docs/src/main/asciidoc/_index_single.adoc @@ -0,0 +1,14 @@ +[[spring-cloud-sleuth-reference-documentation]] += Spring Cloud Sleuth Reference Documentation +Adrian Cole, Spencer Gibb, Marcin Grzejszczak, Dave Syer, Jay Bryant + +:docinfo: shared +include::_attributes.adoc[] + +include::legal.adoc[leveloffset=+1] +include::getting-started.adoc[leveloffset=+1] +include::using.adoc[leveloffset=+1] +include::project-features.adoc[leveloffset=+1] +include::howto.adoc[leveloffset=+1] +include::integrations.adoc[leveloffset=+1] +include::appendix.adoc[leveloffset=+1] \ No newline at end of file diff --git a/docs/src/main/asciidoc/appendix.adoc b/docs/src/main/asciidoc/appendix.adoc index 2c18b8653..8cb9df2dc 100644 --- a/docs/src/main/asciidoc/appendix.adoc +++ b/docs/src/main/asciidoc/appendix.adoc @@ -1,5 +1,5 @@ :numbered!: -[appendix] +[[appendix]] [[common-application-properties]] == Common application properties diff --git a/docs/src/main/asciidoc/documentation-overview.adoc b/docs/src/main/asciidoc/documentation-overview.adoc new file mode 100644 index 000000000..2ee63dc4b --- /dev/null +++ b/docs/src/main/asciidoc/documentation-overview.adoc @@ -0,0 +1,98 @@ +[[documentation]] += Spring Cloud Sleuth Documentation +include::_attributes.adoc[] + +This section provides a brief overview of {project-full-name} reference documentation. It serves +as a map for the rest of the document. + + + +[[sleuth-documentation-about]] +== About the Documentation + +The {project-full-name} reference guide is available as + +* {docs-url}/reference/html[Multi-page HTML] +* {docs-url}/reference/htmlsingle[Single-page HTML] +* {docs-url}/reference/pdf/{project-name}.pdf[PDF] + +Copies of this document may be made for your own use and for distribution to others, +provided that you do not charge any fee for such copies and further provided that each +copy contains this Copyright Notice, whether distributed in print or electronically. + + + +[[documentation-getting-help]] +== Getting Help +If you have trouble with {project-full-name}, we would like to help. + +* Try the <>. They provide solutions to the most +common questions. +* Learn the {project-full-name} basics. If you are +starting out with {project-full-name}, try one of the https://spring.io/guides[guides]. +* Ask a question. We monitor https://stackoverflow.com[stackoverflow.com] for questions +tagged with https://stackoverflow.com/tags/{project-name}[`{project-name}`]. +* Report bugs with {project-full-name} at https://github.com/spring-cloud/{project-name}/issues. +* Chat with us at http://https://gitter.im/spring-cloud/{project-name}[{project-full-name} Gitter] + +NOTE: All of {project-full-name} is open source, including the documentation. If you find +problems with the docs or if you want to improve them, please {github-code}[get +involved]. + + +[[sleuth-documentation-first-steps]] +== First Steps +If you are getting started with {project-full-name} or 'Spring' in general, start with +<>: + +* *From scratch:* <> +* *Tutorial:* <> | <> +* *Running your example:* <> + +[[sleuth-documentation-working-with-sleuth]] +== Working with {project-full-name} +Ready to actually start using {project-full-name}? <>: + +* <> +* <> +* <> + +[[sleuth-documentation-features]] +== Learning about {project-full-name} Features +Need more details about {project-full-name}'s core features? +<>: + +* *Core Features:* +<> | +<> +<> + +* *Tracer Features:* +<> | +<> + +* *Reporting Features:* +<> | +<> + +[[sleuth-documentation-integration]] +== Integrations Topics +Finally, we have topics related to instrumentation integrations: + +* *Integrations:* +<> | +<> | +<> | +<> | +<> | +<> | +<> | +<> | +<> | +<> | +<> | +<> | +<> | +<> | +<> \ No newline at end of file diff --git a/docs/src/main/asciidoc/features.adoc b/docs/src/main/asciidoc/features.adoc deleted file mode 100644 index 33fbad61b..000000000 --- a/docs/src/main/asciidoc/features.adoc +++ /dev/null @@ -1,105 +0,0 @@ -:branch: master - -include::_attributes.adoc[] - -== Features -Sleuth sets up instrumentation not only to track timing, but also to catch -errors so that they can be analyzed or correlated with logs. This works the -same way regardless of if the error came from a common instrumented library, -such as `RestTemplate`, or your own code annotated with `@NewSpan` or similar. - -Below, we'll use the word Zipkin to describe the tracing system, and include -Zipkin screenshots. However, most services accepting https://zipkin.io/zipkin-api/#/default/post_spans[Zipkin format] -have similar base features. Sleuth can also be configured to send data in other -formats, something detailed later. - -=== Contextualizing errors -Without distributed tracing, it can be difficult to understand the impact of a -an exception. For example, it can be hard to know if a specific request caused -the caller to fail or not. - -Zipkin reduces time in triage by contextualizing errors and delays. - -Requests colored red in the search screen failed: - -image::{github-raw}/src/main/asciidoc/images/zipkin-error-traces.png[Error Traces] - -If you then click on one of the traces, you can understand if the failure -happened before the request hit another service or not: - -image::{github-raw}/src/main/asciidoc/images/zipkin-error-trace-screenshot.png[Error Traces Info propagation] - -For example, the above error happened in the "backend" service, and caused the -"frontend" service to fail. - -=== Log correlation -Sleuth configures the logging context with variables including the service name -(`%{spring.zipkin.service.name}`) and the trace ID (`%{traceId}`). These help -you connect logs with distributed traces and allow you choice in what tools you -use to troubleshoot your services. - -Once you find any log with an error, you can look for the trace ID in the -message. Paste that into Zipkin to visualize the entire trace, regardless of -how many services the first request ended up hitting. - -[source] ----- -backend.log: 2020-04-09 17:45:40.516 ERROR [backend,5e8eeec48b08e26882aba313eb08f0a4,dcc1df555b5777b3,true] 97203 --- [nio-9000-exec-1] o.s.c.s.i.web.ExceptionLoggingFilter : Uncaught exception thrown -frontend.log:2020-04-09 17:45:40.574 ERROR [frontend,5e8eeec48b08e26882aba313eb08f0a4,82aba313eb08f0a4,true] 97192 --- [nio-8081-exec-2] o.s.c.s.i.web.ExceptionLoggingFilter : Uncaught exception thrown ----- - -Above, you'll notice the trace ID is `5e8eeec48b08e26882aba313eb08f0a4`, for -example. This log configuration was automatically setup by Sleuth. - -=== Service Dependency Graph -When you consider distributed tracing tracks requests, it makes sense that -trace data can paint a picture of your architecture. - -Zipkin includes a tool to build service dependency diagrams from traces, -including the count of calls and how many errors exist. - -The example application will make a simple diagram like this, but your real -environment diagram may be more complex. - -image::{github-raw}/docs/src/main/asciidoc/images/zipkin-dependencies.png[Zipkin Dependencies] - -*Note*: Production environments will generate a lot of data. You will likely -need to run a separate service to aggregate the dependency graph. You can learn -more https://github.com/openzipkin/zipkin-dependencies/[here]. - -=== Request scoped properties (Baggage) -Distributed tracing works by propagating fields inside and across services that -connect the trace together: traceId and spanId notably. The context that holds -these fields can optionally push other fields that need to be consistent -regardless of many services are touched. The simple name for these extra fields -is "Baggage". - -Sleuth allows you to define which baggage are permitted to exist in the trace -context, including what header names are used. - -The following example shows setting baggage values: - -[source,java] ----- -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/multiple/MultipleHopsIntegrationTests.java[tags=baggage,indent=0] ----- - -IMPORTANT: There is currently no limitation of the count or size of baggage -items. Keep in mind that too many can decrease system throughput or increase -RPC latency. In extreme cases, too much baggage can crash the application, due -to exceeding transport-level message or header capacity. - - -==== Baggage versus Tags - -Like trace IDs, Baggage is attached to messages or requests, usually as -headers. Tags are key value pairs sent in a Span to Zipkin. Baggage values are -not added spans by default, which means you can't search based on Baggage -unless you opt-in. - -To make baggage also tags, use the property `spring.sleuth.baggage.tag-fields` -like so: -[source,yml] ----- -include::{project-root}/spring-cloud-sleuth-core/src/test/resources/application-baggage.yml[indent=0] ----- diff --git a/docs/src/main/asciidoc/getting-started.adoc b/docs/src/main/asciidoc/getting-started.adoc new file mode 100644 index 000000000..2c93ad3a9 --- /dev/null +++ b/docs/src/main/asciidoc/getting-started.adoc @@ -0,0 +1,296 @@ +[[getting-started]] += Getting Started + +include::_attributes.adoc[] + +If you are getting started with {project-full-name} or Spring in general, start by reading this section. +It answers the basic "`what?`", "`how?`" and "`why?`" questions. +It includes an introduction to {project-full-name}, along with installation instructions. +We then walk you through building your first {project-full-name} application, discussing some core principles as we go. + +[[getting-started-introducing-spring-cloud-sleuth]] +== Introducing Spring Cloud Sleuth + +Spring Cloud Sleuth provides API for distributed tracing solution for https://cloud.spring.io[Spring Cloud]. +It integrates out of the box with two tracer implementations: + +* https://github.com/openzipkin/brave[OpenZipkin Brave] +* https://opentelemetry.io[OpenTelemetry SDK] + +Spring Cloud Sleuth is able to trace your requests and messages so that you can correlate that communication to corresponding log entries. +You can also export the tracing information to an external system to visualize latency. Spring Cloud Sleuth supports https://zipkin.io[OpenZipkin] compatible systems directly and various other ones via the OpenTelemetry integration. + +[[getting-started-terminology]] +=== Terminology + +Spring Cloud Sleuth borrows https://research.google.com/pubs/pub36356.html[Dapper's] terminology. + +*Span*: The basic unit of work. +For example, sending an RPC is a new span, as is sending a response to an RPC. +Spans also have other data, such as descriptions, timestamped events, key-value annotations (tags), the ID of the span that caused them, and process IDs (normally IP addresses). + +Spans can be started and stopped, and they keep track of their timing information. +Once you create a span, you must stop it at some point in the future. + +*Trace:* A set of spans forming a tree-like structure. +For example, if you run a distributed big-data store, a trace might be formed by a `PUT` request. + +*Annotation/Event:* Used to record the existence of an event in time. + +Conceptually in a typical RPC scenario we mark these events to highlight what kind of an action took place (it doesn't mean that physically such an event will be set on a span). + +* *cs*: Client Sent. +The client has made a request. +This annotation indicates the start of the span. +* *sr*: Server Received: The server side got the request and started processing it. +Subtracting the `cs` timestamp from this timestamp reveals the network latency. +* *ss*: Server Sent. +Annotated upon completion of request processing (when the response got sent back to the client). +Subtracting the `sr` timestamp from this timestamp reveals the time needed by the server side to process the request. +* *cr*: Client Received. +Signifies the end of the span. +The client has successfully received the response from the server side. +Subtracting the `cs` timestamp from this timestamp reveals the whole time needed by the client to receive the response from the server. + +The following image shows how *Span* and *Trace* look in a system. + +image::{github-raw}/src/main/asciidoc/images/trace-id.png[Trace Info propagation] + +Each color of a note signifies a span (there are seven spans - from *A* to *G*). +Consider the following note: + +[source] +---- +Trace Id = X +Span Id = D +Client Sent +---- + +This note indicates that the current span has *Trace Id* set to *X* and *Span Id* set to *D*. +Also, from the RPC perspective, the `Client Sent` event took place. + +Let's consider more notes: + +[source] +---- +Trace Id = X +Span Id = A +(no custom span) + +Trace Id = X +Span Id = C +(custom span) +---- + +You can continue with a created span (example with `no custom span` indication) or you can create child spans manually (example with `custom span` indication). + +The following image shows how parent-child relationships of spans look: + +image::{github-raw}/docs/src/main/asciidoc/images/parents.png[Parent child relationship] + +[[getting-started-first-application]] +== Developing Your First Spring Cloud sleuth-based Application + +This section describes how to develop a small “Hello World!” web application that highlights some of Spring Cloud Sleuth’s key features. +We use Maven to build this project, since most IDEs support it. As the tracer implementation we'll use https://github.com/openzipkin/brave[OpenZipkin Brave]. + +[TIP] +==== +You can shortcut the steps below by going to https://start.spring.io and choosing the "Web" and "Spring Cloud Sleuth" starters from the dependencies searcher. +Doing so generates a new project structure so that you can <>. +==== + +[[getting-started-first-application-pom]] +=== Creating the POM + +We need to start by creating a Maven `pom.xml` file. +The `pom.xml` is the recipe that is used to build your project. +Open your favorite text editor and add the following: + +[source,xml,indent=0,subs="verbatim,quotes,attributes"] +---- + + + 4.0.0 + + com.example + myproject + 0.0.1-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + + ${spring-boot-version} + + + + + + + org.springframework.cloud + spring-cloud-dependencies + + ${release.train.version} + pom + import + + + + + + + + spring-snapshots + https://repo.spring.io/snapshot + true + + + spring-milestones + https://repo.spring.io/milestone + + + + + spring-snapshots + https://repo.spring.io/snapshot + + + spring-milestones + https://repo.spring.io/milestone + + + +---- + +The preceding listing should give you a working build. +You can test it by running `mvn package` (for now, you can ignore the "`jar will be empty - no content was marked for inclusion!`" warning). + +NOTE: At this point, you could import the project into an IDE (most modern Java IDEs include built-in support for Maven). +For simplicity, we continue to use a plain text editor for this example. + +[[getting-started-first-application-dependencies]] +=== Adding Classpath Dependencies + +To add the necessary dependencies, edit your `pom.xml` and add the `spring-boot-starter-web` dependency immediately below the `parent` section: + +[source,xml,indent=0,subs="verbatim,quotes,attributes"] +---- + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.cloud + spring-cloud-starter-sleuth + + +---- + +To use Sleuth with OpenTelemetry pass `spring-cloud-starter-sleuth-otel` instead of `spring-cloud-stater-sleuth`. + +[[getting-started-first-application-code]] +=== Writing the Code + +To finish our application, we need to create a single Java file. +By default, Maven compiles sources from `src/main/java`, so you need to create that directory structure and then add a file named `src/main/java/Example.java` to contain the following code: + +[source,java,indent=0] +---- + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + import org.springframework.boot.*; + import org.springframework.boot.autoconfigure.*; + import org.springframework.web.bind.annotation.*; + + @RestController + @EnableAutoConfiguration + public class Example { + + private static final Logger log = LoggerFactory.getLogger(Backend.class); + + @RequestMapping("/") + String home() { + log.info("Hello world!"); + return "Hello World!"; + } + + public static void main(String[] args) { + SpringApplication.run(Example.class, args); + } + + } +---- + +Although there is not much code here, quite a lot is going on. +We step through the important parts in the next few sections. + +[getting-started-first-application-annotations]] +==== The @RestController and @RequestMapping Annotations + +Spring Boot sets up the Rest Controller and makes our application bind to a Tomcat port. Spring Cloud Sleuth with Brave tracer will provide instrumentation of the incoming request. + +[[getting-started-first-application-run]] +=== Running the Example + +At this point, your application should work. +Since you used the `spring-boot-starter-parent` POM, you have a useful `run` goal that you can use to start the application. +Type `SPRING_APPLICATION_NAME=backend mvn spring-boot:run` from the root project directory to start the application. +You should see output similar to the following: + +[indent=0,subs="attributes"] +---- + $ mvn spring-boot:run + + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ + ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ + \\/ ___)| |_)| | | | | || (_| | ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | / / / / + =========|_|==============|___/=/_/_/_/ + ... + ....... . . . + ....... . . . (log output here) + ....... . . . + ........ Started Example in 2.222 seconds (JVM running for 6.514) +---- + +If you open a web browser to `http://localhost:8080`, you should see the following output: + +[indent=0] +---- + Hello World! +---- + +If you check the logs you should see a similar output + +[indent=0] +---- +2020-10-21 12:01:16.285 INFO [backend,0b6aaf642574edd3,0b6aaf642574edd3,true] 289589 --- [nio-9000-exec-1] Example : Hello world! +---- + +You can notice that the logging format has been updated with the following information `[backend,0b6aaf642574edd3,0b6aaf642574edd3,true]`. This entry corresponds to `[application name,trace id, span id, whether the trace should be propagated to an external system]`. The application name got read from the `SPRING_APPLICATION_NAME` environment variable. + +NOTE: Instead of logging the request in the handler explicitly, you +could set `logging.level.org.springframework.web.servlet.DispatcherServlet=DEBUG`. + +To gracefully exit the application, press `ctrl-c`. + +[[getting-started-whats-next]] +== Next Steps + +Hopefully, this section provided some of the {project-full-name} basics and got you on your way to writing your own applications. +If you are a task-oriented type of developer, you might want to jump over to https://spring.io and check out some of the +https://spring.io/guides/[getting started] guides that solve specific "`How do I do that with Spring?`" problems. +We also have {project-full-name}-specific "`<>`" reference documentation. + +Otherwise, the next logical step is to read <>. +If you are really impatient, you could also jump ahead and read about +<>. + +You can find the default project samples at +https://github.com/spring-cloud/spring-cloud-sleuth/tree/{branch}/spring-cloud-sleuth-samples[samples]. diff --git a/docs/src/main/asciidoc/howto.adoc b/docs/src/main/asciidoc/howto.adoc new file mode 100644 index 000000000..16212d7e7 --- /dev/null +++ b/docs/src/main/asciidoc/howto.adoc @@ -0,0 +1,756 @@ +[[howto]] += "`How-to`" Guides + +include::_attributes.adoc[] + +This section provides answers to some common "`how do I do that...?`" questions that often arise when using {project-full-name}. +Its coverage is not exhaustive, but it does cover quite a lot. + +If you have a specific problem that we do not cover here, you might want to check out +https://stackoverflow.com/tags/{project-name}[stackoverflow.com] to see if someone has already provided an answer. +Stack Overflow is also a great place to ask new questions (please use the `{project-name}` tag). + +We are also more than happy to extend this section. +If you want to add a "`how-to`", send us a {github-code}[pull request]. + +[[how-to-set-up-sleuth-with-brave]] +== How to Set Up Sleuth with Brave? + +Add the Sleuth starter to the classpath. + +==== +[source,xml,indent=0,subs="verbatim,attributes",role="primary"] +.Maven +---- + + + + org.springframework.cloud + spring-cloud-dependencies + ${release.train-version} + pom + import + + + + + + org.springframework.cloud + spring-cloud-starter-sleuth + +---- + +[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] +.Gradle +---- +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}" + } +} + +dependencies { + compile "org.springframework.cloud:spring-cloud-starter-sleuth" +} +---- +==== + +[[how-to-set-up-sleuth-with-otel]] +== How to Set Up Sleuth with OpenTelemetry? + +Add the Sleuth OpenTelemetry starter to the classpath. + +==== +[source,xml,indent=0,subs="verbatim,attributes",role="primary"] +.Maven +---- + + + + org.springframework.cloud + spring-cloud-dependencies + ${release.train-version} + pom + import + + + + + + org.springframework.cloud + spring-cloud-starter-sleuth-otel + +---- + +[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] +.Gradle +---- +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}" + } +} + +dependencies { + compile "org.springframework.cloud:spring-cloud-starter-sleuth-otel" +} +---- +==== + +[[how-to-set-up-sleuth-with-brave-zipkin-http]] +== How to Set Up Sleuth with Brave & Zipkin via HTTP? + +Add the Sleuth starter and Zipkin to the classpath. + +==== +[source,xml,indent=0,subs="verbatim,attributes",role="primary"] +.Maven +---- + + + + org.springframework.cloud + spring-cloud-dependencies + ${release.train-version} + pom + import + + + + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.cloud + spring-cloud-sleuth-zipkin + +---- + +[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] +.Gradle +---- +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}" + } +} + +dependencies { + compile "org.springframework.cloud:spring-cloud-starter-sleuth" + compile "org.springframework.cloud:spring-cloud-sleuth-zipkin" +} +---- +==== + +[[how-to-set-up-sleuth-with-otel-zipkin-http]] +== How to Set Up Sleuth with OpenTelemetry & Zipkin via HTTP? + +Add the Sleuth starter and Zipkin to the classpath. + +==== +[source,xml,indent=0,subs="verbatim,attributes",role="primary"] +.Maven +---- + + + + org.springframework.cloud + spring-cloud-dependencies + ${release.train-version} + pom + import + + + + + + org.springframework.cloud + spring-cloud-starter-sleuth-otel + + + org.springframework.cloud + spring-cloud-sleuth-zipkin + +---- + +[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] +.Gradle +---- +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}" + } +} + +dependencies { + compile "org.springframework.cloud:spring-cloud-starter-sleuth-otel" + compile "org.springframework.cloud:spring-cloud-sleuth-zipkin" +} +---- +==== + +[[how-to-set-up-sleuth-with-brave-zipkin-messaging]] +== How to Set Up Sleuth with Brave & Zipkin via Messaging? + +If you want to use RabbitMQ, Kafka or ActiveMQ instead of HTTP, add the `spring-rabbit`, `spring-kafka` or `org.apache.activemq:activemq-client` dependency. +The default destination name is `Zipkin`. + +If using Kafka, you must add the Kafka dependency. + +==== +[source,xml,indent=0,subs="verbatim,attributes",role="primary"] +.Maven +---- + + + + org.springframework.cloud + spring-cloud-dependencies + ${release.train-version} + pom + import + + + + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.cloud + spring-cloud-sleuth-zipkin + + + org.springframework.kafka + spring-kafka + +---- + +[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] +.Gradle +---- +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}" + } +} + +dependencies { + compile "org.springframework.cloud:spring-cloud-starter-sleuth" + compile "org.springframework.cloud:spring-cloud-sleuth-zipkin" + compile "org.springframework.kafka:spring-kafka" +} +---- +==== + +Also, you need to set the property `spring.zipkin.sender.type` property accordingly: + +[source,yaml] +---- +spring.zipkin.sender.type: kafka +---- + +If you want Sleuth over RabbitMQ, add the `spring-cloud-starter-sleuth`, `spring-cloud-sleuth-zipkin` and `spring-rabbit` dependencies. + +==== +[source,xml,indent=0,subs="verbatim,attributes",role="primary"] +.Maven +---- + + + + org.springframework.cloud + spring-cloud-dependencies + ${release.train-version} + pom + import + + + + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.cloud + spring-cloud-sleuth-zipkin + + + org.springframework.amqp + spring-rabbit + +---- + +[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] +.Gradle +---- +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}" + } +} + +dependencies { + compile "org.springframework.cloud:spring-cloud-starter-sleuth" + compile "org.springframework.cloud:spring-cloud-sleuth-zipkin" + compile "org.springframework.amqp:spring-rabbit" +} +---- +==== + +If you want Sleuth over RabbitMQ, add the `spring-cloud-starter-sleuth`, `spring-cloud-sleuth-zipkin` and `activemq-client` dependencies. + +==== +[source,xml,indent=0,subs="verbatim,attributes",role="primary"] +.Maven +---- + + + + org.springframework.cloud + spring-cloud-dependencies + ${release.train-version} + pom + import + + + + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.cloud + spring-cloud-sleuth-zipkin + + + org.apache.activemq + activemq-client + +---- + +[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] +.Gradle +---- +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}" + } +} + +dependencies { + compile "org.springframework.cloud:spring-cloud-starter-sleuth" + compile "org.springframework.cloud:spring-cloud-sleuth-zipkin" + compile "org.apache.activemq:activemq-client" +} +---- +==== + +Also, you need to set the property `spring.zipkin.sender.type` property accordingly: + +[source,yaml] +---- +spring.zipkin.sender.type: activemq +---- + +[[how-to-set-up-sleuth-with-otel-zipkin-messaging]] +== How to Set Up Sleuth with OpenTelemetry & Zipkin via Messaging? + +If you want to use RabbitMQ, Kafka or ActiveMQ instead of HTTP, add the `spring-rabbit`, `spring-kafka` or `org.apache.activemq:activemq-client` dependency. +The default destination name is `Zipkin`. + +If using Kafka, you must add the Kafka dependency. + +==== +[source,xml,indent=0,subs="verbatim,attributes",role="primary"] +.Maven +---- + + + + org.springframework.cloud + spring-cloud-dependencies + ${release.train-version} + pom + import + + + + + + org.springframework.cloud + spring-cloud-starter-sleuth-otel + + + org.springframework.cloud + spring-cloud-sleuth-zipkin + + + org.springframework.kafka + spring-kafka + +---- + +[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] +.Gradle +---- +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}" + } +} + +dependencies { + compile "org.springframework.cloud:spring-cloud-starter-sleuth-otel" + compile "org.springframework.cloud:spring-cloud-sleuth-zipkin" + compile "org.springframework.kafka:spring-kafka" +} +---- +==== + +Also, you need to set the property `spring.zipkin.sender.type` property accordingly: + +[source,yaml] +---- +spring.zipkin.sender.type: kafka +---- + +If you want Sleuth over RabbitMQ, add the `spring-cloud-starter-sleuth-otel`, `spring-cloud-sleuth-zipkin` and `spring-rabbit` dependencies. + +==== +[source,xml,indent=0,subs="verbatim,attributes",role="primary"] +.Maven +---- + + + + org.springframework.cloud + spring-cloud-dependencies + ${release.train-version} + pom + import + + + + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.cloud + spring-cloud-sleuth-zipkin + + + org.springframework.amqp + spring-rabbit + +---- + +[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] +.Gradle +---- +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}" + } +} + +dependencies { + compile "org.springframework.cloud:spring-cloud-starter-sleuth" + compile "org.springframework.cloud:spring-cloud-sleuth-zipkin" + compile "org.springframework.amqp:spring-rabbit" +} +---- +==== + +If you want Sleuth over RabbitMQ, add the `spring-cloud-starter-sleuth-otel`, `spring-cloud-sleuth-zipkin` and `activemq-client` dependencies. + +==== +[source,xml,indent=0,subs="verbatim,attributes",role="primary"] +.Maven +---- + + + + org.springframework.cloud + spring-cloud-dependencies + ${release.train-version} + pom + import + + + + + + org.springframework.cloud + spring-cloud-starter-sleuth-otel + + + org.springframework.cloud + spring-cloud-sleuth-zipkin + + + org.apache.activemq + activemq-client + +---- + +[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] +.Gradle +---- +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}" + } +} + +dependencies { + compile "org.springframework.cloud:spring-cloud-starter-sleuth-otel" + compile "org.springframework.cloud:spring-cloud-sleuth-zipkin" + compile "org.apache.activemq:activemq-client" +} +---- +==== + +Also, you need to set the property `spring.zipkin.sender.type` property accordingly: + +[source,yaml] +---- +spring.zipkin.sender.type: activemq +---- + +[[how-to-see-spans-in-an-external-system]] +== How to See Spans in an External System? + +If you can't see spans get reported to an external system (e.g. Zipkin), then it's most likely due to the following causes: + +* <> +* <> +* <> + +[[not-sampled-span]] +=== Your Span Is Not Being Sampled + +In order to check if the span is not being sampled it's enough to see if the exportable flag is being set. +Let's look at the following example: + +[indent=0] +---- +2020-10-21 12:01:16.285 INFO [backend,0b6aaf642574edd3,0b6aaf642574edd3,true] 289589 --- [nio-9000-exec-1] Example : Hello world! +---- + +If the boolean value in the section `[backend,0b6aaf642574edd3,0b6aaf642574edd3,true]` is `true` means that the span is being sampled and should be reported. + +[[missing-dependency]] +=== Missing Dependency + +Up till Sleuth 3.0.0 the dependency `spring-cloud-starter-zipkin` included the `spring-cloud-starter-sleuth` dependency and the `spring-cloud-sleuth-zipkin` dependency. +With 3.0.0 `spring-cloud-starter-zipkin` was removed, so you need to change it to `spring-cloud-sleuth-zipkin`. + +If you're working with OpenTelemetry you need to provide the dependency with your exporter. +For example if you want to use the OpenTelemetry Zipkin Exporter just add a dependency. + +==== +[source,xml,indent=0,subs="verbatim,attributes",role="primary"] +.Maven +---- + + io.opentelemetry + opentelemetry-exporters-zipkin + +---- + +[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] +.Gradle +---- +dependencies { + compile "io.opentelemetry:opentelemetry-exporters-zipkin" +} +---- +==== + +Then add the `SpanExporter` bean. + +==== +[source,java,indent=0] +---- +@Bean SpanExporter zipkinExporter() { + return ZipkinSpanExporter.builder() + .setEndpoint("http://localhost/api/v2/spans") + .setServiceName("my-service") + .build(); +} +---- +==== + +[[connection-misconfiguration]] +=== Connection Misconfiguration + +Double check if the remote system address is correct (e.g. `spring.zipkin.baseUrl`) and that if trying to communicate over the broker, your broker connection is set up properly. + +[[how-to-make-components-work]] +== How to Make RestTemplate, WebClient, etc. Work? + +If you're observing that the tracing context is not being propagated then cause is one of the following: + +* We are not instrumenting the given library +* We are instrumenting the library, however you misconfigured the setup + +In case of lack of instrumentation capabilities please file https://github.com/spring-cloud/spring-cloud-sleuth/issues[an issue] with a request to add such instrumentation. + +In case of the misconfiguration please ensure that the client you're using to communicate is a Spring bean. +If you create the client manually via the `new` operator the instrumentation will not work. + +Example where instrumentation will work: + +==== +[source,java,indent=0] +---- +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +class MyConfiguration { + @Bean RestTemplate myRestTemplate() { + return new RestTemplate(); + } +} + +@Service +class MyService { + private final RestTemplate restTemplate; + + MyService(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + + String makeACall() { + return this.restTemplate.getForObject("http://example.com", String.class); + } + +} + +---- +==== + +Example where instrumentation will **NOT** work: + +==== +[source,java,indent=0] +---- +@Service +class MyService { + + String makeACall() { + // This will not work because RestTemplate is not a bean + return new RestTemplate().getForObject("http://example.com", String.class); + } + +} + +---- +==== + +[[how-to-add-headers-to-the-http-server-response]] +== How to Add Headers to the HTTP Server Response? + +Register a bean of `HttpResponseParser` type whose name is `HttpServerResponseParser.NAME`. + +==== +[source,java,indent=0] +---- +import org.springframework.cloud.sleuth.api.http.HttpResponseParser; +import org.springframework.cloud.sleuth.instrument.web.HttpServerResponseParser; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +class MyConfig { + + @Bean(name = HttpServerResponseParser.NAME) + HttpResponseParser myHttpResponseParser() { + return (response, context, span) -> { + Object unwrap = response.unwrap(); + if (unwrap instanceof HttpServletResponse) { + HttpServletResponse resp = (HttpServletResponse) unwrap; + resp.addHeader("MyCustom", "Header"); + } + }; + } + +} + +---- +==== + +[[how-to-cutomize-http-client-spans]] +== How to Customize HTTP Client Spans? + +Register a bean of `HttpRequestParser` type whose name is `HttpClientRequestParser.NAME` to add customization for the request side. +Register a bean of `HttpResponseParser` type whose name is `HttpClientRequestParser.NAME` to add customization for the response side. + +==== +[source,java,indent=0] +---- +include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/integration/parser/WebClientCustomParserTests.java[tags=client_parser_config,indent=0] +---- +==== + +[[how-to-cutomize-http-server-spans]] +== How to Customize HTTP Server Spans? + +Register a bean of `HttpRequestParser` type whose name is `HttpServerRequestParser.NAME` to add customization for the request side. +Register a bean of `HttpResponseParser` type whose name is `HttpServerResponseParser.NAME` to add customization for the response side. + +==== +[source,java,indent=0] +---- +include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerParserTests.java[tags=server_parser_config,indent=0] +---- +==== + +[[how-to-see-application-name-in-logs]] +== How to See the Application Name in Logs? + +Assuming that you haven't changed the default logging format set the `spring.application.name` property in `bootstrap.yml`, not in `application.yml`. + +TIP: With the new Spring Cloud configuration bootstrap this should no longer be required since there will be no Bootstrap Context anymore. + +[[how-to-change-context-propagation]] +== How to Change The Context Propagation Mechanism? + +To use the provided defaults you can set the `spring.sleuth.propagation.type` property. +The value can be a list in which case you will propagate more tracing headers. + +For Brave we support `AWS`, `B3`, `W3C` propagation types. +For OpenTelemetry we support `AWS`, `B3`, `JAEGER`, `W3C`, `OT_TRACER` and `W3C` via the `io.opentelemetry:opentelemetry-extension-trace-propagators` that we provide via the `spring-cloud-starter-sleuth-otel` starter. + +If you want to provide a custom propagation mechanism set the `spring.sleuth.propagation.type` property to `CUSTOM` and implement your own bean (`Propagation.Factory` for Brave and `TextMapPropagator` for OpenTelemetry). +Below you can find the examples: + +==== +[source,java,indent=0,subs="verbatim,attributes",role="primary"] +.Brave +---- +@Component +include::{brave_path}/src/test/java/org/springframework/cloud/sleuth/brave/propagation/TraceBravePropagationAutoConfigurationTests.java[tags=custom_propagator,indent=0] +---- + +[source,java,indent=0,subs="verbatim,attributes",role="secondary"] +.OpenTelemetry +---- +@Component +include::{otel_path}/src/test/java/org/springframework/cloud/sleuth/otel/propagation/TraceOtelPropagationAutoConfigurationTests.java[tags=custom_propagator,indent=0] +---- +==== + +[[how-to-implement-own-tracer]] +== How to Implement My Own Tracer? + +Spring Cloud Sleuth Core in its `api` module contains all necessary interfaces to be implemented by a tracer. The project comes with OpenZipkin Brave and OpenTelemetry implementations. You can check how both tracers are bridged to the Sleuth's API by looking at the `org.springframework.cloud.sleuth.brave.bridge` and `org.springframework.cloud.sleuth.otel.bridge` modules respectively. \ No newline at end of file diff --git a/docs/src/main/asciidoc/images/parents.jpg b/docs/src/main/asciidoc/images/parents.jpg new file mode 100644 index 000000000..b62bde166 Binary files /dev/null and b/docs/src/main/asciidoc/images/parents.jpg differ diff --git a/docs/src/main/asciidoc/images/trace-id.jpg b/docs/src/main/asciidoc/images/trace-id.jpg new file mode 100644 index 000000000..04f06e133 Binary files /dev/null and b/docs/src/main/asciidoc/images/trace-id.jpg differ diff --git a/docs/src/main/asciidoc/index.adoc b/docs/src/main/asciidoc/index.adoc deleted file mode 120000 index 2ab5e96e8..000000000 --- a/docs/src/main/asciidoc/index.adoc +++ /dev/null @@ -1 +0,0 @@ -spring-cloud-sleuth.adoc \ No newline at end of file diff --git a/docs/src/main/asciidoc/index.htmladoc b/docs/src/main/asciidoc/index.htmladoc new file mode 100644 index 000000000..c674268b6 --- /dev/null +++ b/docs/src/main/asciidoc/index.htmladoc @@ -0,0 +1 @@ +include::_index.adoc[] \ No newline at end of file diff --git a/docs/src/main/asciidoc/index.htmlsingleadoc b/docs/src/main/asciidoc/index.htmlsingleadoc new file mode 100644 index 000000000..67d39bee3 --- /dev/null +++ b/docs/src/main/asciidoc/index.htmlsingleadoc @@ -0,0 +1 @@ +include::_index_single.adoc[] \ No newline at end of file diff --git a/docs/src/main/asciidoc/integrations.adoc b/docs/src/main/asciidoc/integrations.adoc new file mode 100644 index 000000000..655a5ec32 --- /dev/null +++ b/docs/src/main/asciidoc/integrations.adoc @@ -0,0 +1,542 @@ +[[sleuth-integration]] += Spring Cloud Sleuth customization +include::_attributes.adoc[] + +In this section, we describe how to customize various parts of Spring Cloud Sleuth. + +[[sleuth-async-integration]] +== Asynchronous Communication + +In this section, we describe how to customize asynchronous communication with Spring Cloud Sleuth. + +[[sleuth-async-annotation-integration]] +=== `@Async` Annotated methods + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +In Spring Cloud Sleuth, we instrument async-related components so that the tracing information is passed between threads. +You can disable this behavior by setting the value of `spring.sleuth.async.enabled` to `false`. + +If you annotate your method with `@Async`, we automatically create a new Span with the following characteristics: + +* If the method is annotated with `@SpanName`, the value of the annotation is the Span's name. +* If the method is not annotated with `@SpanName`, the Span name is the annotated method name. +* The span is tagged with the method's class name and method name. + +[[sleuth-async-scheduled-integration]] +=== `@Scheduled` Annotated Methods + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +In Spring Cloud Sleuth, we instrument scheduled method execution so that the tracing information is passed between threads. +You can disable this behavior by setting the value of `spring.sleuth.scheduled.enabled` to `false`. + +If you annotate your method with `@Scheduled`, we automatically create a new span with the following characteristics: + +* The span name is the annotated method name. +* The span is tagged with the method's class name and method name. + +If you want to skip span creation for some `@Scheduled` annotated classes, you can set the `spring.sleuth.scheduled.skipPattern` with a regular expression that matches the fully qualified name of the `@Scheduled` annotated class. + +[[sleuth-async-executor-service-integration]] +=== Executor, ExecutorService, and ScheduledExecutorService + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +We provide `LazyTraceExecutor`, `TraceableExecutorService`, and `TraceableScheduledExecutorService`. Those implementations create spans each time a new task is submitted, invoked, or scheduled. + +The following example shows how to pass tracing information with `TraceableExecutorService` when working with `CompletableFuture`: + +[source,java,indent=0] +---- + +include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorServiceTests.java[tags=completablefuture,indent=0] +---- + +IMPORTANT: Sleuth does not work with `parallelStream()` out of the box. +If you want to have the tracing information propagated through the stream, you have to use the approach with `supplyAsync(...)`, as shown earlier. + +If there are beans that implement the `Executor` interface that you would like +to exclude from span creation, you can use the `spring.sleuth.async.ignored-beans` +property where you can provide a list of bean names. + +You can disable this behavior by setting the value of `spring.sleuth.async.enabled` to `false`. + +[[sleuth-async-executor-integration]] +==== Customization of Executors + +Sometimes, you need to set up a custom instance of the `AsyncExecutor`. +The following example shows how to set up such a custom `Executor`: + +[source,java,indent=0] +---- +include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/MultipleAsyncRestTemplateTests.java[tags=custom_executor,indent=0] +---- + +TIP: To ensure that your configuration gets post processed, remember +to add the `@Role(BeanDefinition.ROLE_INFRASTRUCTURE)` on your +`@Configuration` class + + +[[sleuth-http-client-integration]] +== HTTP Client Integration + +Features from this section can be disabled by setting the `spring.sleuth.web.client.enabled` property with value equal to `false`. + +[[sleuth-http-client-rest-template-integration]] +=== Synchronous Rest Template + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +We inject a `RestTemplate` interceptor to ensure that all the tracing information is passed to the requests. +Each time a call is made, a new Span is created. +It gets closed upon receiving the response. +To block the synchronous `RestTemplate` features, set `spring.sleuth.web.client.enabled` to `false`. + +IMPORTANT: You have to register `RestTemplate` as a bean so that the interceptors get injected. +If you create a `RestTemplate` instance with a `new` keyword, the instrumentation does NOT work. + +[[sleuth-http-client-async-rest-template-integration]] +=== Asynchronous Rest Template + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +IMPORTANT: Starting with Sleuth `2.0.0`, we no longer register a bean of `AsyncRestTemplate` type. It is up to you to create such a bean. Then we instrument it. + +To block the `AsyncRestTemplate` features, set `spring.sleuth.web.async.client.enabled` to `false`. +To disable creation of the default `TraceAsyncClientHttpRequestFactoryWrapper`, set `spring.sleuth.web.async.client.factory.enabled` +to `false`. +If you do not want to create `AsyncRestClient` at all, set `spring.sleuth.web.async.client.template.enabled` to `false`. + +[[sleuth-http-client-multiple-async-rest-template-integration]] +==== Multiple Asynchronous Rest Templates + +Sometimes you need to use multiple implementations of the Asynchronous Rest Template. +In the following snippet, you can see an example of how to set up such a custom `AsyncRestTemplate`: + +[source,java,indent=0] +---- +include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/MultipleAsyncRestTemplateTests.java[tags=custom_async_rest_template,indent=0] +---- + +[[sleuth-http-client-webclient-integration]] +==== `WebClient` + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +We inject a `ExchangeFilterFunction` implementation that creates a span and, through on-success and on-error callbacks, takes care of closing client-side spans. + +To block this feature, set `spring.sleuth.web.client.enabled` to `false`. + +IMPORTANT: You have to register `WebClient` as a bean so that the tracing instrumentation gets applied. +If you create a `WebClient` instance with a `new` keyword, the instrumentation does NOT work. + +[[sleuth-http-client-traverson-integration]] +==== Traverson + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +If you use the https://docs.spring.io/spring-hateoas/docs/current/reference/html/#client.traverson[Traverson] library, you can inject a `RestTemplate` as a bean into your Traverson object. +Since `RestTemplate` is already intercepted, you get full support for tracing in your client. The following pseudo code +shows how to do that: + +[source,java,indent=0] +---- +@Autowired RestTemplate restTemplate; + +Traverson traverson = new Traverson(URI.create("https://some/address"), + MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON_UTF8).setRestOperations(restTemplate); +// use Traverson +---- + +[[sleuth-http-client-apache-integration]] +==== Apache `HttpClientBuilder` and `HttpAsyncClientBuilder` + +This feature is available for Brave tracer implementation. + +We instrument the `HttpClientBuilder` and `HttpAsyncClientBuilder` so that +tracing context gets injected to the sent requests. + +To block these features, set `spring.sleuth.web.client.enabled` to `false`. + +[[sleuth-http-client-netty-integration]] +==== Netty `HttpClient` + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +We instrument the Netty's `HttpClient`. + +To block this feature, set `spring.sleuth.web.client.enabled` to `false`. + +IMPORTANT: You have to register `HttpClient` as a bean so that the instrumentation happens. +If you create a `HttpClient` instance with a `new` keyword, the instrumentation does NOT work. + +[[sleuth-http-client-userinfo-integration]] +==== `UserInfoRestTemplateCustomizer` + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +We instrument the Spring Security's `UserInfoRestTemplateCustomizer`. + +To block this feature, set `spring.sleuth.web.client.enabled` to `false`. + +[[sleuth-http-server-integration]] +== HTTP Server Integration + +Features from this section can be disabled by setting the `spring.sleuth.web.enabled` property with value equal to `false`. + +[[sleuth-http-server-http-filter-integration]] +=== HTTP Filter + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +Through the `TracingFilter`, all sampled incoming requests result in creation of a Span. +You can configure which URIs you would like to skip by setting the `spring.sleuth.web.skipPattern` property. +If you have `ManagementServerProperties` on classpath, its value of `contextPath` gets appended to the provided skip pattern. +If you want to reuse the Sleuth's default skip patterns and just append your own, pass those patterns by using the `spring.sleuth.web.additionalSkipPattern`. + +By default, all the spring boot actuator endpoints are automatically added to the skip pattern. +If you want to disable this behaviour set `spring.sleuth.web.ignore-auto-configured-skip-patterns` +to `true`. + +To change the order of tracing filter registration, please set the +`spring.sleuth.web.filter-order` property. + +To disable the filter that logs uncaught exceptions you can disable the +`spring.sleuth.web.exception-throwing-filter-enabled` property. + +[[sleuth-http-server-handler-interceptor-integration]] +=== HandlerInterceptor + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +Since we want the span names to be precise, we use a `TraceHandlerInterceptor` that either wraps an existing `HandlerInterceptor` or is added directly to the list of existing `HandlerInterceptors`. +The `TraceHandlerInterceptor` adds a special request attribute to the given `HttpServletRequest`. +If the the `TracingFilter` does not see this attribute, it creates a "`fallback`" span, which is an additional span created on the server side so that the trace is presented properly in the UI. +If that happens, there is probably missing instrumentation. +In that case, please file an issue in Spring Cloud Sleuth. + +[[sleuth-http-server-async-integration]] +=== Async Servlet support + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +If your controller returns a `Callable` or a `WebAsyncTask`, Spring Cloud Sleuth continues the existing span instead of creating a new one. + +[[sleuth-http-server-webflux-integration]] +=== WebFlux support + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +Through `TraceWebFilter`, all sampled incoming requests result in creation of a Span. +That Span's name is `http:` + the path to which the request was sent. +For example, if the request was sent to `/this/that`, the name is `http:/this/that`. +You can configure which URIs you would like to skip by using the `spring.sleuth.web.skipPattern` property. +If you have `ManagementServerProperties` on the classpath, its value of `contextPath` gets appended to the provided skip pattern. +If you want to reuse Sleuth's default skip patterns and append your own, pass those patterns by using the `spring.sleuth.web.additionalSkipPattern`. + +In order to achieve best results in terms of performance and context propagation we suggest that you switch the `spring.sleuth.reactor.instrumentation-type` to `MANUAL`. In order to execute code with the span in scope you can call `WebFluxSleuthOperators.withSpanInScope`. Example: + +[source,java,indent=0] +----- +include::{project-root}/benchmarks/src/main/java/org/springframework/cloud/sleuth/benchmarks/app/webflux/SleuthBenchmarkingSpringWebFluxApp.java[tags=simple_manual,indent=0] +----- + +To change the order of tracing filter registration, please set the +`spring.sleuth.web.filter-order` property. + +[[sleuth-messaging-integration]] +== Messaging + +Features from this section can be disabled by setting the `spring.sleuth.messaging.enabled` property with value equal to `false`. + +[[sleuth-messaging-spring-integration-integration]] +=== Spring Integration + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +Spring Cloud Sleuth integrates with https://projects.spring.io/spring-integration/[Spring Integration]. +It creates spans for publish and subscribe events. +To disable Spring Integration instrumentation, set `spring.sleuth.integration.enabled` to `false`. + +You can provide the `spring.sleuth.integration.patterns` pattern to explicitly provide the names of channels that you want to include for tracing. +By default, all channels but `hystrixStreamOutput` channel are included. + +IMPORTANT: When using the `Executor` to build a Spring Integration `IntegrationFlow`, you must use the untraced version of the `Executor`. +Decorating the Spring Integration Executor Channel with `TraceableExecutorService` causes the spans to be improperly closed. + +If you want to customize the way tracing context is read from and written to message headers, +it's enough for you to register beans of types: + +* `Propagator.Setter` - for writing headers to the message +* `Propagator.Getter` - for reading headers from the message + +[[sleuth-messaging-spring-cloud-function-integration]] +=== Spring Cloud Function and Spring Cloud Stream + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +Spring Cloud Sleuth can instrument Spring Cloud Function. The way to achieve it is to provide a `Function` or `Consumer` or `Supplier` that takes in a `Message` as a parameter e.g. `Function, Message>`. If the type is not `Message` then instrumentation will not take place. Out of the box instrumentation will not take place when dealing with Reactor based streams - e.g. `Function>, Flux>>`. + +Since Spring Cloud Stream reuses Spring Cloud Function, you'll get the instrumentation out of the box. + +You can disable this behavior by setting the value of `spring.sleuth.function.enabled` to `false`. + +In order to work with reactive Stream functions you can leverage the `MessagingSleuthOperators` utility class that allows you to manipulate the input and output messages in order to continue the tracing context and to execute custom code within the tracing context. + +[source,java,indent=0] +----- +include::{project-root}/benchmarks/src/main/java/org/springframework/cloud/sleuth/benchmarks/app/stream/SleuthBenchmarkingStreamApplication.java[tags=simple_reactive,indent=0] +----- + +[[sleuth-messaging-spring-rabbitmq-integration]] +=== Spring RabbitMq + +This feature is available for Brave tracer implementation. + +We instrument the `RabbitTemplate` so that tracing headers get injected +into the message. + +To block this feature, set `spring.sleuth.messaging.rabbit.enabled` to `false`. + +[[sleuth-messaging-spring-kafka-integration]] +=== Spring Kafka + +This feature is available for Brave tracer implementation. + +We instrument the Spring Kafka's `ProducerFactory` and `ConsumerFactory` +so that tracing headers get injected into the created Spring Kafka's +`Producer` and `Consumer`. + +To block this feature, set `spring.sleuth.messaging.kafka.enabled` to `false`. + +[[sleuth-messaging-spring-kafka-streams-integration]] +=== Spring Kafka Streams + +This feature is available for Brave tracer implementation. + +We instrument the `KafkaStreams` `KafkaClientSupplier` so that tracing headers +get injected into the `Producer` and `Consumer`s. A `KafkaStreamsTracing` bean +allows for further instrumentation through additional `TransformerSupplier` and +`ProcessorSupplier` methods. + +To block this feature, set `spring.sleuth.messaging.kafka.streams.enabled` to `false`. + +[[sleuth-messaging-spring-jms-integration]] +=== Spring JMS + +This feature is available for Brave tracer implementation. + +We instrument the `JmsTemplate` so that tracing headers get injected +into the message. We also support `@JmsListener` annotated methods on the consumer side. + +To block this feature, set `spring.sleuth.messaging.jms.enabled` to `false`. + +IMPORTANT: We don't support baggage propagation for JMS + +[[sleuth-openfeign-integration]] +== OpenFeign + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +By default, Spring Cloud Sleuth provides integration with Feign through `TraceFeignClientAutoConfiguration`. +You can disable it entirely by setting `spring.sleuth.feign.enabled` to `false`. +If you do so, no Feign-related instrumentation take place. + +Part of Feign instrumentation is done through a `FeignBeanPostProcessor`. +You can disable it by setting `spring.sleuth.feign.processor.enabled` to `false`. +If you set it to `false`, Spring Cloud Sleuth does not instrument any of your custom Feign components. +However, all the default instrumentation is still there. + + +[[sleuth-opentracing-integration]] +== OpenTracing + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +Spring Cloud Sleuth is compatible with https://opentracing.io/[OpenTracing]. +If you have OpenTracing on the classpath, we automatically register the OpenTracing `Tracer` bean. +If you wish to disable this, set `spring.sleuth.opentracing.enabled` to `false` + +[[sleuth-quartz-integration]] +== Quartz + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +We instrument quartz jobs by adding Job/Trigger listeners to the Quartz Scheduler. + +To turn off this feature, set the `spring.sleuth.quartz.enabled` property to `false`. + +[[sleuth-reactor-integration]] +== Reactor + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +We have three modes of instrumenting reactor based applications that can +be set via `spring.sleuth.reactor.instrumentation-type` property: + +* `ON_EACH` - wraps every Reactor operator in a trace representation. Passes the tracing context in most cases. This mode might lead to drastic performance degradation. +* `ON_LAST` - wraps last Reactor operator in a trace representation. Passes the tracing context in some cases thus accessing MDC context might not work. This mode might lead to medium performance degradation. +* `MANUAL` - wraps every Reactor in the least invasive way without passing of tracing context. It's up to the user to do it. + +Current default is `ON_EACH` for backward compatibility reasons, however we encourage the users to migrate to the `MANUAL` instrumentation and profit from `WebFluxSleuthOperators` and `MessagingSleuthOperators`. The performance improvement can be substantial. Example: + +[source,java,indent=0] +----- +include::{project-root}/benchmarks/src/main/java/org/springframework/cloud/sleuth/benchmarks/app/webflux/SleuthBenchmarkingSpringWebFluxApp.java[tags=simple_manual,indent=0] +----- + +[[sleuth-redis-integration]] +== Redis + +This feature is available for Brave tracer implementation. + +We set `tracing` property to Lettuce `ClientResources` instance to enable Brave tracing built in Lettuce . +To disable Redis support, set the `spring.sleuth.redis.enabled` property to `false`. + +[[sleuth-runnablecallable-integration]] +== Runnable and Callable + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +If you wrap your logic in `Runnable` or `Callable`, you can wrap those classes in their Sleuth representative, as shown in the following example for `Runnable`: + +[source,java,indent=0] +---- +include::{brave_path}/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=trace_runnable,indent=0] +---- + +The following example shows how to do so for `Callable`: + +[source,java,indent=0] +---- +include::{brave_path}/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=trace_callable,indent=0] +---- + +That way, you ensure that a new span is created and closed for each execution. + + +[[sleuth-rpc-integration]] +== RPC + +This feature is available for Brave tracer implementation. + +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,indent=0] +---- +@Configuration(proxyBeanMethods = false) + class Config { +include::{brave_path}/src/test/java/org/springframework/cloud/sleuth/brave/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 + +[[sleuth-rpc-dubbo-integration]] +=== 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` dependency: + +[source,xml,indent=0] +---- + + io.zipkin.brave + brave-instrumentation-dubbo + +---- + +You need to also set a `dubbo.properties` file with the following contents: + +```properties +dubbo.provider.filter=tracing +dubbo.consumer.filter=tracing +``` + +You can read more about Brave - Dubbo integration https://github.com/openzipkin/brave/tree/master/instrumentation/dubbo-rpc[here]. +An example of Spring Cloud Sleuth and Dubbo can be found https://github.com/openzipkin/sleuth-webmvc-example/compare/add-dubbo-tracing[here]. + +[[sleuth-rpc-grpc-integration]] +=== gRPC + +Spring Cloud Sleuth provides instrumentation for https://grpc.io/[gRPC] through `TraceGrpcAutoConfiguration` via the Brave tracer. You can disable it entirely by setting `spring.sleuth.grpc.enabled` to `false`. + +[[sleuth-rpc-grpc-variant1-integration]] +==== Variant 1 + +[[sleuth-rpc-grpc-variant1-dependencies-integration]] +===== Dependencies +IMPORTANT: The gRPC integration relies on two external libraries to instrument clients and servers and both of those libraries must be on the class path to enable the instrumentation. + +Maven: +``` + + io.github.lognet + grpc-spring-boot-starter + + + io.zipkin.brave + brave-instrumentation-grpc + +``` +Gradle: +``` + compile("io.github.lognet:grpc-spring-boot-starter") + compile("io.zipkin.brave:brave-instrumentation-grpc") +``` + +[[sleuth-rpc-grpc-variant1-server-integration]] +===== Server Instrumentation + +Spring Cloud Sleuth leverages grpc-spring-boot-starter to register Brave's gRPC server interceptor with all services annotated with `@GRpcService`. + +[[sleuth-rpc-grpc-variant1-client-integration]] +===== Client Instrumentation + +gRPC clients leverage a `ManagedChannelBuilder` to construct a `ManagedChannel` used to communicate to the gRPC server. The native `ManagedChannelBuilder` provides static methods as entry points for construction of `ManagedChannel` instances, however, this mechanism is outside the influence of the Spring application context. + +IMPORTANT: Spring Cloud Sleuth provides a `SpringAwareManagedChannelBuilder` that can be customized through the Spring application context and injected by gRPC clients. *This builder must be used when creating `ManagedChannel` instances.* + +Sleuth creates a `TracingManagedChannelBuilderCustomizer` which inject Brave's client interceptor into the `SpringAwareManagedChannelBuilder`. + +[[sleuth-rpc-grpc-variant2-integration]] +==== Variant 2 + +https://github.com/yidongnan/grpc-spring-boot-starter[Grpc Spring Boot Starter] automatically detects the presence of Spring Cloud Sleuth and Brave's instrumentation for gRPC and registers the necessary client and/or server tooling. + +[[sleuth-rxjava-integration]] +== RxJava + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +We registering a custom https://github.com/ReactiveX/RxJava/wiki/Plugins#rxjavaschedulershook[`RxJavaSchedulersHook`] that wraps all `Action0` instances in their Sleuth representative, which is called `TraceAction`. +The hook either starts or continues a span, depending on whether tracing was already going on before the Action was scheduled. +To disable the custom `RxJavaSchedulersHook`, set the `spring.sleuth.rxjava.schedulers.hook.enabled` to `false`. + +You can define a list of regular expressions for thread names for which you do not want spans to be created. +To do so, provide a comma-separated list of regular expressions in the `spring.sleuth.rxjava.schedulers.ignoredthreads` property. + +IMPORTANT: The suggested approach to reactive programming and Sleuth is to use +the Reactor support. + +[[sleuth-circuitbreaker-integration]] +== Spring Cloud CircuitBreaker + +This feature is available for both Brave and OpenTelemetry tracer implementation. + +If you have Spring Cloud CircuitBreaker on the classpath, we will wrap the passed command `Supplier` and the fallback `Function` in its trace representations. In order to disable this instrumentation set `spring.sleuth.circuitbreaker.enabled` to `false`. \ No newline at end of file diff --git a/docs/src/main/asciidoc/legal.adoc b/docs/src/main/asciidoc/legal.adoc new file mode 100644 index 000000000..e88a801e3 --- /dev/null +++ b/docs/src/main/asciidoc/legal.adoc @@ -0,0 +1,11 @@ +[[legal]] += Legal + +{project-version} + +Copyright © 2012-2020 + +Copies of this document may be made for your own use and for distribution to +others, provided that you do not charge any fee for such copies and further +provided that each copy contains this Copyright Notice, whether distributed in +print or electronically. \ No newline at end of file diff --git a/docs/src/main/asciidoc/overview.adoc b/docs/src/main/asciidoc/overview.adoc deleted file mode 100644 index 6b509851f..000000000 --- a/docs/src/main/asciidoc/overview.adoc +++ /dev/null @@ -1,29 +0,0 @@ -:branch: master - -== Overview -Spring Cloud Sleuth provides Spring Boot auto-configuration for distributed -tracing. Underneath, Spring Cloud Sleuth is a layer over a Tracer library named -https://github.com/openzipkin/brave[Brave]. - -Sleuth configures everything you need to get started. This includes where trace -data (spans) are reported to, how many traces to keep (sampling), if remote -fields (baggage) are sent, and which libraries are traced. - -We maintain an https://github.com/openzipkin/sleuth-webmvc-example[example app] where two Spring Boot services collaborate on an -HTTP request. Sleuth configures these apps, so that timing of these requests are -recorded into https://zipkin.io[Zipkin], a distributed tracing system. Tracing -UIs visualize latency, such as time in one service vs waiting for other -services. - -Here's an example of what it looks like: - -image::{github-raw}/src/main/asciidoc/images/zipkin-trace-screenshot.png[Zipkin Trace] - -The https://github.com/openzipkin/sleuth-webmvc-example[source repository] of this -example includes demonstrations of many things, including WebFlux and messaging. -Most features require only a property or dependency change to work. These -snippets showcase the value of Spring Cloud Sleuth: Through auto-configuration, -Sleuth make getting started with distributed tracing easy! - -To keep things simple, the same example is used throughout documentation using -basic HTTP communication. diff --git a/docs/src/main/asciidoc/project-features.adoc b/docs/src/main/asciidoc/project-features.adoc new file mode 100644 index 000000000..944f02558 --- /dev/null +++ b/docs/src/main/asciidoc/project-features.adoc @@ -0,0 +1,482 @@ +[[features]] +[[project-features]] += Spring Cloud Sleuth Features +include::_attributes.adoc[] + +This section dives into the details of {project-full-name}. Here you can learn about the key +features that you may want to use and customize. If you have not already done so, you +might want to read the "<>" and +"<>" sections, so that you have a good grounding in the +basics. + +[[features-context-propagation]] +== Context Propagation + +Traces connect from service to service using header propagation. The default +format is https://github.com/openzipkin/b3-propagation[B3]. Similar to data +formats, you can configure alternate header formats also, provided trace and +span IDs are compatible with B3. Most notably, this means the trace ID and span +IDs are lower-case hex, not UUIDs. Besides trace identifiers, other properties +(Baggage) can also be passed along with the request. Remote Baggage must be +predefined, but is flexible otherwise. + +To use the provided defaults you can set the `spring.sleuth.propagation.type` property. +The value can be a list in which case you will propagate more tracing headers. + +For Brave we support `AWS`, `B3`, `W3C` propagation types. +For OpenTelemetry we support `AWS`, `B3`, `JAEGER`, `OT_TRACER` and `W3C` via the `io.opentelemetry:opentelemetry-extension-trace-propagators` dependency that you have to manually add to your classpath. + +You can read more about how to provide custom context propagation in this "<>". + +[[features-sampling]] +== Sampling + +Spring Cloud Sleuth pushes the sampling decision down to the tracer implementation. However, there are cases where you can change the sampling decision at runtime. + +One of such cases is skip reporting of certain client spans. To achieve that you can set the `spring.sleuth.web.client.skip-pattern` with the path patterns to be skipped. Another option is to provide your own custom `org.springframework.cloud.sleuth.api.SamplerFunction<`org.springframework.cloud.sleuth.api.http.HttpRequest>` implementation and define when a given `HttpRequest` should not be sampled. + +[[features-baggage]] +== Baggage + +Distributed tracing works by propagating fields inside and across services that +connect the trace together: traceId and spanId notably. The context that holds +these fields can optionally push other fields that need to be consistent +regardless of many services are touched. The simple name for these extra fields +is "Baggage". + +Sleuth allows you to define which baggage are permitted to exist in the trace +context, including what header names are used. + +The following example shows setting baggage values using Spring Cloud Sleuth's API: + +[source,java,indent=0] +---- +include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/baggage/multiple/MultipleHopsIntegrationTests.java[tags=baggage,indent=0] +---- + +IMPORTANT: There is currently no limitation of the count or size of baggage +items. Keep in mind that too many can decrease system throughput or increase +RPC latency. In extreme cases, too much baggage can crash the application, due +to exceeding transport-level message or header capacity. + +You can use +properties to define fields that have no special configuration such as name mapping: + +* `spring.sleuth.baggage.remote-fields` is a list of header names to accept and propagate to remote services. +* `spring.sleuth.baggage.local-fields` is a list of names to propagate locally + +No prefixing applies with these keys. What you set is literally what is used. + +A name set in either of these properties will result in a `Baggage` of the same name. + +In order to automatically set the baggage values to Slf4j's MDC, you have to set +the `spring.sleuth.baggage.correlation-fields` property with a list of allowed +local or remote keys. E.g. `spring.sleuth.baggage.correlation-fields=country-code` will set the +value of the `country-code` baggage into MDC. + +IMPORTANT: Remember that adding entries to MDC can drastically decrease the performance of your application! + +If you want to add the baggage entries as tags, to make it possible to search for spans via the baggage entries, you can set the value of +`spring.sleuth.baggage.tag-fields` with a list of allowed baggage keys. To disable the feature you have to pass the `spring.sleuth.propagation.tag.enabled=false` property. + +[[features-baggage-vs-tags]] +=== Baggage versus Tags + +Like trace IDs, Baggage is attached to messages or requests, usually as +headers. Tags are key value pairs sent in a Span to Zipkin. Baggage values are +not added spans by default, which means you can't search based on Baggage +unless you opt-in. + +To make baggage also tags, use the property `spring.sleuth.baggage.tag-fields` +like so: +[source,yml] +---- +include::{brave_path}/src/test/resources/application-baggage.yml[indent=0] +---- + +[[features-brave]] +== OpenZipkin Brave Tracer Integration + +Spring Cloud Sleuth integrates with the OpenZipkin Brave tracer via the bridge that is available in the `spring-cloud-sleuth-brave` module. In this section you can read about specific Brave integrations. + +You can choose to use either Sleuth's API or the Brave API directly in your code (e.g. either Sleuth's `Tracer` or Brave's `Tracer`). If you want to use this tracer implementation's API directly please read https://github.com/openzipkin/brave[their documentation to learn more about it]. + +[[features-brave-basics]] +=== Brave Basics + +Here are the most core types you might use: + +* `brave.SpanCustomizer` - to change the span currently in progress +* `brave.Tracer` - to get a start new spans ad-hoc + +Here are the most relevant links from the OpenZipkin Brave project: + +* https://github.com/openzipkin/brave/tree/master/brave[Brave's core library] +* https://github.com/openzipkin/brave/tree/master/brave#baggage[Baggage (propagated fields)] +* https://github.com/openzipkin/brave/tree/master/instrumentation/http[HTTP tracing] + +[[features-brave-sampling]] +=== Brave Sampling + +Sampling only applies to tracing backends, such as Zipkin. Trace IDs appear in logs regardless of +sample rate. Sampling is a way to prevent overloading the system, by consistently tracing some, but +not all requests. + +The default rate of 10 traces per second is controlled by the `spring.sleuth.sampler.rate` +property and applies when we know Sleuth is used for reasons besides logging. Use a rate above 100 +traces per second with extreme caution as it can overload your tracing system. + +The sampler can be set by Java Config also, as shown in the following example: + +[source,java,indent=0] +---- +include::{brave_path}/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=always_sampler,indent=0] +---- + +TIP: You can set the HTTP header `b3` to `1`, or, when doing messaging, you can set the `spanFlags` header to `1`. +Doing so forces the current request to be sampled regardless of configuration. + +By default samplers will work with the refresh scope mechanism. That means that you can change the sampling properties at runtime, refresh the application and the changes will be reflected. However, sometimes the fact of creating a proxy around samplers and calling it from too early (from `@PostConstruct` annotated method) may lead to dead locks. In such a case either create a sampler bean explicitly, or set the property `spring.sleuth.sampler.refresh.enabled` to `false` to disable the refresh scope support. + +[[features-brave-baggage]] +=== Brave Baggage Java configuration + +If you need to do anything more advanced than above, do not define properties and instead use a +`@Bean` config for the baggage fields you use. + +* `BaggagePropagationCustomizer` sets up baggage fields +* Add a `SingleBaggageField` to control header names for a `Baggage`. +* `CorrelationScopeCustomizer` sets up MDC fields +* Add a `SingleCorrelationField` to change the MDC name of a `Baggage` or if updates flush. + +[[features-brave-customizations]] +=== Brave Customizations + +The `brave.Tracer` object is fully managed by sleuth, so you rarely need to affect it. That said, +Sleuth supports a number of `Customizer` types, that allow you to configure +anything not already done by Sleuth with auto-configuration or properties. + +If you define one of the following as a `Bean`, Sleuth will invoke it to +customize behaviour: + +* `RpcTracingCustomizer` - for RPC tagging and sampling policy +* `HttpTracingCustomizer` - for HTTP tagging and sampling policy +* `MessagingTracingCustomizer` - for messaging tagging and sampling policy +* `CurrentTraceContextCustomizer` - to integrate decorators such as correlation. +* `BaggagePropagationCustomize`r - for propagating baggage fields in process and over headers +* `CorrelationScopeDecoratorCustomizer` - for scope decorations such as MDC (logging) field correlation + +[[features-brave-sampling-customizations]] +==== Brave Sampling Customizations + +If client /server sampling is required, just 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, `Sampler`. + +[source,java,indent=0] +---- +@Configuration(proxyBeanMethods = false) + class Config { +include::{tests_path}/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceFilterWebIntegrationTests.java[tags=custom_server_sampler,indent=2] +} +---- + +[[features-brave-messaging]] +=== Brave Messaging + +Sleuth automatically configures the `MessagingTracing` bean which serves as a +foundation for Messaging instrumentation such as Kafka or JMS. + +If a customization of producer / consumer sampling of messaging traces is required, +just register a bean of type `brave.sampler.SamplerFunction` and +name the bean `sleuthProducerSampler` for producer sampler and `sleuthConsumerSampler` +for consumer sampler. + +For your convenience the `@ProducerSampler` and `@ConsumerSampler` +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 consumer requests per second, except for +the "alerts" channel. Other requests will use a global rate provided by the +`Tracing` component. + +[source,java,indent=0] +---- +@Configuration(proxyBeanMethods = false) + class Config { +include::{brave_path}/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TraceMessagingAutoConfigurationIntegrationTests.java[tags=custom_messaging_consumer_sampler,indent=2] +} +---- + +For more, see https://github.com/openzipkin/brave/tree/master/instrumentation/messaging#sampling-policy + +[[features-brave-opentracing]] +=== Brave Opentracing + +You can integrate with Brave and https://opentracing.io/[OpenTracing] via the +`io.opentracing.brave:brave-opentracing` bridge. Just add it to the classpath and the OpenTracing `Tracer` will be set up automatically. + +[[features-otel]] +== OpenTelemetry Tracer Integration + +Spring Cloud Sleuth integrates with the OpenTelemetry (OTel in short) SDK tracer via the bridge that is available in the `spring-cloud-sleuth-otel` module. In this section you can read about specific OTel integrations. + +You can choose to use either Sleuth's API or the OpenTelemetry API directly in your code (e.g. either Sleuth's `Tracer` or OpenTelemetry's `Tracer`). If you want to use this tracer implementation's API directly please read https://github.com/open-telemetry/opentelemetry-java[their documentation to learn more about it]. + +[[features-otel-logging]] +=== OpenTelemetry Logging Integration + +We're providing an Slf4j integration via a `SpanProcessor` that injects to and removes entries (trace / span ids, baggage, tags etc.) from MDC. You can disable that via the `spring.sleuth.otel.log.slf4j.enabled=false` property. + +If it's there on the classpath, we integrate with the `LoggingSpanExporter`. You can disable that integration via the `spring.sleuth.otel.log.exporter.enabled=false` property. + +[[features-otel-opentracing]] +=== OpenTelemetry Opentracing + +You can integrate with OpenTelemetry and https://opentracing.io/[OpenTracing] via the +`io.opentelemetry:opentelemetry-opentracing-shim` bridge. Just add it to the classpath and the OpenTracing `Tracer` will be set up automatically. + +[[features-zipkin]] +== Sending Spans to Zipkin + +Spring Cloud Sleuth provides various integrations with the https://zipkin.io[OpenZipkin] distributed tracing system. Regardless of the chosen tracer implementation it's enough to add `spring-cloud-sleuth-zipkin` to the classpath to start sending spans to Zipkin. You can choose whether to do that via HTTP or messaging. You can read more about how to do that in "<>". + +When the span is closed, it is sent to Zipkin over HTTP. The communication is asynchronous. You can configure the URL by setting the `spring.zipkin.baseUrl` property, as follows: + +[source,yaml] +---- +spring.zipkin.baseUrl: https://192.168.99.100:9411/ +---- + +If you want to find Zipkin through service discovery, you can pass the Zipkin's service ID inside the URL, as shown in the following example for `zipkinserver` service ID: + +[source,yaml] +---- +spring.zipkin.baseUrl: https://zipkinserver/ +---- + +To disable this feature just set `spring.zipkin.discovery-client-enabled` to `false. + +When the Discovery Client feature is enabled, Sleuth uses +`LoadBalancerClient` to find the URL of the Zipkin Server. It means +that you can set up the load balancing configuration. + +If you have `web`, `rabbit`, `activemq` or `kafka` together on the classpath, you might need to pick the means by which you would like to send spans to zipkin. +To do so, set `web`, `rabbit`, `activemq` or `kafka` to the `spring.zipkin.sender.type` property. +The following example shows setting the sender type for `web`: + +[source,yaml] +---- +spring.zipkin.sender.type: web +---- + +To customize the `RestTemplate` that sends spans to Zipkin via HTTP, you can register +the `ZipkinRestTemplateCustomizer` bean. + +[source,java,indent=0] +---- +@Configuration(proxyBeanMethods = false) + class MyConfig { + @Bean ZipkinRestTemplateCustomizer myCustomizer() { + return new ZipkinRestTemplateCustomizer() { + @Override + void customize(RestTemplate restTemplate) { + // customize the RestTemplate + } + }; + } +} +---- + +If, however, you would like to control the full process of creating the `RestTemplate` +object, you will have to create a bean of `zipkin2.reporter.Sender` type. + +[source,java,indent=0] +---- + @Bean Sender myRestTemplateSender(ZipkinProperties zipkin, + ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer) { + RestTemplate restTemplate = mySuperCustomRestTemplate(); + zipkinRestTemplateCustomizer.customize(restTemplate); + return myCustomSender(zipkin, restTemplate); + } +---- + +[[features-zipkin-custom-service-name]] +=== 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. +That is not always the case, though. +There are situations in which you want to explicitly provide a different service name for all spans coming from your application. +To achieve that, you can pass the following property to your application to override that value (the example is for a service named `myService`): + +[source,yaml] +---- +spring.zipkin.service.name: myService +---- + +[[features-zipkin-host-locator]] +=== Host Locator + +IMPORTANT: This section is about defining *host* from service discovery. +It is *NOT* about finding Zipkin through service discovery. + +To define the host that corresponds to a particular span, we need to resolve the host name and port. +The default approach is to take these values from server properties. +If those are not set, we try to retrieve the host name from the network interfaces. + +If you have the discovery client enabled and prefer to retrieve the host address from the registered instance in a service registry, you have to set the `spring.zipkin.locator.discovery.enabled` property (it is applicable for both HTTP-based and Stream-based span reporting), as follows: + +[source,yaml] +---- +spring.zipkin.locator.discovery.enabled: true +---- + +[[features-zipkin-custom-reported-spans]] +=== Customization of Reported Spans + +In Sleuth, we generate spans with a fixed name. +Some users want to modify the name depending on values of tags. + +Sleuth registers a `SpanFilter` bean that can automatically skip reporting spans of given name patterns. The property `spring.sleuth.span-filter.span-name-patterns-to-skip` contains the default skip patterns for span names. The property `spring.sleuth.span-filter.additional-span-name-patterns-to-skip` will append the provided span name patterns to the existing ones. In order to disable this functionality just set `spring.sleuth.span-filter.enabled` to `false`. + +[[features-zipkin-custom-reported-spans-brave]] +==== Brave Customization of Reported Spans + +IMPORTANT: This section is applicable for Brave tracer only. + + +Before reporting spans (for example, to Zipkin) you may want to modify that span in some way. +You can do so by implementing a `SpanHandler`. + +The following example shows how to register two beans that implement `SpanHandler`: + +[source,java,indent=0] +---- +include::{brave_path}/src/test/java/org/springframework/cloud/sleuth/SpanHandlerTests.java[tags=spanHandler,indent=0] +---- + +The preceding example results in changing the name of the reported span to `foo bar`, just before it gets reported (for example, to Zipkin). + +=== Overriding the auto-configuration of Zipkin + +Spring Cloud Sleuth supports sending traces to multiple tracing systems as of version 2.1.0. +In order to get this to work, every tracing system needs to have a `Reporter` and `Sender`. +If you want to override the provided beans you need to give them a specific name. +To do this you can use respectively `ZipkinAutoConfiguration.REPORTER_BEAN_NAME` and `ZipkinAutoConfiguration.SENDER_BEAN_NAME`. + +[source,java,indent=0] +---- +include::{project-root}/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfigurationTests.java[tags=override_default_beans,indent=0] +---- + +[[features-log-integration]] +== Log integration + +Sleuth configures the logging context with variables including the service name +(`%{spring.zipkin.service.name}` or `%{spring.application.name}` if the previous one was not set), span ID (`%{spanId}`) and the trace ID (`%{traceId}`). These help +you connect logs with distributed traces and allow you choice in what tools you +use to troubleshoot your services. + +Once you find any log with an error, you can look for the trace ID in the +message. Paste that into your distributed tracing system to visualize the entire trace, regardless of how many services the first request ended up hitting. + +[source] +---- +backend.log: 2020-04-09 17:45:40.516 ERROR [backend,5e8eeec48b08e26882aba313eb08f0a4,dcc1df555b5777b3,true] 97203 --- [nio-9000-exec-1] o.s.c.s.i.web.ExceptionLoggingFilter : Uncaught exception thrown +frontend.log:2020-04-09 17:45:40.574 ERROR [frontend,5e8eeec48b08e26882aba313eb08f0a4,82aba313eb08f0a4,true] 97192 --- [nio-8081-exec-2] o.s.c.s.i.web.ExceptionLoggingFilter : Uncaught exception thrown +---- + +Above, you'll notice the trace ID is `5e8eeec48b08e26882aba313eb08f0a4`, for +example. This log configuration was automatically setup by Sleuth. You can disable it by disabling Sleuth via `spring.sleuth.enabled=false` property or putting your own `logging.pattern.level` property. + +If you use a log aggregating tool (such as https://www.elastic.co/products/kibana[Kibana], https://www.splunk.com/[Splunk], and others), you can order the events that took place. +An example from Kibana would resemble the following image: + +image::{github-raw}/src/main/asciidoc/images/kibana.png[Log correlation with Kibana] + +If you want to use https://www.elastic.co/guide/en/logstash/current/index.html[Logstash], the following listing shows the Grok pattern for Logstash: + +[source] +---- +filter { + # pattern matching logback pattern + grok { + match => { "message" => "%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" } + } + date { + match => ["timestamp", "ISO8601"] + } + mutate { + remove_field => ["timestamp"] + } +} +---- + +NOTE: If you want to use Grok together with the logs from Cloud Foundry, you have to use the following pattern: +[source] +---- +filter { + # pattern matching logback pattern + grok { + match => { "message" => "(?m)OUT\s+%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" } + } + date { + match => ["timestamp", "ISO8601"] + } + mutate { + remove_field => ["timestamp"] + } +} +---- + +[[features-log-integration-json-logback]] +=== JSON Logback with Logstash + +Often, you do not want to store your logs in a text file but in a JSON file that Logstash can immediately pick. +To do so, you have to do the following (for readability, we pass the dependencies in the `groupId:artifactId:version` notation). + +*Dependencies Setup* + +. Ensure that Logback is on the classpath (`ch.qos.logback:logback-core`). +. Add Logstash Logback encode. For example, to use version `4.6`, add `net.logstash.logback:logstash-logback-encoder:4.6`. + +*Logback Setup* + +Consider the following example of a Logback configuration file (logback-spring.xml). + +[source,xml] +----- +include::{project-root}/docs/src/main/asciidoc/logback-spring.xml[] +----- + +That Logback configuration file: + +* Logs information from the application in a JSON format to a `build/${spring.application.name}.json` file. +* Has commented out two additional appenders: console and standard log file. +* Has the same logging pattern as the one presented in the previous section. + +NOTE: If you use a custom `logback-spring.xml`, you must pass the `spring.application.name` in the `bootstrap` rather than the `application` property file. +Otherwise, your custom logback file does not properly read the property. + +[[features-whats-next]] +== What to Read Next + +If you want to learn more about any of the classes discussed in this section, you can browse the +{github-code}[source code directly]. If you have specific questions, see the +<> section. + +If you are comfortable with {project-full-name}'s core features, you can continue on and read +about +<>. diff --git a/docs/src/main/asciidoc/setup.adoc b/docs/src/main/asciidoc/setup.adoc deleted file mode 100644 index 33feb2b06..000000000 --- a/docs/src/main/asciidoc/setup.adoc +++ /dev/null @@ -1,179 +0,0 @@ -:branch: master - -[[sleuth-adding-project]] -== Adding Sleuth to your Project - -This section addresses how to add Sleuth to your project with either Maven or Gradle. - -IMPORTANT: To ensure that your application name is properly displayed in Zipkin, set the `spring.application.name` property in `bootstrap.yml`. - -=== Sleuth with Zipkin via HTTP - -If you want both Sleuth and Zipkin, add the `spring-cloud-starter-zipkin` dependency. - -The following example shows how to do so for Maven: - -.Maven -[source,xml,indent=0,subs="verbatim,attributes",role="primary"] ----- - <1> - - - org.springframework.cloud - spring-cloud-dependencies - ${release.train.version} - pom - import - - - - - <2> - org.springframework.cloud - spring-cloud-starter-zipkin - ----- -<1> We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself. -<2> Add the dependency to `spring-cloud-starter-zipkin`. - -The following example shows how to do so for Gradle: - -.Gradle -[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] ----- -dependencyManagement { <1> - imports { - mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}" - } -} - -dependencies { <2> - compile "org.springframework.cloud:spring-cloud-starter-zipkin" -} ----- -<1> We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself. -<2> Add the dependency to `spring-cloud-starter-zipkin`. - -=== Sleuth with Zipkin over RabbitMQ or Kafka - -If you want to use RabbitMQ or Kafka instead of HTTP, add the `spring-rabbit` or `spring-kafka` dependency. -The default destination name is `zipkin`. - -If using Kafka, you must set the property `spring.zipkin.sender.type` property accordingly: - -[source,yaml] ----- -spring.zipkin.sender.type: kafka ----- - -CAUTION: `spring-cloud-sleuth-stream` is deprecated and incompatible with these destinations. - -If you want Sleuth over RabbitMQ, add the `spring-cloud-starter-zipkin` and `spring-rabbit` -dependencies. - -The following example shows how to do so for Gradle: - -.Maven -[source,xml,indent=0,subs="verbatim,attributes",role="primary"] ----- - <1> - - - org.springframework.cloud - spring-cloud-dependencies - ${release.train.version} - pom - import - - - - - <2> - org.springframework.cloud - spring-cloud-starter-zipkin - - <3> - org.springframework.amqp - spring-rabbit - ----- -<1> We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself. -<2> Add the dependency to `spring-cloud-starter-zipkin`. That way, all nested dependencies get downloaded. -<3> To automatically configure RabbitMQ, add the `spring-rabbit` dependency. - -.Gradle -[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] ----- -dependencyManagement { <1> - imports { - mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}" - } -} - -dependencies { - compile "org.springframework.cloud:spring-cloud-starter-zipkin" <2> - compile "org.springframework.amqp:spring-rabbit" <3> -} ----- -<1> We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself. -<2> Add the dependency to `spring-cloud-starter-zipkin`. That way, all nested dependencies get downloaded. -<3> To automatically configure RabbitMQ, add the `spring-rabbit` dependency. - -=== Overriding the auto-configuration of Zipkin - -Spring Cloud Sleuth supports sending traces to multiple tracing systems as of version 2.1.0. -In order to get this to work, every tracing system needs to have a `Reporter` and `Sender`. -If you want to override the provided beans you need to give them a specific name. -To do this you can use respectively `ZipkinAutoConfiguration.REPORTER_BEAN_NAME` and `ZipkinAutoConfiguration.SENDER_BEAN_NAME`. - -[source,java] ----- -include::{project-root}/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfigurationTests.java[tags=override_default_beans,indent=0] ----- - -=== Only Sleuth (log correlation) - -If you want to use only Spring Cloud Sleuth without the Zipkin integration, add the `spring-cloud-starter-sleuth` module to your project. - -The following example shows how to add Sleuth with Maven: - -[source,xml,indent=0,subs="verbatim,attributes",role="primary"] -.Maven ----- - <1> - - - org.springframework.cloud - spring-cloud-dependencies - ${release.train.version} - pom - import - - - - - <2> - org.springframework.cloud - spring-cloud-starter-sleuth - ----- -<1> We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself. -<2> Add the dependency to `spring-cloud-starter-sleuth`. - -The following example shows how to add Sleuth with Gradle: - -.Gradle -[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"] ----- -dependencyManagement { <1> - imports { - mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}" - } -} - -dependencies { <2> - compile "org.springframework.cloud:spring-cloud-starter-sleuth" -} ----- -<1> We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself. -<2> Add the dependency to `spring-cloud-starter-sleuth`. diff --git a/docs/src/main/asciidoc/spring-cloud-sleuth.adoc b/docs/src/main/asciidoc/spring-cloud-sleuth.adoc deleted file mode 100644 index 903c83ef3..000000000 --- a/docs/src/main/asciidoc/spring-cloud-sleuth.adoc +++ /dev/null @@ -1,1117 +0,0 @@ -Spring Cloud Sleuth -==================== -Adrian Cole, Spencer Gibb, Marcin Grzejszczak, Dave Syer, Jay Bryant - -include::_attributes.adoc[] - -*{spring-cloud-version}* - -:doctype: book - -include::overview.adoc[] - -include::features.adoc[] - -include::setup.adoc[] - -== How Sleuth works - -Spring Cloud Sleuth is a layer over https://github.com/openzipkin/brave[Brave]. - -Brave is a distributed tracing instrumentation library. Brave typically -intercepts production requests to gather timing data, correlate and propagate -trace contexts. - -Trace data, also called spans, are typically reported to https://zipkin.io[Zipkin]. -Zipkin is an Open Source tracing system, which includes a UI and various -collectors, such as HTTP and messaging. - -Many Open Source and commercial products accept https://zipkin.io/zipkin-api/#/default/post_spans[Zipkin format]. -Some options are documented https://zipkin.io/pages/extensions_choices.html[here], -but many are not. If you cannot use Zipkin and your product isn't listed, clarify -with your support representative and have them update that page. In many cases, -products already support Zipkin format, they just don't document it. - -Traces connect from service to service using header propagation. The default -format is https://github.com/openzipkin/b3-propagation[B3]. Similar to data -formats, you can configure alternate header formats also, provided trace and -span IDs are compatible with B3. Most notably, this means the trace ID and span -IDs are lower-case hex, not UUIDs. Besides trace identifiers, other properties -(Baggage) can also be passed along with the request. Remote Baggage must be -predefined, but is flexible otherwise. - -Sleuth configures everything you need to get started with tracing. Sleuth -configures where trace data (spans) are reported to, how many traces to keep -(sampling), if remote fields (baggage) are sent, and which libraries are traced. -Sleuth also adds annotation based tracing features and some instrumentation not -available otherwise, such as Reactor. If cannot find the configuration you are -looking for in the documentation, ask https://gitter.im/spring-cloud/spring-cloud-sleuth[Gitter] -before assuming something cannot be done. - -=== Brave Basics - -Most instrumentation work is done for you by default. Sleuth provides beans to -allow you to change what's traced, and it even provides annotations to avoid -using tracing libraries! All of this is explained later in this document. - -That said, you might want to know more about how things work underneath. Here -are some pointers. - -Here are the most core types you might use: - -* `SpanCustomizer` - to change the span currently in progress -* `Tracer` - to get a start new spans ad-hoc - -Here are the most relevant links from the OpenZipkin Brave project: - -* https://github.com/openzipkin/brave/tree/master/brave[Brave's core library] -* https://github.com/openzipkin/brave/tree/master/brave#baggage[Baggage (propagated fields)] -* https://github.com/openzipkin/brave/tree/master/instrumentation/http[HTTP tracing] - -== Sampling - -Sampling only applies to tracing backends, such as Zipkin. Trace IDs appear in logs regardless of -sample rate. Sampling is a way to prevent overloading the system, by consistently tracing some, but -not all requests. - -The default rate of 10 traces per second is controlled by the `spring.sleuth.sampler.rate` -property and applies when we know Sleuth is used for reasons besides logging. Use a rate above 100 -traces per second with extreme caution as it can overload your tracing system. - -The sampler can be set by Java Config also, as shown in the following example: - -[source,java] ----- -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=always_sampler,indent=0] ----- - -TIP: You can set the HTTP header `b3` to `1`, or, when doing messaging, you can set the `spanFlags` header to `1`. -Doing so forces the current request to be sampled regardless of configuration. - -By default samplers will work with the refresh scope mechanism. That means that you can change the sampling properties at runtime, refresh the application and the changes will be reflected. However, sometimes the fact of creating a proxy around samplers and calling it from too early (from `@PostConstruct` annotated method) may lead to dead locks. In such a case either create a sampler bean explicitly, or set the property `spring.sleuth.sampler.refresh.enabled` to `false` to disable the refresh scope support. - -== Baggage -Baggage are fields that are propagated with the trace, optionally out of process. You can use -properties to define fields that have no special configuration such as name mapping: - - * `spring.sleuth.baggage.remote-fields` is a list of header names to accept and propagate to remote services. - * `spring.sleuth.baggage.local-fields` is a list of names to propagate locally - -No prefixing applies with these keys. What you set is literally what is used. - -A name set in either of these properties will result in a `BaggageField` of the same name. - -In order to automatically set the baggage values to Slf4j's MDC, you have to set -the `spring.sleuth.baggage.correlation-fields` property with a list of allowed -local or remote keys. E.g. `spring.sleuth.baggage.correlation-fields=country-code` will set the -value of the `country-code` baggage into MDC. - -IMPORTANT: Remember that adding entries to MDC can drastically decrease the performance of your application! - -If you want to add the baggage entries as tags, to make it possible to search for spans via the baggage entries, you can set the value of -`spring.sleuth.baggage.tag-fields` with a list of allowed baggage keys. To disable the feature you have to pass the `spring.sleuth.propagation.tag.enabled=false` property. - -=== Java configuration - -If you need to do anything more advanced than above, do not define properties and instead use a -`@Bean` config for the baggage fields you use. - - * `BaggagePropagationCustomizer` sets up baggage fields - * Add a `SingleBaggageField` to control header names for a `BaggageField`. - * `CorrelationScopeCustomizer` sets up MDC fields - * Add a `SingleCorrelationField` to change the MDC name of a `BaggageField` or if updates flush. - -== Instrumentation - -Spring Cloud Sleuth automatically instruments all your Spring applications, so you should not have to do anything to activate it. -The instrumentation is added by using a variety of technologies according to the stack that is available. For example, for a servlet web application, we use a `Filter`, and, for Spring Integration, we use `ChannelInterceptors`. - -You can customize the keys used in span tags. -To limit the volume of span data, an HTTP request is, by default, tagged only with a handful of metadata, such as the status code, the host, and the URL. -You can add request headers by configuring `spring.sleuth.keys.http.headers` (a list of header names). - -NOTE: Tags are collected and exported only if there is a `Sampler` that allows it. By default, there is no such `Sampler`, to ensure that there is no danger of accidentally collecting too much data without configuring something). - -== Span lifecycle - -You can do the following operations on the Span by means of `brave.Tracer`: - -* <>: When you start a span, its name is assigned and the start timestamp is recorded. -* <>: The span gets finished (the end time of the span is recorded) and, if the span is sampled, it is eligible for collection (for example, to Zipkin). -* <>: A new instance of span is created. -It is a copy of the one that it continues. -* <>: The span does not get stopped or closed. -It only gets removed from the current thread. -* <>: You can create a new span and set an explicit parent for it. - -TIP: Spring Cloud Sleuth creates an instance of `Tracer` for you. In order to use it, you can autowire it. - -=== Creating and finishing spans [[creating-and-finishing-spans]] - -You can manually create spans by using the `Tracer`, as shown in the following example: - -[source,java] ----- -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=manual_span_creation,indent=0] ----- - -In the preceding example, we could see how to create a new instance of the span. -If there is already a span in this thread, it becomes the parent of the new span. - -IMPORTANT: Always clean after you create a span. Also, always finish any span that you want to send to Zipkin. - -IMPORTANT: If your span contains a name greater than 50 chars, that name is truncated to 50 chars. -Your names have to be explicit and concrete. Big names lead to latency issues and sometimes even exceptions. - -[[continuing-spans]] -=== Continuing Spans - -Sometimes, you do not want to create a new span but you want to continue one. An example of such a -situation might be as follows: - -* *AOP*: If there was already a span created before an aspect was reached, you might not want to create a new span. - -To continue a span, you can use `brave.Tracer`, as shown in the following example: - -[source,java] ----- -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=manual_span_continuation,indent=0] ----- - -[[creating-spans-with-explicit-parent]] -=== Creating a Span with an explicit Parent - -You might want to start a new span and provide an explicit parent of that span. -Assume that the parent of a span is in one thread and you want to start a new span in another thread. -In Brave, whenever you call `nextSpan()`, it creates a span in reference to the span that is currently in scope. -You can put the span in scope and then call `nextSpan()`, as shown in the following example: - -[source,java] ----- -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=manual_span_joining,indent=0] ----- - -IMPORTANT: After creating such a span, you must finish it. Otherwise it is not reported (for example, to Zipkin). - -== Naming spans - -Picking a span name is not a trivial task. A span name should depict an operation name. -The name should be low cardinality, so it should not include identifiers. - -Since there is a lot of instrumentation going on, some span names are artificial: - -* `controller-method-name` when received by a Controller with a method name of `controllerMethodName` -* `async` for asynchronous operations done with wrapped `Callable` and `Runnable` interfaces. -* Methods annotated with `@Scheduled` return the simple name of the class. - -Fortunately, for asynchronous processing, you can provide explicit naming. - -=== `@SpanName` Annotation - -You can name the span explicitly by using the `@SpanName` annotation, as shown in the following example: - -[source,java] ----- -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=span_name_annotation,indent=0] ----- - -In this case, when processed in the following manner, the span is named `calculateTax`: - -[source,java] ----- -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=span_name_annotated_runnable_execution,indent=0] ----- - -=== `toString()` method - -It is pretty rare to create separate classes for `Runnable` or `Callable`. -Typically, one creates an anonymous instance of those classes. -You cannot annotate such classes. -To overcome that limitation, if there is no `@SpanName` annotation present, we check whether the class has a custom implementation of the `toString()` method. - -Running such code leads to creating a span named `calculateTax`, as shown in the following example: - -[source,java] ----- -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=span_name_to_string_runnable_execution,indent=0] ----- - -== Managing Spans with Annotations - -You can manage spans with a variety of annotations. - -=== Rationale - -There are a number of good reasons to manage spans with annotations, including: - -* API-agnostic means to collaborate with a span. Use of annotations lets users add to a span with no library dependency on a span api. -Doing so lets Sleuth change its core API to create less impact to user code. -* Reduced surface area for basic span operations. Without this feature, you must use the span api, which has lifecycle commands that could be used incorrectly. -By only exposing scope, tag, and log functionality, you can collaborate without accidentally breaking span lifecycle. -* Collaboration with runtime generated code. With libraries such as Spring Data and Feign, the implementations of interfaces are generated at runtime. -Consequently, span wrapping of objects was tedious. -Now you can provide annotations over interfaces and the arguments of those interfaces. - -=== Creating New Spans - -If you do not want to create local spans manually, you can use the `@NewSpan` annotation. -Also, we provide the `@SpanTag` annotation to add tags in an automated fashion. - -Now we can consider some examples of usage. - -[source,java] ----- -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectTests.java[tags=annotated_method,indent=0] ----- - -Annotating the method without any parameter leads to creating a new span whose name equals the annotated method name. - -[source,java] ----- -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectTests.java[tags=custom_name_on_annotated_method,indent=0] ----- - -If you provide the value in the annotation (either directly or by setting the `name` parameter), the created span has the provided value as the name. - -[source,java] ----- -// method declaration -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectTests.java[tags=custom_name_and_tag_on_annotated_method,indent=0] - -// and method execution -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectTests.java[tags=execution,indent=0] ----- - -You can combine both the name and a tag. Let's focus on the latter. -In this case, the value of the annotated method's parameter runtime value becomes the value of the tag. -In our sample, the tag key is `testTag`, and the tag value is `test`. - -[source,java] ----- -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectTests.java[tags=name_on_implementation,indent=0] ----- - -You can place the `@NewSpan` annotation on both the class and an interface. -If you override the interface's method and provide a different value for the `@NewSpan` annotation, the most -concrete one wins (in this case `customNameOnTestMethod3` is set). - -=== Continuing Spans - -If you want to add tags and annotations to an existing span, you can use the `@ContinueSpan` annotation, as shown in the following example: - -[source,java] ----- -// method declaration -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectTests.java[tags=continue_span,indent=0] - -// method execution -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectTests.java[tags=continue_span_execution,indent=0] ----- - -(Note that, in contrast with the `@NewSpan` annotation ,you can also add logs with the `log` parameter.) - -That way, the span gets continued and: - -* Log entries named `testMethod11.before` and `testMethod11.after` are created. -* If an exception is thrown, a log entry named `testMethod11.afterFailure` is also created. -* A tag with a key of `testTag11` and a value of `test` is created. - -=== Advanced Tag Setting - -There are 3 different ways to add tags to a span. All of them are controlled by the `SpanTag` annotation. -The precedence is as follows: - -. Try with a bean of `TagValueResolver` type and a provided name. -. If the bean name has not been provided, try to evaluate an expression. -We search for a `TagValueExpressionResolver` bean. -The default implementation uses SPEL expression resolution. -**IMPORTANT** You can only reference properties from the SPEL expression. Method execution is not allowed due to security constraints. -. If we do not find any expression to evaluate, return the `toString()` value of the parameter. - -==== Custom extractor - -The value of the tag for the following method is computed by an implementation of `TagValueResolver` interface. -Its class name has to be passed as the value of the `resolver` attribute. - -Consider the following annotated method: - -[source,java] ----- -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SpanTagAnnotationHandlerTests.java[tags=resolver_bean,indent=0] ----- - -Now further consider the following `TagValueResolver` bean implementation: - -[source,java] ----- -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SpanTagAnnotationHandlerTests.java[tags=custom_resolver,indent=0] ----- - -The two preceding examples lead to setting a tag value equal to `Value from myCustomTagValueResolver`. - -==== Resolving Expressions for a Value - -Consider the following annotated method: - -[source,java] ----- -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SpanTagAnnotationHandlerTests.java[tags=spel,indent=0] ----- - -No custom implementation of a `TagValueExpressionResolver` leads to evaluation of the SPEL expression, and a tag with a value of `4 characters` is set on the span. -If you want to use some other expression resolution mechanism, you can create your own implementation of the bean. - -==== Using the `toString()` method - -Consider the following annotated method: - -[source,java] ----- -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SpanTagAnnotationHandlerTests.java[tags=toString,indent=0] ----- - -Running the preceding method with a value of `15` leads to setting a tag with a String value of `"15"`. - -== Customizations - -The `Tracer` object is fully managed by sleuth, so you rarely need to affect it. That said, -Sleuth supports a number of `Customizer` types, that allow you to configure -anything not already done by Sleuth with auto-configuration or properties. - -If you define one of the following as a `Bean`, Sleuth will invoke it to -customize behaviour: - -* `RpcTracingCustomizer` - for RPC tagging and sampling policy -* `HttpTracingCustomizer` - for HTTP tagging and sampling policy -* `MessagingTracingCustomizer` - for messaging tagging and sampling policy -* `CurrentTraceContextCustomizer` - to integrate decorators such as correlation. -* `BaggagePropagationCustomize`r - for propagating baggage fields in process and over headers -* `CorrelationScopeDecoratorCustomizer` - for scope decorations such as MDC (logging) field correlation - -=== HTTP - -==== Data Policy - -The default span data policy for HTTP requests is described in Brave: -https://github.com/openzipkin/brave/tree/master/instrumentation/http#span-data-policy - -To add different data to the span, you need to register a bean of type -`brave.http.HttpRequestParser` or `brave.http.HttpResponseParser` based on when -the data is collected. - -The bean names correspond to the request or response side, and whether it is -a client or server. For example, `sleuthHttpClientRequestParser` changes what -is collected before a client request is sent to the server. - -For your convenience `@HttpClientRequestParser`, `@HttpClientResponseParser` -and corresponding server annotations can be used to inject the proper beans -or to reference the bean names via their static String `NAME` fields. - -Here's an example adding the HTTP url in addition to defaults: -[source,java] ----- -@Configuration -class Config { -include::{project-root}/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterWebIntegrationTests.java[tags=custom_parser,indent=2] -} ----- - -==== Sampling - -If client /server sampling is required, just 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, `Sampler`. - -[source,java] ----- -@Configuration -class Config { -include::{project-root}/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterWebIntegrationTests.java[tags=custom_server_sampler,indent=2] -} ----- - -=== `TracingFilter` - -You can also modify the behavior of the `TracingFilter`, which is the component that is responsible for processing the input HTTP request and adding tags basing on the HTTP response. -You can customize the tags or modify the response headers by registering your own instance of the `TracingFilter` bean. - -In the following example, we register the `TracingFilter` bean, add the `ZIPKIN-TRACE-ID` response header containing the current Span's trace id, and add a tag with key `custom` and a value `tag` to the span. - -[source,java] ----- -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] ----- - -=== Messaging - -Sleuth automatically configures the `MessagingTracing` bean which serves as a -foundation for Messaging instrumentation such as Kafka or JMS. - -If a customization of producer / consumer sampling of messaging traces is required, -just register a bean of type `brave.sampler.SamplerFunction` and -name the bean `sleuthProducerSampler` for producer sampler and `sleuthConsumerSampler` -for consumer sampler. - -For your convenience the `@ProducerSampler` and `@ConsumerSampler` -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 consumer requests per second, except for -the "alerts" channel. Other requests will use a global rate provided by the -`Tracing` component. - -[source,java] ----- -@Configuration -class Config { -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessagingAutoConfigurationIntegrationTests.java[tags=custom_messaging_server_sampler,indent=2] -} ----- - -For more, see https://github.com/openzipkin/brave/tree/master/instrumentation/messaging#sampling-policy - -=== 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. -That is not always the case, though. -There are situations in which you want to explicitly provide a different service name for all spans coming from your application. -To achieve that, you can pass the following property to your application to override that value (the example is for a service named `myService`): - -[source,yaml] ----- -spring.zipkin.service.name: myService ----- - -=== Customization of Reported Spans - -Before reporting spans (for example, to Zipkin) you may want to modify that span in some way. -You can do so by implementing a `SpanHandler`. - -In Sleuth, we generate spans with a fixed name. -Some users want to modify the name depending on values of tags. -You can implement the `SpanHandler` interface to alter that name. - -The following example shows how to register two beans that implement `SpanHandler`: - -[source,java] ----- -include::{project-root}//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SpanHandlerTests.java[tags=spanHandler,indent=0] ----- - -The preceding example results in changing the name of the reported span to `foo bar`, just before it gets reported (for example, to Zipkin). - -Sleuth registers a `SpanHandler` bean that can automatically skip reporting spans of given name patterns. The property `spring.sleuth.span-handler.span-name-patterns-to-skip` contains the default skip patterns for span names. The property `spring.sleuth.span-handler.additional-span-name-patterns-to-skip` will append the provided span name patterns to the existing ones. In order to disable this functionality just set `spring.sleuth.span-handler.enabled` to `false`. - -=== Host Locator - -IMPORTANT: This section is about defining *host* from service discovery. -It is *NOT* about finding Zipkin through service discovery. - -To define the host that corresponds to a particular span, we need to resolve the host name and port. -The default approach is to take these values from server properties. -If those are not set, we try to retrieve the host name from the network interfaces. - -If you have the discovery client enabled and prefer to retrieve the host address from the registered instance in a service registry, you have to set the `spring.zipkin.locator.discovery.enabled` property (it is applicable for both HTTP-based and Stream-based span reporting), as follows: - -[source,yaml] ----- -spring.zipkin.locator.discovery.enabled: true ----- - -== Sending Spans to Zipkin - -By default, if you add `spring-cloud-starter-zipkin` as a dependency to your project, when the span is closed, it is sent to Zipkin over HTTP. -The communication is asynchronous. -You can configure the URL by setting the `spring.zipkin.baseUrl` property, as follows: - -[source,yaml] ----- -spring.zipkin.baseUrl: https://192.168.99.100:9411/ ----- - -If you want to find Zipkin through service discovery, you can pass the Zipkin's service ID inside the URL, as shown in the following example for `zipkinserver` service ID: - -[source,yaml] ----- -spring.zipkin.baseUrl: https://zipkinserver/ ----- - -To disable this feature just set `spring.zipkin.discoveryClientEnabled` to `false. - -When the Discovery Client feature is enabled, Sleuth uses -`LoadBalancerClient` to find the URL of the Zipkin Server. It means -that you can set up the load balancing configuration e.g. via Ribbon. - -[source,yaml] ----- -zipkinserver: - ribbon: - ListOfServers: host1,host2 ----- - -If you have web, rabbit, activemq or kafka together on the classpath, you might need to pick the means by which you would like to send spans to zipkin. -To do so, set `web`, `rabbit`, `activemq` or `kafka` to the `spring.zipkin.sender.type` property. -The following example shows setting the sender type for `web`: - -[source,yaml] ----- -spring.zipkin.sender.type: web ----- - -To customize the `RestTemplate` that sends spans to Zipkin via HTTP, you can register -the `ZipkinRestTemplateCustomizer` bean. - -[source,java] ----- -@Configuration -class MyConfig { - @Bean ZipkinRestTemplateCustomizer myCustomizer() { - return new ZipkinRestTemplateCustomizer() { - @Override - void customize(RestTemplate restTemplate) { - // customize the RestTemplate - } - }; - } -} ----- - -If, however, you would like to control the full process of creating the `RestTemplate` -object, you will have to create a bean of `zipkin2.reporter.Sender` type. - -[source,java] ----- - @Bean Sender myRestTemplateSender(ZipkinProperties zipkin, - ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer) { - RestTemplate restTemplate = mySuperCustomRestTemplate(); - zipkinRestTemplateCustomizer.customize(restTemplate); - return myCustomSender(zipkin, restTemplate); - } ----- - -== Integrations - -=== OpenTracing - -Spring Cloud Sleuth is compatible with https://opentracing.io/[OpenTracing]. -If you have OpenTracing on the classpath, we automatically register the OpenTracing `Tracer` bean. -If you wish to disable this, set `spring.sleuth.opentracing.enabled` to `false` - -=== Runnable and Callable - -If you wrap your logic in `Runnable` or `Callable`, you can wrap those classes in their Sleuth representative, as shown in the following example for `Runnable`: - -[source,java] ----- -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=trace_runnable,indent=0] ----- - -The following example shows how to do so for `Callable`: - -[source,java] ----- -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=trace_callable,indent=0] ----- - -That way, you ensure that a new span is created and closed for each execution. - -=== Spring Cloud CircuitBreaker - -If you have Spring Cloud CircuitBreaker on the classpath, we will wrap the passed command `Supplier` and the fallback `Function` in its trace representations. In order to disable this instrumentation set `spring.sleuth.circuitbreaker.enabled` to `false`. - -=== RxJava - -We registering a custom https://github.com/ReactiveX/RxJava/wiki/Plugins#rxjavaschedulershook[`RxJavaSchedulersHook`] that wraps all `Action0` instances in their Sleuth representative, which is called `TraceAction`. -The hook either starts or continues a span, depending on whether tracing was already going on before the Action was scheduled. -To disable the custom `RxJavaSchedulersHook`, set the `spring.sleuth.rxjava.schedulers.hook.enabled` to `false`. - -You can define a list of regular expressions for thread names for which you do not want spans to be created. -To do so, provide a comma-separated list of regular expressions in the `spring.sleuth.rxjava.schedulers.ignoredthreads` property. - -IMPORTANT: The suggest approach to reactive programming and Sleuth is to use -the Reactor support. - -=== HTTP integration - -Features from this section can be disabled by setting the `spring.sleuth.web.enabled` property with value equal to `false`. - -==== HTTP Filter - -Through the `TracingFilter`, all sampled incoming requests result in creation of a Span. -That Span's name is `http:` + the path to which the request was sent. -For example, if the request was sent to `/this/that` then the name will be `http:/this/that`. -You can configure which URIs you would like to skip by setting the `spring.sleuth.web.skipPattern` property. -If you have `ManagementServerProperties` on classpath, its value of `contextPath` gets appended to the provided skip pattern. -If you want to reuse the Sleuth's default skip patterns and just append your own, pass those patterns by using the `spring.sleuth.web.additionalSkipPattern`. - -By default, all the spring boot actuator endpoints are automatically added to the skip pattern. -If you want to disable this behaviour set `spring.sleuth.web.ignore-auto-configured-skip-patterns` -to `true`. - -To change the order of tracing filter registration, please set the -`spring.sleuth.web.filter-order` property. - -To disable the filter that logs uncaught exceptions you can disable the -`spring.sleuth.web.exception-throwing-filter-enabled` property. - -==== HandlerInterceptor - -Since we want the span names to be precise, we use a `TraceHandlerInterceptor` that either wraps an existing `HandlerInterceptor` or is added directly to the list of existing `HandlerInterceptors`. -The `TraceHandlerInterceptor` adds a special request attribute to the given `HttpServletRequest`. -If the the `TracingFilter` does not see this attribute, it creates a "`fallback`" span, which is an additional span created on the server side so that the trace is presented properly in the UI. -If that happens, there is probably missing instrumentation. -In that case, please file an issue in Spring Cloud Sleuth. - -==== Async Servlet support - -If your controller returns a `Callable` or a `WebAsyncTask`, Spring Cloud Sleuth continues the existing span instead of creating a new one. - -==== WebFlux support - -Through `TraceWebFilter`, all sampled incoming requests result in creation of a Span. -That Span's name is `http:` + the path to which the request was sent. -For example, if the request was sent to `/this/that`, the name is `http:/this/that`. -You can configure which URIs you would like to skip by using the `spring.sleuth.web.skipPattern` property. -If you have `ManagementServerProperties` on the classpath, its value of `contextPath` gets appended to the provided skip pattern. -If you want to reuse Sleuth's default skip patterns and append your own, pass those patterns by using the `spring.sleuth.web.additionalSkipPattern`. - -In order to achieve best results in terms of performance and context propagation we suggest that you switch the `spring.sleuth.reactor.instrumentation-type` to `MANUAL`. In order to execute code with the span in scope you can call `WebFluxSleuthOperators.withSpanInScope`. - -To change the order of tracing filter registration, please set the -`spring.sleuth.web.filter-order` property. - -==== 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` dependency: - -[source,xml,indent=0] ----- - - io.zipkin.brave - brave-instrumentation-dubbo - ----- - -You need to also set a `dubbo.properties` file with the following contents: - -```properties -dubbo.provider.filter=tracing -dubbo.consumer.filter=tracing -``` - -You can read more about Brave - Dubbo integration https://github.com/openzipkin/brave/tree/master/instrumentation/dubbo-rpc[here]. -An example of Spring Cloud Sleuth and Dubbo can be found https://github.com/openzipkin/sleuth-webmvc-example/compare/add-dubbo-tracing[here]. - -=== HTTP Client Integration - -==== Synchronous Rest Template - -We inject a `RestTemplate` interceptor to ensure that all the tracing information is passed to the requests. -Each time a call is made, a new Span is created. -It gets closed upon receiving the response. -To block the synchronous `RestTemplate` features, set `spring.sleuth.web.client.enabled` to `false`. - -IMPORTANT: You have to register `RestTemplate` as a bean so that the interceptors get injected. -If you create a `RestTemplate` instance with a `new` keyword, the instrumentation does NOT work. - -==== Asynchronous Rest Template - -IMPORTANT: Starting with Sleuth `2.0.0`, we no longer register a bean of `AsyncRestTemplate` type. -It is up to you to create such a bean. -Then we instrument it. - -To block the `AsyncRestTemplate` features, set `spring.sleuth.web.async.client.enabled` to `false`. -To disable creation of the default `TraceAsyncClientHttpRequestFactoryWrapper`, set `spring.sleuth.web.async.client.factory.enabled` -to `false`. -If you do not want to create `AsyncRestClient` at all, set `spring.sleuth.web.async.client.template.enabled` to `false`. - -===== Multiple Asynchronous Rest Templates - -Sometimes you need to use multiple implementations of the Asynchronous Rest Template. -In the following snippet, you can see an example of how to set up such a custom `AsyncRestTemplate`: - -[source,java] ----- -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/MultipleAsyncRestTemplateTests.java[tags=custom_async_rest_template,indent=0] ----- - -==== `WebClient` - -We inject a `ExchangeFilterFunction` implementation that creates a span and, through on-success and on-error callbacks, takes care of closing client-side spans. - -To block this feature, set `spring.sleuth.web.client.enabled` to `false`. - -IMPORTANT: You have to register `WebClient` as a bean so that the tracing instrumentation gets applied. -If you create a `WebClient` instance with a `new` keyword, the instrumentation does NOT work. - -==== Traverson - -If you use the https://docs.spring.io/spring-hateoas/docs/current/reference/html/#client.traverson[Traverson] library, you can inject a `RestTemplate` as a bean into your Traverson object. -Since `RestTemplate` is already intercepted, you get full support for tracing in your client. The following pseudo code -shows how to do that: - -[source,java] ----- -@Autowired RestTemplate restTemplate; - -Traverson traverson = new Traverson(URI.create("https://some/address"), - MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON_UTF8).setRestOperations(restTemplate); -// use Traverson ----- - -==== Apache `HttpClientBuilder` and `HttpAsyncClientBuilder` - -We instrument the `HttpClientBuilder` and `HttpAsyncClientBuilder` so that -tracing context gets injected to the sent requests. - -To block these features, set `spring.sleuth.web.client.enabled` to `false`. - -==== Netty `HttpClient` - -We instrument the Netty's `HttpClient`. - -To block this feature, set `spring.sleuth.web.client.enabled` to `false`. - -IMPORTANT: You have to register `HttpClient` as a bean so that the instrumentation happens. -If you create a `HttpClient` instance with a `new` keyword, the instrumentation does NOT work. - -==== `UserInfoRestTemplateCustomizer` - -We instrument the Spring Security's `UserInfoRestTemplateCustomizer`. - -To block this feature, set `spring.sleuth.web.client.enabled` to `false`. - -=== Feign - -By default, Spring Cloud Sleuth provides integration with Feign through `TraceFeignClientAutoConfiguration`. -You can disable it entirely by setting `spring.sleuth.feign.enabled` to `false`. -If you do so, no Feign-related instrumentation take place. - -Part of Feign instrumentation is done through a `FeignBeanPostProcessor`. -You can disable it by setting `spring.sleuth.feign.processor.enabled` to `false`. -If you set it to `false`, Spring Cloud Sleuth does not instrument any of your custom Feign components. -However, all the default instrumentation is still there. - -=== gRPC - -Spring Cloud Sleuth provides instrumentation for https://grpc.io/[gRPC] through `TraceGrpcAutoConfiguration`. You can disable it entirely by setting `spring.sleuth.grpc.enabled` to `false`. - -==== Variant 1 - -===== Dependencies -IMPORTANT: The gRPC integration relies on two external libraries to instrument clients and servers and both of those libraries must be on the class path to enable the instrumentation. - -Maven: -``` - - io.github.lognet - grpc-spring-boot-starter - - - io.zipkin.brave - brave-instrumentation-grpc - -``` -Gradle: -``` - compile("io.github.lognet:grpc-spring-boot-starter") - compile("io.zipkin.brave:brave-instrumentation-grpc") -``` - -===== Server Instrumentation - -Spring Cloud Sleuth leverages grpc-spring-boot-starter to register Brave's gRPC server interceptor with all services annotated with `@GRpcService`. - -===== Client Instrumentation - -gRPC clients leverage a `ManagedChannelBuilder` to construct a `ManagedChannel` used to communicate to the gRPC server. The native `ManagedChannelBuilder` provides static methods as entry points for construction of `ManagedChannel` instances, however, this mechanism is outside the influence of the Spring application context. - -IMPORTANT: Spring Cloud Sleuth provides a `SpringAwareManagedChannelBuilder` that can be customized through the Spring application context and injected by gRPC clients. *This builder must be used when creating `ManagedChannel` instances.* - - -Sleuth creates a `TracingManagedChannelBuilderCustomizer` which inject Brave's client interceptor into the `SpringAwareManagedChannelBuilder`. - -==== Variant 2 - -https://github.com/yidongnan/grpc-spring-boot-starter[Grpc Spring Boot Starter] automatically detects the presence of Spring Cloud Sleuth and brave's instrumentation for gRPC and registers the necessary client and/or server tooling. - -=== Asynchronous Communication - -==== `@Async` Annotated methods - -In Spring Cloud Sleuth, we instrument async-related components so that the tracing information is passed between threads. -You can disable this behavior by setting the value of `spring.sleuth.async.enabled` to `false`. - -If you annotate your method with `@Async`, we automatically create a new Span with the following characteristics: - -* If the method is annotated with `@SpanName`, the value of the annotation is the Span's name. -* If the method is not annotated with `@SpanName`, the Span name is the annotated method name. -* The span is tagged with the method's class name and method name. - -==== `@Scheduled` Annotated Methods - -In Spring Cloud Sleuth, we instrument scheduled method execution so that the tracing information is passed between threads. -You can disable this behavior by setting the value of `spring.sleuth.scheduled.enabled` to `false`. - -If you annotate your method with `@Scheduled`, we automatically create a new span with the following characteristics: - -* The span name is the annotated method name. -* The span is tagged with the method's class name and method name. - -If you want to skip span creation for some `@Scheduled` annotated classes, you can set the `spring.sleuth.scheduled.skipPattern` with a regular expression that matches the fully qualified name of the `@Scheduled` annotated class. - -==== Executor, ExecutorService, and ScheduledExecutorService - -We provide `LazyTraceExecutor`, `TraceableExecutorService`, and `TraceableScheduledExecutorService`. Those implementations create spans each time a new task is submitted, invoked, or scheduled. - -The following example shows how to pass tracing information with `TraceableExecutorService` when working with `CompletableFuture`: - -[source,java] ----- - -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorServiceTests.java[tags=completablefuture,indent=0] ----- - -IMPORTANT: Sleuth does not work with `parallelStream()` out of the box. -If you want to have the tracing information propagated through the stream, you have to use the approach with `supplyAsync(...)`, as shown earlier. - -If there are beans that implement the `Executor` interface that you would like -to exclude from span creation, you can use the `spring.sleuth.async.ignored-beans` -property where you can provide a list of bean names. - -===== Customization of Executors - -Sometimes, you need to set up a custom instance of the `AsyncExecutor`. -The following example shows how to set up such a custom `Executor`: - -[source,java] ----- -include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/MultipleAsyncRestTemplateTests.java[tags=custom_executor,indent=0] ----- - -TIP: To ensure that your configuration gets post processed, remember -to add the `@Role(BeanDefinition.ROLE_INFRASTRUCTURE)` on your -`@Configuration` class - -=== Messaging - -Features from this section can be disabled by setting the `spring.sleuth.messaging.enabled` property with value equal to `false`. - -==== Spring Integration - -Spring Cloud Sleuth integrates with https://projects.spring.io/spring-integration/[Spring Integration]. -It creates spans for publish and subscribe events. -To disable Spring Integration instrumentation, set `spring.sleuth.integration.enabled` to `false`. - -You can provide the `spring.sleuth.integration.patterns` pattern to explicitly provide the names of channels that you want to include for tracing. -By default, all channels but `hystrixStreamOutput` channel are included. - -IMPORTANT: When using the `Executor` to build a Spring Integration `IntegrationFlow`, you must use the untraced version of the `Executor`. -Decorating the Spring Integration Executor Channel with `TraceableExecutorService` causes the spans to be improperly closed. - -If you want to customize the way tracing context is read from and written to message headers, -it's enough for you to register beans of types: - -* `Propagation.Setter` - for writing headers to the message -* `Propagation.Getter` - for reading headers from the message - -==== Spring Cloud Function and Spring Cloud Stream - -Spring Cloud Sleuth can instrument Spring Cloud Function. The way to achieve it is to provide a `Function` or `Consumer` or `Supplier` that takes in a `Message` as a parameter e.g. `Function, Message>`. If the type is not `Message` then instrumentation will not take place. Out of the box instrumentation will not take place when dealing with Reactor based streams - e.g. `Function>, Flux>>`. - -Since Spring Cloud Stream reuses Spring Cloud Function, you'll get the instrumentation out of the box. - -You can disable this behavior by setting the value of `spring.sleuth.function.enabled` to `false`. - -In order to work with reactive Stream functions you can leverage the `MessagingSleuthOperators` utility class that allows you to manipulate the input and output messages in order to continue the tracing context and to execute custom code within the tracing context. - -[source,java] ------ -include::{project-root}/benchmarks/src/main/java/org/springframework/cloud/sleuth/benchmarks/app/stream/SleuthBenchmarkingStreamApplication.java[tags=simple_reactive,indent=0] ------ - -==== Spring RabbitMq - -We instrument the `RabbitTemplate` so that tracing headers get injected -into the message. - -To block this feature, set `spring.sleuth.messaging.rabbit.enabled` to `false`. - -==== Spring Kafka - -We instrument the Spring Kafka's `ProducerFactory` and `ConsumerFactory` -so that tracing headers get injected into the created Spring Kafka's -`Producer` and `Consumer`. - -To block this feature, set `spring.sleuth.messaging.kafka.enabled` to `false`. - -==== Spring Kafka Streams - -We instrument the `KafkaStreams` `KafkaClientSupplier` so that tracing headers -get injected into the `Producer` and `Consumer`s. A `KafkaStreamsTracing` bean -allows for further instrumentation through additional `TransformerSupplier` and -`ProcessorSupplier` methods. - -To block this feature, set `spring.sleuth.messaging.kafka.streams.enabled` to `false`. - -==== Spring JMS - -We instrument the `JmsTemplate` so that tracing headers get injected -into the message. We also support `@JmsListener` annotated methods on the consumer side. - -To block this feature, set `spring.sleuth.messaging.jms.enabled` to `false`. - -IMPORTANT: We don't support baggage propagation for JMS - -==== Spring Cloud AWS Messaging SQS - -We instrument `@SqsListener` which is provided by `org.springframework.cloud:spring-cloud-aws-messaging` -so that tracing headers get extracted from the message and a trace gets put into the context. - -To block this feature, set `spring.sleuth.messaging.sqs.enabled` to `false`. - -=== Redis - -We set `tracing` property to Lettcue `ClientResources` instance to enable Brave tracing built in Lettuce . -To disable Redis support, set the `spring.sleuth.redis.enabled` property to `false`. - -=== Quartz - -We instrument quartz jobs by adding Job/Trigger listeners to the Quartz Scheduler. - -To turn off this feature, set the `spring.sleuth.quartz.enabled` property to `false`. - -=== Project Reactor - -We have three modes of instrumenting reactor based applications that can -be set via `spring.sleuth.reactor.instrumentation-type` property: - -* `ON_EACH` - wraps every Reactor operator in a trace representation. Passes the tracing context in most cases. This mode might lead to drastic performance degradation. -* `ON_LAST` - wraps last Reactor operator in a trace representation. Passes the tracing context in some cases thus accessing MDC context might not work. This mode might lead to medium performance degradation. -* `MANUAL` - wraps every Reactor in the least invasive way without passing of tracing context. It's up to the user to do it. - -Current default is `ON_EACH` for backward compatibility reasons, however we encourage the users to migrate to the `MANUAL` instrumentation and profit from `WebFluxSleuthOperators` and `MessagingSleuthOperators`. The performance improvement can be substantial. Example: - -[source,java] ------ -include::{project-root}/benchmarks/src/main/java/org/springframework/cloud/sleuth/benchmarks/app/webflux/SleuthBenchmarkingSpringWebFluxApp.java[tags=simple_manual,indent=0] ------ - -== Log integration -Sleuth configures the logging context with variables including the service name -(`%{spring.zipkin.service.name}`) and the trace ID (`%{traceId}`). These help -you connect logs with distributed traces and allow you choice in what tools you -use to troubleshoot your services. - -If you use a log aggregating tool (such as https://www.elastic.co/products/kibana[Kibana], https://www.splunk.com/[Splunk], and others), you can order the events that took place. -An example from Kibana would resemble the following image: - -image::{github-raw}/src/main/asciidoc/images/kibana.png[Log correlation with Kibana] - -If you want to use https://www.elastic.co/guide/en/logstash/current/index.html[Logstash], the following listing shows the Grok pattern for Logstash: - -[source] ----- -filter { - # pattern matching logback pattern - grok { - match => { "message" => "%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" } - } - date { - match => ["timestamp", "ISO8601"] - } - mutate { - remove_field => ["timestamp"] - } -} ----- - -NOTE: If you want to use Grok together with the logs from Cloud Foundry, you have to use the following pattern: -[source] ----- -filter { - # pattern matching logback pattern - grok { - match => { "message" => "(?m)OUT\s+%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" } - } - date { - match => ["timestamp", "ISO8601"] - } - mutate { - remove_field => ["timestamp"] - } -} ----- - -=== JSON Logback with Logstash - -Often, you do not want to store your logs in a text file but in a JSON file that Logstash can immediately pick. -To do so, you have to do the following (for readability, we pass the dependencies in the `groupId:artifactId:version` notation). - -*Dependencies Setup* - -. Ensure that Logback is on the classpath (`ch.qos.logback:logback-core`). -. Add Logstash Logback encode. For example, to use version `4.6`, add `net.logstash.logback:logstash-logback-encoder:4.6`. - -*Logback Setup* - -Consider the following example of a Logback configuration file (logback-spring.xml). - -[source,xml] ------ -include::{project-root}/docs/src/main/asciidoc/logback-spring.xml[] ------ - -That Logback configuration file: - -* Logs information from the application in a JSON format to a `build/${spring.application.name}.json` file. -* Has commented out two additional appenders: console and standard log file. -* Has the same logging pattern as the one presented in the previous section. - -NOTE: If you use a custom `logback-spring.xml`, you must pass the `spring.application.name` in the `bootstrap` rather than the `application` property file. -Otherwise, your custom logback file does not properly read the property. - -== Configuration properties - -To see the list of all Sleuth related configuration properties please check link:appendix.html[the Appendix page]. diff --git a/docs/src/main/asciidoc/spring-cloud-sleuth.adoc b/docs/src/main/asciidoc/spring-cloud-sleuth.adoc new file mode 120000 index 000000000..1abdb4fda --- /dev/null +++ b/docs/src/main/asciidoc/spring-cloud-sleuth.adoc @@ -0,0 +1 @@ +index.htmladoc \ No newline at end of file diff --git a/docs/src/main/asciidoc/spring-cloud-sleuth.htmlsingleadoc b/docs/src/main/asciidoc/spring-cloud-sleuth.htmlsingleadoc new file mode 120000 index 000000000..edc86da18 --- /dev/null +++ b/docs/src/main/asciidoc/spring-cloud-sleuth.htmlsingleadoc @@ -0,0 +1 @@ +index.htmlsingleadoc \ No newline at end of file diff --git a/docs/src/main/asciidoc/spring-cloud-sleuth.pdfadoc b/docs/src/main/asciidoc/spring-cloud-sleuth.pdfadoc new file mode 100644 index 000000000..572640be0 --- /dev/null +++ b/docs/src/main/asciidoc/spring-cloud-sleuth.pdfadoc @@ -0,0 +1 @@ +include::_index_pdf.adoc[] \ No newline at end of file diff --git a/docs/src/main/asciidoc/using.adoc b/docs/src/main/asciidoc/using.adoc new file mode 100644 index 000000000..d1691fb02 --- /dev/null +++ b/docs/src/main/asciidoc/using.adoc @@ -0,0 +1,272 @@ +[[using]] += Using Spring Cloud Sleuth +include::_attributes.adoc[] + +This section goes into more detail about how you should use {project-full-name}. It covers topics such as controlling the span lifecycle with {project-full-name} API or via annotations. We also cover some {project-full-name} best practices. + +If you are starting out with {project-full-name}, you should probably read the +<> guide before diving into this section. + +[[using-span-lifecycle]] +== Span Lifecycle with Spring Cloud Sleuth's API + +Spring Cloud Sleuth Core in its `api` module contains all necessary interfaces to be implemented by a tracer. The project comes with OpenZipkin Brave and OpenTelemetry implementations. You can check how both tracers are bridged to the Sleuth's API by looking at the `org.springframework.cloud.sleuth.brave.bridge` and `org.springframework.cloud.sleuth.otel.bridge` modules respectively. + +The most commonly used interfaces are: + +* `org.springframework.cloud.sleuth.api.Tracer` - Using a tracer, you can create a root span capturing the critical path of a request. +* `org.springframework.cloud.sleuth.api.Span` - Span is a single unit of work that needs to be started and stopped. Contains timing information and events and tags. + +You can also use your tracer implementation's API directly. + +Let's look at the following Span lifecycle actions. + +* <>: When you start a span, its name is assigned and the start timestamp is recorded. +* <>: The span gets finished (the end time of the span is recorded) and, if the span is sampled, it is eligible for collection (e.g. to Zipkin). +* <>: The span gets continued e.g. in another thread. +* <>: You can create a new span and set an explicit parent for it. + +TIP: Spring Cloud Sleuth creates an instance of `Tracer` for you. In order to use it, you can autowire it. + +[[using-creating-and-ending-spans]] +=== Creating and Ending Spans + +You can manually create spans by using the `Tracer`, as shown in the following example: + +[source,java,indent=0] +---- +include::{brave_path}/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=manual_span_creation,indent=0] +---- + +In the preceding example, we could see how to create a new instance of the span. +If there is already a span in this thread, it becomes the parent of the new span. + +IMPORTANT: Always clean after you create a span. + +IMPORTANT: If your span contains a name greater than 50 chars, that name is truncated to 50 chars. +Your names have to be explicit and concrete. Big names lead to latency issues and sometimes even exceptions. + +[[using-continuing-spans]] +=== Continuing Spans + +Sometimes, you do not want to create a new span but you want to continue one. An example of such a +situation might be as follows: + +* *AOP*: If there was already a span created before an aspect was reached, you might not want to create a new span. + +To continue a span, you can store the span in one thread and pass it on to another one as shown in the example below. + +[source,java,indent=0] +---- +include::{brave_path}/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=manual_span_continuation,indent=0] +---- + +[[using-creating-spans-with-explicit-parent]] +=== Creating a Span with an explicit Parent + +You might want to start a new span and provide an explicit parent of that span. +Assume that the parent of a span is in one thread and you want to start a new span in another thread. +Whenever you call `Tracer.nextSpan()`, it creates a span in reference to the span that is currently in scope. +You can put the span in scope and then call `Tracer.nextSpan()`, as shown in the following example: + +[source,java,indent=0] +---- +include::{brave_path}/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=manual_span_joining,indent=0] +---- + +IMPORTANT: After creating such a span, you must finish it. Otherwise it is not reported (e.g. to Zipkin). + +You can also use the `Tracer.nextSpan(Span parentSpan)` version to provide the parent span explicitly. + +[[using-naming-spans]] +== Naming Spans + +Picking a span name is not a trivial task. A span name should depict an operation name. +The name should be low cardinality, so it should not include identifiers. + +Since there is a lot of instrumentation going on, some span names are artificial: + +* `controller-method-name` when received by a Controller with a method name of `controllerMethodName` +* `async` for asynchronous operations done with wrapped `Callable` and `Runnable` interfaces. +* Methods annotated with `@Scheduled` return the simple name of the class. + +Fortunately, for asynchronous processing, you can provide explicit naming. + +[[using-naming-spans-annotation]] +=== `@SpanName` Annotation + +You can name the span explicitly by using the `@SpanName` annotation, as shown in the following example: + +[source,java,indent=0] +---- +include::{brave_path}/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=span_name_annotation,indent=0] +---- + +In this case, when processed in the following manner, the span is named `calculateTax`: + +[source,java,indent=0] +---- +include::{brave_path}/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=span_name_annotated_runnable_execution,indent=0] +---- + +[[using-naming-spans-to-string]] +=== `toString()` Method + +It is pretty rare to create separate classes for `Runnable` or `Callable`. +Typically, one creates an anonymous instance of those classes. +You cannot annotate such classes. +To overcome that limitation, if there is no `@SpanName` annotation present, we check whether the class has a custom implementation of the `toString()` method. + +Running such code leads to creating a span named `calculateTax`, as shown in the following example: + +[source,java,indent=0] +---- +include::{brave_path}/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=span_name_to_string_runnable_execution,indent=0] +---- + +[[using-annotations]] +== Managing Spans with Annotations + +There are a number of good reasons to manage spans with annotations, including: + +* API-agnostic means to collaborate with a span. Use of annotations lets users add to a span with no library dependency on a span api. +Doing so lets Sleuth change its core API to create less impact to user code. +* Reduced surface area for basic span operations. Without this feature, you must use the span api, which has lifecycle commands that could be used incorrectly. +By only exposing scope, tag, and log functionality, you can collaborate without accidentally breaking span lifecycle. +* Collaboration with runtime generated code. With libraries such as Spring Data and Feign, the implementations of interfaces are generated at runtime. +Consequently, span wrapping of objects was tedious. +Now you can provide annotations over interfaces and the arguments of those interfaces. + +[[using-annotations-new-spans]] +=== Creating New Spans + +If you do not want to create local spans manually, you can use the `@NewSpan` annotation. +Also, we provide the `@SpanTag` annotation to add tags in an automated fashion. + +Now we can consider some examples of usage. + +[source,java,indent=0] +---- +include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectTests.java[tags=annotated_method,indent=0] +---- + +Annotating the method without any parameter leads to creating a new span whose name equals the annotated method name. + +[source,java,indent=0] +---- +include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectTests.java[tags=custom_name_on_annotated_method,indent=0] +---- + +If you provide the value in the annotation (either directly or by setting the `name` parameter), the created span has the provided value as the name. + +[source,java,indent=0] +---- +// method declaration +include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectTests.java[tags=custom_name_and_tag_on_annotated_method,indent=0] + +// and method execution +include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectTests.java[tags=execution,indent=0] +---- + +You can combine both the name and a tag. Let's focus on the latter. +In this case, the value of the annotated method's parameter runtime value becomes the value of the tag. +In our sample, the tag key is `testTag`, and the tag value is `test`. + +[source,java,indent=0] +---- +include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectTests.java[tags=name_on_implementation,indent=0] +---- + +You can place the `@NewSpan` annotation on both the class and an interface. +If you override the interface's method and provide a different value for the `@NewSpan` annotation, the most +concrete one wins (in this case `customNameOnTestMethod3` is set). + +[[using-annotations-continuing-spans]] +=== Continuing Spans + +If you want to add tags and annotations to an existing span, you can use the `@ContinueSpan` annotation, as shown in the following example: + +[source,java,indent=0] +---- +// method declaration +include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectTests.java[tags=continue_span,indent=0] + +// method execution +include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectTests.java[tags=continue_span_execution,indent=0] +---- + +(Note that, in contrast with the `@NewSpan` annotation ,you can also add logs with the `log` parameter.) + +That way, the span gets continued and: + +* Log entries named `testMethod11.before` and `testMethod11.after` are created. +* If an exception is thrown, a log entry named `testMethod11.afterFailure` is also created. +* A tag with a key of `testTag11` and a value of `test` is created. + +[[using-annotations-advanced-tag-setting]] +=== Advanced Tag Setting + +There are 3 different ways to add tags to a span. All of them are controlled by the `SpanTag` annotation. +The precedence is as follows: + +. Try with a bean of `TagValueResolver` type and a provided name. +. If the bean name has not been provided, try to evaluate an expression. +We search for a `TagValueExpressionResolver` bean. +The default implementation uses SPEL expression resolution. +**IMPORTANT** You can only reference properties from the SPEL expression. Method execution is not allowed due to security constraints. +. If we do not find any expression to evaluate, return the `toString()` value of the parameter. + +[[using-annotations-custom-extractor]] +==== Custom Extractor + +The value of the tag for the following method is computed by an implementation of `TagValueResolver` interface. +Its class name has to be passed as the value of the `resolver` attribute. + +Consider the following annotated method: + +[source,java,indent=0] +---- +include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/annotation/SpanTagAnnotationHandlerTests.java[tags=resolver_bean,indent=0] +---- + +Now further consider the following `TagValueResolver` bean implementation: + +[source,java,indent=0] +---- +include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/annotation/SpanTagAnnotationHandlerTests.java[tags=custom_resolver,indent=0] +---- + +The two preceding examples lead to setting a tag value equal to `Value from myCustomTagValueResolver`. + +[[using-annotations-resolving-expressions]] +==== Resolving Expressions for a Value + +Consider the following annotated method: + +[source,java,indent=0] +---- +include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/annotation/SpanTagAnnotationHandlerTests.java[tags=spel,indent=0] +---- + +No custom implementation of a `TagValueExpressionResolver` leads to evaluation of the SPEL expression, and a tag with a value of `4 characters` is set on the span. +If you want to use some other expression resolution mechanism, you can create your own implementation of the bean. + +[[using-annotations-to-string]] +==== Using The `toString()` Method + +Consider the following annotated method: + +[source,java,indent=0] +---- +include::{common_tests_path}/src/main/java/org/springframework/cloud/sleuth/annotation/SpanTagAnnotationHandlerTests.java[tags=toString,indent=0] +---- + +Running the preceding method with a value of `15` leads to setting a tag with a String value of `"15"`. + +[[using-whats-next]] +== What to Read Next + +You should now understand how you can use {project-full-name} and some best practices that you +should follow. You can now go on to learn about specific +<>, or you could +skip ahead and read about the link:integrations[integrations available in {project-full-name}]. diff --git a/pom.xml b/pom.xml index ab34b9b8d..5cce733e9 100644 --- a/pom.xml +++ b/pom.xml @@ -47,10 +47,12 @@ spring-cloud-sleuth-dependencies spring-cloud-sleuth-core + spring-cloud-sleuth-brave + spring-cloud-sleuth-otel tests spring-cloud-sleuth-zipkin spring-cloud-starter-sleuth - spring-cloud-starter-zipkin + spring-cloud-starter-sleuth-otel spring-cloud-sleuth-samples docs @@ -188,6 +190,13 @@ pom import + + io.opentelemetry + opentelemetry-bom + ${opentelemetry.version} + pom + import + org.spockframework @@ -218,16 +227,37 @@ ${objenesis.version} + + com.squareup.okhttp3 + mockwebserver + ${mockwebserver.version} + + + + io.opentelemetry.instrumentation + opentelemetry-instrumentation-api + ${opentelemetry-instrumentation.version} + + + org.springframework.security.oauth + spring-security-oauth2 + ${spring-security-oauth2.version} + + + io.zipkin.aws + brave-propagation-aws + ${brave-propagation-aws.version} + org.hamcrest hamcrest-core - 2.2 + ${hamcrest-core.version} test org.awaitility awaitility - 4.0.3 + ${awaitility.version} test @@ -252,6 +282,10 @@ 3.0.0-SNAPSHOT 3.0.0-SNAPSHOT 5.12.3 + 0.32.0 + 0.9.1 + + 0.9.0-SNAPSHOT 2.3.3.RELEASE false 4.9.0 @@ -260,12 +294,46 @@ 1.7.1 3.3.0 3.0.1 + 2.2.0.RELEASE true false + 3.8.1 + 2.2 + 4.0.3 + 0.21.3 + + + + false + + bintray-open-telemetry-maven + bintray + https://dl.bintray.com/open-telemetry/maven + + + + true + + bintray-open-telemetry-maven-snapshot + bintray + https://oss.jfrog.org/oss-snapshot-local/ + + + + + + false + + bintray-open-telemetry-maven + bintray-plugins + https://dl.bintray.com/open-telemetry/maven + + + spring diff --git a/spring-cloud-sleuth-brave/pom.xml b/spring-cloud-sleuth-brave/pom.xml new file mode 100644 index 000000000..62975299f --- /dev/null +++ b/spring-cloud-sleuth-brave/pom.xml @@ -0,0 +1,370 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-brave + jar + Spring Cloud Sleuth Brave + Spring Cloud Sleuth Brave + + + org.springframework.cloud + spring-cloud-sleuth + 3.0.0-SNAPSHOT + .. + + + + + org.springframework.cloud + spring-cloud-sleuth-core + + + org.springframework.boot + spring-boot-starter-web + true + + + io.micrometer + micrometer-core + true + + + org.springframework.boot + spring-boot-starter-webflux + true + + + io.projectreactor + reactor-core + true + + + io.projectreactor.netty + reactor-netty-http + true + + + org.reactivestreams + reactive-streams + true + + + org.springframework.boot + spring-boot-starter-websocket + true + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.springframework.boot + spring-boot-starter-actuator + true + + + org.springframework.cloud + spring-cloud-commons + + + org.springframework.cloud + spring-cloud-stream + true + + + org.springframework.cloud + spring-cloud-starter-gateway + true + + + org.springframework.cloud + spring-cloud-starter-circuitbreaker-resilience4j + true + + + org.springframework.cloud + spring-cloud-starter-openfeign + true + + + org.springframework.cloud + spring-cloud-function-context + true + + + org.springframework.integration + spring-integration-core + true + + + org.springframework.amqp + spring-rabbit + true + + + org.springframework.kafka + spring-kafka + true + + + org.apache.kafka + kafka-streams + true + + + org.springframework.boot + spring-boot-starter-security + true + + + org.springframework + spring-context + + + org.springframework.cloud + spring-cloud-context + true + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + true + + + io.github.openfeign + feign-core + true + + + io.github.openfeign.form + feign-form-spring + true + + + io.reactivex + rxjava + true + + + com.squareup.okhttp3 + okhttp + ${okhttp.version} + true + + + org.apache.httpcomponents + httpclient + true + + + io.github.openfeign + feign-okhttp + true + + + org.springframework.boot + spring-boot-starter-data-mongodb + true + + + org.aspectj + aspectjrt + + + + io.zipkin.brave + brave + + + io.zipkin.reporter2 + * + + + io.zipkin.zipkin2 + * + + + + + io.zipkin.brave + brave-context-slf4j + + + io.zipkin.brave + brave-instrumentation-messaging + + + io.zipkin.brave + brave-instrumentation-rpc + + + io.zipkin.brave + brave-instrumentation-spring-rabbit + + + io.zipkin.brave + brave-instrumentation-kafka-clients + + + io.zipkin.brave + brave-instrumentation-kafka-streams + + + io.zipkin.brave + brave-instrumentation-httpclient + + + io.zipkin.brave + brave-instrumentation-httpasyncclient + + + io.zipkin.brave + brave-instrumentation-jms + + + io.zipkin.brave + brave-instrumentation-mongodb + + + io.zipkin.aws + brave-propagation-aws + + + javax.jms + javax.jms-api + true + + + io.opentracing.brave + brave-opentracing + true + + + org.apache.httpcomponents + httpasyncclient + true + + + org.springframework + spring-jms + true + + + + io.github.lognet + grpc-spring-boot-starter + true + + + org.springframework.boot + spring-boot-starter + + + + + io.zipkin.brave + brave-instrumentation-grpc + true + + + io.zipkin.reporter2 + zipkin-reporter-metrics-micrometer + + + io.micrometer + micrometer-core + + + + + + io.lettuce + lettuce-core + true + + + + org.springframework.boot + spring-boot-starter-quartz + true + + + org.springframework.boot + spring-boot-autoconfigure-processor + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + io.zipkin.brave + brave-instrumentation-http-tests + test + + + com.squareup.okhttp3 + mockwebserver + test + + + org.assertj + assertj-core + test + + + org.awaitility + awaitility + test + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + test + + + + + + fast + + false + + + + + maven-surefire-plugin + + 4 + true + -Xmx1024m -XX:MaxPermSize=256m + + + + + + + + diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/LocalServiceName.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/LocalServiceName.java similarity index 96% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/LocalServiceName.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/LocalServiceName.java index 04dafecf2..47e01f134 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/LocalServiceName.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/LocalServiceName.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth; +package org.springframework.cloud.sleuth.brave; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/autoconfig/CompositeSpanHandler.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/autoconfig/CompositeSpanHandler.java new file mode 100644 index 000000000..ad29f7786 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/autoconfig/CompositeSpanHandler.java @@ -0,0 +1,58 @@ +/* + * Copyright 2013-2020 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.brave.autoconfig; + +import java.util.Collections; +import java.util.List; + +import brave.handler.MutableSpan; +import brave.handler.SpanHandler; +import brave.propagation.TraceContext; + +import org.springframework.cloud.sleuth.api.exporter.SpanFilter; +import org.springframework.cloud.sleuth.brave.bridge.BraveFinishedSpan; + +class CompositeSpanHandler extends SpanHandler { + + private final List exporters; + + CompositeSpanHandler(List exporters) { + this.exporters = exporters == null ? Collections.emptyList() : exporters; + } + + @Override + public boolean end(TraceContext context, MutableSpan span, Cause cause) { + if (cause != Cause.FINISHED) { + return true; + } + boolean shouldProcess = shouldProcess(span); + if (!shouldProcess) { + return false; + } + return super.end(context, span, cause); + } + + private boolean shouldProcess(MutableSpan span) { + for (SpanFilter exporter : this.exporters) { + if (!exporter.isExportable(BraveFinishedSpan.fromBrave(span))) { + return false; + } + } + return true; + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/autoconfig/SleuthProperties.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/autoconfig/SleuthProperties.java new file mode 100644 index 000000000..5c3c6f3c5 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/autoconfig/SleuthProperties.java @@ -0,0 +1,65 @@ +/* + * Copyright 2013-2020 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.brave.autoconfig; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Sleuth settings. + * + * @author Marcin Grzejszczak + * @since 1.0.11 + */ +@ConfigurationProperties("spring.sleuth") +class SleuthProperties { + + private boolean enabled = true; + + /** When true, generate 128-bit trace IDs instead of 64-bit ones. */ + private boolean traceId128 = false; + + /** + * True means the tracing system supports sharing a span ID between a client and + * server. + */ + private boolean supportsJoin = true; + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isTraceId128() { + return this.traceId128; + } + + public void setTraceId128(boolean traceId128) { + this.traceId128 = traceId128; + } + + public boolean isSupportsJoin() { + return this.supportsJoin; + } + + public void setSupportsJoin(boolean supportsJoin) { + this.supportsJoin = supportsJoin; + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfiguration.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/autoconfig/TraceBaggageConfiguration.java similarity index 93% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfiguration.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/autoconfig/TraceBaggageConfiguration.java index 79410e164..8ba1c94bf 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfiguration.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/autoconfig/TraceBaggageConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.autoconfig; +package org.springframework.cloud.sleuth.brave.autoconfig; import java.util.ArrayList; import java.util.List; @@ -48,6 +48,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties; +import org.springframework.cloud.sleuth.brave.propagation.PropagationFactorySupplier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @@ -62,7 +64,7 @@ import org.springframework.lang.Nullable; * @since 2.0.0 */ @Configuration(proxyBeanMethods = false) -@EnableConfigurationProperties({ SleuthProperties.class, SleuthBaggageProperties.class }) +@EnableConfigurationProperties(SleuthBaggageProperties.class) class TraceBaggageConfiguration { static final Log logger = LogFactory.getLog(TraceBaggageConfiguration.class); @@ -73,11 +75,6 @@ class TraceBaggageConfiguration { static final String WHITELISTED_KEYS = "spring.sleuth.propagation.tag.whitelisted-keys"; static final String WHITELISTED_MDC_KEYS = "spring.sleuth.log.slf4j.whitelisted-mdc-keys"; - // Note: Versions <2.2.3 use injectFormat(MULTI) for non-remote (ex spring-messaging) - // See #1643 - static final Propagation.Factory B3_FACTORY = B3Propagation.newFactoryBuilder() - .injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build(); - // These List beans allow us to get deprecated property values, regardless of // if they were comma or yaml encoded. This keeps them out of SleuthBaggageProperties @@ -105,6 +102,14 @@ class TraceBaggageConfiguration { return new ArrayList<>(); } + // Note: Versions <2.2.3 use injectFormat(MULTI) for non-remote (ex spring-messaging) + // See #1643 + @Bean + @ConditionalOnMissingBean + PropagationFactorySupplier defaultPropagationFactorySupplier() { + return () -> B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build(); + } + /** * To override the underlying context format, override this bean and set the delegate * to what you need. {@link BaggagePropagation.FactoryBuilder} will unwrap itself if @@ -116,8 +121,8 @@ class TraceBaggageConfiguration { */ @Bean @ConditionalOnMissingBean - BaggagePropagation.FactoryBuilder baggagePropagationFactoryBuilder() { - return BaggagePropagation.newFactoryBuilder(B3_FACTORY); + BaggagePropagation.FactoryBuilder baggagePropagationFactoryBuilder(PropagationFactorySupplier supplier) { + return BaggagePropagation.newFactoryBuilder(supplier.get()); } @Bean @@ -209,7 +214,7 @@ class TraceBaggageConfiguration { * *

* {@link SpanHandler} beans, even if {@link SpanHandler#NOOP}, can trigger - * {@code org.springframework.cloud.sleuth.sampler.SamplerCondition} + * {@code org.springframework.cloud.sleuth.brave.sampler.SamplerCondition} */ @Configuration(proxyBeanMethods = false) @Conditional(BaggageTagSpanHandlerCondition.class) diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/autoconfig/TraceBraveAutoConfiguration.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/autoconfig/TraceBraveAutoConfiguration.java new file mode 100644 index 000000000..ba798c649 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/autoconfig/TraceBraveAutoConfiguration.java @@ -0,0 +1,159 @@ +/* + * Copyright 2013-2020 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.brave.autoconfig; + +import java.util.Collections; +import java.util.List; + +import brave.CurrentSpanCustomizer; +import brave.Tracer; +import brave.Tracing; +import brave.TracingCustomizer; +import brave.handler.SpanHandler; +import brave.propagation.CurrentTraceContext; +import brave.propagation.CurrentTraceContextCustomizer; +import brave.propagation.Propagation; +import brave.propagation.ThreadLocalCurrentTraceContext; +import brave.sampler.Sampler; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.api.exporter.SpanFilter; +import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.cloud.sleuth.brave.LocalServiceName; +import org.springframework.cloud.sleuth.brave.propagation.TraceBravePropagationAutoConfiguration; +import org.springframework.cloud.sleuth.brave.sampler.SamplerAutoConfiguration; +import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.lang.Nullable; +import org.springframework.util.StringUtils; + +/** + * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration + * Auto-configuration} to enable tracing via Spring Cloud Sleuth with Brave. + * + * @author Spencer Gibb + * @author Marcin Grzejszczak + * @author Tim Ysewyn + * @since 3.0.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(value = "spring.sleuth.enabled", matchIfMissing = true) +@EnableConfigurationProperties(SleuthProperties.class) +@Import({ TraceBaggageConfiguration.class, SamplerAutoConfiguration.class }) +@AutoConfigureBefore(TraceAutoConfiguration.class) +@AutoConfigureAfter(TraceBravePropagationAutoConfiguration.class) +// public allows @AutoConfigureAfter(TraceAutoConfiguration) +// for components needing Tracing +public class TraceBraveAutoConfiguration { + + /** + * Tracing bean name. Name of the bean matters for some instrumentations. + */ + public static final String TRACING_BEAN_NAME = "tracing"; + + /** + * Tracer bean name. Name of the bean matters for some instrumentations. + */ + public static final String TRACER_BEAN_NAME = "tracer"; + + /** + * Default value used for service name if none provided. + */ + public static final String DEFAULT_SERVICE_NAME = "default"; + + @Bean(name = TRACING_BEAN_NAME) + @ConditionalOnMissingBean + // NOTE: stable bean name as might be used outside sleuth + Tracing tracing(@LocalServiceName String serviceName, Propagation.Factory factory, + CurrentTraceContext currentTraceContext, Sampler sampler, SleuthProperties sleuthProperties, + @Nullable List spanHandlers, @Nullable List tracingCustomizers) { + Tracing.Builder builder = Tracing.newBuilder().sampler(sampler) + .localServiceName(StringUtils.isEmpty(serviceName) ? DEFAULT_SERVICE_NAME : serviceName) + .propagationFactory(factory).currentTraceContext(currentTraceContext) + .traceId128Bit(sleuthProperties.isTraceId128()).supportsJoin(sleuthProperties.isSupportsJoin()); + if (spanHandlers != null) { + for (SpanHandler spanHandlerFactory : spanHandlers) { + builder.addSpanHandler(spanHandlerFactory); + } + } + if (tracingCustomizers != null) { + for (TracingCustomizer customizer : tracingCustomizers) { + customizer.customize(builder); + } + } + + return builder.build(); + } + + @Bean(name = TRACER_BEAN_NAME) + @ConditionalOnMissingBean + Tracer tracer(Tracing tracing) { + return tracing.tracer(); + } + + @Bean + @ConditionalOnMissingBean + SpanNamer sleuthSpanNamer() { + return new DefaultSpanNamer(); + } + + @Bean + CurrentTraceContext sleuthCurrentTraceContext(CurrentTraceContext.Builder builder, + @Nullable List scopeDecorators, + @Nullable List currentTraceContextCustomizers) { + if (scopeDecorators == null) { + scopeDecorators = Collections.emptyList(); + } + if (currentTraceContextCustomizers == null) { + currentTraceContextCustomizers = Collections.emptyList(); + } + + for (CurrentTraceContext.ScopeDecorator scopeDecorator : scopeDecorators) { + builder.addScopeDecorator(scopeDecorator); + } + for (CurrentTraceContextCustomizer customizer : currentTraceContextCustomizers) { + customizer.customize(builder); + } + return builder.build(); + } + + @Bean + @ConditionalOnMissingBean + CurrentTraceContext.Builder sleuthCurrentTraceContextBuilder() { + return ThreadLocalCurrentTraceContext.newBuilder(); + } + + @Bean + @ConditionalOnMissingBean + // NOTE: stable bean name as might be used outside sleuth + CurrentSpanCustomizer spanCustomizer(Tracing tracing) { + return CurrentSpanCustomizer.create(tracing); + } + + @Bean + SpanHandler compositeSpanHandler(@Nullable List exporters) { + return new CompositeSpanHandler(exporters); + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveBaggageEntry.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveBaggageEntry.java new file mode 100644 index 000000000..7ef0e7e6e --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveBaggageEntry.java @@ -0,0 +1,63 @@ +/* + * Copyright 2013-2020 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.brave.bridge; + +import brave.baggage.BaggageField; + +import org.springframework.cloud.sleuth.api.BaggageEntry; +import org.springframework.cloud.sleuth.api.TraceContext; + +/** + * Brave implementation of a {@link BaggageEntry}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BraveBaggageEntry implements BaggageEntry { + + private final BaggageField delegate; + + public BraveBaggageEntry(BaggageField delegate) { + this.delegate = delegate; + } + + @Override + public String name() { + return this.delegate.name(); + } + + @Override + public String get() { + return this.delegate.getValue(); + } + + @Override + public String get(TraceContext traceContext) { + return this.delegate.getValue(BraveTraceContext.toBrave(traceContext)); + } + + @Override + public void set(String value) { + this.delegate.updateValue(value); + } + + @Override + public void set(TraceContext traceContext, String value) { + this.delegate.updateValue(BraveTraceContext.toBrave(traceContext), value); + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveBaggageManager.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveBaggageManager.java new file mode 100644 index 000000000..4e52c4083 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveBaggageManager.java @@ -0,0 +1,58 @@ +/* + * Copyright 2013-2020 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.brave.bridge; + +import java.io.Closeable; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import brave.baggage.BaggageField; + +import org.springframework.cloud.sleuth.api.BaggageEntry; +import org.springframework.cloud.sleuth.api.BaggageManager; + +/** + * Brave implementation of a {@link BaggageManager}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BraveBaggageManager implements BaggageManager, Closeable { + + private static Map CACHE = new ConcurrentHashMap<>(); + + @Override + public Map getAllBaggage() { + return BaggageField.getAllValues(); + } + + @Override + public BaggageEntry getBaggage(String name) { + return createBaggage(name); + } + + @Override + public BaggageEntry createBaggage(String name) { + return CACHE.computeIfAbsent(name, s -> new BraveBaggageEntry(BaggageField.create(s))); + } + + @Override + public void close() { + CACHE.clear(); + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveCurrentTraceContext.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveCurrentTraceContext.java new file mode 100644 index 000000000..98791f480 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveCurrentTraceContext.java @@ -0,0 +1,78 @@ +/* + * Copyright 2013-2020 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.brave.bridge; + +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.TraceContext; + +/** + * Brave implementation of a {@link CurrentTraceContext}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BraveCurrentTraceContext implements CurrentTraceContext { + + final brave.propagation.CurrentTraceContext delegate; + + public BraveCurrentTraceContext(brave.propagation.CurrentTraceContext delegate) { + this.delegate = delegate; + } + + @Override + public TraceContext get() { + brave.propagation.TraceContext context = this.delegate.get(); + if (context == null) { + return null; + } + return new BraveTraceContext(context); + } + + @Override + public Scope newScope(TraceContext context) { + return new BraveScope(this.delegate.newScope(BraveTraceContext.toBrave(context))); + } + + @Override + public Scope maybeScope(TraceContext context) { + return new BraveScope(this.delegate.maybeScope(BraveTraceContext.toBrave(context))); + } + + public static brave.propagation.CurrentTraceContext toBrave(CurrentTraceContext context) { + return ((BraveCurrentTraceContext) context).delegate; + } + + public static CurrentTraceContext fromBrave(brave.propagation.CurrentTraceContext context) { + return new BraveCurrentTraceContext(context); + } + +} + +class BraveScope implements CurrentTraceContext.Scope { + + private final brave.propagation.CurrentTraceContext.Scope delegate; + + BraveScope(brave.propagation.CurrentTraceContext.Scope delegate) { + this.delegate = delegate; + } + + @Override + public void close() { + this.delegate.close(); + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveFinishedSpan.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveFinishedSpan.java new file mode 100644 index 000000000..2e63221e1 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveFinishedSpan.java @@ -0,0 +1,118 @@ +/* + * Copyright 2013-2020 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.brave.bridge; + +import java.util.Collection; +import java.util.Map; + +import brave.handler.MutableSpan; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; + +/** + * Brave implementation of a {@link FinishedSpan}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BraveFinishedSpan implements FinishedSpan { + + private final MutableSpan mutableSpan; + + public BraveFinishedSpan(MutableSpan mutableSpan) { + this.mutableSpan = mutableSpan; + } + + @Override + public String name() { + return this.mutableSpan.name(); + } + + @Override + public long startTimestamp() { + return this.mutableSpan.startTimestamp(); + } + + @Override + public long endTimestamp() { + return this.mutableSpan.finishTimestamp(); + } + + @Override + public Map tags() { + return this.mutableSpan.tags(); + } + + @Override + public Collection> events() { + return this.mutableSpan.annotations(); + } + + @Override + public String spanId() { + return this.mutableSpan.id(); + } + + @Override + public String parentId() { + return this.mutableSpan.parentId(); + } + + @Override + public String remoteIp() { + return this.mutableSpan.remoteIp(); + } + + @Override + public int remotePort() { + return this.mutableSpan.remotePort(); + } + + @Override + public String traceId() { + return this.mutableSpan.traceId(); + } + + @Override + public Throwable error() { + return this.mutableSpan.error(); + } + + @Override + public Span.Kind kind() { + if (this.mutableSpan.kind() == null) { + return null; + } + return Span.Kind.valueOf(this.mutableSpan.kind().name()); + } + + @Override + public String remoteServiceName() { + return this.mutableSpan.remoteServiceName(); + } + + public static FinishedSpan fromBrave(MutableSpan mutableSpan) { + return new BraveFinishedSpan(mutableSpan); + } + + @Override + public String toString() { + return "BraveFinishedSpan{" + "mutableSpan=" + mutableSpan + '}'; + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BravePropagator.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BravePropagator.java new file mode 100644 index 000000000..3fe419f9b --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BravePropagator.java @@ -0,0 +1,63 @@ +/* + * Copyright 2013-2020 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.brave.bridge; + +import java.util.List; + +import brave.Tracing; +import brave.propagation.SamplingFlags; +import brave.propagation.TraceContextOrSamplingFlags; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.propagation.Propagator; + +/** + * Brave implementation of a {@link Propagator}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BravePropagator implements Propagator { + + private final Tracing tracing; + + public BravePropagator(Tracing tracing) { + this.tracing = tracing; + } + + @Override + public List fields() { + return this.tracing.propagation().keys(); + } + + @Override + public void inject(TraceContext traceContext, C carrier, Setter setter) { + this.tracing.propagation().injector(setter::set).inject(BraveTraceContext.toBrave(traceContext), carrier); + } + + @Override + public Span.Builder extract(C carrier, Getter getter) { + TraceContextOrSamplingFlags extract = this.tracing.propagation().extractor(getter::get).extract(carrier); + if (extract.samplingFlags() == SamplingFlags.EMPTY) { + this.tracing.tracer().nextSpan(); + return new BraveSpanBuilder(this.tracing.tracer()); + } + return BraveSpanBuilder.toBuilder(this.tracing.tracer(), extract); + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveSamplerFunction.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveSamplerFunction.java new file mode 100644 index 000000000..ea89f7dba --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveSamplerFunction.java @@ -0,0 +1,58 @@ +/* + * Copyright 2013-2020 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.brave.bridge; + +import brave.sampler.SamplerFunctions; + +import org.springframework.cloud.sleuth.api.SamplerFunction; +import org.springframework.cloud.sleuth.api.http.HttpRequest; +import org.springframework.cloud.sleuth.brave.bridge.http.BraveHttpRequest; + +/** + * Brave implementation of a {@link SamplerFunction}. + * + * @param type of the input, for example a request or method + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BraveSamplerFunction implements SamplerFunction { + + final brave.sampler.SamplerFunction samplerFunction; + + public BraveSamplerFunction(brave.sampler.SamplerFunction samplerFunction) { + this.samplerFunction = samplerFunction; + } + + @Override + public Boolean trySample(T arg) { + return this.samplerFunction.trySample(arg); + } + + public static brave.sampler.SamplerFunction toBrave(SamplerFunction samplerFunction, + Class sleuthInput, Class braveInput) { + if (sleuthInput.equals(HttpRequest.class) && braveInput.equals(brave.http.HttpRequest.class)) { + return arg -> samplerFunction.trySample((T) BraveHttpRequest.fromBrave((brave.http.HttpRequest) arg)); + } + return SamplerFunctions.deferDecision(); + } + + public static brave.sampler.SamplerFunction toHttpBrave( + SamplerFunction samplerFunction) { + return arg -> samplerFunction.trySample(BraveHttpRequest.fromBrave((brave.http.HttpRequest) arg)); + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveScopedSpan.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveScopedSpan.java new file mode 100644 index 000000000..7e8d376c2 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveScopedSpan.java @@ -0,0 +1,71 @@ +/* + * Copyright 2013-2020 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.brave.bridge; + +import org.springframework.cloud.sleuth.api.ScopedSpan; +import org.springframework.cloud.sleuth.api.TraceContext; + +/** + * Brave implementation of a {@link ScopedSpan}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BraveScopedSpan implements ScopedSpan { + + final brave.ScopedSpan span; + + public BraveScopedSpan(brave.ScopedSpan span) { + this.span = span; + } + + @Override + public boolean isNoop() { + return this.span.isNoop(); + } + + @Override + public TraceContext context() { + return new BraveTraceContext(this.span.context()); + } + + @Override + public ScopedSpan name(String name) { + return new BraveScopedSpan(this.span.name(name)); + } + + @Override + public ScopedSpan tag(String key, String value) { + return new BraveScopedSpan(this.span.tag(key, value)); + } + + @Override + public ScopedSpan event(String value) { + return new BraveScopedSpan(this.span.annotate(value)); + } + + @Override + public ScopedSpan error(Throwable throwable) { + return new BraveScopedSpan(this.span.error(throwable)); + } + + @Override + public void end() { + this.span.finish(); + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveSpan.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveSpan.java new file mode 100644 index 000000000..afbc32fac --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveSpan.java @@ -0,0 +1,100 @@ +/* + * Copyright 2013-2020 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.brave.bridge; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; + +/** + * Brave implementation of a {@link Span}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BraveSpan implements Span { + + final brave.Span delegate; + + public BraveSpan(brave.Span delegate) { + this.delegate = delegate; + } + + @Override + public boolean isNoop() { + return this.delegate.isNoop(); + } + + @Override + public TraceContext context() { + if (this.delegate == null) { + return null; + } + return new BraveTraceContext(this.delegate.context()); + } + + @Override + public Span start() { + return new BraveSpan(this.delegate.start()); + } + + @Override + public Span name(String name) { + return new BraveSpan(this.delegate.name(name)); + } + + @Override + public Span event(String value) { + return new BraveSpan(this.delegate.annotate(value)); + } + + @Override + public Span tag(String key, String value) { + return new BraveSpan(this.delegate.tag(key, value)); + } + + @Override + public Span error(Throwable throwable) { + String message = throwable.getMessage() == null ? throwable.getClass().getSimpleName() : throwable.getMessage(); + this.delegate.tag("error", message); + this.delegate.error(throwable); + return new BraveSpan(this.delegate); + } + + @Override + public void end() { + this.delegate.finish(); + } + + @Override + public void abandon() { + this.delegate.abandon(); + } + + @Override + public String toString() { + return this.delegate != null ? this.delegate.toString() : "null"; + } + + public static brave.Span toBrave(Span span) { + return ((BraveSpan) span).delegate; + } + + public static Span fromBrave(brave.Span span) { + return new BraveSpan(span); + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveSpanBuilder.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveSpanBuilder.java new file mode 100644 index 000000000..ad00901b4 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveSpanBuilder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2013-2020 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.brave.bridge; + +import brave.Tracer; +import brave.propagation.TraceContextOrSamplingFlags; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; + +/** + * Brave implementation of a {@link Span.Builder}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BraveSpanBuilder implements Span.Builder { + + brave.Span delegate; + + TraceContextOrSamplingFlags parentContext; + + private final Tracer tracer; + + private long startTimestamp; + + public BraveSpanBuilder(Tracer tracer) { + this.tracer = tracer; + } + + public BraveSpanBuilder(Tracer tracer, TraceContextOrSamplingFlags parentContext) { + this.tracer = tracer; + this.parentContext = parentContext; + } + + brave.Span span() { + if (this.delegate != null) { + return this.delegate; + } + else if (this.parentContext != null) { + this.delegate = this.tracer.nextSpan(this.parentContext); + } + else { + this.delegate = this.tracer.nextSpan(); + } + return this.delegate; + } + + @Override + public Span.Builder setParent(TraceContext context) { + this.parentContext = TraceContextOrSamplingFlags.create(BraveTraceContext.toBrave(context)); + return this; + } + + @Override + public Span.Builder setNoParent() { + return this; + } + + @Override + public Span.Builder name(String name) { + span().name(name); + return this; + } + + @Override + public Span.Builder event(String value) { + span().annotate(value); + return this; + } + + @Override + public Span.Builder tag(String key, String value) { + span().tag(key, value); + return this; + } + + @Override + public Span.Builder error(Throwable throwable) { + span().error(throwable); + return this; + } + + @Override + public Span.Builder kind(Span.Kind kind) { + span().kind(kind != null ? brave.Span.Kind.valueOf(kind.toString()) : null); + return this; + } + + @Override + public Span.Builder remoteServiceName(String remoteServiceName) { + span().remoteServiceName(remoteServiceName); + return this; + } + + @Override + public Span start() { + if (this.startTimestamp > 0) { + span().start(this.startTimestamp); + } + else { + span().start(); + } + return BraveSpan.fromBrave(this.delegate); + } + + public static Span.Builder toBuilder(Tracer tracer, TraceContextOrSamplingFlags context) { + return new BraveSpanBuilder(tracer, context); + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveSpanCustomizer.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveSpanCustomizer.java new file mode 100644 index 000000000..714aa0a79 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveSpanCustomizer.java @@ -0,0 +1,58 @@ +/* + * Copyright 2013-2020 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.brave.bridge; + +import org.springframework.cloud.sleuth.api.SpanCustomizer; + +/** + * Brave implementation of a {@link SpanCustomizer}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BraveSpanCustomizer implements SpanCustomizer { + + private final brave.SpanCustomizer spanCustomizer; + + public BraveSpanCustomizer(brave.SpanCustomizer spanCustomizer) { + this.spanCustomizer = spanCustomizer; + } + + @Override + public SpanCustomizer name(String name) { + return new BraveSpanCustomizer(this.spanCustomizer.name(name)); + } + + @Override + public SpanCustomizer tag(String key, String value) { + return new BraveSpanCustomizer(this.spanCustomizer.tag(key, value)); + } + + @Override + public SpanCustomizer event(String value) { + return new BraveSpanCustomizer(this.spanCustomizer.annotate(value)); + } + + public static brave.SpanCustomizer toBrave(SpanCustomizer spanCustomizer) { + return ((BraveSpanCustomizer) spanCustomizer).spanCustomizer; + } + + public static SpanCustomizer fromBrave(brave.SpanCustomizer spanCustomizer) { + return new BraveSpanCustomizer(spanCustomizer); + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveTraceContext.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveTraceContext.java new file mode 100644 index 000000000..e2b609008 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveTraceContext.java @@ -0,0 +1,85 @@ +/* + * Copyright 2013-2020 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.brave.bridge; + +import java.util.Objects; + +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.lang.Nullable; + +/** + * Brave implementation of a {@link TraceContext}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BraveTraceContext implements TraceContext { + + final brave.propagation.TraceContext traceContext; + + public BraveTraceContext(brave.propagation.TraceContext traceContext) { + this.traceContext = traceContext; + } + + @Override + public String traceId() { + return this.traceContext.traceIdString(); + } + + @Override + @Nullable + public String parentId() { + return this.traceContext.parentIdString(); + } + + @Override + public String spanId() { + return this.traceContext.spanIdString(); + } + + @Override + public String toString() { + return this.traceContext != null ? this.traceContext.toString() : "null"; + } + + @Override + public boolean equals(Object o) { + return Objects.equals(this.traceContext, o); + } + + @Override + public int hashCode() { + return Objects.hashCode(this.traceContext); + } + + @Nullable + public Boolean sampled() { + return this.traceContext.sampled(); + } + + public static brave.propagation.TraceContext toBrave(TraceContext traceContext) { + if (traceContext == null) { + return null; + } + return ((BraveTraceContext) traceContext).traceContext; + } + + public static TraceContext fromBrave(brave.propagation.TraceContext traceContext) { + return new BraveTraceContext(traceContext); + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveTracer.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveTracer.java new file mode 100644 index 000000000..34444b4b8 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveTracer.java @@ -0,0 +1,125 @@ +/* + * Copyright 2013-2020 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.brave.bridge; + +import java.util.Map; + +import brave.propagation.TraceContextOrSamplingFlags; + +import org.springframework.cloud.sleuth.api.BaggageEntry; +import org.springframework.cloud.sleuth.api.ScopedSpan; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.SpanCustomizer; +import org.springframework.cloud.sleuth.api.Tracer; + +/** + * Brave implementation of a {@link Tracer}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BraveTracer implements Tracer { + + private final brave.Tracer tracer; + + private final BraveBaggageManager braveBaggageManager = new BraveBaggageManager(); + + public BraveTracer(brave.Tracer tracer) { + this.tracer = tracer; + } + + @Override + public Span nextSpan(Span parent) { + if (parent == null) { + return nextSpan(); + } + brave.propagation.TraceContext context = (((BraveTraceContext) parent.context()).traceContext); + if (context == null) { + return null; + } + return new BraveSpan(this.tracer.nextSpan(TraceContextOrSamplingFlags.create(context))); + } + + @Override + public SpanInScope withSpan(Span span) { + return new BraveSpanInScope(tracer.withSpanInScope(span == null ? null : ((BraveSpan) span).delegate)); + } + + @Override + public SpanCustomizer currentSpanCustomizer() { + return new BraveSpanCustomizer(this.tracer.currentSpanCustomizer()); + } + + @Override + public Span currentSpan() { + brave.Span currentSpan = this.tracer.currentSpan(); + if (currentSpan == null) { + return null; + } + return new BraveSpan(currentSpan); + } + + @Override + public Span nextSpan() { + return new BraveSpan(this.tracer.nextSpan()); + } + + @Override + public ScopedSpan startScopedSpan(String name) { + return new BraveScopedSpan(this.tracer.startScopedSpan(name)); + } + + @Override + public Span.Builder spanBuilder() { + return new BraveSpanBuilder(this.tracer); + } + + public static Tracer fromBrave(brave.Tracer tracer) { + return new BraveTracer(tracer); + } + + @Override + public Map getAllBaggage() { + return this.braveBaggageManager.getAllBaggage(); + } + + @Override + public BaggageEntry getBaggage(String name) { + return this.braveBaggageManager.getBaggage(name); + } + + @Override + public BaggageEntry createBaggage(String name) { + return this.braveBaggageManager.createBaggage(name); + } + +} + +class BraveSpanInScope implements Tracer.SpanInScope { + + final brave.Tracer.SpanInScope delegate; + + BraveSpanInScope(brave.Tracer.SpanInScope delegate) { + this.delegate = delegate; + } + + @Override + public void close() { + this.delegate.close(); + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/TraceBraveBridgeAutoConfiguation.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/TraceBraveBridgeAutoConfiguation.java new file mode 100644 index 000000000..cb8fa4477 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/TraceBraveBridgeAutoConfiguation.java @@ -0,0 +1,70 @@ +/* + * Copyright 2013-2020 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.brave.bridge; + +import brave.Tracing; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.SpanCustomizer; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.propagation.Propagator; +import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.cloud.sleuth.brave.autoconfig.TraceBraveAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration + * Auto-configuration} to enable the bridge between Sleuth API and Brave. + * + * @author Spencer Gibb + * @author Marcin Grzejszczak + * @author Tim Ysewyn + * @since 3.0.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(value = "spring.sleuth.enabled", matchIfMissing = true) +@ConditionalOnBean(brave.Tracer.class) +@AutoConfigureAfter(TraceBraveAutoConfiguration.class) +@AutoConfigureBefore(TraceAutoConfiguration.class) +public class TraceBraveBridgeAutoConfiguation { + + @Bean + Tracer braveTracer(brave.Tracer tracer) { + return new BraveTracer(tracer); + } + + @Bean + CurrentTraceContext braveCurrentTraceContext(brave.propagation.CurrentTraceContext currentTraceContext) { + return new BraveCurrentTraceContext(currentTraceContext); + } + + @Bean + SpanCustomizer braveSpanCustomizer(brave.SpanCustomizer spanCustomizer) { + return new BraveSpanCustomizer(spanCustomizer); + } + + @Bean + Propagator bravePropagator(Tracing tracing) { + return new BravePropagator(tracing); + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpClientHandler.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpClientHandler.java new file mode 100644 index 000000000..7bb29904f --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpClientHandler.java @@ -0,0 +1,67 @@ +/* + * Copyright 2013-2020 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.brave.bridge.http; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; +import org.springframework.cloud.sleuth.api.http.HttpClientRequest; +import org.springframework.cloud.sleuth.api.http.HttpClientResponse; +import org.springframework.cloud.sleuth.brave.bridge.BraveSpan; +import org.springframework.cloud.sleuth.brave.bridge.BraveTraceContext; + +/** + * Brave implementation of a {@link HttpClientHandler}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BraveHttpClientHandler implements HttpClientHandler { + + final brave.http.HttpClientHandler delegate; + + public BraveHttpClientHandler( + brave.http.HttpClientHandler delegate) { + this.delegate = delegate; + } + + @Override + public Span handleSend(HttpClientRequest request) { + return BraveSpan.fromBrave(this.delegate.handleSend(BraveHttpClientRequest.toBrave(request))); + } + + @Override + public Span handleSend(HttpClientRequest request, TraceContext parent) { + brave.Span span = this.delegate.handleSendWithParent(BraveHttpClientRequest.toBrave(request), + BraveTraceContext.toBrave(parent)); + if (!span.isNoop()) { + span.remoteIpAndPort(request.remoteIp(), request.remotePort()); + } + return BraveSpan.fromBrave(span); + } + + @Override + public void handleReceive(HttpClientResponse response, Span span) { + this.delegate.handleReceive(BraveHttpClientResponse.toBrave(response), BraveSpan.toBrave(span)); + } + + public static HttpClientHandler fromBrave( + brave.http.HttpClientHandler handler) { + return new BraveHttpClientHandler(handler); + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpClientRequest.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpClientRequest.java new file mode 100644 index 000000000..9dc4744ec --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpClientRequest.java @@ -0,0 +1,114 @@ +/* + * Copyright 2013-2020 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.brave.bridge.http; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.http.HttpClientRequest; + +/** + * Brave implementation of a {@link HttpClientRequest}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BraveHttpClientRequest implements HttpClientRequest { + + final brave.http.HttpClientRequest delegate; + + public BraveHttpClientRequest(brave.http.HttpClientRequest delegate) { + this.delegate = delegate; + } + + @Override + public String method() { + return this.delegate.method(); + } + + @Override + public String route() { + return this.delegate.route(); + } + + @Override + public Object unwrap() { + return this.delegate.unwrap(); + } + + @Override + public Span.Kind spanKind() { + return Span.Kind.valueOf(this.delegate.spanKind().name()); + } + + @Override + public void header(String name, String value) { + this.delegate.header(name, value); + } + + @Override + public String path() { + return this.delegate.path(); + } + + @Override + public String url() { + return this.delegate.url(); + } + + @Override + public String header(String name) { + return this.delegate.header(name); + } + + public static brave.http.HttpClientRequest toBrave(HttpClientRequest httpClientRequest) { + if (httpClientRequest instanceof BraveHttpClientRequest) { + return ((BraveHttpClientRequest) httpClientRequest).delegate; + } + return new brave.http.HttpClientRequest() { + + @Override + public Object unwrap() { + return httpClientRequest.unwrap(); + } + + @Override + public String method() { + return httpClientRequest.method(); + } + + @Override + public String path() { + return httpClientRequest.path(); + } + + @Override + public String url() { + return httpClientRequest.url(); + } + + @Override + public String header(String name) { + return httpClientRequest.header(name); + } + + @Override + public void header(String name, String value) { + httpClientRequest.header(name, value); + } + }; + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpClientResponse.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpClientResponse.java new file mode 100644 index 000000000..218a1e066 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpClientResponse.java @@ -0,0 +1,116 @@ +/* + * Copyright 2013-2020 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.brave.bridge.http; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.http.HttpClientRequest; +import org.springframework.cloud.sleuth.api.http.HttpClientResponse; + +/** + * Brave implementation of a {@link HttpClientResponse}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BraveHttpClientResponse implements HttpClientResponse { + + final brave.http.HttpClientResponse delegate; + + public BraveHttpClientResponse(brave.http.HttpClientResponse delegate) { + this.delegate = delegate; + } + + @Override + public String method() { + return this.delegate.method(); + } + + @Override + public String route() { + return this.delegate.route(); + } + + @Override + public int statusCode() { + return this.delegate.statusCode(); + } + + @Override + public Object unwrap() { + return this.delegate.unwrap(); + } + + @Override + public Span.Kind spanKind() { + return Span.Kind.valueOf(this.delegate.spanKind().name()); + } + + @Override + public HttpClientRequest request() { + brave.http.HttpClientRequest request = this.delegate.request(); + if (request == null) { + return null; + } + return new BraveHttpClientRequest(request); + } + + @Override + public Throwable error() { + return this.delegate.error(); + } + + public static brave.http.HttpClientResponse toBrave(HttpClientResponse httpClientResponse) { + if (httpClientResponse == null) { + return null; + } + else if (httpClientResponse instanceof BraveHttpClientResponse) { + return ((BraveHttpClientResponse) httpClientResponse).delegate; + } + return new brave.http.HttpClientResponse() { + @Override + public int statusCode() { + return httpClientResponse.statusCode(); + } + + @Override + public Object unwrap() { + return httpClientResponse.unwrap(); + } + + @Override + public brave.http.HttpClientRequest request() { + return BraveHttpClientRequest.toBrave(httpClientResponse.request()); + } + + @Override + public Throwable error() { + return httpClientResponse.error(); + } + + @Override + public String method() { + return httpClientResponse.method(); + } + + @Override + public String route() { + return httpClientResponse.route(); + } + }; + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpRequest.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpRequest.java new file mode 100644 index 000000000..460cdfd42 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpRequest.java @@ -0,0 +1,74 @@ +/* + * Copyright 2013-2020 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.brave.bridge.http; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.http.HttpRequest; + +/** + * Brave implementation of a {@link HttpRequest}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BraveHttpRequest implements HttpRequest { + + final brave.http.HttpRequest delegate; + + public BraveHttpRequest(brave.http.HttpRequest delegate) { + this.delegate = delegate; + } + + @Override + public String method() { + return this.delegate.method(); + } + + @Override + public String path() { + return this.delegate.path(); + } + + @Override + public String url() { + return this.delegate.url(); + } + + @Override + public String header(String name) { + return this.delegate.header(name); + } + + @Override + public Span.Kind spanKind() { + return Span.Kind.valueOf(this.delegate.spanKind().name()); + } + + @Override + public Object unwrap() { + return this.delegate.unwrap(); + } + + public static brave.http.HttpRequest toBrave(HttpRequest httpRequest) { + return ((BraveHttpRequest) httpRequest).delegate; + } + + public static HttpRequest fromBrave(brave.http.HttpRequest httpRequest) { + return new BraveHttpRequest(httpRequest); + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpRequestParser.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpRequestParser.java new file mode 100644 index 000000000..f1c3dd6b6 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpRequestParser.java @@ -0,0 +1,54 @@ +/* + * Copyright 2013-2020 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.brave.bridge.http; + +import org.springframework.cloud.sleuth.api.SpanCustomizer; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.http.HttpRequest; +import org.springframework.cloud.sleuth.api.http.HttpRequestParser; +import org.springframework.cloud.sleuth.brave.bridge.BraveSpanCustomizer; +import org.springframework.cloud.sleuth.brave.bridge.BraveTraceContext; + +/** + * Brave implementation of a {@link HttpRequestParser}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BraveHttpRequestParser implements HttpRequestParser { + + final brave.http.HttpRequestParser delegate; + + public BraveHttpRequestParser(brave.http.HttpRequestParser delegate) { + this.delegate = delegate; + } + + @Override + public void parse(HttpRequest request, TraceContext context, SpanCustomizer span) { + this.delegate.parse(BraveHttpRequest.toBrave(request), BraveTraceContext.toBrave(context), + BraveSpanCustomizer.toBrave(span)); + } + + public static brave.http.HttpRequestParser toBrave(HttpRequestParser parser) { + if (parser instanceof BraveHttpRequestParser) { + return ((BraveHttpRequestParser) parser).delegate; + } + return (request, context, span) -> parser.parse(BraveHttpRequest.fromBrave(request), + BraveTraceContext.fromBrave(context), BraveSpanCustomizer.fromBrave(span)); + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpResponse.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpResponse.java new file mode 100644 index 000000000..7522afb58 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpResponse.java @@ -0,0 +1,84 @@ +/* + * Copyright 2013-2020 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.brave.bridge.http; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.http.HttpRequest; +import org.springframework.cloud.sleuth.api.http.HttpResponse; + +/** + * Brave implementation of a {@link HttpResponse}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BraveHttpResponse implements HttpResponse { + + final brave.http.HttpResponse delegate; + + public BraveHttpResponse(brave.http.HttpResponse delegate) { + this.delegate = delegate; + } + + @Override + public String method() { + return this.delegate.method(); + } + + @Override + public String route() { + return this.delegate.route(); + } + + @Override + public int statusCode() { + return this.delegate.statusCode(); + } + + @Override + public Object unwrap() { + return this.delegate.unwrap(); + } + + @Override + public Span.Kind spanKind() { + return Span.Kind.valueOf(this.delegate.spanKind().name()); + } + + @Override + public HttpRequest request() { + brave.http.HttpRequest request = this.delegate.request(); + if (request == null) { + return null; + } + return new BraveHttpRequest(request); + } + + @Override + public Throwable error() { + return this.delegate.error(); + } + + public static brave.http.HttpResponse toBrave(HttpResponse httpResponse) { + return ((BraveHttpResponse) httpResponse).delegate; + } + + public static HttpResponse fromBrave(brave.http.HttpResponse httpResponse) { + return new BraveHttpResponse(httpResponse); + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpResponseParser.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpResponseParser.java new file mode 100644 index 000000000..29083e35b --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpResponseParser.java @@ -0,0 +1,54 @@ +/* + * Copyright 2013-2020 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.brave.bridge.http; + +import org.springframework.cloud.sleuth.api.SpanCustomizer; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.http.HttpResponse; +import org.springframework.cloud.sleuth.api.http.HttpResponseParser; +import org.springframework.cloud.sleuth.brave.bridge.BraveSpanCustomizer; +import org.springframework.cloud.sleuth.brave.bridge.BraveTraceContext; + +/** + * Brave implementation of a {@link HttpResponseParser}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BraveHttpResponseParser implements HttpResponseParser { + + final brave.http.HttpResponseParser delegate; + + public BraveHttpResponseParser(brave.http.HttpResponseParser delegate) { + this.delegate = delegate; + } + + @Override + public void parse(HttpResponse response, TraceContext context, SpanCustomizer span) { + this.delegate.parse(BraveHttpResponse.toBrave(response), BraveTraceContext.toBrave(context), + BraveSpanCustomizer.toBrave(span)); + } + + public static brave.http.HttpResponseParser toBrave(HttpResponseParser parser) { + if (parser instanceof BraveHttpResponseParser) { + return ((BraveHttpResponseParser) parser).delegate; + } + return (response, context, span) -> parser.parse(BraveHttpResponse.fromBrave(response), + BraveTraceContext.fromBrave(context), BraveSpanCustomizer.fromBrave(span)); + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpServerHandler.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpServerHandler.java new file mode 100644 index 000000000..31295a3f5 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpServerHandler.java @@ -0,0 +1,50 @@ +/* + * Copyright 2013-2020 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.brave.bridge.http; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.http.HttpServerHandler; +import org.springframework.cloud.sleuth.api.http.HttpServerRequest; +import org.springframework.cloud.sleuth.api.http.HttpServerResponse; +import org.springframework.cloud.sleuth.brave.bridge.BraveSpan; + +/** + * Brave implementation of a {@link HttpServerHandler}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BraveHttpServerHandler implements HttpServerHandler { + + final brave.http.HttpServerHandler delegate; + + public BraveHttpServerHandler( + brave.http.HttpServerHandler delegate) { + this.delegate = delegate; + } + + @Override + public Span handleReceive(HttpServerRequest request) { + return BraveSpan.fromBrave(this.delegate.handleReceive(BraveHttpServerRequest.toBrave(request))); + } + + @Override + public void handleSend(HttpServerResponse response, Span span) { + this.delegate.handleSend(BraveHttpServerResponse.toBrave(response), BraveSpan.toBrave(span)); + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpServerRequest.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpServerRequest.java new file mode 100644 index 000000000..c8c6df245 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpServerRequest.java @@ -0,0 +1,132 @@ +/* + * Copyright 2013-2020 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.brave.bridge.http; + +import java.net.InetSocketAddress; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.http.HttpServerRequest; +import org.springframework.http.server.reactive.ServerHttpRequest; + +/** + * Brave implementation of a {@link HttpServerRequest}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BraveHttpServerRequest implements HttpServerRequest { + + final brave.http.HttpServerRequest delegate; + + public BraveHttpServerRequest(brave.http.HttpServerRequest delegate) { + this.delegate = delegate; + } + + @Override + public String method() { + return this.delegate.method(); + } + + @Override + public String route() { + return this.delegate.route(); + } + + @Override + public Object unwrap() { + return this.delegate.unwrap(); + } + + @Override + public Span.Kind spanKind() { + return Span.Kind.valueOf(this.delegate.spanKind().name()); + } + + @Override + public String path() { + return this.delegate.path(); + } + + @Override + public String url() { + return this.delegate.url(); + } + + @Override + public String header(String name) { + return this.delegate.header(name); + } + + public static brave.http.HttpServerRequest toBrave(HttpServerRequest request) { + if (request == null) { + return null; + } + if (request instanceof BraveHttpServerRequest) { + return ((BraveHttpServerRequest) request).delegate; + } + return new brave.http.HttpServerRequest() { + + @Override + public Object unwrap() { + return request.unwrap(); + } + + @Override + public String method() { + return request.method(); + } + + @Override + public String path() { + return request.path(); + } + + @Override + public String url() { + return request.url(); + } + + @Override + public String header(String name) { + return request.header(name); + } + + @Override + public boolean parseClientIpAndPort(brave.Span span) { + boolean clientIpAndPortParsed = super.parseClientIpAndPort(span); + if (clientIpAndPortParsed) { + return true; + } + return resolveFromInetAddress(span); + } + + private boolean resolveFromInetAddress(brave.Span span) { + Object delegate = request.unwrap(); + if (delegate instanceof ServerHttpRequest) { + InetSocketAddress addr = ((ServerHttpRequest) delegate).getRemoteAddress(); + if (addr == null) { + return false; + } + return span.remoteIpAndPort(addr.getAddress().getHostAddress(), addr.getPort()); + } + return false; + } + + }; + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpServerResponse.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpServerResponse.java new file mode 100644 index 000000000..75d8f9f32 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/BraveHttpServerResponse.java @@ -0,0 +1,121 @@ +/* + * Copyright 2013-2020 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.brave.bridge.http; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.http.HttpServerRequest; +import org.springframework.cloud.sleuth.api.http.HttpServerResponse; + +/** + * Brave implementation of a {@link HttpServerResponse}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class BraveHttpServerResponse implements HttpServerResponse { + + final brave.http.HttpServerResponse delegate; + + public BraveHttpServerResponse(brave.http.HttpServerResponse delegate) { + this.delegate = delegate; + } + + @Override + public String method() { + return this.delegate.method(); + } + + @Override + public String route() { + return this.delegate.route(); + } + + @Override + public int statusCode() { + return this.delegate.statusCode(); + } + + @Override + public Object unwrap() { + return this.delegate.unwrap(); + } + + @Override + public Span.Kind spanKind() { + return Span.Kind.valueOf(this.delegate.spanKind().name()); + } + + @Override + public HttpServerRequest request() { + brave.http.HttpServerRequest request = this.delegate.request(); + if (request == null) { + return null; + } + return new BraveHttpServerRequest(request); + } + + @Override + public Throwable error() { + return this.delegate.error(); + } + + public static brave.http.HttpServerResponse toBrave(HttpServerResponse response) { + if (response == null) { + return null; + } + else if (response instanceof BraveHttpServerResponse) { + return ((BraveHttpServerResponse) response).delegate; + } + return new brave.http.HttpServerResponse() { + @Override + public brave.http.HttpServerRequest request() { + return BraveHttpServerRequest.toBrave(response.request()); + } + + @Override + public Throwable error() { + return response.error(); + } + + @Override + public String method() { + return response.method(); + } + + @Override + public String route() { + return response.route(); + } + + @Override + public String toString() { + return response.toString(); + } + + @Override + public int statusCode() { + return response.statusCode(); + } + + @Override + public Object unwrap() { + return response.unwrap(); + } + }; + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/TraceBraveHttpBridgeAutoConfiguration.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/TraceBraveHttpBridgeAutoConfiguration.java new file mode 100644 index 000000000..0df762c60 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/http/TraceBraveHttpBridgeAutoConfiguration.java @@ -0,0 +1,57 @@ +/* + * Copyright 2013-2020 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.brave.bridge.http; + +import brave.http.HttpTracing; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; +import org.springframework.cloud.sleuth.api.http.HttpServerHandler; +import org.springframework.cloud.sleuth.brave.bridge.TraceBraveBridgeAutoConfiguation; +import org.springframework.cloud.sleuth.instrument.web.TraceHttpAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration + * Auto-configuration} to enable HTTP client and server handling. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(value = "spring.sleuth.enabled", matchIfMissing = true) +@ConditionalOnBean({ Tracer.class, HttpTracing.class }) +@AutoConfigureBefore(TraceHttpAutoConfiguration.class) +@AutoConfigureAfter(TraceBraveBridgeAutoConfiguation.class) +public class TraceBraveHttpBridgeAutoConfiguration { + + @Bean + HttpClientHandler braveHttpClientHandler(HttpTracing httpTracing) { + return new BraveHttpClientHandler(brave.http.HttpClientHandler.create(httpTracing)); + } + + @Bean + HttpServerHandler braveHttpServerHandler(HttpTracing httpTracing) { + return new BraveHttpServerHandler(brave.http.HttpServerHandler.create(httpTracing)); + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/grpc/GrpcManagedChannelBuilderCustomizer.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/grpc/GrpcManagedChannelBuilderCustomizer.java similarity index 94% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/grpc/GrpcManagedChannelBuilderCustomizer.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/grpc/GrpcManagedChannelBuilderCustomizer.java index 476b38d1a..f25e79a3a 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/grpc/GrpcManagedChannelBuilderCustomizer.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/grpc/GrpcManagedChannelBuilderCustomizer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.grpc; +package org.springframework.cloud.sleuth.brave.instrument.grpc; import io.grpc.ManagedChannelBuilder; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/grpc/SpringAwareManagedChannelBuilder.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/grpc/SpringAwareManagedChannelBuilder.java similarity index 97% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/grpc/SpringAwareManagedChannelBuilder.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/grpc/SpringAwareManagedChannelBuilder.java index 125303364..076aa8201 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/grpc/SpringAwareManagedChannelBuilder.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/grpc/SpringAwareManagedChannelBuilder.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.grpc; +package org.springframework.cloud.sleuth.brave.instrument.grpc; import java.util.List; import java.util.Optional; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/grpc/TraceGrpcAutoConfiguration.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/grpc/TraceGrpcAutoConfiguration.java similarity index 94% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/grpc/TraceGrpcAutoConfiguration.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/grpc/TraceGrpcAutoConfiguration.java index c1f5b5610..66369ac12 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/grpc/TraceGrpcAutoConfiguration.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/grpc/TraceGrpcAutoConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.grpc; +package org.springframework.cloud.sleuth.brave.instrument.grpc; import java.util.List; import java.util.Optional; @@ -29,7 +29,7 @@ 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.cloud.sleuth.brave.instrument.rpc.TraceRpcAutoConfiguration; import org.springframework.context.annotation.Bean; /** diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/grpc/TracingManagedChannelBuilderCustomizer.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/grpc/TracingManagedChannelBuilderCustomizer.java similarity index 94% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/grpc/TracingManagedChannelBuilderCustomizer.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/grpc/TracingManagedChannelBuilderCustomizer.java index 5e6438074..9a03d5892 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/grpc/TracingManagedChannelBuilderCustomizer.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/grpc/TracingManagedChannelBuilderCustomizer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.grpc; +package org.springframework.cloud.sleuth.brave.instrument.grpc; import brave.grpc.GrpcTracing; import io.grpc.ManagedChannelBuilder; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/ConsumerSampler.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/ConsumerSampler.java similarity index 95% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/ConsumerSampler.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/ConsumerSampler.java index 0b81bfb36..b86c7c3b0 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/ConsumerSampler.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/ConsumerSampler.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.messaging; +package org.springframework.cloud.sleuth.brave.instrument.messaging; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/OnMessagingEnabled.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/OnMessagingEnabled.java similarity index 94% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/OnMessagingEnabled.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/OnMessagingEnabled.java index 7b0aaf09b..bbd9b9305 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/OnMessagingEnabled.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/OnMessagingEnabled.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.messaging; +package org.springframework.cloud.sleuth.brave.instrument.messaging; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/ProducerSampler.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/ProducerSampler.java similarity index 95% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/ProducerSampler.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/ProducerSampler.java index 6d941fe06..c00948986 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/ProducerSampler.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/ProducerSampler.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.messaging; +package org.springframework.cloud.sleuth.brave.instrument.messaging; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/SleuthKafkaStreamsConfiguration.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/SleuthKafkaStreamsConfiguration.java similarity index 98% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/SleuthKafkaStreamsConfiguration.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/SleuthKafkaStreamsConfiguration.java index 70adfeeb6..1b2cf9010 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/SleuthKafkaStreamsConfiguration.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/SleuthKafkaStreamsConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.messaging; +package org.springframework.cloud.sleuth.brave.instrument.messaging; import brave.Tracing; import brave.kafka.streams.KafkaStreamsTracing; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/SleuthMessagingProperties.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/SleuthMessagingProperties.java similarity index 73% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/SleuthMessagingProperties.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/SleuthMessagingProperties.java index 9b3817447..b162684b6 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/SleuthMessagingProperties.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/SleuthMessagingProperties.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.messaging; +package org.springframework.cloud.sleuth.brave.instrument.messaging; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -24,21 +24,11 @@ import org.springframework.boot.context.properties.ConfigurationProperties; * @author Marcin Grzejszczak * @since 2.0.0 */ -@ConfigurationProperties("spring.sleuth") +@ConfigurationProperties("spring.sleuth.messaging") class SleuthMessagingProperties { - private Integration integration = new Integration(); - private Messaging messaging = new Messaging(); - public Integration getIntegration() { - return this.integration; - } - - public void setIntegration(Integration integration) { - this.integration = integration; - } - public Messaging getMessaging() { return this.messaging; } @@ -47,44 +37,6 @@ class SleuthMessagingProperties { this.messaging = messaging; } - /** - * Properties for Spring Integration. - * - * @author Marcin Grzejszczak - */ - public static class Integration { - - /** - * An array of patterns against which channel names will be matched. - * @see org.springframework.integration.config.GlobalChannelInterceptor#patterns() - * Defaults to any channel name not matching the Hystrix Stream and functional - * Stream channel names. - */ - private String[] patterns = new String[] { "!hystrixStreamOutput*", "*", "!channel*" }; - - /** - * Enable Spring Integration sleuth instrumentation. - */ - private boolean enabled; - - public String[] getPatterns() { - return this.patterns; - } - - public void setPatterns(String[] patterns) { - this.patterns = patterns; - } - - public boolean isEnabled() { - return this.enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - } - /** * Generic messaging properties. * diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessagingAutoConfiguration.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TraceMessagingAutoConfiguration.java similarity index 98% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessagingAutoConfiguration.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TraceMessagingAutoConfiguration.java index 86d653fc5..8d9a7308e 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessagingAutoConfiguration.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TraceMessagingAutoConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.messaging; +package org.springframework.cloud.sleuth.brave.instrument.messaging; import java.lang.reflect.Field; import java.util.List; @@ -82,7 +82,7 @@ import org.springframework.util.ReflectionUtils; @Configuration(proxyBeanMethods = false) @ConditionalOnBean(Tracing.class) @ConditionalOnClass(MessagingTracing.class) -@AutoConfigureAfter({ TraceAutoConfiguration.class, TraceSpringMessagingAutoConfiguration.class }) +@AutoConfigureAfter(TraceAutoConfiguration.class) @OnMessagingEnabled @EnableConfigurationProperties(SleuthMessagingProperties.class) // public allows @AutoConfigureAfter(TraceMessagingAutoConfiguration) diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingConnectionFactoryBeanPostProcessor.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TracingConnectionFactoryBeanPostProcessor.java similarity index 99% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingConnectionFactoryBeanPostProcessor.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TracingConnectionFactoryBeanPostProcessor.java index 100252c60..f8e61b6e7 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingConnectionFactoryBeanPostProcessor.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TracingConnectionFactoryBeanPostProcessor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.messaging; +package org.springframework.cloud.sleuth.brave.instrument.messaging; import javax.jms.Connection; import javax.jms.ConnectionFactory; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/mongodb/TraceMongoDbAutoConfiguration.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/mongodb/TraceMongoDbAutoConfiguration.java similarity index 92% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/mongodb/TraceMongoDbAutoConfiguration.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/mongodb/TraceMongoDbAutoConfiguration.java index eb069553d..959b45543 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/mongodb/TraceMongoDbAutoConfiguration.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/mongodb/TraceMongoDbAutoConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.mongodb; +package org.springframework.cloud.sleuth.brave.instrument.mongodb; import brave.Tracing; import brave.mongodb.MongoDBTracing; @@ -28,7 +28,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoClientSettingsBuilderCustomizer; -import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.cloud.sleuth.brave.autoconfig.TraceBraveAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -41,7 +41,7 @@ import org.springframework.context.annotation.Configuration; */ @Configuration(proxyBeanMethods = false) @ConditionalOnBean(Tracing.class) -@AutoConfigureAfter(TraceAutoConfiguration.class) +@AutoConfigureAfter(TraceBraveAutoConfiguration.class) @AutoConfigureBefore(MongoAutoConfiguration.class) @ConditionalOnProperty(value = "spring.sleuth.mongodb.enabled", matchIfMissing = true) @ConditionalOnClass(MongoClientSettings.Builder.class) diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/opentracing/OpentracingAutoConfiguration.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/opentracing/OpentracingAutoConfiguration.java similarity index 87% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/opentracing/OpentracingAutoConfiguration.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/opentracing/OpentracingAutoConfiguration.java index 42e3de548..d12bf2d36 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/opentracing/OpentracingAutoConfiguration.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/opentracing/OpentracingAutoConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.opentracing; +package org.springframework.cloud.sleuth.brave.instrument.opentracing; import brave.Tracing; import brave.opentracing.BraveTracer; @@ -26,7 +26,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.cloud.sleuth.brave.autoconfig.TraceBraveAutoConfiguration; +import org.springframework.cloud.sleuth.opentracing.SleuthOpentracingProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -42,7 +43,7 @@ import org.springframework.context.annotation.Configuration; @ConditionalOnProperty(value = "spring.sleuth.opentracing.enabled", matchIfMissing = true) @ConditionalOnBean(Tracing.class) @ConditionalOnClass(Tracer.class) -@AutoConfigureAfter(TraceAutoConfiguration.class) +@AutoConfigureAfter(TraceBraveAutoConfiguration.class) @EnableConfigurationProperties(SleuthOpentracingProperties.class) class OpentracingAutoConfiguration { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/redis/TraceRedisAutoConfiguration.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/redis/TraceRedisAutoConfiguration.java similarity index 94% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/redis/TraceRedisAutoConfiguration.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/redis/TraceRedisAutoConfiguration.java index d170f12d5..6d73b94c3 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/redis/TraceRedisAutoConfiguration.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/redis/TraceRedisAutoConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.redis; +package org.springframework.cloud.sleuth.brave.instrument.redis; import brave.Tracing; import io.lettuce.core.resource.ClientResources; @@ -29,7 +29,7 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.cloud.sleuth.brave.autoconfig.TraceBraveAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -43,7 +43,7 @@ import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(value = "spring.sleuth.redis.enabled", matchIfMissing = true) @ConditionalOnBean({ Tracing.class, ClientResources.class }) -@AutoConfigureAfter({ TraceAutoConfiguration.class }) +@AutoConfigureAfter({ TraceBraveAutoConfiguration.class }) @EnableConfigurationProperties(TraceRedisProperties.class) class TraceRedisAutoConfiguration { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/redis/TraceRedisProperties.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/redis/TraceRedisProperties.java similarity index 95% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/redis/TraceRedisProperties.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/redis/TraceRedisProperties.java index d7d5a1366..a5ebe33b0 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/redis/TraceRedisProperties.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/redis/TraceRedisProperties.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.redis; +package org.springframework.cloud.sleuth.brave.instrument.redis; import org.springframework.boot.context.properties.ConfigurationProperties; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rpc/RpcClientSampler.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/rpc/RpcClientSampler.java similarity index 96% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rpc/RpcClientSampler.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/rpc/RpcClientSampler.java index 0c823f444..905669c59 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rpc/RpcClientSampler.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/rpc/RpcClientSampler.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.rpc; +package org.springframework.cloud.sleuth.brave.instrument.rpc; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rpc/RpcServerSampler.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/rpc/RpcServerSampler.java similarity index 96% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rpc/RpcServerSampler.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/rpc/RpcServerSampler.java index a36bec711..e9c58e678 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rpc/RpcServerSampler.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/rpc/RpcServerSampler.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.rpc; +package org.springframework.cloud.sleuth.brave.instrument.rpc; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfiguration.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/rpc/TraceRpcAutoConfiguration.java similarity index 92% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfiguration.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/rpc/TraceRpcAutoConfiguration.java index 5cba9ee12..58bf0c111 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfiguration.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/rpc/TraceRpcAutoConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.rpc; +package org.springframework.cloud.sleuth.brave.instrument.rpc; import java.util.List; @@ -29,7 +29,7 @@ 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.autoconfig.TraceAutoConfiguration; +import org.springframework.cloud.sleuth.brave.autoconfig.TraceBraveAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.lang.Nullable; @@ -44,7 +44,7 @@ import org.springframework.lang.Nullable; @ConditionalOnProperty(name = "spring.sleuth.rpc.enabled", havingValue = "true", matchIfMissing = true) @ConditionalOnBean(Tracing.class) @ConditionalOnClass(RpcTracing.class) -@AutoConfigureAfter(TraceAutoConfiguration.class) +@AutoConfigureAfter(TraceBraveAutoConfiguration.class) // public allows @AutoConfigureAfter(TraceRpcAutoConfiguration) // for components needing RpcTracing public class TraceRpcAutoConfiguration { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ServletUtils.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/web/ServletUtils.java similarity index 94% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ServletUtils.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/web/ServletUtils.java index 72cc4b573..4746a0743 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ServletUtils.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/web/ServletUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web; +package org.springframework.cloud.sleuth.brave.instrument.web; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipPatternSampler.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/web/SkipPatternSampler.java similarity index 95% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipPatternSampler.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/web/SkipPatternSampler.java index 83717ef77..68b4e3313 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipPatternSampler.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/web/SkipPatternSampler.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web; +package org.springframework.cloud.sleuth.brave.instrument.web; import java.util.regex.Pattern; diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceHttpAutoConfiguration.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceHttpAutoConfiguration.java new file mode 100644 index 000000000..02db0767d --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceHttpAutoConfiguration.java @@ -0,0 +1,291 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web; + +import java.util.List; +import java.util.regex.Pattern; + +import brave.Tracing; +import brave.http.HttpRequest; +import brave.http.HttpTracing; +import brave.http.HttpTracingCustomizer; +import brave.sampler.SamplerFunction; +import brave.sampler.SamplerFunctions; +import org.jetbrains.annotations.NotNull; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +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.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.sleuth.api.http.HttpRequestParser; +import org.springframework.cloud.sleuth.api.http.HttpResponseParser; +import org.springframework.cloud.sleuth.brave.autoconfig.TraceBraveAutoConfiguration; +import org.springframework.cloud.sleuth.brave.bridge.BraveSamplerFunction; +import org.springframework.cloud.sleuth.brave.bridge.http.BraveHttpRequestParser; +import org.springframework.cloud.sleuth.brave.bridge.http.BraveHttpResponseParser; +import org.springframework.cloud.sleuth.brave.bridge.http.TraceBraveHttpBridgeAutoConfiguration; +import org.springframework.cloud.sleuth.instrument.web.HttpClientRequestParser; +import org.springframework.cloud.sleuth.instrument.web.HttpClientResponseParser; +import org.springframework.cloud.sleuth.instrument.web.HttpClientSampler; +import org.springframework.cloud.sleuth.instrument.web.HttpServerRequestParser; +import org.springframework.cloud.sleuth.instrument.web.HttpServerResponseParser; +import org.springframework.cloud.sleuth.instrument.web.HttpServerSampler; +import org.springframework.cloud.sleuth.instrument.web.SkipPatternConfiguration; +import org.springframework.cloud.sleuth.instrument.web.SkipPatternProvider; +import org.springframework.cloud.sleuth.instrument.web.SleuthHttpProperties; +import org.springframework.cloud.sleuth.instrument.web.SleuthWebProperties; +import org.springframework.cloud.sleuth.instrument.web.client.feign.TraceFeignClientAutoConfiguration; +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 HTTP based communication. + * + * @author Marcin Grzejszczak + * @since 2.0.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(name = { "spring.sleuth.http.enabled", "spring.sleuth.web.enabled" }, havingValue = "true", + matchIfMissing = true) +@ConditionalOnBean(Tracing.class) +@ConditionalOnClass(HttpTracing.class) +@AutoConfigureAfter({ TraceBraveAutoConfiguration.class, SkipPatternConfiguration.class }) +@EnableConfigurationProperties({ SleuthWebProperties.class, SleuthHttpProperties.class }) +// public allows @AutoConfigureAfter(TraceHttpAutoConfiguration) +// for components needing HttpTracing +@AutoConfigureBefore({ TraceFeignClientAutoConfiguration.class, TraceBraveHttpBridgeAutoConfiguration.class }) +public class TraceHttpAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + // NOTE: stable bean name as might be used outside sleuth + HttpTracing httpTracing(Tracing tracing, @Nullable SkipPatternProvider provider, + @Nullable brave.http.HttpClientParser clientParser, @Nullable brave.http.HttpServerParser serverParser, + BeanFactory beanFactory, @Nullable List httpTracingCustomizers) { + HttpTracing.Builder builder = httpTracingBuilder(tracing, provider, beanFactory); + brave.http.HttpRequestParser httpClientRequestParser = httpRequestParser(beanFactory, + HttpClientRequestParser.NAME); + brave.http.HttpResponseParser httpClientResponseParser = httpResponseParser(beanFactory, + HttpClientResponseParser.NAME); + brave.http.HttpRequestParser httpServerRequestParser = httpRequestParser(beanFactory, + HttpServerRequestParser.NAME); + brave.http.HttpResponseParser httpServerResponseParser = httpResponseParser(beanFactory, + HttpServerResponseParser.NAME); + + if (httpClientRequestParser != null || httpClientResponseParser != null) { + if (httpClientRequestParser != null) { + builder.clientRequestParser(httpClientRequestParser); + } + if (httpClientResponseParser != null) { + builder.clientResponseParser(httpClientResponseParser); + } + } + else if (clientParser != null) { // consider deprecated last + builder.clientParser(clientParser); + } + + if (httpServerRequestParser != null || httpServerResponseParser != null) { + if (httpServerRequestParser != null) { + builder.serverRequestParser(httpServerRequestParser); + } + if (httpServerResponseParser != null) { + builder.serverResponseParser(httpServerResponseParser); + } + } + else if (serverParser != null) { // consider deprecated last + builder.serverParser(serverParser); + } + + if (httpTracingCustomizers != null) { + for (HttpTracingCustomizer customizer : httpTracingCustomizers) { + customizer.customize(builder); + } + } + return builder.build(); + } + + private brave.http.HttpRequestParser httpRequestParser(BeanFactory beanFactory, String name) { + return beanFactory.containsBean(name) ? toBraveHttpRequestParser(beanFactory, name) : null; + } + + private brave.http.HttpResponseParser httpResponseParser(BeanFactory beanFactory, String name) { + return beanFactory.containsBean(name) ? toBraveHttpResponseParser(beanFactory, name) : null; + } + + @NotNull + private HttpTracing.Builder httpTracingBuilder(Tracing tracing, @Nullable SkipPatternProvider provider, + BeanFactory beanFactory) { + SamplerFunction httpClientSampler = toBraveSampler(beanFactory, HttpClientSampler.NAME); + SamplerFunction httpServerSampler = httpServerSampler(beanFactory); + SamplerFunction combinedSampler = combineUserProvidedSamplerWithSkipPatternSampler( + httpServerSampler, provider); + return HttpTracing.newBuilder(tracing).clientSampler(httpClientSampler).serverSampler(combinedSampler); + } + + @org.jetbrains.annotations.Nullable + private SamplerFunction httpServerSampler(BeanFactory beanFactory) { + return beanFactory.containsBean(HttpServerSampler.NAME) ? toBraveSampler(beanFactory, HttpServerSampler.NAME) + : null; + } + + private brave.http.HttpRequestParser toBraveHttpRequestParser(BeanFactory beanFactory, String beanName) { + Object bean = beanFactory.getBean(beanName); + brave.http.HttpRequestParser parser = bean instanceof brave.http.HttpRequestParser + ? (brave.http.HttpRequestParser) bean + : bean instanceof HttpRequestParser ? BraveHttpRequestParser.toBrave((HttpRequestParser) bean) : null; + return returnOrThrow(bean, parser, beanName, brave.http.HttpRequestParser.class, HttpRequestParser.class); + } + + private brave.http.HttpResponseParser toBraveHttpResponseParser(BeanFactory beanFactory, String beanName) { + Object bean = beanFactory.getBean(beanName); + brave.http.HttpResponseParser parser = bean instanceof brave.http.HttpResponseParser + ? (brave.http.HttpResponseParser) bean : bean instanceof HttpResponseParser + ? BraveHttpResponseParser.toBrave((HttpResponseParser) bean) : null; + return returnOrThrow(bean, parser, beanName, brave.http.HttpResponseParser.class, HttpResponseParser.class); + } + + private SamplerFunction toBraveSampler(BeanFactory beanFactory, String beanName) { + Object bean = beanFactory.getBean(beanName); + SamplerFunction braveSampler = bean instanceof SamplerFunction + ? (SamplerFunction) bean + : bean instanceof org.springframework.cloud.sleuth.api.SamplerFunction + ? BraveSamplerFunction.toHttpBrave( + (org.springframework.cloud.sleuth.api.SamplerFunction) bean) + : null; + return returnOrThrow(bean, braveSampler, beanName, SamplerFunction.class, + org.springframework.cloud.sleuth.api.SamplerFunction.class); + } + + @NotNull + private T returnOrThrow(Object bean, T convertedBean, String name, Class brave, Class sleuth) { + if (convertedBean == null) { + throw new IllegalStateException( + "Bean with name [" + name + "] is of type [" + bean.getClass() + "] and only [" + + brave.getCanonicalName() + "] and [" + sleuth.getCanonicalName() + "] are supported"); + } + return convertedBean; + } + + private SamplerFunction combineUserProvidedSamplerWithSkipPatternSampler( + @Nullable SamplerFunction serverSampler, @Nullable SkipPatternProvider provider) { + SamplerFunction skipPatternSampler = provider != null + ? new SkipPatternHttpServerSampler(provider) : null; + if (serverSampler == null && skipPatternSampler == null) { + return SamplerFunctions.deferDecision(); + } + else if (serverSampler == null) { + return skipPatternSampler; + } + else if (skipPatternSampler == null) { + return serverSampler; + } + return new CompositeHttpSampler(skipPatternSampler, serverSampler); + } + + @Bean + @ConditionalOnMissingBean(name = HttpClientSampler.NAME) + SamplerFunction sleuthHttpClientSampler(SleuthWebProperties sleuthWebProperties) { + String skipPattern = sleuthWebProperties.getClient().getSkipPattern(); + if (skipPattern == null) { + return SamplerFunctions.deferDecision(); + } + + return new SkipPatternHttpClientSampler(Pattern.compile(skipPattern)); + } + +} + +/** + * Composite Http Sampler. + * + * @author Adrian Cole + */ +final class CompositeHttpSampler implements SamplerFunction { + + final SamplerFunction left; + + final SamplerFunction right; + + CompositeHttpSampler(SamplerFunction left, SamplerFunction right) { + this.left = left; + this.right = right; + } + + @Override + public Boolean trySample(brave.http.HttpRequest request) { + // If either decision is false, return false + Boolean leftDecision = this.left.trySample(request); + if (Boolean.FALSE.equals(leftDecision)) { + return false; + } + Boolean rightDecision = this.right.trySample(request); + if (Boolean.FALSE.equals(rightDecision)) { + return false; + } + // If either decision is null, return the other + if (leftDecision == null) { + return rightDecision; + } + if (rightDecision == null) { + return leftDecision; + } + // Neither are null and at least one is true + return rightDecision; + } + +} + +/** + * Http Sampler that looks at paths. + * + * @author Marcin Grzejszczak + */ +final class SkipPatternHttpServerSampler extends SkipPatternSampler { + + private final SkipPatternProvider provider; + + SkipPatternHttpServerSampler(SkipPatternProvider provider) { + this.provider = provider; + } + + @Override + Pattern getPattern() { + return this.provider.skipPattern(); + } + +} + +final class SkipPatternHttpClientSampler extends SkipPatternSampler { + + private final Pattern skipPattern; + + SkipPatternHttpClientSampler(Pattern skipPattern) { + this.skipPattern = skipPattern; + } + + @Override + Pattern getPattern() { + return skipPattern; + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/web/client/TraceWebClientAutoConfiguration.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/web/client/TraceWebClientAutoConfiguration.java new file mode 100644 index 000000000..23b046715 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/web/client/TraceWebClientAutoConfiguration.java @@ -0,0 +1,76 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web.client; + +import brave.http.HttpTracing; +import brave.httpasyncclient.TracingHttpAsyncClientBuilder; +import brave.httpclient.TracingHttpClientBuilder; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cloud.commons.httpclient.HttpClientConfiguration; +import org.springframework.cloud.sleuth.brave.instrument.web.TraceHttpAutoConfiguration; +import org.springframework.cloud.sleuth.instrument.web.client.SleuthWebClientEnabled; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +/** + * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration + * Auto-configuration} enables span information propagation when using + * {@link RestTemplate}. + * + * @author Marcin Grzejszczak + * @since 1.0.0 + */ +@Configuration(proxyBeanMethods = false) +@SleuthWebClientEnabled +@ConditionalOnBean(HttpTracing.class) +@AutoConfigureAfter(TraceHttpAutoConfiguration.class) +@AutoConfigureBefore(HttpClientConfiguration.class) +class TraceWebClientAutoConfiguration { + + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(HttpClientBuilder.class) + static class HttpClientBuilderConfig { + + @Bean + @ConditionalOnMissingBean + HttpClientBuilder traceHttpClientBuilder(HttpTracing httpTracing) { + return TracingHttpClientBuilder.create(httpTracing); + } + + } + + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(HttpAsyncClientBuilder.class) + static class HttpAsyncClientBuilderConfig { + + @Bean + @ConditionalOnMissingBean + HttpAsyncClientBuilder traceHttpAsyncClientBuilder(HttpTracing httpTracing) { + return TracingHttpAsyncClientBuilder.create(httpTracing); + } + + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/propagation/CompositePropagationFactorySupplier.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/propagation/CompositePropagationFactorySupplier.java new file mode 100644 index 000000000..9c28299b5 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/propagation/CompositePropagationFactorySupplier.java @@ -0,0 +1,124 @@ +/* + * Copyright 2013-2020 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.brave.propagation; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import brave.internal.propagation.StringPropagationAdapter; +import brave.propagation.B3Propagation; +import brave.propagation.Propagation; +import brave.propagation.TraceContext; +import brave.propagation.TraceContextOrSamplingFlags; +import brave.propagation.aws.AWSPropagation; + +class CompositePropagationFactorySupplier implements PropagationFactorySupplier { + + private final SleuthPropagationProperties properties; + + CompositePropagationFactorySupplier(SleuthPropagationProperties properties) { + this.properties = properties; + } + + @Override + public Propagation.Factory get() { + return new CompositePropagationFactory(this.properties); + } + +} + +class CompositePropagationFactory extends Propagation.Factory implements Propagation { + + private final Map> mapping = new HashMap<>(); + + private final SleuthPropagationProperties properties; + + CompositePropagationFactory(SleuthPropagationProperties properties) { + this.properties = properties; + this.mapping.put(SleuthPropagationProperties.PropagationType.AWS, AWSPropagation.FACTORY.get()); + // Note: Versions <2.2.3 use injectFormat(MULTI) for non-remote (ex + // spring-messaging) + // See #1643 + this.mapping.put(SleuthPropagationProperties.PropagationType.B3, + B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build().get()); + this.mapping.put(SleuthPropagationProperties.PropagationType.W3C, W3CPropagation.getInstance()); + this.mapping.put(SleuthPropagationProperties.PropagationType.CUSTOM, NoOpPropagation.INSTANCE); + } + + @Override + public List keys() { + return this.properties.getType().stream().map(this.mapping::get).flatMap(p -> p.keys().stream()) + .collect(Collectors.toList()); + } + + @Override + public TraceContext.Injector injector(Setter setter) { + return (traceContext, request) -> { + this.properties.getType().stream().map(this.mapping::get) + .forEach(p -> p.injector(setter).inject(traceContext, request)); + }; + } + + @Override + public TraceContext.Extractor extractor(Getter getter) { + return request -> { + for (SleuthPropagationProperties.PropagationType type : this.properties.getType()) { + Propagation propagator = this.mapping.get(type); + if (propagator == null || propagator == NoOpPropagation.INSTANCE) { + continue; + } + TraceContextOrSamplingFlags extract = propagator.extractor(getter).extract(request); + if (extract != TraceContextOrSamplingFlags.EMPTY) { + return extract; + } + } + return TraceContextOrSamplingFlags.EMPTY; + }; + } + + @Override + public Propagation create(KeyFactory keyFactory) { + return StringPropagationAdapter.create(this, keyFactory); + } + + private static class NoOpPropagation implements Propagation { + + static final NoOpPropagation INSTANCE = new NoOpPropagation(); + + @Override + public List keys() { + return Collections.emptyList(); + } + + @Override + public TraceContext.Injector injector(Setter setter) { + return (traceContext, request) -> { + + }; + } + + @Override + public TraceContext.Extractor extractor(Getter getter) { + return request -> TraceContextOrSamplingFlags.EMPTY; + } + + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/propagation/PropagationFactorySupplier.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/propagation/PropagationFactorySupplier.java new file mode 100644 index 000000000..fe7755277 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/propagation/PropagationFactorySupplier.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2020 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.brave.propagation; + +import brave.propagation.Propagation; + +/** + * Provides logic for supplying of a {@link Propagation.Factory}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface PropagationFactorySupplier { + + /** + * @return an instance of a {@link Propagation.Factory} + */ + Propagation.Factory get(); + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/propagation/SleuthPropagationProperties.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/propagation/SleuthPropagationProperties.java new file mode 100644 index 000000000..f9d78000b --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/propagation/SleuthPropagationProperties.java @@ -0,0 +1,70 @@ +/* + * Copyright 2013-2020 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.brave.propagation; + +import java.util.Collections; +import java.util.List; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Sleuth settings for OpenTelemetry. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +@ConfigurationProperties("spring.sleuth.propagation") +public class SleuthPropagationProperties { + + /** + * Type of propagation. + */ + private List type = Collections.singletonList(PropagationType.B3); + + public List getType() { + return this.type; + } + + public void setType(List type) { + this.type = type; + } + + public enum PropagationType { + + /** + * AWS propagation type. + */ + AWS, + + /** + * B3 propagation type. + */ + B3, + + /** + * W3C propagation type. + */ + W3C, + + /** + * Custom propagation type. + */ + CUSTOM + + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/propagation/TraceBravePropagationAutoConfiguration.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/propagation/TraceBravePropagationAutoConfiguration.java new file mode 100644 index 000000000..6f8597438 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/propagation/TraceBravePropagationAutoConfiguration.java @@ -0,0 +1,43 @@ +/* + * Copyright 2013-2020 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.brave.propagation; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration + * Auto-configuration} to enable propagation factories. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(value = "spring.sleuth.enabled", matchIfMissing = true) +@EnableConfigurationProperties(SleuthPropagationProperties.class) +public class TraceBravePropagationAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + PropagationFactorySupplier compositePropagationFactorySupplier(SleuthPropagationProperties properties) { + return new CompositePropagationFactorySupplier(properties); + } + +} diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/propagation/W3CPropagation.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/propagation/W3CPropagation.java new file mode 100644 index 000000000..9cde399ea --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/propagation/W3CPropagation.java @@ -0,0 +1,459 @@ +/* + * Copyright 2013-2020 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.brave.propagation; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.logging.Logger; + +import brave.internal.propagation.StringPropagationAdapter; +import brave.propagation.Propagation; +import brave.propagation.TraceContext; +import brave.propagation.TraceContextOrSamplingFlags; + +/** + * Adopted from OpenTelemetry API. + * + * Implementation of the TraceContext propagation protocol. See w3c/distributed-tracing. + * + * @author OpenTelemetry Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public final class W3CPropagation extends Propagation.Factory implements Propagation { + + private static final Logger logger = Logger.getLogger(W3CPropagation.class.getName()); + + static final String TRACE_PARENT = "traceparent"; + static final String TRACE_STATE = "tracestate"; + + private static final List FIELDS = Collections.unmodifiableList(Arrays.asList(TRACE_PARENT, TRACE_STATE)); + + private static final String VERSION = "00"; + + private static final int VERSION_SIZE = 2; + + private static final char TRACEPARENT_DELIMITER = '-'; + + private static final int TRACEPARENT_DELIMITER_SIZE = 1; + + private static final int LONG_BYTES = Long.SIZE / Byte.SIZE; + + private static final int BYTE_BASE16 = 2; + + private static final int LONG_BASE16 = BYTE_BASE16 * LONG_BYTES; + + private static final int TRACE_ID_HEX_SIZE = 2 * LONG_BASE16; + + private static final int SPAN_ID_SIZE = 8; + + private static final int SPAN_ID_HEX_SIZE = 2 * SPAN_ID_SIZE; + + private static final int FLAGS_SIZE = 1; + + private static final int TRACE_OPTION_HEX_SIZE = 2 * FLAGS_SIZE; + + private static final int TRACE_ID_OFFSET = VERSION_SIZE + TRACEPARENT_DELIMITER_SIZE; + + private static final int SPAN_ID_OFFSET = TRACE_ID_OFFSET + TRACE_ID_HEX_SIZE + TRACEPARENT_DELIMITER_SIZE; + + private static final int TRACE_OPTION_OFFSET = SPAN_ID_OFFSET + SPAN_ID_HEX_SIZE + TRACEPARENT_DELIMITER_SIZE; + + private static final int TRACEPARENT_HEADER_SIZE = TRACE_OPTION_OFFSET + TRACE_OPTION_HEX_SIZE; + + private static final String INVALID_TRACE_ID = "00000000000000000000000000000000"; + + private static final String INVALID_SPAN_ID = "0000000000000000"; + + private static final char TRACESTATE_ENTRY_DELIMITER = ','; + + private static final Set VALID_VERSIONS; + + private static final String VERSION_00 = "00"; + + private static final W3CPropagation INSTANCE = new W3CPropagation(); + + static { + // A valid version is 1 byte representing an 8-bit unsigned integer, version ff is + // invalid. + VALID_VERSIONS = new HashSet<>(); + for (int i = 0; i < 255; i++) { + String version = Long.toHexString(i); + if (version.length() < 2) { + version = '0' + version; + } + VALID_VERSIONS.add(version); + } + } + + private W3CPropagation() { + // singleton + } + + @Override + public Propagation create(KeyFactory keyFactory) { + return StringPropagationAdapter.create(this, keyFactory); + } + + public static W3CPropagation getInstance() { + return INSTANCE; + } + + @Override + public List keys() { + return FIELDS; + } + + @Override + public TraceContext.Injector injector(Setter setter) { + return (context, carrier) -> { + Objects.requireNonNull(context, "context"); + Objects.requireNonNull(setter, "setter"); + char[] chars = TemporaryBuffers.chars(TRACEPARENT_HEADER_SIZE); + chars[0] = VERSION.charAt(0); + chars[1] = VERSION.charAt(1); + chars[2] = TRACEPARENT_DELIMITER; + String traceId = context.traceIdString(); + for (int i = 0; i < traceId.length(); i++) { + chars[TRACE_ID_OFFSET + i] = traceId.charAt(i); + } + chars[SPAN_ID_OFFSET - 1] = TRACEPARENT_DELIMITER; + String spanId = context.spanIdString(); + for (int i = 0; i < spanId.length(); i++) { + chars[SPAN_ID_OFFSET + i] = spanId.charAt(i); + } + chars[TRACE_OPTION_OFFSET - 1] = TRACEPARENT_DELIMITER; + copyTraceFlagsHexTo(chars, TRACE_OPTION_OFFSET, context); + setter.put(carrier, TRACE_PARENT, new String(chars, 0, TRACEPARENT_HEADER_SIZE)); + // Does not inject trace state + }; + } + + public void copyTraceFlagsHexTo(char[] dest, int destOffset, TraceContext context) { + dest[destOffset] = '0'; + dest[destOffset + 1] = Boolean.TRUE.equals(context.sampled()) ? '1' : '0'; + } + + @Override + public TraceContext.Extractor extractor(Getter getter) { + Objects.requireNonNull(getter, "getter"); + return carrier -> { + String traceParent = getter.get(carrier, TRACE_PARENT); + if (traceParent == null) { + return TraceContextOrSamplingFlags.EMPTY; + } + TraceContext contextFromParentHeader = extractContextFromTraceParent(traceParent); + if (contextFromParentHeader == null) { + return TraceContextOrSamplingFlags.EMPTY; + } + String traceStateHeader = getter.get(carrier, TRACE_STATE); + if (traceStateHeader == null || traceStateHeader.isEmpty()) { + return TraceContextOrSamplingFlags.create(contextFromParentHeader); + } + try { + return TraceContextOrSamplingFlags.create(TraceContext.newBuilder() + .traceId(contextFromParentHeader.traceId()).traceIdHigh(contextFromParentHeader.traceIdHigh()) + .spanId(contextFromParentHeader.spanId()).sampled(contextFromParentHeader.sampled()) + .shared(true).build()); + } + catch (IllegalArgumentException e) { + logger.info("Unparseable tracestate header. Returning span context without state."); + return TraceContextOrSamplingFlags.create(contextFromParentHeader); + } + }; + } + + private static boolean isTraceIdValid(CharSequence traceId) { + return (traceId.length() == TRACE_ID_HEX_SIZE) && !INVALID_TRACE_ID.contentEquals(traceId) + && BigendianEncoding.isValidBase16String(traceId); + } + + private static boolean isSpanIdValid(String spanId) { + return (spanId.length() == SPAN_ID_HEX_SIZE) && !INVALID_SPAN_ID.equals(spanId) + && BigendianEncoding.isValidBase16String(spanId); + } + + private static TraceContext extractContextFromTraceParent(String traceparent) { + // TODO(bdrutu): Do we need to verify that version is hex and that + // for the version the length is the expected one? + boolean isValid = (traceparent.length() == TRACEPARENT_HEADER_SIZE + || (traceparent.length() > TRACEPARENT_HEADER_SIZE + && traceparent.charAt(TRACEPARENT_HEADER_SIZE) == TRACEPARENT_DELIMITER)) + && traceparent.charAt(TRACE_ID_OFFSET - 1) == TRACEPARENT_DELIMITER + && traceparent.charAt(SPAN_ID_OFFSET - 1) == TRACEPARENT_DELIMITER + && traceparent.charAt(TRACE_OPTION_OFFSET - 1) == TRACEPARENT_DELIMITER; + if (!isValid) { + logger.info("Unparseable traceparent header. Returning INVALID span context."); + return null; + } + + try { + String version = traceparent.substring(0, 2); + if (!VALID_VERSIONS.contains(version)) { + return null; + } + if (version.equals(VERSION_00) && traceparent.length() > TRACEPARENT_HEADER_SIZE) { + return null; + } + + String traceId = traceparent.substring(TRACE_ID_OFFSET, TRACE_ID_OFFSET + TRACE_ID_HEX_SIZE); + String spanId = traceparent.substring(SPAN_ID_OFFSET, SPAN_ID_OFFSET + SPAN_ID_HEX_SIZE); + if (isTraceIdValid(traceId) && isSpanIdValid(spanId)) { + String traceIdHigh = traceId.substring(0, traceId.length() / 2); + String traceIdLow = traceId.substring(traceId.length() / 2); + byte isSampled = TraceFlags.byteFromHex(traceparent, TRACE_OPTION_OFFSET); + return TraceContext.newBuilder().shared(true) + .traceIdHigh(BigendianEncoding.longFromBase16String(traceIdHigh)) + .traceId(BigendianEncoding.longFromBase16String(traceIdLow)) + .spanId(BigendianEncoding.longFromBase16String(spanId)) + .sampled(isSampled == TraceFlags.IS_SAMPLED).build(); + } + return null; + } + catch (IllegalArgumentException e) { + logger.info("Unparseable traceparent header. Returning INVALID span context."); + return null; + } + } + +} + +/** + * Taken from OpenTelemetry API. + * + * {@link ThreadLocal} buffers for use when creating new derived objects such as + * {@link String}s. These buffers are reused within a single thread - it is _not safe_ to + * use the buffer to generate multiple derived objects at the same time because the same + * memory will be used. In general, you should get a temporary buffer, fill it with data, + * and finish by converting into the derived object within the same method to avoid + * multiple usages of the same buffer. + */ +final class TemporaryBuffers { + + private static final ThreadLocal CHAR_ARRAY = new ThreadLocal<>(); + + /** + * A {@link ThreadLocal} {@code char[]} of size {@code len}. Take care when using a + * large value of {@code len} as this buffer will remain for the lifetime of the + * thread. The returned buffer will not be zeroed and may be larger than the requested + * size, you must make sure to fill the entire content to the desired value and set + * the length explicitly when converting to a {@link String}. + */ + public static char[] chars(int len) { + char[] buffer = CHAR_ARRAY.get(); + if (buffer == null) { + buffer = new char[len]; + CHAR_ARRAY.set(buffer); + } + else if (buffer.length < len) { + buffer = new char[len]; + CHAR_ARRAY.set(buffer); + } + return buffer; + } + + // Visible for testing + static void clearChars() { + CHAR_ARRAY.set(null); + } + + private TemporaryBuffers() { + } + +} + +/** + * Taken from OpenTelemetry API. + */ +final class Utils { + + private Utils() { + + } + + /** + * Throws an {@link IllegalArgumentException} if the argument is false. This method is + * similar to {@code Preconditions.checkArgument(boolean, Object)} from Guava. + * @param isValid whether the argument check passed. + * @param errorMessage the message to use for the exception. + */ + static void checkArgument(boolean isValid, String errorMessage) { + if (!isValid) { + throw new IllegalArgumentException(errorMessage); + } + } + + /** + * Throws an {@link IllegalStateException} if the argument is false. This method is + * similar to {@code Preconditions.checkState(boolean, Object)} from Guava. + * @param isValid whether the state check passed. + * @param errorMessage the message to use for the exception. + */ + static void checkState(boolean isValid, String errorMessage) { + if (!isValid) { + throw new IllegalStateException(String.valueOf(errorMessage)); + } + } + +} + +/** + * Taken from OpenTelemetry API. + */ +final class TraceFlags { + + private TraceFlags() { + } + + // Bit to represent whether trace is sampled or not. + static final byte IS_SAMPLED = 0x1; + + /** Extract the byte representation of the flags from a hex-representation. */ + static byte byteFromHex(CharSequence src, int srcOffset) { + return BigendianEncoding.byteFromBase16String(src, srcOffset); + } + +} + +/** + * Taken from OpenTelemetry API. + */ +final class BigendianEncoding { + + private BigendianEncoding() { + } + + static final int LONG_BYTES = Long.SIZE / Byte.SIZE; + + static final int BYTE_BASE16 = 2; + + static final int LONG_BASE16 = BYTE_BASE16 * LONG_BYTES; + + private static final String ALPHABET = "0123456789abcdef"; + + private static final int ASCII_CHARACTERS = 128; + + private static final char[] ENCODING = buildEncodingArray(); + + private static final byte[] DECODING = buildDecodingArray(); + + private static char[] buildEncodingArray() { + char[] encoding = new char[512]; + for (int i = 0; i < 256; ++i) { + encoding[i] = ALPHABET.charAt(i >>> 4); + encoding[i | 0x100] = ALPHABET.charAt(i & 0xF); + } + return encoding; + } + + private static byte[] buildDecodingArray() { + byte[] decoding = new byte[ASCII_CHARACTERS]; + Arrays.fill(decoding, (byte) -1); + for (int i = 0; i < ALPHABET.length(); i++) { + char c = ALPHABET.charAt(i); + decoding[c] = (byte) i; + } + return decoding; + } + + /** + * Returns the {@code long} value whose base16 representation is stored in the first + * 16 chars of {@code chars} starting from the {@code offset}. + * @param chars the base16 representation of the {@code long}. + */ + static long longFromBase16String(CharSequence chars) { + return longFromBase16String(chars, 0); + } + + /** + * Returns the {@code long} value whose base16 representation is stored in the first + * 16 chars of {@code chars} starting from the {@code offset}. + * @param chars the base16 representation of the {@code long}. + */ + static long longFromBase16String(CharSequence chars, int offset) { + Utils.checkArgument(chars.length() >= offset + LONG_BASE16, "chars too small"); + return (decodeByte(chars.charAt(offset), chars.charAt(offset + 1)) & 0xFFL) << 56 + | (decodeByte(chars.charAt(offset + 2), chars.charAt(offset + 3)) & 0xFFL) << 48 + | (decodeByte(chars.charAt(offset + 4), chars.charAt(offset + 5)) & 0xFFL) << 40 + | (decodeByte(chars.charAt(offset + 6), chars.charAt(offset + 7)) & 0xFFL) << 32 + | (decodeByte(chars.charAt(offset + 8), chars.charAt(offset + 9)) & 0xFFL) << 24 + | (decodeByte(chars.charAt(offset + 10), chars.charAt(offset + 11)) & 0xFFL) << 16 + | (decodeByte(chars.charAt(offset + 12), chars.charAt(offset + 13)) & 0xFFL) << 8 + | (decodeByte(chars.charAt(offset + 14), chars.charAt(offset + 15)) & 0xFFL); + } + + /** + * Decodes the specified two character sequence, and returns the resulting + * {@code byte}. + * @param chars the character sequence to be decoded. + * @param offset the starting offset in the {@code CharSequence}. + * @return the resulting {@code byte} + * @throws IllegalArgumentException if the input is not a valid encoded string + * according to this encoding. + */ + static byte byteFromBase16String(CharSequence chars, int offset) { + Utils.checkArgument(chars.length() >= offset + 2, "chars too small"); + return decodeByte(chars.charAt(offset), chars.charAt(offset + 1)); + } + + private static byte decodeByte(char hi, char lo) { + Utils.checkArgument(lo < ASCII_CHARACTERS && DECODING[lo] != -1, "invalid character " + lo); + Utils.checkArgument(hi < ASCII_CHARACTERS && DECODING[hi] != -1, "invalid character " + hi); + int decoded = DECODING[hi] << 4 | DECODING[lo]; + return (byte) decoded; + } + + /** + * Returns the {@code long} value whose big-endian representation is stored in the + * first 8 bytes of {@code bytes} starting from the {@code offset}. + * @param bytes the byte array representation of the {@code long}. + * @param offset the starting offset in the byte array. + * @return the {@code long} value whose big-endian representation is given. + * @throws IllegalArgumentException if {@code bytes} has fewer than 8 elements. + */ + static long longFromByteArray(byte[] bytes, int offset) { + Utils.checkArgument(bytes.length >= offset + LONG_BYTES, "array too small"); + return (bytes[offset] & 0xFFL) << 56 | (bytes[offset + 1] & 0xFFL) << 48 | (bytes[offset + 2] & 0xFFL) << 40 + | (bytes[offset + 3] & 0xFFL) << 32 | (bytes[offset + 4] & 0xFFL) << 24 + | (bytes[offset + 5] & 0xFFL) << 16 | (bytes[offset + 6] & 0xFFL) << 8 | (bytes[offset + 7] & 0xFFL); + } + + static boolean isValidBase16String(CharSequence value) { + for (int i = 0; i < value.length(); i++) { + char b = value.charAt(i); + // 48..57 && 97..102 are valid + if (!isDigit(b) && !isLowercaseHexCharacter(b)) { + return false; + } + } + return true; + } + + private static boolean isLowercaseHexCharacter(char b) { + return 97 <= b && b <= 102; + } + + private static boolean isDigit(char b) { + return 48 <= b && b <= 57; + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/ProbabilityBasedSampler.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/sampler/ProbabilityBasedSampler.java similarity index 98% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/ProbabilityBasedSampler.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/sampler/ProbabilityBasedSampler.java index ed030e4b2..e3efb4542 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/ProbabilityBasedSampler.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/sampler/ProbabilityBasedSampler.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.sampler; +package org.springframework.cloud.sleuth.brave.sampler; import java.util.BitSet; import java.util.Random; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/RateLimitingSampler.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/sampler/RateLimitingSampler.java similarity index 96% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/RateLimitingSampler.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/sampler/RateLimitingSampler.java index 62b24ba99..2bfb124f8 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/RateLimitingSampler.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/sampler/RateLimitingSampler.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.sampler; +package org.springframework.cloud.sleuth.brave.sampler; import brave.sampler.Sampler; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/SamplerAutoConfiguration.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/sampler/SamplerAutoConfiguration.java similarity index 98% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/SamplerAutoConfiguration.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/sampler/SamplerAutoConfiguration.java index ca7cecaf9..9fa516914 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/SamplerAutoConfiguration.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/sampler/SamplerAutoConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.sampler; +package org.springframework.cloud.sleuth.brave.sampler; import brave.sampler.CountingSampler; import brave.sampler.Sampler; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/SamplerCondition.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/sampler/SamplerCondition.java similarity index 98% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/SamplerCondition.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/sampler/SamplerCondition.java index 6c19937b0..e7a8508cf 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/SamplerCondition.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/sampler/SamplerCondition.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.sampler; +package org.springframework.cloud.sleuth.brave.sampler; import brave.TracingCustomizer; import brave.handler.SpanHandler; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/SamplerProperties.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/sampler/SamplerProperties.java similarity index 97% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/SamplerProperties.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/sampler/SamplerProperties.java index 0f1f39f9d..51efe1992 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/SamplerProperties.java +++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/sampler/SamplerProperties.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.sampler; +package org.springframework.cloud.sleuth.brave.sampler; import org.springframework.boot.context.properties.ConfigurationProperties; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/jms/config/TracingJmsListenerEndpointRegistry.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/jms/config/TracingJmsListenerEndpointRegistry.java similarity index 100% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/jms/config/TracingJmsListenerEndpointRegistry.java rename to spring-cloud-sleuth-brave/src/main/java/org/springframework/jms/config/TracingJmsListenerEndpointRegistry.java diff --git a/spring-cloud-sleuth-brave/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-sleuth-brave/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 000000000..828f8064d --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,100 @@ +{ + "properties": [ + { + "name": "spring.sleuth.integration.enabled", + "type": "java.lang.Boolean", + "description": "Enable Spring Integration sleuth instrumentation.", + "defaultValue": true + }, + { + "name": "spring.sleuth.integration.websockets.enabled", + "type": "java.lang.Boolean", + "description": "Enable tracing for WebSockets.", + "defaultValue": true + }, + { + "name": "spring.sleuth.async.enabled", + "type": "java.lang.Boolean", + "description": "Enable instrumenting async related components so that the tracing information is passed between threads.", + "defaultValue": true + }, + { + "name": "spring.sleuth.async.configurer.enabled", + "type": "java.lang.Boolean", + "description": "Enable default AsyncConfigurer.", + "defaultValue": true + }, + { + "name": "spring.sleuth.feign.enabled", + "type": "java.lang.Boolean", + "description": "Enable span information propagation when using Feign.", + "defaultValue": true + }, + { + "name": "spring.sleuth.feign.processor.enabled", + "type": "java.lang.Boolean", + "description": "Enable post processor that wraps Feign Context in its tracing representations.", + "defaultValue": true + }, + { + "name": "spring.sleuth.grpc.enabled", + "type": "java.lang.Boolean", + "description": "Enable span information propagation when using GRPC.", + "defaultValue": true + }, + { + "name": "spring.sleuth.messaging.jms.enabled", + "type": "java.lang.Boolean", + "description": "Enable tracing of JMS.", + "defaultValue": true + }, + { + "name": "spring.sleuth.messaging.rabbit.enabled", + "type": "java.lang.Boolean", + "description": "Enable tracing of RabbitMQ.", + "defaultValue": true + }, + { + "name": "spring.sleuth.messaging.kafka.enabled", + "type": "java.lang.Boolean", + "description": "Enable tracing of Kafka.", + "defaultValue": true + }, + { + "name": "spring.sleuth.messaging.kafka.mapper.enabled", + "type": "java.lang.Boolean", + "description": "Enable DefaultKafkaHeaderMapper tracing for Kafka.", + "defaultValue": true + }, + { + "name": "spring.sleuth.quartz.enabled", + "type": "java.lang.Boolean", + "description": "Enable tracing for Quartz.", + "defaultValue": true + }, + { + "name": "spring.sleuth.mongodb.enabled", + "type": "java.lang.Boolean", + "description": "Enable tracing for MongoDb.", + "defaultValue": true + }, + { + "name": "spring.sleuth.rpc.enabled", + "type": "java.lang.Boolean", + "description": "Enable tracing of RPC.", + "defaultValue": true + }, + { + "name": "spring.sleuth.sampler.refresh.enabled", + "type": "java.lang.Boolean", + "description": "Enable refresh scope for sampler.", + "defaultValue": true + }, + { + "name": "spring.sleuth.web.webclient.enabled", + "type": "java.lang.Boolean", + "description": "Enable tracing instrumentation for WebClient.", + "defaultValue": true + } + ] +} diff --git a/spring-cloud-sleuth-brave/src/main/resources/META-INF/spring.factories b/spring-cloud-sleuth-brave/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..07c86e7f5 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/main/resources/META-INF/spring.factories @@ -0,0 +1,15 @@ +# Auto Configuration +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.springframework.cloud.sleuth.brave.autoconfig.TraceBraveAutoConfiguration,\ +org.springframework.cloud.sleuth.brave.bridge.TraceBraveBridgeAutoConfiguation,\ +org.springframework.cloud.sleuth.brave.bridge.http.TraceBraveHttpBridgeAutoConfiguration,\ +org.springframework.cloud.sleuth.brave.propagation.TraceBravePropagationAutoConfiguration,\ +org.springframework.cloud.sleuth.brave.instrument.web.TraceHttpAutoConfiguration,\ +org.springframework.cloud.sleuth.brave.instrument.web.client.TraceWebClientAutoConfiguration,\ +org.springframework.cloud.sleuth.brave.instrument.rpc.TraceRpcAutoConfiguration,\ +org.springframework.cloud.sleuth.brave.instrument.grpc.TraceGrpcAutoConfiguration,\ +org.springframework.cloud.sleuth.brave.instrument.messaging.SleuthKafkaStreamsConfiguration,\ +org.springframework.cloud.sleuth.brave.instrument.messaging.TraceMessagingAutoConfiguration,\ +org.springframework.cloud.sleuth.brave.instrument.opentracing.OpentracingAutoConfiguration,\ +org.springframework.cloud.sleuth.brave.instrument.redis.TraceRedisAutoConfiguration,\ +org.springframework.cloud.sleuth.brave.instrument.mongodb.TraceMongoDbAutoConfiguration diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/DisableSecurity.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/DisableSecurity.java similarity index 100% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/DisableSecurity.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/DisableSecurity.java diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/DisableWebFluxSecurity.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/DisableWebFluxSecurity.java similarity index 100% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/DisableWebFluxSecurity.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/DisableWebFluxSecurity.java diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/PermitAllServletConfiguration.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/PermitAllServletConfiguration.java similarity index 96% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/PermitAllServletConfiguration.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/PermitAllServletConfiguration.java index d3bbce2d7..e597420ff 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/PermitAllServletConfiguration.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/PermitAllServletConfiguration.java @@ -22,7 +22,7 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -@Configuration +@Configuration(proxyBeanMethods = false) @EnableWebSecurity @Order(99) class PermitAllServletConfiguration extends WebSecurityConfigurerAdapter { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/PermitAllWebFluxSecurityConfiguration.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/PermitAllWebFluxSecurityConfiguration.java similarity index 96% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/PermitAllWebFluxSecurityConfiguration.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/PermitAllWebFluxSecurityConfiguration.java index 59e4f0e57..6341101cb 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/PermitAllWebFluxSecurityConfiguration.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/PermitAllWebFluxSecurityConfiguration.java @@ -21,7 +21,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.web.server.SecurityWebFilterChain; -@Configuration +@Configuration(proxyBeanMethods = false) class PermitAllWebFluxSecurityConfiguration { @Bean diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SleuthTestAutoConfiguration.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/SleuthTestAutoConfiguration.java similarity index 85% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SleuthTestAutoConfiguration.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/SleuthTestAutoConfiguration.java index a76e9ef6f..4756e5b9b 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SleuthTestAutoConfiguration.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/SleuthTestAutoConfiguration.java @@ -26,30 +26,33 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Primary; /** * @author Marcin Grzejszczak */ -@Configuration +@Configuration(proxyBeanMethods = false) class SleuthTestAutoConfiguration { - @Configuration + @Configuration(proxyBeanMethods = false) static class TestMongoConfiguration { @Bean + @Primary @ConditionalOnProperty(value = "test.mongo.mock.enabled", matchIfMissing = true) - MongoClient mongoClient() { + MongoClient testMongoClient() { return BDDMockito.mock(MongoClient.class); } @Bean(name = "mongoHealthIndicator") - HealthIndicator mongoHealthIndicator() { + @Primary + HealthIndicator testMongoHealthIndicator() { return () -> Health.up().build(); } } - @Configuration + @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) static class ReactiveConfiguration { @@ -60,7 +63,7 @@ class SleuthTestAutoConfiguration { } - @Configuration + @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) static class ServletConfiguration { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SpanHandlerTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/SpanHandlerTests.java similarity index 92% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SpanHandlerTests.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/SpanHandlerTests.java index 819171464..8e7979da3 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SpanHandlerTests.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/SpanHandlerTests.java @@ -31,6 +31,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.cloud.sleuth.brave.autoconfig.TraceBraveAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -63,8 +64,8 @@ public class SpanHandlerTests { BDDAssertions.then(this.spans.get(0).name()).isEqualTo("foo bar"); } - @Configuration - @ImportAutoConfiguration(TraceAutoConfiguration.class) + @Configuration(proxyBeanMethods = false) + @ImportAutoConfiguration({ TraceAutoConfiguration.class, TraceBraveAutoConfiguration.class }) static class SpanHandlerAspectTestsConfig { @Bean diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfigurationTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/autoconfig/TraceBaggageEntryConfigurationTests.java similarity index 96% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfigurationTests.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/autoconfig/TraceBaggageEntryConfigurationTests.java index 0e0a57fd3..a923c497c 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfigurationTests.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/autoconfig/TraceBaggageEntryConfigurationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.autoconfig; +package org.springframework.cloud.sleuth.brave.autoconfig; import java.util.List; import java.util.Set; @@ -39,7 +39,7 @@ import org.junit.jupiter.api.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.autoconfig.TraceBaggageConfiguration.BaggageTagSpanHandler; +import org.springframework.cloud.sleuth.brave.autoconfig.TraceBaggageConfiguration.BaggageTagSpanHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -47,7 +47,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; import static org.assertj.core.api.InstanceOfAssertFactories.array; -public class TraceBaggageConfigurationTests { +public class TraceBaggageEntryConfigurationTests { static final String[] EMPTY_ARRAY = {}; @@ -161,7 +161,7 @@ public class TraceBaggageConfigurationTests { .extracting(SingleCorrelationField::dirty).containsExactly(true)); } - @Configuration + @Configuration(proxyBeanMethods = false) static class DirtyCorrelationFieldConfiguration { @Bean @@ -184,7 +184,7 @@ public class TraceBaggageConfigurationTests { } - @Configuration + @Configuration(proxyBeanMethods = false) static class OldCorrelationFieldsForLogScrapingConfiguration { @Bean @@ -195,7 +195,7 @@ public class TraceBaggageConfigurationTests { } - @Configuration + @Configuration(proxyBeanMethods = false) static class CustomBaggageConfiguration { @Bean diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationCustomizersTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/autoconfig/TraceBraveAutoConfigurationCustomizersTests.java similarity index 84% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationCustomizersTests.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/autoconfig/TraceBraveAutoConfigurationCustomizersTests.java index 2b4fdf7a1..2044d63bf 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationCustomizersTests.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/autoconfig/TraceBraveAutoConfigurationCustomizersTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.autoconfig; +package org.springframework.cloud.sleuth.brave.autoconfig; import brave.TracingCustomizer; import brave.baggage.BaggagePropagationCustomizer; @@ -29,21 +29,22 @@ import org.junit.jupiter.api.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.messaging.TraceMessagingAutoConfiguration; -import org.springframework.cloud.sleuth.instrument.rpc.TraceRpcAutoConfiguration; -import org.springframework.cloud.sleuth.instrument.web.TraceHttpAutoConfiguration; +import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.cloud.sleuth.brave.instrument.messaging.TraceMessagingAutoConfiguration; +import org.springframework.cloud.sleuth.brave.instrument.rpc.TraceRpcAutoConfiguration; +import org.springframework.cloud.sleuth.brave.instrument.web.TraceHttpAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.support.MessageHeaderAccessor; import static org.assertj.core.api.BDDAssertions.then; -public class TraceAutoConfigurationCustomizersTests { +public class TraceBraveAutoConfigurationCustomizersTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(TraceAutoConfiguration.class, TraceHttpAutoConfiguration.class, - TraceRpcAutoConfiguration.class, TraceMessagingAutoConfiguration.class, - FakeSpringMessagingAutoConfiguration.class)) + .withConfiguration(AutoConfigurations.of(TraceAutoConfiguration.class, TraceBraveAutoConfiguration.class, + TraceHttpAutoConfiguration.class, TraceRpcAutoConfiguration.class, + TraceMessagingAutoConfiguration.class, FakeSpringMessagingAutoConfiguration.class)) .withUserConfiguration(Customizers.class); @Test @@ -78,7 +79,7 @@ public class TraceAutoConfigurationCustomizersTests { } // SQS has a dependency on the getter and this is better than exposing things public - @Configuration + @Configuration(proxyBeanMethods = false) static class FakeSpringMessagingAutoConfiguration { @Bean @@ -88,7 +89,7 @@ public class TraceAutoConfigurationCustomizersTests { } - @Configuration + @Configuration(proxyBeanMethods = false) static class Customizers { boolean tracingCustomizerApplied; diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationPropagationCustomizationTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/autoconfig/TraceBraveAutoConfigurationPropagationCustomizationTests.java similarity index 80% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationPropagationCustomizationTests.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/autoconfig/TraceBraveAutoConfigurationPropagationCustomizationTests.java index 0ed193699..4a262ad7c 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationPropagationCustomizationTests.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/autoconfig/TraceBraveAutoConfigurationPropagationCustomizationTests.java @@ -14,9 +14,10 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.autoconfig; +package org.springframework.cloud.sleuth.brave.autoconfig; import brave.baggage.BaggagePropagation; +import brave.propagation.B3Propagation; import brave.propagation.B3SinglePropagation; import brave.propagation.Propagation; import org.assertj.core.api.BDDAssertions; @@ -24,35 +25,36 @@ import org.junit.jupiter.api.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; -public class TraceAutoConfigurationPropagationCustomizationTests { +public class TraceBraveAutoConfigurationPropagationCustomizationTests { + + private static final Propagation.Factory B3_FACTORY = B3Propagation.newFactoryBuilder() + .injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build(); private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(TraceAutoConfiguration.class)); + .withConfiguration(AutoConfigurations.of(TraceAutoConfiguration.class, TraceBraveAutoConfiguration.class)); @Test public void stillCreatesDefault() { this.contextRunner.run((context) -> { - BDDAssertions.then(context.getBean(Propagation.Factory.class)) - .isEqualTo(TraceBaggageConfiguration.B3_FACTORY); + BDDAssertions.then(context.getBean(Propagation.Factory.class)).isEqualTo(B3_FACTORY); }); } @Test public void allowsCustomization() { this.contextRunner.withPropertyValues("spring.sleuth.baggage.remote-fields=country-code").run((context) -> { - BDDAssertions.then(context.getBean(Propagation.Factory.class)).extracting("delegate") - .isEqualTo(TraceBaggageConfiguration.B3_FACTORY); + BDDAssertions.then(context.getBean(Propagation.Factory.class)).extracting("delegate").isEqualTo(B3_FACTORY); }); } @Test public void defaultValueUsedWhenApplicationNameNotSet() { this.contextRunner.withPropertyValues("spring.application.name=").run((context) -> { - BDDAssertions.then(context.getBean(Propagation.Factory.class)) - .isEqualTo(TraceBaggageConfiguration.B3_FACTORY); + BDDAssertions.then(context.getBean(Propagation.Factory.class)).isEqualTo(B3_FACTORY); }); } @@ -64,7 +66,7 @@ public class TraceAutoConfigurationPropagationCustomizationTests { .isSameAs(B3SinglePropagation.FACTORY)); } - @Configuration + @Configuration(proxyBeanMethods = false) static class CustomPropagationFactoryBuilderConfig { @Bean diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/autoconfig/TraceBraveAutoConfigurationTests.java similarity index 86% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationTests.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/autoconfig/TraceBraveAutoConfigurationTests.java index bdb5df4e0..06bf42cff 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationTests.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/autoconfig/TraceBraveAutoConfigurationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.autoconfig; +package org.springframework.cloud.sleuth.brave.autoconfig; import java.util.List; @@ -39,18 +39,19 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.cloud.sleuth.brave.sampler.SamplerAutoConfigurationTests; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -public class TraceAutoConfigurationTests { +public class TraceBraveAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(TraceAutoConfiguration.class)); + .withConfiguration(AutoConfigurations.of(TraceAutoConfiguration.class, TraceBraveAutoConfiguration.class)); /** - * Duplicates - * {@link org.springframework.cloud.sleuth.sampler.SamplerAutoConfigurationTests} - * intentionally, to ensure configuration condition bugs do not exist. + * Duplicates {@link SamplerAutoConfigurationTests} intentionally, to ensure + * configuration condition bugs do not exist. */ @Test void should_use_NEVER_SAMPLER_when_only_logging() { @@ -61,9 +62,8 @@ public class TraceAutoConfigurationTests { } /** - * Duplicates - * {@link org.springframework.cloud.sleuth.sampler.SamplerAutoConfigurationTests} - * intentionally, to ensure configuration condition bugs do not exist. + * Duplicates {@link SamplerAutoConfigurationTests} intentionally, to ensure + * configuration condition bugs do not exist. */ @Test void should_use_RateLimitedSampler_withSpanHandler() { @@ -74,9 +74,8 @@ public class TraceAutoConfigurationTests { } /** - * Duplicates - * {@link org.springframework.cloud.sleuth.sampler.SamplerAutoConfigurationTests} - * intentionally, to ensure configuration condition bugs do not exist. + * Duplicates {@link SamplerAutoConfigurationTests} intentionally, to ensure + * configuration condition bugs do not exist. */ @Test void should_override_sampler() { @@ -131,7 +130,7 @@ public class TraceAutoConfigurationTests { .isSameAs(B3SinglePropagation.FACTORY))); } - @Configuration + @Configuration(proxyBeanMethods = false) static class Baggage { List fields; @@ -147,7 +146,7 @@ public class TraceAutoConfigurationTests { } - @Configuration + @Configuration(proxyBeanMethods = false) static class WithBaggageBeans { @Bean @@ -162,7 +161,7 @@ public class TraceAutoConfigurationTests { } - @Configuration + @Configuration(proxyBeanMethods = false) static class WithSpanHandler { @Bean @@ -177,7 +176,7 @@ public class TraceAutoConfigurationTests { } - @Configuration + @Configuration(proxyBeanMethods = false) static class WithSampler { @Bean @@ -187,7 +186,7 @@ public class TraceAutoConfigurationTests { } - @Configuration + @Configuration(proxyBeanMethods = false) static class WithLocalKeys { @Bean @@ -197,7 +196,7 @@ public class TraceAutoConfigurationTests { } - @Configuration + @Configuration(proxyBeanMethods = false) static class WithBaggagePropagationFactoryBuilderBean { @Bean diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationWithDisabledSleuthTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/autoconfig/TraceBraveAutoConfigurationWithDisabledSleuthTests.java similarity index 87% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationWithDisabledSleuthTests.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/autoconfig/TraceBraveAutoConfigurationWithDisabledSleuthTests.java index 4e51f84c5..1740b6e76 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationWithDisabledSleuthTests.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/autoconfig/TraceBraveAutoConfigurationWithDisabledSleuthTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.autoconfig; +package org.springframework.cloud.sleuth.brave.autoconfig; import brave.Tracing; import org.apache.commons.logging.Log; @@ -38,12 +38,12 @@ import static org.assertj.core.api.Assertions.assertThat; // WebEnvironment.NONE will not read a Yaml profile webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { "spring.config.use-legacy-processing=true" }, - classes = TraceAutoConfigurationWithDisabledSleuthTests.Config.class) + classes = TraceBraveAutoConfigurationWithDisabledSleuthTests.Config.class) @ActiveProfiles("disabled") @ExtendWith(OutputCaptureExtension.class) -public class TraceAutoConfigurationWithDisabledSleuthTests { +public class TraceBraveAutoConfigurationWithDisabledSleuthTests { - private static final Log log = LogFactory.getLog(TraceAutoConfigurationWithDisabledSleuthTests.class); + private static final Log log = LogFactory.getLog(TraceBraveAutoConfigurationWithDisabledSleuthTests.class); @Autowired(required = false) Tracing tracing; @@ -71,7 +71,7 @@ public class TraceAutoConfigurationWithDisabledSleuthTests { } @EnableAutoConfiguration - @Configuration + @Configuration(proxyBeanMethods = false) static class Config { } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/baggage/CorrelationScopeDecoratorTest.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/baggage/CorrelationScopeDecoratorTest.java similarity index 98% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/baggage/CorrelationScopeDecoratorTest.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/baggage/CorrelationScopeDecoratorTest.java index 8746a9a37..add616b43 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/baggage/CorrelationScopeDecoratorTest.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/baggage/CorrelationScopeDecoratorTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.baggage; +package org.springframework.cloud.sleuth.brave.baggage; import brave.Span; import brave.Tracer; diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/KafkaFactoryBeanPostProcessorTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/KafkaFactoryBeanPostProcessorTests.java similarity index 98% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/KafkaFactoryBeanPostProcessorTests.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/KafkaFactoryBeanPostProcessorTests.java index 43bed8da9..368b07d45 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/KafkaFactoryBeanPostProcessorTests.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/KafkaFactoryBeanPostProcessorTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.messaging; +package org.springframework.cloud.sleuth.brave.instrument.messaging; import java.util.ArrayList; import java.util.List; diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/SleuthKafkaStreamsConfigurationIntegrationTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/SleuthKafkaStreamsConfigurationIntegrationTests.java similarity index 91% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/SleuthKafkaStreamsConfigurationIntegrationTests.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/SleuthKafkaStreamsConfigurationIntegrationTests.java index be92a559b..b9cbacccb 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/SleuthKafkaStreamsConfigurationIntegrationTests.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/SleuthKafkaStreamsConfigurationIntegrationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.messaging; +package org.springframework.cloud.sleuth.brave.instrument.messaging; import javax.annotation.PostConstruct; @@ -33,6 +33,7 @@ import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.OutputCaptureExtension; import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.cloud.sleuth.brave.autoconfig.TraceBraveAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.kafka.config.StreamsBuilderFactoryBean; @@ -46,8 +47,8 @@ import static org.mockito.Mockito.verify; class SleuthKafkaStreamsConfigurationIntegrationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration( - AutoConfigurations.of(TraceAutoConfiguration.class, SleuthKafkaStreamsConfiguration.class)) + .withConfiguration(AutoConfigurations.of(TraceAutoConfiguration.class, TraceBraveAutoConfiguration.class, + SleuthKafkaStreamsConfiguration.class)) .withUserConfiguration(UserConfig.class); @Test @@ -91,7 +92,7 @@ class SleuthKafkaStreamsConfigurationIntegrationTests { assertThat(output).doesNotContain("is not eligible for getting processed by all BeanPostProcessors"); } - @Configuration + @Configuration(proxyBeanMethods = false) static class UserConfig { static StreamsBuilderFactoryBean streamsBuilderFactoryBean; @@ -104,7 +105,7 @@ class SleuthKafkaStreamsConfigurationIntegrationTests { } - @Configuration + @Configuration(proxyBeanMethods = false) static class EagerInitializationConfig { @Bean diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessagingAutoConfigurationIntegrationTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TraceMessagingAutoConfigurationIntegrationTests.java similarity index 95% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessagingAutoConfigurationIntegrationTests.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TraceMessagingAutoConfigurationIntegrationTests.java index d918f88a5..d462abc2c 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessagingAutoConfigurationIntegrationTests.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TraceMessagingAutoConfigurationIntegrationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.messaging; +package org.springframework.cloud.sleuth.brave.instrument.messaging; import brave.handler.SpanHandler; import brave.messaging.MessagingRequest; @@ -49,7 +49,7 @@ public class TraceMessagingAutoConfigurationIntegrationTests { } @EnableAutoConfiguration - @Configuration + @Configuration(proxyBeanMethods = false) public static class Config { @Bean diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/mongodb/TraceMongoDbAutoConfigurationTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/mongodb/TraceMongoDbAutoConfigurationTests.java similarity index 96% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/mongodb/TraceMongoDbAutoConfigurationTests.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/mongodb/TraceMongoDbAutoConfigurationTests.java index fa992af87..277e5e900 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/mongodb/TraceMongoDbAutoConfigurationTests.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/mongodb/TraceMongoDbAutoConfigurationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.mongodb; +package org.springframework.cloud.sleuth.brave.instrument.mongodb; import brave.Tracing; import brave.handler.MutableSpan; @@ -47,7 +47,7 @@ class TraceMongoDbAutoConfigurationTests { then(span.remoteServiceName()).contains("mongodb"); } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration static class TestTraceMongoDbAutoConfiguration { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/opentracing/OpenTracingTest.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/opentracing/OpenTracingTest.java similarity index 98% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/opentracing/OpenTracingTest.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/opentracing/OpenTracingTest.java index 98b25a1d5..c7625e628 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/opentracing/OpenTracingTest.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/opentracing/OpenTracingTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.opentracing; +package org.springframework.cloud.sleuth.brave.instrument.opentracing; import java.util.LinkedHashMap; import java.util.Map; @@ -249,7 +249,7 @@ public class OpenTracingTest { this.spans.clear(); } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration static class Config { diff --git a/tests/spring-cloud-sleuth-instrumentation-rpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfigurationIntegrationTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/rpc/TraceRpcAutoConfigurationIntegrationTests.java similarity index 95% rename from tests/spring-cloud-sleuth-instrumentation-rpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfigurationIntegrationTests.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/rpc/TraceRpcAutoConfigurationIntegrationTests.java index 9dd9fba79..0926ea22c 100644 --- a/tests/spring-cloud-sleuth-instrumentation-rpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfigurationIntegrationTests.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/rpc/TraceRpcAutoConfigurationIntegrationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.rpc; +package org.springframework.cloud.sleuth.brave.instrument.rpc; import brave.handler.SpanHandler; import brave.rpc.RpcRequest; @@ -51,7 +51,7 @@ public class TraceRpcAutoConfigurationIntegrationTests { } @EnableAutoConfiguration - @Configuration + @Configuration(proxyBeanMethods = false) public static class Config { @Bean diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/CompositeHttpSamplerTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/CompositeHttpSamplerTests.java similarity index 97% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/CompositeHttpSamplerTests.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/CompositeHttpSamplerTests.java index 149f4712d..939b0c785 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/CompositeHttpSamplerTests.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/CompositeHttpSamplerTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web; +package org.springframework.cloud.sleuth.brave.instrument.web; import java.util.stream.Stream; diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/EndpointWithCyclicDependenciesTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/EndpointWithCyclicDependenciesTests.java similarity index 94% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/EndpointWithCyclicDependenciesTests.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/EndpointWithCyclicDependenciesTests.java index a998461e6..ce91a4c87 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/EndpointWithCyclicDependenciesTests.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/EndpointWithCyclicDependenciesTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web; +package org.springframework.cloud.sleuth.brave.instrument.web; import brave.http.HttpTracing; import org.junit.jupiter.api.Test; @@ -44,7 +44,7 @@ public class EndpointWithCyclicDependenciesTests { } @EnableAutoConfiguration - @Configuration + @Configuration(proxyBeanMethods = false) static class ClientConfig { @Bean diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipPatternSamplerTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/SkipPatternSamplerTests.java similarity index 97% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipPatternSamplerTests.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/SkipPatternSamplerTests.java index 535c5c18b..951811e06 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipPatternSamplerTests.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/SkipPatternSamplerTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web; +package org.springframework.cloud.sleuth.brave.instrument.web; import java.util.regex.Pattern; diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceHttpAutoConfigurationTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceHttpAutoConfigurationTests.java similarity index 73% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceHttpAutoConfigurationTests.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceHttpAutoConfigurationTests.java index 359c57094..0c661de3b 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceHttpAutoConfigurationTests.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceHttpAutoConfigurationTests.java @@ -14,22 +14,28 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web; +package org.springframework.cloud.sleuth.brave.instrument.web; import brave.http.HttpRequest; import brave.http.HttpRequestParser; import brave.http.HttpResponseParser; import brave.http.HttpTracing; import brave.sampler.SamplerFunction; -import brave.sampler.SamplerFunctions; import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.Test; +import org.mockito.BDDMockito; 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.cloud.sleuth.brave.autoconfig.TraceBraveAutoConfiguration; +import org.springframework.cloud.sleuth.instrument.web.HttpClientRequestParser; +import org.springframework.cloud.sleuth.instrument.web.HttpClientResponseParser; +import org.springframework.cloud.sleuth.instrument.web.HttpClientSampler; +import org.springframework.cloud.sleuth.instrument.web.HttpServerRequestParser; +import org.springframework.cloud.sleuth.instrument.web.HttpServerResponseParser; +import org.springframework.cloud.sleuth.instrument.web.HttpServerSampler; +import org.springframework.cloud.sleuth.instrument.web.SkipPatternConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.GenericApplicationContext; @@ -43,7 +49,7 @@ public class TraceHttpAutoConfigurationTests { contextRunner().run((context) -> { SamplerFunction clientSampler = context.getBean(HttpTracing.class).clientRequestSampler(); - then(clientSampler).isSameAs(SamplerFunctions.deferDecision()); + then(clientSampler.trySample(mockHttpRequestForPath("foo"))).isNull(); }); } @@ -52,25 +58,35 @@ public class TraceHttpAutoConfigurationTests { contextRunner().withPropertyValues("spring.sleuth.web.client.skip-pattern=foo.*|bar.*").run((context) -> { SamplerFunction clientSampler = context.getBean(HttpTracing.class).clientRequestSampler(); - then(clientSampler).isInstanceOf(SkipPatternHttpClientSampler.class); + then(clientSampler.trySample(mockHttpRequestForPath("foo"))).isFalse(); + then(clientSampler.trySample(mockHttpRequestForPath("bar"))).isFalse(); + then(clientSampler.trySample(mockHttpRequestForPath("baz"))).isNull(); }); } + private HttpRequest mockHttpRequestForPath(String path) { + HttpRequest httpRequest = BDDMockito.mock(HttpRequest.class); + BDDMockito.given(httpRequest.path()).willReturn(path); + return httpRequest; + } + @Test public void configuresUserProvidedHttpClientSampler() { contextRunner().withUserConfiguration(HttpClientSamplerConfig.class).run((context) -> { SamplerFunction clientSampler = context.getBean(HttpTracing.class).clientRequestSampler(); - then(clientSampler).isSameAs(HttpClientSamplerConfig.INSTANCE); + then(clientSampler.trySample(mockHttpRequestForPath("foo"))).isNull(); }); } @Test public void defaultsServerSamplerToSkipPattern() { - contextRunner().run((context) -> { + contextRunner().withPropertyValues("spring.sleuth.web.skip-pattern=foo.*|bar.*").run((context) -> { SamplerFunction serverSampler = context.getBean(HttpTracing.class).serverRequestSampler(); - then(serverSampler).isInstanceOf(SkipPatternHttpServerSampler.class); + then(serverSampler.trySample(mockHttpRequestForPath("foo"))).isFalse(); + then(serverSampler.trySample(mockHttpRequestForPath("bar"))).isFalse(); + then(serverSampler.trySample(mockHttpRequestForPath("baz"))).isNull(); }); } @@ -79,29 +95,10 @@ public class TraceHttpAutoConfigurationTests { contextRunner().withPropertyValues("spring.sleuth.web.skip-pattern").run((context) -> { SamplerFunction clientSampler = context.getBean(HttpTracing.class).serverRequestSampler(); - then(clientSampler).isSameAs(SamplerFunctions.deferDecision()); + then(clientSampler.trySample(mockHttpRequestForPath("foo"))).isNull(); }); } - @Test - public void wrapsUserProvidedHttpServerSampler() { - contextRunner().withUserConfiguration(HttpServerSamplerConfig.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); - }; - } - @Test public void defaultHttpClientParser() { contextRunner().run((context) -> { @@ -176,37 +173,38 @@ public class TraceHttpAutoConfigurationTests { } private ApplicationContextRunner contextRunner(String... propertyValues) { - return new ApplicationContextRunner().withPropertyValues(propertyValues).withConfiguration(AutoConfigurations - .of(TraceAutoConfiguration.class, TraceHttpAutoConfiguration.class, SkipPatternConfiguration.class)); + return new ApplicationContextRunner().withPropertyValues(propertyValues).withConfiguration( + AutoConfigurations.of(TraceAutoConfiguration.class, TraceBraveAutoConfiguration.class, + TraceHttpAutoConfiguration.class, SkipPatternConfiguration.class)); } } -@Configuration +@Configuration(proxyBeanMethods = false) class HttpClientSamplerConfig { - static final SamplerFunction INSTANCE = request -> null; + static final org.springframework.cloud.sleuth.api.SamplerFunction INSTANCE = request -> null; @Bean(HttpClientSampler.NAME) - SamplerFunction sleuthHttpClientSampler() { + org.springframework.cloud.sleuth.api.SamplerFunction sleuthHttpClientSampler() { return INSTANCE; } } -@Configuration +@Configuration(proxyBeanMethods = false) class HttpServerSamplerConfig { - static final SamplerFunction INSTANCE = request -> null; + static final org.springframework.cloud.sleuth.api.SamplerFunction INSTANCE = request -> null; @Bean(HttpServerSampler.NAME) - SamplerFunction sleuthHttpServerSampler() { + org.springframework.cloud.sleuth.api.SamplerFunction sleuthHttpServerSampler() { return INSTANCE; } } -@Configuration +@Configuration(proxyBeanMethods = false) class HttpClientParserConfig { static final HttpRequestParser REQUEST_PARSER = (r, c, s) -> { @@ -226,7 +224,7 @@ class HttpClientParserConfig { } -@Configuration +@Configuration(proxyBeanMethods = false) class HttpServerParserConfig { static final HttpRequestParser REQUEST_PARSER = (r, c, s) -> { @@ -246,7 +244,7 @@ class HttpServerParserConfig { } -@Configuration +@Configuration(proxyBeanMethods = false) class HttpParserConfig { static final HttpRequestParser REQUEST_PARSER = (r, c, s) -> { diff --git a/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/WebClientTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/WebClientTests.java new file mode 100644 index 000000000..5c4a9b5c0 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/WebClientTests.java @@ -0,0 +1,197 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web.client; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Future; + +import brave.Span; +import brave.Tracer; +import brave.baggage.BaggagePropagation; +import brave.handler.SpanHandler; +import brave.propagation.B3Propagation; +import brave.sampler.Sampler; +import brave.test.TestSpanHandler; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.concurrent.FutureCallback; +import org.apache.http.impl.client.BasicResponseHandler; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; +import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.cloud.gateway.config.GatewayAutoConfiguration; +import org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration; +import org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpHeaders; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import static brave.Span.Kind.CLIENT; +import static brave.propagation.B3Propagation.Format.SINGLE_NO_PARENT; +import static org.assertj.core.api.BDDAssertions.then; + +@SpringBootTest(classes = WebClientTests.TestConfiguration.class, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { "spring.application.name=fooservice", "spring.sleuth.web.client.skip-pattern=/skip.*" }) +@DirtiesContext +public class WebClientTests { + + @Autowired + HttpClientBuilder httpClientBuilder; // #845 + + @Autowired + HttpAsyncClientBuilder httpAsyncClientBuilder; // #845 + + @Autowired + TestSpanHandler spans; + + @Autowired + Tracer tracer; + + @LocalServerPort + int port; + + @Autowired + FooController fooController; + + @AfterEach + @BeforeEach + public void close() { + this.spans.clear(); + this.fooController.clear(); + } + + @Test + @SuppressWarnings("unchecked") + public void shouldAttachTraceIdWhenCallingAnotherServiceForHttpClient() throws Exception { + Span span = this.tracer.nextSpan().name("foo").start(); + + try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) { + String response = this.httpClientBuilder.build().execute(new HttpGet("http://localhost:" + this.port), + new BasicResponseHandler()); + + then(response).isNotEmpty(); + } + + then(this.tracer.currentSpan()).isNull(); + then(this.spans).isNotEmpty().extracting("traceId", String.class).containsOnly(span.context().traceIdString()); + then(this.spans).extracting("kind.name").contains("CLIENT"); + } + + @Test + @SuppressWarnings("unchecked") + public void shouldAttachTraceIdWhenCallingAnotherServiceForAsyncHttpClient() throws Exception { + Span span = this.tracer.nextSpan().name("foo").start(); + + CloseableHttpAsyncClient client = this.httpAsyncClientBuilder.build(); + try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) { + client.start(); + Future future = client.execute(new HttpGet("http://localhost:" + this.port), + new FutureCallback() { + @Override + public void completed(HttpResponse result) { + + } + + @Override + public void failed(Exception ex) { + + } + + @Override + public void cancelled() { + + } + }); + then(future.get()).isNotNull(); + } + finally { + client.close(); + } + + then(this.tracer.currentSpan()).isNull(); + then(this.spans).isNotEmpty().extracting("traceId", String.class).containsOnly(span.context().traceIdString()); + then(this.spans).extracting("kind.name").contains("CLIENT"); + } + + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration(exclude = { TraceWebServletAutoConfiguration.class, + GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class }) + public static class TestConfiguration { + + @Bean + BaggagePropagation.FactoryBuilder baggagePropagationFactoryBuilder() { + // Use b3 single format as it is less verbose + return BaggagePropagation.newFactoryBuilder( + B3Propagation.newFactoryBuilder().injectFormat(CLIENT, SINGLE_NO_PARENT).build()); + } + + @Bean + FooController fooController() { + return new FooController(); + } + + @Bean + Sampler testSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + SpanHandler testSpanHandler() { + return new TestSpanHandler(); + } + + } + + @RestController + public static class FooController { + + Span span; + + @RequestMapping("/") + public Map home(@RequestHeader HttpHeaders headers) { + Map map = new HashMap<>(); + for (String key : headers.keySet()) { + map.put(key, headers.getFirst(key)); + } + return map; + } + + public Span getSpan() { + return this.span; + } + + public void clear() { + this.span = null; + } + + } + +} diff --git a/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/propagation/TraceBravePropagationAutoConfigurationTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/propagation/TraceBravePropagationAutoConfigurationTests.java new file mode 100644 index 000000000..272820592 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/propagation/TraceBravePropagationAutoConfigurationTests.java @@ -0,0 +1,165 @@ +/* + * Copyright 2013-2020 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.brave.propagation; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import brave.internal.codec.HexCodec; +import brave.internal.propagation.StringPropagationAdapter; +import brave.propagation.Propagation; +import brave.propagation.TraceContext; +import brave.propagation.TraceContextOrSamplingFlags; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; + +class TraceBravePropagationAutoConfigurationTests { + + @Test + void should_start_a_composite_propagation_factory_supplier_with_b3_as_default() { + ApplicationContextRunner runner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(TraceBravePropagationAutoConfiguration.class)) + .withUserConfiguration(Config.class); + + runner.run(context -> { + assertThat(context).hasNotFailed(); + Propagation propagator = context.getBean(CompositePropagationFactorySupplier.class).get().get(); + assertThat(propagator.keys()).contains("X-B3-TraceId"); + }); + } + + @Test + void should_start_a_composite_propagation_factory_supplier_with_a_single_propagation_type() { + ApplicationContextRunner runner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(TraceBravePropagationAutoConfiguration.class)) + .withUserConfiguration(Config.class).withPropertyValues("spring.sleuth.propagation.type=w3c"); + + runner.run(context -> { + assertThat(context).hasNotFailed(); + Propagation propagator = context.getBean(CompositePropagationFactorySupplier.class).get().get(); + assertThat(propagator.keys()).doesNotContain("X-B3-TraceId").contains("traceparent"); + }); + } + + @Test + void should_start_a_composite_propagation_factory_supplier_with_multiple_propagation_types() { + ApplicationContextRunner runner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(TraceBravePropagationAutoConfiguration.class)) + .withUserConfiguration(Config.class).withPropertyValues("spring.sleuth.propagation.type=b3,w3c"); + + runner.run(context -> { + assertThat(context).hasNotFailed(); + Propagation propagator = context.getBean(CompositePropagationFactorySupplier.class).get().get(); + assertThat(propagator.keys()).contains("X-B3-TraceId", "traceparent"); + }); + } + + @Test + void should_start_a_composite_propagation_factory_supplier_with_custom_propagation_types() { + ApplicationContextRunner runner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(TraceBravePropagationAutoConfiguration.class)) + .withUserConfiguration(CustomPropagatorConfig.class) + .withPropertyValues("spring.sleuth.propagation.type=custom"); + + runner.run(context -> { + assertThat(context).hasNotFailed().doesNotHaveBean(CompositePropagationFactorySupplier.class); + Propagation propagator = context.getBean(PropagationFactorySupplier.class).get().get(); + assertThat(propagator.keys()).contains("myCustomTraceId", "myCustomSpanId"); + }); + } + + @Test + void should_inject_and_extract_from_custom_propagator() { + CustomPropagator customPropagator = new CustomPropagator(); + Map carrier = carrierWithTracingData(); + + // Extraction + TraceContextOrSamplingFlags extract = customPropagator + .extractor((Propagation.Getter, String>) Map::get).extract(carrier); + assertThat(extract.context().traceIdString()).isEqualTo("ff00000000000041"); + assertThat(extract.context().spanIdString()).isEqualTo("ff00000000000041"); + + // Injection + Map emptyMap = new HashMap<>(); + customPropagator.injector((Propagation.Setter, String>) Map::put).inject(extract.context(), + emptyMap); + assertThat(emptyMap).containsEntry("myCustomTraceId", "ff00000000000041").containsEntry("myCustomSpanId", + "ff00000000000041"); + } + + private Map carrierWithTracingData() { + Map carrier = new HashMap<>(); + carrier.put("myCustomTraceId", "ff00000000000041"); + carrier.put("myCustomSpanId", "ff00000000000041"); + return carrier; + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + } + + @Configuration(proxyBeanMethods = false) + static class CustomPropagatorConfig { + + @Bean + PropagationFactorySupplier myCustomPropagator() { + return CustomPropagator::new; + } + + } + +} + +// tag::custom_propagator[] +class CustomPropagator extends Propagation.Factory implements Propagation { + + @Override + public List keys() { + return Arrays.asList("myCustomTraceId", "myCustomSpanId"); + } + + @Override + public TraceContext.Injector injector(Setter setter) { + return (traceContext, request) -> { + setter.put(request, "myCustomTraceId", traceContext.traceIdString()); + setter.put(request, "myCustomSpanId", traceContext.spanIdString()); + }; + } + + @Override + public TraceContext.Extractor extractor(Getter getter) { + return request -> TraceContextOrSamplingFlags.create(TraceContext.newBuilder() + .traceId(HexCodec.lowerHexToUnsignedLong(getter.get(request, "myCustomTraceId"))) + .spanId(HexCodec.lowerHexToUnsignedLong(getter.get(request, "myCustomSpanId"))).build()); + } + + @Override + public Propagation create(KeyFactory keyFactory) { + return StringPropagationAdapter.create(this, keyFactory); + } + +} +// end::custom_propagator[] diff --git a/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/propagation/W3CPropagationTest.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/propagation/W3CPropagationTest.java new file mode 100644 index 000000000..96e46febf --- /dev/null +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/propagation/W3CPropagationTest.java @@ -0,0 +1,278 @@ +/* + * Copyright 2013-2020 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.brave.propagation; + +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import brave.propagation.Propagation; +import brave.propagation.TraceContext; +import brave.propagation.TraceContextOrSamplingFlags; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; +import static org.springframework.cloud.sleuth.brave.propagation.W3CPropagation.TRACE_PARENT; + +/** + * Test taken from OpenTelemetry. + */ +class W3CPropagationTest { + + private static final String TRACE_STATE = "tracestate"; + + private static final String TRACE_ID_BASE16 = "ff000000000000000000000000000041"; + + private static final String SPAN_ID_BASE16 = "ff00000000000041"; + + private static final boolean SAMPLED_TRACE_OPTIONS = true; + + private static final String TRACEPARENT_HEADER_SAMPLED = "00-" + TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-01"; + + private static final String TRACEPARENT_HEADER_NOT_SAMPLED = "00-" + TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-00"; + + private static final Propagation.Getter, String> getter = Map::get; + + private static final String TRACESTATE_NOT_DEFAULT_ENCODING_WITH_SPACES = "bar=baz , foo=bar"; + + private final W3CPropagation w3CPropagation = W3CPropagation.getInstance(); + + @Test + void inject_NullCarrierUsage() { + final Map carrier = new LinkedHashMap<>(); + TraceContext traceContext = sampledTraceContext().build(); + w3CPropagation.injector((ignored, key, value) -> carrier.put(key, value)).inject(traceContext, null); + assertThat(carrier).containsExactly(entry(TRACE_PARENT, TRACEPARENT_HEADER_SAMPLED)); + } + + @NotNull + private TraceContext.Builder sampledTraceContext() { + return TraceContext.newBuilder().sampled(SAMPLED_TRACE_OPTIONS) + .spanId(BigendianEncoding.longFromBase16String("ff00000000000041")) + .traceIdHigh(BigendianEncoding.longFromBase16String("ff00000000000000")) + .traceId(BigendianEncoding.longFromBase16String("0000000000000041")); + } + + @Test + void inject_SampledContext() { + final Map carrier = new LinkedHashMap<>(); + TraceContext traceContext = sampledTraceContext().build(); + w3CPropagation.injector((ignored, key, value) -> carrier.put(key, value)).inject(traceContext, carrier); + assertThat(carrier).containsExactly(entry(TRACE_PARENT, TRACEPARENT_HEADER_SAMPLED)); + } + + @Test + void inject_NotSampledContext() { + final Map carrier = new LinkedHashMap<>(); + TraceContext traceContext = notSampledTraceContext().build(); + w3CPropagation.injector((ignored, key, value) -> carrier.put(key, value)).inject(traceContext, carrier); + assertThat(carrier).containsExactly(entry(TRACE_PARENT, TRACEPARENT_HEADER_NOT_SAMPLED)); + } + + @Test + void extract_Nothing() { + // Context remains untouched. + assertThat(w3CPropagation.extractor(getter).extract(Collections.emptyMap())) + .isSameAs(TraceContextOrSamplingFlags.EMPTY); + } + + @Test + void extract_SampledContext() { + Map carrier = new LinkedHashMap<>(); + carrier.put(TRACE_PARENT, TRACEPARENT_HEADER_SAMPLED); + assertThat(w3CPropagation.extractor(getter).extract(carrier).context()).isEqualTo(sharedTraceContext().build()); + } + + @Test + void extract_NullCarrier() { + Map carrier = new LinkedHashMap<>(); + carrier.put(TRACE_PARENT, TRACEPARENT_HEADER_SAMPLED); + assertThat(w3CPropagation.extractor((request, key) -> carrier.get(key)).extract(null).context()) + .isEqualTo(sharedTraceContext().build()); + } + + @Test + void extract_NotSampledContext() { + Map carrier = new LinkedHashMap<>(); + carrier.put(TRACE_PARENT, TRACEPARENT_HEADER_NOT_SAMPLED); + assertThat(w3CPropagation.extractor(getter).extract(carrier).context()) + .isEqualTo(notSampledTraceContext().shared(true).build()); + } + + @Test + void extract_NotSampledContext_NextVersion() { + Map carrier = new LinkedHashMap<>(); + carrier.put(TRACE_PARENT, "01-" + TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-00-02"); + assertThat(w3CPropagation.extractor(getter).extract(carrier).context()).isEqualTo(sharedTraceContext().build()); + } + + @Test + void extract_NotSampledContext_EmptyTraceState() { + Map carrier = new LinkedHashMap<>(); + carrier.put(TRACE_PARENT, TRACEPARENT_HEADER_NOT_SAMPLED); + carrier.put(TRACE_STATE, ""); + assertThat(w3CPropagation.extractor(getter).extract(carrier).context()) + .isEqualTo(notSampledTraceContext().shared(true).build()); + } + + @NotNull + private TraceContext.Builder notSampledTraceContext() { + return sampledTraceContext().sampled(false); + } + + @Test + void extract_NotSampledContext_TraceStateWithSpaces() { + Map carrier = new LinkedHashMap<>(); + carrier.put(TRACE_PARENT, TRACEPARENT_HEADER_NOT_SAMPLED); + carrier.put(TRACE_STATE, TRACESTATE_NOT_DEFAULT_ENCODING_WITH_SPACES); + assertThat(w3CPropagation.extractor(getter).extract(carrier).context()) + .isEqualTo(sharedTraceContext().sampled(false).build()); + } + + @Test + void extract_EmptyHeader() { + Map invalidHeaders = new LinkedHashMap<>(); + invalidHeaders.put(TRACE_PARENT, ""); + assertThat(w3CPropagation.extractor(getter).extract(invalidHeaders)) + .isSameAs(TraceContextOrSamplingFlags.EMPTY); + } + + @Test + void extract_InvalidTraceId() { + Map invalidHeaders = new LinkedHashMap<>(); + invalidHeaders.put(TRACE_PARENT, "00-" + "abcdefghijklmnopabcdefghijklmnop" + "-" + SPAN_ID_BASE16 + "-01"); + assertThat(w3CPropagation.extractor(getter).extract(invalidHeaders)) + .isSameAs(TraceContextOrSamplingFlags.EMPTY); + } + + @Test + void extract_InvalidTraceId_Size() { + Map invalidHeaders = new LinkedHashMap<>(); + invalidHeaders.put(TRACE_PARENT, "00-" + TRACE_ID_BASE16 + "00-" + SPAN_ID_BASE16 + "-01"); + assertThat(w3CPropagation.extractor(getter).extract(invalidHeaders)) + .isSameAs(TraceContextOrSamplingFlags.EMPTY); + } + + @Test + void extract_InvalidSpanId() { + Map invalidHeaders = new HashMap<>(); + invalidHeaders.put(TRACE_PARENT, "00-" + TRACE_ID_BASE16 + "-" + "abcdefghijklmnop" + "-01"); + assertThat(w3CPropagation.extractor(getter).extract(invalidHeaders)) + .isSameAs(TraceContextOrSamplingFlags.EMPTY); + } + + @Test + void extract_InvalidSpanId_Size() { + Map invalidHeaders = new HashMap<>(); + invalidHeaders.put(TRACE_PARENT, "00-" + TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "00-01"); + assertThat(w3CPropagation.extractor(getter).extract(invalidHeaders)) + .isSameAs(TraceContextOrSamplingFlags.EMPTY); + } + + @Test + void extract_InvalidTraceFlags() { + Map invalidHeaders = new HashMap<>(); + invalidHeaders.put(TRACE_PARENT, "00-" + TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-gh"); + assertThat(w3CPropagation.extractor(getter).extract(invalidHeaders)) + .isSameAs(TraceContextOrSamplingFlags.EMPTY); + } + + @Test + void extract_InvalidTraceFlags_Size() { + Map invalidHeaders = new HashMap<>(); + invalidHeaders.put(TRACE_PARENT, "00-" + TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-0100"); + assertThat(w3CPropagation.extractor(getter).extract(invalidHeaders)) + .isSameAs(TraceContextOrSamplingFlags.EMPTY); + } + + @Test + void extract_InvalidTracestate_EntriesDelimiter() { + Map invalidHeaders = new HashMap<>(); + invalidHeaders.put(TRACE_PARENT, "00-" + TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-01"); + invalidHeaders.put(TRACE_STATE, "foo=bar;test=test"); + assertThat(w3CPropagation.extractor(getter).extract(invalidHeaders).context()) + .isEqualTo(sharedTraceContext().build()); + } + + @NotNull + private TraceContext.Builder sharedTraceContext() { + return sampledTraceContext().shared(true); + } + + @Test + void extract_InvalidTracestate_KeyValueDelimiter() { + Map invalidHeaders = new HashMap<>(); + invalidHeaders.put(TRACE_PARENT, "00-" + TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-01"); + invalidHeaders.put(TRACE_STATE, "foo=bar,test-test"); + assertThat(w3CPropagation.extractor(getter).extract(invalidHeaders).context()) + .isEqualTo(sharedTraceContext().build()); + } + + @Test + void extract_InvalidTracestate_OneString() { + Map invalidHeaders = new HashMap<>(); + invalidHeaders.put(TRACE_PARENT, "00-" + TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-01"); + invalidHeaders.put(TRACE_STATE, "test-test"); + assertThat(w3CPropagation.extractor(getter).extract(invalidHeaders).context()) + .isEqualTo(sampledTraceContext().shared(true).build()); + } + + @Test + void extract_InvalidVersion_ff() { + Map invalidHeaders = new HashMap<>(); + invalidHeaders.put(TRACE_PARENT, "ff-" + TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-01"); + assertThat(w3CPropagation.extractor(getter).extract(invalidHeaders)) + .isSameAs(TraceContextOrSamplingFlags.EMPTY); + } + + @Test + void extract_InvalidTraceparent_extraTrailing() { + Map invalidHeaders = new HashMap<>(); + invalidHeaders.put(TRACE_PARENT, "00-" + TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-00-01"); + assertThat(w3CPropagation.extractor(getter).extract(invalidHeaders)) + .isSameAs(TraceContextOrSamplingFlags.EMPTY); + } + + @Test + void extract_ValidTraceparent_nextVersion_extraTrailing() { + Map invalidHeaders = new HashMap<>(); + invalidHeaders.put(TRACE_PARENT, "01-" + TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-00-01"); + assertThat(w3CPropagation.extractor(getter).extract(invalidHeaders).context()) + .isEqualTo(sharedTraceContext().build()); + } + + @Test + void fieldsList() { + assertThat(w3CPropagation.keys()).containsExactly(TRACE_PARENT, TRACE_STATE); + } + + @Test + void headerNames() { + assertThat(TRACE_PARENT).isEqualTo("traceparent"); + assertThat(TRACE_STATE).isEqualTo("tracestate"); + } + + @Test + void extract_emptyCarrier() { + Map emptyHeaders = new HashMap<>(); + assertThat(w3CPropagation.extractor(getter).extract(emptyHeaders)).isSameAs(TraceContextOrSamplingFlags.EMPTY); + } + +} diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/sampler/ProbabilityBasedSamplerTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/sampler/ProbabilityBasedSamplerTests.java similarity index 98% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/sampler/ProbabilityBasedSamplerTests.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/sampler/ProbabilityBasedSamplerTests.java index cdd032311..402f79994 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/sampler/ProbabilityBasedSamplerTests.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/sampler/ProbabilityBasedSamplerTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.sampler; +package org.springframework.cloud.sleuth.brave.sampler; import java.util.Random; diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/sampler/SamplerAutoConfigurationTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/sampler/SamplerAutoConfigurationTests.java similarity index 96% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/sampler/SamplerAutoConfigurationTests.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/sampler/SamplerAutoConfigurationTests.java index f1cbf6c6f..4fe5157b3 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/sampler/SamplerAutoConfigurationTests.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/brave/sampler/SamplerAutoConfigurationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.sampler; +package org.springframework.cloud.sleuth.brave.sampler; import brave.Tracing; import brave.TracingCustomizer; @@ -138,7 +138,7 @@ public class SamplerAutoConfigurationTests { BDDAssertions.then(sampler).isSameAs(Sampler.NEVER_SAMPLE); } - @Configuration + @Configuration(proxyBeanMethods = false) static class WithSpanHandler { @Bean @@ -153,7 +153,7 @@ public class SamplerAutoConfigurationTests { } - @Configuration + @Configuration(proxyBeanMethods = false) static class WithTracingCustomizer { @Bean @@ -163,7 +163,7 @@ public class SamplerAutoConfigurationTests { } - @Configuration + @Configuration(proxyBeanMethods = false) static class WithRefreshScope { @Bean diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java similarity index 70% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java index 878e10901..d826b4147 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java @@ -22,9 +22,9 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; -import brave.Span; -import brave.Tracer; import brave.Tracing; import brave.handler.MutableSpan; import brave.propagation.StrictCurrentTraceContext; @@ -37,6 +37,9 @@ import org.junit.jupiter.api.Test; import org.springframework.cloud.sleuth.SpanName; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.brave.bridge.BraveTracer; import org.springframework.cloud.sleuth.instrument.async.TraceCallable; import org.springframework.cloud.sleuth.instrument.async.TraceRunnable; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; @@ -47,7 +50,8 @@ import static org.assertj.core.api.BDDAssertions.then; /** * Test class to be embedded in the - * {@code docs/src/main/asciidoc/spring-cloud-sleuth.adoc} file. + * {@code docs/src/main/asciidoc/spring-cloud-sleuth.adoc} file. They use Sleuth's API + * with Brave as tracer implementation. * * @author Marcin Grzejszczak */ @@ -55,12 +59,14 @@ public class SpringCloudSleuthDocTests { TestSpanHandler spans = new TestSpanHandler(); - StrictCurrentTraceContext currentTraceContext = StrictCurrentTraceContext.create(); + StrictCurrentTraceContext braveCurrentTraceContext = StrictCurrentTraceContext.create(); - Tracing tracing = Tracing.newBuilder().currentTraceContext(this.currentTraceContext).sampler(Sampler.ALWAYS_SAMPLE) - .addSpanHandler(this.spans).build(); + Tracing tracing = Tracing.newBuilder().currentTraceContext(this.braveCurrentTraceContext) + .sampler(Sampler.ALWAYS_SAMPLE).addSpanHandler(this.spans).build(); - Tracer tracer = this.tracing.tracer(); + brave.Tracer braveTracer = this.tracing.tracer(); + + org.springframework.cloud.sleuth.api.Tracer tracer = BraveTracer.fromBrave(braveTracer); @BeforeEach public void setup() { @@ -70,7 +76,7 @@ public class SpringCloudSleuthDocTests { @AfterEach public void close() { this.tracing.close(); - this.currentTraceContext.close(); + this.braveCurrentTraceContext.close(); } @Test @@ -79,7 +85,7 @@ public class SpringCloudSleuthDocTests { SpanNamer spanNamer = new DefaultSpanNamer(); // tag::span_name_annotated_runnable_execution[] - Runnable runnable = new TraceRunnable(this.tracing, spanNamer, new TaxCountingRunnable()); + Runnable runnable = new TraceRunnable(this.tracer, spanNamer, new TaxCountingRunnable()); Future future = executorService.submit(runnable); // ... some additional logic ... future.get(); @@ -95,7 +101,7 @@ public class SpringCloudSleuthDocTests { SpanNamer spanNamer = new DefaultSpanNamer(); // tag::span_name_to_string_runnable_execution[] - Runnable runnable = new TraceRunnable(this.tracing, spanNamer, new Runnable() { + Runnable runnable = new TraceRunnable(this.tracer, spanNamer, new Runnable() { @Override public void run() { // perform logic @@ -124,18 +130,18 @@ public class SpringCloudSleuthDocTests { // Start a span. If there was a span present in this thread it will become // the `newSpan`'s parent. Span newSpan = this.tracer.nextSpan().name("calculateTax"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(newSpan.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(newSpan.start())) { // ... // You can tag a span newSpan.tag("taxValue", taxValue); // ... // You can log an event on a span - newSpan.annotate("taxCalculated"); + newSpan.event("taxCalculated"); } finally { - // Once done remember to finish the span. This will allow collecting - // the span to send it to Zipkin - newSpan.finish(); + // Once done remember to end the span. This will allow collecting + // the span to send it to a distributed tracing system e.g. Zipkin + newSpan.end(); } // end::manual_span_creation[] @@ -149,32 +155,24 @@ public class SpringCloudSleuthDocTests { public void should_continue_a_span_with_tracer() throws Exception { ExecutorService executorService = Executors.newSingleThreadExecutor(); String taxValue = "10"; - Span newSpan = this.tracer.nextSpan().name("calculateTax"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(newSpan.start())) { + // tag::manual_span_continuation[] + Span spanFromThreadX = this.tracer.nextSpan().name("calculateTax"); + try (Tracer.SpanInScope ws = this.tracer.withSpan(spanFromThreadX.start())) { executorService.submit(() -> { - // tag::manual_span_continuation[] - // let's assume that we're in a thread Y and we've received - // the `initialSpan` from thread X - Span continuedSpan = this.tracer.toSpan(newSpan.context()); - try { - // ... - // You can tag a span - continuedSpan.tag("taxValue", taxValue); - // ... - // You can log an event on a span - continuedSpan.annotate("taxCalculated"); - } - finally { - // Once done remember to flush the span. That means that - // it will get reported but the span itself is not yet finished - continuedSpan.flush(); - } - // end::manual_span_continuation[] + // Pass the span from thread X + Span continuedSpan = spanFromThreadX; + // ... + // You can tag a span + continuedSpan.tag("taxValue", taxValue); + // ... + // You can log an event on a span + continuedSpan.event("taxCalculated"); }).get(); } finally { - newSpan.finish(); + spanFromThreadX.end(); } + // end::manual_span_continuation[] BDDAssertions.then(spans).hasSize(1); BDDAssertions.then(spans.get(0).name()).isEqualTo("calculateTax"); @@ -195,21 +193,21 @@ public class SpringCloudSleuthDocTests { // the `initialSpan` from thread X. `initialSpan` will be the parent // of the `newSpan` Span newSpan = null; - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(initialSpan)) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(initialSpan)) { newSpan = this.tracer.nextSpan().name("calculateCommission"); // ... // You can tag a span newSpan.tag("commissionValue", commissionValue); // ... // You can log an event on a span - newSpan.annotate("commissionCalculated"); + newSpan.event("commissionCalculated"); } finally { - // Once done remember to finish the span. This will allow collecting - // the span to send it to Zipkin. The tags and events set on the + // Once done remember to end the span. This will allow collecting + // the span to send it to e.g. Zipkin. The tags and events set on the // newSpan will not be present on the parent if (newSpan != null) { - newSpan.finish(); + newSpan.end(); } } // end::manual_span_joining[] @@ -224,7 +222,8 @@ public class SpringCloudSleuthDocTests { } @Test - public void should_wrap_runnable_in_its_sleuth_representative() { + public void should_wrap_runnable_in_its_sleuth_representative() + throws InterruptedException, ExecutionException, TimeoutException { SpanNamer spanNamer = new DefaultSpanNamer(); // tag::trace_runnable[] Runnable runnable = new Runnable() { @@ -239,17 +238,20 @@ public class SpringCloudSleuthDocTests { } }; // Manual `TraceRunnable` creation with explicit "calculateTax" Span name - Runnable traceRunnable = new TraceRunnable(this.tracing, spanNamer, runnable, "calculateTax"); - // Wrapping `Runnable` with `Tracing`. That way the current span will be available - // in the thread of `Runnable` - Runnable traceRunnableFromTracer = this.tracing.currentTraceContext().wrap(runnable); + Runnable traceRunnable = new TraceRunnable(this.tracer, spanNamer, runnable, "calculateTax"); // end::trace_runnable[] - then(traceRunnable).isExactlyInstanceOf(TraceRunnable.class); + ExecutorService executorService = Executors.newSingleThreadExecutor(); + executorService.submit(traceRunnable).get(10, TimeUnit.MILLISECONDS); + Optional calculateTax = spans.spans().stream().filter(span -> span.name().equals("calculateTax")) + .findFirst(); + BDDAssertions.then(calculateTax).isPresent(); + executorService.shutdown(); } @Test - public void should_wrap_callable_in_its_sleuth_representative() { + public void should_wrap_callable_in_its_sleuth_representative() + throws InterruptedException, ExecutionException, TimeoutException { SpanNamer spanNamer = new DefaultSpanNamer(); // tag::trace_callable[] Callable callable = new Callable() { @@ -264,18 +266,23 @@ public class SpringCloudSleuthDocTests { } }; // Manual `TraceCallable` creation with explicit "calculateTax" Span name - Callable traceCallable = new TraceCallable<>(this.tracing, spanNamer, callable, "calculateTax"); - // Wrapping `Callable` with `Tracing`. That way the current span will be available - // in the thread of `Callable` - Callable traceCallableFromTracer = this.tracing.currentTraceContext().wrap(callable); + Callable traceCallable = new TraceCallable<>(tracer, spanNamer, callable, "calculateTax"); // end::trace_callable[] + + ExecutorService executorService = Executors.newSingleThreadExecutor(); + String result = executorService.submit(traceCallable).get(10, TimeUnit.MILLISECONDS); + BDDAssertions.then(result).isEqualTo("some logic"); + Optional calculateTax = spans.spans().stream().filter(span -> span.name().equals("calculateTax")) + .findFirst(); + BDDAssertions.then(calculateTax).isPresent(); + executorService.shutdown(); } private String someLogic() { return "some logic"; } - @Configuration + @Configuration(proxyBeanMethods = false) static class SamplingConfiguration { // tag::always_sampler[] diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/internal/LazyBeanTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/internal/LazyBeanTests.java similarity index 97% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/internal/LazyBeanTests.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/internal/LazyBeanTests.java index 9bc2a2581..c39352cf2 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/internal/LazyBeanTests.java +++ b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/internal/LazyBeanTests.java @@ -54,7 +54,7 @@ public class LazyBeanTests { then(provider.get()).isNull(); } - @Configuration + @Configuration(proxyBeanMethods = false) static class BasicConfig { @Bean diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/internal/SpanNameUtilTests.java b/spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/internal/SpanNameUtilTests.java similarity index 100% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/internal/SpanNameUtilTests.java rename to spring-cloud-sleuth-brave/src/test/java/org/springframework/cloud/sleuth/internal/SpanNameUtilTests.java diff --git a/spring-cloud-sleuth-core/src/test/resources/META-INF/spring.factories b/spring-cloud-sleuth-brave/src/test/resources/META-INF/spring.factories similarity index 100% rename from spring-cloud-sleuth-core/src/test/resources/META-INF/spring.factories rename to spring-cloud-sleuth-brave/src/test/resources/META-INF/spring.factories diff --git a/spring-cloud-sleuth-core/src/test/resources/application-baggage.yml b/spring-cloud-sleuth-brave/src/test/resources/application-baggage.yml similarity index 100% rename from spring-cloud-sleuth-core/src/test/resources/application-baggage.yml rename to spring-cloud-sleuth-brave/src/test/resources/application-baggage.yml diff --git a/spring-cloud-sleuth-brave/src/test/resources/application.yml b/spring-cloud-sleuth-brave/src/test/resources/application.yml new file mode 100644 index 000000000..8e8a5afaa --- /dev/null +++ b/spring-cloud-sleuth-brave/src/test/resources/application.yml @@ -0,0 +1,9 @@ +eureka.client.enabled: false + +spring.sleuth.scheduled.skipPattern: "^org.*TestBeanWithScheduledMethodToBeIgnored$" + +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE + +spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.cloud.gateway.config.GatewayAutoConfiguration, org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration, org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration diff --git a/spring-cloud-sleuth-brave/src/test/resources/beans/applicationContext.xml b/spring-cloud-sleuth-brave/src/test/resources/beans/applicationContext.xml new file mode 100644 index 000000000..f12e78e89 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/test/resources/beans/applicationContext.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-cloud-sleuth-core/src/test/resources/bootstrap-disabled.yml b/spring-cloud-sleuth-brave/src/test/resources/bootstrap-disabled.yml similarity index 100% rename from spring-cloud-sleuth-core/src/test/resources/bootstrap-disabled.yml rename to spring-cloud-sleuth-brave/src/test/resources/bootstrap-disabled.yml diff --git a/spring-cloud-sleuth-brave/src/test/resources/logback.xml b/spring-cloud-sleuth-brave/src/test/resources/logback.xml new file mode 100644 index 000000000..9f976f524 --- /dev/null +++ b/spring-cloud-sleuth-brave/src/test/resources/logback.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + diff --git a/spring-cloud-sleuth-core/pom.xml b/spring-cloud-sleuth-core/pom.xml index 78d21a354..9249b9fef 100644 --- a/spring-cloud-sleuth-core/pom.xml +++ b/spring-cloud-sleuth-core/pom.xml @@ -24,9 +24,6 @@ jar Spring Cloud Sleuth Core Spring Cloud Sleuth Core - - 2.2.0.RELEASE - org.springframework.cloud @@ -46,31 +43,16 @@ micrometer-core true - - org.springframework.boot - spring-boot-starter-webflux - true - io.projectreactor reactor-core true - - io.projectreactor.netty - reactor-netty-http - true - org.reactivestreams reactive-streams true - - org.springframework.boot - spring-boot-starter-websocket - true - org.springframework.boot spring-boot-configuration-processor @@ -82,27 +64,8 @@ true - org.springframework.cloud - spring-cloud-commons - - - org.springframework.cloud - spring-cloud-stream - true - - - org.springframework.cloud - spring-cloud-starter-gateway - true - - - org.springframework.cloud - spring-cloud-starter-circuitbreaker-resilience4j - true - - - org.springframework.cloud - spring-cloud-starter-openfeign + org.springframework.integration + spring-integration-core true @@ -110,37 +73,20 @@ spring-cloud-function-context true - - org.springframework.integration - spring-integration-core - true - - - org.springframework.amqp - spring-rabbit - true - - - org.springframework.kafka - spring-kafka - true - - - org.apache.kafka - kafka-streams - true - - - org.springframework.security.oauth - spring-security-oauth2 - ${spring-security-oauth2.version} - true - org.springframework.boot - spring-boot-starter-security + spring-boot-starter-websocket true + + org.springframework.cloud + spring-cloud-stream + true + + + org.springframework.cloud + spring-cloud-commons + org.springframework spring-context @@ -150,172 +96,35 @@ spring-cloud-context true - - org.springframework.cloud - spring-cloud-starter-loadbalancer - true - - - io.github.openfeign - feign-core - true - - - io.github.openfeign.form - feign-form-spring - true - io.reactivex rxjava true - - com.squareup.okhttp3 - okhttp - ${okhttp.version} - true - - - org.apache.httpcomponents - httpclient - true - io.github.openfeign feign-okhttp true - org.springframework.boot - spring-boot-starter-data-mongodb + org.springframework.cloud + spring-cloud-starter-openfeign + true + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + true + + + org.springframework.cloud + spring-cloud-starter-gateway true org.aspectj aspectjrt - - - io.zipkin.brave - brave - - - io.zipkin.reporter2 - * - - - io.zipkin.zipkin2 - * - - - - - io.zipkin.brave - brave-context-slf4j - - - io.zipkin.brave - brave-instrumentation-messaging - - - io.zipkin.brave - brave-instrumentation-rpc - - - io.zipkin.brave - brave-instrumentation-spring-web - - - io.zipkin.brave - brave-instrumentation-spring-rabbit - - - io.zipkin.brave - brave-instrumentation-kafka-clients - - - io.zipkin.brave - brave-instrumentation-kafka-streams - - - io.zipkin.brave - brave-instrumentation-httpclient - - - io.zipkin.brave - brave-instrumentation-httpasyncclient - - - io.zipkin.brave - brave-instrumentation-spring-webmvc - - - io.zipkin.brave - brave-instrumentation-jms - - - io.zipkin.brave - brave-instrumentation-mongodb - - - javax.jms - javax.jms-api - true - - - io.opentracing.brave - brave-opentracing - true - - - org.apache.httpcomponents - httpasyncclient - true - - - org.springframework.security.oauth.boot - spring-security-oauth2-autoconfigure - true - - - org.springframework - spring-jms - true - - - - io.github.lognet - grpc-spring-boot-starter - true - - - org.springframework.boot - spring-boot-starter - - - - - io.zipkin.brave - brave-instrumentation-grpc - true - - - io.zipkin.reporter2 - zipkin-reporter-metrics-micrometer - - - io.micrometer - micrometer-core - - - - - - io.lettuce - lettuce-core - true - org.springframework.boot @@ -327,38 +136,27 @@ spring-boot-autoconfigure-processor true + + org.springframework.security.oauth + spring-security-oauth2 + true + + + org.springframework.security.oauth.boot + spring-security-oauth2-autoconfigure + true + org.springframework.boot spring-boot-starter-test test - - io.zipkin.brave - brave-instrumentation-http-tests - test - - - com.squareup.okhttp3 - mockwebserver - ${mockwebserver.version} - test - - - org.assertj - assertj-core - test - org.awaitility awaitility test - - org.springframework.cloud - spring-cloud-starter-netflix-eureka-client - test - diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/AbstractSleuthMethodInvocationProcessor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/AbstractSleuthMethodInvocationProcessor.java index f598e8975..71db3718f 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/AbstractSleuthMethodInvocationProcessor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/AbstractSleuthMethodInvocationProcessor.java @@ -16,9 +16,6 @@ package org.springframework.cloud.sleuth.annotation; -import brave.Span; -import brave.Tracer; -import brave.propagation.CurrentTraceContext; import org.aopalliance.intercept.MethodInvocation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -26,6 +23,9 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; /** * Sleuth annotation processor. @@ -63,7 +63,7 @@ abstract class AbstractSleuthMethodInvocationProcessor implements SleuthMethodIn logEvent(span, log + ".after"); } if (isNewSpan) { - span.finish(); + span.end(); } } @@ -89,7 +89,7 @@ abstract class AbstractSleuthMethodInvocationProcessor implements SleuthMethodIn + "the same class then the aspect will not be properly resolved"); return; } - span.annotate(name); + span.event(name); } String log(ContinueSpan continueSpan) { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/DefaultSpanCreator.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/DefaultSpanCreator.java index 42103d946..b6995d30b 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/DefaultSpanCreator.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/DefaultSpanCreator.java @@ -16,11 +16,11 @@ package org.springframework.cloud.sleuth.annotation; -import brave.SpanCustomizer; import org.aopalliance.intercept.MethodInvocation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.cloud.sleuth.api.Span; import org.springframework.cloud.sleuth.internal.SpanNameUtil; import org.springframework.util.StringUtils; @@ -35,7 +35,7 @@ class DefaultSpanCreator implements NewSpanParser { private static final Log log = LogFactory.getLog(DefaultSpanCreator.class); @Override - public void parse(MethodInvocation pjp, NewSpan newSpan, SpanCustomizer span) { + public void parse(MethodInvocation pjp, NewSpan newSpan, Span span) { String name = newSpan == null || StringUtils.isEmpty(newSpan.name()) ? pjp.getMethod().getName() : newSpan.name(); String changedName = SpanNameUtil.toLowerHyphen(name); diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/NewSpanParser.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/NewSpanParser.java index 808d01cae..fb07d23f9 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/NewSpanParser.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/NewSpanParser.java @@ -16,9 +16,10 @@ package org.springframework.cloud.sleuth.annotation; -import brave.SpanCustomizer; import org.aopalliance.intercept.MethodInvocation; +import org.springframework.cloud.sleuth.api.Span; + /** * Parses data for a span created via a {@link NewSpan} annotation. * @@ -33,6 +34,6 @@ public interface NewSpanParser { * @param newSpan meta data of the new span * @param span span to customize */ - void parse(MethodInvocation methodInvocation, NewSpan newSpan, SpanCustomizer span); + void parse(MethodInvocation methodInvocation, NewSpan newSpan, Span span); } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/NonReactorSleuthMethodInvocationProcessor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/NonReactorSleuthMethodInvocationProcessor.java index 2033cf69c..65caecd05 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/NonReactorSleuthMethodInvocationProcessor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/NonReactorSleuthMethodInvocationProcessor.java @@ -16,10 +16,10 @@ package org.springframework.cloud.sleuth.annotation; -import brave.Span; -import brave.Tracer; import org.aopalliance.intercept.MethodInvocation; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.util.StringUtils; /** @@ -48,7 +48,7 @@ class NonReactorSleuthMethodInvocationProcessor extends AbstractSleuthMethodInvo } String log = log(continueSpan); boolean hasLog = StringUtils.hasText(log); - try (Tracer.SpanInScope ws = tracer().withSpanInScope(span)) { + try (Tracer.SpanInScope scope = tracer().withSpan(span)) { before(invocation, span, log, hasLog); return invocation.proceed(); } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/ReactorSleuthMethodInvocationProcessor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/ReactorSleuthMethodInvocationProcessor.java index 803e938ea..1bec84718 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/ReactorSleuthMethodInvocationProcessor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/ReactorSleuthMethodInvocationProcessor.java @@ -18,12 +18,6 @@ package org.springframework.cloud.sleuth.annotation; import java.lang.reflect.Method; -import brave.Span; -import brave.Tracer; -import brave.Tracing; -import brave.propagation.CurrentTraceContext; -import brave.propagation.CurrentTraceContext.Scope; -import brave.propagation.TraceContext; import org.aopalliance.intercept.MethodInvocation; import org.reactivestreams.Publisher; import org.reactivestreams.Subscription; @@ -36,6 +30,10 @@ import reactor.core.publisher.MonoOperator; import reactor.util.annotation.Nullable; import reactor.util.context.Context; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.util.StringUtils; /** @@ -46,17 +44,8 @@ import org.springframework.util.StringUtils; */ class ReactorSleuthMethodInvocationProcessor extends AbstractSleuthMethodInvocationProcessor { - Tracing tracing; - private NonReactorSleuthMethodInvocationProcessor nonReactorSleuthMethodInvocationProcessor; - Tracing tracing() { - if (this.tracing == null) { - this.tracing = this.beanFactory.getBean(Tracing.class); - } - return this.tracing; - } - @Override public Object process(MethodInvocation invocation, NewSpan newSpan, ContinueSpan continueSpan) throws Throwable { Method method = invocation.getMethod(); @@ -147,7 +136,7 @@ class ReactorSleuthMethodInvocationProcessor extends AbstractSleuthMethodInvocat else { span = this.span; } - try (Scope ws = this.processor.currentTraceContext().maybeScope(span.context())) { + try (CurrentTraceContext.Scope ws = this.processor.currentTraceContext().maybeScope(span.context())) { this.source.subscribe(new SpanSubscriber(actual, this.processor, this.invocation, this.span == null, span, this.log, this.hasLog)); } @@ -192,7 +181,7 @@ class ReactorSleuthMethodInvocationProcessor extends AbstractSleuthMethodInvocat else { span = this.span; } - try (Scope ws = this.processor.currentTraceContext().maybeScope(span.context())) { + try (CurrentTraceContext.Scope ws = this.processor.currentTraceContext().maybeScope(span.context())) { this.source.subscribe(new SpanSubscriber(actual, this.processor, this.invocation, this.span == null, span, this.log, this.hasLog)); } @@ -212,7 +201,7 @@ class ReactorSleuthMethodInvocationProcessor extends AbstractSleuthMethodInvocat final boolean hasLog; - final CurrentTraceContext currentTraceContext; + final Tracer tracer; final ReactorSleuthMethodInvocationProcessor processor; @@ -228,23 +217,21 @@ class ReactorSleuthMethodInvocationProcessor extends AbstractSleuthMethodInvocat this.log = log; this.hasLog = hasLog; this.processor = processor; - - this.currentTraceContext = processor.tracing().currentTraceContext(); - this.context = actual.currentContext().put(TraceContext.class, span.context()); - + this.context = actual.currentContext().put(Span.class, span).put(TraceContext.class, span.context()); + this.tracer = processor.tracer(); processor.before(invocation, this.span, this.log, this.hasLog); } @Override public void request(long n) { - try (Scope scope = this.currentTraceContext.maybeScope(this.span.context())) { + try (Tracer.SpanInScope scope = this.tracer.withSpan(this.span)) { this.parent.request(n); } } @Override public void cancel() { - try (Scope scope = this.currentTraceContext.maybeScope(this.span.context())) { + try (Tracer.SpanInScope scope = this.tracer.withSpan(this.span)) { this.parent.cancel(); } finally { @@ -260,21 +247,21 @@ class ReactorSleuthMethodInvocationProcessor extends AbstractSleuthMethodInvocat @Override public void onSubscribe(Subscription subscription) { this.parent = subscription; - try (Scope scope = this.currentTraceContext.maybeScope(this.span.context())) { + try (Tracer.SpanInScope scope = this.tracer.withSpan(this.span)) { this.actual.onSubscribe(this); } } @Override public void onNext(Object o) { - try (Scope scope = this.currentTraceContext.maybeScope(this.span.context())) { + try (Tracer.SpanInScope scope = this.tracer.withSpan(this.span)) { this.actual.onNext(o); } } @Override public void onError(Throwable error) { - try (Scope scope = this.currentTraceContext.maybeScope(this.span.context())) { + try (Tracer.SpanInScope scope = this.tracer.withSpan(this.span)) { this.processor.onFailure(this.span, this.log, this.hasLog, error); this.actual.onError(error); } @@ -285,7 +272,7 @@ class ReactorSleuthMethodInvocationProcessor extends AbstractSleuthMethodInvocat @Override public void onComplete() { - try (Scope scope = this.currentTraceContext.maybeScope(this.span.context())) { + try (Tracer.SpanInScope scope = this.tracer.withSpan(this.span)) { this.actual.onComplete(); } finally { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthAnnotationAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthAnnotationAutoConfiguration.java index 3ff9955ca..93edc1d01 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthAnnotationAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthAnnotationAutoConfiguration.java @@ -16,8 +16,6 @@ package org.springframework.cloud.sleuth.annotation; -import brave.Tracing; - import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -25,6 +23,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -42,7 +41,7 @@ import org.springframework.context.annotation.Role; */ @Configuration(proxyBeanMethods = false) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) -@ConditionalOnBean(Tracing.class) +@ConditionalOnBean(Tracer.class) @ConditionalOnProperty(name = "spring.sleuth.annotation.enabled", matchIfMissing = true) @AutoConfigureAfter(TraceAutoConfiguration.class) class SleuthAnnotationAutoConfiguration { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/SpanTagAnnotationHandler.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/SpanTagAnnotationHandler.java index 957de3d51..a6a5d275d 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/SpanTagAnnotationHandler.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/annotation/SpanTagAnnotationHandler.java @@ -20,13 +20,13 @@ import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; -import brave.SpanCustomizer; import org.aopalliance.intercept.MethodInvocation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.BeanFactory; +import org.springframework.cloud.sleuth.api.SpanCustomizer; import org.springframework.util.StringUtils; /** diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/BaggageEntry.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/BaggageEntry.java new file mode 100644 index 000000000..4463b9c60 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/BaggageEntry.java @@ -0,0 +1,63 @@ +/* + * Copyright 2013-2020 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.api; + +import org.springframework.lang.Nullable; + +/** + * Inspired by OpenZipkin Brave's {@code BaggageField}. + * + * Represents a single baggage entry. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface BaggageEntry { + + /** + * @return name of the baggage entry + */ + String name(); + + /** + * @return value of the baggage entry or {@code null} if not set. + */ + @Nullable + String get(); + + /** + * Retrieves baggage from the given {@link TraceContext}. + * @param traceContext context containing baggage + * @return value of the baggage entry or {@code null} if not set. + */ + @Nullable + String get(TraceContext traceContext); + + /** + * Sets the baggage value. + * @param value to set + */ + void set(String value); + + /** + * Sets the baggage value for the given {@link TraceContext}. + * @param traceContext context containing baggage + * @param value to set + */ + void set(TraceContext traceContext, String value); + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/BaggageManager.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/BaggageManager.java new file mode 100644 index 000000000..e36c5d100 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/BaggageManager.java @@ -0,0 +1,50 @@ +/* + * Copyright 2013-2020 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.api; + +import java.util.Map; + +/** + * Manages {@link BaggageEntry} entries. + * + * @author OpenTelemetry Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface BaggageManager { + + /** + * @return mapping of all baggage entries + */ + Map getAllBaggage(); + + /** + * Retrieves {@link BaggageEntry} for the given name. + * @param name baggage name + * @return baggage or {@code null} if not present + */ + BaggageEntry getBaggage(String name); + + /** + * Creates a new {@link BaggageEntry} entry for the given name or returns an existing + * one if it's already present. + * @param name baggage name + * @return new or already created baggage + */ + BaggageEntry createBaggage(String name); + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/CurrentTraceContext.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/CurrentTraceContext.java new file mode 100644 index 000000000..b06946bdf --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/CurrentTraceContext.java @@ -0,0 +1,70 @@ +/* + * Copyright 2013-2020 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.api; + +import java.io.Closeable; + +import org.springframework.lang.Nullable; + +/** + * This API was heavily influenced by Brave. Parts of its documentation were taken + * directly from Brave. + * + * This makes a given span the current span by placing it in scope (usually but not always + * a thread local scope). + * + * @author OpenZipkin Brave Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface CurrentTraceContext { + + /** + * @return current {@link TraceContext} or {@code null} if not set. + */ + @Nullable + TraceContext get(); + + /** + * Sets the current span in scope until the returned object is closed. It is a + * programming error to drop or never close the result. Using try-with-resources is + * preferred for this reason. + * @param context span to place into scope or {@code null} to clear the scope + * @return the scope with the span set + */ + CurrentTraceContext.Scope newScope(@Nullable TraceContext context); + + /** + * Like {@link #newScope(TraceContext)}, except returns a noop scope if the given + * context is already in scope. + * @param context span to place into scope or {@code null} to clear the scope + * @return the scope with the span set + */ + CurrentTraceContext.Scope maybeScope(@Nullable TraceContext context); + + /** + * Scope of a span. Needs to be closed so that resources are let go (e.g. MDC is + * cleared). + */ + interface Scope extends Closeable { + + @Override + void close(); + + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/SamplerFunction.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/SamplerFunction.java new file mode 100644 index 000000000..92438dade --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/SamplerFunction.java @@ -0,0 +1,123 @@ +/* + * Copyright 2013-2020 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.api; + +import org.springframework.lang.Nullable; + +/** + * This API was heavily influenced by Brave. Parts of its documentation were taken + * directly from Brave. + * + * Decides whether to start a new trace based on request properties such as an HTTP path. + * + * @param type of the input, for example a request or method + * @author OpenZipkin Brave Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface SamplerFunction { + + /** + * Returns an overriding sampling decision for a new trace. + * @param arg parameter to evaluate for a sampling decision. {@code null} input + * results in a {@code null} result + * @return {@code true} to sample a new trace or {@code false} to deny. {@code null} + * defers the decision. + */ + @Nullable + Boolean trySample(@Nullable T arg); + + /** + * Always deferring {@link SamplerFunction}. + * @param type of the input, for example a request or method + * @return decision deferring sampler function + */ + static SamplerFunction deferDecision() { + return (SamplerFunction) Constants.DEFER_DECISION; + } + + /** + * Never sampling {@link SamplerFunction}. + * @param type of the input, for example a request or method + * @return never sampling sampler function + */ + static SamplerFunction neverSample() { + return (SamplerFunction) Constants.NEVER_SAMPLE; + } + + /** + * Always sampling {@link SamplerFunction}. + * @param type of the input, for example a request or method + * @return always sampling sampler function + */ + static SamplerFunction alwaysSample() { + return (SamplerFunction) Constants.ALWAYS_SAMPLE; + } + + /** + * Constant {@link SamplerFunction}s. + */ + enum Constants implements SamplerFunction { + + /** + * Always defers sampling decision. + */ + DEFER_DECISION { + @Override + public Boolean trySample(Object request) { + return null; + } + + @Override + public String toString() { + return "DeferDecision"; + } + }, + + /** + * Will never sample this trace. + */ + NEVER_SAMPLE { + @Override + public Boolean trySample(Object request) { + return false; + } + + @Override + public String toString() { + return "NeverSample"; + } + }, + + /** + * Will always sample this trace. + */ + ALWAYS_SAMPLE { + @Override + public Boolean trySample(Object request) { + return true; + } + + @Override + public String toString() { + return "AlwaysSample"; + } + } + + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/ScopedSpan.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/ScopedSpan.java new file mode 100644 index 000000000..a4eb57180 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/ScopedSpan.java @@ -0,0 +1,74 @@ +/* + * Copyright 2013-2020 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.api; + +/** + * Represents the "current span" until {@link ScopedSpan#end()} ()} is called. + * + * @author OpenZipkin Brave Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface ScopedSpan { + + /** + * @return {@code true} when no recording is done and nothing is reported to an + * external system. However, this span should still be injected into outgoing + * requests. Use this flag to avoid performing expensive computation. + */ + boolean isNoop(); + + /** + * @return {@link TraceContext} corresponding to this span. + */ + TraceContext context(); + + /** + * Sets a name on this span. + * @param name name to set on the span + * @return this span + */ + ScopedSpan name(String name); + + /** + * Sets a tag on this span. + * @param key tag key + * @param value tag value + * @return this span + */ + ScopedSpan tag(String key, String value); + + /** + * Sets an event on this span. + * @param value event name to set on the span + * @return this span + */ + ScopedSpan event(String value); + + /** + * Records an exception for this span. + * @param throwable to record + * @return this span + */ + ScopedSpan error(Throwable throwable); + + /** + * Ends the span. The span gets stopped and recorded if not noop. + */ + void end(); + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/Span.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/Span.java new file mode 100644 index 000000000..98b39c5d3 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/Span.java @@ -0,0 +1,203 @@ +/* + * Copyright 2013-2020 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.api; + +import org.springframework.cloud.sleuth.api.propagation.Propagator; + +/** + * + * This API was heavily influenced by Brave. Parts of its documentation were taken + * directly from Brave. + * + * Span is a single unit of work that needs to be started and stopped. Contains timing + * information and events and tags. + * + * @author OpenZipkin Brave Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface Span extends SpanCustomizer { + + /** + * @return {@code true} when no recording is done and nothing is reported to an + * external system. However, this span should still be injected into outgoing + * requests. Use this flag to avoid performing expensive computation. + */ + boolean isNoop(); + + /** + * @return {@link TraceContext} corresponding to this span. + */ + TraceContext context(); + + /** + * Starts this span. + * @return this span + */ + Span start(); + + /** + * Sets a name on this span. + * @param name name to set on the span + * @return this span + */ + Span name(String name); + + /** + * Sets an event on this span. + * @param value event name to set on the span + * @return this span + */ + Span event(String value); + + /** + * Sets a tag on this span. + * @param key tag key + * @param value tag value + * @return this span + */ + Span tag(String key, String value); + + /** + * Records an exception for this span. + * @param throwable to record + * @return this span + */ + Span error(Throwable throwable); + + /** + * Ends the span. The span gets stopped and recorded if not noop. + */ + void end(); + + /** + * Ends the span. The span gets stopped but does not get recorded. + */ + void abandon(); + + /** + * Type of span. Can be used to specify additional relationships between spans in + * addition to a parent/child relationship. + * + * Documentation of the enum taken from OpenTelemetry. + */ + enum Kind { + + /** + * Indicates that the span covers server-side handling of an RPC or other remote + * request. + */ + SERVER, + + /** + * Indicates that the span covers the client-side wrapper around an RPC or other + * remote request. + */ + CLIENT, + + /** + * Indicates that the span describes producer sending a message to a broker. + * Unlike client and server, there is no direct critical path latency relationship + * between producer and consumer spans. + */ + PRODUCER, + + /** + * Indicates that the span describes consumer receiving a message from a broker. + * Unlike client and server, there is no direct critical path latency relationship + * between producer and consumer spans. + */ + CONSUMER + + } + + /** + * In some cases (e.g. when dealing with + * {@link Propagator#extract(Object, Propagator.Getter)}'s we want to create a span + * that has not yet been started, yet it's heavily configurable (some options are not + * possible to be set when a span has already been started). We can achieve that by + * using a builder. + * + * Inspired by OpenZipkin Brave and OpenTelemetry API. + */ + interface Builder { + + /** + * Sets the parent of the built span. + * @param context parent's context + * @return this + */ + Builder setParent(TraceContext context); + + /** + * Sets no parent of the built span. + * @return this + */ + Builder setNoParent(); + + /** + * Sets the name of the span. + * @param name span name + * @return this + */ + Builder name(String name); + + /** + * Sets an event on the span. + * @param value event value + * @return this + */ + Builder event(String value); + + /** + * Sets a tag on the span. + * @param key tag key + * @param value tag value + * @return this + */ + Builder tag(String key, String value); + + /** + * Sets an error on the span. + * @param throwable error to set + * @return this + */ + Builder error(Throwable throwable); + + /** + * Sets the kind on the span. + * @param spanKind kind of the span + * @return this + */ + Builder kind(Span.Kind spanKind); + + /** + * Sets the remote service name for the span. + * @param remoteServiceName remote service name + * @return this + */ + Builder remoteServiceName(String remoteServiceName); + + /** + * Builds and starts the span. + * @return started span + */ + Span start(); + + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/SpanCustomizer.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/SpanCustomizer.java new file mode 100644 index 000000000..a7f0b1d0c --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/SpanCustomizer.java @@ -0,0 +1,50 @@ +/* + * Copyright 2013-2020 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.api; + +/** + * Allows to customize the current span in scope. + * + * @author OpenZipkin Brave Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface SpanCustomizer { + + /** + * Sets a name on a span. + * @param name name to set on a span + * @return this, for chaining + */ + SpanCustomizer name(String name); + + /** + * Sets a tag on a span. + * @param key tag key + * @param value tag value + * @return this, for chaining + */ + SpanCustomizer tag(String key, String value); + + /** + * Sets an event on a span. + * @param value event name + * @return this, for chaining + */ + SpanCustomizer event(String value); + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/TraceContext.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/TraceContext.java new file mode 100644 index 000000000..65bf0ce59 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/TraceContext.java @@ -0,0 +1,48 @@ +/* + * Copyright 2013-2020 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.api; + +import org.springframework.lang.Nullable; + +/** + * Contains trace and span data. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface TraceContext { + + /** + * Trace id. + * @return trace id of a span + */ + String traceId(); + + /** + * Parent span id. + * @return parent span id or {@code null} if one is not set + */ + @Nullable + String parentId(); + + /** + * Span id. + * @return span id + */ + String spanId(); + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/Tracer.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/Tracer.java new file mode 100644 index 000000000..0fd8b14f5 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/Tracer.java @@ -0,0 +1,162 @@ +/* + * Copyright 2013-2020 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.api; + +import java.io.Closeable; + +import org.springframework.cloud.sleuth.api.propagation.Propagator; +import org.springframework.lang.Nullable; + +/** + * This API was heavily influenced by Brave. Parts of its documentation were taken + * directly from Brave. + * + * Using a tracer, you can create a root span capturing the critical path of a request. + * Child spans can be created to allocate latency relating to outgoing requests. + * + * When tracing single-threaded code, just run it inside a scoped span:
{@code
+ * // Start a new trace or a span within an existing trace representing an operation
+ * ScopedSpan span = tracer.startScopedSpan("encode");
+ * try {
+ *   // The span is in "scope" so that downstream code such as loggers can see trace IDs
+ *   return encoder.encode();
+ * } catch (RuntimeException | Error e) {
+ *   span.error(e); // Unless you handle exceptions, you might not know the operation failed!
+ *   throw e;
+ * } finally {
+ *   span.end();
+ * }
+ * }
+ * + * When you need more features, or finer control, use the {@linkplain Span} type: + *
{@code
+ * // Start a new trace or a span within an existing trace representing an operation
+ * Span span = tracer.nextSpan().name("encode").start();
+ * // Put the span in "scope" so that downstream code such as loggers can see trace IDs
+ * try (SpanInScope ws = tracer.withSpanInScope(span)) {
+ *   return encoder.encode();
+ * } catch (RuntimeException | Error e) {
+ *   span.error(e); // Unless you handle exceptions, you might not know the operation failed!
+ *   throw e;
+ * } finally {
+ *   span.end(); // note the scope is independent of the span. Always finish a span.
+ * }
+ * }
+ * + * Both of the above examples report the exact same span on finish! + * + * @author OpenZipkin Brave Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + * @see Span + * @see ScopedSpan + * @see Propagator + */ +public interface Tracer extends BaggageManager { + + /** + * This creates a new span based on the current span in scope. If there's no such span + * a new trace will be created. + * @return a child span or a new trace if no span was present + */ + Span nextSpan(); + + /** + * This creates a new span whose parent is {@link Span}. If parent is {@code null} + * then will create act as {@link #nextSpan()}. + * @param parent parent span + * @return a child span for the given parent, {@code null} if context was empty. + */ + Span nextSpan(@Nullable Span parent); + + /** + * Makes the given span the "current span" and returns an object that exits that scope + * on close. Calls to {@link #currentSpan()} and {@link #currentSpanCustomizer()} will + * affect this span until the return value is closed. + * + * The most convenient way to use this method is via the try-with-resources idiom. + * + * When tracing in-process commands, prefer {@link #startScopedSpan(String)} which + * scopes by default. + * + * Note: While downstream code might affect the span, calling this method, and calling + * close on the result have no effect on the input. For example, calling close on the + * result does not finish the span. Not only is it safe to call close, you must call + * close to end the scope, or risk leaking resources associated with the scope. + * @param span span to place into scope or null to clear the scope + * @return scope with span in it + */ + Tracer.SpanInScope withSpan(@Nullable Span span); + + /** + * Returns a new child span if there's a {@link #currentSpan()} or a new trace if + * there isn't. The result is the "current span" until {@link ScopedSpan#end()} ()} is + * called. + * + * Here's an example:
{@code
+	 * ScopedSpan span = tracer.startScopedSpan("encode");
+	 * try {
+	 *   // The span is in "scope" so that downstream code such as loggers can see trace IDs
+	 *   return encoder.encode();
+	 * } catch (RuntimeException | Error e) {
+	 *   span.error(e); // Unless you handle exceptions, you might not know the operation failed!
+	 *   throw e;
+	 * } finally {
+	 *   span.end();
+	 * }
+	 * }
+ * @param name of the span in scope + * @return span in scope + */ + ScopedSpan startScopedSpan(String name); + + /** + * In some cases (e.g. when dealing with + * {@link Propagator#extract(Object, Propagator.Getter)}'s we want to create a span + * that has not yet been started, yet it's heavily configurable (some options are not + * possible to be set when a span has already been started). We can achieve that by + * using a builder. + * @return a span builder + */ + Span.Builder spanBuilder(); + + /** + * Allows to customize the current span in scope. + * @return current span customizer + */ + @Nullable + SpanCustomizer currentSpanCustomizer(); + + /** + * Retrieves the current span in scope or {@code null} if one is not available. + * @return current span in scope + */ + @Nullable + Span currentSpan(); + + /** + * Scope of a span. Needs to be closed so that resources are let go (e.g. MDC is + * cleared). + */ + interface SpanInScope extends Closeable { + + @Override + void close(); + + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/exporter/FinishedSpan.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/exporter/FinishedSpan.java new file mode 100644 index 000000000..cce5fd224 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/exporter/FinishedSpan.java @@ -0,0 +1,106 @@ +/* + * Copyright 2013-2020 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.api.exporter; + +import java.util.Collection; +import java.util.Map; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.lang.Nullable; + +/** + * This API is inspired by OpenZipkin Brave (from {code MutableSpan}). + * + * Represents a span that has been finished and is ready to be sent to an external + * location (e.g. Zipkin). + * + * @author OpenZipkin Brave Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface FinishedSpan { + + /** + * @return span's name + */ + String name(); + + /** + * @return span's start timestamp + */ + long startTimestamp(); + + /** + * @return span's end timestamp + */ + long endTimestamp(); + + /** + * @return span's tags + */ + Map tags(); + + /** + * @return span's events as timestamp to value mapping + */ + Collection> events(); + + /** + * @return span's span id + */ + String spanId(); + + /** + * @return span's parent id or {@code null} if not set + */ + @Nullable + String parentId(); + + /** + * @return span's remote ip + */ + @Nullable + String remoteIp(); + + /** + * @return span's remote port + */ + int remotePort(); + + /** + * @return span's trace id + */ + String traceId(); + + /** + * @return corresponding error or {@code null} if one was not thrown + */ + @Nullable + Throwable error(); + + /** + * @return span's kind + */ + Span.Kind kind(); + + /** + * @return remote service name or {@code null} if not set + */ + @Nullable + String remoteServiceName(); + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/exporter/SpanFilter.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/exporter/SpanFilter.java new file mode 100644 index 000000000..cc2946b33 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/exporter/SpanFilter.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2020 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.api.exporter; + +/** + * An interface that allows to filter whether a given reported span should be exported or + * not. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface SpanFilter { + + /** + * Called to export sampled {@code Span}s. + * @param span the collection of sampled Spans to be exported. + * @return whether should export spans + */ + boolean isExportable(FinishedSpan span); + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpClientHandler.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpClientHandler.java new file mode 100644 index 000000000..7e29be066 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpClientHandler.java @@ -0,0 +1,63 @@ +/* + * Copyright 2013-2020 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.api.http; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.lang.Nullable; + +/** + * This API is taken from OpenZipkin Brave. + * + * This standardizes a way to instrument http clients, particularly in a way that + * encourages use of portable customizations via {@link HttpRequestParser} and + * {@link HttpResponseParser}. + * + * @author OpenZipkin Brave Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface HttpClientHandler { + + /** + * Starts the client span after assigning it a name and tags. This injects the trace + * context onto the request before returning. + * + * Call this before sending the request on the wire. + * @param request to inject the tracing context with + * @return client side span + */ + Span handleSend(HttpClientRequest request); + + /** + * Same as {@link #handleSend(HttpClientRequest)} but with an explicit parent + * {@link TraceContext}. + * @param request to inject the tracing context with + * @param parent {@link TraceContext} that is to be the client side span's parent + * @return client side span + */ + Span handleSend(HttpClientRequest request, @Nullable TraceContext parent); + + /** + * Finishes the client span after assigning it tags according to the response or + * error. + * @param response the HTTP response + * @param span span to be ended + */ + void handleReceive(HttpClientResponse response, Span span); + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpClientRequest.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpClientRequest.java new file mode 100644 index 000000000..266b873a8 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpClientRequest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2013-2020 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.api.http; + +import org.springframework.cloud.sleuth.api.Span; + +/** + * This API is taken from OpenZipkin Brave. + * + * Abstract request type used for parsing and sampling. Represents an HTTP Client request. + * + * @author OpenZipkin Brave Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface HttpClientRequest extends HttpRequest { + + @Override + default Span.Kind spanKind() { + return Span.Kind.CLIENT; + } + + /** + * @param name header name + * @param value header value + */ + void header(String name, String value); + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpClientResponse.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpClientResponse.java new file mode 100644 index 000000000..70dc6ac06 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpClientResponse.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2020 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.api.http; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.lang.Nullable; + +/** + * This API is taken from OpenZipkin Brave. + * + * Abstract response type used for parsing and sampling. Represents an HTTP Client + * response. + * + * @author OpenZipkin Brave Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface HttpClientResponse extends HttpResponse { + + @Override + default Span.Kind spanKind() { + return Span.Kind.CLIENT; + } + + @Nullable + default HttpClientRequest request() { + return null; + } + + @Override + default Throwable error() { + return null; + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpRequest.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpRequest.java new file mode 100644 index 000000000..d1651d113 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpRequest.java @@ -0,0 +1,82 @@ +/* + * Copyright 2013-2020 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.api.http; + +import org.springframework.lang.Nullable; + +/** + * This API is taken from OpenZipkin Brave. + * + * Abstract response type used for parsing and sampling. Represents an HTTP request. + * + * @author OpenZipkin Brave Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface HttpRequest extends Request { + + /** + * @return HTTP method. + */ + String method(); + + /** + * @return HTTP path or {@code null} if not set. + */ + @Nullable + String path(); + + /** + * Returns an expression such as "/items/:itemId" representing an application + * endpoint, conventionally associated with the tag key "http.route". If no route + * matched, "" (empty string) is returned. {@code null} indicates this instrumentation + * doesn't understand http routes. + * @return HTTP route or {@code null} if not set. + */ + @Nullable + default String route() { + return null; + } + + /** + * @return HTTP URL or {@code null} if not set. + */ + @Nullable + String url(); + + /** + * @param name header name + * @return HTTP header or {@code null} if not set. + */ + @Nullable + String header(String name); + + /** + * @return remote IP for the given connection. + */ + default String remoteIp() { + return null; + } + + /** + * @return remote port for the given connection. + */ + default int remotePort() { + return 0; + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpRequestParser.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpRequestParser.java new file mode 100644 index 000000000..9430f9d5c --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpRequestParser.java @@ -0,0 +1,42 @@ +/* + * Copyright 2013-2020 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.api.http; + +import org.springframework.cloud.sleuth.api.SpanCustomizer; +import org.springframework.cloud.sleuth.api.TraceContext; + +/** + * This API is taken from OpenZipkin Brave. + * + * Use this to control the request data recorded. + * + * @author OpenZipkin Brave Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface HttpRequestParser { + + /** + * Implement to choose what data from the http request are parsed into the span + * representing it. + * @param request current request + * @param context corresponding trace context + * @param span customizer for the current span + */ + void parse(HttpRequest request, TraceContext context, SpanCustomizer span); + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpResponse.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpResponse.java new file mode 100644 index 000000000..52284a312 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpResponse.java @@ -0,0 +1,73 @@ +/* + * Copyright 2013-2020 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.api.http; + +import org.springframework.lang.Nullable; + +/** + * This API is taken from OpenZipkin Brave. + * + * Abstract response type used for parsing and sampling. Represents an HTTP response. + * + * @author OpenZipkin Brave Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface HttpResponse extends Response { + + @Nullable + @Override + default HttpRequest request() { + return null; + } + + /** + * @return HTTP method + */ + @Nullable + default String method() { + HttpRequest request = request(); + return request != null ? request.method() : null; + } + + /** + * Returns an expression such as "/items/:itemId" representing an application + * endpoint, conventionally associated with the tag key "http.route". If no route + * matched, "" (empty string) is returned. {@code null} indicates this instrumentation + * doesn't understand http routes. + * @return HTTP route or {@code null} if not set. + */ + @Nullable + default String route() { + HttpRequest request = request(); + return request != null ? request.route() : null; + } + + /** + * @return The HTTP status code or zero if unreadable. + */ + int statusCode(); + + /** + * @param header header name + * @return HTTP header or {@code null} if not set. + */ + default String header(String header) { + return null; + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpResponseParser.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpResponseParser.java new file mode 100644 index 000000000..c42f93efb --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpResponseParser.java @@ -0,0 +1,42 @@ +/* + * Copyright 2013-2020 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.api.http; + +import org.springframework.cloud.sleuth.api.SpanCustomizer; +import org.springframework.cloud.sleuth.api.TraceContext; + +/** + * This API is taken from OpenZipkin Brave. + * + * Use this to control the response data recorded. + * + * @author OpenZipkin Brave Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface HttpResponseParser { + + /** + * Implement to choose what data from the http response are parsed into the span + * representing it. + * @param response current response + * @param context corresponding trace context + * @param span customizer for the current span + */ + void parse(HttpResponse response, TraceContext context, SpanCustomizer span); + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpServerHandler.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpServerHandler.java new file mode 100644 index 000000000..be12545ee --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpServerHandler.java @@ -0,0 +1,50 @@ +/* + * Copyright 2013-2020 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.api.http; + +import org.springframework.cloud.sleuth.api.Span; + +/** + * This API is taken from OpenZipkin Brave. + * + * This standardizes a way to instrument http servers, particularly in a way that + * encourages use of portable customizations via {@link HttpRequestParser} and + * {@link HttpResponseParser}. + * + * @author OpenZipkin Brave Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface HttpServerHandler { + + /** + * Conditionally joins a span, or starts a new trace, depending on if a trace context + * was extracted from the request. Tags are added before the span is started. + * @param request HTTP request + * @return server side span (either joined or a new trace) + */ + Span handleReceive(HttpServerRequest request); + + /** + * Finishes the server span after assigning it tags according to the response or + * error. + * @param response HTTP response + * @param span server side span to end + */ + void handleSend(HttpServerResponse response, Span span); + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpServerRequest.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpServerRequest.java new file mode 100644 index 000000000..7acb5d02a --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpServerRequest.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2020 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.api.http; + +import org.springframework.cloud.sleuth.api.Span; + +/** + * This API is taken from OpenZipkin Brave. + * + * Abstract request type used for parsing and sampling. Represents an HTTP Server request. + * + * @author OpenZipkin Brave Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface HttpServerRequest extends HttpRequest { + + @Override + default Span.Kind spanKind() { + return Span.Kind.SERVER; + } + + /** + * @param key attribute key + * @return attribute with the given key or {@code null} if not set + */ + default Object getAttribute(String key) { + return null; + } + + /** + * @param key attribute key + * @param value attribute value + */ + default void setAttribute(String key, Object value) { + + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpServerResponse.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpServerResponse.java new file mode 100644 index 000000000..833a3c4cf --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/HttpServerResponse.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2020 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.api.http; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.lang.Nullable; + +/** + * This API is taken from OpenZipkin Brave. + * + * Abstract response type used for parsing and sampling. Represents an HTTP Server + * response. + * + * @author OpenZipkin Brave Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface HttpServerResponse extends HttpResponse { + + @Override + default Span.Kind spanKind() { + return Span.Kind.SERVER; + } + + @Nullable + default HttpServerRequest request() { + return null; + } + + @Override + default Throwable error() { + return null; + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/Request.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/Request.java new file mode 100644 index 000000000..2c5b6df8f --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/Request.java @@ -0,0 +1,43 @@ +/* + * Copyright 2013-2020 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.api.http; + +import org.springframework.cloud.sleuth.api.Span; + +/** + * This API is taken from OpenZipkin Brave. + * + * Abstract request type used for parsing and sampling. + * + * @author OpenZipkin Brave Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface Request { + + /** + * @return The remote {@link Span.Kind} describing the direction and type of the + * request. + */ + Span.Kind spanKind(); + + /** + * @return the underlying request object or {@code null} if there is none. + */ + Object unwrap(); + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/Response.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/Response.java new file mode 100644 index 000000000..311a285ac --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/http/Response.java @@ -0,0 +1,56 @@ +/* + * Copyright 2013-2020 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.api.http; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.lang.Nullable; + +/** + * This API is taken from OpenZipkin Brave. + * + * Abstract response type used for parsing and sampling. + * + * @author OpenZipkin Brave Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface Response { + + /** + * @return The remote {@link Span.Kind} describing the direction and type of the + * request. + */ + Span.Kind spanKind(); + + /** + * @return corresponding request + */ + @Nullable + Request request(); + + /** + * @return exception that occurred or {@code null} if there was none. + */ + @Nullable + Throwable error(); + + /** + * @return the underlying request object or {@code null} if there is none. + */ + Object unwrap(); + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpBaggageEntry.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpBaggageEntry.java new file mode 100644 index 000000000..5c431ed40 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpBaggageEntry.java @@ -0,0 +1,55 @@ +/* + * Copyright 2013-2020 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.api.noop; + +import org.springframework.cloud.sleuth.api.BaggageEntry; +import org.springframework.cloud.sleuth.api.TraceContext; + +/** + * A noop implementation. Does nothing. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class NoOpBaggageEntry implements BaggageEntry { + + @Override + public String name() { + return null; + } + + @Override + public String get() { + return null; + } + + @Override + public String get(TraceContext traceContext) { + return null; + } + + @Override + public void set(String value) { + + } + + @Override + public void set(TraceContext traceContext, String value) { + + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpCurrentTraceContext.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpCurrentTraceContext.java new file mode 100644 index 000000000..e485b6123 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpCurrentTraceContext.java @@ -0,0 +1,47 @@ +/* + * Copyright 2013-2020 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.api.noop; + +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.TraceContext; + +/** + * A noop implementation. Does nothing. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class NoOpCurrentTraceContext implements CurrentTraceContext { + + @Override + public TraceContext get() { + return new NoOpTraceContext(); + } + + @Override + public Scope newScope(TraceContext context) { + return () -> { + }; + } + + @Override + public Scope maybeScope(TraceContext context) { + return () -> { + }; + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpHttpClientHandler.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpHttpClientHandler.java new file mode 100644 index 000000000..f994715d1 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpHttpClientHandler.java @@ -0,0 +1,48 @@ +/* + * Copyright 2013-2020 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.api.noop; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; +import org.springframework.cloud.sleuth.api.http.HttpClientRequest; +import org.springframework.cloud.sleuth.api.http.HttpClientResponse; + +/** + * A noop implementation. Does nothing. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class NoOpHttpClientHandler implements HttpClientHandler { + + @Override + public Span handleSend(HttpClientRequest request) { + return new NoOpSpan(); + } + + @Override + public Span handleSend(HttpClientRequest request, TraceContext parent) { + return new NoOpSpan(); + } + + @Override + public void handleReceive(HttpClientResponse response, Span span) { + + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpHttpServerHandler.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpHttpServerHandler.java new file mode 100644 index 000000000..ceeb27b79 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpHttpServerHandler.java @@ -0,0 +1,42 @@ +/* + * Copyright 2013-2020 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.api.noop; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.http.HttpServerHandler; +import org.springframework.cloud.sleuth.api.http.HttpServerRequest; +import org.springframework.cloud.sleuth.api.http.HttpServerResponse; + +/** + * A noop implementation. Does nothing. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class NoOpHttpServerHandler implements HttpServerHandler { + + @Override + public Span handleReceive(HttpServerRequest request) { + return new NoOpSpan(); + } + + @Override + public void handleSend(HttpServerResponse response, Span span) { + + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpPropagator.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpPropagator.java new file mode 100644 index 000000000..42bd745de --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpPropagator.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2020 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.api.noop; + +import java.util.Collections; +import java.util.List; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.propagation.Propagator; + +/** + * A noop implementation. Does nothing. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class NoOpPropagator implements Propagator { + + @Override + public List fields() { + return Collections.emptyList(); + } + + @Override + public void inject(TraceContext context, C carrier, Setter setter) { + + } + + @Override + public Span.Builder extract(C carrier, Getter getter) { + return new NoOpSpanBuilder(); + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpScopedSpan.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpScopedSpan.java new file mode 100644 index 000000000..ed6bb6a77 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpScopedSpan.java @@ -0,0 +1,65 @@ +/* + * Copyright 2013-2020 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.api.noop; + +import org.springframework.cloud.sleuth.api.ScopedSpan; +import org.springframework.cloud.sleuth.api.TraceContext; + +/** + * A noop implementation. Does nothing. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +class NoOpScopedSpan implements ScopedSpan { + + @Override + public boolean isNoop() { + return false; + } + + @Override + public TraceContext context() { + return new NoOpTraceContext(); + } + + @Override + public ScopedSpan name(String name) { + return this; + } + + @Override + public ScopedSpan tag(String key, String value) { + return this; + } + + @Override + public ScopedSpan event(String value) { + return this; + } + + @Override + public ScopedSpan error(Throwable throwable) { + return this; + } + + @Override + public void end() { + + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpSpan.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpSpan.java new file mode 100644 index 000000000..33ee8ab9d --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpSpan.java @@ -0,0 +1,75 @@ +/* + * Copyright 2013-2020 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.api.noop; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; + +/** + * A noop implementation. Does nothing. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +class NoOpSpan implements Span { + + @Override + public boolean isNoop() { + return true; + } + + @Override + public TraceContext context() { + return new NoOpTraceContext(); + } + + @Override + public Span start() { + return this; + } + + @Override + public Span name(String name) { + return this; + } + + @Override + public Span event(String value) { + return this; + } + + @Override + public Span tag(String key, String value) { + return this; + } + + @Override + public Span error(Throwable throwable) { + return this; + } + + @Override + public void end() { + + } + + @Override + public void abandon() { + + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpSpanBuilder.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpSpanBuilder.java new file mode 100644 index 000000000..1c940d5f7 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpSpanBuilder.java @@ -0,0 +1,75 @@ +/* + * Copyright 2013-2020 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.api.noop; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; + +/** + * A noop implementation. Does nothing. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +class NoOpSpanBuilder implements Span.Builder { + + @Override + public Span.Builder setParent(TraceContext context) { + return this; + } + + @Override + public Span.Builder setNoParent() { + return this; + } + + @Override + public Span.Builder name(String name) { + return this; + } + + @Override + public Span.Builder event(String value) { + return this; + } + + @Override + public Span.Builder tag(String key, String value) { + return this; + } + + @Override + public Span.Builder error(Throwable throwable) { + return this; + } + + @Override + public Span.Builder kind(Span.Kind spanKind) { + return this; + } + + @Override + public Span.Builder remoteServiceName(String remoteServiceName) { + return this; + } + + @Override + public Span start() { + return new NoOpSpan(); + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpSpanCustomizer.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpSpanCustomizer.java new file mode 100644 index 000000000..3aea4b0f3 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpSpanCustomizer.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2020 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.api.noop; + +import org.springframework.cloud.sleuth.api.SpanCustomizer; + +/** + * A noop implementation. Does nothing. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class NoOpSpanCustomizer implements SpanCustomizer { + + @Override + public SpanCustomizer name(String name) { + return this; + } + + @Override + public SpanCustomizer tag(String key, String value) { + return this; + } + + @Override + public SpanCustomizer event(String value) { + return this; + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpSpanInScope.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpSpanInScope.java new file mode 100644 index 000000000..e80c9c260 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpSpanInScope.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2020 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.api.noop; + +import org.springframework.cloud.sleuth.api.Tracer; + +/** + * A noop implementation. Does nothing. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +class NoOpSpanInScope implements Tracer.SpanInScope { + + @Override + public void close() { + + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpTraceContext.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpTraceContext.java new file mode 100644 index 000000000..02ba14ba3 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpTraceContext.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2020 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.api.noop; + +import org.springframework.cloud.sleuth.api.TraceContext; + +/** + * A noop implementation. Does nothing. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +class NoOpTraceContext implements TraceContext { + + @Override + public String traceId() { + return ""; + } + + @Override + public String parentId() { + return ""; + } + + @Override + public String spanId() { + return ""; + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpTracer.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpTracer.java new file mode 100644 index 000000000..a4a8fd3d6 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/noop/NoOpTracer.java @@ -0,0 +1,86 @@ +/* + * Copyright 2013-2020 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.api.noop; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.cloud.sleuth.api.BaggageEntry; +import org.springframework.cloud.sleuth.api.ScopedSpan; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.SpanCustomizer; +import org.springframework.cloud.sleuth.api.Tracer; + +/** + * A noop implementation. Does nothing. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class NoOpTracer implements Tracer { + + @Override + public Span nextSpan(Span parent) { + return new NoOpSpan(); + } + + @Override + public SpanInScope withSpan(Span span) { + return new NoOpSpanInScope(); + } + + @Override + public SpanCustomizer currentSpanCustomizer() { + return new NoOpSpanCustomizer(); + } + + @Override + public Span currentSpan() { + return new NoOpSpan(); + } + + @Override + public Span nextSpan() { + return new NoOpSpan(); + } + + @Override + public ScopedSpan startScopedSpan(String name) { + return new NoOpScopedSpan(); + } + + @Override + public Span.Builder spanBuilder() { + return new NoOpSpanBuilder(); + } + + @Override + public Map getAllBaggage() { + return new HashMap<>(); + } + + @Override + public BaggageEntry getBaggage(String name) { + return new NoOpBaggageEntry(); + } + + @Override + public BaggageEntry createBaggage(String name) { + return new NoOpBaggageEntry(); + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/propagation/Propagator.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/propagation/Propagator.java new file mode 100644 index 000000000..48d7c20c6 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/api/propagation/Propagator.java @@ -0,0 +1,127 @@ +/* + * Copyright 2013-2020 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.api.propagation; + +import java.util.List; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.lang.Nullable; + +/** + * Inspired by OpenZipkin Brave and OpenTelemetry. Most of the documentation is taken + * directly from OpenTelemetry. + * + * Injects and extracts a value as text into carriers that travel in-band across process + * boundaries. Encoding is expected to conform to the HTTP Header Field semantics. Values + * are often encoded as RPC/HTTP request headers. + * + * @author OpenZipkin Brave Authors + * @author OpenTelemetry Authors + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface Propagator { + + /** + * @return collection of headers that contain tracing information + */ + List fields(); + + /** + * Injects the value downstream, for example as HTTP headers. The carrier may be null + * to facilitate calling this method with a lambda for the {@link Setter}, in which + * case that null will be passed to the {@link Setter} implementation. + * @param context the {@code Context} containing the value to be injected. + * @param carrier holds propagation fields. For example, an outgoing message or http + * request. + * @param setter invoked for each propagation key to add or remove. + * @param carrier of propagation fields, such as an http request + */ + void inject(TraceContext context, @Nullable C carrier, Setter setter); + + /** + * Extracts the value from upstream. For example, as http headers. + * + *

+ * If the value could not be parsed, the underlying implementation will decide to set + * an object representing either an empty value, an invalid value, or a valid value. + * Implementation must not set {@code null}. + * @param carrier holds propagation fields. For example, an outgoing message or http + * request. + * @param getter invoked for each propagation key to get. + * @param carrier of propagation fields, such as an http request. + * @return the {@code Context} containing the extracted value. + */ + Span.Builder extract(C carrier, Getter getter); + + /** + * Class that allows a {@code TextMapPropagator} to set propagated fields into a + * carrier. + * + *

+ * {@code Setter} is stateless and allows to be saved as a constant to avoid runtime + * allocations. + * + * @param carrier of propagation fields, such as an http request + * @since 0.1.0 + */ + interface Setter { + + /** + * Replaces a propagated field with the given value. + * + *

+ * For example, a setter for an {@link java.net.HttpURLConnection} would be the + * method reference + * {@link java.net.HttpURLConnection#addRequestProperty(String, String)} + * @param carrier holds propagation fields. For example, an outgoing message or + * http request. To facilitate implementations as java lambdas, this parameter may + * be null. + * @param key the key of the field. + * @param value the value of the field. + */ + void set(@Nullable C carrier, String key, String value); + + } + + /** + * Interface that allows a {@code TextMapPropagator} to read propagated fields from a + * carrier. + * + *

+ * {@code Getter} is stateless and allows to be saved as a constant to avoid runtime + * allocations. + * + * @param carrier of propagation fields, such as an http request. + */ + interface Getter { + + /** + * Returns the first value of the given propagation {@code key} or returns + * {@code null}. + * @param carrier carrier of propagation fields, such as an http request. + * @param key the key of the field. + * @return the first value of the given propagation {@code key} or returns + * {@code null}. + */ + @Nullable + String get(C carrier, String key); + + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/SleuthBaggageProperties.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/SleuthBaggageProperties.java index e3a5ecc19..a590d1af7 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/SleuthBaggageProperties.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/SleuthBaggageProperties.java @@ -19,12 +19,6 @@ package org.springframework.cloud.sleuth.autoconfig; import java.util.ArrayList; import java.util.List; -import brave.Tags; -import brave.baggage.BaggageField; -import brave.baggage.BaggagePropagationConfig; -import brave.baggage.CorrelationScopeConfig; -import brave.baggage.CorrelationScopeDecorator; - import org.springframework.boot.context.properties.ConfigurationProperties; /** @@ -34,42 +28,27 @@ import org.springframework.boot.context.properties.ConfigurationProperties; * @since 3.0 */ @ConfigurationProperties("spring.sleuth.baggage") -class SleuthBaggageProperties { +public class SleuthBaggageProperties { /** - * Adds a {@link CorrelationScopeDecorator} to put baggage values into the correlation * context. */ private boolean correlationEnabled = true; /** - * A list of {@link BaggageField#name() fields} to add to correlation (MDC) context. - * - * @see CorrelationScopeConfig.SingleCorrelationField#create(BaggageField) */ private List correlationFields = new ArrayList<>(); - /** - * Same as {@link #remoteFields} except that this field is not propagated to remote - * services. - * - * @see BaggagePropagationConfig.SingleBaggageField#local(BaggageField) - */ private List localFields = new ArrayList<>(); /** * List of fields that are referenced the same in-process as it is on the wire. For * example, the field "x-vcap-request-id" would be set as-is including the prefix. * - * @see BaggagePropagationConfig.SingleBaggageField#remote(BaggageField) - * @see BaggagePropagationConfig.SingleBaggageField.Builder#addKeyName(String) */ private List remoteFields = new ArrayList<>(); /** - * A list of {@link BaggageField#name() fields} to tag into the span. - * - * @see Tags#BAGGAGE_FIELD */ private List tagFields = new ArrayList<>(); diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/SleuthProperties.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/SleuthProperties.java deleted file mode 100644 index 0666b3fe3..000000000 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/SleuthProperties.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2013-2020 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.autoconfig; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -/** - * Sleuth settings. - * - * @author Marcin Grzejszczak - * @since 1.0.11 - */ -@ConfigurationProperties("spring.sleuth") -class SleuthProperties { - - private boolean enabled = true; - - /** When true, generate 128-bit trace IDs instead of 64-bit ones. */ - private boolean traceId128 = false; - - /** - * True means the tracing system supports sharing a span ID between a client and - * server. - */ - private boolean supportsJoin = true; - - /** - * Properties related to handling of spans. - */ - private SpanHandler spanHandler = new SpanHandler(); - - public boolean isEnabled() { - return this.enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public boolean isTraceId128() { - return this.traceId128; - } - - public void setTraceId128(boolean traceId128) { - this.traceId128 = traceId128; - } - - public boolean isSupportsJoin() { - return this.supportsJoin; - } - - public void setSupportsJoin(boolean supportsJoin) { - this.supportsJoin = supportsJoin; - } - - public SpanHandler getSpanHandler() { - return this.spanHandler; - } - - public void setSpanHandler(SpanHandler spanHandler) { - this.spanHandler = spanHandler; - } - - /** - * Properties related to handling of spans. - */ - public static class SpanHandler { - - /** - * Will turn on the default Sleuth handler mechanism. Might ignore exporting of - * certain spans; - */ - private boolean enabled; - - /** - * List of span names to ignore. They will not be sent to external systems. - */ - private List spanNamePatternsToSkip = Arrays.asList("^catalogWatchTaskScheduler$"); - - /** - * Additional list of span names to ignore. Will be appended to - * {@link #spanNamePatternsToSkip}. - */ - private List additionalSpanNamePatternsToIgnore = Collections.emptyList(); - - public boolean isEnabled() { - return this.enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public List getSpanNamePatternsToSkip() { - return this.spanNamePatternsToSkip; - } - - public void setSpanNamePatternsToSkip(List spanNamePatternsToSkip) { - this.spanNamePatternsToSkip = spanNamePatternsToSkip; - } - - public List getAdditionalSpanNamePatternsToIgnore() { - return this.additionalSpanNamePatternsToIgnore; - } - - public void setAdditionalSpanNamePatternsToIgnore(List additionalSpanNamePatternsToIgnore) { - this.additionalSpanNamePatternsToIgnore = additionalSpanNamePatternsToIgnore; - } - - } - -} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/SleuthSpanFilterProperties.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/SleuthSpanFilterProperties.java new file mode 100644 index 000000000..982126e2b --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/SleuthSpanFilterProperties.java @@ -0,0 +1,75 @@ +/* + * Copyright 2013-2020 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.autoconfig; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Sleuth settings. + * + * @author Marcin Grzejszczak + * @since 1.0.11 + */ +@ConfigurationProperties("spring.sleuth.span-filter") +class SleuthSpanFilterProperties { + + /** + * Will turn on the default Sleuth handler mechanism. Might ignore exporting of + * certain spans; + */ + private boolean enabled; + + /** + * List of span names to ignore. They will not be sent to external systems. + */ + private List spanNamePatternsToSkip = Arrays.asList("^catalogWatchTaskScheduler$"); + + /** + * Additional list of span names to ignore. Will be appended to + * {@link #spanNamePatternsToSkip}. + */ + private List additionalSpanNamePatternsToIgnore = Collections.emptyList(); + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public List getSpanNamePatternsToSkip() { + return this.spanNamePatternsToSkip; + } + + public void setSpanNamePatternsToSkip(List spanNamePatternsToSkip) { + this.spanNamePatternsToSkip = spanNamePatternsToSkip; + } + + public List getAdditionalSpanNamePatternsToIgnore() { + return this.additionalSpanNamePatternsToIgnore; + } + + public void setAdditionalSpanNamePatternsToIgnore(List additionalSpanNamePatternsToIgnore) { + this.additionalSpanNamePatternsToIgnore = additionalSpanNamePatternsToIgnore; + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/SpanIgnoringSpanHandler.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/SpanIgnoringSpanFilter.java similarity index 69% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/SpanIgnoringSpanHandler.java rename to spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/SpanIgnoringSpanFilter.java index b20308608..582983c26 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/SpanIgnoringSpanHandler.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/SpanIgnoringSpanFilter.java @@ -23,46 +23,29 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; import java.util.stream.Collectors; -import brave.handler.MutableSpan; -import brave.handler.SpanHandler; -import brave.propagation.TraceContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.api.exporter.SpanFilter; import org.springframework.util.StringUtils; /** - * {@link SpanHandler} that ignores spans via names. + * {@link SpanFilter} that ignores spans via names. * * @author Marcin Grzejszczak * @since 3.0.0 */ -class SpanIgnoringSpanHandler extends SpanHandler { +class SpanIgnoringSpanFilter implements SpanFilter { - private static final Log log = LogFactory.getLog(SpanIgnoringSpanHandler.class); + private static final Log log = LogFactory.getLog(SpanIgnoringSpanFilter.class); - private final SleuthProperties sleuthProperties; + private final SleuthSpanFilterProperties sleuthSpanFilterProperties; static final Map cache = new ConcurrentHashMap<>(); - SpanIgnoringSpanHandler(SleuthProperties sleuthProperties) { - this.sleuthProperties = sleuthProperties; - } - - @Override - public boolean end(TraceContext context, MutableSpan span, Cause cause) { - if (cause != Cause.FINISHED) { - return true; - } - List spanNamesToIgnore = spanNamesToIgnore(); - String name = span.name(); - if (StringUtils.hasText(name) && spanNamesToIgnore.stream().anyMatch(p -> p.matcher(name).matches())) { - if (log.isDebugEnabled()) { - log.debug("Will ignore a span with name [" + name + "]"); - } - return false; - } - return super.end(context, span, cause); + SpanIgnoringSpanFilter(SleuthSpanFilterProperties sleuthSpanFilterProperties) { + this.sleuthSpanFilterProperties = sleuthSpanFilterProperties; } private List spanNamesToIgnore() { @@ -71,10 +54,22 @@ class SpanIgnoringSpanHandler extends SpanHandler { } private List spanNames() { - List spanNamesToIgnore = new ArrayList<>( - this.sleuthProperties.getSpanHandler().getSpanNamePatternsToSkip()); - spanNamesToIgnore.addAll(this.sleuthProperties.getSpanHandler().getAdditionalSpanNamePatternsToIgnore()); + List spanNamesToIgnore = new ArrayList<>(this.sleuthSpanFilterProperties.getSpanNamePatternsToSkip()); + spanNamesToIgnore.addAll(this.sleuthSpanFilterProperties.getAdditionalSpanNamePatternsToIgnore()); return spanNamesToIgnore; } + @Override + public boolean isExportable(FinishedSpan span) { + List spanNamesToIgnore = spanNamesToIgnore(); + String name = span.name(); + if (StringUtils.hasText(name) && spanNamesToIgnore.stream().anyMatch(p -> p.matcher(name).matches())) { + if (log.isDebugEnabled()) { + log.debug("Will ignore a span with name [" + name + "]"); + } + return false; + } + return true; + } + } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfiguration.java index 39a45c433..af7c9105e 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfiguration.java @@ -16,32 +16,25 @@ package org.springframework.cloud.sleuth.autoconfig; -import java.util.Collections; -import java.util.List; - -import brave.CurrentSpanCustomizer; -import brave.Tracer; -import brave.Tracing; -import brave.TracingCustomizer; -import brave.handler.SpanHandler; -import brave.propagation.CurrentTraceContext; -import brave.propagation.CurrentTraceContextCustomizer; -import brave.propagation.Propagation; -import brave.propagation.ThreadLocalCurrentTraceContext; -import brave.sampler.Sampler; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.sleuth.LocalServiceName; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.SpanCustomizer; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.exporter.SpanFilter; +import org.springframework.cloud.sleuth.api.noop.NoOpCurrentTraceContext; +import org.springframework.cloud.sleuth.api.noop.NoOpPropagator; +import org.springframework.cloud.sleuth.api.noop.NoOpSpanCustomizer; +import org.springframework.cloud.sleuth.api.noop.NoOpTracer; +import org.springframework.cloud.sleuth.api.propagation.Propagator; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; -import org.springframework.cloud.sleuth.sampler.SamplerAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.lang.Nullable; -import org.springframework.util.StringUtils; /** * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration @@ -54,95 +47,49 @@ import org.springframework.util.StringUtils; */ @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(value = "spring.sleuth.enabled", matchIfMissing = true) -@EnableConfigurationProperties(SleuthProperties.class) -@Import({ TraceBaggageConfiguration.class, SamplerAutoConfiguration.class }) -// public allows @AutoConfigureAfter(TraceAutoConfiguration) -// for components needing Tracing +@EnableConfigurationProperties({ SleuthSpanFilterProperties.class, SleuthBaggageProperties.class }) public class TraceAutoConfiguration { - /** - * Tracer bean name. Name of the bean matters for some instrumentations. - */ - public static final String TRACER_BEAN_NAME = "tracer"; - - /** - * Default value used for service name if none provided. - */ - public static final String DEFAULT_SERVICE_NAME = "default"; + private static final Log log = LogFactory.getLog(TraceAutoConfiguration.class); @Bean @ConditionalOnMissingBean - // NOTE: stable bean name as might be used outside sleuth - Tracing tracing(@LocalServiceName String serviceName, Propagation.Factory factory, - CurrentTraceContext currentTraceContext, Sampler sampler, SleuthProperties sleuthProperties, - @Nullable List spanHandlers, @Nullable List tracingCustomizers) { - Tracing.Builder builder = Tracing.newBuilder().sampler(sampler) - .localServiceName(StringUtils.isEmpty(serviceName) ? DEFAULT_SERVICE_NAME : serviceName) - .propagationFactory(factory).currentTraceContext(currentTraceContext) - .traceId128Bit(sleuthProperties.isTraceId128()).supportsJoin(sleuthProperties.isSupportsJoin()); - if (spanHandlers != null) { - for (SpanHandler spanHandlerFactory : spanHandlers) { - builder.addSpanHandler(spanHandlerFactory); - } + Tracer defaultTracer() { + if (log.isWarnEnabled()) { + log.warn( + "You have not provided a tracer implementation. A default, noop one will be set up. You will not see any spans get reported to external systems (e.g. Zipkin) nor will any context get propagated."); } - if (tracingCustomizers != null) { - for (TracingCustomizer customizer : tracingCustomizers) { - customizer.customize(builder); - } - } - - return builder.build(); - } - - @Bean(name = TRACER_BEAN_NAME) - @ConditionalOnMissingBean - Tracer tracer(Tracing tracing) { - return tracing.tracer(); + return new NoOpTracer(); } @Bean @ConditionalOnMissingBean - SpanNamer sleuthSpanNamer() { + SpanNamer defaultSpanNamer() { return new DefaultSpanNamer(); } @Bean - CurrentTraceContext sleuthCurrentTraceContext(CurrentTraceContext.Builder builder, - @Nullable List scopeDecorators, - @Nullable List currentTraceContextCustomizers) { - if (scopeDecorators == null) { - scopeDecorators = Collections.emptyList(); - } - if (currentTraceContextCustomizers == null) { - currentTraceContextCustomizers = Collections.emptyList(); - } - - for (CurrentTraceContext.ScopeDecorator scopeDecorator : scopeDecorators) { - builder.addScopeDecorator(scopeDecorator); - } - for (CurrentTraceContextCustomizer customizer : currentTraceContextCustomizers) { - customizer.customize(builder); - } - return builder.build(); + @ConditionalOnMissingBean + Propagator defaultPropagator() { + return new NoOpPropagator(); } @Bean @ConditionalOnMissingBean - CurrentTraceContext.Builder sleuthCurrentTraceContextBuilder() { - return ThreadLocalCurrentTraceContext.newBuilder(); + CurrentTraceContext defaultCurrentTraceContext() { + return new NoOpCurrentTraceContext(); } @Bean @ConditionalOnMissingBean - // NOTE: stable bean name as might be used outside sleuth - CurrentSpanCustomizer spanCustomizer(Tracing tracing) { - return CurrentSpanCustomizer.create(tracing); + SpanCustomizer defaultSpanCustomizer() { + return new NoOpSpanCustomizer(); } @Bean - @ConditionalOnProperty(value = "spring.sleuth.span-handler.enabled", matchIfMissing = true) - SpanHandler spanIgnoringSpanHandler(SleuthProperties sleuthProperties) { - return new SpanIgnoringSpanHandler(sleuthProperties); + @ConditionalOnProperty(value = "spring.sleuth.span-filter.enabled", matchIfMissing = true) + SpanFilter spanIgnoringSpanExporter(SleuthSpanFilterProperties sleuthSpanFilterProperties) { + return new SpanIgnoringSpanFilter(sleuthSpanFilterProperties); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/AsyncAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/AsyncAutoConfiguration.java index 8cf6dd3f9..fcc2439a5 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/AsyncAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/AsyncAutoConfiguration.java @@ -16,11 +16,10 @@ package org.springframework.cloud.sleuth.instrument.async; -import brave.Tracing; - import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -32,8 +31,8 @@ import org.springframework.context.annotation.Configuration; * @since 2.1.0 */ @Configuration(proxyBeanMethods = false) -@ConditionalOnBean(Tracing.class) -@ConditionalOnProperty(value = "spring.sleuth.scheduled.enabled", matchIfMissing = true) +@ConditionalOnBean(Tracer.class) +@ConditionalOnProperty(value = "spring.sleuth.async.enabled", matchIfMissing = true) @EnableConfigurationProperties(SleuthAsyncProperties.class) class AsyncAutoConfiguration { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/AsyncDefaultAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/AsyncDefaultAutoConfiguration.java index 199bbd6f0..6f1eb3505 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/AsyncDefaultAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/AsyncDefaultAutoConfiguration.java @@ -18,8 +18,6 @@ package org.springframework.cloud.sleuth.instrument.async; import java.util.concurrent.Executor; -import brave.Tracer; -import brave.Tracing; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -34,6 +32,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Role; @@ -55,7 +54,7 @@ import org.springframework.scheduling.annotation.AsyncConfigurerSupport; @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(SleuthAsyncProperties.class) @ConditionalOnProperty(value = "spring.sleuth.async.enabled", matchIfMissing = true) -@ConditionalOnBean(Tracing.class) +@ConditionalOnBean(Tracer.class) class AsyncDefaultAutoConfiguration { @Bean diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceAsyncTaskExecutor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceAsyncTaskExecutor.java index 691da2e65..e1fa5a734 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceAsyncTaskExecutor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceAsyncTaskExecutor.java @@ -19,13 +19,13 @@ package org.springframework.cloud.sleuth.instrument.async; import java.util.concurrent.Callable; import java.util.concurrent.Future; -import brave.Tracing; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; import org.springframework.core.task.AsyncTaskExecutor; @@ -47,7 +47,7 @@ public class LazyTraceAsyncTaskExecutor implements AsyncTaskExecutor { private final String beanName; - private Tracing tracing; + private Tracer tracing; private SpanNamer spanNamer; @@ -113,10 +113,10 @@ public class LazyTraceAsyncTaskExecutor implements AsyncTaskExecutor { return this.spanNamer; } - private Tracing tracing() { + private Tracer tracing() { if (this.tracing == null) { try { - this.tracing = this.beanFactory.getBean(Tracing.class); + this.tracing = this.beanFactory.getBean(Tracer.class); } catch (NoSuchBeanDefinitionException e) { return null; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceExecutor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceExecutor.java index ee5814d7a..c233ff40f 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceExecutor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceExecutor.java @@ -18,13 +18,13 @@ package org.springframework.cloud.sleuth.instrument.async; import java.util.concurrent.Executor; -import brave.Tracing; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; /** @@ -44,7 +44,7 @@ public class LazyTraceExecutor implements Executor { private final String beanName; - private Tracing tracing; + private Tracer tracer; private SpanNamer spanNamer; @@ -66,16 +66,16 @@ public class LazyTraceExecutor implements Executor { this.delegate.execute(command); return; } - if (this.tracing == null) { + if (this.tracer == null) { try { - this.tracing = this.beanFactory.getBean(Tracing.class); + this.tracer = this.beanFactory.getBean(Tracer.class); } catch (NoSuchBeanDefinitionException e) { this.delegate.execute(command); return; } } - this.delegate.execute(new TraceRunnable(this.tracing, spanNamer(), command, this.beanName)); + this.delegate.execute(new TraceRunnable(this.tracer, spanNamer(), command, this.beanName)); } // due to some race conditions trace keys might not be ready yet diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceScheduledThreadPoolExecutor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceScheduledThreadPoolExecutor.java index 0575551e4..2cf81b2ad 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceScheduledThreadPoolExecutor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceScheduledThreadPoolExecutor.java @@ -33,13 +33,13 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import brave.Tracing; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; import org.springframework.util.ReflectionUtils; @@ -77,7 +77,7 @@ class LazyTraceScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor { private final Method newTaskForCallable; - private Tracing tracing; + private Tracer tracing; private SpanNamer spanNamer; @@ -443,9 +443,9 @@ class LazyTraceScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor { return this.delegate.invokeAll(wrapCallableCollection(tasks), timeout, unit); } - private Tracing tracing() { + private Tracer tracing() { if (this.tracing == null) { - this.tracing = this.beanFactory.getBean(Tracing.class); + this.tracing = this.beanFactory.getBean(Tracer.class); } return this.tracing; } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskExecutor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskExecutor.java index 919161657..dee2c4333 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskExecutor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskExecutor.java @@ -22,13 +22,13 @@ import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; -import brave.Tracing; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; import org.springframework.core.task.TaskDecorator; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @@ -41,7 +41,6 @@ import org.springframework.util.concurrent.ListenableFuture; * @since 1.0.10 */ @SuppressWarnings("serial") -// public as most types in this package were documented for use public class LazyTraceThreadPoolTaskExecutor extends ThreadPoolTaskExecutor { private static final Log log = LogFactory.getLog(LazyTraceThreadPoolTaskExecutor.class); @@ -52,7 +51,7 @@ public class LazyTraceThreadPoolTaskExecutor extends ThreadPoolTaskExecutor { private final String beanName; - private Tracing tracing; + private Tracer tracer; private SpanNamer spanNamer; @@ -272,11 +271,11 @@ public class LazyTraceThreadPoolTaskExecutor extends ThreadPoolTaskExecutor { this.delegate.setTaskDecorator(taskDecorator); } - private Tracing tracing() { - if (this.tracing == null) { - this.tracing = this.beanFactory.getBean(Tracing.class); + private Tracer tracing() { + if (this.tracer == null) { + this.tracer = this.beanFactory.getBean(Tracer.class); } - return this.tracing; + return this.tracer; } private SpanNamer spanNamer() { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskScheduler.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskScheduler.java index 4fe9d4c6f..6c6494f25 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskScheduler.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskScheduler.java @@ -29,13 +29,13 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; -import brave.Tracing; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; import org.springframework.lang.Nullable; import org.springframework.scheduling.Trigger; @@ -73,7 +73,7 @@ class LazyTraceThreadPoolTaskScheduler extends ThreadPoolTaskScheduler { private final Method getDefaultThreadNamePrefix; - private Tracing tracing; + private Tracer tracing; private SpanNamer spanNamer; @@ -394,9 +394,9 @@ class LazyTraceThreadPoolTaskScheduler extends ThreadPoolTaskScheduler { delay); } - private Tracing tracing() { + private Tracer tracing() { if (this.tracing == null) { - this.tracing = this.beanFactory.getBean(Tracing.class); + this.tracing = this.beanFactory.getBean(Tracer.class); } return this.tracing; } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/SleuthAsyncProperties.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/SleuthAsyncProperties.java index be249c804..10450e663 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/SleuthAsyncProperties.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/SleuthAsyncProperties.java @@ -30,6 +30,16 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "spring.sleuth.async") class SleuthAsyncProperties { + private boolean enabled; + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + /** * List of {@link java.util.concurrent.Executor} bean names that should be ignored and * not wrapped in a trace representation. diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncAspect.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncAspect.java index 6dc22c95a..c63f0b4d6 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncAspect.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncAspect.java @@ -18,14 +18,14 @@ package org.springframework.cloud.sleuth.instrument.async; import java.lang.reflect.Method; -import brave.Span; -import brave.Tracer; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.internal.SpanNameUtil; import org.springframework.util.ReflectionUtils; @@ -61,13 +61,13 @@ class TraceAsyncAspect { span = this.tracer.nextSpan(); } span = span.name(spanName); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { span.tag(CLASS_KEY, pjp.getTarget().getClass().getSimpleName()); span.tag(METHOD_KEY, pjp.getSignature().getName()); return pjp.proceed(); } finally { - span.finish(); + span.end(); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncListenableTaskExecutor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncListenableTaskExecutor.java index 92785f857..ff24a9861 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncListenableTaskExecutor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncListenableTaskExecutor.java @@ -19,8 +19,8 @@ package org.springframework.cloud.sleuth.instrument.async; import java.util.concurrent.Callable; import java.util.concurrent.Future; -import brave.Tracing; - +import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.core.task.AsyncListenableTaskExecutor; import org.springframework.util.concurrent.ListenableFuture; @@ -30,49 +30,50 @@ import org.springframework.util.concurrent.ListenableFuture; * * @author Marcin Grzejszczak * @since 1.0.0 - * @see brave.propagation.CurrentTraceContext#wrap(Runnable) - * @see brave.propagation.CurrentTraceContext#wrap(Callable) */ // public as most types in this package were documented for use public class TraceAsyncListenableTaskExecutor implements AsyncListenableTaskExecutor { private final AsyncListenableTaskExecutor delegate; - private final Tracing tracing; + private final Tracer tracer; - TraceAsyncListenableTaskExecutor(AsyncListenableTaskExecutor delegate, Tracing tracing) { + private final SpanNamer spanNamer; + + TraceAsyncListenableTaskExecutor(AsyncListenableTaskExecutor delegate, Tracer tracer, SpanNamer spanNamer) { this.delegate = delegate; - this.tracing = tracing; + this.tracer = tracer; + this.spanNamer = spanNamer; } @Override public ListenableFuture submitListenable(Runnable task) { - return this.delegate.submitListenable(this.tracing.currentTraceContext().wrap(task)); + return this.delegate.submitListenable(new TraceRunnable(this.tracer, this.spanNamer, task)); } @Override public ListenableFuture submitListenable(Callable task) { - return this.delegate.submitListenable(this.tracing.currentTraceContext().wrap(task)); + return this.delegate.submitListenable(new TraceCallable<>(this.tracer, this.spanNamer, task)); } @Override public void execute(Runnable task, long startTimeout) { - this.delegate.execute(this.tracing.currentTraceContext().wrap(task), startTimeout); + this.delegate.execute(new TraceRunnable(this.tracer, this.spanNamer, task), startTimeout); } @Override public Future submit(Runnable task) { - return this.delegate.submit(this.tracing.currentTraceContext().wrap(task)); + return this.delegate.submit(new TraceRunnable(this.tracer, this.spanNamer, task)); } @Override public Future submit(Callable task) { - return this.delegate.submit(this.tracing.currentTraceContext().wrap(task)); + return this.delegate.submit(new TraceCallable<>(this.tracer, this.spanNamer, task)); } @Override public void execute(Runnable task) { - this.delegate.execute(this.tracing.currentTraceContext().wrap(task)); + this.delegate.execute(new TraceRunnable(this.tracer, this.spanNamer, task)); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceCallable.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceCallable.java index 4ead089eb..0e9f334d7 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceCallable.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceCallable.java @@ -18,12 +18,9 @@ package org.springframework.cloud.sleuth.instrument.async; import java.util.concurrent.Callable; -import brave.ScopedSpan; -import brave.Tracer; -import brave.Tracing; -import brave.propagation.TraceContext; - import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; /** * Callable that passes Span between threads. The Span name is taken either from the @@ -47,33 +44,33 @@ public class TraceCallable implements Callable { private final Callable delegate; - private final TraceContext parent; + private final Span parent; private final String spanName; - public TraceCallable(Tracing tracing, SpanNamer spanNamer, Callable delegate) { - this(tracing, spanNamer, delegate, null); + public TraceCallable(Tracer tracer, SpanNamer spanNamer, Callable delegate) { + this(tracer, spanNamer, delegate, null); } - public TraceCallable(Tracing tracing, SpanNamer spanNamer, Callable delegate, String name) { - this.tracer = tracing.tracer(); + public TraceCallable(Tracer tracer, SpanNamer spanNamer, Callable delegate, String name) { + this.tracer = tracer; this.delegate = delegate; - this.parent = tracing.currentTraceContext().get(); + this.parent = tracer.currentSpan(); this.spanName = name != null ? name : spanNamer.name(delegate, DEFAULT_SPAN_NAME); } @Override public V call() throws Exception { - ScopedSpan span = this.tracer.startScopedSpanWithParent(this.spanName, this.parent); - try { + Span childSpan = this.tracer.nextSpan(this.parent).name(this.spanName); + try (Tracer.SpanInScope ws = this.tracer.withSpan(childSpan.start())) { return this.delegate.call(); } catch (Exception | Error ex) { - span.error(ex); + childSpan.error(ex); throw ex; } finally { - span.finish(); + childSpan.end(); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnable.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnable.java index be4a46a24..b5cc62a69 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnable.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnable.java @@ -16,12 +16,9 @@ package org.springframework.cloud.sleuth.instrument.async; -import brave.ScopedSpan; -import brave.Tracer; -import brave.Tracing; -import brave.propagation.TraceContext; - import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; /** * Runnable that passes Span between threads. The Span name is taken either from the @@ -44,33 +41,33 @@ public class TraceRunnable implements Runnable { private final Runnable delegate; - private final TraceContext parent; + private final Span parent; private final String spanName; - public TraceRunnable(Tracing tracing, SpanNamer spanNamer, Runnable delegate) { - this(tracing, spanNamer, delegate, null); + public TraceRunnable(Tracer tracer, SpanNamer spanNamer, Runnable delegate) { + this(tracer, spanNamer, delegate, null); } - public TraceRunnable(Tracing tracing, SpanNamer spanNamer, Runnable delegate, String name) { - this.tracer = tracing.tracer(); + public TraceRunnable(Tracer tracer, SpanNamer spanNamer, Runnable delegate, String name) { + this.tracer = tracer; this.delegate = delegate; - this.parent = tracing.currentTraceContext().get(); + this.parent = tracer.currentSpan(); this.spanName = name != null ? name : spanNamer.name(delegate, DEFAULT_SPAN_NAME); } @Override public void run() { - ScopedSpan span = this.tracer.startScopedSpanWithParent(this.spanName, this.parent); - try { + Span childSpan = this.tracer.nextSpan(this.parent).name(this.spanName); + try (Tracer.SpanInScope ws = this.tracer.withSpan(childSpan.start())) { this.delegate.run(); } catch (Exception | Error e) { - span.error(e); + childSpan.error(e); throw e; } finally { - span.finish(); + childSpan.end(); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorService.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorService.java index 88b18f897..28defc50a 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorService.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorService.java @@ -26,10 +26,9 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import brave.Tracing; - import org.springframework.beans.factory.BeanFactory; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.api.Tracer; /** * A decorator class for {@link ExecutorService} to support tracing in Executors. @@ -44,7 +43,7 @@ public class TraceableExecutorService implements ExecutorService { final String spanName; - Tracing tracing; + Tracer tracer; SpanNamer spanNamer; @@ -63,7 +62,7 @@ public class TraceableExecutorService implements ExecutorService { @Override public void execute(Runnable command) { this.delegate.execute(ContextUtil.isContextUnusable(this.beanFactory) ? command - : new TraceRunnable(tracing(), spanNamer(), command, this.spanName)); + : new TraceRunnable(tracer(), spanNamer(), command, this.spanName)); } @Override @@ -94,19 +93,19 @@ public class TraceableExecutorService implements ExecutorService { @Override public Future submit(Callable task) { return this.delegate.submit(ContextUtil.isContextUnusable(this.beanFactory) ? task - : new TraceCallable<>(tracing(), spanNamer(), task, this.spanName)); + : new TraceCallable<>(tracer(), spanNamer(), task, this.spanName)); } @Override public Future submit(Runnable task, T result) { return this.delegate.submit(ContextUtil.isContextUnusable(this.beanFactory) ? task - : new TraceRunnable(tracing(), spanNamer(), task, this.spanName), result); + : new TraceRunnable(tracer(), spanNamer(), task, this.spanName), result); } @Override public Future submit(Runnable task) { return this.delegate.submit(ContextUtil.isContextUnusable(this.beanFactory) ? task - : new TraceRunnable(tracing(), spanNamer(), task, this.spanName)); + : new TraceRunnable(tracer(), spanNamer(), task, this.spanName)); } @Override @@ -139,17 +138,17 @@ public class TraceableExecutorService implements ExecutorService { List> ts = new ArrayList<>(); for (Callable task : tasks) { if (!(task instanceof TraceCallable)) { - ts.add(new TraceCallable<>(tracing(), spanNamer(), task, this.spanName)); + ts.add(new TraceCallable<>(tracer(), spanNamer(), task, this.spanName)); } } return ts; } - Tracing tracing() { - if (this.tracing == null && this.beanFactory != null) { - this.tracing = this.beanFactory.getBean(Tracing.class); + Tracer tracer() { + if (this.tracer == null && this.beanFactory != null) { + this.tracer = this.beanFactory.getBean(Tracer.class); } - return this.tracing; + return this.tracer; } SpanNamer spanNamer() { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorService.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorService.java index 2bd77df93..67875abd9 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorService.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorService.java @@ -48,21 +48,22 @@ public class TraceableScheduledExecutorService extends TraceableExecutorService @Override public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { return getScheduledExecutorService().schedule(ContextUtil.isContextUnusable(this.beanFactory) ? command - : new TraceRunnable(tracing(), spanNamer(), command, this.spanName), delay, unit); + : new TraceRunnable(tracer(), spanNamer(), command, this.spanName), delay, unit); } @Override public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) { return getScheduledExecutorService().schedule(ContextUtil.isContextUnusable(this.beanFactory) ? callable - : new TraceCallable<>(tracing(), spanNamer(), callable, this.spanName), delay, unit); + : new TraceCallable<>(tracer(), spanNamer(), callable, this.spanName), delay, unit); } @Override public ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { - return getScheduledExecutorService().scheduleAtFixedRate( - ContextUtil.isContextUnusable(this.beanFactory) ? command - : new TraceRunnable(tracing(), spanNamer(), command, this.spanName), - initialDelay, period, unit); + return getScheduledExecutorService() + .scheduleAtFixedRate( + ContextUtil.isContextUnusable(this.beanFactory) ? command + : new TraceRunnable(tracer(), spanNamer(), command, this.spanName), + initialDelay, period, unit); } @Override @@ -70,7 +71,7 @@ public class TraceableScheduledExecutorService extends TraceableExecutorService return getScheduledExecutorService() .scheduleWithFixedDelay( ContextUtil.isContextUnusable(this.beanFactory) ? command - : new TraceRunnable(tracing(), spanNamer(), command, this.spanName), + : new TraceRunnable(tracer(), spanNamer(), command, this.spanName), initialDelay, delay, unit); } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/SleuthCircuitBreakerAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/SleuthCircuitBreakerAutoConfiguration.java index 2838ddf33..fa6376482 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/SleuthCircuitBreakerAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/SleuthCircuitBreakerAutoConfiguration.java @@ -19,8 +19,6 @@ package org.springframework.cloud.sleuth.instrument.circuitbreaker; import java.util.function.Function; import java.util.function.Supplier; -import brave.Tracer; -import brave.Tracing; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @@ -32,6 +30,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.client.circuitbreaker.CircuitBreaker; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -46,7 +45,7 @@ import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) @AutoConfigureAfter(TraceAutoConfiguration.class) @ConditionalOnClass(CircuitBreaker.class) -@ConditionalOnBean(Tracing.class) +@ConditionalOnBean(Tracer.class) @ConditionalOnProperty(value = "spring.sleuth.circuitbreaker.enabled", matchIfMissing = true) @EnableConfigurationProperties(SleuthCircuitBreakerProperties.class) class SleuthCircuitBreakerAutoConfiguration { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/TraceFunction.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/TraceFunction.java index 3830827e5..6d547791b 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/TraceFunction.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/TraceFunction.java @@ -19,8 +19,8 @@ package org.springframework.cloud.sleuth.instrument.circuitbreaker; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; -import brave.Span; -import brave.Tracer; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; /** * Trace representation of a {@link Function}. @@ -47,7 +47,7 @@ class TraceFunction implements Function { String name = this.delegate.getClass().getSimpleName(); Span span = this.span.get().name(name); Throwable tr = null; - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { return this.delegate.apply(throwable); } catch (Throwable t) { @@ -56,10 +56,9 @@ class TraceFunction implements Function { } finally { if (tr != null) { - String message = tr.getMessage() == null ? tr.getClass().getSimpleName() : tr.getMessage(); - span.tag("error", message); + span.error(tr); } - span.finish(); + span.end(); this.span.set(null); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/TraceSupplier.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/TraceSupplier.java index bbf4ab0ef..33e00d3d6 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/TraceSupplier.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/TraceSupplier.java @@ -19,8 +19,8 @@ package org.springframework.cloud.sleuth.instrument.circuitbreaker; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; -import brave.Span; -import brave.Tracer; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; /** * Trace representation of a {@link Supplier}. @@ -47,7 +47,7 @@ class TraceSupplier implements Supplier { String name = this.delegate.getClass().getSimpleName(); Span span = this.span.get().name(name); Throwable tr = null; - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { return this.delegate.get(); } catch (Throwable t) { @@ -56,10 +56,9 @@ class TraceSupplier implements Supplier { } finally { if (tr != null) { - String message = tr.getMessage() == null ? tr.getClass().getSimpleName() : tr.getMessage(); - span.tag("error", message); + span.error(tr); } - span.finish(); + span.end(); this.span.set(null); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessageHeaderPropagation.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessageHeaderPropagation.java index 0f29a76a8..8a23d3ca7 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessageHeaderPropagation.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessageHeaderPropagation.java @@ -22,10 +22,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import brave.propagation.Propagation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.cloud.sleuth.api.propagation.Propagator; import org.springframework.messaging.support.MessageHeaderAccessor; import org.springframework.messaging.support.NativeMessageHeaderAccessor; import org.springframework.util.LinkedMultiValueMap; @@ -37,8 +37,8 @@ import org.springframework.util.StringUtils; * * @author Marcin Grzejszczak */ -enum MessageHeaderPropagation implements Propagation.Setter, - Propagation.Getter { +enum MessageHeaderPropagation + implements Propagator.Setter, Propagator.Getter { INSTANCE; @@ -93,7 +93,7 @@ enum MessageHeaderPropagation implements Propagation.Setter - type of payload - * @return message with tracing context + * @return message with tracer context */ - public static Message forInputMessage(Tracing tracing, Message message, + public static Message forInputMessage(BeanFactory beanFactory, Message message, Consumer> withSpanInScope) { - TraceMessageHandler traceMessageHandler = TraceMessageHandler.forNonSpringIntegration(tracing); + TraceMessageHandler traceMessageHandler = TraceMessageHandler.forNonSpringIntegration(beanFactory); MessageAndSpans wrappedInputMessage = traceMessageHandler.wrapInputMessage(message, ""); if (log.isDebugEnabled()) { log.debug("Wrapped input msg " + wrappedInputMessage); } - Tracer tracer = tracing.tracer(); Throwable t = null; - try (Tracer.SpanInScope ws = tracer.withSpanInScope(wrappedInputMessage.childSpan.start())) { + try (Tracer.SpanInScope ws = traceMessageHandler.tracer.withSpan(wrappedInputMessage.childSpan.start())) { withSpanInScope.accept(wrappedInputMessage.msg); } catch (Exception e) { @@ -86,13 +84,13 @@ public final class MessagingSleuthOperators { /** * Processes the input message and returns a message with a header containing a span. - * @param tracing - tracing bean + * @param beanFactory - bean factory * @param message - input message to process * @param - payload type - * @return message with tracing context + * @return message with tracer context */ - public static Message forInputMessage(Tracing tracing, Message message) { - TraceMessageHandler traceMessageHandler = TraceMessageHandler.forNonSpringIntegration(tracing); + public static Message forInputMessage(BeanFactory beanFactory, Message message) { + TraceMessageHandler traceMessageHandler = TraceMessageHandler.forNonSpringIntegration(beanFactory); MessageAndSpans wrappedInputMessage = traceMessageHandler.wrapInputMessage(message, ""); if (log.isDebugEnabled()) { log.debug("Wrapped input msg " + wrappedInputMessage); @@ -101,25 +99,29 @@ public final class MessagingSleuthOperators { } /** - * Function converting an input message to a message with tracing headers. - * @param tracing - tracing bean + * Function converting an input message to a message with tracer headers. + * @param beanFactory - bean factory * @param inputMessage - input message to process * @param input message type - * @return function representation of input message with tracing context + * @return function representation of input message with tracer context */ - public static Function, Message> asFunction(Tracing tracing, Message inputMessage) { - return stringMessage -> MessagingSleuthOperators.forInputMessage(tracing, inputMessage); + public static Function, Message> asFunction(BeanFactory beanFactory, Message inputMessage) { + return stringMessage -> MessagingSleuthOperators.forInputMessage(beanFactory, inputMessage); } /** - * Retrieves tracing information from message headers. - * @param tracing - tracing bean + * Retrieves tracer information from message headers. + * @param beanFactory - bean factory * @param message - message to process * @param - payload type * @return span retrieved from message or {@code null} if there was no span */ - public static Span spanFromMessage(Tracing tracing, Message message) { - TraceMessageHandler traceMessageHandler = TraceMessageHandler.forNonSpringIntegration(tracing); + public static Span spanFromMessage(BeanFactory beanFactory, Message message) { + TraceMessageHandler traceMessageHandler = TraceMessageHandler.forNonSpringIntegration(beanFactory); + return spanFromMessage(traceMessageHandler, message); + } + + private static Span spanFromMessage(TraceMessageHandler traceMessageHandler, Message message) { Span span = traceMessageHandler.spanFromMessage(message); if (log.isDebugEnabled()) { log.debug("Found the following span in message " + span); @@ -128,73 +130,73 @@ public final class MessagingSleuthOperators { } /** - * Retrieves tracing information from message headers and applies the operation. - * @param tracing - tracing bean + * Retrieves tracer information from message headers and applies the operation. + * @param beanFactory - bean factory * @param message - message to process * @param withSpanInScope - an operation that will be wrapped in a span but will not * be reported * @param - payload type */ - public static void withSpanInScope(Tracing tracing, Message message, Consumer> withSpanInScope) { - Span span = spanFromMessage(tracing, message); - Tracer tracer = tracing.tracer(); - try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) { + public static void withSpanInScope(BeanFactory beanFactory, Message message, + Consumer> withSpanInScope) { + TraceMessageHandler traceMessageHandler = TraceMessageHandler.forNonSpringIntegration(beanFactory); + Span span = spanFromMessage(traceMessageHandler, message); + try (Tracer.SpanInScope ws = traceMessageHandler.tracer.withSpan(span)) { withSpanInScope.accept(message); } } /** - * Retrieves tracing information from message headers and applies the operation. - * @param tracing - tracing bean + * Retrieves tracer information from message headers and applies the operation. + * @param beanFactory - bean factory * @param message - message to process * @param withSpanInScope - an operation that will be wrapped in a span but will not * be reported * @param - payload type - * @return a message with tracing headers. + * @return a message with tracer headers. */ - public static Message withSpanInScope(Tracing tracing, Message message, + public static Message withSpanInScope(BeanFactory beanFactory, Message message, Function, Message> withSpanInScope) { - Span span = spanFromMessage(tracing, message); - Tracer tracer = tracing.tracer(); - try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) { + TraceMessageHandler traceMessageHandler = TraceMessageHandler.forNonSpringIntegration(beanFactory); + Span span = spanFromMessage(traceMessageHandler, message); + try (Tracer.SpanInScope ws = traceMessageHandler.tracer.withSpan(span)) { return withSpanInScope.apply(message); } } /** - * Creates an output message with tracing headers and reports the corresponding + * Creates an output message with tracer headers and reports the corresponding * producer span. If the message contains a header called {@code destination} it will * be used to tag the span with destination name. - * @param tracing - tracing bean - * @param message - message to which tracing headers should be injected + * @param beanFactory - bean factory + * @param message - message to which tracer headers should be injected * @param - message payload * @return instrumented message */ - public static Message handleOutputMessage(Tracing tracing, Message message) { - return handleOutputMessage(tracing, message, null); + public static Message handleOutputMessage(BeanFactory beanFactory, Message message) { + return handleOutputMessage(beanFactory, message, null); } /** - * Creates an output message with tracing headers and reports the corresponding + * Creates an output message with tracer headers and reports the corresponding * producer span. If the message contains a header called {@code destination} it will * be used to tag the span with destination name. - * @param tracing - tracing bean - * @param message - message to which tracing headers should be injected + * @param beanFactory - bean factory + * @param message - message to which tracer headers should be injected * @param throwable - exception that took place while processing the message * @param - message payload * @return instrumented message */ - public static Message handleOutputMessage(Tracing tracing, Message message, Throwable throwable) { - TraceMessageHandler traceMessageHandler = TraceMessageHandler.forNonSpringIntegration(tracing); + public static Message handleOutputMessage(BeanFactory beanFactory, Message message, Throwable throwable) { + TraceMessageHandler traceMessageHandler = TraceMessageHandler.forNonSpringIntegration(beanFactory); Span span = traceMessageHandler.parentSpan(message); span = span != null ? span : traceMessageHandler.consumerSpan(message); if (span == null) { log.warn( - "Can't find neither parent nor consumer span. Will return the message with no tracing header changes"); + "Can't find neither parent nor consumer span. Will return the message with no tracer header changes"); return message; } - MessageAndSpan messageAndSpan = traceMessageHandler.wrapOutputMessage(message, - TraceContextOrSamplingFlags.create(span.context()), + MessageAndSpan messageAndSpan = traceMessageHandler.wrapOutputMessage(message, span, String.valueOf(message.getHeaders().getOrDefault("destination", ""))); traceMessageHandler.afterMessageHandled(messageAndSpan.span, throwable); return messageAndSpan.msg; @@ -202,14 +204,14 @@ public final class MessagingSleuthOperators { /** * Reports the span stored in the message. - * @param tracing - tracing bean - * @param message - message with tracing context + * @param beanFactory - bean factory + * @param message - message with tracer context * @param ex - potential exception that took place while processing * @param - message payload * @return instrumented message */ - public static Message afterMessageHandled(Tracing tracing, Message message, Throwable ex) { - TraceMessageHandler traceMessageHandler = TraceMessageHandler.forNonSpringIntegration(tracing); + public static Message afterMessageHandled(BeanFactory beanFactory, Message message, Throwable ex) { + TraceMessageHandler traceMessageHandler = TraceMessageHandler.forNonSpringIntegration(beanFactory); Span span = traceMessageHandler.spanFromMessage(message); traceMessageHandler.afterMessageHandled(span, ex); return message; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/SleuthIntegrationMessagingProperties.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/SleuthIntegrationMessagingProperties.java new file mode 100644 index 000000000..403d64845 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/SleuthIntegrationMessagingProperties.java @@ -0,0 +1,59 @@ +/* + * Copyright 2013-2020 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.messaging; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Properties for Spring Integration messaging. + * + * @author Marcin Grzejszczak + * @since 2.0.0 + */ +@ConfigurationProperties("spring.sleuth.integration") +class SleuthIntegrationMessagingProperties { + + /** + * An array of patterns against which channel names will be matched. + * @see org.springframework.integration.config.GlobalChannelInterceptor#patterns() + * Defaults to any channel name not matching the Hystrix Stream and functional Stream + * channel names. + */ + private String[] patterns = new String[] { "!hystrixStreamOutput*", "*", "!channel*" }; + + /** + * Enable Spring Integration sleuth instrumentation. + */ + private boolean enabled; + + public String[] getPatterns() { + return this.patterns; + } + + public void setPatterns(String[] patterns) { + this.patterns = patterns; + } + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceFunctionAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceFunctionAutoConfiguration.java index 293459eac..014c42158 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceFunctionAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceFunctionAutoConfiguration.java @@ -19,9 +19,6 @@ package org.springframework.cloud.sleuth.instrument.messaging; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import brave.Tracer; -import brave.Tracing; -import brave.propagation.TraceContextOrSamplingFlags; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -32,6 +29,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent; import org.springframework.cloud.function.context.catalog.FunctionAroundWrapper; import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.propagation.Propagator; import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Bean; @@ -39,6 +38,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; +import org.springframework.messaging.support.MessageHeaderAccessor; /** * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration @@ -50,14 +50,15 @@ import org.springframework.messaging.support.MessageBuilder; */ @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(value = "spring.sleuth.function.enabled", matchIfMissing = true) -@ConditionalOnBean(Tracing.class) -@ConditionalOnClass({ Tracer.class, FunctionAroundWrapper.class }) +@ConditionalOnBean(Tracer.class) +@ConditionalOnClass({ FunctionAroundWrapper.class, RefreshScopeRefreshedEvent.class }) @AutoConfigureAfter(TraceAutoConfiguration.class) class TraceFunctionAutoConfiguration { @Bean - TraceFunctionAroundWrapper traceFunctionAroundWrapper(Environment environment, Tracing tracing) { - return new TraceFunctionAroundWrapper(environment, tracing); + TraceFunctionAroundWrapper traceFunctionAroundWrapper(Environment environment, Tracer tracer, Propagator propagator, + Propagator.Setter injector, Propagator.Getter extractor) { + return new TraceFunctionAroundWrapper(environment, tracer, propagator, injector, extractor); } } @@ -69,18 +70,29 @@ class TraceFunctionAroundWrapper extends FunctionAroundWrapper private final Environment environment; - private final Tracing tracing; + private final Tracer tracer; + + private final Propagator propagator; + + private final Propagator.Setter injector; + + private final Propagator.Getter extractor; final Map functionToDestinationCache = new ConcurrentHashMap<>(); - TraceFunctionAroundWrapper(Environment environment, Tracing tracing) { + TraceFunctionAroundWrapper(Environment environment, Tracer tracer, Propagator propagator, + Propagator.Setter injector, Propagator.Getter extractor) { this.environment = environment; - this.tracing = tracing; + this.tracer = tracer; + this.propagator = propagator; + this.injector = injector; + this.extractor = extractor; } @Override protected Object doApply(Message message, SimpleFunctionRegistry.FunctionInvocationWrapper targetFunction) { - TraceMessageHandler traceMessageHandler = TraceMessageHandler.forNonSpringIntegration(this.tracing); + TraceMessageHandler traceMessageHandler = TraceMessageHandler.forNonSpringIntegration(this.tracer, + this.propagator, this.injector, this.extractor); if (log.isDebugEnabled()) { log.debug("Will retrieve the tracing headers from the message"); } @@ -89,10 +101,9 @@ class TraceFunctionAroundWrapper extends FunctionAroundWrapper if (log.isDebugEnabled()) { log.debug("Wrapped input msg " + wrappedInputMessage); } - Tracer tracer = this.tracing.tracer(); Object result; Throwable throwable = null; - try (Tracer.SpanInScope ws = tracer.withSpanInScope(wrappedInputMessage.childSpan.start())) { + try (Tracer.SpanInScope ws = tracer.withSpan(wrappedInputMessage.childSpan.start())) { result = targetFunction.apply(wrappedInputMessage.msg); } catch (Exception e) { @@ -110,8 +121,7 @@ class TraceFunctionAroundWrapper extends FunctionAroundWrapper } Message msgResult = toMessage(result); MessageAndSpan wrappedOutputMessage = traceMessageHandler.wrapOutputMessage(msgResult, - TraceContextOrSamplingFlags.create(wrappedInputMessage.parentSpan.context()), - outputDestination(targetFunction)); + wrappedInputMessage.parentSpan, outputDestination(targetFunction)); if (log.isDebugEnabled()) { log.debug("Wrapped output msg " + wrappedOutputMessage); } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessageHandler.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessageHandler.java index ac84c62f3..c373a087f 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessageHandler.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessageHandler.java @@ -21,16 +21,17 @@ import java.util.Arrays; import java.util.List; import java.util.function.Function; -import brave.Span; -import brave.SpanCustomizer; -import brave.Tracer; -import brave.Tracing; -import brave.propagation.TraceContext; -import brave.propagation.TraceContextOrSamplingFlags; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.propagation.Propagator; import org.springframework.cloud.sleuth.internal.SpanNameUtil; +import org.springframework.core.ResolvableType; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.MessagingException; @@ -66,44 +67,62 @@ class TraceMessageHandler { private static final String TRACE_HANDLER_PARENT_SPAN = "traceHandlerParentSpan"; - private final Tracing tracing; + final Tracer tracer; - private final Tracer tracer; + private final Propagator propagator; - private final TraceContext.Injector injector; + private final Propagator.Setter injector; - private final TraceContext.Extractor extractor; + private final Propagator.Getter extractor; - private final Function preSendFunction; + private final Function preSendFunction; private final TriConsumer preSendMessageManipulator; - private final Function outputMessageSpanFunction; + private final Function outputMessageSpanFunction; - TraceMessageHandler(Tracing tracing, Function preSendFunction, + TraceMessageHandler(Tracer tracer, Propagator propagator, Propagator.Setter injector, + Propagator.Getter extractor, Function preSendFunction, TriConsumer preSendMessageManipulator, - Function outputMessageSpanFunction) { - this.tracing = tracing; - this.tracer = tracing.tracer(); - this.injector = tracing.propagation().injector(MessageHeaderPropagation.INSTANCE); - this.extractor = tracing.propagation().extractor(MessageHeaderPropagation.INSTANCE); + Function outputMessageSpanFunction) { + this.tracer = tracer; + this.propagator = propagator; + this.injector = injector; + this.extractor = extractor; // TODO: Abstractions to reuse in TraceChannelInterceptors? this.preSendFunction = preSendFunction; this.preSendMessageManipulator = preSendMessageManipulator; this.outputMessageSpanFunction = outputMessageSpanFunction; } - static TraceMessageHandler forNonSpringIntegration(Tracing tracing) { - Tracer tracer = tracing.tracer(); - Function preSendFunction = ctx -> tracer.nextSpan(TraceContextOrSamplingFlags.create(ctx)) - .name("handle").start(); + static TraceMessageHandler forNonSpringIntegration(Tracer tracer, Propagator propagator, + Propagator.Setter injector, Propagator.Getter extractor) { + Function preSendFunction = span -> tracer.nextSpan(span).name("handle").start(); TriConsumer preSendMessageManipulator = (headers, parentSpan, childSpan) -> { headers.setHeader("traceHandlerParentSpan", parentSpan); headers.setHeader(Span.class.getName(), childSpan); }; - Function postReceiveFunction = ctx -> tracer - .nextSpan(TraceContextOrSamplingFlags.create(ctx)); - return new TraceMessageHandler(tracing, preSendFunction, preSendMessageManipulator, postReceiveFunction); + Function postReceiveFunction = span -> tracer.spanBuilder().setParent(span.context()); + return new TraceMessageHandler(tracer, propagator, injector, extractor, preSendFunction, + preSendMessageManipulator, postReceiveFunction); + } + + @SuppressWarnings("unchecked") + static TraceMessageHandler forNonSpringIntegration(BeanFactory beanFactory) { + Propagator.Setter setter = firstBeanOrException(beanFactory, Propagator.Setter.class); + Propagator.Getter getter = firstBeanOrException(beanFactory, Propagator.Getter.class); + return forNonSpringIntegration(beanFactory.getBean(Tracer.class), beanFactory.getBean(Propagator.class), setter, + getter); + } + + private static T firstBeanOrException(BeanFactory beanFactory, Class clazz) { + ObjectProvider setterObjectProvider = beanFactory + .getBeanProvider(ResolvableType.forClassWithGenerics(clazz, MessageHeaderAccessor.class)); + T object = setterObjectProvider.iterator().hasNext() ? setterObjectProvider.iterator().next() : null; + if (object == null) { + throw new NoSuchBeanDefinitionException("No Propagator.Setter has been defined"); + } + return object; } /** @@ -115,24 +134,19 @@ class TraceMessageHandler { */ MessageAndSpans wrapInputMessage(Message message, String destinationName) { MessageHeaderAccessor headers = mutableHeaderAccessor(message); - TraceContextOrSamplingFlags extracted = this.extractor.extract(headers); + Span extracted = this.propagator.extract(headers, this.extractor).start(); // Start and finish a consumer span as we will immediately process it. - Span consumerSpan = this.tracer.nextSpan(extracted); - if (!consumerSpan.isNoop()) { - consumerSpan.kind(Span.Kind.CONSUMER).start(); - consumerSpan.remoteServiceName(REMOTE_SERVICE_NAME); - addTags(consumerSpan, destinationName); - consumerSpan.finish(); - } + Span.Builder consumerSpanBuilder = this.tracer.spanBuilder().setParent(extracted.context()); + Span consumerSpan = consumerSpan(destinationName, extracted, consumerSpanBuilder); // create and scope a span for the message processor - Span span = this.preSendFunction.apply(consumerSpan.context()); + Span span = this.preSendFunction.apply(consumerSpan); // remove any trace headers, but don't re-inject as we are synchronously // processing the // message and can rely on scoping to access this span later. clearTracingHeaders(headers); this.preSendMessageManipulator.accept(headers, consumerSpan, span); if (log.isDebugEnabled()) { - log.debug("Created a handle span after retrieving the message " + consumerSpan); + log.debug("Created a handle span after retrieving the message " + consumerSpanBuilder); } if (message instanceof ErrorMessage) { return new MessageAndSpans(new ErrorMessage((Throwable) message.getPayload(), headers.getMessageHeaders()), @@ -143,6 +157,21 @@ class TraceMessageHandler { consumerSpan, span); } + private Span consumerSpan(String destinationName, Span extracted, Span.Builder consumerSpanBuilder) { + Span consumerSpan; + if (!extracted.isNoop()) { + consumerSpanBuilder.kind(Span.Kind.CONSUMER).start(); + addTags(consumerSpanBuilder, destinationName); + consumerSpanBuilder.remoteServiceName(REMOTE_SERVICE_NAME); + consumerSpan = consumerSpanBuilder.start(); + consumerSpan.end(); + } + else { + consumerSpan = consumerSpanBuilder.start(); + } + return consumerSpan; + } + Span spanFromMessage(Message message) { MessageHeaderAccessor headers = mutableHeaderAccessor(message); Span span = span(headers, Span.class.getName()); @@ -153,14 +182,16 @@ class TraceMessageHandler { if (span != null) { return span; } - TraceContextOrSamplingFlags extracted = this.extractor.extract(headers); - if (extracted == TraceContextOrSamplingFlags.EMPTY) { - return null; - } - return this.tracer.nextSpan(extracted); + return this.propagator.extract(headers, this.extractor).start(); } - private void addTags(SpanCustomizer result, String destinationName) { + private void addTags(Span.Builder result, String destinationName) { + if (StringUtils.hasText(destinationName)) { + result.tag("channel", SpanNameUtil.shorten(destinationName)); + } + } + + private void addTags(Span result, String destinationName) { if (StringUtils.hasText(destinationName)) { result.tag("channel", SpanNameUtil.shorten(destinationName)); } @@ -197,26 +228,26 @@ class TraceMessageHandler { * @param destinationName - destination to which the message should be sent * @return a tuple with the wrapped message and a corresponding span */ - MessageAndSpan wrapOutputMessage(Message message, TraceContextOrSamplingFlags parentSpan, - String destinationName) { + MessageAndSpan wrapOutputMessage(Message message, Span parentSpan, String destinationName) { Message retrievedMessage = getMessage(message); MessageHeaderAccessor headers = mutableHeaderAccessor(retrievedMessage); - Span span = this.outputMessageSpanFunction.apply(parentSpan.context()); + Span.Builder span = this.outputMessageSpanFunction.apply(parentSpan); clearTracingHeaders(headers); - this.injector.inject(span.context(), headers); - markProducerSpan(headers, span, destinationName); + Span producerSpan = createProducerSpan(headers, span, destinationName); + this.propagator.inject(producerSpan.context(), headers, this.injector); if (log.isDebugEnabled()) { log.debug("Created a new span output message " + span); } - return new MessageAndSpan(outputMessage(message, retrievedMessage, headers), span); + return new MessageAndSpan(outputMessage(message, retrievedMessage, headers), producerSpan); } - private void markProducerSpan(MessageHeaderAccessor headers, Span span, String destinationName) { + private Span createProducerSpan(MessageHeaderAccessor headers, Span.Builder spanBuilder, String destinationName) { + spanBuilder.kind(Span.Kind.PRODUCER).name("send").remoteServiceName(toRemoteServiceName(headers)); + Span span = spanBuilder.start(); if (!span.isNoop()) { - span.kind(Span.Kind.PRODUCER).name("send").start(); - span.remoteServiceName(toRemoteServiceName(headers)); - addTags(span, destinationName); + addTags(spanBuilder, destinationName); } + return span; } private String toRemoteServiceName(MessageHeaderAccessor headers) { @@ -238,7 +269,7 @@ class TraceMessageHandler { if (originalMessage instanceof ErrorMessage) { ErrorMessage errorMessage = (ErrorMessage) originalMessage; headers.copyHeaders(MessageHeaderPropagation.propagationHeaders(additionalHeaders.getMessageHeaders(), - this.tracing.propagation().keys())); + this.propagator.fields())); return new ErrorMessage(errorMessage.getPayload(), isWebSockets(headers) ? headers.getMessageHeaders() : new MessageHeaders(headers.getMessageHeaders()), errorMessage.getOriginalMessage()); } @@ -269,7 +300,7 @@ class TraceMessageHandler { } private void clearTracingHeaders(MessageHeaderAccessor headers) { - List keysToRemove = new ArrayList<>(this.tracing.propagation().keys()); + List keysToRemove = new ArrayList<>(this.propagator.fields()); keysToRemove.add(Span.class.getName()); keysToRemove.add("traceHandlerParentSpan"); MessageHeaderPropagation.removeAnyTraceHeaders(headers, keysToRemove); @@ -291,7 +322,7 @@ class TraceMessageHandler { } span.tag("error", message); } - span.finish(); + span.end(); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceSpringIntegrationAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceSpringIntegrationAutoConfiguration.java index 4e89936f4..9b159b22a 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceSpringIntegrationAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceSpringIntegrationAutoConfiguration.java @@ -16,9 +16,6 @@ package org.springframework.cloud.sleuth.instrument.messaging; -import brave.Tracing; -import brave.propagation.Propagation; - import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -27,6 +24,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClas import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.function.context.FunctionCatalog; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.propagation.Propagator; import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ConditionContext; @@ -50,26 +49,27 @@ import org.springframework.util.ObjectUtils; */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass(GlobalChannelInterceptor.class) -@ConditionalOnBean(Tracing.class) +@ConditionalOnBean(Tracer.class) @AutoConfigureAfter({ TraceAutoConfiguration.class, TraceSpringMessagingAutoConfiguration.class }) -@OnMessagingEnabled -@EnableConfigurationProperties(SleuthMessagingProperties.class) +@ConditionalOnProperty(value = "spring.sleuth.messaging.enabled", matchIfMissing = true) +@EnableConfigurationProperties(SleuthIntegrationMessagingProperties.class) @Conditional(TracingChannelInterceptorCondition.class) class TraceSpringIntegrationAutoConfiguration { @Bean public GlobalChannelInterceptorWrapper tracingGlobalChannelInterceptorWrapper(TracingChannelInterceptor interceptor, - SleuthMessagingProperties properties) { + SleuthIntegrationMessagingProperties properties) { GlobalChannelInterceptorWrapper wrapper = new GlobalChannelInterceptorWrapper(interceptor); - wrapper.setPatterns(properties.getIntegration().getPatterns()); + wrapper.setPatterns(properties.getPatterns()); return wrapper; } @Bean - TracingChannelInterceptor traceChannelInterceptor(Tracing tracing, SleuthMessagingProperties properties, - Propagation.Setter traceMessagePropagationSetter, - Propagation.Getter traceMessagePropagationGetter) { - return new TracingChannelInterceptor(tracing, properties, traceMessagePropagationSetter, + TracingChannelInterceptor traceChannelInterceptor(Tracer tracer, Propagator propagator, + SleuthIntegrationMessagingProperties properties, + Propagator.Setter traceMessagePropagationSetter, + Propagator.Getter traceMessagePropagationGetter) { + return new TracingChannelInterceptor(tracer, propagator, properties, traceMessagePropagationSetter, traceMessagePropagationGetter); } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceSpringMessagingAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceSpringMessagingAutoConfiguration.java index aae0cdfbb..ac78b8484 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceSpringMessagingAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceSpringMessagingAutoConfiguration.java @@ -16,30 +16,30 @@ package org.springframework.cloud.sleuth.instrument.messaging; -import brave.propagation.Propagation; - import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.sleuth.api.propagation.Propagator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.support.MessageHeaderAccessor; @Configuration(proxyBeanMethods = false) @ConditionalOnClass(MessageHeaderAccessor.class) -@OnMessagingEnabled -@EnableConfigurationProperties(SleuthMessagingProperties.class) +@ConditionalOnProperty(value = "spring.sleuth.messaging.enabled", matchIfMissing = true) +@EnableConfigurationProperties(SleuthIntegrationMessagingProperties.class) class TraceSpringMessagingAutoConfiguration { @Bean @ConditionalOnMissingBean - Propagation.Setter traceMessagePropagationSetter() { + Propagator.Setter traceMessagePropagationSetter() { return MessageHeaderPropagation.INSTANCE; } @Bean @ConditionalOnMissingBean - Propagation.Getter traceMessagePropagationGetter() { + Propagator.Getter traceMessagePropagationGetter() { return MessageHeaderPropagation.INSTANCE; } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceWebSocketAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceWebSocketAutoConfiguration.java index 1b2de82d7..cbf094849 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceWebSocketAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceWebSocketAutoConfiguration.java @@ -16,12 +16,12 @@ package org.springframework.cloud.sleuth.instrument.messaging; -import brave.Tracing; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.propagation.Propagator; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.ChannelRegistration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; @@ -39,15 +39,18 @@ import org.springframework.web.socket.config.annotation.StompEndpointRegistry; */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass(DelegatingWebSocketMessageBrokerConfiguration.class) -@ConditionalOnBean(Tracing.class) +@ConditionalOnBean(Tracer.class) @ConditionalOnProperty(value = "spring.sleuth.integration.websockets.enabled", matchIfMissing = true) class TraceWebSocketAutoConfiguration extends AbstractWebSocketMessageBrokerConfigurer { @Autowired - Tracing tracing; + Tracer tracer; @Autowired - SleuthMessagingProperties properties; + Propagator propagator; + + @Autowired + SleuthIntegrationMessagingProperties properties; @Override public void registerStompEndpoints(StompEndpointRegistry registry) { @@ -57,17 +60,17 @@ class TraceWebSocketAutoConfiguration extends AbstractWebSocketMessageBrokerConf @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.configureBrokerChannel() - .setInterceptors(TracingChannelInterceptor.create(this.tracing, this.properties)); + .setInterceptors(TracingChannelInterceptor.create(this.tracer, this.propagator, this.properties)); } @Override public void configureClientOutboundChannel(ChannelRegistration registration) { - registration.setInterceptors(TracingChannelInterceptor.create(this.tracing, this.properties)); + registration.setInterceptors(TracingChannelInterceptor.create(this.tracer, this.propagator, this.properties)); } @Override public void configureClientInboundChannel(ChannelRegistration registration) { - registration.setInterceptors(TracingChannelInterceptor.create(this.tracing, this.properties)); + registration.setInterceptors(TracingChannelInterceptor.create(this.tracer, this.propagator, this.properties)); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptor.java index 527631cf7..833d4ad64 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptor.java @@ -16,19 +16,17 @@ package org.springframework.cloud.sleuth.instrument.messaging; -import brave.Span; -import brave.SpanCustomizer; -import brave.Tracer; -import brave.Tracing; -import brave.propagation.Propagation; -import brave.propagation.ThreadLocalSpan; -import brave.propagation.TraceContext; -import brave.propagation.TraceContextOrSamplingFlags; +import java.util.concurrent.LinkedBlockingDeque; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jetbrains.annotations.NotNull; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.propagation.Propagator; import org.springframework.cloud.sleuth.internal.SpanNameUtil; import org.springframework.integration.channel.AbstractMessageChannel; import org.springframework.integration.channel.DirectChannel; @@ -50,10 +48,7 @@ import org.springframework.util.ClassUtils; * native headers. It also extracts or creates a {@link Span.Kind#CONSUMER} span for each * message received. This span is injected onto each message so it becomes the parent when * a handler later calls {@link MessageHandler#handleMessage(Message)}, or a another - * processing library calls {@link #nextSpan(Message)}. This implementation uses - * {@link ThreadLocalSpan} to propagate context between callbacks. This is an alternative - * to {@code ThreadStatePropagationChannelInterceptor} which is less sensitive to message - * manipulation by other interceptors. + * processing library calls {@link #nextSpan(Message)}. * * @author Marcin Grzejszczak */ @@ -87,17 +82,13 @@ final class TracingChannelInterceptor extends ChannelInterceptorAdapter implemen */ private static final String REMOTE_SERVICE_NAME = "broker"; - final Tracing tracing; - final Tracer tracer; - final ThreadLocalSpan threadLocalSpan; + final Propagator.Setter injector; - final TraceContext.Injector injector; + final Propagator.Getter extractor; - final TraceContext.Extractor extractor; - - final SleuthMessagingProperties properties; + final SleuthIntegrationMessagingProperties properties; final boolean integrationObjectSupportPresent; @@ -106,20 +97,22 @@ final class TracingChannelInterceptor extends ChannelInterceptorAdapter implemen // special case of a Stream private final Class directWithAttributesChannelClass; + private final Propagator propagator; + + private final ThreadLocalSpan threadLocalSpan = new ThreadLocalSpan(); + @Autowired - TracingChannelInterceptor(Tracing tracing, SleuthMessagingProperties properties) { - this(tracing, properties, MessageHeaderPropagation.INSTANCE, MessageHeaderPropagation.INSTANCE); + TracingChannelInterceptor(Tracer tracer, Propagator propagator, SleuthIntegrationMessagingProperties properties) { + this(tracer, propagator, properties, MessageHeaderPropagation.INSTANCE, MessageHeaderPropagation.INSTANCE); } - TracingChannelInterceptor(Tracing tracing, SleuthMessagingProperties properties, - Propagation.Setter setter, - Propagation.Getter getter) { - this.tracing = tracing; + TracingChannelInterceptor(Tracer tracer, Propagator propagator, SleuthIntegrationMessagingProperties properties, + Propagator.Setter setter, Propagator.Getter getter) { this.properties = properties; - this.tracer = tracing.tracer(); - this.threadLocalSpan = ThreadLocalSpan.create(this.tracer); - this.injector = tracing.propagation().injector(setter); - this.extractor = tracing.propagation().extractor(getter); + this.tracer = tracer; + this.propagator = propagator; + this.injector = setter; + this.extractor = getter; this.integrationObjectSupportPresent = ClassUtils .isPresent("org.springframework.integration.context.IntegrationObjectSupport", null); this.hasDirectChannelClass = ClassUtils.isPresent("org.springframework.integration.channel.DirectChannel", @@ -128,8 +121,9 @@ final class TracingChannelInterceptor extends ChannelInterceptorAdapter implemen ? ClassUtils.resolveClassName(STREAM_DIRECT_CHANNEL, null) : null; } - public static TracingChannelInterceptor create(Tracing tracing, SleuthMessagingProperties properties) { - return new TracingChannelInterceptor(tracing, properties); + public static TracingChannelInterceptor create(Tracer tracer, Propagator propagator, + SleuthIntegrationMessagingProperties properties) { + return new TracingChannelInterceptor(tracer, propagator, properties); } /** @@ -141,10 +135,9 @@ final class TracingChannelInterceptor extends ChannelInterceptorAdapter implemen */ public Span nextSpan(Message message) { MessageHeaderAccessor headers = mutableHeaderAccessor(message); - TraceContextOrSamplingFlags extracted = this.extractor.extract(headers); + Span result = this.propagator.extract(headers, this.extractor).start(); headers.setImmutable(); - Span result = this.tracer.nextSpan(extracted); - if (extracted.context() == null && !result.isNoop()) { + if (!result.isNoop()) { addTags(message, result, null); } if (log.isDebugEnabled()) { @@ -162,18 +155,22 @@ final class TracingChannelInterceptor extends ChannelInterceptorAdapter implemen return message; } Message retrievedMessage = getMessage(message); - MessageHeaderAccessor headers = mutableHeaderAccessor(retrievedMessage); - TraceContextOrSamplingFlags extracted = this.extractor.extract(headers); - Span span = this.threadLocalSpan.next(extracted); - MessageHeaderPropagation.removeAnyTraceHeaders(headers, this.tracing.propagation().keys()); - this.injector.inject(span.context(), headers); - if (!span.isNoop()) { - span.kind(Span.Kind.PRODUCER).name("send").start(); - span.remoteServiceName(toRemoteServiceName(headers)); - addTags(message, span, channel); - } if (log.isDebugEnabled()) { - log.debug("Created a new span in pre send" + span); + log.debug("Received a message in pre-send " + retrievedMessage); + } + MessageHeaderAccessor headers = mutableHeaderAccessor(retrievedMessage); + Span.Builder spanBuilder = this.propagator.extract(headers, this.extractor); + MessageHeaderPropagation.removeAnyTraceHeaders(headers, this.propagator.fields()); + spanBuilder.kind(Span.Kind.PRODUCER).name("send").remoteServiceName(toRemoteServiceName(headers)); + addTags(message, spanBuilder, channel); + Span span = spanBuilder.start(); + if (log.isDebugEnabled()) { + log.debug("Extracted result from headers " + span); + } + setSpanInScope(span); + this.propagator.inject(span.context(), headers, this.injector); + if (log.isDebugEnabled()) { + log.debug("Created a new span in pre send " + span); } Message outputMessage = outputMessage(message, retrievedMessage, headers); if (isDirectChannel(channel)) { @@ -182,13 +179,21 @@ final class TracingChannelInterceptor extends ChannelInterceptorAdapter implemen return outputMessage; } + private void setSpanInScope(Span span) { + Tracer.SpanInScope spanInScope = this.tracer.withSpan(span); + this.threadLocalSpan.set(new SpanAndScope(span, spanInScope)); + if (log.isDebugEnabled()) { + log.debug("Put span in scope " + span); + } + } + private String toRemoteServiceName(MessageHeaderAccessor headers) { for (String key : headers.getMessageHeaders().keySet()) { if (key.startsWith("kafka_")) { - return this.properties.getMessaging().getKafka().getRemoteServiceName(); + return "kafka"; } else if (key.startsWith("amqp_")) { - return this.properties.getMessaging().getRabbit().getRemoteServiceName(); + return "rabbitmq"; } } return REMOTE_SERVICE_NAME; @@ -200,7 +205,7 @@ final class TracingChannelInterceptor extends ChannelInterceptorAdapter implemen if (originalMessage instanceof ErrorMessage) { ErrorMessage errorMessage = (ErrorMessage) originalMessage; headers.copyHeaders(MessageHeaderPropagation.propagationHeaders(additionalHeaders.getMessageHeaders(), - this.tracing.propagation().keys())); + this.propagator.fields())); return new ErrorMessage(errorMessage.getPayload(), isWebSockets(headers) ? headers.getMessageHeaders() : new MessageHeaders(headers.getMessageHeaders()), errorMessage.getOriginalMessage()); } @@ -254,15 +259,19 @@ final class TracingChannelInterceptor extends ChannelInterceptorAdapter implemen return message; } MessageHeaderAccessor headers = mutableHeaderAccessor(message); - TraceContextOrSamplingFlags extracted = this.extractor.extract(headers); - Span span = this.threadLocalSpan.next(extracted); - MessageHeaderPropagation.removeAnyTraceHeaders(headers, this.tracing.propagation().keys()); - this.injector.inject(span.context(), headers); - if (!span.isNoop()) { - span.kind(Span.Kind.CONSUMER).name("receive").start(); - span.remoteServiceName(toRemoteServiceName(headers)); - addTags(message, span, channel); + if (log.isDebugEnabled()) { + log.debug("Received a message in post-receive " + message); } + Span result = this.propagator.extract(headers, this.extractor).start(); + if (log.isDebugEnabled()) { + log.debug("Extracted result from headers " + result); + } + Span span = consumerSpanReceive(message, channel, headers, result); + setSpanInScope(span); + if (log.isDebugEnabled()) { + log.debug("Created a new span that will be injected in the headers " + span); + } + this.propagator.inject(span.context(), headers, this.injector); if (log.isDebugEnabled()) { log.debug("Created a new span in post receive " + span); } @@ -275,6 +284,15 @@ final class TracingChannelInterceptor extends ChannelInterceptorAdapter implemen return new GenericMessage<>(message.getPayload(), headers.getMessageHeaders()); } + private Span consumerSpanReceive(Message message, MessageChannel channel, MessageHeaderAccessor headers, + Span result) { + Span.Builder builder = this.tracer.spanBuilder().setParent(result.context()); + MessageHeaderPropagation.removeAnyTraceHeaders(headers, this.propagator.fields()); + builder.kind(Span.Kind.CONSUMER).name("receive").remoteServiceName(toRemoteServiceName(headers)); + addTags(message, builder, channel); + return builder.start(); + } + @Override public void afterReceiveCompletion(Message message, MessageChannel channel, Exception ex) { if (emptyMessage(message)) { @@ -296,23 +314,22 @@ final class TracingChannelInterceptor extends ChannelInterceptorAdapter implemen return message; } MessageHeaderAccessor headers = mutableHeaderAccessor(message); - TraceContextOrSamplingFlags extracted = this.extractor.extract(headers); - // Start and finish a consumer span as we will immediately process it. - Span consumerSpan = this.tracer.nextSpan(extracted); - if (!consumerSpan.isNoop()) { - consumerSpan.kind(Span.Kind.CONSUMER).start(); - consumerSpan.remoteServiceName(REMOTE_SERVICE_NAME); - addTags(message, consumerSpan, channel); - consumerSpan.finish(); + if (log.isDebugEnabled()) { + log.debug("Received a message in before handle " + message); } + Span consumerSpan = consumerSpan(message, channel, headers); // create and scope a span for the message processor - this.threadLocalSpan.next(TraceContextOrSamplingFlags.create(consumerSpan.context())).name("handle").start(); + Span handle = this.tracer.nextSpan(consumerSpan).name("handle").start(); + if (log.isDebugEnabled()) { + log.debug("Created consumer span " + handle); + } + setSpanInScope(handle); // remove any trace headers, but don't re-inject as we are synchronously // processing the // message and can rely on scoping to access this span later. - MessageHeaderPropagation.removeAnyTraceHeaders(headers, this.tracing.propagation().keys()); + MessageHeaderPropagation.removeAnyTraceHeaders(headers, this.propagator.fields()); if (log.isDebugEnabled()) { - log.debug("Created a new span in before handle" + consumerSpan); + log.debug("Created a new span in before handle " + handle); } if (message instanceof ErrorMessage) { return new ErrorMessage((Throwable) message.getPayload(), headers.getMessageHeaders()); @@ -321,6 +338,21 @@ final class TracingChannelInterceptor extends ChannelInterceptorAdapter implemen return new GenericMessage<>(message.getPayload(), headers.getMessageHeaders()); } + @NotNull + private Span consumerSpan(Message message, MessageChannel channel, MessageHeaderAccessor headers) { + Span.Builder consumerSpanBuilder = this.propagator.extract(headers, this.extractor); + if (log.isDebugEnabled()) { + log.debug("Extracted result from headers - will finish it immediately " + consumerSpanBuilder); + } + // Start and finish a consumer span as we will immediately process it. + consumerSpanBuilder.kind(Span.Kind.CONSUMER).start(); + consumerSpanBuilder.remoteServiceName(REMOTE_SERVICE_NAME); + addTags(message, consumerSpanBuilder, channel); + Span consumerSpan = consumerSpanBuilder.start(); + consumerSpan.end(); + return consumerSpan; + } + @Override public void afterMessageHandled(Message message, MessageChannel channel, MessageHandler handler, Exception ex) { if (emptyMessage(message)) { @@ -338,7 +370,14 @@ final class TracingChannelInterceptor extends ChannelInterceptorAdapter implemen * @param result span to customize * @param channel channel to which a message was sent */ - void addTags(Message message, SpanCustomizer result, MessageChannel channel) { + void addTags(Message message, Span.Builder result, MessageChannel channel) { + // TODO topic etc + if (channel != null) { + result.tag("channel", messageChannelName(channel)); + } + } + + void addTags(Message message, Span result, MessageChannel channel) { // TODO topic etc if (channel != null) { result.tag("channel", messageChannelName(channel)); @@ -366,8 +405,17 @@ final class TracingChannelInterceptor extends ChannelInterceptorAdapter implemen } void finishSpan(Exception error) { - Span span = this.threadLocalSpan.remove(); - if (span == null || span.isNoop()) { + SpanAndScope spanAndScope = getSpanFromThreadLocal(); + if (spanAndScope == null) { + return; + } + Span span = spanAndScope.span; + Tracer.SpanInScope scope = spanAndScope.scope; + if (span.isNoop()) { + if (log.isDebugEnabled()) { + log.debug("Span " + span + " is noop - will stope the scope"); + } + scope.close(); return; } if (error != null) { // an error occurred, adding error to span @@ -377,7 +425,20 @@ final class TracingChannelInterceptor extends ChannelInterceptorAdapter implemen } span.tag("error", message); } - span.finish(); + if (log.isDebugEnabled()) { + log.debug("Will finish the and its corresponding scope " + span); + } + span.end(); + scope.close(); + } + + private SpanAndScope getSpanFromThreadLocal() { + SpanAndScope span = this.threadLocalSpan.get(); + if (log.isDebugEnabled()) { + log.debug("Took span [" + span + "] from thread local"); + } + this.threadLocalSpan.remove(); + return span; } private MessageHeaderAccessor mutableHeaderAccessor(Message message) { @@ -401,3 +462,50 @@ final class TracingChannelInterceptor extends ChannelInterceptorAdapter implemen } } + +class SpanAndScope { + + final Span span; + + final Tracer.SpanInScope scope; + + SpanAndScope(Span span, Tracer.SpanInScope scope) { + this.span = span; + this.scope = scope; + } + +} + +class ThreadLocalSpan { + + private static final Log log = LogFactory.getLog(ThreadLocalSpan.class); + + final ThreadLocal threadLocalSpan = new ThreadLocal<>(); + + final LinkedBlockingDeque spans = new LinkedBlockingDeque<>(); + + void set(SpanAndScope spanAndScope) { + SpanAndScope scope = this.threadLocalSpan.get(); + if (scope != null) { + this.spans.addFirst(scope); + } + this.threadLocalSpan.set(spanAndScope); + } + + SpanAndScope get() { + return this.threadLocalSpan.get(); + } + + void remove() { + this.threadLocalSpan.remove(); + if (this.spans.isEmpty()) { + return; + } + SpanAndScope span = this.spans.removeFirst(); + if (log.isDebugEnabled()) { + log.debug("Took span [" + span + "] from thread local"); + } + this.threadLocalSpan.set(span); + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingMethodMessageHandlerAdapter.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingMethodMessageHandlerAdapter.java deleted file mode 100644 index 7bae20f77..000000000 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingMethodMessageHandlerAdapter.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 2013-2020 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.messaging; - -import java.util.function.BiConsumer; - -import brave.Span; -import brave.Tracer; -import brave.Tracing; -import brave.messaging.ConsumerRequest; -import brave.messaging.MessagingTracing; -import brave.propagation.Propagation.Getter; -import brave.propagation.TraceContext.Extractor; -import brave.propagation.TraceContextOrSamplingFlags; - -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHandler; -import org.springframework.messaging.support.MessageHeaderAccessor; - -import static brave.Span.Kind.CONSUMER; - -/** - * Adds tracing extraction to an instance of - * {@link org.springframework.messaging.handler.invocation.AbstractMethodMessageHandler} - * in a reusable way. When sub-classing a provider specific class of that type you would - * wrap the

super.handleMessage(...)
call with a call to this. - * - * This implementation also allows for supplying a {@link java.util.function.BiConsumer} - * instance that can be used to add queue specific tags and modifications to the span. - * - * @author Brian Devins-Suresh - */ -class TracingMethodMessageHandlerAdapter { - - private final Tracing tracing; - - private final Tracer tracer; - - private final Extractor extractor; - - private final Getter getter; - - TracingMethodMessageHandlerAdapter(MessagingTracing messagingTracing, - Getter getter) { - this.tracing = messagingTracing.tracing(); - this.tracer = tracing.tracer(); - this.extractor = tracing.propagation().extractor(MessageConsumerRequest.GETTER); - this.getter = getter; - } - - void wrapMethodMessageHandler(Message message, MessageHandler messageHandler, - BiConsumer> messageSpanTagger) { - MessageConsumerRequest request = new MessageConsumerRequest(message, this.getter); - TraceContextOrSamplingFlags extracted = extractAndClearHeaders(request); - - Span consumerSpan = tracer.nextSpan(extracted); - Span listenerSpan = tracer.newChild(consumerSpan.context()); - - if (!consumerSpan.isNoop()) { - consumerSpan.name("next-message").kind(CONSUMER); - if (messageSpanTagger != null) { - messageSpanTagger.accept(consumerSpan, message); - } - - // incur timestamp overhead only once - long timestamp = tracing.clock(consumerSpan.context()).currentTimeMicroseconds(); - consumerSpan.start(timestamp); - long consumerFinish = timestamp + 1L; // save a clock reading - consumerSpan.finish(consumerFinish); - - // not using scoped span as we want to start with a pre-configured time - listenerSpan.name("on-message").start(consumerFinish); - } - - try (Tracer.SpanInScope ws = tracer.withSpanInScope(listenerSpan)) { - messageHandler.handleMessage(message); - } - catch (Throwable t) { - listenerSpan.error(t); - throw t; - } - finally { - listenerSpan.finish(); - } - } - - private TraceContextOrSamplingFlags extractAndClearHeaders(MessageConsumerRequest request) { - TraceContextOrSamplingFlags extracted = extractor.extract(request); - - for (String propagationKey : tracing.propagation().keys()) { - request.removeHeader(propagationKey); - } - - return extracted; - } - -} - -final class MessageConsumerRequest extends ConsumerRequest { - - static final String LOGICAL_RESOURCE_ID = "LogicalResourceId"; - - static final Getter GETTER = new Getter() { - @Override - public String get(MessageConsumerRequest request, String name) { - return request.getHeader(name); - } - - @Override - public String toString() { - return "MessageConsumerRequest::getHeader"; - } - }; - - final Message delegate; - - final MessageHeaderAccessor mutableHeaders; - - final Getter getter; - - MessageConsumerRequest(Message delegate, Getter getter) { - this.delegate = delegate; - this.mutableHeaders = MessageHeaderAccessor.getMutableAccessor(delegate); - this.getter = getter; - } - - @Override - public Span.Kind spanKind() { - return Span.Kind.CONSUMER; - } - - @Override - public Object unwrap() { - return this.delegate; - } - - @Override - public String operation() { - return "receive"; - } - - @Override - public String channelKind() { - return "queue"; - } - - @Override - public String channelName() { - return this.delegate.getHeaders().get(LOGICAL_RESOURCE_ID).toString(); - } - - String getHeader(String name) { - return this.getter.get(this.mutableHeaders, name); - } - - void removeHeader(String name) { - this.mutableHeaders.removeHeader(name); - } - -} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/quartz/TraceQuartzAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/quartz/TraceQuartzAutoConfiguration.java index 78389bebf..1643f471b 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/quartz/TraceQuartzAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/quartz/TraceQuartzAutoConfiguration.java @@ -16,7 +16,6 @@ package org.springframework.cloud.sleuth.instrument.quartz; -import brave.Tracing; import org.quartz.Scheduler; import org.springframework.beans.factory.BeanFactory; @@ -26,6 +25,8 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.propagation.Propagator; import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -38,18 +39,21 @@ import org.springframework.context.annotation.Configuration; * @since 2.2.0 */ @Configuration(proxyBeanMethods = false) -@ConditionalOnBean({ Tracing.class, Scheduler.class }) +@ConditionalOnBean({ Tracer.class, Scheduler.class }) @AutoConfigureAfter({ TraceAutoConfiguration.class, QuartzAutoConfiguration.class }) @ConditionalOnProperty(value = "spring.sleuth.quartz.enabled", matchIfMissing = true) class TraceQuartzAutoConfiguration implements InitializingBean { - private Scheduler scheduler; + private final Scheduler scheduler; - private Tracing tracing; + private final Tracer tracer; - TraceQuartzAutoConfiguration(Scheduler scheduler, Tracing tracing) { + private final Propagator propagator; + + TraceQuartzAutoConfiguration(Scheduler scheduler, Tracer tracer, Propagator propagator) { this.scheduler = scheduler; - this.tracing = tracing; + this.tracer = tracer; + this.propagator = propagator; } @Autowired @@ -57,14 +61,14 @@ class TraceQuartzAutoConfiguration implements InitializingBean { @Bean public TracingJobListener tracingJobListener() { - return new TracingJobListener(tracing); + return new TracingJobListener(this.tracer, this.propagator); } @Override public void afterPropertiesSet() throws Exception { - TracingJobListener tracingJobListener = beanFactory.getBean(TracingJobListener.class); - scheduler.getListenerManager().addTriggerListener(tracingJobListener); - scheduler.getListenerManager().addJobListener(tracingJobListener); + TracingJobListener tracingJobListener = this.beanFactory.getBean(TracingJobListener.class); + this.scheduler.getListenerManager().addTriggerListener(tracingJobListener); + this.scheduler.getListenerManager().addJobListener(tracingJobListener); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/quartz/TracingJobListener.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/quartz/TracingJobListener.java index d3dcd2743..ab9ae825e 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/quartz/TracingJobListener.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/quartz/TracingJobListener.java @@ -16,11 +16,6 @@ package org.springframework.cloud.sleuth.instrument.quartz; -import brave.Span; -import brave.Tracer.SpanInScope; -import brave.Tracing; -import brave.propagation.Propagation.Getter; -import brave.propagation.TraceContextOrSamplingFlags; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; @@ -29,6 +24,10 @@ import org.quartz.Trigger; import org.quartz.Trigger.CompletedExecutionInstruction; import org.quartz.TriggerListener; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.propagation.Propagator; + /** * {@link org.quartz.JobListener JobListener} that will wrap a span around quartz jobs * when they start and finish. @@ -42,9 +41,9 @@ class TracingJobListener implements JobListener, TriggerListener { static final String CONTEXT_SPAN_KEY = Span.class.getName(); - static final String CONTEXT_SPAN_IN_SCOPE_KEY = SpanInScope.class.getName(); + static final String CONTEXT_SPAN_IN_SCOPE_KEY = Tracer.SpanInScope.class.getName(); - private static final Getter GETTER = (carrier, key) -> { + private static final Propagator.Getter GETTER = (carrier, key) -> { Object value = carrier.get(key); if (value instanceof String) { return (String) value; @@ -52,10 +51,13 @@ class TracingJobListener implements JobListener, TriggerListener { return null; }; - private final Tracing tracing; + private final Tracer tracer; - TracingJobListener(Tracing tracing) { - this.tracing = tracing; + private final Propagator propagator; + + TracingJobListener(Tracer tracer, Propagator propagator) { + this.tracer = tracer; + this.propagator = propagator; } @Override @@ -65,12 +67,11 @@ class TracingJobListener implements JobListener, TriggerListener { @Override public void triggerFired(Trigger trigger, JobExecutionContext context) { - TraceContextOrSamplingFlags extracted = tracing.propagation().extractor(GETTER) - .extract(context.getMergedJobDataMap()); - Span span = tracing.tracer().nextSpan(extracted).name(context.getTrigger().getJobKey().toString()) - .tag(TRIGGER_TAG_KEY, context.getTrigger().getKey().toString()); + Span nextSpan = propagator.extract(context.getMergedJobDataMap(), GETTER).start(); + Span span = nextSpan.name(context.getTrigger().getJobKey().toString()).tag(TRIGGER_TAG_KEY, + context.getTrigger().getKey().toString()); context.put(CONTEXT_SPAN_KEY, span); - context.put(CONTEXT_SPAN_IN_SCOPE_KEY, tracing.tracer().withSpanInScope(span.start())); + context.put(CONTEXT_SPAN_IN_SCOPE_KEY, tracer.withSpan(span.start())); } @Override @@ -106,11 +107,11 @@ class TracingJobListener implements JobListener, TriggerListener { private void closeTrace(JobExecutionContext context) { Object spanInScope = context.get(CONTEXT_SPAN_IN_SCOPE_KEY); Object span = context.get(CONTEXT_SPAN_KEY); - if (spanInScope instanceof SpanInScope) { - ((SpanInScope) spanInScope).close(); + if (spanInScope instanceof Tracer.SpanInScope) { + ((Tracer.SpanInScope) spanInScope).close(); } if (span instanceof Span) { - ((Span) span).finish(); + ((Span) span).end(); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/ReactorSleuth.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/ReactorSleuth.java index 20a2259d3..742d08694 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/ReactorSleuth.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/ReactorSleuth.java @@ -18,9 +18,6 @@ package org.springframework.cloud.sleuth.instrument.reactor; import java.util.function.Function; -import brave.Tracing; -import brave.propagation.CurrentTraceContext; -import brave.propagation.TraceContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.reactivestreams.Publisher; @@ -32,6 +29,9 @@ import reactor.core.Scannable; import reactor.core.publisher.Operators; import reactor.util.context.Context; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.internal.LazyBean; import org.springframework.context.ConfigurableApplicationContext; @@ -51,8 +51,8 @@ public abstract class ReactorSleuth { } /** - * Return a span operator pointcut given a {@link Tracing}. This can be used in - * reactor via {@link reactor.core.publisher.Flux#transform(Function)}, + * Return a span operator pointcut given a Tracing. This can be used in reactor via + * {@link reactor.core.publisher.Flux#transform(Function)}, * {@link reactor.core.publisher.Mono#transform(Function)}, * {@link reactor.core.publisher.Hooks#onLastOperator(Function)} or * {@link reactor.core.publisher.Hooks#onLastOperator(Function)}. The Span operator @@ -138,8 +138,8 @@ public abstract class ReactorSleuth { private static Context contextWithBeans(ConfigurableApplicationContext springContext, CoreSubscriber sub) { Context context = sub.currentContext(); - if (!context.hasKey(Tracing.class)) { - context = context.put(Tracing.class, springContext.getBean(Tracing.class)); + if (!context.hasKey(Tracer.class)) { + context = context.put(Tracer.class, springContext.getBean(Tracer.class)); } if (!context.hasKey(CurrentTraceContext.class)) { context = context.put(CurrentTraceContext.class, springContext.getBean(CurrentTraceContext.class)); diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/ScopePassingSpanSubscriber.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/ScopePassingSpanSubscriber.java index ce10ab56f..1310f9e0c 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/ScopePassingSpanSubscriber.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/ScopePassingSpanSubscriber.java @@ -16,11 +16,6 @@ package org.springframework.cloud.sleuth.instrument.reactor; -import javax.annotation.Nullable; - -import brave.propagation.CurrentTraceContext; -import brave.propagation.CurrentTraceContext.Scope; -import brave.propagation.TraceContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.reactivestreams.Subscriber; @@ -28,6 +23,10 @@ import org.reactivestreams.Subscription; import reactor.core.Scannable; import reactor.util.context.Context; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.lang.Nullable; + /** * A trace representation of the {@link Subscriber} that always continues a span. * @@ -64,42 +63,42 @@ final class ScopePassingSpanSubscriber implements SpanSubscription, Scanna @Override public void onSubscribe(Subscription subscription) { this.s = subscription; - try (Scope scope = this.currentTraceContext.maybeScope(this.parent)) { + try (CurrentTraceContext.Scope scope = this.currentTraceContext.maybeScope(this.parent)) { this.subscriber.onSubscribe(this); } } @Override public void request(long n) { - try (Scope scope = this.currentTraceContext.maybeScope(this.parent)) { + try (CurrentTraceContext.Scope scope = this.currentTraceContext.maybeScope(this.parent)) { this.s.request(n); } } @Override public void cancel() { - try (Scope scope = this.currentTraceContext.maybeScope(this.parent)) { + try (CurrentTraceContext.Scope scope = this.currentTraceContext.maybeScope(this.parent)) { this.s.cancel(); } } @Override public void onNext(T o) { - try (Scope scope = this.currentTraceContext.maybeScope(this.parent)) { + try (CurrentTraceContext.Scope scope = this.currentTraceContext.maybeScope(this.parent)) { this.subscriber.onNext(o); } } @Override public void onError(Throwable throwable) { - try (Scope scope = this.currentTraceContext.maybeScope(this.parent)) { + try (CurrentTraceContext.Scope scope = this.currentTraceContext.maybeScope(this.parent)) { this.subscriber.onError(throwable); } } @Override public void onComplete() { - try (Scope scope = this.currentTraceContext.maybeScope(this.parent)) { + try (CurrentTraceContext.Scope scope = this.currentTraceContext.maybeScope(this.parent)) { this.subscriber.onComplete(); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/TraceReactorAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/TraceReactorAutoConfiguration.java index 660dcff0d..be98de5b8 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/TraceReactorAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/TraceReactorAutoConfiguration.java @@ -20,7 +20,6 @@ import java.io.Closeable; import java.io.IOException; import java.util.function.Function; -import brave.Tracing; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.reactivestreams.Publisher; @@ -40,6 +39,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.context.scope.refresh.RefreshScope; import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.instrument.async.TraceableScheduledExecutorService; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; @@ -47,8 +47,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.ConfigurableEnvironment; -import static org.springframework.cloud.sleuth.instrument.reactor.ReactorSleuth.scopePassingSpanOperator; -import static org.springframework.cloud.sleuth.instrument.reactor.ReactorSleuth.springContextSpanOperator; import static org.springframework.cloud.sleuth.instrument.reactor.TraceReactorAutoConfiguration.TraceReactorConfiguration.SLEUTH_TRACE_REACTOR_KEY; /** @@ -69,7 +67,7 @@ class TraceReactorAutoConfiguration { static final String SLEUTH_REACTOR_EXECUTOR_SERVICE_KEY = "sleuth"; @Configuration(proxyBeanMethods = false) - @ConditionalOnBean(Tracing.class) + @ConditionalOnBean(Tracer.class) static class TraceReactorConfiguration { static final String SLEUTH_TRACE_REACTOR_KEY = TraceReactorConfiguration.class.getName(); @@ -89,7 +87,7 @@ class TraceReactorAutoConfiguration { return new HookRegisteringBeanDefinitionRegistryPostProcessor(context); } - @Configuration + @Configuration(proxyBeanMethods = false) @ConditionalOnClass(RefreshScope.class) static class HooksRefresherConfiguration { @@ -130,16 +128,16 @@ class HooksRefresher implements ApplicationListener if (log.isTraceEnabled()) { log.trace("Decorating onEach operator instrumentation"); } - Hooks.onEachOperator(SLEUTH_TRACE_REACTOR_KEY, scopePassingSpanOperator(this.context)); + Hooks.onEachOperator(SLEUTH_TRACE_REACTOR_KEY, ReactorSleuth.scopePassingSpanOperator(this.context)); break; case DECORATE_ON_LAST: if (log.isTraceEnabled()) { log.trace("Decorating onLast operator instrumentation"); } - Hooks.onLastOperator(SLEUTH_TRACE_REACTOR_KEY, scopePassingSpanOperator(this.context)); + Hooks.onLastOperator(SLEUTH_TRACE_REACTOR_KEY, ReactorSleuth.scopePassingSpanOperator(this.context)); break; case MANUAL: - Hooks.onLastOperator(SLEUTH_TRACE_REACTOR_KEY, springContextSpanOperator(this.context)); + Hooks.onLastOperator(SLEUTH_TRACE_REACTOR_KEY, ReactorSleuth.springContextSpanOperator(this.context)); break; } } @@ -174,16 +172,16 @@ class HookRegisteringBeanDefinitionRegistryPostProcessor implements BeanDefiniti if (!decorateOnEach) { log.warn( "You're using the deprecated [spring.sleuth.reactor.decorate-on-each] property. Please use the [spring.sleuth.reactor.instrumentation-type] one instead."); - decorateOnLast(scopePassingSpanOperator(springContext)); + decorateOnLast(ReactorSleuth.scopePassingSpanOperator(springContext)); } else if (property == SleuthReactorProperties.InstrumentationType.DECORATE_ON_EACH) { decorateOnEach(springContext); } else if (property == SleuthReactorProperties.InstrumentationType.DECORATE_ON_LAST) { - decorateOnLast(scopePassingSpanOperator(springContext)); + decorateOnLast(ReactorSleuth.scopePassingSpanOperator(springContext)); } else if (property == SleuthReactorProperties.InstrumentationType.MANUAL) { - decorateOnLast(springContextSpanOperator(springContext)); + decorateOnLast(ReactorSleuth.springContextSpanOperator(springContext)); } Schedulers.setExecutorServiceDecorator(TraceReactorAutoConfiguration.SLEUTH_REACTOR_EXECUTOR_SERVICE_KEY, (scheduler, scheduledExecutorService) -> new TraceableScheduledExecutorService(springContext, @@ -201,7 +199,7 @@ class HookRegisteringBeanDefinitionRegistryPostProcessor implements BeanDefiniti if (log.isTraceEnabled()) { log.trace("Decorating onEach operator instrumentation"); } - Hooks.onEachOperator(SLEUTH_TRACE_REACTOR_KEY, scopePassingSpanOperator(springContext)); + Hooks.onEachOperator(SLEUTH_TRACE_REACTOR_KEY, ReactorSleuth.scopePassingSpanOperator(springContext)); } @Override diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rxjava/RxJavaAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rxjava/RxJavaAutoConfiguration.java index c635ebee3..5d47d3184 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rxjava/RxJavaAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rxjava/RxJavaAutoConfiguration.java @@ -18,8 +18,6 @@ package org.springframework.cloud.sleuth.instrument.rxjava; import java.util.Arrays; -import brave.Tracer; -import brave.Tracing; import rx.plugins.RxJavaSchedulersHook; import org.springframework.boot.autoconfigure.AutoConfigureAfter; @@ -27,6 +25,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -40,7 +39,7 @@ import org.springframework.context.annotation.Configuration; */ @Configuration(proxyBeanMethods = false) @AutoConfigureAfter(TraceAutoConfiguration.class) -@ConditionalOnBean(Tracing.class) +@ConditionalOnBean(Tracer.class) @ConditionalOnClass(RxJavaSchedulersHook.class) @ConditionalOnProperty(value = "spring.sleuth.rxjava.schedulers.hook.enabled", matchIfMissing = true) @EnableConfigurationProperties(SleuthRxJavaSchedulersProperties.class) diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rxjava/SleuthRxJavaSchedulersHook.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rxjava/SleuthRxJavaSchedulersHook.java index 281987ecc..2be5ef1d0 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rxjava/SleuthRxJavaSchedulersHook.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/rxjava/SleuthRxJavaSchedulersHook.java @@ -18,8 +18,6 @@ package org.springframework.cloud.sleuth.instrument.rxjava; import java.util.List; -import brave.Span; -import brave.Tracer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import rx.functions.Action0; @@ -28,6 +26,9 @@ import rx.plugins.RxJavaObservableExecutionHook; import rx.plugins.RxJavaPlugins; import rx.plugins.RxJavaSchedulersHook; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; + /** * {@link RxJavaSchedulersHook} that wraps an {@link Action0} into its tracing * representation. @@ -133,20 +134,17 @@ class SleuthRxJavaSchedulersHook extends RxJavaSchedulersHook { } Span span = this.parent; boolean created = false; - if (span != null) { - span = this.tracer.toSpan(this.parent.context()); - } - else { + if (span == null) { span = this.tracer.nextSpan().name(RXJAVA_COMPONENT).start(); span.tag(THREAD_NAME_KEY, Thread.currentThread().getName()); created = true; } - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span)) { this.actual.call(); } finally { if (created) { - span.finish(); + span.end(); } } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/scheduling/TraceSchedulingAspect.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/scheduling/TraceSchedulingAspect.java index 1175ad2db..7b9ade67f 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/scheduling/TraceSchedulingAspect.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/scheduling/TraceSchedulingAspect.java @@ -18,13 +18,12 @@ package org.springframework.cloud.sleuth.instrument.scheduling; import java.util.regex.Pattern; -import brave.Span; -import brave.Tracer; -import brave.Tracing; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.internal.SpanNameUtil; import org.springframework.lang.Nullable; @@ -40,7 +39,6 @@ import org.springframework.lang.Nullable; * @author Marcin Grzejszczak * @author Spencer Gibb * @since 1.0.0 - * @see Tracing */ @Aspect class TraceSchedulingAspect { @@ -64,23 +62,22 @@ class TraceSchedulingAspect { if (this.skipPattern != null && this.skipPattern.matcher(pjp.getTarget().getClass().getName()).matches()) { // we might have a span in context due to wrapping of runnables // we want to clear that context - this.tracer.withSpanInScope(null); + this.tracer.withSpan(null); return pjp.proceed(); } String spanName = SpanNameUtil.toLowerHyphen(pjp.getSignature().getName()); Span span = startOrContinueRenamedSpan(spanName); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { span.tag(CLASS_KEY, pjp.getTarget().getClass().getSimpleName()); span.tag(METHOD_KEY, pjp.getSignature().getName()); return pjp.proceed(); } catch (Throwable ex) { - String message = ex.getMessage() == null ? ex.getClass().getSimpleName() : ex.getMessage(); - span.tag("error", message); + span.error(ex); throw ex; } finally { - span.finish(); + span.end(); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/scheduling/TraceSchedulingAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/scheduling/TraceSchedulingAutoConfiguration.java index ab9fb1a4d..1c424f9f0 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/scheduling/TraceSchedulingAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/scheduling/TraceSchedulingAutoConfiguration.java @@ -18,14 +18,12 @@ package org.springframework.cloud.sleuth.instrument.scheduling; import java.util.regex.Pattern; -import brave.Tracer; -import brave.Tracing; - 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.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -41,7 +39,7 @@ import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) @ConditionalOnClass(name = "org.aspectj.lang.ProceedingJoinPoint") @ConditionalOnProperty(value = "spring.sleuth.scheduled.enabled", matchIfMissing = true) -@ConditionalOnBean(Tracing.class) +@ConditionalOnBean(Tracer.class) @AutoConfigureAfter(TraceAutoConfiguration.class) @EnableConfigurationProperties(SleuthSchedulingProperties.class) class TraceSchedulingAutoConfiguration { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpClientRequestParser.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpClientRequestParser.java index 7b612b7a7..5efdadaa5 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpClientRequestParser.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpClientRequestParser.java @@ -23,14 +23,10 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import brave.http.HttpRequestParser; -import brave.http.HttpTracing; - import org.springframework.beans.factory.annotation.Qualifier; /** - * Annotate a client {@link HttpRequestParser} that should be injected to - * {@link HttpTracing.Builder#clientRequestParser(HttpRequestParser)}. + * Annotate a client {@link org.springframework.cloud.sleuth.api.http.HttpRequestParser}. * * @see Qualifier * @since 2.2.2 diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpClientResponseParser.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpClientResponseParser.java index 600af21e8..e727d0843 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpClientResponseParser.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpClientResponseParser.java @@ -23,15 +23,9 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import brave.http.HttpResponseParser; -import brave.http.HttpTracing; - import org.springframework.beans.factory.annotation.Qualifier; /** - * Annotate a client {@link HttpResponseParser} that should be injected to - * {@link HttpTracing.Builder#clientResponseParser(HttpResponseParser)}. - * * @see Qualifier * @since 2.2.2 */ 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 index 559b892a3..e9a307e59 100644 --- 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 @@ -23,13 +23,11 @@ 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; +import org.springframework.cloud.sleuth.api.SamplerFunction; /** - * Annotate a client {@link brave.sampler.SamplerFunction} that should be injected to - * {@link brave.http.HttpTracing.Builder#clientSampler(SamplerFunction)}. + * Annotate a client {@link SamplerFunction} that should be injected to a client sampler. * * @since 2.2.0 * @see Qualifier diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerRequestParser.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerRequestParser.java index 87216a1f5..f7bb7a6a5 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerRequestParser.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerRequestParser.java @@ -23,15 +23,9 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import brave.http.HttpRequestParser; -import brave.http.HttpTracing; - import org.springframework.beans.factory.annotation.Qualifier; /** - * Annotate a server {@link HttpRequestParser} that should be injected to - * {@link HttpTracing.Builder#serverRequestParser(HttpRequestParser)}. - * * @see Qualifier * @since 2.2.2 */ diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerResponseParser.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerResponseParser.java index 1aa5940a8..d059aa917 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerResponseParser.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerResponseParser.java @@ -23,15 +23,9 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import brave.http.HttpResponseParser; -import brave.http.HttpTracing; - import org.springframework.beans.factory.annotation.Qualifier; /** - * Annotate a server {@link HttpResponseParser} that should be injected to - * {@link HttpTracing.Builder#serverResponseParser(HttpResponseParser)}. - * * @see Qualifier * @since 2.2.2 */ 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 index 188e2d65d..11cd1dd19 100644 --- 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 @@ -23,13 +23,11 @@ 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; +import org.springframework.cloud.sleuth.api.SamplerFunction; /** - * Annotate a client {@link brave.sampler.SamplerFunction} that should be injected to - * {@link brave.http.HttpTracing.Builder#serverSampler(SamplerFunction)}. + * Annotate a server {@link SamplerFunction}. * * @since 2.2.0 * @see Qualifier diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipPatternConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipPatternConfiguration.java index 5f3914a27..10c2bc74f 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipPatternConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipPatternConfiguration.java @@ -23,8 +23,6 @@ import java.util.StringJoiner; import java.util.regex.Pattern; import java.util.stream.Collectors; -import brave.Tracing; - import org.springframework.beans.factory.BeanCurrentlyInCreationException; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.web.server.ConditionalOnManagementPort; @@ -39,6 +37,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -57,10 +56,10 @@ import org.springframework.util.StringUtils; */ @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(value = "spring.sleuth.web.enabled", matchIfMissing = true) -@ConditionalOnBean(Tracing.class) +@ConditionalOnBean(Tracer.class) @AutoConfigureAfter(TraceAutoConfiguration.class) @EnableConfigurationProperties(SleuthWebProperties.class) -class SkipPatternConfiguration { +public class SkipPatternConfiguration { @Bean @ConditionalOnMissingBean diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/SleuthHttpProperties.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/SleuthHttpProperties.java index 77d46a7bd..5364552e4 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/SleuthHttpProperties.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/SleuthHttpProperties.java @@ -25,12 +25,10 @@ import org.springframework.boot.context.properties.ConfigurationProperties; * @since 2.0.0 */ @ConfigurationProperties("spring.sleuth.http") -class SleuthHttpProperties { +public class SleuthHttpProperties { private boolean enabled = true; - private Legacy legacy = new Legacy(); - public boolean isEnabled() { return this.enabled; } @@ -39,29 +37,4 @@ class SleuthHttpProperties { this.enabled = enabled; } - public Legacy getLegacy() { - return this.legacy; - } - - public void setLegacy(Legacy legacy) { - this.legacy = legacy; - } - - /** - * Legacy Sleuth support. Related to the way headers are parsed and tags are set - */ - public static class Legacy { - - private boolean enabled = false; - - public boolean isEnabled() { - return this.enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - } - } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/SleuthWebProperties.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/SleuthWebProperties.java index 335ff3868..5e45ea619 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/SleuthWebProperties.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/SleuthWebProperties.java @@ -26,7 +26,14 @@ import org.springframework.boot.context.properties.NestedConfigurationProperty; * @since 1.0.12 */ @ConfigurationProperties("spring.sleuth.web") -class SleuthWebProperties { +public class SleuthWebProperties { + + /** + * If you register your filter before the {@link TraceWebFilter} then you will not + * have the tracing context passed for you out of the box. That means that e.g. your + * logs will not get correlated. + */ + public static final int TRACING_FILTER_ORDER = 5; /** * Default set of skip patterns. @@ -52,9 +59,9 @@ class SleuthWebProperties { /** * Order in which the tracing filters should be registered. Defaults to - * {@link TraceHttpAutoConfiguration#TRACING_FILTER_ORDER}. + * {@link TraceWebServletAutoConfiguration#TRACING_FILTER_ORDER}. */ - private int filterOrder = TraceHttpAutoConfiguration.TRACING_FILTER_ORDER; + private int filterOrder = TraceWebServletAutoConfiguration.TRACING_FILTER_ORDER; /** * If set to true, auto-configured skip patterns will be ignored. 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 9d5577bb7..c8be7b5f9 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 @@ -16,204 +16,38 @@ package org.springframework.cloud.sleuth.instrument.web; -import java.util.List; -import java.util.regex.Pattern; - -import brave.Tracing; -import brave.http.HttpRequest; -import brave.http.HttpRequestParser; -import brave.http.HttpResponseParser; -import brave.http.HttpTracing; -import brave.http.HttpTracingCustomizer; -import brave.sampler.SamplerFunction; -import brave.sampler.SamplerFunctions; - -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.autoconfig.TraceAutoConfiguration; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; +import org.springframework.cloud.sleuth.api.http.HttpServerHandler; +import org.springframework.cloud.sleuth.api.noop.NoOpHttpClientHandler; +import org.springframework.cloud.sleuth.api.noop.NoOpHttpServerHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.core.Ordered; -import org.springframework.lang.Nullable; /** * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration - * Auto-configuration} related to HTTP based communication. + * Auto-configuration} to enable HTTP tracing via Spring Cloud Sleuth. * + * @author Spencer Gibb * @author Marcin Grzejszczak + * @author Tim Ysewyn * @since 2.0.0 */ @Configuration(proxyBeanMethods = false) -// This was formerly conditional on TraceWebAutoConfiguration, which was -// conditional on "spring.sleuth.web.enabled". As this is conditional on -// "spring.sleuth.http.enabled", to be compatible with old behavior we have -// to be conditional on two properties. -@ConditionalOnProperty(name = { "spring.sleuth.http.enabled", "spring.sleuth.web.enabled" }, havingValue = "true", - matchIfMissing = true) -@ConditionalOnBean(Tracing.class) -@ConditionalOnClass(HttpTracing.class) -@AutoConfigureAfter(TraceAutoConfiguration.class) -@Import(SkipPatternConfiguration.class) -// public allows @AutoConfigureAfter(TraceHttpAutoConfiguration) -// for components needing HttpTracing +@ConditionalOnProperty(value = "spring.sleuth.http.enabled", matchIfMissing = true) public class TraceHttpAutoConfiguration { - static final int TRACING_FILTER_ORDER = Ordered.HIGHEST_PRECEDENCE + 5; - @Bean @ConditionalOnMissingBean - // NOTE: stable bean name as might be used outside sleuth - HttpTracing httpTracing(Tracing tracing, @Nullable SkipPatternProvider provider, - @Nullable @HttpClientRequestParser HttpRequestParser httpClientRequestParser, - @Nullable @HttpClientResponseParser HttpResponseParser httpClientResponseParser, - @Nullable brave.http.HttpClientParser clientParser, - @Nullable @HttpServerRequestParser HttpRequestParser httpServerRequestParser, - @Nullable @HttpServerResponseParser HttpResponseParser httpServerResponseParser, - @Nullable brave.http.HttpServerParser serverParser, - @HttpClientSampler SamplerFunction httpClientSampler, - @Nullable @HttpServerSampler SamplerFunction httpServerSampler, - @Nullable List httpTracingCustomizers) { - SamplerFunction combinedSampler = combineUserProvidedSamplerWithSkipPatternSampler( - httpServerSampler, provider); - HttpTracing.Builder builder = HttpTracing.newBuilder(tracing).clientSampler(httpClientSampler) - .serverSampler(combinedSampler); - - if (httpClientRequestParser != null || httpClientResponseParser != null) { - if (httpClientRequestParser != null) { - builder.clientRequestParser(httpClientRequestParser); - } - if (httpClientResponseParser != null) { - builder.clientResponseParser(httpClientResponseParser); - } - } - else if (clientParser != null) { // consider deprecated last - builder.clientParser(clientParser); - } - - if (httpServerRequestParser != null || httpServerResponseParser != null) { - if (httpServerRequestParser != null) { - builder.serverRequestParser(httpServerRequestParser); - } - if (httpServerResponseParser != null) { - builder.serverResponseParser(httpServerResponseParser); - } - } - else if (serverParser != null) { // consider deprecated last - builder.serverParser(serverParser); - } - - if (httpTracingCustomizers != null) { - for (HttpTracingCustomizer customizer : httpTracingCustomizers) { - customizer.customize(builder); - } - } - return builder.build(); - } - - private SamplerFunction combineUserProvidedSamplerWithSkipPatternSampler( - @Nullable SamplerFunction serverSampler, @Nullable SkipPatternProvider provider) { - SamplerFunction skipPatternSampler = provider != null ? new SkipPatternHttpServerSampler(provider) - : null; - if (serverSampler == null && skipPatternSampler == null) { - return SamplerFunctions.deferDecision(); - } - else if (serverSampler == null) { - return skipPatternSampler; - } - else if (skipPatternSampler == null) { - return serverSampler; - } - return new CompositeHttpSampler(skipPatternSampler, serverSampler); + HttpClientHandler defaultHttpClientHandler() { + return new NoOpHttpClientHandler(); } @Bean - @ConditionalOnMissingBean(name = HttpClientSampler.NAME) - SamplerFunction sleuthHttpClientSampler(SleuthWebProperties sleuthWebProperties) { - String skipPattern = sleuthWebProperties.getClient().getSkipPattern(); - if (skipPattern == null) { - return SamplerFunctions.deferDecision(); - } - - return new SkipPatternHttpClientSampler(Pattern.compile(skipPattern)); - } - -} - -/** - * Composite Http Sampler. - * - * @author Adrian Cole - */ -final class CompositeHttpSampler implements SamplerFunction { - - final SamplerFunction left; - - final SamplerFunction right; - - CompositeHttpSampler(SamplerFunction left, SamplerFunction right) { - this.left = left; - this.right = right; - } - - @Override - public Boolean trySample(HttpRequest request) { - // If either decision is false, return false - Boolean leftDecision = this.left.trySample(request); - if (Boolean.FALSE.equals(leftDecision)) { - return false; - } - Boolean rightDecision = this.right.trySample(request); - if (Boolean.FALSE.equals(rightDecision)) { - return false; - } - // If either decision is null, return the other - if (leftDecision == null) { - return rightDecision; - } - if (rightDecision == null) { - return leftDecision; - } - // Neither are null and at least one is true - return rightDecision; - } - -} - -/** - * Http Sampler that looks at paths. - * - * @author Marcin Grzejszczak - */ -final class SkipPatternHttpServerSampler extends SkipPatternSampler { - - private final SkipPatternProvider provider; - - SkipPatternHttpServerSampler(SkipPatternProvider provider) { - this.provider = provider; - } - - @Override - Pattern getPattern() { - return this.provider.skipPattern(); - } - -} - -final class SkipPatternHttpClientSampler extends SkipPatternSampler { - - private final Pattern skipPattern; - - SkipPatternHttpClientSampler(Pattern skipPattern) { - this.skipPattern = skipPattern; - } - - @Override - Pattern getPattern() { - return skipPattern; + @ConditionalOnMissingBean + HttpServerHandler defaultHttpServerHandler() { + return new NoOpHttpServerHandler(); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAspect.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAspect.java index 392294a57..0b0fffe8a 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAspect.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAspect.java @@ -19,8 +19,6 @@ package org.springframework.cloud.sleuth.instrument.web; import java.lang.reflect.Field; import java.util.concurrent.Callable; -import brave.Tracing; -import brave.propagation.TraceContext; import org.apache.commons.logging.Log; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; @@ -28,6 +26,9 @@ import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.instrument.async.TraceCallable; import org.springframework.web.context.request.async.WebAsyncTask; @@ -58,12 +59,15 @@ class TraceWebAspect { private static final Log log = org.apache.commons.logging.LogFactory.getLog(TraceWebAspect.class); - private final Tracing tracing; + private final Tracer tracer; + + private final CurrentTraceContext currentTraceContext; private final SpanNamer spanNamer; - TraceWebAspect(Tracing tracing, SpanNamer spanNamer) { - this.tracing = tracing; + TraceWebAspect(Tracer tracer, CurrentTraceContext currentTraceContext, SpanNamer spanNamer) { + this.tracer = tracer; + this.currentTraceContext = currentTraceContext; this.spanNamer = spanNamer; } @@ -95,20 +99,20 @@ class TraceWebAspect { @SuppressWarnings("unchecked") public Object wrapWithCorrelationId(ProceedingJoinPoint pjp) throws Throwable { Callable callable = (Callable) pjp.proceed(); - TraceContext currentSpan = this.tracing.currentTraceContext().get(); + TraceContext currentSpan = this.currentTraceContext.get(); if (currentSpan == null) { return callable; } if (log.isDebugEnabled()) { log.debug("Wrapping callable with span [" + currentSpan + "]"); } - return new TraceCallable<>(this.tracing, this.spanNamer, callable); + return new TraceCallable<>(this.tracer, this.spanNamer, callable); } @Around("anyControllerOrRestControllerWithPublicWebAsyncTaskMethod()") public Object wrapWebAsyncTaskWithCorrelationId(ProceedingJoinPoint pjp) throws Throwable { final WebAsyncTask webAsyncTask = (WebAsyncTask) pjp.proceed(); - TraceContext currentSpan = this.tracing.currentTraceContext().get(); + TraceContext currentSpan = this.currentTraceContext.get(); if (currentSpan == null) { return webAsyncTask; } @@ -119,7 +123,7 @@ class TraceWebAspect { Field callableField = WebAsyncTask.class.getDeclaredField("callable"); callableField.setAccessible(true); callableField.set(webAsyncTask, - new TraceCallable<>(this.tracing, this.spanNamer, webAsyncTask.getCallable())); + new TraceCallable<>(this.tracer, this.spanNamer, webAsyncTask.getCallable())); } catch (NoSuchFieldException ex) { log.warn("Cannot wrap webAsyncTask's callable with TraceCallable", ex); diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebFilter.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebFilter.java index 2774adec7..20538f80f 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebFilter.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebFilter.java @@ -16,17 +16,8 @@ package org.springframework.cloud.sleuth.instrument.web; -import java.net.InetSocketAddress; import java.util.concurrent.atomic.AtomicBoolean; -import brave.Span; -import brave.Tracer; -import brave.http.HttpServerHandler; -import brave.http.HttpServerRequest; -import brave.http.HttpServerResponse; -import brave.http.HttpTracing; -import brave.propagation.TraceContext; -import brave.propagation.TraceContextOrSamplingFlags; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.reactivestreams.Subscription; @@ -37,6 +28,12 @@ import reactor.util.annotation.Nullable; import reactor.util.context.Context; import org.springframework.beans.factory.BeanFactory; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.http.HttpServerHandler; +import org.springframework.cloud.sleuth.api.http.HttpServerRequest; +import org.springframework.cloud.sleuth.api.http.HttpServerResponse; import org.springframework.cloud.sleuth.instrument.reactor.SleuthReactorProperties; import org.springframework.core.Ordered; import org.springframework.http.HttpStatus; @@ -62,10 +59,10 @@ final class TraceWebFilter implements WebFilter, Ordered { * have the tracing context passed for you out of the box. That means that e.g. your * logs will not get correlated. */ - public static final int ORDER = TraceHttpAutoConfiguration.TRACING_FILTER_ORDER; + public static final int ORDER = SleuthWebProperties.TRACING_FILTER_ORDER; // Remember that this can be used in other packages - protected static final String TRACE_REQUEST_ATTR = TraceContext.class.getName(); + protected static final String TRACE_REQUEST_ATTR = Span.class.getName(); static final String MVC_CONTROLLER_CLASS_KEY = "mvc.controller.class"; static final String MVC_CONTROLLER_METHOD_KEY = "mvc.controller.method"; @@ -80,7 +77,7 @@ final class TraceWebFilter implements WebFilter, Ordered { Tracer tracer; - HttpServerHandler handler; + HttpServerHandler handler; SleuthWebProperties webProperties; @@ -95,16 +92,16 @@ final class TraceWebFilter implements WebFilter, Ordered { } @SuppressWarnings("unchecked") - HttpServerHandler handler() { + HttpServerHandler handler() { if (this.handler == null) { - this.handler = HttpServerHandler.create(this.beanFactory.getBean(HttpTracing.class)); + this.handler = this.beanFactory.getBean(HttpServerHandler.class); } return this.handler; } Tracer tracer() { if (this.tracer == null) { - this.tracer = this.beanFactory.getBean(HttpTracing.class).tracing().tracer(); + this.tracer = this.beanFactory.getBean(Tracer.class); } return this.tracer; } @@ -141,7 +138,7 @@ final class TraceWebFilter implements WebFilter, Ordered { boolean tracePresent = tracer().currentSpan() != null; if (tracePresent) { // clear any previous trace - tracer().withSpanInScope(null); // TODO: dangerous and also allocates stuff + tracer().withSpan(null); // TODO: dangerous and also allocates stuff } return tracePresent; } @@ -157,9 +154,9 @@ final class TraceWebFilter implements WebFilter, Ordered { final Tracer tracer; - final TraceContext traceContext; + final Span span; - final HttpServerHandler handler; + final HttpServerHandler handler; final AtomicBoolean initialSpanAlreadyRemoved = new AtomicBoolean(); @@ -171,7 +168,7 @@ final class TraceWebFilter implements WebFilter, Ordered { this.tracer = parent.tracer(); this.handler = parent.handler(); this.exchange = exchange; - this.traceContext = exchange.getAttribute(TRACE_REQUEST_ATTR); + this.span = exchange.getAttribute(TRACE_REQUEST_ATTR); this.initialTracePresent = initialTracePresent; } @@ -183,7 +180,7 @@ final class TraceWebFilter implements WebFilter, Ordered { private Context contextWithoutInitialSpan(Context context) { if (this.initialTracePresent && !this.initialSpanAlreadyRemoved.get()) { - context = context.delete(TraceContext.class); + context = context.delete(Span.class); this.initialSpanAlreadyRemoved.set(true); } return context; @@ -191,16 +188,20 @@ final class TraceWebFilter implements WebFilter, Ordered { private Span findOrCreateSpan(Context c) { Span span; - if (c.hasKey(TraceContext.class)) { - TraceContext parent = c.get(TraceContext.class); - span = this.tracer.newChild(parent).start(); + if (c.hasKey(Span.class)) { + Span parent = c.get(Span.class); + try (Tracer.SpanInScope spanInScope = this.tracer.withSpan(parent)) { + span = this.tracer.nextSpan(); + } if (log.isDebugEnabled()) { log.debug("Found span in reactor context" + span); } } else { - if (this.traceContext != null) { - span = this.tracer.nextSpan(TraceContextOrSamplingFlags.create(this.traceContext)); + if (this.span != null) { + try (Tracer.SpanInScope spanInScope = this.tracer.withSpan(this.span)) { + span = this.tracer.nextSpan(); + } if (log.isDebugEnabled()) { log.debug("Found span in attribute " + span); } @@ -211,7 +212,7 @@ final class TraceWebFilter implements WebFilter, Ordered { log.debug("Handled receive of span " + span); } } - this.exchange.getAttributes().put(TRACE_REQUEST_ATTR, span.context()); + this.exchange.getAttributes().put(TRACE_REQUEST_ATTR, span); } return span; } @@ -226,7 +227,7 @@ final class TraceWebFilter implements WebFilter, Ordered { final ServerWebExchange exchange; - final HttpServerHandler handler; + final HttpServerHandler handler; WebFilterTraceSubscriber(CoreSubscriber actual, Context context, Span span, MonoWebFilterTrace parent) { @@ -272,8 +273,8 @@ final class TraceWebFilter implements WebFilter, Ordered { String httpRoute = pattern != null ? pattern.toString() : ""; addResponseTagsForSpanWithoutParent(this.exchange, this.exchange.getResponse(), this.span); WrappedResponse response = new WrappedResponse(this.exchange.getResponse(), - this.exchange.getRequest().getMethodValue(), httpRoute); - this.handler.handleSend(response, t, this.span); + this.exchange.getRequest().getMethodValue(), httpRoute, t); + this.handler.handleSend(response, this.span); if (log.isDebugEnabled()) { log.debug("Handled send of " + this.span); } @@ -321,7 +322,7 @@ final class TraceWebFilter implements WebFilter, Ordered { } - static final class WrappedRequest extends HttpServerRequest { + static final class WrappedRequest implements HttpServerRequest { final ServerHttpRequest delegate; @@ -334,23 +335,6 @@ final class TraceWebFilter implements WebFilter, Ordered { return delegate; } - @Override - public boolean parseClientIpAndPort(Span span) { - boolean clientIpAndPortParsed = super.parseClientIpAndPort(span); - if (clientIpAndPortParsed) { - return true; - } - return resolveFromInetAddress(span); - } - - private boolean resolveFromInetAddress(Span span) { - InetSocketAddress addr = delegate.getRemoteAddress(); - if (addr == null) { - return false; - } - return span.remoteIpAndPort(addr.getAddress().getHostAddress(), addr.getPort()); - } - @Override public String method() { return delegate.getMethodValue(); @@ -373,7 +357,7 @@ final class TraceWebFilter implements WebFilter, Ordered { } - static final class WrappedResponse extends HttpServerResponse { + static final class WrappedResponse implements HttpServerResponse { final ServerHttpResponse delegate; @@ -381,10 +365,13 @@ final class TraceWebFilter implements WebFilter, Ordered { final String httpRoute; - WrappedResponse(ServerHttpResponse resp, String method, String httpRoute) { + final Throwable throwable; + + WrappedResponse(ServerHttpResponse resp, String method, String httpRoute, Throwable throwable) { this.delegate = resp; this.method = method; this.httpRoute = httpRoute; + this.throwable = throwable; } @Override @@ -408,6 +395,11 @@ final class TraceWebFilter implements WebFilter, Ordered { return statusCode != null ? statusCode.value() : 0; } + @Override + public Throwable error() { + return this.throwable; + } + } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebFluxAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebFluxAutoConfiguration.java index f73d337ef..b37a1135e 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebFluxAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebFluxAutoConfiguration.java @@ -16,13 +16,12 @@ package org.springframework.cloud.sleuth.instrument.web; -import brave.Tracing; - import org.springframework.beans.factory.BeanFactory; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -36,7 +35,7 @@ import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(value = "spring.sleuth.web.enabled", matchIfMissing = true) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) -@ConditionalOnBean(Tracing.class) +@ConditionalOnBean(Tracer.class) @AutoConfigureAfter(SkipPatternConfiguration.class) class TraceWebFluxAutoConfiguration { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebMvcConfigurer.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebMvcConfigurer.java index 5d67ff5ef..5d6c4c524 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebMvcConfigurer.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebMvcConfigurer.java @@ -16,9 +16,8 @@ package org.springframework.cloud.sleuth.instrument.web; -import brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor; - import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.sleuth.instrument.web.mvc.SpanCustomizingAsyncHandlerInterceptor; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebServletAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebServletAutoConfiguration.java index a448b1fe0..2d9b53c92 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebServletAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebServletAutoConfiguration.java @@ -26,11 +26,6 @@ import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; -import brave.Tracing; -import brave.http.HttpTracing; -import brave.servlet.TracingFilter; -import brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor; - import org.springframework.beans.factory.BeanFactory; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -38,12 +33,18 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; -import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.http.HttpServerHandler; +import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.cloud.sleuth.instrument.web.mvc.SpanCustomizingAsyncHandlerInterceptor; +import org.springframework.cloud.sleuth.instrument.web.servlet.TracingFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.core.Ordered; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** @@ -57,20 +58,19 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(value = "spring.sleuth.web.enabled", matchIfMissing = true) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) -@ConditionalOnBean(HttpTracing.class) -@AutoConfigureAfter(TraceHttpAutoConfiguration.class) -@EnableConfigurationProperties(SleuthWebProperties.class) +@ConditionalOnBean(Tracer.class) +@AutoConfigureAfter(TraceAutoConfiguration.class) @Import(SpanCustomizingAsyncHandlerInterceptor.class) -class TraceWebServletAutoConfiguration { +public class TraceWebServletAutoConfiguration { /** * Default filter order for the Http tracing filter. */ - public static final int TRACING_FILTER_ORDER = TraceHttpAutoConfiguration.TRACING_FILTER_ORDER; + public static final int TRACING_FILTER_ORDER = Ordered.HIGHEST_PRECEDENCE + 5; @Bean - TraceWebAspect traceWebAspect(Tracing tracing, SpanNamer spanNamer) { - return new TraceWebAspect(tracing, spanNamer); + TraceWebAspect traceWebAspect(Tracer tracer, CurrentTraceContext currentTraceContext, SpanNamer spanNamer) { + return new TraceWebAspect(tracer, currentTraceContext, spanNamer); } @Bean @@ -84,8 +84,8 @@ class TraceWebServletAutoConfiguration { @Bean @ConditionalOnMissingBean - public TracingFilter tracingFilter(HttpTracing tracing) { - return (TracingFilter) TracingFilter.create(tracing); + public TracingFilter tracingFilter(CurrentTraceContext currentTraceContext, HttpServerHandler httpServerHandler) { + return TracingFilter.create(currentTraceContext, httpServerHandler); } /** diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/WebFluxSleuthOperators.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/WebFluxSleuthOperators.java index b0684c025..f27da8072 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/WebFluxSleuthOperators.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/WebFluxSleuthOperators.java @@ -19,15 +19,16 @@ package org.springframework.cloud.sleuth.instrument.web; import java.util.concurrent.Callable; import java.util.function.Consumer; -import brave.Tracing; -import brave.propagation.CurrentTraceContext; -import brave.propagation.TraceContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import reactor.core.publisher.Signal; import reactor.core.publisher.SignalType; import reactor.util.context.Context; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.web.server.ServerWebExchange; /** @@ -114,44 +115,46 @@ public final class WebFluxSleuthOperators { } private static TraceContext traceContextOrNew(Context context) { - Tracing tracing = context.get(Tracing.class); + Tracer tracer = context.get(Tracer.class); if (!context.hasKey(TraceContext.class)) { if (log.isDebugEnabled()) { log.debug("No trace context found, will create a new span"); } - return tracing.tracer().nextSpan().context(); + return tracer.nextSpan().context(); } return context.get(TraceContext.class); } /** * Wraps a runnable with a span. - * @param tracing - tracing bean + * @param tracer - tracer bean + * @param currentTraceContext - currentTraceContext bean * @param exchange - server web exchange that can contain the {@link TraceContext} in * its attribute - * @param runnable - lambda to execute within the tracing context + * @param runnable - lambda to execute within the currentTraceContext context */ - public static void withSpanInScope(Tracing tracing, ServerWebExchange exchange, Runnable runnable) { - CurrentTraceContext currentTraceContext = tracing.currentTraceContext(); - TraceContext traceContext = traceContextFromExchangeOrNew(tracing, exchange); - try (CurrentTraceContext.Scope scope = currentTraceContext.maybeScope(traceContext)) { + public static void withSpanInScope(Tracer tracer, CurrentTraceContext currentTraceContext, + ServerWebExchange exchange, Runnable runnable) { + Span span = spanFromExchangeOrNew(tracer, exchange); + try (CurrentTraceContext.Scope scope = currentTraceContext.maybeScope(span.context())) { runnable.run(); } } /** * Wraps a callable with a span. - * @param tracing - tracing bean + * @param tracer - tracer bean + * @param currentTraceContext - currentTraceContext bean * @param exchange - server web exchange that can contain the {@link TraceContext} in * its attribute * @param callable - lambda to execute within the tracing context * @param callable's return type * @return value from the callable */ - public static T withSpanInScope(Tracing tracing, ServerWebExchange exchange, Callable callable) { - CurrentTraceContext currentTraceContext = tracing.currentTraceContext(); - TraceContext traceContext = traceContextFromExchangeOrNew(tracing, exchange); - return withContext(callable, currentTraceContext, traceContext); + public static T withSpanInScope(Tracer tracer, CurrentTraceContext currentTraceContext, + ServerWebExchange exchange, Callable callable) { + Span span = spanFromExchangeOrNew(tracer, exchange); + return withContext(callable, currentTraceContext, span.context()); } /** @@ -195,15 +198,15 @@ public final class WebFluxSleuthOperators { } } - private static TraceContext traceContextFromExchangeOrNew(Tracing tracing, ServerWebExchange exchange) { - TraceContext traceContext = exchange.getAttribute(TraceContext.class.getName()); - if (traceContext == null) { + private static Span spanFromExchangeOrNew(Tracer tracer, ServerWebExchange exchange) { + Span span = exchange.getAttribute(Span.class.getName()); + if (span == null) { if (log.isDebugEnabled()) { log.debug("No trace context found, will create a new span"); } - traceContext = tracing.tracer().nextSpan().context(); + span = tracer.nextSpan(); } - return traceContext; + return span; } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/HttpClientBeanPostProcessor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/HttpClientBeanPostProcessor.java index 74d8b1b6c..afe5c1bea 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/HttpClientBeanPostProcessor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/HttpClientBeanPostProcessor.java @@ -17,17 +17,12 @@ package org.springframework.cloud.sleuth.instrument.web.client; import java.net.InetSocketAddress; -import java.net.SocketAddress; import java.util.concurrent.CancellationException; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Supplier; -import brave.Span; -import brave.http.HttpClientHandler; -import brave.http.HttpTracing; -import brave.propagation.TraceContext; import reactor.core.publisher.Mono; import reactor.netty.Connection; import reactor.netty.http.client.HttpClient; @@ -37,6 +32,10 @@ import reactor.util.context.Context; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; import org.springframework.cloud.sleuth.internal.LazyBean; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.lang.Nullable; @@ -51,7 +50,7 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - LazyBean httpTracing = LazyBean.create(this.springContext, HttpTracing.class); + LazyBean currentContext = LazyBean.create(this.springContext, CurrentTraceContext.class); if (bean instanceof HttpClient) { // This adds handlers to manage the span lifecycle. All require explicit // propagation of the current span as a reactor context property. @@ -60,13 +59,13 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor { // In our case, we treat a normal response no differently than one in // preparation of a redirect follow-up. - TracingDoOnResponse doOnResponse = new TracingDoOnResponse(httpTracing); - return ((HttpClient) bean).doOnResponseError(new TracingDoOnErrorResponse(httpTracing)) + TracingDoOnResponse doOnResponse = new TracingDoOnResponse(springContext); + return ((HttpClient) bean).doOnResponseError(new TracingDoOnErrorResponse(springContext)) .doOnRedirect(doOnResponse).doOnResponse(doOnResponse) - .doOnRequestError(new TracingDoOnErrorRequest(httpTracing)) - .doOnRequest(new TracingDoOnRequest(httpTracing)).mapConnect(new TracingMapConnect(() -> { - HttpTracing ref = httpTracing.get(); - return ref != null ? ref.tracing().currentTraceContext().get() : null; + .doOnRequestError(new TracingDoOnErrorRequest(springContext)) + .doOnRequest(new TracingDoOnRequest(springContext)).mapConnect(new TracingMapConnect(() -> { + CurrentTraceContext ref = currentContext.get(); + return ref != null ? ref.get() : null; })); } return bean; @@ -97,7 +96,7 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor { // This function is invoked once per-request. We keep a reference to the // pending client span here, so that only one signal completes the span. PendingSpan pendingSpan = new PendingSpan(); - return mono.subscriberContext(context -> { + return mono.contextWrite(context -> { TraceContext invocationContext = currentTraceContext.get(); if (invocationContext != null) { // Read in this processor and also in ScopePassingSpanSubscriber @@ -110,7 +109,7 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor { Span span = pendingSpan.getAndSet(null); if (span != null) { span.error(CANCELLED_ERROR); - span.finish(); + span.end(); } }); } @@ -119,24 +118,24 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor { private static class TracingDoOnRequest implements BiConsumer { - final LazyBean httpTracing; + final ConfigurableApplicationContext context; - HttpClientHandler handler; + HttpClientHandler handler; - TracingDoOnRequest(LazyBean httpTracing) { - this.httpTracing = httpTracing; + TracingDoOnRequest(ConfigurableApplicationContext context) { + this.context = context; } - HttpClientHandler handler() { + HttpClientHandler handler() { if (this.handler == null) { - this.handler = HttpClientHandler.create(httpTracing.get()); + this.handler = context.getBean(HttpClientHandler.class); } return this.handler; } @Override public void accept(HttpClientRequest req, Connection connection) { - PendingSpan pendingSpan = req.currentContext().getOrDefault(PendingSpan.class, null); + PendingSpan pendingSpan = req.currentContextView().getOrDefault(PendingSpan.class, null); if (pendingSpan == null) { return; // Somehow TracingMapConnect was not invoked.. skip out } @@ -151,32 +150,20 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor { } // Start a new client span with the appropriate parent - TraceContext parent = req.currentContext().getOrDefault(TraceContext.class, null); - HttpClientRequestWrapper request = new HttpClientRequestWrapper(req); + TraceContext parent = req.currentContextView().getOrDefault(TraceContext.class, null); + HttpClientRequestWrapper request = new HttpClientRequestWrapper(req, connection); - span = handler().handleSendWithParent(request, parent); - parseConnectionAddress(connection, span); + span = handler().handleSend(request, parent); pendingSpan.set(span); } - static void parseConnectionAddress(Connection connection, Span span) { - if (span.isNoop()) { - return; - } - SocketAddress socketAddress = connection.address(); - if (socketAddress instanceof InetSocketAddress) { - InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress; - span.remoteIpAndPort(inetSocketAddress.getHostString(), inetSocketAddress.getPort()); - } - } - } private static class TracingDoOnResponse extends AbstractTracingDoOnHandler implements BiConsumer { - TracingDoOnResponse(LazyBean httpTracing) { - super(httpTracing); + TracingDoOnResponse(ConfigurableApplicationContext context) { + super(context); } @Override @@ -189,8 +176,8 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor { private static class TracingDoOnErrorRequest extends AbstractTracingDoOnHandler implements BiConsumer { - TracingDoOnErrorRequest(LazyBean httpTracing) { - super(httpTracing); + TracingDoOnErrorRequest(ConfigurableApplicationContext context) { + super(context); } @Override @@ -203,8 +190,8 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor { private static class TracingDoOnErrorResponse extends AbstractTracingDoOnHandler implements BiConsumer { - TracingDoOnErrorResponse(LazyBean httpTracing) { - super(httpTracing); + TracingDoOnErrorResponse(ConfigurableApplicationContext context) { + super(context); } @Override @@ -216,17 +203,17 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor { private static abstract class AbstractTracingDoOnHandler { - final LazyBean httpTracing; + final ConfigurableApplicationContext context; - HttpClientHandler handler; + HttpClientHandler handler; - AbstractTracingDoOnHandler(LazyBean httpTracing) { - this.httpTracing = httpTracing; + AbstractTracingDoOnHandler(ConfigurableApplicationContext context) { + this.context = context; } - HttpClientHandler handler() { + HttpClientHandler handler() { if (this.handler == null) { - this.handler = HttpClientHandler.create(httpTracing.get()); + this.handler = this.context.getBean(HttpClientHandler.class); } return this.handler; } @@ -241,18 +228,39 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor { if (span == null) { return; // Unexpected. In the handle method, without a span to finish! } - HttpClientResponseWrapper response = resp != null ? new HttpClientResponseWrapper(resp) : null; - handler().handleReceive(response, error, span); + HttpClientResponseWrapper response = new HttpClientResponseWrapper(resp, error); + handler().handleReceive(response, span); } } - static final class HttpClientRequestWrapper extends brave.http.HttpClientRequest { + static final class HttpClientRequestWrapper implements org.springframework.cloud.sleuth.api.http.HttpClientRequest { final HttpClientRequest delegate; - HttpClientRequestWrapper(HttpClientRequest delegate) { + final Connection connection; + + Boolean inetSocketAddress; + + InetSocketAddress address; + + HttpClientRequestWrapper(HttpClientRequest delegate, Connection connection) { this.delegate = delegate; + this.connection = connection; + } + + InetSocketAddress address() { + this.inetSocketAddress = this.inetSocketAddress != null ? this.inetSocketAddress + : connection.address() instanceof InetSocketAddress; + if (this.address != null && this.inetSocketAddress) { + return this.address; + } + else if (this.address == null && this.inetSocketAddress) { + this.address = (InetSocketAddress) connection.address(); + this.inetSocketAddress = true; + return this.address; + } + return null; } @Override @@ -285,39 +293,69 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor { delegate.header(name, value); } + @Override + public String remoteIp() { + InetSocketAddress address = address(); + return address != null ? address.getHostString() : null; + } + + @Override + public int remotePort() { + InetSocketAddress address = address(); + return address != null ? address.getPort() : 0; + } + } - static final class HttpClientResponseWrapper extends brave.http.HttpClientResponse { + static final class HttpClientResponseWrapper + implements org.springframework.cloud.sleuth.api.http.HttpClientResponse { + @Nullable final HttpClientResponse delegate; HttpClientRequestWrapper request; - HttpClientResponseWrapper(HttpClientResponse delegate) { + final Throwable error; + + HttpClientResponseWrapper(@Nullable HttpClientResponse delegate, Throwable error) { this.delegate = delegate; + this.error = error; } @Override public Object unwrap() { - return delegate; + return this.delegate; } @Override public HttpClientRequestWrapper request() { if (request == null) { if (delegate instanceof HttpClientRequest) { - request = new HttpClientRequestWrapper((HttpClientRequest) delegate); - } - else { - assert false : "We expect the response to be the same reference as the request"; + this.request = new HttpClientRequestWrapper((HttpClientRequest) delegate, null); } } - return request; + return this.request; } @Override public int statusCode() { - return delegate.status().code(); + if (this.delegate == null) { + return 0; + } + return this.delegate.status().code(); + } + + @Override + public Throwable error() { + return this.error; + } + + @Override + public String header(String header) { + if (this.delegate == null) { + return null; + } + return this.delegate.responseHeaders().get(header); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/SleuthWebClientEnabled.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/SleuthWebClientEnabled.java index 0561f9d5c..0f9d1ca26 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/SleuthWebClientEnabled.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/SleuthWebClientEnabled.java @@ -34,6 +34,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @Target({ ElementType.TYPE, ElementType.METHOD }) @Documented @ConditionalOnProperty(value = "spring.sleuth.web.client.enabled", matchIfMissing = true) -@interface SleuthWebClientEnabled { +public @interface SleuthWebClientEnabled { } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRequestHttpHeadersFilter.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRequestHttpHeadersFilter.java index 67d800e15..990f114db 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRequestHttpHeadersFilter.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRequestHttpHeadersFilter.java @@ -19,17 +19,17 @@ package org.springframework.cloud.sleuth.instrument.web.client; import java.util.List; import java.util.Map; -import brave.Span; -import brave.Tracer; -import brave.http.HttpClientHandler; -import brave.http.HttpTracing; -import brave.propagation.TraceContext; -import brave.propagation.TraceContext.Extractor; -import brave.propagation.TraceContextOrSamplingFlags; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; +import org.springframework.cloud.sleuth.api.http.HttpClientRequest; +import org.springframework.cloud.sleuth.api.http.HttpClientResponse; +import org.springframework.cloud.sleuth.api.propagation.Propagator; import org.springframework.http.HttpHeaders; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; @@ -41,12 +41,12 @@ final class TraceRequestHttpHeadersFilter extends AbstractHttpHeadersFilter { static final String TRACE_REQUEST_ATTR = TraceContext.class.getName(); - private TraceRequestHttpHeadersFilter(HttpTracing httpTracing) { - super(httpTracing); + private TraceRequestHttpHeadersFilter(Tracer tracer, HttpClientHandler handler, Propagator propagator) { + super(tracer, handler, propagator); } - static HttpHeadersFilter create(HttpTracing httpTracing) { - return new TraceRequestHttpHeadersFilter(httpTracing); + static HttpHeadersFilter create(Tracer tracer, HttpClientHandler handler, Propagator propagator) { + return new TraceRequestHttpHeadersFilter(tracer, handler, propagator); } @Override @@ -54,7 +54,7 @@ final class TraceRequestHttpHeadersFilter extends AbstractHttpHeadersFilter { if (log.isDebugEnabled()) { log.debug("Will instrument the HTTP request headers [" + exchange.getRequest().getHeaders() + "]"); } - HttpClientRequest request = new HttpClientRequest(exchange.getRequest(), input); + ServerHttpClientRequest request = new ServerHttpClientRequest(exchange.getRequest(), input); Span currentSpan = currentSpan(exchange, request); Span span = injectedSpan(request, currentSpan); if (log.isDebugEnabled()) { @@ -72,7 +72,7 @@ final class TraceRequestHttpHeadersFilter extends AbstractHttpHeadersFilter { return headersWithInput; } - private Span currentSpan(ServerWebExchange exchange, HttpClientRequest request) { + private Span currentSpan(ServerWebExchange exchange, ServerHttpClientRequest request) { Span currentSpan = currentSpan(exchange); if (currentSpan != null) { return currentSpan; @@ -80,8 +80,7 @@ final class TraceRequestHttpHeadersFilter extends AbstractHttpHeadersFilter { // Usually, an HTTP client would not attempt to resume a trace from headers, as a // server would always place its span in scope. However, in commit 848442e, // this behavior was added in support of gateway. - TraceContextOrSamplingFlags contextOrFlags = this.extractor.extract(request); - return this.tracer.nextSpan(contextOrFlags); + return this.propagator.extract(request, HttpClientRequest::header).start(); } private Span currentSpan(ServerWebExchange exchange) { @@ -95,12 +94,14 @@ final class TraceRequestHttpHeadersFilter extends AbstractHttpHeadersFilter { return this.tracer.currentSpan(); } - private Span injectedSpan(HttpClientRequest request, Span currentSpan) { + private Span injectedSpan(ServerHttpClientRequest request, Span currentSpan) { if (currentSpan == null) { return this.handler.handleSend(request); } - Span clientSpan = this.tracer.newChild(currentSpan.context()); - return this.handler.handleSend(request, clientSpan); + try (Tracer.SpanInScope ws = this.tracer.withSpan(currentSpan)) { + Span clientSpan = this.tracer.nextSpan(); + return this.handler.handleSend(request, clientSpan.context()); + } } private void addHeadersWithInput(HttpHeaders filteredHeaders, HttpHeaders headersWithInput) { @@ -122,12 +123,12 @@ final class TraceResponseHttpHeadersFilter extends AbstractHttpHeadersFilter { private static final Log log = LogFactory.getLog(TraceResponseHttpHeadersFilter.class); - private TraceResponseHttpHeadersFilter(HttpTracing httpTracing) { - super(httpTracing); + private TraceResponseHttpHeadersFilter(Tracer tracer, HttpClientHandler handler, Propagator propagator) { + super(tracer, handler, propagator); } - static HttpHeadersFilter create(HttpTracing httpTracing) { - return new TraceResponseHttpHeadersFilter(httpTracing); + static HttpHeadersFilter create(Tracer tracer, HttpClientHandler handler, Propagator propagator) { + return new TraceResponseHttpHeadersFilter(tracer, handler, propagator); } @Override @@ -139,8 +140,8 @@ final class TraceResponseHttpHeadersFilter extends AbstractHttpHeadersFilter { if (log.isDebugEnabled()) { log.debug("Will instrument the response"); } - HttpClientResponse response = new HttpClientResponse(exchange.getResponse()); - this.handler.handleReceive(response, null, (Span) storedSpan); + ServerHttpClientResponse response = new ServerHttpClientResponse(exchange.getResponse()); + this.handler.handleReceive(response, (Span) storedSpan); if (log.isDebugEnabled()) { log.debug("The response was handled for span " + storedSpan); } @@ -160,26 +161,23 @@ abstract class AbstractHttpHeadersFilter implements HttpHeadersFilter { final Tracer tracer; - final HttpClientHandler handler; + final HttpClientHandler handler; - final HttpTracing httpTracing; + final Propagator propagator; - final Extractor extractor; - - AbstractHttpHeadersFilter(HttpTracing httpTracing) { - this.tracer = httpTracing.tracing().tracer(); - this.extractor = httpTracing.tracing().propagation().extractor(HttpClientRequest::header); - this.handler = HttpClientHandler.create(httpTracing); - this.httpTracing = httpTracing; + AbstractHttpHeadersFilter(Tracer tracer, HttpClientHandler handler, Propagator propagator) { + this.tracer = tracer; + this.propagator = propagator; + this.handler = handler; } - static final class HttpClientRequest extends brave.http.HttpClientRequest { + static final class ServerHttpClientRequest implements HttpClientRequest { final ServerHttpRequest delegate; final HttpHeaders filteredHeaders; - HttpClientRequest(ServerHttpRequest delegate, HttpHeaders filteredHeaders) { + ServerHttpClientRequest(ServerHttpRequest delegate, HttpHeaders filteredHeaders) { this.delegate = delegate; this.filteredHeaders = filteredHeaders; } @@ -216,11 +214,11 @@ abstract class AbstractHttpHeadersFilter implements HttpHeadersFilter { } - static final class HttpClientResponse extends brave.http.HttpClientResponse { + static final class ServerHttpClientResponse implements HttpClientResponse { final ServerHttpResponse delegate; - HttpClientResponse(ServerHttpResponse delegate) { + ServerHttpClientResponse(ServerHttpResponse delegate) { this.delegate = delegate; } @@ -234,6 +232,15 @@ abstract class AbstractHttpHeadersFilter implements HttpHeadersFilter { return delegate.getStatusCode() != null ? delegate.getStatusCode().value() : 0; } + @Override + public String header(String header) { + List headers = delegate.getHeaders().get(header); + if (headers == null || headers.isEmpty()) { + return null; + } + return headers.get(0); + } + } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebAsyncClientAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebAsyncClientAutoConfiguration.java index 49733f1f1..c00621f00 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebAsyncClientAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebAsyncClientAutoConfiguration.java @@ -22,15 +22,17 @@ import java.util.List; import javax.annotation.PostConstruct; -import brave.http.HttpTracing; -import brave.spring.web.TracingAsyncClientHttpRequestInterceptor; - 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.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; +import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; import org.springframework.cloud.sleuth.instrument.web.TraceHttpAutoConfiguration; +import org.springframework.cloud.sleuth.instrument.web.mvc.TracingAsyncClientHttpRequestInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.AsyncClientHttpRequestFactory; @@ -49,8 +51,8 @@ import org.springframework.web.client.AsyncRestTemplate; @SleuthWebClientEnabled @ConditionalOnProperty(value = "spring.sleuth.web.async.client.enabled", matchIfMissing = true) @ConditionalOnClass(AsyncRestTemplate.class) -@ConditionalOnBean(HttpTracing.class) -@AutoConfigureAfter(TraceHttpAutoConfiguration.class) +@ConditionalOnBean(Tracer.class) +@AutoConfigureAfter({ TraceAutoConfiguration.class, TraceHttpAutoConfiguration.class }) class TraceWebAsyncClientAutoConfiguration { @Configuration(proxyBeanMethods = false) @@ -59,9 +61,9 @@ class TraceWebAsyncClientAutoConfiguration { @Bean public TracingAsyncClientHttpRequestInterceptor asyncTracingClientHttpRequestInterceptor( - HttpTracing httpTracing) { + CurrentTraceContext currentTraceContext, HttpClientHandler httpClientHandler) { return (TracingAsyncClientHttpRequestInterceptor) TracingAsyncClientHttpRequestInterceptor - .create(httpTracing); + .create(currentTraceContext, httpClientHandler); } @Configuration(proxyBeanMethods = false) diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java index f4c4a615c..1da11aae2 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java @@ -20,12 +20,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import brave.http.HttpTracing; -import brave.httpasyncclient.TracingHttpAsyncClientBuilder; -import brave.httpclient.TracingHttpClientBuilder; -import brave.spring.web.TracingClientHttpRequestInterceptor; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; import reactor.netty.http.client.HttpClient; import org.springframework.beans.BeansException; @@ -43,7 +37,12 @@ import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoR import org.springframework.boot.web.client.RestTemplateCustomizer; import org.springframework.cloud.commons.httpclient.HttpClientConfiguration; import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter; -import org.springframework.cloud.sleuth.instrument.web.TraceHttpAutoConfiguration; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; +import org.springframework.cloud.sleuth.api.propagation.Propagator; +import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.cloud.sleuth.instrument.web.mvc.TracingClientHttpRequestInterceptor; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -65,9 +64,9 @@ import org.springframework.web.reactive.function.client.WebClient; * @since 1.0.0 */ @Configuration(proxyBeanMethods = false) -@SleuthWebClientEnabled -@ConditionalOnBean(HttpTracing.class) -@AutoConfigureAfter(TraceHttpAutoConfiguration.class) +@ConditionalOnProperty(value = "spring.sleuth.web.client.enabled", matchIfMissing = true) +@ConditionalOnBean(Tracer.class) +@AutoConfigureAfter(TraceAutoConfiguration.class) @AutoConfigureBefore(HttpClientConfiguration.class) class TraceWebClientAutoConfiguration { @@ -76,8 +75,10 @@ class TraceWebClientAutoConfiguration { static class RestTemplateConfig { @Bean - public TracingClientHttpRequestInterceptor tracingClientHttpRequestInterceptor(HttpTracing httpTracing) { - return (TracingClientHttpRequestInterceptor) TracingClientHttpRequestInterceptor.create(httpTracing); + public TracingClientHttpRequestInterceptor tracingClientHttpRequestInterceptor( + CurrentTraceContext currentTraceContext, HttpClientHandler httpClientHandler) { + return (TracingClientHttpRequestInterceptor) TracingClientHttpRequestInterceptor.create(currentTraceContext, + httpClientHandler); } @Configuration(proxyBeanMethods = false) @@ -103,25 +104,30 @@ class TraceWebClientAutoConfiguration { } @Configuration(proxyBeanMethods = false) - @ConditionalOnClass(HttpClientBuilder.class) - static class HttpClientBuilderConfig { + @ConditionalOnClass(HttpHeadersFilter.class) + static class HttpHeadersFilterConfig { @Bean - @ConditionalOnMissingBean - HttpClientBuilder traceHttpClientBuilder(HttpTracing httpTracing) { - return TracingHttpClientBuilder.create(httpTracing); + HttpHeadersFilter traceRequestHttpHeadersFilter(Tracer tracer, HttpClientHandler handler, + Propagator propagator) { + return TraceRequestHttpHeadersFilter.create(tracer, handler, propagator); + } + + @Bean + HttpHeadersFilter traceResponseHttpHeadersFilter(Tracer tracer, HttpClientHandler handler, + Propagator propagator) { + return TraceResponseHttpHeadersFilter.create(tracer, handler, propagator); } } @Configuration(proxyBeanMethods = false) - @ConditionalOnClass(HttpAsyncClientBuilder.class) - static class HttpAsyncClientBuilderConfig { + @ConditionalOnClass(HttpClient.class) + static class NettyConfiguration { @Bean - @ConditionalOnMissingBean - HttpAsyncClientBuilder traceHttpAsyncClientBuilder(HttpTracing httpTracing) { - return TracingHttpAsyncClientBuilder.create(httpTracing); + static HttpClientBeanPostProcessor httpClientBeanPostProcessor(ConfigurableApplicationContext springContext) { + return new HttpClientBeanPostProcessor(springContext); } } @@ -139,33 +145,6 @@ class TraceWebClientAutoConfiguration { } - @Configuration(proxyBeanMethods = false) - @ConditionalOnClass(HttpHeadersFilter.class) - static class HttpHeadersFilterConfig { - - @Bean - HttpHeadersFilter traceRequestHttpHeadersFilter(HttpTracing httpTracing) { - return TraceRequestHttpHeadersFilter.create(httpTracing); - } - - @Bean - HttpHeadersFilter traceResponseHttpHeadersFilter(HttpTracing httpTracing) { - return TraceResponseHttpHeadersFilter.create(httpTracing); - } - - } - - @Configuration(proxyBeanMethods = false) - @ConditionalOnClass(HttpClient.class) - static class NettyConfiguration { - - @Bean - static HttpClientBeanPostProcessor httpClientBeanPostProcessor(ConfigurableApplicationContext springContext) { - return new HttpClientBeanPostProcessor(springContext); - } - - } - @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ UserInfoRestTemplateCustomizer.class, OAuth2RestTemplate.class }) protected static class TraceOAuthConfiguration { @@ -211,6 +190,34 @@ class TraceWebClientAutoConfiguration { } +class TraceRestTemplateBeanPostProcessor implements BeanPostProcessor { + + private final BeanFactory beanFactory; + + TraceRestTemplateBeanPostProcessor(BeanFactory beanFactory) { + this.beanFactory = beanFactory; + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof RestTemplate) { + RestTemplate rt = (RestTemplate) bean; + new RestTemplateInterceptorInjector(interceptor()).inject(rt); + } + return bean; + } + + private LazyTracingClientHttpRequestInterceptor interceptor() { + return new LazyTracingClientHttpRequestInterceptor(this.beanFactory); + } + +} + class RestTemplateInterceptorInjector { private final ClientHttpRequestInterceptor interceptor; @@ -256,34 +263,6 @@ class TraceRestTemplateCustomizer implements RestTemplateCustomizer { } -class TraceRestTemplateBeanPostProcessor implements BeanPostProcessor { - - private final BeanFactory beanFactory; - - TraceRestTemplateBeanPostProcessor(BeanFactory beanFactory) { - this.beanFactory = beanFactory; - } - - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - return bean; - } - - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - if (bean instanceof RestTemplate) { - RestTemplate rt = (RestTemplate) bean; - new RestTemplateInterceptorInjector(interceptor()).inject(rt); - } - return bean; - } - - private LazyTracingClientHttpRequestInterceptor interceptor() { - return new LazyTracingClientHttpRequestInterceptor(this.beanFactory); - } - -} - class LazyTracingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { private final BeanFactory beanFactory; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientBeanPostProcessor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientBeanPostProcessor.java index 0cce3641f..778568aba 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientBeanPostProcessor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientBeanPostProcessor.java @@ -20,19 +20,9 @@ import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -import java.util.function.Function; -import brave.Span; -import brave.http.HttpClientHandler; -import brave.http.HttpClientRequest; -import brave.http.HttpClientResponse; -import brave.http.HttpTracing; -import brave.propagation.CurrentTraceContext; -import brave.propagation.CurrentTraceContext.Scope; -import brave.propagation.TraceContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.reactivestreams.Publisher; import org.reactivestreams.Subscription; import reactor.core.CoreSubscriber; import reactor.core.publisher.Mono; @@ -40,17 +30,19 @@ import reactor.util.annotation.Nullable; import reactor.util.context.Context; import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.cloud.sleuth.internal.LazyBean; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; +import org.springframework.cloud.sleuth.api.http.HttpClientRequest; +import org.springframework.cloud.sleuth.api.http.HttpClientResponse; import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.core.io.buffer.DataBuffer; import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.ExchangeFilterFunction; import org.springframework.web.reactive.function.client.ExchangeFunction; import org.springframework.web.reactive.function.client.WebClient; -import static org.springframework.cloud.sleuth.instrument.reactor.ReactorSleuth.scopePassingSpanOperator; - /** * {@link BeanPostProcessor} to wrap a {@link WebClient} instance into its trace * representation. @@ -112,18 +104,15 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction { private static final Log log = LogFactory.getLog(TraceExchangeFilterFunction.class); - final LazyBean httpTracing; - - final Function, ? extends Publisher> scopePassingTransformer; + final ConfigurableApplicationContext springContext; // Lazy initialized fields - HttpClientHandler handler; + HttpClientHandler handler; CurrentTraceContext currentTraceContext; TraceExchangeFilterFunction(ConfigurableApplicationContext springContext) { - this.httpTracing = LazyBean.create(springContext, HttpTracing.class); - this.scopePassingTransformer = scopePassingSpanOperator(springContext); + this.springContext = springContext; } public static ExchangeFilterFunction create(ConfigurableApplicationContext springContext) { @@ -137,14 +126,14 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction { CurrentTraceContext currentTraceContext() { if (this.currentTraceContext == null) { - this.currentTraceContext = httpTracing.get().tracing().currentTraceContext(); + this.currentTraceContext = this.springContext.getBean(CurrentTraceContext.class); } return this.currentTraceContext; } - HttpClientHandler handler() { + HttpClientHandler handler() { if (this.handler == null) { - this.handler = HttpClientHandler.create(this.httpTracing.get()); + this.handler = this.springContext.getBean(HttpClientHandler.class); } return this.handler; } @@ -155,7 +144,7 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction { final ClientRequest request; - final HttpClientHandler handler; + final HttpClientHandler handler; final CurrentTraceContext currentTraceContext; @@ -172,11 +161,11 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction { if (log.isTraceEnabled()) { log.trace("Got the following context [" + context + "]"); } - ClientRequestWrapper wrapper = new ClientRequestWrapper(request); + ClientRequestWrapper wrapper = new ClientRequestWrapper(this.request); TraceContext parent = context.hasKey(TraceContext.class) ? context.get(TraceContext.class) : null; - Span span = handler.handleSendWithParent(wrapper, parent); - if (log.isDebugEnabled()) { - log.debug("HttpClientHandler::handleSend: " + span); + Span span = handler.handleSend(wrapper, parent); + if (log.isTraceEnabled()) { + log.trace("HttpClientHandler::handleSend: " + span); } // NOTE: We are starting the client span for the request here, but it could be // canceled prior to actually being invoked. TraceWebClientSubscription will @@ -187,6 +176,9 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction { } + /** + * Subscriber for WebClient. + */ static final class TraceWebClientSubscriber extends AtomicReference implements CoreSubscriber { @@ -197,7 +189,7 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction { @Nullable final TraceContext parent; - final HttpClientHandler handler; + final HttpClientHandler handler; final CurrentTraceContext currentTraceContext; @@ -219,36 +211,51 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction { @Override public void onNext(ClientResponse response) { - try (Scope scope = currentTraceContext.maybeScope(parent)) { + try (CurrentTraceContext.Scope scope = this.currentTraceContext.maybeScope(parent)) { + if (log.isTraceEnabled()) { + log.trace("OnNext"); + } // decorate response body this.actual.onNext(response); } finally { Span span = getAndSet(null); if (span != null) { + if (log.isTraceEnabled()) { + log.trace("OnNext finally"); + } // TODO: is there a way to read the request at response time? - this.handler.handleReceive(new ClientResponseWrapper(response), null, span); + this.handler.handleReceive(new ClientResponseWrapper(response), span); } } } @Override public void onError(Throwable t) { - try (Scope scope = currentTraceContext.maybeScope(parent)) { + try (CurrentTraceContext.Scope scope = this.currentTraceContext.maybeScope(parent)) { + if (log.isTraceEnabled()) { + log.trace("OnError"); + } this.actual.onError(t); } finally { Span span = getAndSet(null); if (span != null) { + if (log.isTraceEnabled()) { + log.trace("OnError finally"); + } span.error(t); - span.finish(); + span.end(); } } } @Override public void onComplete() { - try (Scope scope = currentTraceContext.maybeScope(parent)) { + try (CurrentTraceContext.Scope scope = this.currentTraceContext.maybeScope(parent)) { + if (log.isTraceEnabled()) { + log.trace("OnComplete"); + } this.actual.onComplete(); } finally { @@ -256,8 +263,8 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction { if (span != null) { // TODO: backfill empty test: // https://github.com/spring-cloud/spring-cloud-sleuth/issues/1570 - if (log.isDebugEnabled()) { - log.debug("Reached OnComplete without finishing [" + span + "]"); + if (log.isTraceEnabled()) { + log.trace("Reached OnComplete without finishing [" + span + "]"); } span.abandon(); } @@ -305,8 +312,8 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction { // but before another signal (like onComplete) completed the span. Span span = pendingSpan.getAndSet(null); if (span != null) { - if (log.isDebugEnabled()) { - log.debug("Subscription was cancelled. TraceWebClientBeanPostProcessor Will close the span [" + span + if (log.isTraceEnabled()) { + log.trace("Subscription was cancelled. TraceWebClientBeanPostProcessor Will close the span [" + span + "]"); } @@ -315,14 +322,14 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction { } else { // Request was canceled in-flight span.error(CANCELLED_ERROR); - span.finish(); + span.end(); } } } } - private static final class ClientRequestWrapper extends HttpClientRequest { + private static final class ClientRequestWrapper implements HttpClientRequest { final ClientRequest delegate; @@ -369,7 +376,7 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction { } - static final class ClientResponseWrapper extends HttpClientResponse { + static final class ClientResponseWrapper implements HttpClientResponse { final ClientResponse delegate; @@ -388,6 +395,15 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction { return Math.max(delegate.rawStatusCode(), 0); } + @Override + public String header(String header) { + List headers = delegate.headers().header(header); + if (headers.isEmpty()) { + return null; + } + return headers.get(0); + } + } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/LazyClient.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/LazyClient.java index c7ca6436a..4d1c1c0b3 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/LazyClient.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/LazyClient.java @@ -18,13 +18,14 @@ package org.springframework.cloud.sleuth.instrument.web.client.feign; import java.io.IOException; -import brave.http.HttpTracing; import feign.Client; import feign.Request; import feign.Response; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; /** * Lazy implementation of the Feign Client. @@ -59,8 +60,8 @@ class LazyClient implements Client { this.delegate = this.beanFactory.getBean(Client.class); } catch (BeansException ex) { - this.delegate = TracingFeignClient.create(beanFactory.getBean(HttpTracing.class), - new Client.Default(null, null)); + this.delegate = TracingFeignClient.create(this.beanFactory.getBean(CurrentTraceContext.class), + this.beanFactory.getBean(HttpClientHandler.class), new Client.Default(null, null)); } } return this.delegate; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/LazyTracingFeignClient.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/LazyTracingFeignClient.java index f79c4894b..dd8ee9851 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/LazyTracingFeignClient.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/LazyTracingFeignClient.java @@ -18,7 +18,6 @@ package org.springframework.cloud.sleuth.instrument.web.client.feign; import java.io.IOException; -import brave.http.HttpTracing; import feign.Client; import feign.Request; import feign.Response; @@ -26,6 +25,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; /** * Lazilly resolves the Trace Feign Client. @@ -43,7 +44,9 @@ class LazyTracingFeignClient implements Client { private Client tracingFeignClient; - private HttpTracing httpTracing; + private CurrentTraceContext currentTraceContext; + + private HttpClientHandler httpClientHandler; LazyTracingFeignClient(BeanFactory beanFactory, Client delegate) { this.beanFactory = beanFactory; @@ -61,16 +64,24 @@ class LazyTracingFeignClient implements Client { private Client tracingFeignClient() { if (this.tracingFeignClient == null) { - this.tracingFeignClient = TracingFeignClient.create(httpTracing(), this.delegate); + this.tracingFeignClient = TracingFeignClient.create(currentTraceContext(), httpClientHandler(), + this.delegate); } return this.tracingFeignClient; } - private HttpTracing httpTracing() { - if (this.httpTracing == null) { - this.httpTracing = this.beanFactory.getBean(HttpTracing.class); + private CurrentTraceContext currentTraceContext() { + if (this.currentTraceContext == null) { + this.currentTraceContext = this.beanFactory.getBean(CurrentTraceContext.class); } - return this.httpTracing; + return this.currentTraceContext; + } + + private HttpClientHandler httpClientHandler() { + if (this.httpClientHandler == null) { + this.httpClientHandler = this.beanFactory.getBean(HttpClientHandler.class); + } + return this.httpClientHandler; } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignBlockingLoadBalancerClient.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignBlockingLoadBalancerClient.java index 44ddc6585..cef46883d 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignBlockingLoadBalancerClient.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignBlockingLoadBalancerClient.java @@ -18,9 +18,6 @@ package org.springframework.cloud.sleuth.instrument.web.client.feign; import java.io.IOException; -import brave.Span; -import brave.Tracer; -import brave.http.HttpTracing; import feign.Client; import feign.Request; import feign.Response; @@ -31,6 +28,10 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerProperties; import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; /** * A trace representation of {@link FeignBlockingLoadBalancerClient}. @@ -47,7 +48,9 @@ class TraceFeignBlockingLoadBalancerClient extends FeignBlockingLoadBalancerClie Tracer tracer; - HttpTracing httpTracing; + CurrentTraceContext currentTraceContext; + + HttpClientHandler httpClientHandler; TracingFeignClient tracingFeignClient; @@ -111,16 +114,24 @@ class TraceFeignBlockingLoadBalancerClient extends FeignBlockingLoadBalancerClie return tracer; } - private HttpTracing httpTracing() { - if (httpTracing == null) { - httpTracing = beanFactory.getBean(HttpTracing.class); + private CurrentTraceContext currentTraceContext() { + if (currentTraceContext == null) { + currentTraceContext = beanFactory.getBean(CurrentTraceContext.class); } - return httpTracing; + return currentTraceContext; + } + + private HttpClientHandler httpClientHandler() { + if (httpClientHandler == null) { + httpClientHandler = beanFactory.getBean(HttpClientHandler.class); + } + return httpClientHandler; } private TracingFeignClient tracingFeignClient() { if (tracingFeignClient == null) { - tracingFeignClient = (TracingFeignClient) TracingFeignClient.create(httpTracing(), getDelegate()); + tracingFeignClient = (TracingFeignClient) TracingFeignClient.create(currentTraceContext(), + httpClientHandler(), getDelegate()); } return tracingFeignClient; } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignClientAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignClientAutoConfiguration.java index 397458a93..9affcf734 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignClientAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignClientAutoConfiguration.java @@ -16,13 +16,11 @@ package org.springframework.cloud.sleuth.instrument.web.client.feign; -import brave.http.HttpTracing; import feign.Client; import feign.Feign; import feign.okhttp.OkHttpClient; import org.springframework.beans.factory.BeanFactory; -import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -30,7 +28,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.openfeign.FeignAutoConfiguration; import org.springframework.cloud.openfeign.FeignContext; -import org.springframework.cloud.sleuth.instrument.web.TraceHttpAutoConfiguration; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; @@ -45,10 +43,9 @@ import org.springframework.context.annotation.Scope; @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(value = "spring.sleuth.feign.enabled", matchIfMissing = true) @ConditionalOnClass({ Client.class, FeignContext.class }) -@ConditionalOnBean(HttpTracing.class) +@ConditionalOnBean(Tracer.class) @AutoConfigureBefore(FeignAutoConfiguration.class) -@AutoConfigureAfter(TraceHttpAutoConfiguration.class) -class TraceFeignClientAutoConfiguration { +public class TraceFeignClientAutoConfiguration { @Bean @ConditionalOnMissingBean diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignObjectWrapper.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignObjectWrapper.java index ffdab5dc9..41be08f84 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignObjectWrapper.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignObjectWrapper.java @@ -58,7 +58,7 @@ final class TraceFeignObjectWrapper { private Object loadBalancerClient; - private Object loadBalancerProperties; + private LoadBalancerProperties loadBalancerProperties; TraceFeignObjectWrapper(BeanFactory beanFactory) { this.beanFactory = beanFactory; @@ -80,8 +80,7 @@ final class TraceFeignObjectWrapper { FeignBlockingLoadBalancerClient client = ProxyUtils.getTargetObject(bean); return new TraceFeignBlockingLoadBalancerClient( (Client) new TraceFeignObjectWrapper(this.beanFactory).wrap(client.getDelegate()), - (LoadBalancerClient) loadBalancerClient(), this.beanFactory, - (LoadBalancerProperties) loadBalancerProperties()); + (LoadBalancerClient) loadBalancerClient(), this.beanFactory, loadBalancerProperties()); } else { FeignBlockingLoadBalancerClient client = ProxyUtils.getTargetObject(bean); @@ -94,7 +93,7 @@ final class TraceFeignObjectWrapper { log.warn(EXCEPTION_WARNING, e); } return new TraceFeignBlockingLoadBalancerClient(client, (LoadBalancerClient) loadBalancerClient(), - this.beanFactory, (LoadBalancerProperties) loadBalancerProperties()); + this.beanFactory, loadBalancerProperties()); } } @@ -105,7 +104,7 @@ final class TraceFeignObjectWrapper { return loadBalancerClient; } - private Object loadBalancerProperties() { + private LoadBalancerProperties loadBalancerProperties() { if (loadBalancerProperties == null) { loadBalancerProperties = beanFactory.getBean(LoadBalancerProperties.class); } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignClient.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignClient.java index 6e81a8370..8148e3b68 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignClient.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignClient.java @@ -24,19 +24,17 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; -import brave.Span; -import brave.http.HttpClientHandler; -import brave.http.HttpClientRequest; -import brave.http.HttpClientResponse; -import brave.http.HttpTracing; -import brave.propagation.CurrentTraceContext; -import brave.propagation.CurrentTraceContext.Scope; import feign.Client; import feign.Request; import feign.Response; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; +import org.springframework.cloud.sleuth.api.http.HttpClientRequest; +import org.springframework.cloud.sleuth.api.http.HttpClientResponse; import org.springframework.cloud.util.ProxyUtils; import org.springframework.lang.Nullable; @@ -54,18 +52,18 @@ final class TracingFeignClient implements Client { final Client delegate; - final HttpClientHandler handler; + final HttpClientHandler handler; - TracingFeignClient(HttpTracing httpTracing, Client delegate) { - this.currentTraceContext = httpTracing.tracing().currentTraceContext(); - this.handler = HttpClientHandler.create(httpTracing); + TracingFeignClient(CurrentTraceContext currentTraceContext, HttpClientHandler handler, Client delegate) { + this.currentTraceContext = currentTraceContext; + this.handler = handler; Client delegateTarget = ProxyUtils.getTargetObject(delegate); this.delegate = delegateTarget instanceof TracingFeignClient ? ((TracingFeignClient) delegateTarget).delegate : delegateTarget; } - static Client create(HttpTracing httpTracing, Client delegate) { - return new TracingFeignClient(httpTracing, delegate); + static Client create(CurrentTraceContext currentTraceContext, HttpClientHandler handler, Client delegate) { + return new TracingFeignClient(currentTraceContext, handler, delegate); } @Override @@ -77,7 +75,7 @@ final class TracingFeignClient implements Client { } Response res = null; Throwable error = null; - try (Scope ws = this.currentTraceContext.newScope(span.context())) { + try (CurrentTraceContext.Scope ws = this.currentTraceContext.newScope(span.context())) { res = this.delegate.execute(request.build(), options); if (res == null) { // possibly null on bad implementation or mocks res = Response.builder().request(req).build(); @@ -89,8 +87,8 @@ final class TracingFeignClient implements Client { throw e; } finally { - ResponseWrapper response = res != null ? new ResponseWrapper(request, res, error) : null; - this.handler.handleReceive(response, error, span); + ResponseWrapper response = new ResponseWrapper(request, res, error); + this.handler.handleReceive(response, span); if (log.isDebugEnabled()) { log.debug("Handled receive of " + span); @@ -100,12 +98,12 @@ final class TracingFeignClient implements Client { void handleSendAndReceive(Span span, Request req, @Nullable Response res, @Nullable Throwable error) { RequestWrapper request = new RequestWrapper(req); - this.handler.handleSend(request, span); + this.handler.handleSend(request, span.context()); ResponseWrapper response = res != null ? new ResponseWrapper(request, res, error) : null; - this.handler.handleReceive(response, error, span); + this.handler.handleReceive(response, span); } - static final class RequestWrapper extends HttpClientRequest { + static final class RequestWrapper implements HttpClientRequest { final Request delegate; @@ -122,7 +120,7 @@ final class TracingFeignClient implements Client { @Override public String method() { - return delegate.method(); + return delegate.httpMethod().name(); } @Override @@ -176,7 +174,7 @@ final class TracingFeignClient implements Client { } - static final class ResponseWrapper extends HttpClientResponse { + static final class ResponseWrapper implements HttpClientResponse { final RequestWrapper request; @@ -185,7 +183,7 @@ final class TracingFeignClient implements Client { @Nullable final Throwable error; - ResponseWrapper(RequestWrapper request, Response response, @Nullable Throwable error) { + ResponseWrapper(RequestWrapper request, @Nullable Response response, @Nullable Throwable error) { this.request = request; this.response = response; this.error = error; @@ -209,9 +207,24 @@ final class TracingFeignClient implements Client { @Override public int statusCode() { + if (response == null) { + return 0; + } return response.status(); } + @Override + public String header(String header) { + if (response == null) { + return null; + } + Collection strings = response.headers().get(header); + if (strings.isEmpty()) { + return null; + } + return strings.iterator().next(); + } + } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/HandlerParser.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/HandlerParser.java new file mode 100644 index 000000000..b1587f2bf --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/HandlerParser.java @@ -0,0 +1,79 @@ +/* + * Copyright 2013-2020 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.mvc; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.cloud.sleuth.api.SpanCustomizer; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; + +/** + * Spring MVC specific type used to customize traced requests based on the handler. + * + *

+ * Note: This should not duplicate data. For example, this should not add the tag + * "http.url". + * + *

+ * Tagging policy adopted from spring cloud sleuth 1.3.x + */ +public class HandlerParser { + + /** Adds no tags to the span representing the request. */ + public static final HandlerParser NOOP = new HandlerParser() { + @Override + protected void preHandle(HttpServletRequest request, Object handler, SpanCustomizer customizer) { + } + }; + + /** Simple class name that processed the request. ex BookController */ + public static final String CONTROLLER_CLASS = "mvc.controller.class"; + + /** Method name that processed the request. ex listOfBooks */ + public static final String CONTROLLER_METHOD = "mvc.controller.method"; + + /** + * Invoked prior to request invocation during + * {@link HandlerInterceptor#preHandle(HttpServletRequest, HttpServletResponse, Object)}. + * + *

+ * Adds the tags {@link #CONTROLLER_CLASS} and {@link #CONTROLLER_METHOD}. Override or + * use {@link #NOOP} to change this behavior. + * @param request request + * @param handler handler + * @param customizer span customizer + */ + protected void preHandle(HttpServletRequest request, Object handler, SpanCustomizer customizer) { + if (WebMvcRuntime.get().isHandlerMethod(handler)) { + HandlerMethod handlerMethod = ((HandlerMethod) handler); + customizer.tag(CONTROLLER_CLASS, handlerMethod.getBeanType().getSimpleName()); + customizer.tag(CONTROLLER_METHOD, handlerMethod.getMethod().getName()); + } + else { + customizer.tag(CONTROLLER_CLASS, handler.getClass().getSimpleName()); + } + } + + /* + * Intentionally public for @Autowired to work without explicit binding + */ + public HandlerParser() { + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/SpanCustomizingAsyncHandlerInterceptor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/SpanCustomizingAsyncHandlerInterceptor.java new file mode 100644 index 000000000..355dcc5d5 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/SpanCustomizingAsyncHandlerInterceptor.java @@ -0,0 +1,68 @@ +/* + * Copyright 2013-2020 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.mvc; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.sleuth.api.SpanCustomizer; +import org.springframework.cloud.sleuth.instrument.web.servlet.TracingFilter; +import org.springframework.web.servlet.AsyncHandlerInterceptor; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +import static org.springframework.cloud.sleuth.instrument.web.mvc.SpanCustomizingHandlerInterceptor.setErrorAttribute; +import static org.springframework.cloud.sleuth.instrument.web.mvc.SpanCustomizingHandlerInterceptor.setHttpRouteAttribute; + +/** + * Same as {@link SpanCustomizingHandlerInterceptor} except it can be used as both an + * {@link AsyncHandlerInterceptor} or a normal {@link HandlerInterceptor}. + */ +public final class SpanCustomizingAsyncHandlerInterceptor extends HandlerInterceptorAdapter { + + @Autowired(required = false) + HandlerParser handlerParser = new HandlerParser(); + + SpanCustomizingAsyncHandlerInterceptor() { // hide the ctor so we can change later if + // needed + } + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) { + Object span = request.getAttribute(SpanCustomizer.class.getName()); + if (span instanceof SpanCustomizer) { + handlerParser.preHandle(request, o, (SpanCustomizer) span); + } + return true; + } + + /** + * Sets the "error" and "http.route" attributes so that the {@link TracingFilter} can + * read them. + */ + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, + Exception ex) { + Object span = request.getAttribute(SpanCustomizer.class.getName()); + if (span instanceof SpanCustomizer) { + setErrorAttribute(request, ex); + setHttpRouteAttribute(request); + } + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/SpanCustomizingHandlerInterceptor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/SpanCustomizingHandlerInterceptor.java new file mode 100644 index 000000000..2ae085a86 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/SpanCustomizingHandlerInterceptor.java @@ -0,0 +1,99 @@ +/* + * Copyright 2013-2020 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.mvc; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.sleuth.api.SpanCustomizer; +import org.springframework.cloud.sleuth.instrument.web.servlet.TracingFilter; +import org.springframework.lang.Nullable; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +/** + * Adds application-tier data to an existing http span via {@link HandlerParser}. This + * also sets the request property "http.route" so that it can be used in naming the http + * span. + * + *

+ * Use this when you start traces at the servlet layer via {@link TracingFilter}. + */ +public final class SpanCustomizingHandlerInterceptor implements HandlerInterceptor { + + /** + * Redefined from HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE added in Spring 3. + */ + static final String BEST_MATCHING_PATTERN_ATTRIBUTE = "org.springframework.web.servlet.HandlerMapping.bestMatchingPattern"; + + @Autowired(required = false) + HandlerParser handlerParser = new HandlerParser(); + + SpanCustomizingHandlerInterceptor() { // hide the ctor so we can change later if + // needed + } + + /** + * Parses the request and sets the "http.route" attribute so that the + * {@link TracingFilter} can read it. + */ + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) { + Object span = request.getAttribute(SpanCustomizer.class.getName()); + if (span instanceof SpanCustomizer) { + setHttpRouteAttribute(request); + handlerParser.preHandle(request, o, (SpanCustomizer) span); + } + return true; + } + + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, + ModelAndView modelAndView) { + } + + /** Sets the "error" attribute so that the {@link TracingFilter} can read it. */ + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, + Exception ex) { + Object span = request.getAttribute(SpanCustomizer.class.getName()); + if (span instanceof SpanCustomizer) { + setErrorAttribute(request, ex); + } + } + + /** + * Sets the "error" attribute if not already set, so that the {@link TracingFilter} + * can read it. + */ + static void setErrorAttribute(HttpServletRequest request, @Nullable Exception ex) { + if (ex != null && request.getAttribute("error") == null) { + request.setAttribute("error", ex); + } + } + + /** + * Sets the "http.route" attribute from {@link #BEST_MATCHING_PATTERN_ATTRIBUTE} so + * that the {@link TracingFilter} can read it. + */ + static void setHttpRouteAttribute(HttpServletRequest request) { + Object httpRoute = request.getAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE); + request.setAttribute("http.route", httpRoute != null ? httpRoute.toString() : ""); + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/TraceContextListenableFuture.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/TraceContextListenableFuture.java new file mode 100644 index 000000000..93f50196c --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/TraceContextListenableFuture.java @@ -0,0 +1,187 @@ +/* + * Copyright 2013-2020 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.mvc; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.util.concurrent.FailureCallback; +import org.springframework.util.concurrent.ListenableFuture; +import org.springframework.util.concurrent.ListenableFutureCallback; +import org.springframework.util.concurrent.SuccessCallback; + +/** + * Ensures callbacks run in the invocation trace context. + * + *

+ * Note: {@link #completable()} is not instrumented to propagate the invocation trace + * context. + */ +final class TraceContextListenableFuture implements ListenableFuture { + + final ListenableFuture delegate; + + final CurrentTraceContext currentTraceContext; + + final TraceContext invocationContext; + + TraceContextListenableFuture(ListenableFuture delegate, CurrentTraceContext currentTraceContext, + TraceContext invocationContext) { + this.delegate = delegate; + this.currentTraceContext = currentTraceContext; + this.invocationContext = invocationContext; + } + + @Override + public void addCallback(ListenableFutureCallback callback) { + delegate.addCallback(callback != null ? new TraceContextListenableFutureCallback<>(callback, this) : null); + } + + // Do not use @Override annotation to avoid compatibility issue version < 4.1 + public void addCallback(SuccessCallback successCallback, FailureCallback failureCallback) { + delegate.addCallback(successCallback != null ? new TraceContextSuccessCallback<>(successCallback, this) : null, + failureCallback != null ? new TraceContextFailureCallback(failureCallback, this) : null); + } + + // Do not use @Override annotation to avoid compatibility issue version < 5.0 + // Only called when in JRE 1.8+ + public CompletableFuture completable() { + return delegate.completable(); // NOTE: trace context is not propagated + } + + // Methods from java.util.concurrent.Future + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return delegate.cancel(mayInterruptIfRunning); + } + + @Override + public boolean isCancelled() { + return delegate.isCancelled(); + } + + @Override + public boolean isDone() { + return delegate.isDone(); + } + + @Override + public T get() throws InterruptedException, ExecutionException { + return delegate.get(); + } + + @Override + public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException { + return delegate.get(); + } + + static final class TraceContextListenableFutureCallback implements ListenableFutureCallback { + + final ListenableFutureCallback delegate; + + final CurrentTraceContext currentTraceContext; + + final TraceContext invocationContext; + + TraceContextListenableFutureCallback(ListenableFutureCallback delegate, + TraceContextListenableFuture future) { + this.delegate = delegate; + this.currentTraceContext = future.currentTraceContext; + this.invocationContext = future.invocationContext; + } + + @Override + public void onSuccess(T result) { + try (CurrentTraceContext.Scope scope = currentTraceContext.maybeScope(invocationContext)) { + delegate.onSuccess(result); + } + } + + @Override + public void onFailure(Throwable ex) { + try (CurrentTraceContext.Scope scope = currentTraceContext.maybeScope(invocationContext)) { + delegate.onFailure(ex); + } + } + + @Override + public String toString() { + return delegate.toString(); + } + + } + + static final class TraceContextSuccessCallback implements SuccessCallback { + + final SuccessCallback delegate; + + final CurrentTraceContext currentTraceContext; + + final TraceContext invocationContext; + + TraceContextSuccessCallback(SuccessCallback delegate, TraceContextListenableFuture future) { + this.delegate = delegate; + this.currentTraceContext = future.currentTraceContext; + this.invocationContext = future.invocationContext; + } + + @Override + public void onSuccess(T result) { + try (CurrentTraceContext.Scope scope = currentTraceContext.maybeScope(invocationContext)) { + delegate.onSuccess(result); + } + } + + @Override + public String toString() { + return delegate.toString(); + } + + } + + static final class TraceContextFailureCallback implements FailureCallback { + + final FailureCallback delegate; + + final CurrentTraceContext currentTraceContext; + + final TraceContext invocationContext; + + TraceContextFailureCallback(FailureCallback delegate, TraceContextListenableFuture future) { + this.delegate = delegate; + this.currentTraceContext = future.currentTraceContext; + this.invocationContext = future.invocationContext; + } + + @Override + public void onFailure(Throwable ex) { + try (CurrentTraceContext.Scope scope = currentTraceContext.maybeScope(invocationContext)) { + delegate.onFailure(ex); + } + } + + @Override + public String toString() { + return delegate.toString(); + } + + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/TracingAsyncClientHttpRequestInterceptor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/TracingAsyncClientHttpRequestInterceptor.java new file mode 100644 index 000000000..aa1ce7276 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/TracingAsyncClientHttpRequestInterceptor.java @@ -0,0 +1,101 @@ +/* + * Copyright 2013-2020 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.mvc; + +import java.io.IOException; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; +import org.springframework.cloud.sleuth.instrument.web.mvc.TracingClientHttpRequestInterceptor.ClientHttpResponseWrapper; +import org.springframework.cloud.sleuth.instrument.web.mvc.TracingClientHttpRequestInterceptor.HttpRequestWrapper; +import org.springframework.http.HttpRequest; +import org.springframework.http.client.AsyncClientHttpRequestExecution; +import org.springframework.http.client.AsyncClientHttpRequestInterceptor; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.util.concurrent.ListenableFuture; +import org.springframework.util.concurrent.ListenableFutureCallback; + +public final class TracingAsyncClientHttpRequestInterceptor implements AsyncClientHttpRequestInterceptor { + + public static AsyncClientHttpRequestInterceptor create(CurrentTraceContext currentTraceContext, + HttpClientHandler httpClientHandler) { + return new TracingAsyncClientHttpRequestInterceptor(currentTraceContext, httpClientHandler); + } + + final CurrentTraceContext currentTraceContext; + + final HttpClientHandler handler; + + @Autowired + TracingAsyncClientHttpRequestInterceptor(CurrentTraceContext currentTraceContext, + HttpClientHandler httpClientHandler) { + this.currentTraceContext = currentTraceContext; + this.handler = httpClientHandler; + } + + @Override + public ListenableFuture intercept(HttpRequest req, byte[] body, + AsyncClientHttpRequestExecution execution) throws IOException { + HttpRequestWrapper request = new HttpRequestWrapper(req); + Span span = handler.handleSend(request); + + // avoid context sync overhead when we are the root span + String parentId = span.context().parentId(); + TraceContext invocationContext = parentId != null ? currentTraceContext.get() : null; + + try (CurrentTraceContext.Scope ws = currentTraceContext.maybeScope(span.context())) { + ListenableFuture result = execution.executeAsync(req, body); + result.addCallback(new TraceListenableFutureCallback(request, span, handler)); + return invocationContext != null + ? new TraceContextListenableFuture<>(result, currentTraceContext, invocationContext) : result; + } + catch (Throwable e) { + handler.handleReceive(new ClientHttpResponseWrapper(request, null, e), span); + throw e; + } + } + + static final class TraceListenableFutureCallback implements ListenableFutureCallback { + + final HttpRequestWrapper request; + + final Span span; + + final HttpClientHandler handler; + + TraceListenableFutureCallback(HttpRequestWrapper request, Span span, HttpClientHandler handler) { + this.request = request; + this.span = span; + this.handler = handler; + } + + @Override + public void onFailure(Throwable ex) { + handler.handleReceive(new ClientHttpResponseWrapper(request, null, ex), span); + } + + @Override + public void onSuccess(ClientHttpResponse response) { + handler.handleReceive(new ClientHttpResponseWrapper(request, response, null), span); + } + + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/TracingClientHttpRequestInterceptor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/TracingClientHttpRequestInterceptor.java new file mode 100644 index 000000000..903696073 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/TracingClientHttpRequestInterceptor.java @@ -0,0 +1,168 @@ +/* + * Copyright 2013-2020 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.mvc; + +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; +import org.springframework.cloud.sleuth.api.http.HttpClientRequest; +import org.springframework.cloud.sleuth.api.http.HttpClientResponse; +import org.springframework.http.HttpRequest; +import org.springframework.http.client.ClientHttpRequestExecution; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.lang.Nullable; +import org.springframework.web.client.HttpStatusCodeException; + +public final class TracingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { + + private static final Log log = LogFactory.getLog(TracingClientHttpRequestInterceptor.class); + + public static ClientHttpRequestInterceptor create(CurrentTraceContext currentTraceContext, + HttpClientHandler httpClientHandler) { + return new TracingClientHttpRequestInterceptor(currentTraceContext, httpClientHandler); + } + + final CurrentTraceContext currentTraceContext; + + final HttpClientHandler handler; + + @Autowired + TracingClientHttpRequestInterceptor(CurrentTraceContext currentTraceContext, HttpClientHandler httpClientHandler) { + this.currentTraceContext = currentTraceContext; + this.handler = httpClientHandler; + } + + @Override + public ClientHttpResponse intercept(HttpRequest req, byte[] body, ClientHttpRequestExecution execution) + throws IOException { + HttpRequestWrapper request = new HttpRequestWrapper(req); + Span span = handler.handleSend(request); + if (log.isDebugEnabled()) { + log.debug("Wrapping an outbound http call with span [" + span + "]"); + } + ClientHttpResponse response = null; + Throwable error = null; + try (CurrentTraceContext.Scope ws = currentTraceContext.newScope(span.context())) { + response = execution.execute(req, body); + return response; + } + catch (Throwable e) { + error = e; + throw e; + } + finally { + handler.handleReceive(new ClientHttpResponseWrapper(request, response, error), span); + } + } + + static final class HttpRequestWrapper implements HttpClientRequest { + + final HttpRequest delegate; + + HttpRequestWrapper(HttpRequest delegate) { + this.delegate = delegate; + } + + @Override + public Object unwrap() { + return delegate; + } + + @Override + public String method() { + return delegate.getMethod().name(); + } + + @Override + public String path() { + return delegate.getURI().getPath(); + } + + @Override + public String url() { + return delegate.getURI().toString(); + } + + @Override + public String header(String name) { + Object result = delegate.getHeaders().getFirst(name); + return result != null ? result.toString() : null; + } + + @Override + public void header(String name, String value) { + delegate.getHeaders().set(name, value); + } + + } + + static final class ClientHttpResponseWrapper implements HttpClientResponse { + + final HttpRequestWrapper request; + + @Nullable + final ClientHttpResponse response; + + @Nullable + final Throwable error; + + ClientHttpResponseWrapper(HttpRequestWrapper request, @Nullable ClientHttpResponse response, + @Nullable Throwable error) { + this.request = request; + this.response = response; + this.error = error; + } + + @Override + public Object unwrap() { + return response; + } + + @Override + public HttpRequestWrapper request() { + return request; + } + + @Override + public Throwable error() { + return error; + } + + @Override + public int statusCode() { + try { + int result = response != null ? response.getRawStatusCode() : 0; + if (result <= 0 && error instanceof HttpStatusCodeException) { + result = ((HttpStatusCodeException) error).getRawStatusCode(); + } + return result; + } + catch (Exception e) { + return 0; + } + } + + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/WebMvcRuntime.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/WebMvcRuntime.java new file mode 100644 index 000000000..68eb5d0e0 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/WebMvcRuntime.java @@ -0,0 +1,82 @@ +/* + * Copyright 2013-2020 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.mvc; + +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.http.HttpServerHandler; +import org.springframework.context.ApplicationContext; +import org.springframework.web.method.HandlerMethod; + +/** + * Access to Spring WebMvc version-specific features. + * + *

+ * Originally designed by OkHttp team, derived from + * {@code okhttp3.internal.platform.Platform} + */ +abstract class WebMvcRuntime { + + private static final WebMvcRuntime WEBMVC_RUNTIME = findWebMvcRuntime(); + + abstract CurrentTraceContext currentTraceContext(ApplicationContext ctx); + + abstract HttpServerHandler httpServerHandler(ApplicationContext ctx); + + abstract boolean isHandlerMethod(Object handler); + + WebMvcRuntime() { + } + + static WebMvcRuntime get() { + return WEBMVC_RUNTIME; + } + + /** Attempt to match the host runtime to a capable Platform implementation. */ + static WebMvcRuntime findWebMvcRuntime() { + // Find spring-webmvc v3.1 new methods + try { + Class.forName("org.springframework.web.method.HandlerMethod"); + return new WebMvc31(); // intentionally doesn't not access the type prior to + // the above guard + } + catch (ClassNotFoundException e) { + // pre spring-webmvc v3.1 + } + + throw new UnsupportedOperationException("Pre Spring Web 3.1 not supported"); + } + + static final class WebMvc31 extends WebMvcRuntime { + + @Override + CurrentTraceContext currentTraceContext(ApplicationContext ctx) { + return ctx.getBean(CurrentTraceContext.class); + } + + @Override + HttpServerHandler httpServerHandler(ApplicationContext ctx) { + return ctx.getBean(HttpServerHandler.class); + } + + @Override + boolean isHandlerMethod(Object handler) { + return handler instanceof HandlerMethod; + } + + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/package-info.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/package-info.java new file mode 100644 index 000000000..5a386ca08 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/mvc/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright 2013-2020 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. + */ + +/** + * This module is fully adopted from io.zipkin.brave-instrumentation-web and + * io.zipkin.brave-instrumentation-webmvc JAR. + */ +package org.springframework.cloud.sleuth.instrument.web.mvc; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/servlet/HttpServletRequestWrapper.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/servlet/HttpServletRequestWrapper.java new file mode 100644 index 000000000..305095746 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/servlet/HttpServletRequestWrapper.java @@ -0,0 +1,98 @@ +/* + * Copyright 2013-2020 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.servlet; + +import javax.servlet.RequestDispatcher; +import javax.servlet.http.HttpServletRequest; + +import org.springframework.cloud.sleuth.api.http.HttpServerRequest; +import org.springframework.lang.Nullable; + +/** + * Besides delegating to {@link HttpServletRequest} methods, this also parses the remote + * IP of the client. + * + * @since 5.10 + */ +// Public for use in sparkjava or other frameworks that re-use servlet types +class HttpServletRequestWrapper implements HttpServerRequest { + + /** @since 5.10 */ + public static HttpServerRequest create(HttpServletRequest request) { + return new HttpServletRequestWrapper(request); + } + + HttpServletRequest delegate; + + HttpServletRequestWrapper(HttpServletRequest delegate) { + if (delegate == null) { + throw new NullPointerException("delegate == null"); + } + this.delegate = delegate; + } + + @Override + public Object unwrap() { + return delegate; + } + + @Override + public String method() { + return delegate.getMethod(); + } + + @Override + public String route() { + Object maybeRoute = delegate.getAttribute("http.route"); + return maybeRoute instanceof String ? (String) maybeRoute : null; + } + + @Override + public String path() { + return delegate.getRequestURI(); + } + + // not as some implementations may be able to do this more efficiently + @Override + public String url() { + StringBuffer url = delegate.getRequestURL(); + if (delegate.getQueryString() != null && !delegate.getQueryString().isEmpty()) { + url.append('?').append(delegate.getQueryString()); + } + return url.toString(); + } + + @Override + public String header(String name) { + return delegate.getHeader(name); + } + + /** Looks for a valid request attribute "error". */ + @Nullable + Throwable maybeError() { + Object maybeError = delegate.getAttribute("error"); + if (maybeError instanceof Throwable) { + return (Throwable) maybeError; + } + maybeError = delegate.getAttribute(RequestDispatcher.ERROR_EXCEPTION); + if (maybeError instanceof Throwable) { + return (Throwable) maybeError; + } + return null; + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/servlet/HttpServletResponseWrapper.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/servlet/HttpServletResponseWrapper.java new file mode 100644 index 000000000..c7fcd62e2 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/servlet/HttpServletResponseWrapper.java @@ -0,0 +1,102 @@ +/* + * Copyright 2013-2020 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.servlet; + +import javax.servlet.UnavailableException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.cloud.sleuth.api.http.HttpServerResponse; +import org.springframework.lang.Nullable; + +/** + * This delegates to {@link HttpServletResponse} methods, taking care to portably handle + * {@link #statusCode()}. + * + * @since 5.10 + */ +// Public for use in sparkjava or other frameworks that re-use servlet types +class HttpServletResponseWrapper implements HttpServerResponse { + + // not final for inner + // subtype + /** + * @param caught an exception caught serving the request. + * @since 5.10 + */ + public static HttpServerResponse create(@Nullable HttpServletRequest request, HttpServletResponse response, + @Nullable Throwable caught) { + return new HttpServletResponseWrapper(request, response, caught); + } + + @Nullable + final HttpServletRequestWrapper request; + + final HttpServletResponse response; + + @Nullable + final Throwable caught; + + HttpServletResponseWrapper(@Nullable HttpServletRequest request, HttpServletResponse response, + @Nullable Throwable caught) { + if (response == null) { + throw new NullPointerException("response == null"); + } + this.request = request != null ? new HttpServletRequestWrapper(request) : null; + this.response = response; + this.caught = caught; + } + + @Override + public final Object unwrap() { + return response; + } + + @Override + @Nullable + public HttpServletRequestWrapper request() { + return request; + } + + @Override + public Throwable error() { + if (caught != null) { + return caught; + } + if (request == null) { + return null; + } + return request.maybeError(); + } + + @Override + public int statusCode() { + int result = ServletRuntime.get().status(response); + if (caught != null && result == 200) { // We may have a potentially bad status due + // to defaults + // Servlet only seems to define one exception that has a built-in code. Logic + // in Jetty + // defaults the status to 500 otherwise. + if (caught instanceof UnavailableException) { + return ((UnavailableException) caught).isPermanent() ? 404 : 503; + } + return 500; + } + return result; + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/servlet/ServletRuntime.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/servlet/ServletRuntime.java new file mode 100644 index 000000000..e157aa6af --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/servlet/ServletRuntime.java @@ -0,0 +1,213 @@ +/* + * Copyright 2013-2020 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.servlet; + +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.servlet.AsyncContext; +import javax.servlet.AsyncEvent; +import javax.servlet.AsyncListener; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.http.HttpServerHandler; +import org.springframework.cloud.sleuth.api.http.HttpServerResponse; + +/** + * Access to servlet version-specific features. + * + *

+ * Originally designed by OkHttp team, derived from + * {@code okhttp3.internal.platform.Platform} + */ +abstract class ServletRuntime { + + private static final ServletRuntime SERVLET_RUNTIME = findServletRuntime(); + + public HttpServletResponse httpServletResponse(ServletResponse response) { + return (HttpServletResponse) response; + } + + /** + * public for + * {@link org.springframework.cloud.sleuth.instrument.web.servlet.HttpServletResponseWrapper}. + */ + public abstract int status(HttpServletResponse response); + + public abstract boolean isAsync(HttpServletRequest request); + + public abstract void handleAsync(HttpServerHandler handler, HttpServletRequest request, + HttpServletResponse response, Span span); + + ServletRuntime() { + } + + public static ServletRuntime get() { + return SERVLET_RUNTIME; + } + + /** Attempt to match the host runtime to a capable Platform implementation. */ + private static ServletRuntime findServletRuntime() { + // Find Servlet v3 new methods + try { + Class.forName("javax.servlet.AsyncEvent"); + HttpServletRequest.class.getMethod("isAsyncStarted"); + return new Servlet3(); // intentionally doesn't not access the type prior to + // the above guard + } + catch (NoSuchMethodException e) { + // pre Servlet v3 + } + catch (ClassNotFoundException e) { + // pre Servlet v3 + } + + throw new UnsupportedOperationException("Unsupported Servlet type"); + } + + // Taken from RxJava throwIfFatal, which was taken from scala + public static void propagateIfFatal(Throwable t) { + if (t instanceof VirtualMachineError) { + throw (VirtualMachineError) t; + } + else if (t instanceof ThreadDeath) { + throw (ThreadDeath) t; + } + else if (t instanceof LinkageError) { + throw (LinkageError) t; + } + } + + static final class Servlet3 extends ServletRuntime { + + @Override + public boolean isAsync(HttpServletRequest request) { + return request.isAsyncStarted(); + } + + @Override + public int status(HttpServletResponse response) { + return response.getStatus(); + } + + @Override + public void handleAsync(HttpServerHandler handler, HttpServletRequest request, HttpServletResponse response, + Span span) { + if (span.isNoop()) { + return; // don't add overhead when we aren't httpTracing + } + TracingAsyncListener listener = new TracingAsyncListener(handler, span); + request.getAsyncContext().addListener(listener, request, response); + } + + static final class TracingAsyncListener implements AsyncListener { + + final HttpServerHandler handler; + + final Span span; + + TracingAsyncListener(HttpServerHandler handler, Span span) { + this.handler = handler; + this.span = span; + } + + @Override + public void onComplete(AsyncEvent e) { + HttpServletRequest req = (HttpServletRequest) e.getSuppliedRequest(); + // Use package-private attribute to check if this hook was called + // redundantly + Object sendHandled = req.getAttribute( + "org.springframework.cloud.sleuth.instrument.web.servlet.TracingFilter$SendHandled"); + if (sendHandled instanceof AtomicBoolean && ((AtomicBoolean) sendHandled).compareAndSet(false, true)) { + HttpServletResponse res = (HttpServletResponse) e.getSuppliedResponse(); + + HttpServerResponse response = HttpServletResponseWrapper.create(req, res, e.getThrowable()); + handler.handleSend(response, span); + } + else { + // TODO: None of our tests reach this condition. Make a concrete case + // that re-enters the + // onComplete hook or remove the special case + } + } + + // Per Servlet 3 section 2.3.3.3, we can't see the final HTTP status, yet. + // defer to onComplete + // https://download.oracle.com/otndocs/jcp/servlet-3.0-mrel-eval-oth-JSpec/ + @Override + public void onTimeout(AsyncEvent e) { + // Propagate the timeout so that the onComplete hook can see it. + ServletRequest request = e.getSuppliedRequest(); + if (request.getAttribute("error") == null) { + request.setAttribute("error", new AsyncTimeoutException(e)); + } + } + + // Per Servlet 3 section 2.3.3.3, we can't see the final HTTP status, yet. + // defer to onComplete + // https://download.oracle.com/otndocs/jcp/servlet-3.0-mrel-eval-oth-JSpec/ + @Override + public void onError(AsyncEvent e) { + ServletRequest request = e.getSuppliedRequest(); + if (request.getAttribute("error") == null) { + request.setAttribute("error", e.getThrowable()); + } + } + + /** + * If another async is created (ex via asyncContext.dispatch), this needs to + * be re-attached. + */ + @Override + public void onStartAsync(AsyncEvent e) { + AsyncContext eventAsyncContext = e.getAsyncContext(); + if (eventAsyncContext != null) { + eventAsyncContext.addListener(this, e.getSuppliedRequest(), e.getSuppliedResponse()); + } + } + + @Override + public String toString() { + return "TracingAsyncListener{" + span + "}"; + } + + } + + /** + * Async timeout exception. + */ + static final class AsyncTimeoutException extends TimeoutException { + + AsyncTimeoutException(AsyncEvent e) { + super("Timed out after " + e.getAsyncContext().getTimeout() + "ms"); + } + + @Override + public Throwable fillInStackTrace() { + return this; // stack trace doesn't add value as this is used in a + // callback + } + + } + + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/servlet/TracingFilter.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/servlet/TracingFilter.java new file mode 100644 index 000000000..25002f775 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/servlet/TracingFilter.java @@ -0,0 +1,125 @@ +/* + * Copyright 2013-2020 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.servlet; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.SpanCustomizer; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.http.HttpServerHandler; +import org.springframework.cloud.sleuth.api.http.HttpServerResponse; + +public final class TracingFilter implements Filter { + + final ServletRuntime servlet = ServletRuntime.get(); + + final CurrentTraceContext currentTraceContext; + + final HttpServerHandler handler; + + public static TracingFilter create(CurrentTraceContext currentTraceContext, HttpServerHandler httpServerHandler) { + return new TracingFilter(currentTraceContext, httpServerHandler); + } + + TracingFilter(CurrentTraceContext currentTraceContext, HttpServerHandler httpServerHandler) { + this.currentTraceContext = currentTraceContext; + this.handler = httpServerHandler; + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse res = servlet.httpServletResponse(response); + + // Prevent duplicate spans for the same request + TraceContext context = (TraceContext) request.getAttribute(TraceContext.class.getName()); + if (context != null) { + // A forwarded request might end up on another thread, so make sure it is + // scoped + CurrentTraceContext.Scope scope = currentTraceContext.maybeScope(context); + try { + chain.doFilter(request, response); + } + finally { + scope.close(); + } + return; + } + + Span span = handler.handleReceive(new HttpServletRequestWrapper(req)); + + // Add attributes for explicit access to customization or span context + request.setAttribute(SpanCustomizer.class.getName(), span); + request.setAttribute(TraceContext.class.getName(), span.context()); + SendHandled sendHandled = new SendHandled(); + request.setAttribute(SendHandled.class.getName(), sendHandled); + + Throwable error = null; + CurrentTraceContext.Scope scope = currentTraceContext.newScope(span.context()); + try { + // any downstream code can see Tracer.currentSpan() or use + // Tracer.currentSpanCustomizer() + chain.doFilter(req, res); + } + catch (Throwable e) { + error = e; + throw e; + } + finally { + // When async, even if we caught an exception, we don't have the final + // response: defer + if (servlet.isAsync(req)) { + servlet.handleAsync(handler, req, res, span); + } + else if (sendHandled.compareAndSet(false, true)) { + // we have a synchronous response or error: finish the span + HttpServerResponse responseWrapper = HttpServletResponseWrapper.create(req, res, error); + handler.handleSend(responseWrapper, span); + } + scope.close(); + } + } + + @Override + public void destroy() { + } + + @Override + public void init(FilterConfig filterConfig) { + } + + /** + * Special type used to ensure handleSend is only called once. + */ + static final class SendHandled extends AtomicBoolean { + + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/servlet/package-info.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/servlet/package-info.java new file mode 100644 index 000000000..4d7afb534 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/servlet/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2013-2020 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. + */ + +/** + * This module is fully adopted from io.zipkin.brave-instrumentation-servlet JAR. + */ +package org.springframework.cloud.sleuth.instrument.web.servlet; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/internal/SpanNameUtil.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/internal/SpanNameUtil.java index 9b07b3751..0a567ad4e 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/internal/SpanNameUtil.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/internal/SpanNameUtil.java @@ -36,7 +36,7 @@ public final class SpanNameUtil { if (StringUtils.isEmpty(name)) { return name; } - int maxLength = name.length() > MAX_NAME_LENGTH ? (MAX_NAME_LENGTH) : (name.length()); + int maxLength = Math.min(name.length(), MAX_NAME_LENGTH); return name.substring(0, maxLength); } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/opentracing/SleuthOpentracingProperties.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/opentracing/SleuthOpentracingProperties.java similarity index 90% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/opentracing/SleuthOpentracingProperties.java rename to spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/opentracing/SleuthOpentracingProperties.java index 3b4d68e83..b6729ba67 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/opentracing/SleuthOpentracingProperties.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/opentracing/SleuthOpentracingProperties.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.opentracing; +package org.springframework.cloud.sleuth.opentracing; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -25,7 +25,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; * @since 2.0.0 */ @ConfigurationProperties("spring.sleuth.opentracing") -class SleuthOpentracingProperties { +public class SleuthOpentracingProperties { private boolean enabled = true; 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 19451fb5e..b41d4c94a 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 @@ -3,32 +3,26 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.cloud.sleuth.annotation.SleuthAnnotationAutoConfiguration,\ org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.web.TraceHttpAutoConfiguration,\ -org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration,\ -org.springframework.cloud.sleuth.instrument.web.client.TraceWebClientAutoConfiguration,\ -org.springframework.cloud.sleuth.instrument.web.client.TraceWebAsyncClientAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.async.AsyncAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.async.AsyncCustomAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.async.AsyncDefaultAutoConfiguration,\ -org.springframework.cloud.sleuth.instrument.scheduling.TraceSchedulingAutoConfiguration,\ -org.springframework.cloud.sleuth.instrument.web.client.feign.TraceFeignClientAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.circuitbreaker.SleuthCircuitBreakerAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.rxjava.RxJavaAutoConfiguration,\ -org.springframework.cloud.sleuth.instrument.reactor.TraceReactorAutoConfiguration,\ +org.springframework.cloud.sleuth.instrument.quartz.TraceQuartzAutoConfiguration,\ +org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.web.TraceWebFluxAutoConfiguration,\ -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,\ +org.springframework.cloud.sleuth.instrument.web.client.TraceWebClientAutoConfiguration,\ +org.springframework.cloud.sleuth.instrument.web.client.TraceWebAsyncClientAutoConfiguration,\ +org.springframework.cloud.sleuth.instrument.web.client.feign.TraceFeignClientAutoConfiguration,\ +org.springframework.cloud.sleuth.instrument.web.SkipPatternConfiguration,\ +org.springframework.cloud.sleuth.instrument.scheduling.TraceSchedulingAutoConfiguration,\ +org.springframework.cloud.sleuth.instrument.reactor.TraceReactorAutoConfiguration,\ +org.springframework.cloud.sleuth.instrument.messaging.TraceFunctionAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.messaging.TraceSpringIntegrationAutoConfiguration,\ org.springframework.cloud.sleuth.instrument.messaging.TraceSpringMessagingAutoConfiguration,\ -org.springframework.cloud.sleuth.instrument.messaging.TraceWebSocketAutoConfiguration,\ -org.springframework.cloud.sleuth.instrument.messaging.TraceFunctionAutoConfiguration,\ -org.springframework.cloud.sleuth.instrument.opentracing.OpentracingAutoConfiguration,\ -org.springframework.cloud.sleuth.instrument.redis.TraceRedisAutoConfiguration,\ -org.springframework.cloud.sleuth.instrument.quartz.TraceQuartzAutoConfiguration,\ -org.springframework.cloud.sleuth.instrument.mongodb.TraceMongoDbAutoConfiguration +org.springframework.cloud.sleuth.instrument.messaging.TraceWebSocketAutoConfiguration # Environment Post Processor org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.cloud.sleuth.autoconfig.TraceEnvironmentPostProcessor,\ -org.springframework.cloud.sleuth.instrument.web.client.TraceGatewayEnvironmentPostProcessor +org.springframework.cloud.sleuth.instrument.web.client.TraceGatewayEnvironmentPostProcessor \ No newline at end of file diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthNewSpanParserAnnotationNoSleuthTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthNewSpanParserAnnotationNoSleuthTests.java index 471fc919b..221d45ff8 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthNewSpanParserAnnotationNoSleuthTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthNewSpanParserAnnotationNoSleuthTests.java @@ -16,11 +16,12 @@ package org.springframework.cloud.sleuth.annotation; -import brave.Tracing; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.api.Tracer; import static org.assertj.core.api.Assertions.assertThat; @@ -31,12 +32,12 @@ public class SleuthNewSpanParserAnnotationNoSleuthTests { NewSpanParser newSpanParser; @Autowired(required = false) - Tracing tracing; + Tracer tracer; @Test public void shouldNotAutowireBecauseConfigIsDisabled() { assertThat(this.newSpanParser).isNull(); - assertThat(this.tracing).isNull(); + Assertions.assertThat(this.tracer).isNull(); } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/SpanIgnoringSpanFilterTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/SpanIgnoringSpanFilterTests.java new file mode 100644 index 000000000..26a8138ea --- /dev/null +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/SpanIgnoringSpanFilterTests.java @@ -0,0 +1,106 @@ +/* + * Copyright 2013-2020 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.autoconfig; + +import java.util.Collections; + +import org.assertj.core.api.BDDAssertions; +import org.junit.jupiter.api.Test; +import org.mockito.BDDMockito; + +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; + +import static org.assertj.core.api.BDDAssertions.then; + +class SpanIgnoringSpanFilterTests { + + private FinishedSpan namedSpan() { + FinishedSpan span = BDDMockito.mock(FinishedSpan.class); + BDDMockito.given(span.name()).willReturn("someName"); + return span; + } + + @Test + void should_not_handle_span_when_present_in_main_list_of_spans_to_skip() { + SleuthSpanFilterProperties SleuthSpanFilterProperties = new SleuthSpanFilterProperties(); + SleuthSpanFilterProperties.setSpanNamePatternsToSkip(Collections.singletonList("someName")); + SpanIgnoringSpanFilter handler = new SpanIgnoringSpanFilter(SleuthSpanFilterProperties); + + then(handler.isExportable(namedSpan())).isFalse(); + } + + @Test + void should_not_handle_span_when_present_in_additional_list_of_spans_to_skip() { + SleuthSpanFilterProperties SleuthSpanFilterProperties = SleuthSpanExporterPropertiesWithAdditionalEntries(); + SpanIgnoringSpanFilter handler = new SpanIgnoringSpanFilter(SleuthSpanFilterProperties); + + then(handler.isExportable(namedSpan())).isFalse(); + } + + @Test + void should_use_cached_entry_for_same_patterns() { + export(handler(SleuthSpanExporterPropertiesWithAdditionalEntries("someOtherName"))); + export(handler(SleuthSpanExporterPropertiesWithAdditionalEntries("someOtherName"))); + export(handler(SleuthSpanExporterPropertiesWithAdditionalEntries("someOtherName"))); + + then(SpanIgnoringSpanFilter.cache).containsKey("someOtherName"); + + export(handler(SleuthSpanExporterPropertiesWithAdditionalEntries("a"))); + export(handler(SleuthSpanExporterPropertiesWithAdditionalEntries("b"))); + export(handler(SleuthSpanExporterPropertiesWithAdditionalEntries("c"))); + + then(SpanIgnoringSpanFilter.cache).containsKey("someOtherName").containsKey("a").containsKey("b") + .containsKey("c"); + } + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(TraceAutoConfiguration.class)); + + @Test + void should_not_register_span_handler_when_property_passed() { + this.contextRunner.withPropertyValues("spring.sleuth.span-filter.enabled=false") + .run((context) -> BDDAssertions.thenThrownBy(() -> context.getBean(SpanIgnoringSpanFilter.class)) + .isInstanceOf(NoSuchBeanDefinitionException.class)); + } + + @Test + void should_register_span_handler_by_default() { + this.contextRunner.run((context) -> context.getBean(SpanIgnoringSpanFilter.class)); + } + + private SleuthSpanFilterProperties SleuthSpanExporterPropertiesWithAdditionalEntries() { + return SleuthSpanExporterPropertiesWithAdditionalEntries("someName"); + } + + private SleuthSpanFilterProperties SleuthSpanExporterPropertiesWithAdditionalEntries(String name) { + SleuthSpanFilterProperties SleuthSpanFilterProperties = new SleuthSpanFilterProperties(); + SleuthSpanFilterProperties.setAdditionalSpanNamePatternsToIgnore(Collections.singletonList(name)); + return SleuthSpanFilterProperties; + } + + private void export(SpanIgnoringSpanFilter handler) { + handler.isExportable(namedSpan()); + } + + private SpanIgnoringSpanFilter handler(SleuthSpanFilterProperties SleuthSpanFilterProperties) { + return new SpanIgnoringSpanFilter(SleuthSpanFilterProperties); + } + +} diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/SpanIgnoringSpanHandlerTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/SpanIgnoringSpanHandlerTests.java deleted file mode 100644 index 4e19d9450..000000000 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/SpanIgnoringSpanHandlerTests.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2013-2020 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.autoconfig; - -import java.util.Collections; - -import brave.handler.MutableSpan; -import brave.handler.SpanHandler; -import org.assertj.core.api.BDDAssertions; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.test.context.runner.ApplicationContextRunner; - -import static org.assertj.core.api.BDDAssertions.then; - -class SpanIgnoringSpanHandlerTests { - - @Test - void should_handle_span_when_not_yet_finished() { - SpanIgnoringSpanHandler handler = new SpanIgnoringSpanHandler(new SleuthProperties()); - - then(handler.end(null, null, SpanHandler.Cause.ABANDONED)).isTrue(); - } - - @Test - void should_handle_span_when_name_null() { - SpanIgnoringSpanHandler handler = new SpanIgnoringSpanHandler(new SleuthProperties()); - - then(handler.end(null, new MutableSpan(), SpanHandler.Cause.FINISHED)).isTrue(); - } - - @Test - void should_handle_span_when_not_present_in_main_list_of_spans_to_skip() { - SpanIgnoringSpanHandler handler = new SpanIgnoringSpanHandler(new SleuthProperties()); - - then(handler.end(null, namedSpan(), SpanHandler.Cause.FINISHED)).isTrue(); - } - - private MutableSpan namedSpan() { - MutableSpan span = new MutableSpan(); - span.name("someName"); - return span; - } - - @Test - void should_not_handle_span_when_present_in_main_list_of_spans_to_skip() { - SleuthProperties sleuthProperties = new SleuthProperties(); - sleuthProperties.getSpanHandler().setSpanNamePatternsToSkip(Collections.singletonList("someName")); - SpanIgnoringSpanHandler handler = new SpanIgnoringSpanHandler(sleuthProperties); - - then(handler.end(null, namedSpan(), SpanHandler.Cause.FINISHED)).isFalse(); - } - - @Test - void should_not_handle_span_when_present_in_additional_list_of_spans_to_skip() { - SleuthProperties sleuthProperties = sleuthPropertiesWithAdditionalEntries(); - SpanIgnoringSpanHandler handler = new SpanIgnoringSpanHandler(sleuthProperties); - - then(handler.end(null, namedSpan(), SpanHandler.Cause.FINISHED)).isFalse(); - } - - @Test - void should_use_cached_entry_for_same_patterns() { - end(handler(sleuthPropertiesWithAdditionalEntries("someOtherName"))); - end(handler(sleuthPropertiesWithAdditionalEntries("someOtherName"))); - end(handler(sleuthPropertiesWithAdditionalEntries("someOtherName"))); - - then(SpanIgnoringSpanHandler.cache).containsKey("someOtherName"); - - end(handler(sleuthPropertiesWithAdditionalEntries("a"))); - end(handler(sleuthPropertiesWithAdditionalEntries("b"))); - end(handler(sleuthPropertiesWithAdditionalEntries("c"))); - - then(SpanIgnoringSpanHandler.cache).containsKey("someOtherName").containsKey("a").containsKey("b") - .containsKey("c"); - } - - private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(TraceAutoConfiguration.class)); - - @Test - void should_not_register_span_handler_when_property_passed() { - this.contextRunner.withPropertyValues("spring.sleuth.span-handler.enabled=false") - .run((context) -> BDDAssertions.thenThrownBy(() -> context.getBean(SpanIgnoringSpanHandler.class)) - .isInstanceOf(NoSuchBeanDefinitionException.class)); - } - - @Test - void should_register_span_handler_by_default() { - this.contextRunner.run((context) -> context.getBean(SpanIgnoringSpanHandler.class)); - } - - private SleuthProperties sleuthPropertiesWithAdditionalEntries() { - return sleuthPropertiesWithAdditionalEntries("someName"); - } - - private SleuthProperties sleuthPropertiesWithAdditionalEntries(String name) { - SleuthProperties sleuthProperties = new SleuthProperties(); - sleuthProperties.getSpanHandler().setAdditionalSpanNamePatternsToIgnore(Collections.singletonList(name)); - return sleuthProperties; - } - - private void end(SpanIgnoringSpanHandler handler) { - handler.end(null, namedSpan(), SpanHandler.Cause.FINISHED); - } - - private SpanIgnoringSpanHandler handler(SleuthProperties sleuthProperties) { - return new SpanIgnoringSpanHandler(sleuthProperties); - } - -} diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/AsyncCustomAutoConfigurationTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/AsyncCustomAutoConfigurationTest.java index a0f2642b6..89e9c826f 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/AsyncCustomAutoConfigurationTest.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/AsyncCustomAutoConfigurationTest.java @@ -16,13 +16,12 @@ package org.springframework.cloud.sleuth.instrument.async; +import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.springframework.scheduling.annotation.AsyncConfigurer; -import static org.assertj.core.api.BDDAssertions.then; -import static org.mockito.Mockito.mock; - /** * @author Marcin Grzejszczak */ @@ -34,16 +33,16 @@ public class AsyncCustomAutoConfigurationTest { Object bean = configuration.postProcessAfterInitialization(new Object(), "someName"); - then(bean).isNotInstanceOf(LazyTraceAsyncCustomizer.class); + BDDAssertions.then(bean).isNotInstanceOf(LazyTraceAsyncCustomizer.class); } @Test public void should_return_lazy_async_configurer_when_bean_is_async_configurer() throws Exception { AsyncCustomAutoConfiguration configuration = new AsyncCustomAutoConfiguration(); - Object bean = configuration.postProcessAfterInitialization(mock(AsyncConfigurer.class), "someName"); + Object bean = configuration.postProcessAfterInitialization(Mockito.mock(AsyncConfigurer.class), "someName"); - then(bean).isInstanceOf(LazyTraceAsyncCustomizer.class); + BDDAssertions.then(bean).isInstanceOf(LazyTraceAsyncCustomizer.class); } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/AsyncDefaultAutoConfigurationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/AsyncDefaultAutoConfigurationTests.java index 865e12c5b..de79a4b5c 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/AsyncDefaultAutoConfigurationTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/AsyncDefaultAutoConfigurationTests.java @@ -23,8 +23,12 @@ import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.config.GatewayAutoConfiguration; +import org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration; +import org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -39,8 +43,9 @@ public class AsyncDefaultAutoConfigurationTests { BDDAssertions.then(this.executor).isNotNull().isInstanceOf(TraceableScheduledExecutorService.class); } - @Configuration - @EnableAutoConfiguration + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class, + GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class }) static class Config { @Bean diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/ExecutorBeanPostProcessorTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/ExecutorBeanPostProcessorTests.java index b86ee9faa..73b3ac8fc 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/ExecutorBeanPostProcessorTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/ExecutorBeanPostProcessorTests.java @@ -32,11 +32,9 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; -import brave.Tracing; import org.aopalliance.aop.Advice; import org.assertj.core.api.BDDAssertions; import org.awaitility.Awaitility; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -54,9 +52,6 @@ import org.springframework.core.task.SimpleAsyncTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService; -import static org.assertj.core.api.BDDAssertions.then; -import static org.assertj.core.api.BDDAssertions.thenThrownBy; - /** * @author Marcin Grzejszczak * @author Denys Ivano @@ -68,8 +63,6 @@ public class ExecutorBeanPostProcessorTests { @Mock(lenient = true) BeanFactory beanFactory; - Tracing tracing = Tracing.newBuilder().build(); - private SleuthAsyncProperties sleuthAsyncProperties; @BeforeEach @@ -78,17 +71,12 @@ public class ExecutorBeanPostProcessorTests { Mockito.when(this.beanFactory.getBean(SleuthAsyncProperties.class)).thenReturn(this.sleuthAsyncProperties); } - @AfterEach - public void clear() { - this.tracing.close(); - } - @Test public void should_create_a_cglib_proxy_by_default() throws Exception { Object o = new ExecutorBeanPostProcessor(this.beanFactory).postProcessAfterInitialization(new Foo(), "foo"); - then(o).isInstanceOf(Foo.class); - then(AopUtils.isCglibProxy(o)).isTrue(); + BDDAssertions.then(o).isInstanceOf(Foo.class); + BDDAssertions.then(AopUtils.isCglibProxy(o)).isTrue(); } @Test @@ -97,7 +85,7 @@ public class ExecutorBeanPostProcessorTests { Object o = new ExecutorBeanPostProcessor(this.beanFactory).postProcessAfterInitialization(service, "foo"); - then(o).isInstanceOf(TraceableExecutorService.class); + BDDAssertions.then(o).isInstanceOf(TraceableExecutorService.class); service.shutdown(); } @@ -108,7 +96,7 @@ public class ExecutorBeanPostProcessorTests { Object o = new ExecutorBeanPostProcessor(this.beanFactory).postProcessAfterInitialization(service, "foo"); - then(o).isInstanceOf(TraceableScheduledExecutorService.class); + BDDAssertions.then(o).isInstanceOf(TraceableScheduledExecutorService.class); service.shutdown(); } @@ -118,7 +106,7 @@ public class ExecutorBeanPostProcessorTests { Object o = new ExecutorBeanPostProcessor(this.beanFactory).postProcessAfterInitialization(service, "foo"); - then(o).isSameAs(service); + BDDAssertions.then(o).isSameAs(service); } @Test @@ -127,7 +115,7 @@ public class ExecutorBeanPostProcessorTests { Object o = new ExecutorBeanPostProcessor(this.beanFactory).postProcessAfterInitialization(service, "foo"); - then(o).isSameAs(service); + BDDAssertions.then(o).isSameAs(service); } @Test @@ -136,7 +124,7 @@ public class ExecutorBeanPostProcessorTests { Object o = new ExecutorBeanPostProcessor(this.beanFactory).postProcessAfterInitialization(service, "foo"); - then(o).isSameAs(service); + BDDAssertions.then(o).isSameAs(service); } @Test @@ -145,7 +133,7 @@ public class ExecutorBeanPostProcessorTests { Object o = new ExecutorBeanPostProcessor(this.beanFactory).postProcessAfterInitialization(service, "foo"); - then(o).isSameAs(service); + BDDAssertions.then(o).isSameAs(service); } @Test @@ -154,7 +142,7 @@ public class ExecutorBeanPostProcessorTests { Object o = new ExecutorBeanPostProcessor(this.beanFactory).postProcessAfterInitialization(service, "foo"); - then(o).isSameAs(service); + BDDAssertions.then(o).isSameAs(service); } @Test @@ -171,7 +159,7 @@ public class ExecutorBeanPostProcessorTests { Object wrappedService = bpp.postProcessAfterInitialization(service, "foo"); - then(wrappedService).isInstanceOf(TraceableScheduledExecutorService.class); + BDDAssertions.then(wrappedService).isInstanceOf(TraceableScheduledExecutorService.class); service.shutdown(); } @@ -180,8 +168,8 @@ public class ExecutorBeanPostProcessorTests { Object o = new ExecutorBeanPostProcessor(this.beanFactory) .postProcessAfterInitialization(new FooThreadPoolTaskExecutor(), "foo"); - then(o).isInstanceOf(FooThreadPoolTaskExecutor.class); - then(AopUtils.isCglibProxy(o)).isTrue(); + BDDAssertions.then(o).isInstanceOf(FooThreadPoolTaskExecutor.class); + BDDAssertions.then(AopUtils.isCglibProxy(o)).isTrue(); } @Test @@ -196,7 +184,7 @@ public class ExecutorBeanPostProcessorTests { } }; - thenThrownBy(() -> bpp.postProcessAfterInitialization(taskExecutor, "foo")) + BDDAssertions.thenThrownBy(() -> bpp.postProcessAfterInitialization(taskExecutor, "foo")) .isInstanceOf(AopConfigException.class).hasMessage("foo"); } @@ -213,7 +201,7 @@ public class ExecutorBeanPostProcessorTests { Object o = bpp.postProcessAfterInitialization(service, "foo"); - then(o).isInstanceOf(TraceableExecutorService.class); + BDDAssertions.then(o).isInstanceOf(TraceableExecutorService.class); } @Test @@ -223,7 +211,7 @@ public class ExecutorBeanPostProcessorTests { ExecutorService o = (ExecutorService) bpp.postProcessAfterInitialization(service, "foo"); - thenThrownBy(() -> o.submit((Callable) () -> "hello")).hasMessage("foo") + BDDAssertions.thenThrownBy(() -> o.submit((Callable) () -> "hello")).hasMessage("foo") .isInstanceOf(IllegalStateException.class); } @@ -305,14 +293,14 @@ public class ExecutorBeanPostProcessorTests { Executor executor = Runnable::run; Executor wrappedExecutor = (Executor) beanPostProcessor.postProcessAfterInitialization(executor, "executor"); - then(AopUtils.isJdkDynamicProxy(wrappedExecutor)).isTrue(); - then(AopUtils.isCglibProxy(wrappedExecutor)).isFalse(); + BDDAssertions.then(AopUtils.isJdkDynamicProxy(wrappedExecutor)).isTrue(); + BDDAssertions.then(AopUtils.isCglibProxy(wrappedExecutor)).isFalse(); AtomicBoolean wasCalled = new AtomicBoolean(false); wrappedExecutor.execute(() -> { wasCalled.set(true); }); - then(wasCalled).isTrue(); + BDDAssertions.then(wasCalled).isTrue(); } @Test @@ -322,11 +310,11 @@ public class ExecutorBeanPostProcessorTests { ScheduledThreadPoolExecutor wrappedExecutor = (ScheduledThreadPoolExecutor) beanPostProcessor .postProcessAfterInitialization(executor, "executor"); - then(AopUtils.isCglibProxy(wrappedExecutor)).isTrue(); + BDDAssertions.then(AopUtils.isCglibProxy(wrappedExecutor)).isTrue(); AtomicBoolean wasCalled = new AtomicBoolean(false); wrappedExecutor.execute(() -> wasCalled.set(true)); - Awaitility.await().untilAsserted(() -> then(wasCalled).isTrue()); + Awaitility.await().untilAsserted(() -> BDDAssertions.then(wasCalled).isTrue()); } @Test @@ -337,9 +325,9 @@ public class ExecutorBeanPostProcessorTests { ExecutorService wrappedExecutor = (ExecutorService) beanPostProcessor .postProcessAfterInitialization(executorService, "executorService"); - then(AopUtils.isJdkDynamicProxy(wrappedExecutor)).isTrue(); - then(AopUtils.isCglibProxy(wrappedExecutor)).isFalse(); - then(wrappedExecutor.submit(() -> "done").get()).isEqualTo("done"); + BDDAssertions.then(AopUtils.isJdkDynamicProxy(wrappedExecutor)).isTrue(); + BDDAssertions.then(AopUtils.isCglibProxy(wrappedExecutor)).isFalse(); + BDDAssertions.then(wrappedExecutor.submit(() -> "done").get()).isEqualTo("done"); wrappedExecutor.shutdownNow(); } @@ -350,9 +338,9 @@ public class ExecutorBeanPostProcessorTests { AsyncTaskExecutor wrappedExecutor = (AsyncTaskExecutor) beanPostProcessor .postProcessAfterInitialization(new DirectTaskExecutor(), "taskExecutor"); - then(AopUtils.isJdkDynamicProxy(wrappedExecutor)).isTrue(); - then(AopUtils.isCglibProxy(wrappedExecutor)).isFalse(); - then(wrappedExecutor.submit(() -> "done").get()).isEqualTo("done"); + BDDAssertions.then(AopUtils.isJdkDynamicProxy(wrappedExecutor)).isTrue(); + BDDAssertions.then(AopUtils.isCglibProxy(wrappedExecutor)).isFalse(); + BDDAssertions.then(wrappedExecutor.submit(() -> "done").get()).isEqualTo("done"); } @Test @@ -363,9 +351,9 @@ public class ExecutorBeanPostProcessorTests { ThreadPoolTaskExecutor wrappedTaskExecutor = (ThreadPoolTaskExecutor) postProcessor .postProcessAfterInitialization(threadPoolTaskExecutor, "threadPoolTaskExecutor"); - then(wrappedTaskExecutor).isInstanceOf(LazyTraceThreadPoolTaskExecutor.class); - then(AopUtils.isCglibProxy(wrappedTaskExecutor)).isFalse(); - then(AopUtils.isJdkDynamicProxy(wrappedTaskExecutor)).isFalse(); + BDDAssertions.then(wrappedTaskExecutor).isInstanceOf(LazyTraceThreadPoolTaskExecutor.class); + BDDAssertions.then(AopUtils.isCglibProxy(wrappedTaskExecutor)).isFalse(); + BDDAssertions.then(AopUtils.isJdkDynamicProxy(wrappedTaskExecutor)).isFalse(); threadPoolTaskExecutor.shutdown(); } @@ -375,14 +363,14 @@ public class ExecutorBeanPostProcessorTests { boolean isProxyNeeded = new ExecutorBeanPostProcessor(this.beanFactory).isProxyNeeded("fooExecutor"); - then(isProxyNeeded).isFalse(); + BDDAssertions.then(isProxyNeeded).isFalse(); } @Test public void proxy_is_needed() throws Exception { boolean isProxyNeeded = new ExecutorBeanPostProcessor(this.beanFactory).isProxyNeeded("fooExecutor"); - then(isProxyNeeded).isTrue(); + BDDAssertions.then(isProxyNeeded).isTrue(); } @Test @@ -392,8 +380,8 @@ public class ExecutorBeanPostProcessorTests { Object o = new ExecutorBeanPostProcessor(this.beanFactory) .postProcessAfterInitialization(new ThreadPoolTaskExecutor(), "fooExecutor"); - then(o).isInstanceOf(ThreadPoolTaskExecutor.class); - then(AopUtils.isCglibProxy(o)).isFalse(); + BDDAssertions.then(o).isInstanceOf(ThreadPoolTaskExecutor.class); + BDDAssertions.then(AopUtils.isCglibProxy(o)).isFalse(); } @Test @@ -401,9 +389,9 @@ public class ExecutorBeanPostProcessorTests { Object o = new ExecutorBeanPostProcessor(this.beanFactory) .postProcessAfterInitialization(new RejectedExecutionExecutor(), "fooExecutor"); - then(o).isInstanceOf(RejectedExecutionExecutor.class); - then(AopUtils.isCglibProxy(o)).isTrue(); - thenThrownBy(() -> ((RejectedExecutionExecutor) o).execute(() -> { + BDDAssertions.then(o).isInstanceOf(RejectedExecutionExecutor.class); + BDDAssertions.then(AopUtils.isCglibProxy(o)).isTrue(); + BDDAssertions.thenThrownBy(() -> ((RejectedExecutionExecutor) o).execute(() -> { })).isInstanceOf(RejectedExecutionException.class).hasMessage("rejected"); } @@ -441,13 +429,13 @@ public class ExecutorBeanPostProcessorTests { Executor wrappedExecutor = (Executor) beanPostProcessor .postProcessAfterInitialization(new ExecutorWithFinalMethod(), "executorWithFinalMethod"); - then(AopUtils.isJdkDynamicProxy(wrappedExecutor)).isTrue(); - then(AopUtils.isCglibProxy(wrappedExecutor)).isFalse(); + BDDAssertions.then(AopUtils.isJdkDynamicProxy(wrappedExecutor)).isTrue(); + BDDAssertions.then(AopUtils.isCglibProxy(wrappedExecutor)).isFalse(); AtomicBoolean wasCalled = new AtomicBoolean(false); wrappedExecutor.execute(() -> { wasCalled.set(true); }); - then(wasCalled).isTrue(); + BDDAssertions.then(wasCalled).isTrue(); } // #1569 @@ -458,13 +446,13 @@ public class ExecutorBeanPostProcessorTests { Executor wrappedExecutor = (Executor) beanPostProcessor .postProcessAfterInitialization(new ExecutorWithInheritedFinalMethod(), "executorWithFinalMethod"); - then(AopUtils.isJdkDynamicProxy(wrappedExecutor)).isTrue(); - then(AopUtils.isCglibProxy(wrappedExecutor)).isFalse(); + BDDAssertions.then(AopUtils.isJdkDynamicProxy(wrappedExecutor)).isTrue(); + BDDAssertions.then(AopUtils.isCglibProxy(wrappedExecutor)).isFalse(); AtomicBoolean wasCalled = new AtomicBoolean(false); wrappedExecutor.execute(() -> { wasCalled.set(true); }); - then(wasCalled).isTrue(); + BDDAssertions.then(wasCalled).isTrue(); } class Foo implements Executor { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceAsyncCustomizerTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceAsyncCustomizerTest.java index 1c29eabbd..d99a56d56 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceAsyncCustomizerTest.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceAsyncCustomizerTest.java @@ -18,17 +18,16 @@ package org.springframework.cloud.sleuth.instrument.async; import java.util.concurrent.Executor; -import org.apache.commons.configuration.beanutils.BeanFactory; +import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.BeanFactory; import org.springframework.scheduling.annotation.AsyncConfigurer; -import static org.assertj.core.api.BDDAssertions.then; - /** * @author Marcin Grzejszczak */ @@ -48,7 +47,7 @@ public class LazyTraceAsyncCustomizerTest { public void should_wrap_async_executor_in_trace_version() throws Exception { Executor executor = this.lazyTraceAsyncCustomizer.getAsyncExecutor(); - then(executor).isExactlyInstanceOf(LazyTraceExecutor.class); + BDDAssertions.then(executor).isExactlyInstanceOf(LazyTraceExecutor.class); } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListenerTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListenerTest.java index 12206a2d4..a95ce32c0 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListenerTest.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListenerTest.java @@ -16,14 +16,13 @@ package org.springframework.cloud.sleuth.instrument.async; +import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ConfigurableApplicationContext; -import static org.assertj.core.api.BDDAssertions.then; - @SpringBootTest(classes = SleuthContextListener.class) class SleuthContextListenerTest { @@ -34,16 +33,16 @@ class SleuthContextListenerTest { void should_be_usable_using_context() { SleuthContextListener listener = SleuthContextListener.getBean(applicationContext); - then(listener).isNotNull(); - then(listener.isUnusable()).isFalse(); + BDDAssertions.then(listener).isNotNull(); + BDDAssertions.then(listener.isUnusable()).isFalse(); } @Test void should_be_usable_using_beanfactory() { SleuthContextListener listener = SleuthContextListener.getBean(applicationContext.getBeanFactory()); - then(listener).isNotNull(); - then(listener.isUnusable()).isFalse(); + BDDAssertions.then(listener).isNotNull(); + BDDAssertions.then(listener.isUnusable()).isFalse(); } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/CircuitBreakerTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/CircuitBreakerTests.java deleted file mode 100644 index 56c39ad7a..000000000 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/CircuitBreakerTests.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2013-2020 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.circuitbreaker; - -import java.util.concurrent.atomic.AtomicReference; - -import brave.ScopedSpan; -import brave.Span; -import brave.Tracer; -import brave.Tracing; -import brave.handler.MutableSpan; -import brave.propagation.StrictCurrentTraceContext; -import brave.sampler.Sampler; -import brave.test.TestSpanHandler; -import org.assertj.core.api.BDDAssertions; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory; - -import static org.assertj.core.api.BDDAssertions.then; - -public class CircuitBreakerTests { - - StrictCurrentTraceContext currentTraceContext = StrictCurrentTraceContext.create(); - - TestSpanHandler spans = new TestSpanHandler(); - - Tracing tracing = Tracing.newBuilder().currentTraceContext(this.currentTraceContext).addSpanHandler(this.spans) - .sampler(Sampler.ALWAYS_SAMPLE).build(); - - Tracer tracer = this.tracing.tracer(); - - @BeforeEach - public void setup() { - this.spans.clear(); - } - - @AfterEach - public void close() { - this.tracing.close(); - this.currentTraceContext.close(); - } - - @Test - public void should_pass_tracing_information_when_using_circuit_breaker() { - // given - Tracer tracer = this.tracer; - ScopedSpan scopedSpan = null; - try { - scopedSpan = tracer.startScopedSpan("start"); - // when - Span span = new Resilience4JCircuitBreakerFactory().create("name") - .run(new TraceSupplier<>(tracer, tracer::currentSpan)); - - then(span).isNotNull(); - then(scopedSpan.context().traceIdString()).isEqualTo(span.context().traceIdString()); - } - finally { - scopedSpan.finish(); - } - } - - @Test - public void should_pass_tracing_information_when_using_circuit_breaker_with_fallback() { - // given - Tracer tracer = this.tracer; - AtomicReference first = new AtomicReference<>(); - AtomicReference second = new AtomicReference<>(); - ScopedSpan scopedSpan = null; - try { - scopedSpan = tracer.startScopedSpan("start"); - // when - BDDAssertions.thenThrownBy( - () -> new Resilience4JCircuitBreakerFactory().create("name").run(new TraceSupplier<>(tracer, () -> { - first.set(tracer.currentSpan()); - throw new IllegalStateException("boom"); - }), new TraceFunction<>(tracer, throwable -> { - second.set(tracer.currentSpan()); - throw new IllegalStateException("boom2"); - }))).isInstanceOf(IllegalStateException.class).hasMessageContaining("boom2"); - - then(this.spans).hasSize(2); - then(scopedSpan.context().traceIdString()).isEqualTo(first.get().context().traceIdString()); - then(scopedSpan.context().traceIdString()).isEqualTo(second.get().context().traceIdString()); - then(first.get().context().spanIdString()).isNotEqualTo(second.get().context().spanIdString()); - - MutableSpan reportedSpan = this.spans.get(1); - then(reportedSpan.name()).contains("CircuitBreakerTests"); - then(reportedSpan.tags().get("error")).contains("boom2"); - } - finally { - scopedSpan.finish(); - } - } - -} diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/MessageHeaderPropagationTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/MessageHeaderPropagationTest.java deleted file mode 100644 index 37345ed54..000000000 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/MessageHeaderPropagationTest.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2013-2020 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.messaging; - -import java.util.Collections; - -import brave.propagation.Propagation; -import org.junit.jupiter.api.Test; - -import org.springframework.messaging.support.MessageHeaderAccessor; -import org.springframework.messaging.support.NativeMessageHeaderAccessor; - -import static org.assertj.core.api.Assertions.assertThat; - -public class MessageHeaderPropagationTest extends PropagationSetterTest { - - MessageHeaderAccessor carrier = new MessageHeaderAccessor(); - - @Override - public Propagation.KeyFactory keyFactory() { - return Propagation.KeyFactory.STRING; - } - - @Override - protected MessageHeaderAccessor carrier() { - return this.carrier; - } - - @Override - protected Propagation.Setter setter() { - return MessageHeaderPropagation.INSTANCE; - } - - @Override - protected Iterable read(MessageHeaderAccessor carrier, String key) { - Object result = carrier.getHeader(key); - return result != null ? Collections.singleton(result.toString()) : Collections.emptyList(); - } - - @Test - public void testGetByteArrayValue() { - MessageHeaderAccessor carrier = carrier(); - carrier.setHeader("b3", "48485a3953bb6124-1234".getBytes()); - carrier.setHeader("b3", "48485a3953bb6124000000-1234".getBytes()); - String value = MessageHeaderPropagation.INSTANCE.get(carrier, "b3"); - assertThat(value).isEqualTo("48485a3953bb6124000000-1234"); - } - - @Test - public void testGetStringValue() { - MessageHeaderAccessor carrier = carrier(); - carrier.setHeader("B3", "48485a3953bb6124-1234"); - carrier.setHeader("B3", "48485a3953bb61240000000-1234"); - String value = MessageHeaderPropagation.INSTANCE.get(carrier, "B3"); - assertThat(value).isEqualTo("48485a3953bb61240000000-1234"); - } - - @Test - public void testGetNullValue() { - MessageHeaderAccessor carrier = carrier(); - carrier.setHeader("B3", "48485a3953bb6124-1234"); - carrier.setHeader("B3", "48485a3953bb61240000000-1234"); - String value = MessageHeaderPropagation.INSTANCE.get(carrier, "non existent key"); - assertThat(value).isNull(); - } - - @Test - public void testSkipWrongValueTypeForGet() { - MessageHeaderAccessor carrier = carrier(); - carrier.setHeader(NativeMessageHeaderAccessor.NATIVE_HEADERS, - "{spanTraceId=[123], spanId=[456], spanSampled=[0]}"); - MessageHeaderPropagation.INSTANCE.get(carrier, "b3"); - } - - @Test - public void testSkipWrongValueTypeForRemoval() { - MessageHeaderAccessor carrier = carrier(); - carrier.setHeader(NativeMessageHeaderAccessor.NATIVE_HEADERS, - "{spanTraceId=[123], spanId=[456], spanSampled=[0]}"); - MessageHeaderPropagation.removeAnyTraceHeaders(carrier, Collections.singletonList("b3")); - } - - @Test - public void testSkipWrongValueTypeForPut() { - MessageHeaderAccessor carrier = carrier(); - carrier.setHeader(NativeMessageHeaderAccessor.NATIVE_HEADERS, - "{spanTraceId=[123], spanId=[456], spanSampled=[0]}"); - MessageHeaderPropagation.INSTANCE.put(carrier, "b3", "1234"); - } - -} diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/MessageHeaderPropagation_NativeTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/MessageHeaderPropagation_NativeTest.java deleted file mode 100644 index 23145ffa5..000000000 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/MessageHeaderPropagation_NativeTest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2013-2020 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.messaging; - -import brave.propagation.Propagation; - -import org.springframework.messaging.support.MessageHeaderAccessor; -import org.springframework.messaging.support.NativeMessageHeaderAccessor; - -/** - * Tests that native headers are redundantly added. - * - * @author Marcin Grzejszczak - */ -public class MessageHeaderPropagation_NativeTest extends PropagationSetterTest { - - NativeMessageHeaderAccessor carrier = new NativeMessageHeaderAccessor() { - }; - - @Override - public Propagation.KeyFactory keyFactory() { - return Propagation.KeyFactory.STRING; - } - - @Override - protected MessageHeaderAccessor carrier() { - return this.carrier; - } - - @Override - protected Propagation.Setter setter() { - return MessageHeaderPropagation.INSTANCE; - } - - @Override - protected Iterable read(MessageHeaderAccessor carrier, String key) { - return ((NativeMessageHeaderAccessor) carrier).getNativeHeader(key); - } - -} diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/PropagationSetterTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/PropagationSetterTest.java deleted file mode 100644 index 9f02c2920..000000000 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/PropagationSetterTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2013-2020 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.messaging; - -import brave.propagation.Propagation; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Taken from Brave. - * - * @param carrier type - * @param key type - * @author Marcin Grzejszczak - */ -public abstract class PropagationSetterTest { - - protected abstract Propagation.KeyFactory keyFactory(); - - protected abstract C carrier(); - - protected abstract Propagation.Setter setter(); - - protected abstract Iterable read(C carrier, K key); - - @Test - public void set() throws Exception { - K key = keyFactory().create("b3"); - setter().put(carrier(), key, "1"); - - assertThat(read(carrier(), key)).containsExactly("1"); - } - - @Test - public void set128() throws Exception { - K key = keyFactory().create("b3"); - setter().put(carrier(), key, "1"); - - assertThat(read(carrier(), key)).containsExactly("1"); - } - - @Test - public void setTwoKeys() throws Exception { - K key1 = keyFactory().create("b3"); - K key2 = keyFactory().create("baggage"); - setter().put(carrier(), key1, "1"); - setter().put(carrier(), key2, "country-code=FO"); - - assertThat(read(carrier(), key1)).containsExactly("1"); - assertThat(read(carrier(), key2)).containsExactly("country-code=FO"); - } - - @Test - public void reset() throws Exception { - K key = keyFactory().create("b3"); - setter().put(carrier(), key, "0"); - setter().put(carrier(), key, "1"); - - assertThat(read(carrier(), key)).containsExactly("1"); - } - -} diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceFunctionAroundWrapperTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceFunctionAroundWrapperTests.java index 13ba5c6e4..d42fa50e3 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceFunctionAroundWrapperTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceFunctionAroundWrapperTests.java @@ -24,7 +24,7 @@ class TraceFunctionAroundWrapperTests { @Test void should_clear_cache_on_refresh() { - TraceFunctionAroundWrapper wrapper = new TraceFunctionAroundWrapper(null, null); + TraceFunctionAroundWrapper wrapper = new TraceFunctionAroundWrapper(null, null, null, null, null); wrapper.functionToDestinationCache.put("example", "entry"); then(wrapper.functionToDestinationCache).isNotEmpty(); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceSpringIntegrationAutoConfigurationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceSpringIntegrationAutoConfigurationTests.java index 80af66a89..0c829b185 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceSpringIntegrationAutoConfigurationTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceSpringIntegrationAutoConfigurationTests.java @@ -57,7 +57,7 @@ class TraceSpringIntegrationAutoConfigurationTests { .run(context -> assertThat(context).hasSingleBean(TracingChannelInterceptor.class)); } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableBinding static class WithEnabledBinding { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/quartz/TraceQuartzAutoConfigurationTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/quartz/TraceQuartzAutoConfigurationTest.java index 815a8e23d..9fda4defe 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/quartz/TraceQuartzAutoConfigurationTest.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/quartz/TraceQuartzAutoConfigurationTest.java @@ -16,22 +16,23 @@ package org.springframework.cloud.sleuth.instrument.quartz; -import brave.Tracer; -import brave.Tracing; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.quartz.ListenerManager; import org.quartz.Scheduler; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.cloud.gateway.config.GatewayAutoConfiguration; +import org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration; +import org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import org.springframework.context.annotation.Primary; /** * @author Branden Cash @@ -39,14 +40,14 @@ import static org.mockito.Mockito.when; public class TraceQuartzAutoConfigurationTest { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration( - AutoConfigurations.of(SchedulerConfig.class, TracingConfig.class, TraceQuartzAutoConfiguration.class)); + AutoConfigurations.of(SchedulerConfig.class, TracingConfig.class, EnableAutoConfig.class)); @Test public void should_create_job_listener_bean_when_all_conditions_are_met() { // when this.contextRunner.run(context -> { // expect - assertThat(context).hasSingleBean(TracingJobListener.class); + Assertions.assertThat(context).hasSingleBean(TracingJobListener.class); }); } @@ -55,7 +56,7 @@ public class TraceQuartzAutoConfigurationTest { // when this.contextRunner.run(context -> { // expect - verify(context.getBean(Scheduler.class).getListenerManager()) + Mockito.verify(context.getBean(Scheduler.class).getListenerManager()) .addTriggerListener(context.getBean(TracingJobListener.class)); }); } @@ -65,7 +66,7 @@ public class TraceQuartzAutoConfigurationTest { // when this.contextRunner.run(context -> { // expect - verify(context.getBean(Scheduler.class).getListenerManager()) + Mockito.verify(context.getBean(Scheduler.class).getListenerManager()) .addJobListener(context.getBean(TracingJobListener.class)); }); } @@ -79,7 +80,7 @@ public class TraceQuartzAutoConfigurationTest { // when .run(context -> { // expect - assertThat(context).doesNotHaveBean(TracingJobListener.class); + Assertions.assertThat(context).doesNotHaveBean(TracingJobListener.class); }); } @@ -92,7 +93,7 @@ public class TraceQuartzAutoConfigurationTest { // when .run(context -> { // expect - assertThat(context).doesNotHaveBean(TracingJobListener.class); + Assertions.assertThat(context).doesNotHaveBean(TracingJobListener.class); }); } @@ -102,35 +103,38 @@ public class TraceQuartzAutoConfigurationTest { this.contextRunner.withPropertyValues("spring.sleuth.quartz.enabled=false") // expect .run(context -> { - assertThat(context).doesNotHaveBean(TracingJobListener.class); + Assertions.assertThat(context).doesNotHaveBean(TracingJobListener.class); }); } - @Configuration + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class, + GatewayMetricsAutoConfiguration.class }) + public static class EnableAutoConfig { + + } + + @Configuration(proxyBeanMethods = false) @AutoConfigureBefore(TraceQuartzAutoConfiguration.class) public static class SchedulerConfig { @Bean + @Primary public Scheduler scheduler() throws Exception { - Scheduler scheduler = mock(Scheduler.class); - when(scheduler.getListenerManager()).thenReturn(mock(ListenerManager.class)); + Scheduler scheduler = Mockito.mock(Scheduler.class); + Mockito.when(scheduler.getListenerManager()).thenReturn(Mockito.mock(ListenerManager.class)); return scheduler; } } - @Configuration + @Configuration(proxyBeanMethods = false) @AutoConfigureBefore(TraceQuartzAutoConfiguration.class) public static class TracingConfig { @Bean - public Tracing tracing() { - return mock(Tracing.class); - } - - @Bean - public Tracer tracer() { - return mock(Tracer.class); + public Tracer testTracer() { + return Mockito.mock(Tracer.class); } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfigurationIntegrationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfigurationIntegrationTests.java deleted file mode 100644 index 9dd9fba79..000000000 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfigurationIntegrationTests.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2013-2020 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.handler.SpanHandler; -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 brave.test.TestSpanHandler; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -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; - -@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 - SpanHandler testSpanHandler() { - return new TestSpanHandler(); - } - - // 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[] - - } - -} 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 deleted file mode 100644 index 3d07fe66d..000000000 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/rpc/TraceRpcAutoConfigurationTests.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2013-2020 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.jupiter.api.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)); - } - -} - -@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/scheduling/TraceSchedulingAutoConfigurationTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/scheduling/TraceSchedulingAutoConfigurationTest.java index 580cf7e4a..569f4d911 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/scheduling/TraceSchedulingAutoConfigurationTest.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/scheduling/TraceSchedulingAutoConfigurationTest.java @@ -17,6 +17,7 @@ package org.springframework.cloud.sleuth.instrument.scheduling; import org.aspectj.lang.ProceedingJoinPoint; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -24,8 +25,6 @@ import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; -import static org.assertj.core.api.Assertions.assertThat; - class TraceSchedulingAutoConfigurationTest { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration( @@ -33,13 +32,13 @@ class TraceSchedulingAutoConfigurationTest { @Test void shoud_create_TraceSchedulingAspect() { - this.contextRunner.run(context -> assertThat(context).hasSingleBean(TraceSchedulingAspect.class)); + this.contextRunner.run(context -> Assertions.assertThat(context).hasSingleBean(TraceSchedulingAspect.class)); } @Test void shoud_not_create_TraceSchedulingAspect_without_aspectJ() { this.contextRunner.withClassLoader(new FilteredClassLoader(ProceedingJoinPoint.class)) - .run(context -> assertThat(context).doesNotHaveBean(TraceSchedulingAspect.class)); + .run(context -> Assertions.assertThat(context).doesNotHaveBean(TraceSchedulingAspect.class)); } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipPatternProviderConfigTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipPatternProviderConfigTest.java index 73d580bd9..dfbef304e 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipPatternProviderConfigTest.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipPatternProviderConfigTest.java @@ -24,6 +24,7 @@ import java.util.Optional; import java.util.regex.Pattern; import java.util.stream.Collectors; +import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.Test; import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; @@ -48,8 +49,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.mock.env.MockEnvironment; -import static org.assertj.core.api.BDDAssertions.then; - /** * @author Marcin Grzejszczak */ @@ -63,14 +62,14 @@ public class SkipPatternProviderConfigTest { @Test public void should_return_null_when_cleared() throws Exception { contextRunner.withPropertyValues("spring.sleuth.web.skip-pattern") - .run(context -> then(context.getBean("sleuthSkipPatternProvider")).hasToString("null")); + .run(context -> BDDAssertions.then(context.getBean("sleuthSkipPatternProvider")).hasToString("null")); } @Test public void should_pick_skip_pattern_from_sleuth_properties() throws Exception { contextRunner.withPropertyValues("spring.sleuth.web.skip-pattern=foo.*|bar.*").run(context -> { final String pattern = extractPattern(context); - then(pattern).isEqualTo("foo.*|bar.*"); + BDDAssertions.then(pattern).isEqualTo("foo.*|bar.*"); }); } @@ -79,7 +78,7 @@ public class SkipPatternProviderConfigTest { contextRunner.withPropertyValues("spring.sleuth.web.skip-pattern=foo.*|bar.*", "spring.sleuth.web.additional-skip-pattern=baz.*|faz.*").run(context -> { final String pattern = extractPattern(context); - then(pattern).isEqualTo("foo.*|bar.*|baz.*|faz.*"); + BDDAssertions.then(pattern).isEqualTo("foo.*|bar.*|baz.*|faz.*"); }); } @@ -95,7 +94,7 @@ public class SkipPatternProviderConfigTest { .skipPatternForManagementServerProperties(environment(), new ManagementServerProperties()) .skipPattern(); - then(pattern).isEmpty(); + BDDAssertions.then(pattern).isEmpty(); } @Test @@ -104,7 +103,7 @@ public class SkipPatternProviderConfigTest { .withConfiguration( UserConfigurations.of(ManagementContextAutoConfiguration.class, ServerPropertiesConfig.class)) .withPropertyValues("management.server.servlet.context-path=foo").run(context -> { - then(extractAllPatterns(context)).containsExactlyInAnyOrder( + BDDAssertions.then(extractAllPatterns(context)).containsExactlyInAnyOrder( "/actuator(/|/(health|health/.*|info|info/.*))?", "foo.*", SleuthWebProperties.DEFAULT_SKIP_PATTERN); }); @@ -116,7 +115,7 @@ public class SkipPatternProviderConfigTest { .withConfiguration( UserConfigurations.of(ManagementContextAutoConfiguration.class, ServerPropertiesConfig.class)) .withPropertyValues("management.server.servlet.context-path=${test:value}").run(context -> { - then(extractAllPatterns(context)).containsExactlyInAnyOrder( + BDDAssertions.then(extractAllPatterns(context)).containsExactlyInAnyOrder( "/actuator(/|/(health|health/.*|info|info/.*))?", "value.*", SleuthWebProperties.DEFAULT_SKIP_PATTERN); }); @@ -129,13 +128,13 @@ public class SkipPatternProviderConfigTest { new WebEndpointProperties(), Collections::emptyList) .skipPattern(); - then(pattern).isEmpty(); + BDDAssertions.then(pattern).isEmpty(); } @Test public void should_return_endpoints_without_context_path() { contextRunner.withConfiguration(UserConfigurations.of(ServerPropertiesConfig.class)).run(context -> { - then(extractAllPatterns(context)).containsExactlyInAnyOrder( + BDDAssertions.then(extractAllPatterns(context)).containsExactlyInAnyOrder( "/actuator(/|/(health|health/.*|info|info/.*))?", SleuthWebProperties.DEFAULT_SKIP_PATTERN); }); } @@ -144,7 +143,7 @@ public class SkipPatternProviderConfigTest { public void should_return_endpoints_with_context_path() { contextRunner.withConfiguration(UserConfigurations.of(ServerPropertiesConfig.class)) .withPropertyValues("server.servlet.context-path=foo").run(context -> { - then(extractAllPatterns(context)).containsExactlyInAnyOrder( + BDDAssertions.then(extractAllPatterns(context)).containsExactlyInAnyOrder( "foo/actuator(/|/(health|health/.*|info|info/.*))?", SleuthWebProperties.DEFAULT_SKIP_PATTERN); }); @@ -154,7 +153,7 @@ public class SkipPatternProviderConfigTest { public void should_return_endpoints_with_context_path_with_placeholders() { contextRunner.withConfiguration(UserConfigurations.of(ServerPropertiesConfig.class)) .withPropertyValues("server.servlet.context-path=${test:foo}").run(context -> { - then(extractAllPatterns(context)).containsExactlyInAnyOrder( + BDDAssertions.then(extractAllPatterns(context)).containsExactlyInAnyOrder( "foo/actuator(/|/(health|health/.*|info|info/.*))?", SleuthWebProperties.DEFAULT_SKIP_PATTERN); }); @@ -164,8 +163,8 @@ public class SkipPatternProviderConfigTest { public void should_return_endpoints_without_context_path_and_base_path_set_to_root() { contextRunner.withConfiguration(UserConfigurations.of(ServerPropertiesConfig.class)) .withPropertyValues("management.endpoints.web.base-path=/").run(context -> { - then(extractAllPatterns(context)).containsExactlyInAnyOrder("/(health|health/.*|info|info/.*)", - SleuthWebProperties.DEFAULT_SKIP_PATTERN); + BDDAssertions.then(extractAllPatterns(context)).containsExactlyInAnyOrder( + "/(health|health/.*|info|info/.*)", SleuthWebProperties.DEFAULT_SKIP_PATTERN); }); } @@ -173,8 +172,8 @@ public class SkipPatternProviderConfigTest { public void should_return_endpoints_without_context_path_and_base_path_set_to_root_with_placeholders() { contextRunner.withConfiguration(UserConfigurations.of(ServerPropertiesConfig.class)) .withPropertyValues("management.endpoints.web.base-path=${test:/}").run(context -> { - then(extractAllPatterns(context)).containsExactlyInAnyOrder("/(health|health/.*|info|info/.*)", - SleuthWebProperties.DEFAULT_SKIP_PATTERN); + BDDAssertions.then(extractAllPatterns(context)).containsExactlyInAnyOrder( + "/(health|health/.*|info|info/.*)", SleuthWebProperties.DEFAULT_SKIP_PATTERN); }); } @@ -183,7 +182,7 @@ public class SkipPatternProviderConfigTest { contextRunner.withConfiguration(UserConfigurations.of(ServerPropertiesConfig.class)) .withPropertyValues("management.endpoints.web.base-path=/", "server.servlet.context-path=foo") .run(context -> { - then(extractAllPatterns(context)).containsExactlyInAnyOrder( + BDDAssertions.then(extractAllPatterns(context)).containsExactlyInAnyOrder( "foo(/|/(health|health/.*|info|info/.*))?", SleuthWebProperties.DEFAULT_SKIP_PATTERN); }); } @@ -193,7 +192,7 @@ public class SkipPatternProviderConfigTest { contextRunner.withConfiguration(UserConfigurations.of(ServerPropertiesConfig.class)) .withPropertyValues("management.endpoints.web.base-path=${test:/}", "server.servlet.context-path=foo") .run(context -> { - then(extractAllPatterns(context)).containsExactlyInAnyOrder( + BDDAssertions.then(extractAllPatterns(context)).containsExactlyInAnyOrder( "foo(/|/(health|health/.*|info|info/.*))?", SleuthWebProperties.DEFAULT_SKIP_PATTERN); }); } @@ -204,8 +203,8 @@ public class SkipPatternProviderConfigTest { .withPropertyValues("management.endpoints.web.base-path=/", "management.server.port=0", "server.servlet.context-path=foo") .run(context -> { - then(extractAllPatterns(context)).containsExactlyInAnyOrder("/(health|health/.*|info|info/.*)", - SleuthWebProperties.DEFAULT_SKIP_PATTERN); + BDDAssertions.then(extractAllPatterns(context)).containsExactlyInAnyOrder( + "/(health|health/.*|info|info/.*)", SleuthWebProperties.DEFAULT_SKIP_PATTERN); }); } @@ -215,8 +214,8 @@ public class SkipPatternProviderConfigTest { .withPropertyValues("management.endpoints.web.base-path=/", "management.server.port=${some-port:0}", "server.servlet.context-path=${some-path:foo}") .run(context -> { - then(extractAllPatterns(context)).containsExactlyInAnyOrder("/(health|health/.*|info|info/.*)", - SleuthWebProperties.DEFAULT_SKIP_PATTERN); + BDDAssertions.then(extractAllPatterns(context)).containsExactlyInAnyOrder( + "/(health|health/.*|info|info/.*)", SleuthWebProperties.DEFAULT_SKIP_PATTERN); }); } @@ -225,7 +224,7 @@ public class SkipPatternProviderConfigTest { contextRunner.withConfiguration(UserConfigurations.of(ServerPropertiesConfig.class)) .withPropertyValues("management.endpoints.web.base-path=/mgt", "server.servlet.context-path=foo") .run(context -> { - then(extractAllPatterns(context)).containsExactlyInAnyOrder( + BDDAssertions.then(extractAllPatterns(context)).containsExactlyInAnyOrder( "foo/mgt(/|/(health|health/.*|info|info/.*))?", SleuthWebProperties.DEFAULT_SKIP_PATTERN); }); } @@ -236,7 +235,7 @@ public class SkipPatternProviderConfigTest { .withPropertyValues("management.endpoints.web.base-path=/${test:mgt}", "server.servlet.context-path=${test2:foo}") .run(context -> { - then(extractAllPatterns(context)).containsExactlyInAnyOrder( + BDDAssertions.then(extractAllPatterns(context)).containsExactlyInAnyOrder( "foo/mgt(/|/(health|health/.*|info|info/.*))?", SleuthWebProperties.DEFAULT_SKIP_PATTERN); }); } @@ -245,7 +244,7 @@ public class SkipPatternProviderConfigTest { public void should_return_endpoints_with_actuator_default_context_path_different_port() { contextRunner.withConfiguration(UserConfigurations.of(ServerPropertiesConfig.class)) .withPropertyValues("management.server.port=0", "server.servlet.context-path=foo").run(context -> { - then(extractAllPatterns(context)).containsExactlyInAnyOrder( + BDDAssertions.then(extractAllPatterns(context)).containsExactlyInAnyOrder( "/actuator(/|/(health|health/.*|info|info/.*))?", SleuthWebProperties.DEFAULT_SKIP_PATTERN); }); } @@ -256,7 +255,7 @@ public class SkipPatternProviderConfigTest { .withPropertyValues("management.endpoints.web.base-path=/mgt", "management.server.port=0", "server.servlet.context-path=foo") .run(context -> { - then(extractAllPatterns(context)).containsExactlyInAnyOrder( + BDDAssertions.then(extractAllPatterns(context)).containsExactlyInAnyOrder( "/mgt(/|/(health|health/.*|info|info/.*))?", SleuthWebProperties.DEFAULT_SKIP_PATTERN); }); } @@ -265,7 +264,7 @@ public class SkipPatternProviderConfigTest { public void should_return_endpoints_with_context_path_different_port() { contextRunner.withConfiguration(UserConfigurations.of(ServerPropertiesConfig.class)) .withPropertyValues("management.server.port=0", "server.servlet.context-path=foo").run(context -> { - then(extractAllPatterns(context)).containsExactlyInAnyOrder( + BDDAssertions.then(extractAllPatterns(context)).containsExactlyInAnyOrder( "/actuator(/|/(health|health/.*|info|info/.*))?", SleuthWebProperties.DEFAULT_SKIP_PATTERN); }); } @@ -277,7 +276,7 @@ public class SkipPatternProviderConfigTest { Pattern pattern = configuration.sleuthSkipPatternProvider(patterns).skipPattern(); - then(pattern.pattern()).isEqualTo("foo|bar"); + BDDAssertions.then(pattern.pattern()).isEqualTo("foo|bar"); } private SingleSkipPattern foo() { @@ -304,13 +303,13 @@ public class SkipPatternProviderConfigTest { .filter(Optional::isPresent).map(Optional::get).map(Pattern::pattern).collect(Collectors.toList()); } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(ServerProperties.class) static class ServerPropertiesConfig { } - @Configuration + @Configuration(proxyBeanMethods = false) static class EmptyEndpoints { @Bean diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/GH846Tests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/GH846Tests.java index aeba98dba..46fe61cc4 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/GH846Tests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/GH846Tests.java @@ -24,6 +24,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.cloud.gateway.config.GatewayAutoConfiguration; +import org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration; +import org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.ClientHttpRequestInterceptor; @@ -44,8 +47,9 @@ public class GH846Tests { .as("Change detected in RestTemplate interceptor *after* @PostConstruct").isEqualTo(count); } - @EnableAutoConfiguration - @Configuration + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class, + GatewayMetricsAutoConfiguration.class }) static class App { @Bean diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceNoWebEnvironmentTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceNoWebEnvironmentTests.java similarity index 95% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceNoWebEnvironmentTests.java rename to spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceNoWebEnvironmentTests.java index 9911374f7..b778be3eb 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceNoWebEnvironmentTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceNoWebEnvironmentTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web; +package org.springframework.cloud.sleuth.instrument.web.client; import org.junit.jupiter.api.Test; @@ -51,7 +51,7 @@ public class TraceNoWebEnvironmentTests { } } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration @EnableFeignClients(clients = Config.SomeFeignClient.class) public static class Config { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfigurationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfigurationTests.java index be59a3afa..b3692a556 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfigurationTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfigurationTests.java @@ -23,15 +23,19 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import brave.spring.web.TracingClientHttpRequestInterceptor; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.boot.web.client.RestTemplateCustomizer; +import org.springframework.cloud.gateway.config.GatewayAutoConfiguration; +import org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration; +import org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration; +import org.springframework.cloud.sleuth.instrument.web.mvc.TracingClientHttpRequestInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpRequest; @@ -109,8 +113,9 @@ public class TraceWebClientAutoConfigurationTests { } } - @Configuration - @EnableAutoConfiguration + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class, + GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class }) static class Config { // custom builder diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientBeanPostProcessorTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientBeanPostProcessorTest.java index dd680f31e..c8986fa9d 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientBeanPostProcessorTest.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientBeanPostProcessorTest.java @@ -18,21 +18,20 @@ package org.springframework.cloud.sleuth.instrument.web.client; import java.util.concurrent.atomic.AtomicReference; -import brave.Span; +import org.assertj.core.api.Assertions; import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.reactivestreams.Subscription; +import org.springframework.cloud.sleuth.api.Span; import org.springframework.cloud.sleuth.instrument.web.client.TraceExchangeFilterFunction.TraceWebClientSubscription; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.web.reactive.function.client.WebClient; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.verify; - /** * @author Marcin Grzejszczak */ @@ -84,11 +83,11 @@ public class TraceWebClientBeanPostProcessorTest { traceSubscription.request(1); traceSubscription.cancel(); - verify(span).error(TraceWebClientSubscription.CANCELLED_ERROR); - verify(span).finish(); + Mockito.verify(span).error(TraceWebClientSubscription.CANCELLED_ERROR); + Mockito.verify(span).end(); // Check that the ref is clear following span completion - assertThat(traceSubscription.pendingSpan.get()).isNull(); + Assertions.assertThat(traceSubscription.pendingSpan.get()).isNull(); } @Test @@ -99,8 +98,8 @@ public class TraceWebClientBeanPostProcessorTest { traceSubscription.request(1); traceSubscription.cancel(); - verify(subscription).request(1); - verify(subscription).cancel(); + Mockito.verify(subscription).request(1); + Mockito.verify(subscription).cancel(); } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceWebClientDisabledTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientDisabledTests.java similarity index 63% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceWebClientDisabledTests.java rename to spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientDisabledTests.java index 9a4d09995..cd8aa0b41 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceWebClientDisabledTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientDisabledTests.java @@ -14,12 +14,16 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web; +package org.springframework.cloud.sleuth.instrument.web.client; import org.junit.jupiter.api.Test; +import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.config.GatewayAutoConfiguration; +import org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration; +import org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration; import org.springframework.context.annotation.Configuration; /** @@ -34,8 +38,9 @@ public class TraceWebClientDisabledTests { } - @Configuration - @EnableAutoConfiguration + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class, + GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class }) public static class Config { } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignObjectWrapperTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignObjectWrapperTests.java index 202353ac6..404c6a781 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignObjectWrapperTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignObjectWrapperTests.java @@ -17,10 +17,13 @@ package org.springframework.cloud.sleuth.instrument.web.client.feign; import feign.Client; +import org.assertj.core.api.Assertions; +import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.BeanFactory; @@ -29,11 +32,6 @@ import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerProper import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient; import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.BDDAssertions.then; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - /** * @author Marcin Grzejszczak */ @@ -48,39 +46,40 @@ public class TracingFeignObjectWrapperTests { @Test public void should_wrap_a_client_into_lazy_trace_client() { - then(this.traceFeignObjectWrapper.wrap(mock(Client.class))).isExactlyInstanceOf(LazyTracingFeignClient.class); + BDDAssertions.then(this.traceFeignObjectWrapper.wrap(Mockito.mock(Client.class))) + .isExactlyInstanceOf(LazyTracingFeignClient.class); } @Test public void should_not_wrap_a_bean_that_is_not_feign_related() { String notFeignRelatedObject = "object"; - then(this.traceFeignObjectWrapper.wrap(notFeignRelatedObject)).isSameAs(notFeignRelatedObject); + BDDAssertions.then(this.traceFeignObjectWrapper.wrap(notFeignRelatedObject)).isSameAs(notFeignRelatedObject); } // gh-1528 @Test public void should_wrap_feign_loadbalancer_client() { - Client delegate = mock(Client.class); - BlockingLoadBalancerClient loadBalancerClient = mock(BlockingLoadBalancerClient.class); - when(beanFactory.getBean(LoadBalancerClient.class)).thenReturn(loadBalancerClient); + Client delegate = Mockito.mock(Client.class); + BlockingLoadBalancerClient loadBalancerClient = Mockito.mock(BlockingLoadBalancerClient.class); + Mockito.when(beanFactory.getBean(LoadBalancerClient.class)).thenReturn(loadBalancerClient); Object wrapped = traceFeignObjectWrapper .wrap(new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient, new LoadBalancerProperties())); - assertThat(wrapped).isInstanceOf(TraceFeignBlockingLoadBalancerClient.class); + Assertions.assertThat(wrapped).isInstanceOf(TraceFeignBlockingLoadBalancerClient.class); } // gh-1528, gh-1125 @Test public void should_wrap_subclass_of_feign_loadbalancer_client() { - Client delegate = mock(Client.class); - BlockingLoadBalancerClient loadBalancerClient = mock(BlockingLoadBalancerClient.class); - when(beanFactory.getBean(LoadBalancerClient.class)).thenReturn(loadBalancerClient); + Client delegate = Mockito.mock(Client.class); + BlockingLoadBalancerClient loadBalancerClient = Mockito.mock(BlockingLoadBalancerClient.class); + Mockito.when(beanFactory.getBean(LoadBalancerClient.class)).thenReturn(loadBalancerClient); Object wrapped = traceFeignObjectWrapper .wrap(new TestFeignBlockingLoadBalancerClient(delegate, loadBalancerClient)); - assertThat(wrapped).isInstanceOf(TraceFeignBlockingLoadBalancerClient.class); + Assertions.assertThat(wrapped).isInstanceOf(TraceFeignBlockingLoadBalancerClient.class); } diff --git a/spring-cloud-sleuth-core/src/test/resources/application.yml b/spring-cloud-sleuth-core/src/test/resources/application.yml index 1c75fb5cf..2a3ed553c 100644 --- a/spring-cloud-sleuth-core/src/test/resources/application.yml +++ b/spring-cloud-sleuth-core/src/test/resources/application.yml @@ -1,9 +1,5 @@ -eureka.client.enabled: false - -spring.sleuth.scheduled.skipPattern: "^org.*TestBeanWithScheduledMethodToBeIgnored$" - logging.level.org.springframework.cloud: DEBUG logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR -logging.level.org.springframework.cloud.sleuth.instrument.web.client.feign: TRACE +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE -spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.cloud.gateway.config.GatewayAutoConfiguration, org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration +spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.cloud.gateway.config.GatewayAutoConfiguration, org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration, org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration, org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration diff --git a/spring-cloud-sleuth-dependencies/pom.xml b/spring-cloud-sleuth-dependencies/pom.xml index 4a898fe50..8dd60ac3a 100644 --- a/spring-cloud-sleuth-dependencies/pom.xml +++ b/spring-cloud-sleuth-dependencies/pom.xml @@ -34,6 +34,7 @@ 5.12.3 0.37.2 4.0.0 + 0.9.1 @@ -42,6 +43,16 @@ spring-cloud-sleuth-core ${project.version} + + org.springframework.cloud + spring-cloud-sleuth-brave + ${project.version} + + + org.springframework.cloud + spring-cloud-sleuth-otel + ${project.version} + org.springframework.cloud spring-cloud-sleuth-zipkin @@ -57,6 +68,11 @@ spring-cloud-starter-sleuth ${project.version} + + org.springframework.cloud + spring-cloud-starter-sleuth-otel + ${project.version} + io.zipkin.brave @@ -76,6 +92,14 @@ + + + io.opentelemetry + opentelemetry-bom + ${opentelemetry.version} + pom + import + io.github.lognet diff --git a/spring-cloud-sleuth-otel/pom.xml b/spring-cloud-sleuth-otel/pom.xml new file mode 100644 index 000000000..ead521cdd --- /dev/null +++ b/spring-cloud-sleuth-otel/pom.xml @@ -0,0 +1,134 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-otel + jar + Spring Cloud Sleuth OTel + Spring Cloud Sleuth OpenTelemetry + + + org.springframework.cloud + spring-cloud-sleuth + 3.0.0-SNAPSHOT + .. + + + + + + + com.google.auto.value + auto-value-annotations + 1.7.4 + compile + + + + org.springframework.cloud + spring-cloud-sleuth-core + + + org.springframework.boot + spring-boot-starter + + + + org.springframework + spring-jcl + + + io.opentelemetry + opentelemetry-sdk-tracing + + + io.opentelemetry + opentelemetry-sdk-baggage + + + io.opentelemetry.instrumentation + opentelemetry-instrumentation-api + + + io.opentelemetry + * + + + io.opentelemetry.instrumentation + * + + + + + io.opentelemetry + opentelemetry-extension-auto-annotations + true + + + io.opentelemetry + opentelemetry-exporters-logging + true + + + io.opentelemetry + opentelemetry-extension-trace-propagators + true + + + io.opentelemetry + opentelemetry-opentracing-shim + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.assertj + assertj-core + test + + + + + + fast + + false + + + + + maven-surefire-plugin + + 4 + true + -Xmx1024m -XX:MaxPermSize=256m + + + + + + + + diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/autoconfig/OtelProperties.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/autoconfig/OtelProperties.java new file mode 100644 index 000000000..f069b134f --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/autoconfig/OtelProperties.java @@ -0,0 +1,150 @@ +/* + * Copyright 2013-2020 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.otel.autoconfig; + +import io.opentelemetry.sdk.trace.config.TraceConfig; +import io.opentelemetry.trace.Span; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Sleuth settings for OpenTelemetry. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +@ConfigurationProperties("spring.sleuth.otel.config") +class OtelProperties { + + /** + * Instrumentation name to be used to find a Tracer. + */ + private String instrumentationName = "org.springframework.cloud.spring-cloud-sleuth"; + + /** + * Instrumentation version to be used to find a Tracer. + */ + private String instrumentationVersion; + + /** + * Sets the global default {@code Sampler} value. + */ + private double traceIdRatioBased = 0.1; + + /** + * Returns the global default max number of attributes per {@link Span}. + */ + private int maxAttrs = TraceConfig.getDefault().getMaxNumberOfAttributes(); + + /** + * Returns the global default max number of events per {@link Span}. + */ + private int maxEvents = TraceConfig.getDefault().getMaxNumberOfEvents(); + + /** + * Returns the global default max number of link entries per {@link Span}. + */ + private int maxLinks = TraceConfig.getDefault().getMaxNumberOfLinks(); + + /** + * Returns the global default max number of attributes per event. + */ + private int maxEventAttrs = TraceConfig.getDefault().getMaxNumberOfAttributesPerEvent(); + + /** + * Returns the global default max number of attributes per link. + */ + private int maxLinkAttrs = TraceConfig.getDefault().getMaxNumberOfAttributesPerLink(); + + /** + * Returns the global default max length of string attribute value in characters. + */ + private int maxAttrLength = TraceConfig.getDefault().getMaxLengthOfAttributeValues(); + + public String getInstrumentationName() { + return this.instrumentationName; + } + + public void setInstrumentationName(String instrumentationName) { + this.instrumentationName = instrumentationName; + } + + public String getInstrumentationVersion() { + return instrumentationVersion; + } + + public void setInstrumentationVersion(String instrumentationVersion) { + this.instrumentationVersion = instrumentationVersion; + } + + public double getTraceIdRatioBased() { + return this.traceIdRatioBased; + } + + public void setTraceIdRatioBased(int traceIdRatioBased) { + this.traceIdRatioBased = traceIdRatioBased; + } + + public int getMaxAttrs() { + return this.maxAttrs; + } + + public void setMaxAttrs(int maxAttrs) { + this.maxAttrs = maxAttrs; + } + + public int getMaxEvents() { + return this.maxEvents; + } + + public void setMaxEvents(int maxEvents) { + this.maxEvents = maxEvents; + } + + public int getMaxLinks() { + return this.maxLinks; + } + + public void setMaxLinks(int maxLinks) { + this.maxLinks = maxLinks; + } + + public int getMaxEventAttrs() { + return this.maxEventAttrs; + } + + public void setMaxEventAttrs(int maxEventAttrs) { + this.maxEventAttrs = maxEventAttrs; + } + + public int getMaxLinkAttrs() { + return this.maxLinkAttrs; + } + + public void setMaxLinkAttrs(int maxLinkAttrs) { + this.maxLinkAttrs = maxLinkAttrs; + } + + public int getMaxAttrLength() { + return this.maxAttrLength; + } + + public void setMaxAttrLength(int maxAttrLength) { + this.maxAttrLength = maxAttrLength; + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/autoconfig/TraceOtelAutoConfiguration.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/autoconfig/TraceOtelAutoConfiguration.java new file mode 100644 index 000000000..f58c4bbc1 --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/autoconfig/TraceOtelAutoConfiguration.java @@ -0,0 +1,126 @@ +/* + * Copyright 2013-2020 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.otel.autoconfig; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import io.opentelemetry.baggage.BaggageManager; +import io.opentelemetry.baggage.spi.BaggageManagerFactory; +import io.opentelemetry.sdk.baggage.spi.BaggageManagerFactorySdk; +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; +import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.sdk.trace.TracerSdkProvider; +import io.opentelemetry.sdk.trace.config.TraceConfig; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import io.opentelemetry.sdk.trace.spi.TracerProviderFactorySdk; +import io.opentelemetry.trace.Tracer; +import io.opentelemetry.trace.TracerProvider; +import io.opentelemetry.trace.spi.TracerProviderFactory; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.cloud.sleuth.otel.exporter.SpanExporterCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration + * Auto-configuration} to enable tracing via Spring Cloud Sleuth and OpenTelemetry SDK. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(value = "spring.sleuth.enabled", havingValue = "true", matchIfMissing = true) +@AutoConfigureBefore(TraceAutoConfiguration.class) +@EnableConfigurationProperties(OtelProperties.class) +public class TraceOtelAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + TracerProviderFactory otelTracerProviderFactory() { + return new TracerProviderFactorySdk(); + } + + @Bean + @ConditionalOnMissingBean + TracerProvider otelTracerProvider(TracerProviderFactory tracerProviderFactory) { + return tracerProviderFactory.create(); + } + + @Bean + @ConditionalOnMissingBean + BaggageManagerFactory otelBaggageManagerFactory() { + return new BaggageManagerFactorySdk(); + } + + @Bean + @ConditionalOnMissingBean + BaggageManager otelBaggageManager(BaggageManagerFactory baggageManagerFactory) { + return baggageManagerFactory.create(); + } + + @Bean + @ConditionalOnMissingBean + TraceConfig otelTracerConfig(OtelProperties otelProperties, Sampler sampler) { + return TraceConfig.getDefault().toBuilder().setMaxLengthOfAttributeValues(otelProperties.getMaxAttrLength()) + .setMaxNumberOfAttributes(otelProperties.getMaxAttrs()) + .setMaxNumberOfAttributesPerEvent(otelProperties.getMaxEventAttrs()) + .setMaxNumberOfAttributesPerLink(otelProperties.getMaxLinkAttrs()) + .setMaxNumberOfEvents(otelProperties.getMaxEvents()).setMaxNumberOfLinks(otelProperties.getMaxLinks()) + .setSampler(sampler).build(); + } + + @Bean + @ConditionalOnMissingBean + Tracer otelTracer(TracerProvider tracerProvider, ObjectProvider tracerSdkObjectProvider, + TraceConfig traceConfig, OtelProperties otelProperties, ObjectProvider> spanProcessors, + ObjectProvider> spanExporters, SpanExporterCustomizer spanExporterCustomizer) { + tracerSdkObjectProvider.ifAvailable(tracerSdkProvider -> { + List processors = spanProcessors.getIfAvailable(ArrayList::new); + processors.addAll(spanExporters.getIfAvailable(ArrayList::new).stream() + .map(e -> SimpleSpanProcessor.newBuilder(spanExporterCustomizer.customize(e)).build()) + .collect(Collectors.toList())); + processors.forEach(tracerSdkProvider::addSpanProcessor); + tracerSdkProvider.updateActiveTraceConfig(traceConfig); + }); + return tracerProvider.get(otelProperties.getInstrumentationName()); + } + + @Bean + @ConditionalOnMissingBean + Sampler otelSampler(OtelProperties otelProperties) { + return Samplers.traceIdRatioBased(otelProperties.getTraceIdRatioBased()); + } + + @Bean + @ConditionalOnMissingBean + SpanExporterCustomizer noOpSleuthSpanFilterConverter() { + return new SpanExporterCustomizer() { + + }; + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/BigendianEncoding.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/BigendianEncoding.java new file mode 100644 index 000000000..320d14363 --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/BigendianEncoding.java @@ -0,0 +1,84 @@ +/* + * Copyright 2013-2020 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.otel.bridge; + +import java.util.Arrays; + +import io.opentelemetry.internal.Utils; + +/** + * Copied from io.opentelemetry.trace.BigendianEncoding. + */ +final class BigendianEncoding { + + static final int LONG_BYTES = Long.SIZE / Byte.SIZE; + static final int BYTE_BASE16 = 2; + static final int LONG_BASE16 = BYTE_BASE16 * LONG_BYTES; + + private static final String ALPHABET = "0123456789abcdef"; + + private static final int ASCII_CHARACTERS = 128; + + private static final byte[] DECODING = buildDecodingArray(); + + private static byte[] buildDecodingArray() { + byte[] decoding = new byte[ASCII_CHARACTERS]; + Arrays.fill(decoding, (byte) -1); + for (int i = 0; i < ALPHABET.length(); i++) { + char c = ALPHABET.charAt(i); + decoding[c] = (byte) i; + } + return decoding; + } + + /** + * Returns the {@code long} value whose base16 representation is stored in the first + * 16 chars of {@code chars} starting from the {@code offset}. + * @param chars the base16 representation of the {@code long}. + */ + static long longFromBase16String(CharSequence chars) { + return longFromBase16String(chars, 0); + } + + /** + * Returns the {@code long} value whose base16 representation is stored in the first + * 16 chars of {@code chars} starting from the {@code offset}. + * @param chars the base16 representation of the {@code long}. + */ + static long longFromBase16String(CharSequence chars, int offset) { + Utils.checkArgument(chars.length() >= offset + LONG_BASE16, "chars too small"); + return (decodeByte(chars.charAt(offset), chars.charAt(offset + 1)) & 0xFFL) << 56 + | (decodeByte(chars.charAt(offset + 2), chars.charAt(offset + 3)) & 0xFFL) << 48 + | (decodeByte(chars.charAt(offset + 4), chars.charAt(offset + 5)) & 0xFFL) << 40 + | (decodeByte(chars.charAt(offset + 6), chars.charAt(offset + 7)) & 0xFFL) << 32 + | (decodeByte(chars.charAt(offset + 8), chars.charAt(offset + 9)) & 0xFFL) << 24 + | (decodeByte(chars.charAt(offset + 10), chars.charAt(offset + 11)) & 0xFFL) << 16 + | (decodeByte(chars.charAt(offset + 12), chars.charAt(offset + 13)) & 0xFFL) << 8 + | (decodeByte(chars.charAt(offset + 14), chars.charAt(offset + 15)) & 0xFFL); + } + + private static byte decodeByte(char hi, char lo) { + Utils.checkArgument(lo < ASCII_CHARACTERS && DECODING[lo] != -1, "invalid character " + lo); + Utils.checkArgument(hi < ASCII_CHARACTERS && DECODING[hi] != -1, "invalid character " + hi); + int decoded = DECODING[hi] << 4 | DECODING[lo]; + return (byte) decoded; + } + + private BigendianEncoding() { + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelBaggageEntry.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelBaggageEntry.java new file mode 100644 index 000000000..6c5f5bc2e --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelBaggageEntry.java @@ -0,0 +1,137 @@ +/* + * Copyright 2013-2020 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.otel.bridge; + +import java.util.concurrent.atomic.AtomicReference; + +import io.grpc.Context; +import io.opentelemetry.baggage.BaggageManager; +import io.opentelemetry.baggage.BaggageUtils; +import io.opentelemetry.baggage.EntryMetadata; +import io.opentelemetry.trace.Tracer; + +import org.springframework.cloud.sleuth.api.BaggageEntry; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationEventPublisher; + +/** + * OpenTelemetry implementation of a {@link BaggageEntry}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +// TODO: [OTEL] Experimental - doesn't really work +public class OtelBaggageEntry implements BaggageEntry { + + private final Tracer tracer; + + private final ApplicationEventPublisher publisher; + + private final SleuthBaggageProperties sleuthBaggageProperties; + + private final io.opentelemetry.baggage.Baggage delegate; + + private final io.opentelemetry.baggage.BaggageManager manager; + + private final String name; + + private final EntryMetadata entryMetadata; + + private final AtomicReference context; + + public OtelBaggageEntry(Tracer tracer, ApplicationEventPublisher publisher, + SleuthBaggageProperties sleuthBaggageProperties, io.opentelemetry.baggage.Baggage delegate, + BaggageManager manager, AtomicReference context, String name, EntryMetadata entryMetadata) { + this.tracer = tracer; + this.publisher = publisher; + this.sleuthBaggageProperties = sleuthBaggageProperties; + this.delegate = delegate; + this.manager = manager; + this.name = name; + this.entryMetadata = entryMetadata; + this.context = context; + } + + @Override + public String name() { + return this.name; + } + + @Override + public String get() { + return BaggageUtils.getBaggage(this.context.get()).getEntryValue(this.name); + } + + @Override + public String get(TraceContext traceContext) { + // TODO: [OTEL] Discuss this with OTEL + return null; + } + + @Override + public void set(String value) { + io.opentelemetry.baggage.Baggage baggage = this.manager.baggageBuilder().setParent(this.delegate) + .put(this.name, value, this.entryMetadata).build(); + this.context.set(BaggageUtils.withBaggage(baggage, this.context.get())); + if (this.sleuthBaggageProperties.getTagFields().stream().map(String::toLowerCase) + .anyMatch(s -> s.equals(this.name))) { + this.tracer.getCurrentSpan().setAttribute(this.name, value); + } + this.publisher.publishEvent(new BaggageChanged(this, this.name, value)); + } + + @Override + public void set(TraceContext traceContext, String value) { + // TODO: [OTEL] Discuss this with OTEL + } + + public static class BaggageChanged extends ApplicationEvent { + + /** + * Baggage entry name. + */ + public String name; + + /** + * Baggage entry value. + */ + public String value; + + public BaggageChanged(OtelBaggageEntry source, String name, String value) { + super(source); + this.name = name; + this.value = value; + } + + @Override + public String toString() { + return "BaggageChanged{" + "name='" + name + '\'' + ", value='" + value + '\'' + '}'; + } + + } + + public static class BaggageScopeEnded extends ApplicationEvent { + + public BaggageScopeEnded(Object source) { + super(source); + } + + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelBaggageManager.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelBaggageManager.java new file mode 100644 index 000000000..91c489a63 --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelBaggageManager.java @@ -0,0 +1,123 @@ +/* + * Copyright 2013-2020 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.otel.bridge; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; + +import io.grpc.Context; +import io.opentelemetry.baggage.BaggageUtils; +import io.opentelemetry.baggage.Entry; +import io.opentelemetry.baggage.EntryMetadata; +import io.opentelemetry.trace.Tracer; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.cloud.sleuth.api.BaggageEntry; +import org.springframework.cloud.sleuth.api.BaggageManager; +import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationListener; + +/** + * OpenTelemetry implementation of a {@link BaggageManager}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class OtelBaggageManager implements BaggageManager, ApplicationListener { + + private static final Log log = LogFactory.getLog(OtelBaggageManager.class); + + private final io.opentelemetry.trace.Tracer tracer; + + private final io.opentelemetry.baggage.BaggageManager delegate; + + private final SleuthBaggageProperties sleuthBaggageProperties; + + private final ApplicationEventPublisher publisher; + + AtomicReference context = new AtomicReference<>(Context.ROOT); + + public OtelBaggageManager(Tracer tracer, io.opentelemetry.baggage.BaggageManager delegate, + SleuthBaggageProperties sleuthBaggageProperties, ApplicationEventPublisher publisher) { + this.tracer = tracer; + this.delegate = delegate; + this.sleuthBaggageProperties = sleuthBaggageProperties; + this.publisher = publisher; + } + + public Map getAllBaggage() { + Map baggage = new HashMap<>(); + currentBaggage().getEntries().forEach(entry -> baggage.put(entry.getKey(), entry.getValue())); + return baggage; + } + + private io.opentelemetry.baggage.Baggage currentBaggage() { + return BaggageUtils.getBaggage(Context.current()); + } + + public BaggageEntry getBaggage(String name) { + io.opentelemetry.baggage.Baggage baggage = currentBaggage(); + Entry entry = entryForName(name, baggage); + if (entry == null) { + return null; + } + return otelBaggage(name, baggage, entry); + } + + private Entry entryForName(String name, io.opentelemetry.baggage.Baggage baggage) { + return baggage.getEntries().stream().filter(e -> e.getKey().toLowerCase().equals(name.toLowerCase())) + .findFirst().orElse(null); + } + + private BaggageEntry otelBaggage(String name, io.opentelemetry.baggage.Baggage baggage, Entry entry) { + return new OtelBaggageEntry(this.tracer, this.publisher, this.sleuthBaggageProperties, baggage, this.delegate, + this.context, name, entry.getEntryMetadata()); + } + + public BaggageEntry createBaggage(String name) { + return baggageWithValue(name, ""); + } + + private BaggageEntry baggageWithValue(String name, String value) { + List remoteFieldsFields = this.sleuthBaggageProperties.getRemoteFields(); + boolean remoteField = remoteFieldsFields.stream().map(String::toLowerCase) + .anyMatch(s -> s.equals(name.toLowerCase())); + EntryMetadata.EntryTtl entryTtl = EntryMetadata.EntryTtl.NO_PROPAGATION; + if (remoteField) { + entryTtl = EntryMetadata.EntryTtl.UNLIMITED_PROPAGATION; + } + EntryMetadata entryMetadata = EntryMetadata.create(entryTtl); + io.opentelemetry.baggage.Baggage baggage = this.delegate.baggageBuilder().put(name, value, entryMetadata) + .build(); + this.context.set(BaggageUtils.withBaggage(baggage, this.context.get())); + return new OtelBaggageEntry(this.tracer, this.publisher, this.sleuthBaggageProperties, baggage, this.delegate, + this.context, name, entryMetadata); + } + + @Override + public void onApplicationEvent(OtelBaggageEntry.BaggageScopeEnded event) { + if (log.isTraceEnabled()) { + log.trace("Baggage scope ended"); + } + this.context.set(Context.ROOT); + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelCurrentTraceContext.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelCurrentTraceContext.java new file mode 100644 index 000000000..01c8a090a --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelCurrentTraceContext.java @@ -0,0 +1,183 @@ +/* + * Copyright 2013-2020 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.otel.bridge; + +import java.util.concurrent.LinkedBlockingDeque; + +import io.opentelemetry.trace.DefaultSpan; +import io.opentelemetry.trace.Span; +import io.opentelemetry.trace.SpanContext; +import io.opentelemetry.trace.Tracer; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.lang.Nullable; + +/** + * OpenTelemetry implementation of a {@link CurrentTraceContext}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class OtelCurrentTraceContext implements CurrentTraceContext { + + private static final Log log = LogFactory.getLog(OtelCurrentTraceContext.class); + + final Tracer tracer; + + private final ApplicationEventPublisher publisher; + + public OtelCurrentTraceContext(Tracer tracer, ApplicationEventPublisher publisher) { + this.tracer = tracer; + this.publisher = publisher; + } + + @Override + public TraceContext get() { + Span currentSpan = this.tracer.getCurrentSpan(); + if (DefaultSpan.getInvalid().equals(currentSpan)) { + return null; + } + return new OtelTraceContext(currentSpan); + } + + @Override + public Scope newScope(TraceContext context) { + OtelTraceContext otelTraceContext = (OtelTraceContext) context; + SpanContext spanContext = otelTraceContext.delegate; + Span fromContext = new SpanFromSpanContext(((OtelTraceContext) context).span, spanContext, otelTraceContext); + this.publisher.publishEvent(new ScopeChanged(this, context)); + return new OtelScope(new OtelSpanInScope(this.tracer.withSpan(fromContext), spanContext), publisher); + } + + @Override + public Scope maybeScope(TraceContext context) { + if (log.isTraceEnabled()) { + log.trace("Will check if new scope should be created for context [" + context + "]"); + } + if (context == null || SpanContext.getInvalid().equals(OtelTraceContext.toOtel(context))) { + if (log.isTraceEnabled()) { + log.trace("Invalid context - will return noop"); + } + return new OtelScope.RevertToPrevious(this.publisher, null); + } + OtelTraceContext otelTraceContext = (OtelTraceContext) context; + Span fromContext = new SpanFromSpanContext(otelTraceContext.span, otelTraceContext.delegate, otelTraceContext); + Span currentSpan = this.tracer.getCurrentSpan(); + if (log.isTraceEnabled()) { + log.trace("Span from context [" + fromContext + "], current span [" + currentSpan + "]"); + } + if (traceAndSpanIdsAreEqual(fromContext, currentSpan)) { + if (log.isTraceEnabled()) { + log.trace("Same context as the current one - will return noop"); + } + return new OtelScope.RevertToPrevious(this.publisher, context); + } + return newScope(context); + } + + private boolean traceAndSpanIdsAreEqual(Span fromContext, Span currentSpan) { + return fromContext.getContext().getTraceIdAsHexString().equals(currentSpan.getContext().getTraceIdAsHexString()) + && fromContext.getContext().getSpanIdAsHexString() + .equals(currentSpan.getContext().getSpanIdAsHexString()); + } + + public static class ScopeChanged extends ApplicationEvent { + + /** + * Trace context corresponding to the changed scope. Might be {@code null}. + */ + public final TraceContext context; + + /** + * Create a new {@code ApplicationEvent}. + * @param source the object on which the event initially occurred or with which + * the event is associated (never {@code null}) + * @param context corresponding trace context + */ + public ScopeChanged(Object source, @Nullable TraceContext context) { + super(source); + this.context = context; + } + + } + + public static class ScopeClosed extends ApplicationEvent { + + /** + * Create a new {@code ApplicationEvent}. + * @param source the object on which the event initially occurred or with which + * the event is associated (never {@code null}) + */ + public ScopeClosed(Object source) { + super(source); + } + + } + +} + +class OtelScope implements CurrentTraceContext.Scope { + + private final OtelSpanInScope delegate; + + private final ApplicationEventPublisher publisher; + + OtelScope(OtelSpanInScope delegate, ApplicationEventPublisher publisher) { + this.delegate = delegate; + this.publisher = publisher; + } + + @Override + public void close() { + this.delegate.close(); + this.publisher.publishEvent(new OtelCurrentTraceContext.ScopeClosed(this)); + this.publisher.publishEvent(new OtelBaggageEntry.BaggageScopeEnded(this)); + } + + static class RevertToPrevious implements CurrentTraceContext.Scope { + + private static final Log log = LogFactory.getLog(RevertToPrevious.class); + + private static final LinkedBlockingDeque CONTEXTS = new LinkedBlockingDeque<>(); + + private final ApplicationEventPublisher publisher; + + RevertToPrevious(ApplicationEventPublisher publisher, TraceContext previous) { + this.publisher = publisher; + if (previous != null && !previous.equals(CONTEXTS.peekFirst())) { + CONTEXTS.addFirst(previous); + } + } + + @Override + public void close() { + publisher.publishEvent(new OtelCurrentTraceContext.ScopeClosed(this)); + TraceContext context = CONTEXTS.pollFirst(); + if (log.isTraceEnabled()) { + log.trace("Reverting scope to [" + context + "]"); + } + publisher.publishEvent(new OtelCurrentTraceContext.ScopeChanged(this, context)); + } + + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelFinishedSpan.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelFinishedSpan.java new file mode 100644 index 000000000..a303d1c23 --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelFinishedSpan.java @@ -0,0 +1,154 @@ +/* + * Copyright 2013-2020 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.otel.bridge; + +import java.util.AbstractMap; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import io.opentelemetry.common.AttributeConsumer; +import io.opentelemetry.common.AttributeKey; +import io.opentelemetry.common.Attributes; +import io.opentelemetry.sdk.trace.data.SpanData; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; + +/** + * OpenTelemetry implementation of a {@link FinishedSpan}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class OtelFinishedSpan implements FinishedSpan { + + private final SpanData spanData; + + private final Map tags = new HashMap<>(); + + public OtelFinishedSpan(SpanData spanData) { + this.spanData = spanData; + } + + @Override + public String name() { + return this.spanData.getName(); + } + + @Override + public long startTimestamp() { + return this.spanData.getStartEpochNanos(); + } + + @Override + public long endTimestamp() { + return this.spanData.getEndEpochNanos(); + } + + @Override + public Map tags() { + if (this.tags.isEmpty()) { + this.spanData.getAttributes().forEach(new AttributeConsumer() { + @Override + public void consume(AttributeKey key, T value) { + tags.put(key.getKey(), String.valueOf(value)); + } + }); + } + return this.tags; + } + + @Override + public Collection> events() { + return this.spanData.getEvents().stream() + .map(e -> new AbstractMap.SimpleEntry<>(e.getEpochNanos(), e.getName())).collect(Collectors.toList()); + } + + @Override + public String spanId() { + return this.spanData.getSpanId(); + } + + @Override + public String parentId() { + return this.spanData.getParentSpanId(); + } + + @Override + public String remoteIp() { + return tags().get("net.peer.name"); + } + + @Override + public int remotePort() { + return Integer.valueOf(tags().get("net.peer.port")); + } + + @Override + public String traceId() { + return this.spanData.getTraceId(); + } + + @Override + public Throwable error() { + Attributes attributes = this.spanData.getEvents().stream().filter(e -> e.getName().equals("exception")) + .findFirst().map(e -> e.getAttributes()).orElse(null); + if (attributes != null) { + return new AssertingThrowable(attributes); + } + return null; + } + + @Override + public Span.Kind kind() { + if (this.spanData.getKind() == io.opentelemetry.trace.Span.Kind.INTERNAL) { + return null; + } + return Span.Kind.valueOf(this.spanData.getKind().name()); + } + + @Override + public String remoteServiceName() { + return this.spanData.getAttributes().get(AttributeKey.stringKey("peer.service")); + } + + @Override + public String toString() { + return "SpanDataToReportedSpan{" + "spanData=" + spanData + ", tags=" + tags + '}'; + } + + public static FinishedSpan fromOtel(SpanData span) { + return new OtelFinishedSpan(span); + } + + public static class AssertingThrowable extends Throwable { + + /** + * Attritbues set on the span. + */ + public final Attributes attributes; + + AssertingThrowable(Attributes attributes) { + super(attributes.get(AttributeKey.stringKey("exception.message"))); + this.attributes = attributes; + } + + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelPropagator.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelPropagator.java new file mode 100644 index 000000000..a67f5a9cf --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelPropagator.java @@ -0,0 +1,70 @@ +/* + * Copyright 2013-2020 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.otel.bridge; + +import java.util.List; + +import io.grpc.Context; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.trace.DefaultSpan; +import io.opentelemetry.trace.Tracer; +import io.opentelemetry.trace.TracingContextUtils; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.propagation.Propagator; + +/** + * OpenTelemetry implementation of a {@link Propagator}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class OtelPropagator implements Propagator { + + private final TextMapPropagator propagator; + + private final Tracer tracer; + + public OtelPropagator(ContextPropagators propagation, Tracer tracer) { + this.propagator = propagation.getTextMapPropagator(); + this.tracer = tracer; + } + + @Override + public List fields() { + return this.propagator.fields(); + } + + @Override + public void inject(TraceContext traceContext, C carrier, Setter setter) { + Context context = OtelTraceContext.toOtelContext(traceContext); + this.propagator.inject(context, carrier, setter::set); + } + + @Override + public Span.Builder extract(C carrier, Getter getter) { + Context extracted = this.propagator.extract(Context.current(), carrier, getter::get); + io.opentelemetry.trace.Span span = TracingContextUtils.getSpanWithoutDefault(extracted); + if (span == null || span.equals(DefaultSpan.getInvalid())) { + return OtelSpanBuilder.fromOtel(tracer.spanBuilder("")); + } + return OtelSpanBuilder.fromOtel(this.tracer.spanBuilder("").setParent(extracted)); + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelScopedSpan.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelScopedSpan.java new file mode 100644 index 000000000..7ee95c032 --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelScopedSpan.java @@ -0,0 +1,82 @@ +/* + * Copyright 2013-2020 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.otel.bridge; + +import io.opentelemetry.context.Scope; +import io.opentelemetry.trace.Span; + +import org.springframework.cloud.sleuth.api.ScopedSpan; +import org.springframework.cloud.sleuth.api.TraceContext; + +/** + * OpenTelemetry implementation of a {@link ScopedSpan}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class OtelScopedSpan implements ScopedSpan { + + final Span span; + + final Scope scope; + + public OtelScopedSpan(Span span, Scope scope) { + this.span = span; + this.scope = scope; + } + + @Override + public boolean isNoop() { + return !this.span.isRecording(); + } + + @Override + public TraceContext context() { + return new OtelTraceContext(this.span); + } + + @Override + public ScopedSpan name(String name) { + this.span.updateName(name); + return this; + } + + @Override + public ScopedSpan tag(String key, String value) { + this.span.setAttribute(key, value); + return this; + } + + @Override + public ScopedSpan event(String value) { + this.span.addEvent(value); + return this; + } + + @Override + public ScopedSpan error(Throwable throwable) { + this.span.recordException(throwable); + return this; + } + + @Override + public void end() { + this.scope.close(); + this.span.end(); + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelSpan.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelSpan.java new file mode 100644 index 000000000..24bede8e8 --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelSpan.java @@ -0,0 +1,245 @@ +/* + * Copyright 2013-2020 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.otel.bridge; + +import java.util.Objects; + +import io.opentelemetry.common.AttributeKey; +import io.opentelemetry.common.Attributes; +import io.opentelemetry.trace.EndSpanOptions; +import io.opentelemetry.trace.SpanContext; +import io.opentelemetry.trace.StatusCanonicalCode; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.lang.Nullable; + +/** + * OpenTelemetry implementation of a {@link Span}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class OtelSpan implements Span { + + final io.opentelemetry.trace.Span delegate; + + public OtelSpan(io.opentelemetry.trace.Span delegate) { + this.delegate = delegate; + } + + @Override + public boolean isNoop() { + return !this.delegate.isRecording(); + } + + @Override + public TraceContext context() { + if (this.delegate == null) { + return null; + } + return new OtelTraceContext(this.delegate.getContext(), this.delegate); + } + + @Override + public Span start() { + // they are already started via the builder + return this; + } + + @Override + public Span name(String name) { + this.delegate.updateName(name); + return new OtelSpan(this.delegate); + } + + @Override + public Span event(String value) { + this.delegate.addEvent(value); + return new OtelSpan(this.delegate); + } + + @Override + public Span tag(String key, String value) { + this.delegate.setAttribute(key, value); + return new OtelSpan(this.delegate); + } + + @Override + public Span error(Throwable throwable) { + this.delegate.recordException(throwable); + return new OtelSpan(this.delegate); + } + + @Override + public void end() { + this.delegate.end(); + } + + @Override + public void abandon() { + // TODO: [OTEL] doesn't seem to have this notion yet + } + + @Override + public String toString() { + return this.delegate != null ? this.delegate.toString() : "null"; + } + + public static io.opentelemetry.trace.Span toOtel(Span span) { + return ((OtelSpan) span).delegate; + } + + public static Span fromOtel(io.opentelemetry.trace.Span span) { + return new OtelSpan(span); + } + +} + +class SpanFromSpanContext implements io.opentelemetry.trace.Span { + + final io.opentelemetry.trace.Span span; + + final SpanContext newSpanContext; + + final OtelTraceContext otelTraceContext; + + SpanFromSpanContext(io.opentelemetry.trace.Span span, SpanContext newSpanContext, + OtelTraceContext otelTraceContext) { + this.span = span; + this.newSpanContext = newSpanContext; + this.otelTraceContext = otelTraceContext; + } + + @Override + public void setAttribute(String key, @Nullable String value) { + span.setAttribute(key, value); + } + + @Override + public void setAttribute(String key, long value) { + span.setAttribute(key, value); + } + + @Override + public void setAttribute(String key, double value) { + span.setAttribute(key, value); + } + + @Override + public void setAttribute(String key, boolean value) { + span.setAttribute(key, value); + } + + @Override + public void addEvent(String name) { + span.addEvent(name); + } + + @Override + public void addEvent(String name, long timestamp) { + span.addEvent(name, timestamp); + } + + @Override + public void addEvent(String name, Attributes attributes) { + span.addEvent(name, attributes); + } + + @Override + public void addEvent(String name, Attributes attributes, long timestamp) { + span.addEvent(name, attributes, timestamp); + } + + @Override + public void setAttribute(AttributeKey key, int value) { + span.setAttribute(key, value); + } + + @Override + public void setAttribute(AttributeKey key, T value) { + span.setAttribute(key, value); + } + + @Override + public void setStatus(StatusCanonicalCode canonicalCode) { + span.setStatus(canonicalCode); + } + + @Override + public void setStatus(StatusCanonicalCode canonicalCode, String description) { + span.setStatus(canonicalCode, description); + } + + @Override + public void recordException(Throwable exception) { + span.recordException(exception); + } + + @Override + public void recordException(Throwable exception, Attributes additionalAttributes) { + span.recordException(exception, additionalAttributes); + } + + @Override + public void updateName(String name) { + span.updateName(name); + } + + @Override + public void end() { + span.end(); + } + + @Override + public void end(EndSpanOptions endOptions) { + span.end(endOptions); + } + + @Override + public SpanContext getContext() { + return newSpanContext; + } + + @Override + public boolean isRecording() { + return span.isRecording(); + } + + @Override + public String toString() { + return "SpanFromSpanContext{" + "span=" + span + ", newSpanContext=" + newSpanContext + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SpanFromSpanContext that = (SpanFromSpanContext) o; + return Objects.equals(span, that.span) && Objects.equals(this.newSpanContext, that.newSpanContext); + } + + @Override + public int hashCode() { + return Objects.hash(this.span, this.newSpanContext); + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelSpanBuilder.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelSpanBuilder.java new file mode 100644 index 000000000..3142fa7b5 --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelSpanBuilder.java @@ -0,0 +1,130 @@ +/* + * Copyright 2013-2020 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.otel.bridge; + +import java.util.LinkedList; +import java.util.List; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.util.StringUtils; + +/** + * OpenTelemetry implementation of a {@link Span.Builder}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class OtelSpanBuilder implements Span.Builder { + + private final io.opentelemetry.trace.Span.Builder delegate; + + private String name; + + private Throwable error; + + private final List annotations = new LinkedList<>(); + + public OtelSpanBuilder(io.opentelemetry.trace.Span.Builder delegate) { + this.delegate = delegate; + } + + @Override + public Span.Builder setParent(TraceContext context) { + this.delegate.setParent(OtelTraceContext.toOtelContext(context)); + return this; + } + + @Override + public Span.Builder setNoParent() { + this.delegate.setNoParent(); + return this; + } + + @Override + public Span.Builder name(String name) { + this.name = name; + return this; + } + + @Override + public Span.Builder event(String value) { + this.annotations.add(value); + return this; + } + + @Override + public Span.Builder tag(String key, String value) { + this.delegate.setAttribute(key, value); + return this; + } + + @Override + public Span.Builder error(Throwable throwable) { + this.error = throwable; + return this; + } + + @Override + public Span.Builder kind(Span.Kind spanKind) { + if (spanKind == null) { + this.delegate.setSpanKind(io.opentelemetry.trace.Span.Kind.INTERNAL); + return this; + } + io.opentelemetry.trace.Span.Kind kind = io.opentelemetry.trace.Span.Kind.INTERNAL; + switch (spanKind) { + case CLIENT: + kind = io.opentelemetry.trace.Span.Kind.CLIENT; + break; + case SERVER: + kind = io.opentelemetry.trace.Span.Kind.SERVER; + break; + case PRODUCER: + kind = io.opentelemetry.trace.Span.Kind.PRODUCER; + break; + case CONSUMER: + kind = io.opentelemetry.trace.Span.Kind.CONSUMER; + break; + } + this.delegate.setSpanKind(kind); + return this; + } + + @Override + public Span.Builder remoteServiceName(String remoteServiceName) { + this.delegate.setAttribute("peer.service", remoteServiceName); + return this; + } + + @Override + public Span start() { + io.opentelemetry.trace.Span span = this.delegate.startSpan(); + if (StringUtils.hasText(this.name)) { + span.updateName(this.name); + } + if (this.error != null) { + span.recordException(error); + } + this.annotations.forEach(span::addEvent); + return OtelSpan.fromOtel(span); + } + + public static Span.Builder fromOtel(io.opentelemetry.trace.Span.Builder builder) { + return new OtelSpanBuilder(builder); + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelSpanCustomizer.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelSpanCustomizer.java new file mode 100644 index 000000000..afa381feb --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelSpanCustomizer.java @@ -0,0 +1,69 @@ +/* + * Copyright 2013-2020 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.otel.bridge; + +import io.opentelemetry.trace.Span; +import io.opentelemetry.trace.Tracer; + +import org.springframework.cloud.sleuth.api.SpanCustomizer; +import org.springframework.lang.NonNull; + +/** + * OpenTelemetry implementation of a {@link SpanCustomizer}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class OtelSpanCustomizer implements SpanCustomizer { + + private final Tracer tracer; + + private final Span span; + + public OtelSpanCustomizer(@NonNull Tracer tracer) { + this.tracer = tracer; + this.span = null; + } + + public OtelSpanCustomizer(@NonNull Span span) { + this.tracer = null; + this.span = span; + } + + @Override + public SpanCustomizer name(String name) { + currentSpan().updateName(name); + return this; + } + + private Span currentSpan() { + return this.span != null ? this.span : this.tracer.getCurrentSpan(); + } + + @Override + public SpanCustomizer tag(String key, String value) { + currentSpan().setAttribute(key, value); + return this; + } + + @Override + public SpanCustomizer event(String value) { + currentSpan().addEvent(value); + return this; + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelTraceContext.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelTraceContext.java new file mode 100644 index 000000000..b5d216e88 --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelTraceContext.java @@ -0,0 +1,120 @@ +/* + * Copyright 2013-2020 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.otel.bridge; + +import java.util.Objects; + +import io.grpc.Context; +import io.opentelemetry.sdk.trace.ReadableSpan; +import io.opentelemetry.trace.Span; +import io.opentelemetry.trace.SpanContext; +import io.opentelemetry.trace.TracingContextUtils; + +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.lang.Nullable; + +/** + * OpenTelemetry implementation of a {@link TraceContext}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class OtelTraceContext implements TraceContext { + + final SpanContext delegate; + + final Span span; + + public OtelTraceContext(SpanContext delegate, @Nullable Span span) { + this.delegate = delegate; + this.span = span; + } + + public OtelTraceContext(Span span) { + this(span.getContext(), span); + } + + @Override + public String traceId() { + return this.delegate.getTraceIdAsHexString(); + } + + @Override + @Nullable + public String parentId() { + if (this.span instanceof ReadableSpan) { + ReadableSpan readableSpan = (ReadableSpan) this.span; + return readableSpan.toSpanData().getParentSpanId(); + } + return null; + } + + @Override + public String spanId() { + return this.delegate.getSpanIdAsHexString(); + } + + @Override + public String toString() { + return this.delegate != null ? this.delegate.toString() : "null"; + } + + @Override + public boolean equals(Object o) { + return Objects.equals(this.delegate, o); + } + + @Override + public int hashCode() { + return Objects.hashCode(this.delegate); + } + + @Nullable + public Boolean sampled() { + return this.delegate.isSampled(); + } + + public Span span() { + return this.span; + } + + public SpanContext spanContext() { + return this.delegate; + } + + public static SpanContext toOtel(TraceContext traceContext) { + if (traceContext == null) { + return null; + } + return ((OtelTraceContext) traceContext).delegate; + } + + public static TraceContext fromOtel(SpanContext traceContext) { + return new OtelTraceContext(traceContext, null); + } + + public static Context toOtelContext(TraceContext context) { + if (context instanceof OtelTraceContext) { + Span span = ((OtelTraceContext) context).span; + if (span != null) { + return TracingContextUtils.withSpan(span, Context.current()); + } + } + return Context.current(); + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelTracer.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelTracer.java new file mode 100644 index 000000000..5e3c384eb --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/OtelTracer.java @@ -0,0 +1,138 @@ +/* + * Copyright 2013-2020 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.otel.bridge; + +import java.util.Map; + +import io.opentelemetry.context.Scope; +import io.opentelemetry.trace.DefaultSpan; +import io.opentelemetry.trace.SpanContext; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.cloud.sleuth.api.BaggageEntry; +import org.springframework.cloud.sleuth.api.ScopedSpan; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.SpanCustomizer; +import org.springframework.cloud.sleuth.api.Tracer; + +/** + * OpenTelemetry implementation of a {@link Tracer}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class OtelTracer implements Tracer { + + private final io.opentelemetry.trace.Tracer tracer; + + private final OtelBaggageManager otelBaggageManager; + + public OtelTracer(io.opentelemetry.trace.Tracer tracer, OtelBaggageManager otelBaggageManager) { + this.tracer = tracer; + this.otelBaggageManager = otelBaggageManager; + } + + @Override + public Span nextSpan(Span parent) { + if (parent == null) { + return nextSpan(); + } + return OtelSpan.fromOtel( + this.tracer.spanBuilder("").setParent(OtelTraceContext.toOtelContext(parent.context())).startSpan()); + } + + @Override + public SpanInScope withSpan(Span span) { + return new OtelSpanInScope( + tracer.withSpan(span == null ? DefaultSpan.getInvalid() : ((OtelSpan) span).delegate), + ((OtelSpan) span).delegate.getContext()); + } + + @Override + public SpanCustomizer currentSpanCustomizer() { + return new OtelSpanCustomizer(this.tracer); + } + + @Override + public Span currentSpan() { + io.opentelemetry.trace.Span currentSpan = this.tracer.getCurrentSpan(); + if (currentSpan == null || currentSpan.equals(DefaultSpan.getInvalid())) { + return null; + } + return new OtelSpan(currentSpan); + } + + @Override + public Span nextSpan() { + return new OtelSpan(this.tracer.spanBuilder("").startSpan()); + } + + @Override + public ScopedSpan startScopedSpan(String name) { + io.opentelemetry.trace.Span span = this.tracer.spanBuilder(name).startSpan(); + return new OtelScopedSpan(span, this.tracer.withSpan(span)); + } + + @Override + public Span.Builder spanBuilder() { + return new OtelSpanBuilder(this.tracer.spanBuilder("")); + } + + public static Tracer fromOtel(io.opentelemetry.trace.Tracer tracer, OtelBaggageManager otelBaggageManager) { + return new OtelTracer(tracer, otelBaggageManager); + } + + @Override + public Map getAllBaggage() { + return this.otelBaggageManager.getAllBaggage(); + } + + @Override + public BaggageEntry getBaggage(String name) { + return this.otelBaggageManager.getBaggage(name); + } + + @Override + public BaggageEntry createBaggage(String name) { + return this.otelBaggageManager.createBaggage(name); + } + +} + +class OtelSpanInScope implements Tracer.SpanInScope { + + private static final Log log = LogFactory.getLog(OtelSpanInScope.class); + + final Scope delegate; + + final SpanContext spanContext; + + OtelSpanInScope(Scope delegate, SpanContext spanContext) { + this.delegate = delegate; + this.spanContext = spanContext; + } + + @Override + public void close() { + if (log.isTraceEnabled()) { + log.trace("Will close scope for trace context [" + this.spanContext + "]"); + } + this.delegate.close(); + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/TraceOtelBridgeAutoConfiguation.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/TraceOtelBridgeAutoConfiguation.java new file mode 100644 index 000000000..580059eff --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/TraceOtelBridgeAutoConfiguation.java @@ -0,0 +1,80 @@ +/* + * Copyright 2013-2020 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.otel.bridge; + +import io.opentelemetry.baggage.BaggageManager; +import io.opentelemetry.context.propagation.ContextPropagators; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.SpanCustomizer; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.propagation.Propagator; +import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties; +import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.cloud.sleuth.otel.autoconfig.TraceOtelAutoConfiguration; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration + * Auto-configuration} to enable the bridge between Sleuth API and OpenTelemetry. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +@Configuration(proxyBeanMethods = true) +@ConditionalOnProperty(value = "spring.sleuth.enabled", matchIfMissing = true) +@ConditionalOnBean(io.opentelemetry.trace.Tracer.class) +@AutoConfigureAfter(TraceOtelAutoConfiguration.class) +@AutoConfigureBefore(TraceAutoConfiguration.class) +public class TraceOtelBridgeAutoConfiguation { + + @Bean + Tracer otelTracerBridge(io.opentelemetry.trace.Tracer tracer, BaggageManager baggageManager, + SleuthBaggageProperties sleuthBaggageProperties, ApplicationEventPublisher publisher) { + return new OtelTracer(tracer, + otelBaggageManagerBridge(tracer, baggageManager, sleuthBaggageProperties, publisher)); + } + + @Bean(autowireCandidate = false) + OtelBaggageManager otelBaggageManagerBridge(io.opentelemetry.trace.Tracer tracer, BaggageManager baggageManager, + SleuthBaggageProperties sleuthBaggageProperties, ApplicationEventPublisher publisher) { + return new OtelBaggageManager(tracer, baggageManager, sleuthBaggageProperties, publisher); + } + + @Bean + CurrentTraceContext otelCurrentTraceContext(io.opentelemetry.trace.Tracer tracer, + ApplicationEventPublisher publisher) { + return new OtelCurrentTraceContext(tracer, publisher); + } + + @Bean + SpanCustomizer otelSpanCustomizer(io.opentelemetry.trace.Tracer tracer) { + return new OtelSpanCustomizer(tracer); + } + + @Bean + Propagator otelPropagator(ContextPropagators contextPropagators, io.opentelemetry.trace.Tracer tracer) { + return new OtelPropagator(contextPropagators, tracer); + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/http/OtelHttpClientHandler.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/http/OtelHttpClientHandler.java new file mode 100644 index 000000000..76d147c6b --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/http/OtelHttpClientHandler.java @@ -0,0 +1,196 @@ +/* + * Copyright 2013-2020 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.otel.bridge.http; + +import java.net.URI; +import java.net.URISyntaxException; + +import io.opentelemetry.context.Scope; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer; +import io.opentelemetry.trace.DefaultSpan; +import io.opentelemetry.trace.Tracer; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.cloud.sleuth.api.SamplerFunction; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; +import org.springframework.cloud.sleuth.api.http.HttpClientRequest; +import org.springframework.cloud.sleuth.api.http.HttpClientResponse; +import org.springframework.cloud.sleuth.api.http.HttpRequest; +import org.springframework.cloud.sleuth.api.http.HttpRequestParser; +import org.springframework.cloud.sleuth.api.http.HttpResponseParser; +import org.springframework.cloud.sleuth.otel.bridge.OtelSpan; +import org.springframework.cloud.sleuth.otel.bridge.OtelTraceContext; +import org.springframework.lang.Nullable; +import org.springframework.util.StringUtils; + +/** + * OpenTelemetry implementation of a {@link HttpClientHandler}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class OtelHttpClientHandler extends HttpClientTracer + implements HttpClientHandler { + + private static final Log log = LogFactory.getLog(OtelHttpClientHandler.class); + + private final HttpRequestParser httpClientRequestParser; + + private final HttpResponseParser httpClientResponseParser; + + private final SamplerFunction samplerFunction; + + public OtelHttpClientHandler(Tracer tracer, @Nullable HttpRequestParser httpClientRequestParser, + @Nullable HttpResponseParser httpClientResponseParser, SamplerFunction samplerFunction) { + super(tracer); + this.httpClientRequestParser = httpClientRequestParser; + this.httpClientResponseParser = httpClientResponseParser; + this.samplerFunction = samplerFunction; + } + + @Override + public Span handleSend(HttpClientRequest request) { + if (Boolean.FALSE.equals(this.samplerFunction.trySample(request))) { + if (log.isDebugEnabled()) { + log.debug("The sampler function filtered this request, will return an invalid span"); + } + return OtelSpan.fromOtel(DefaultSpan.getInvalid()); + } + io.opentelemetry.trace.Span span = startSpan(request); + return span(request, span); + } + + @Override + public Span handleSend(HttpClientRequest request, TraceContext parent) { + if (Boolean.FALSE.equals(this.samplerFunction.trySample(request))) { + if (log.isDebugEnabled()) { + log.debug("Returning an invalid span since url [" + request.path() + "] is on a list of urls to skip"); + } + return OtelSpan.fromOtel(DefaultSpan.getInvalid()); + } + io.opentelemetry.trace.Span span = parent != null ? ((OtelTraceContext) parent).span() : null; + if (span == null) { + return span(request, startSpan(request)); + } + try (Scope scope = this.tracer.withSpan(span)) { + io.opentelemetry.trace.Span withParent = startSpan(request); + return span(request, withParent); + } + } + + private Span span(HttpClientRequest request, io.opentelemetry.trace.Span span) { + try (Scope scope = startScope(span, request)) { + if (span.isRecording()) { + String remoteIp = request.remoteIp(); + if (StringUtils.hasText(remoteIp)) { + span.setAttribute("net.peer.ip", remoteIp); + } + span.setAttribute("net.peer.port", request.remotePort()); + } + return OtelSpan.fromOtel(span); + } + } + + @Override + protected io.opentelemetry.trace.Span onRequest(io.opentelemetry.trace.Span span, + HttpClientRequest httpClientRequest) { + io.opentelemetry.trace.Span afterRequest = super.onRequest(span, httpClientRequest); + if (this.httpClientRequestParser != null) { + Span fromOtel = OtelSpan.fromOtel(afterRequest); + this.httpClientRequestParser.parse(httpClientRequest, fromOtel.context(), fromOtel); + } + String path = httpClientRequest.path(); + if (path != null) { + span.setAttribute("http.path", path); + } + return afterRequest; + } + + @Override + protected io.opentelemetry.trace.Span onResponse(io.opentelemetry.trace.Span span, + HttpClientResponse httpClientResponse) { + io.opentelemetry.trace.Span afterResponse = super.onResponse(span, httpClientResponse); + if (this.httpClientResponseParser != null) { + Span fromOtel = OtelSpan.fromOtel(afterResponse); + this.httpClientResponseParser.parse(httpClientResponse, fromOtel.context(), fromOtel); + } + return afterResponse; + } + + @Override + public void handleReceive(HttpClientResponse response, Span span) { + if (OtelSpan.toOtel(span).equals(DefaultSpan.getInvalid())) { + if (log.isDebugEnabled()) { + log.debug("Not doing anything cause the span is invalid"); + } + return; + } + io.opentelemetry.trace.Span otel = OtelSpan.toOtel(span); + if (response.error() != null) { + if (log.isDebugEnabled()) { + log.debug("There was an error, will finish span [" + otel + "] exceptionally"); + } + endExceptionally(otel, response, response.error()); + } + else { + if (log.isDebugEnabled()) { + log.debug("There was no error, will finish span [" + otel + "] in a standard way"); + } + end(otel, response); + } + } + + @Override + protected String method(HttpClientRequest httpClientRequest) { + return httpClientRequest.method(); + } + + @Override + protected URI url(HttpClientRequest httpClientRequest) throws URISyntaxException { + return URI.create(httpClientRequest.url()); + } + + @Override + protected Integer status(HttpClientResponse httpClientResponse) { + return httpClientResponse.statusCode(); + } + + @Override + protected String requestHeader(HttpClientRequest httpClientRequest, String s) { + return httpClientRequest.header(s); + } + + @Override + protected String responseHeader(HttpClientResponse httpClientResponse, String s) { + return httpClientResponse.header(s); + } + + @Override + protected TextMapPropagator.Setter getSetter() { + return HttpClientRequest::header; + } + + @Override + protected String getInstrumentationName() { + return "org.springframework.cloud.sleuth"; + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/http/OtelHttpServerHandler.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/http/OtelHttpServerHandler.java new file mode 100644 index 000000000..c7e3d138d --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/http/OtelHttpServerHandler.java @@ -0,0 +1,171 @@ +/* + * Copyright 2013-2020 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.otel.bridge.http; + +import java.net.URI; +import java.util.regex.Pattern; + +import io.grpc.Context; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.instrumentation.api.tracer.HttpServerTracer; +import io.opentelemetry.trace.DefaultSpan; +import io.opentelemetry.trace.Tracer; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.http.HttpRequest; +import org.springframework.cloud.sleuth.api.http.HttpRequestParser; +import org.springframework.cloud.sleuth.api.http.HttpResponseParser; +import org.springframework.cloud.sleuth.api.http.HttpServerHandler; +import org.springframework.cloud.sleuth.api.http.HttpServerRequest; +import org.springframework.cloud.sleuth.api.http.HttpServerResponse; +import org.springframework.cloud.sleuth.instrument.web.SkipPatternProvider; +import org.springframework.cloud.sleuth.otel.bridge.OtelSpan; +import org.springframework.util.StringUtils; + +/** + * OpenTelemetry implementation of a {@link HttpServerHandler}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public class OtelHttpServerHandler + extends HttpServerTracer + implements HttpServerHandler { + + private final HttpRequestParser httpServerRequestParser; + + private final HttpResponseParser httpServerResponseParser; + + private final Pattern pattern; + + public OtelHttpServerHandler(Tracer tracer, HttpRequestParser httpServerRequestParser, + HttpResponseParser httpServerResponseParser, SkipPatternProvider skipPatternProvider) { + super(tracer); + this.httpServerRequestParser = httpServerRequestParser; + this.httpServerResponseParser = httpServerResponseParser; + this.pattern = skipPatternProvider.skipPattern(); + } + + @Override + public Span handleReceive(HttpServerRequest request) { + String url = request.path(); + boolean shouldSkip = !StringUtils.isEmpty(url) && this.pattern.matcher(url).matches(); + if (shouldSkip) { + return OtelSpan.fromOtel(DefaultSpan.getInvalid()); + } + return OtelSpan.fromOtel(startSpan(request, request, request.method())); + } + + @Override + public void handleSend(HttpServerResponse response, Span span) { + Throwable throwable = response.error(); + io.opentelemetry.trace.Span otel = OtelSpan.toOtel(span); + parseResponse(span, response); + if (throwable == null) { + end(otel, response); + } + else { + endExceptionally(otel, throwable, response); + } + } + + private void parseResponse(Span span, HttpServerResponse response) { + if (this.httpServerResponseParser != null) { + this.httpServerResponseParser.parse(response, span.context(), span); + } + } + + @Override + protected void onConnectionAndRequest(io.opentelemetry.trace.Span span, HttpServerRequest connection, + HttpServerRequest request) { + super.onConnectionAndRequest(span, connection, request); + if (this.httpServerRequestParser != null) { + Span fromOtel = OtelSpan.fromOtel(span); + this.httpServerRequestParser.parse(request, fromOtel.context(), fromOtel); + } + } + + @Override + protected void onRequest(io.opentelemetry.trace.Span span, HttpServerRequest request) { + super.onRequest(span, request); + String path = request.path(); + if (StringUtils.hasText(path)) { + span.setAttribute("http.path", path); + } + } + + @Override + public Context getServerContext(HttpServerRequest request) { + Object context = request.getAttribute(CONTEXT_ATTRIBUTE); + return context instanceof Context ? (Context) context : null; + } + + @Override + protected Integer peerPort(HttpServerRequest request) { + return toUri(request).getPort(); + } + + @Override + protected String peerHostIP(HttpServerRequest request) { + return toUri(request).getHost(); + } + + @Override + protected String flavor(HttpServerRequest request, HttpServerRequest request2) { + return toUri(request).getScheme(); + } + + @Override + protected TextMapPropagator.Getter getGetter() { + return HttpRequest::header; + } + + @Override + protected String url(HttpServerRequest request) { + return request.url(); + } + + protected URI toUri(HttpServerRequest request) { + return URI.create(request.url()); + } + + @Override + protected String method(HttpServerRequest request) { + return request.method(); + } + + @Override + protected String requestHeader(HttpServerRequest request, String s) { + return request.header(s); + } + + @Override + protected int responseStatus(HttpServerResponse httpServerResponse) { + return httpServerResponse.statusCode(); + } + + @Override + protected void attachServerContext(Context context, HttpServerRequest request) { + request.setAttribute(CONTEXT_ATTRIBUTE, context); + } + + @Override + protected String getInstrumentationName() { + return "org.springframework.cloud.sleuth"; + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/http/TraceOtelHttpBridgeAutoConfiguration.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/http/TraceOtelHttpBridgeAutoConfiguration.java new file mode 100644 index 000000000..324ae8952 --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/bridge/http/TraceOtelHttpBridgeAutoConfiguration.java @@ -0,0 +1,101 @@ +/* + * Copyright 2013-2020 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.otel.bridge.http; + +import java.util.regex.Pattern; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +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.api.SamplerFunction; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; +import org.springframework.cloud.sleuth.api.http.HttpRequest; +import org.springframework.cloud.sleuth.api.http.HttpRequestParser; +import org.springframework.cloud.sleuth.api.http.HttpResponseParser; +import org.springframework.cloud.sleuth.api.http.HttpServerHandler; +import org.springframework.cloud.sleuth.instrument.web.HttpClientRequestParser; +import org.springframework.cloud.sleuth.instrument.web.HttpClientResponseParser; +import org.springframework.cloud.sleuth.instrument.web.HttpClientSampler; +import org.springframework.cloud.sleuth.instrument.web.HttpServerRequestParser; +import org.springframework.cloud.sleuth.instrument.web.HttpServerResponseParser; +import org.springframework.cloud.sleuth.instrument.web.SkipPatternConfiguration; +import org.springframework.cloud.sleuth.instrument.web.SkipPatternProvider; +import org.springframework.cloud.sleuth.instrument.web.SleuthWebProperties; +import org.springframework.cloud.sleuth.instrument.web.TraceHttpAutoConfiguration; +import org.springframework.cloud.sleuth.otel.bridge.TraceOtelBridgeAutoConfiguation; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.lang.Nullable; + +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(value = "spring.sleuth.enabled", matchIfMissing = true) +@ConditionalOnBean({ Tracer.class, io.opentelemetry.trace.Tracer.class }) +@AutoConfigureBefore(TraceHttpAutoConfiguration.class) +@AutoConfigureAfter({ TraceOtelBridgeAutoConfiguation.class, SkipPatternConfiguration.class }) +public class TraceOtelHttpBridgeAutoConfiguration { + + @Bean + HttpClientHandler otelHttpClientHandler(io.opentelemetry.trace.Tracer tracer, + @Nullable @HttpClientRequestParser HttpRequestParser httpClientRequestParser, + @Nullable @HttpClientResponseParser HttpResponseParser httpClientResponseParser, + SamplerFunction samplerFunction) { + return new OtelHttpClientHandler(tracer, httpClientRequestParser, httpClientResponseParser, samplerFunction); + } + + @Bean + HttpServerHandler otelHttpServerHandler(io.opentelemetry.trace.Tracer tracer, + @Nullable @HttpServerRequestParser HttpRequestParser httpServerRequestParser, + @Nullable @HttpServerResponseParser HttpResponseParser httpServerResponseParser, + SkipPatternProvider skipPatternProvider) { + return new OtelHttpServerHandler(tracer, httpServerRequestParser, httpServerResponseParser, + skipPatternProvider); + } + + @Bean + @ConditionalOnMissingBean(name = HttpClientSampler.NAME) + SamplerFunction defaultHttpClientSampler(SleuthWebProperties sleuthWebProperties) { + String skipPattern = sleuthWebProperties.getClient().getSkipPattern(); + if (skipPattern == null) { + return SamplerFunction.deferDecision(); + } + return new SkipPatternSampler(Pattern.compile(skipPattern)); + } + +} + +class SkipPatternSampler implements SamplerFunction { + + private final Pattern pattern; + + SkipPatternSampler(Pattern pattern) { + this.pattern = pattern; + } + + @Override + public final Boolean trySample(HttpRequest request) { + String url = request.path(); + boolean shouldSkip = this.pattern.matcher(url).matches(); + if (shouldSkip) { + return false; + } + return null; + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/exporter/ArrayListSpanProcessor.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/exporter/ArrayListSpanProcessor.java new file mode 100644 index 000000000..5e5ba09d1 --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/exporter/ArrayListSpanProcessor.java @@ -0,0 +1,86 @@ +/* + * Copyright 2013-2020 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.otel.exporter; + +import java.util.Collection; +import java.util.Queue; +import java.util.concurrent.LinkedBlockingQueue; + +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.trace.ReadWriteSpan; +import io.opentelemetry.sdk.trace.ReadableSpan; +import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.export.SpanExporter; + +public class ArrayListSpanProcessor implements SpanProcessor, SpanExporter { + + Queue spans = new LinkedBlockingQueue<>(); + + @Override + public void onStart(ReadWriteSpan span) { + + } + + @Override + public boolean isStartRequired() { + return false; + } + + @Override + public void onEnd(ReadableSpan span) { + this.spans.add(span.toSpanData()); + } + + @Override + public boolean isEndRequired() { + return true; + } + + @Override + public CompletableResultCode export(Collection spans) { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode flush() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode shutdown() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode forceFlush() { + return CompletableResultCode.ofSuccess(); + } + + public SpanData takeLocalSpan() { + return this.spans.poll(); + } + + public Queue spans() { + return this.spans; + } + + public void clear() { + this.spans.clear(); + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/exporter/CompositeSpanExporter.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/exporter/CompositeSpanExporter.java new file mode 100644 index 000000000..8747ae0c1 --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/exporter/CompositeSpanExporter.java @@ -0,0 +1,64 @@ +/* + * Copyright 2013-2020 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.otel.exporter; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.trace.data.SpanData; + +import org.springframework.cloud.sleuth.api.exporter.SpanFilter; +import org.springframework.cloud.sleuth.otel.bridge.OtelFinishedSpan; + +class CompositeSpanExporter implements io.opentelemetry.sdk.trace.export.SpanExporter { + + private final io.opentelemetry.sdk.trace.export.SpanExporter delegate; + + private final List filters; + + CompositeSpanExporter(io.opentelemetry.sdk.trace.export.SpanExporter delegate, List filters) { + this.delegate = delegate; + this.filters = filters; + } + + @Override + public CompletableResultCode export(Collection spans) { + return this.delegate.export(spans.stream().filter(this::shouldProcess).collect(Collectors.toList())); + } + + private boolean shouldProcess(SpanData span) { + for (SpanFilter exporter : this.filters) { + if (!exporter.isExportable(OtelFinishedSpan.fromOtel(span))) { + return false; + } + } + return true; + } + + @Override + public CompletableResultCode flush() { + return this.delegate.flush(); + } + + @Override + public CompletableResultCode shutdown() { + return this.delegate.shutdown(); + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/exporter/OtelExporterProperties.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/exporter/OtelExporterProperties.java new file mode 100644 index 000000000..adf33e9fa --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/exporter/OtelExporterProperties.java @@ -0,0 +1,60 @@ +/* + * Copyright 2013-2020 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.otel.exporter; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Sleuth settings for OpenTelemetry. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +@ConfigurationProperties("spring.sleuth.otel.exporter") +public class OtelExporterProperties { + + private SleuthSpanFilter sleuthSpanFilter = new SleuthSpanFilter(); + + public SleuthSpanFilter getSleuthSpanFilter() { + return this.sleuthSpanFilter; + } + + public void setSleuthSpanFilter(SleuthSpanFilter sleuthSpanFilter) { + this.sleuthSpanFilter = sleuthSpanFilter; + } + + /** + * Integrations with core Sleuth handler mechanism. + */ + public static class SleuthSpanFilter { + + /** + * This application service name. + */ + private boolean enabled = true; + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/exporter/SpanExporterCustomizer.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/exporter/SpanExporterCustomizer.java new file mode 100644 index 000000000..2b35fc3ef --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/exporter/SpanExporterCustomizer.java @@ -0,0 +1,38 @@ +/* + * Copyright 2013-2020 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.otel.exporter; + +import io.opentelemetry.sdk.trace.export.SpanExporter; + +/** + * Allows customization of a {@link SpanExporter}. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +public interface SpanExporterCustomizer { + + /** + * Customizes a span exporter. + * @param spanExporter to customize + * @return customized span exporter + */ + default SpanExporter customize(SpanExporter spanExporter) { + return spanExporter; + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/exporter/TraceOtelExporterAutoConfiguration.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/exporter/TraceOtelExporterAutoConfiguration.java new file mode 100644 index 000000000..c75782f4f --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/exporter/TraceOtelExporterAutoConfiguration.java @@ -0,0 +1,60 @@ +/* + * Copyright 2013-2020 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.otel.exporter; + +import java.util.List; + +import io.opentelemetry.sdk.trace.export.SpanExporter; + +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.sleuth.api.exporter.SpanFilter; +import org.springframework.cloud.sleuth.otel.autoconfig.TraceOtelAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration + * Auto-configuration} to enable OpenTelemetry exporters. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(value = "spring.sleuth.enabled", matchIfMissing = true) +@AutoConfigureBefore(TraceOtelAutoConfiguration.class) +@EnableConfigurationProperties(OtelExporterProperties.class) +public class TraceOtelExporterAutoConfiguration { + + @Configuration(proxyBeanMethods = false) + static class SleuthExporterConfiguration { + + @Bean + @ConditionalOnProperty(value = "spring.sleuth.otel.exporter.sleuth-span-filter.enabled", matchIfMissing = true) + SpanExporterCustomizer sleuthSpanFilterConverter(List spanFilters) { + return new SpanExporterCustomizer() { + @Override + public SpanExporter customize(SpanExporter spanExporter) { + return new CompositeSpanExporter(spanExporter, spanFilters); + } + }; + } + + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/log/OtelLogProperties.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/log/OtelLogProperties.java new file mode 100644 index 000000000..5be96cb38 --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/log/OtelLogProperties.java @@ -0,0 +1,84 @@ +/* + * Copyright 2013-2020 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.otel.log; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Sleuth settings for OpenTelemetry. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +@ConfigurationProperties("spring.sleuth.otel.log") +class OtelLogProperties { + + private Exporter exporter = new Exporter(); + + private Slf4j slf4j = new Slf4j(); + + public Exporter getExporter() { + return this.exporter; + } + + public void setExporter(Exporter exporter) { + this.exporter = exporter; + } + + public Slf4j getSlf4j() { + return this.slf4j; + } + + public void setSlf4j(Slf4j slf4j) { + this.slf4j = slf4j; + } + + public static class Exporter { + + /** + * Enable log support for Otel. + */ + private boolean enabled = false; + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + } + + public static class Slf4j { + + /** + * Enable log support for Otel. + */ + private boolean enabled; + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/log/Slf4jSpanProcessor.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/log/Slf4jSpanProcessor.java new file mode 100644 index 000000000..ea206e7cc --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/log/Slf4jSpanProcessor.java @@ -0,0 +1,159 @@ +/* + * Copyright 2013-2020 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.otel.log; + +import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import io.opentelemetry.baggage.BaggageManager; +import io.opentelemetry.baggage.Entry; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.trace.ReadWriteSpan; +import io.opentelemetry.sdk.trace.ReadableSpan; +import io.opentelemetry.sdk.trace.SpanProcessor; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.slf4j.MDC; + +import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties; +import org.springframework.cloud.sleuth.otel.bridge.OtelBaggageEntry; +import org.springframework.cloud.sleuth.otel.bridge.OtelCurrentTraceContext; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; + +class Slf4jSpanProcessor implements SpanProcessor, ApplicationListener { + + private static final Log log = LogFactory.getLog(Slf4jSpanProcessor.class); + + private final SleuthBaggageProperties sleuthBaggageProperties; + + private final BaggageManager baggageManager; + + Slf4jSpanProcessor(SleuthBaggageProperties sleuthBaggageProperties, BaggageManager baggageManager) { + this.sleuthBaggageProperties = sleuthBaggageProperties; + this.baggageManager = baggageManager; + } + + @Override + public void onStart(ReadWriteSpan span) { + onStart(span.getContext().getTraceIdAsHexString(), span.getContext().getSpanIdAsHexString()); + } + + private void onStart(String traceId, String spanId) { + MDC.put("traceId", traceId); + MDC.put("spanId", spanId); + flushAllBaggageEntries(); + } + + @Override + public boolean isStartRequired() { + return true; + } + + @Override + public void onEnd(ReadableSpan span) { + MDC.remove("traceId"); + MDC.remove("spanId"); + removeAllBaggageEntries(); + } + + @Override + public boolean isEndRequired() { + return true; + } + + @Override + public CompletableResultCode shutdown() { + onEnd(null); + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode forceFlush() { + flushAllBaggageEntries(); + return CompletableResultCode.ofSuccess(); + } + + private void flushAllBaggageEntries() { + onEachCorrelatedBaggageEntry(e -> MDC.put(e.getKey(), e.getValue())); + } + + private void onEachCorrelatedBaggageEntry(Consumer consumer) { + if (this.sleuthBaggageProperties.isCorrelationEnabled()) { + List correlationFields = lowerCaseCorrelationFields(); + this.baggageManager.getCurrentBaggage().getEntries().stream() + .filter(e -> correlationFields.contains(e.getKey().toLowerCase())).forEach(consumer); + } + } + + private void removeAllBaggageEntries() { + onEachCorrelatedBaggageEntry(e -> MDC.remove(e.getKey())); + } + + private List lowerCaseCorrelationFields() { + return this.sleuthBaggageProperties.getCorrelationFields().stream().map(String::toLowerCase) + .collect(Collectors.toList()); + } + + private void onBaggageChanged(OtelBaggageEntry.BaggageChanged event) { + if (log.isTraceEnabled()) { + log.trace("Got baggage changed event [" + event + "]"); + } + if (this.sleuthBaggageProperties.isCorrelationEnabled() + && lowerCaseCorrelationFields().contains(event.name.toLowerCase())) { + if (log.isTraceEnabled()) { + log.trace("Correlation enabled and baggage with name [" + event.name + + "] is present on the list of correlated fields"); + } + MDC.put(event.name, event.value); + } + } + + private void onScopeChanged(OtelCurrentTraceContext.ScopeChanged event) { + if (log.isTraceEnabled()) { + log.trace("Got scope changed event [" + event + "]"); + } + if (event.context != null) { + onStart(event.context.traceId(), event.context.spanId()); + } + } + + private void onScopeClosed(OtelCurrentTraceContext.ScopeClosed event) { + if (log.isTraceEnabled()) { + log.trace("Got scope closed event [" + event + "]"); + } + onEnd(null); + } + + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (event instanceof OtelBaggageEntry.BaggageChanged) { + onBaggageChanged((OtelBaggageEntry.BaggageChanged) event); + } + else if (event instanceof OtelCurrentTraceContext.ScopeChanged) { + onScopeChanged((OtelCurrentTraceContext.ScopeChanged) event); + } + else if (event instanceof OtelCurrentTraceContext.ScopeClosed) { + onScopeClosed((OtelCurrentTraceContext.ScopeClosed) event); + } + else if (event instanceof OtelBaggageEntry.BaggageScopeEnded) { + removeAllBaggageEntries(); + } + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/log/TraceOtelLogAutoConfiguration.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/log/TraceOtelLogAutoConfiguration.java new file mode 100644 index 000000000..e8173256d --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/log/TraceOtelLogAutoConfiguration.java @@ -0,0 +1,73 @@ +/* + * Copyright 2013-2020 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.otel.log; + +import io.opentelemetry.baggage.BaggageManager; +import io.opentelemetry.exporters.logging.LoggingSpanExporter; +import io.opentelemetry.trace.Tracer; +import org.slf4j.MDC; + +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties; +import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration + * Auto-configuration} to enable logging configuration via Spring Cloud Sleuth and + * OpenTelemetry SDK. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnBean(Tracer.class) +@AutoConfigureBefore(TraceAutoConfiguration.class) +@EnableConfigurationProperties(OtelLogProperties.class) +public class TraceOtelLogAutoConfiguration { + + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(MDC.class) + @ConditionalOnProperty(value = "spring.sleuth.otel.log.slf4j.enabled", matchIfMissing = true) + static class Slf4jConfiguration { + + @Bean + Slf4jSpanProcessor otelSlf4jSpanProcessor(SleuthBaggageProperties sleuthBaggageProperties, + BaggageManager baggageManager) { + return new Slf4jSpanProcessor(sleuthBaggageProperties, baggageManager); + } + + } + + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(LoggingSpanExporter.class) + @ConditionalOnProperty("spring.sleuth.otel.log.exporter.enabled") + static class LoggingExporterConfiguration { + + @Bean + LoggingSpanExporter otelLoggingSpanExporter() { + return new LoggingSpanExporter(); + } + + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/opentracing/OpentracingAutoConfiguration.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/opentracing/OpentracingAutoConfiguration.java new file mode 100644 index 000000000..7847aaa63 --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/opentracing/OpentracingAutoConfiguration.java @@ -0,0 +1,57 @@ +/* + * Copyright 2013-2020 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.otel.opentracing; + +import io.opentelemetry.baggage.BaggageManager; +import io.opentelemetry.opentracingshim.TraceShim; +import io.opentelemetry.trace.TracerProvider; +import io.opentracing.Tracer; + +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.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.sleuth.opentracing.SleuthOpentracingProperties; +import org.springframework.cloud.sleuth.otel.autoconfig.TraceOtelAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration + * Auto-configuration} to enable tracing via Opentracing. + * + * @author Spencer Gibb + * @author Marcin Grzejszczak + * @since 2.0.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(value = "spring.sleuth.opentracing.enabled", matchIfMissing = true) +@ConditionalOnBean(io.opentelemetry.trace.Tracer.class) +@ConditionalOnClass(TraceShim.class) +@AutoConfigureAfter(TraceOtelAutoConfiguration.class) +@EnableConfigurationProperties(SleuthOpentracingProperties.class) +class OpentracingAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + Tracer sleuthOpenTracing(TracerProvider tracerProvider, BaggageManager contextManager) { + return TraceShim.createTracerShim(tracerProvider, contextManager); + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/propagation/BaggageTextMapPropagator.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/propagation/BaggageTextMapPropagator.java new file mode 100644 index 000000000..87800f405 --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/propagation/BaggageTextMapPropagator.java @@ -0,0 +1,93 @@ +/* + * Copyright 2013-2020 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.otel.propagation; + +import java.util.AbstractMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import io.grpc.Context; +import io.opentelemetry.baggage.Baggage; +import io.opentelemetry.baggage.BaggageUtils; +import io.opentelemetry.baggage.EntryMetadata; +import io.opentelemetry.context.propagation.TextMapPropagator; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.cloud.sleuth.api.BaggageManager; +import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties; +import org.springframework.context.ApplicationEventPublisher; + +// TODO: [OTEL] Experimental - doesn't really work +class BaggageTextMapPropagator implements TextMapPropagator { + + private static final Log log = LogFactory.getLog(BaggageTextMapPropagator.class); + + private final SleuthBaggageProperties properties; + + private final io.opentelemetry.baggage.BaggageManager otelBaggageManager; + + private final BaggageManager baggageManager; + + private final ApplicationEventPublisher publisher; + + BaggageTextMapPropagator(SleuthBaggageProperties properties, + io.opentelemetry.baggage.BaggageManager otelBaggageManager, BaggageManager baggageManager, + ApplicationEventPublisher publisher) { + this.properties = properties; + this.otelBaggageManager = otelBaggageManager; + this.baggageManager = baggageManager; + this.publisher = publisher; + } + + @Override + public List fields() { + return this.properties.getRemoteFields(); + } + + @Override + public void inject(Context context, C c, Setter setter) { + List> baggageEntries = applicableBaggageEntries(); + baggageEntries.forEach(e -> setter.set(c, e.getKey(), e.getValue())); + } + + private List> applicableBaggageEntries() { + Map allBaggage = this.baggageManager.getAllBaggage(); + List lowerCaseKeys = this.properties.getRemoteFields().stream().map(String::toLowerCase) + .collect(Collectors.toList()); + return allBaggage.entrySet().stream().filter(e -> lowerCaseKeys.contains(e.getKey().toLowerCase())) + .collect(Collectors.toList()); + } + + @Override + public Context extract(Context context, C c, Getter getter) { + Map baggageEntries = this.properties.getRemoteFields().stream() + .map(s -> new AbstractMap.SimpleEntry<>(s, getter.get(c, s))).filter(e -> e.getValue() != null) + .collect(Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue())); + Baggage.Builder builder = otelBaggageManager.baggageBuilder().setParent(context); + baggageEntries.forEach((key, value) -> builder.put(key, value, + EntryMetadata.create(EntryMetadata.EntryTtl.UNLIMITED_PROPAGATION))); + Baggage baggage = builder.build(); + Context withBaggage = BaggageUtils.withBaggage(baggage, context); + if (log.isDebugEnabled()) { + log.debug("Will propagate new baggage context for entries " + baggageEntries); + } + return withBaggage; + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/propagation/OtelPropagationProperties.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/propagation/OtelPropagationProperties.java new file mode 100644 index 000000000..04911082c --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/propagation/OtelPropagationProperties.java @@ -0,0 +1,54 @@ +/* + * Copyright 2013-2020 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.otel.propagation; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Sleuth settings for OpenTelemetry. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +@ConfigurationProperties("spring.sleuth.otel.propagation") +class OtelPropagationProperties { + + private SleuthBaggage sleuthBaggage = new SleuthBaggage(); + + public SleuthBaggage getSleuthBaggage() { + return this.sleuthBaggage; + } + + public void setSleuthBaggage(SleuthBaggage sleuthBaggage) { + this.sleuthBaggage = sleuthBaggage; + } + + public static class SleuthBaggage { + + private boolean enabled = true; + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/propagation/SleuthPropagationProperties.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/propagation/SleuthPropagationProperties.java new file mode 100644 index 000000000..4ef6ea647 --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/propagation/SleuthPropagationProperties.java @@ -0,0 +1,80 @@ +/* + * Copyright 2013-2020 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.otel.propagation; + +import java.util.Collections; +import java.util.List; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Sleuth settings for OpenTelemetry. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +@ConfigurationProperties("spring.sleuth.propagation") +public class SleuthPropagationProperties { + + /** + * Type of propagation. + */ + private List type = Collections.singletonList(PropagationType.B3); + + public List getType() { + return this.type; + } + + public void setType(List type) { + this.type = type; + } + + public enum PropagationType { + + /** + * AWS propagation type. + */ + AWS, + + /** + * B3 propagation type. + */ + B3, + + /** + * Jaeger propagation type. + */ + JAEGER, + + /** + * Lightstep propagation type. + */ + OT_TRACER, + + /** + * W3C propagation type. + */ + W3C, + + /** + * Custom propagation type. + */ + CUSTOM + + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/propagation/TraceOtelPropagationAutoConfiguration.java b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/propagation/TraceOtelPropagationAutoConfiguration.java new file mode 100644 index 000000000..ccef7d265 --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/java/org/springframework/cloud/sleuth/otel/propagation/TraceOtelPropagationAutoConfiguration.java @@ -0,0 +1,192 @@ +/* + * Copyright 2013-2020 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.otel.propagation; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import io.grpc.Context; +import io.opentelemetry.OpenTelemetry; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.context.propagation.DefaultContextPropagators; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.extensions.trace.propagation.AwsXRayPropagator; +import io.opentelemetry.extensions.trace.propagation.B3Propagator; +import io.opentelemetry.extensions.trace.propagation.JaegerPropagator; +import io.opentelemetry.extensions.trace.propagation.OtTracerPropagator; +import io.opentelemetry.extensions.trace.propagation.TraceMultiPropagator; +import io.opentelemetry.trace.Span; +import io.opentelemetry.trace.Tracer; +import io.opentelemetry.trace.TracingContextUtils; +import io.opentelemetry.trace.propagation.HttpTraceContext; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.sleuth.api.BaggageManager; +import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties; +import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.cloud.sleuth.otel.bridge.TraceOtelBridgeAutoConfiguation; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.ClassUtils; + +/** + * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration + * Auto-configuration} to enable propagation configuration via Spring Cloud Sleuth and + * OpenTelemetry SDK. + * + * @author Marcin Grzejszczak + * @since 3.0.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnBean(Tracer.class) +@AutoConfigureBefore({ TraceAutoConfiguration.class, TraceOtelBridgeAutoConfiguation.class }) +@EnableConfigurationProperties({ SleuthPropagationProperties.class, OtelPropagationProperties.class }) +public class TraceOtelPropagationAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + ContextPropagators otelContextPropagators(ObjectProvider> propagators) { + List mapPropagators = propagators.getIfAvailable(ArrayList::new); + if (mapPropagators.isEmpty()) { + return OpenTelemetry.getPropagators(); + } + DefaultContextPropagators.Builder builder = DefaultContextPropagators.builder(); + mapPropagators.forEach(builder::addTextMapPropagator); + OpenTelemetry.setPropagators(builder.build()); + return OpenTelemetry.getPropagators(); + } + + @Configuration(proxyBeanMethods = false) + static class PropagatorsConfiguration { + + @Bean + CompositeTextMapPropagator compositeTextMapPropagator(SleuthPropagationProperties properties) { + return new CompositeTextMapPropagator(properties); + } + + } + + @Configuration(proxyBeanMethods = false) + @ConditionalOnProperty(name = "spring.sleuth.otel.propagation.sleuth-baggage.enabled", matchIfMissing = true) + static class BaggagePropagatorConfiguration { + + @Bean + TextMapPropagator baggageTextMapPropagator(SleuthBaggageProperties properties, + io.opentelemetry.baggage.BaggageManager otelBaggageManager, BaggageManager baggageManager, + ApplicationEventPublisher publisher) { + return new BaggageTextMapPropagator(properties, otelBaggageManager, baggageManager, publisher); + } + + } + +} + +class CompositeTextMapPropagator implements TextMapPropagator { + + private static final Log log = LogFactory.getLog(CompositeTextMapPropagator.class); + + private final Map mapping = new HashMap<>(); + + private final SleuthPropagationProperties properties; + + CompositeTextMapPropagator(SleuthPropagationProperties properties) { + this.properties = properties; + if (isOnClasspath("io.opentelemetry.extensions.trace.propagation.AwsXRayPropagator")) { + this.mapping.put(SleuthPropagationProperties.PropagationType.AWS, AwsXRayPropagator.getInstance()); + } + if (isOnClasspath("io.opentelemetry.extensions.trace.propagation.B3Propagator")) { + this.mapping.put(SleuthPropagationProperties.PropagationType.B3, + TraceMultiPropagator.builder().addPropagator(B3Propagator.getSingleHeaderPropagator()) + .addPropagator(B3Propagator.getMultipleHeaderPropagator()).build()); + } + if (isOnClasspath("io.opentelemetry.extensions.trace.propagation.JaegerPropagator")) { + this.mapping.put(SleuthPropagationProperties.PropagationType.JAEGER, JaegerPropagator.getInstance()); + } + if (isOnClasspath("io.opentelemetry.extensions.trace.propagation.OtTracerPropagator")) { + this.mapping.put(SleuthPropagationProperties.PropagationType.OT_TRACER, OtTracerPropagator.getInstance()); + } + this.mapping.put(SleuthPropagationProperties.PropagationType.W3C, HttpTraceContext.getInstance()); + this.mapping.put(SleuthPropagationProperties.PropagationType.CUSTOM, NoopTextMapPropagator.INSTANCE); + log.info("Registered the following context propagation types " + this.mapping.keySet()); + } + + private boolean isOnClasspath(String clazz) { + return ClassUtils.isPresent(clazz, null); + } + + @Override + public List fields() { + return this.properties.getType().stream() + .map(key -> this.mapping.getOrDefault(key, NoopTextMapPropagator.INSTANCE)) + .flatMap(p -> p.fields().stream()).collect(Collectors.toList()); + } + + @Override + public void inject(Context context, C carrier, Setter setter) { + this.properties.getType().stream().map(key -> this.mapping.getOrDefault(key, NoopTextMapPropagator.INSTANCE)) + .forEach(p -> p.inject(context, carrier, setter)); + } + + @Override + public Context extract(Context context, C carrier, Getter getter) { + for (SleuthPropagationProperties.PropagationType type : this.properties.getType()) { + TextMapPropagator propagator = this.mapping.get(type); + if (propagator == null || propagator == NoopTextMapPropagator.INSTANCE) { + continue; + } + Context extractedContext = propagator.extract(context, carrier, getter); + Span span = TracingContextUtils.getSpanWithoutDefault(extractedContext); + if (span != null) { + return extractedContext; + } + } + return context; + } + + private static final class NoopTextMapPropagator implements TextMapPropagator { + + private static final NoopTextMapPropagator INSTANCE = new NoopTextMapPropagator(); + + @Override + public List fields() { + return Collections.emptyList(); + } + + @Override + public void inject(Context context, C carrier, Setter setter) { + } + + @Override + public Context extract(Context context, C carrier, Getter getter) { + return context; + } + + } + +} diff --git a/spring-cloud-sleuth-otel/src/main/resources/META-INF/spring.factories b/spring-cloud-sleuth-otel/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..c8ec1eb35 --- /dev/null +++ b/spring-cloud-sleuth-otel/src/main/resources/META-INF/spring.factories @@ -0,0 +1,9 @@ +# Auto Configuration +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.springframework.cloud.sleuth.otel.autoconfig.TraceOtelAutoConfiguration,\ +org.springframework.cloud.sleuth.otel.bridge.TraceOtelBridgeAutoConfiguation,\ +org.springframework.cloud.sleuth.otel.bridge.http.TraceOtelHttpBridgeAutoConfiguration,\ +org.springframework.cloud.sleuth.otel.propagation.TraceOtelPropagationAutoConfiguration,\ +org.springframework.cloud.sleuth.otel.opentracing.OpentracingAutoConfiguration,\ +org.springframework.cloud.sleuth.otel.log.TraceOtelLogAutoConfiguration,\ +org.springframework.cloud.sleuth.otel.exporter.TraceOtelExporterAutoConfiguration \ No newline at end of file diff --git a/spring-cloud-sleuth-otel/src/test/java/org/springframework/cloud/sleuth/otel/autoconfig/TraceOtelAutoConfigurationTests.java b/spring-cloud-sleuth-otel/src/test/java/org/springframework/cloud/sleuth/otel/autoconfig/TraceOtelAutoConfigurationTests.java new file mode 100644 index 000000000..a5218442c --- /dev/null +++ b/spring-cloud-sleuth-otel/src/test/java/org/springframework/cloud/sleuth/otel/autoconfig/TraceOtelAutoConfigurationTests.java @@ -0,0 +1,51 @@ +/* + * Copyright 2013-2020 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.otel.autoconfig; + +import io.opentelemetry.trace.Tracer; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; + +class TraceOtelAutoConfigurationTests { + + @Test + void should_start_context_with_otel_tracer_when_sleuth_enabled() { + ApplicationContextRunner runner = new ApplicationContextRunner().withUserConfiguration(Config.class); + + runner.run(context -> assertThat(context).hasNotFailed().hasSingleBean(Tracer.class)); + } + + @Test + void should_start_context_without_tracer_when_sleuth_disabled() { + ApplicationContextRunner runner = new ApplicationContextRunner().withUserConfiguration(Config.class) + .withPropertyValues("spring.sleuth.enabled=false"); + + runner.run(context -> assertThat(context).hasNotFailed().doesNotHaveBean(Tracer.class)); + } + + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration + static class Config { + + } + +} diff --git a/spring-cloud-sleuth-otel/src/test/java/org/springframework/cloud/sleuth/otel/opentracing/OpentracingAutoConfigurationTests.java b/spring-cloud-sleuth-otel/src/test/java/org/springframework/cloud/sleuth/otel/opentracing/OpentracingAutoConfigurationTests.java new file mode 100644 index 000000000..e65795c5e --- /dev/null +++ b/spring-cloud-sleuth-otel/src/test/java/org/springframework/cloud/sleuth/otel/opentracing/OpentracingAutoConfigurationTests.java @@ -0,0 +1,52 @@ +/* + * Copyright 2013-2020 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.otel.opentracing; + +import io.opentelemetry.trace.Tracer; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.cloud.sleuth.otel.autoconfig.TraceOtelAutoConfiguration; + +import static org.assertj.core.api.Assertions.assertThat; + +class OpentracingAutoConfigurationTests { + + @Test + void should_start_context_with_otel_tracer_when_sleuth_enabled() { + ApplicationContextRunner runner = withAutoConfiguration(); + + runner.run(context -> assertThat(context).hasNotFailed().hasSingleBean(Tracer.class) + .hasSingleBean(io.opentracing.Tracer.class)); + } + + private ApplicationContextRunner withAutoConfiguration() { + return new ApplicationContextRunner().withConfiguration( + AutoConfigurations.of(TraceOtelAutoConfiguration.class, OpentracingAutoConfiguration.class)); + } + + @Test + void should_start_context_without_tracer_when_sleuth_disabled() { + ApplicationContextRunner runner = withAutoConfiguration() + .withPropertyValues("spring.sleuth.opentracing.enabled=false"); + + runner.run(context -> assertThat(context).hasNotFailed().hasSingleBean(Tracer.class) + .doesNotHaveBean(io.opentracing.Tracer.class)); + } + +} diff --git a/spring-cloud-sleuth-otel/src/test/java/org/springframework/cloud/sleuth/otel/propagation/TraceOtelPropagationAutoConfigurationTests.java b/spring-cloud-sleuth-otel/src/test/java/org/springframework/cloud/sleuth/otel/propagation/TraceOtelPropagationAutoConfigurationTests.java new file mode 100644 index 000000000..6efc438ce --- /dev/null +++ b/spring-cloud-sleuth-otel/src/test/java/org/springframework/cloud/sleuth/otel/propagation/TraceOtelPropagationAutoConfigurationTests.java @@ -0,0 +1,166 @@ +/* + * Copyright 2013-2020 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.otel.propagation; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.grpc.Context; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.trace.DefaultSpan; +import io.opentelemetry.trace.Span; +import io.opentelemetry.trace.SpanContext; +import io.opentelemetry.trace.TraceFlags; +import io.opentelemetry.trace.TraceState; +import io.opentelemetry.trace.TracingContextUtils; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; + +class TraceOtelPropagationAutoConfigurationTests { + + @Test + void should_start_a_composite_text_map_propagator_with_b3_as_default() { + ApplicationContextRunner runner = new ApplicationContextRunner().withUserConfiguration(Config.class); + + runner.run(context -> { + assertThat(context).hasNotFailed(); + CompositeTextMapPropagator propagator = context.getBean(CompositeTextMapPropagator.class); + assertThat(propagator.fields()).contains("X-B3-TraceId"); + }); + } + + @Test + void should_start_a_composite_text_map_propagator_with_a_single_propagation_type() { + ApplicationContextRunner runner = new ApplicationContextRunner().withUserConfiguration(Config.class) + .withPropertyValues("spring.sleuth.propagation.type=w3c"); + + runner.run(context -> { + assertThat(context).hasNotFailed(); + CompositeTextMapPropagator propagator = context.getBean(CompositeTextMapPropagator.class); + assertThat(propagator.fields()).doesNotContain("X-B3-TraceId").contains("traceparent"); + }); + } + + @Test + void should_start_a_composite_text_map_propagator_with_multiple_propagation_types() { + ApplicationContextRunner runner = new ApplicationContextRunner().withUserConfiguration(Config.class) + .withPropertyValues("spring.sleuth.propagation.type=b3,w3c"); + + runner.run(context -> { + assertThat(context).hasNotFailed(); + CompositeTextMapPropagator propagator = context.getBean(CompositeTextMapPropagator.class); + assertThat(propagator.fields()).contains("X-B3-TraceId", "traceparent"); + }); + } + + @Test + void should_start_a_composite_text_map_propagator_with_custom_propagation_types() { + ApplicationContextRunner runner = new ApplicationContextRunner() + .withUserConfiguration(CustomPropagatorConfig.class) + .withPropertyValues("spring.sleuth.propagation.type=custom"); + + runner.run(context -> { + assertThat(context).hasNotFailed(); + CompositeTextMapPropagator propagator = context.getBean(CompositeTextMapPropagator.class); + assertThat(propagator.fields()).doesNotContain("myCustomTraceId", "myCustomSpanId", "X-B3-TraceId", + "traceparent"); + }); + } + + @Test + void should_inject_and_extract_from_custom_propagator() { + CustomPropagator customPropagator = new CustomPropagator(); + Map carrier = carrierWithTracingData(); + + // Extraction + Context extract = customPropagator.extract(Context.current(), carrier, Map::get); + Span spanFromContext = TracingContextUtils.getSpan(extract); + assertThat(spanFromContext.getContext().getTraceIdAsHexString()).isEqualTo("ff000000000000000000000000000041"); + assertThat(spanFromContext.getContext().getSpanIdAsHexString()).isEqualTo("ff00000000000041"); + + // Injection + Map emptyMap = new HashMap<>(); + customPropagator.inject(extract, emptyMap, Map::put); + assertThat(emptyMap).containsEntry("myCustomTraceId", "ff000000000000000000000000000041") + .containsEntry("myCustomSpanId", "ff00000000000041"); + } + + private Map carrierWithTracingData() { + Map carrier = new HashMap<>(); + carrier.put("myCustomTraceId", "ff000000000000000000000000000041"); + carrier.put("myCustomSpanId", "ff00000000000041"); + return carrier; + } + + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration + static class Config { + + } + + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration + static class CustomPropagatorConfig { + + @Bean + TextMapPropagator myCustomPropagator() { + return new CustomPropagator(); + } + + } + +} + +// tag::custom_propagator[] +class CustomPropagator implements TextMapPropagator { + + @Override + public List fields() { + return Arrays.asList("myCustomTraceId", "myCustomSpanId"); + } + + @Override + public void inject(Context context, C carrier, Setter setter) { + SpanContext spanContext = TracingContextUtils.getSpan(context).getContext(); + if (!spanContext.isValid()) { + return; + } + setter.set(carrier, "myCustomTraceId", spanContext.getTraceIdAsHexString()); + setter.set(carrier, "myCustomSpanId", spanContext.getSpanIdAsHexString()); + } + + @Override + public Context extract(Context context, C carrier, Getter getter) { + String traceParent = getter.get(carrier, "myCustomTraceId"); + if (traceParent == null) { + return TracingContextUtils.withSpan(DefaultSpan.create(SpanContext.getInvalid()), context); + } + String spanId = getter.get(carrier, "myCustomSpanId"); + return TracingContextUtils.withSpan(DefaultSpan.create(SpanContext.createFromRemoteParent(traceParent, spanId, + TraceFlags.getSampled(), TraceState.builder().build())), context); + } + +} +// end::custom_propagator[] diff --git a/spring-cloud-sleuth-otel/src/test/resources/META-INF/application.yml b/spring-cloud-sleuth-otel/src/test/resources/META-INF/application.yml new file mode 100644 index 000000000..7d3456888 --- /dev/null +++ b/spring-cloud-sleuth-otel/src/test/resources/META-INF/application.yml @@ -0,0 +1,5 @@ +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE + +spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration diff --git a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-feign/src/main/java/sample/SampleFeignApplication.java b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-feign/src/main/java/sample/SampleFeignApplication.java index 82b2d966f..8264dac44 100644 --- a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-feign/src/main/java/sample/SampleFeignApplication.java +++ b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-feign/src/main/java/sample/SampleFeignApplication.java @@ -16,9 +16,6 @@ package sample; -import brave.handler.MutableSpan; -import brave.handler.SpanHandler; -import brave.propagation.TraceContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -26,6 +23,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.cloud.sleuth.api.exporter.SpanFilter; import org.springframework.context.annotation.Bean; /** @@ -44,13 +42,10 @@ public class SampleFeignApplication { // Use this for debugging (or if there is no Zipkin server running on port 9411) @Bean @ConditionalOnProperty(value = "sample.zipkin.enabled", havingValue = "false") - public SpanHandler spanHandler() { - return new SpanHandler() { - @Override - public boolean end(TraceContext context, MutableSpan span, Cause cause) { - logger.info(span); - return true; - } + public SpanFilter spanHandler() { + return span -> { + logger.info(span); + return true; }; } diff --git a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-messaging/pom.xml b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-messaging/pom.xml index 5ef07808e..fa031cb39 100644 --- a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-messaging/pom.xml +++ b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-messaging/pom.xml @@ -77,7 +77,7 @@ org.springframework.cloud - spring-cloud-sleuth-core + spring-cloud-sleuth-brave org.springframework.cloud diff --git a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-messaging/src/main/java/sample/SampleBackground.java b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-messaging/src/main/java/sample/SampleBackground.java index 8e6ffc131..88814f7ac 100644 --- a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-messaging/src/main/java/sample/SampleBackground.java +++ b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-messaging/src/main/java/sample/SampleBackground.java @@ -18,9 +18,8 @@ package sample; import java.util.Random; -import brave.Tracer; - import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; diff --git a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-messaging/src/main/java/sample/SampleMessagingApplication.java b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-messaging/src/main/java/sample/SampleMessagingApplication.java index 82b6935d3..34ea94c2d 100644 --- a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-messaging/src/main/java/sample/SampleMessagingApplication.java +++ b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-messaging/src/main/java/sample/SampleMessagingApplication.java @@ -16,6 +16,9 @@ package sample; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -30,12 +33,13 @@ import org.springframework.web.client.RestTemplate; * @author Spencer Gibb */ @SpringBootApplication - @EnableAsync @IntegrationComponentScan @RestController public class SampleMessagingApplication { + private static final Log log = LogFactory.getLog(SampleMessagingApplication.class); + @Autowired private SampleSink gateway; @@ -48,6 +52,7 @@ public class SampleMessagingApplication { @RequestMapping("/") public String home() { + log.info("Got request"); String msg = "Hello"; this.gateway.send(msg); return msg; diff --git a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-messaging/src/test/java/integration/MessagingApplicationTests.java b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-messaging/src/test/java/integration/MessagingApplicationTests.java index 4fbdd11fc..83bd7000f 100644 --- a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-messaging/src/test/java/integration/MessagingApplicationTests.java +++ b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-messaging/src/test/java/integration/MessagingApplicationTests.java @@ -18,10 +18,10 @@ package integration; import java.util.List; import java.util.Optional; -import java.util.Random; import java.util.stream.Collectors; import brave.Span; +import brave.Tracer; import brave.handler.MutableSpan; import brave.handler.SpanHandler; import brave.sampler.Sampler; @@ -55,6 +55,9 @@ public class MessagingApplicationTests extends AbstractIntegrationTest { @Autowired IntegrationTestZipkinSpanHandler testSpanHandler; + @Autowired + Tracer tracer; + @AfterEach public void cleanup() { this.testSpanHandler.spans.clear(); @@ -62,23 +65,27 @@ public class MessagingApplicationTests extends AbstractIntegrationTest { @Test public void should_have_passed_trace_id_when_message_is_about_to_be_sent() { - long traceId = new Random().nextLong(); + Span span = tracer.nextSpan().start(); + long traceId = span.context().traceId(); - await().atMost(15, SECONDS).untilAsserted( + await().atMost(3, SECONDS).untilAsserted( () -> httpMessageWithTraceIdInHeadersIsSuccessfullySent(sampleAppUrl + "/", traceId).run()); - await().atMost(15, SECONDS).untilAsserted(() -> thenAllSpansHaveTraceIdEqualTo(traceId)); + span.finish(); + await().atMost(3, SECONDS).untilAsserted(() -> thenAllSpansHaveTraceIdEqualTo(traceId)); } @Test public void should_have_passed_trace_id_and_generate_new_span_id_when_message_is_about_to_be_sent() { - long traceId = new Random().nextLong(); - long spanId = new Random().nextLong(); + Span span = tracer.nextSpan().start(); + long traceId = span.context().traceId(); + long spanId = span.context().spanId(); - await().atMost(15, SECONDS).untilAsserted( + await().atMost(3, SECONDS).untilAsserted( () -> httpMessageWithTraceIdInHeadersIsSuccessfullySent(sampleAppUrl + "/", traceId, spanId).run()); - await().atMost(15, SECONDS).untilAsserted(() -> { + span.finish(); + await().atMost(3, SECONDS).untilAsserted(() -> { thenAllSpansHaveTraceIdEqualTo(traceId); thenTheSpansHaveProperParentStructure(); }); @@ -86,12 +93,14 @@ public class MessagingApplicationTests extends AbstractIntegrationTest { @Test public void should_have_passed_trace_id_with_annotations_in_async_thread_when_message_is_about_to_be_sent() { - long traceId = new Random().nextLong(); + Span span = tracer.nextSpan().start(); + long traceId = span.context().traceId(); - await().atMost(15, SECONDS).untilAsserted( + await().atMost(3, SECONDS).untilAsserted( () -> httpMessageWithTraceIdInHeadersIsSuccessfullySent(sampleAppUrl + "/xform", traceId).run()); - await().atMost(15, SECONDS).untilAsserted(() -> { + span.finish(); + await().atMost(3, SECONDS).untilAsserted(() -> { thenAllSpansHaveTraceIdEqualTo(traceId); thenThereIsAtLeastOneTagWithKey("background-sleep-millis"); }); @@ -121,12 +130,17 @@ public class MessagingApplicationTests extends AbstractIntegrationTest { // "http:/parent/" -> "message:messages" -> "http:/foo" (CS + CR) -> "http:/foo" // (SS) thenAllSpansArePresent(firstHttpSpan, eventSpans, lastHttpSpansParent, eventSentSpan, producerSpan); - then(this.testSpanHandler.spans).as("There were 6 spans").hasSize(6); + List spans = this.testSpanHandler.spans; + then(spans).as("There were 7 spans").hasSize(7); log.info("Checking the parent child structure"); - List> parentChild = this.testSpanHandler.spans.stream() - .filter(span -> span.parentId() != null).map(span -> this.testSpanHandler.spans.stream() - .filter(span1 -> span1.id().equals(span.parentId())).findAny()) - .collect(Collectors.toList()); + List> parentChild = spans.stream().filter(span -> span.parentId() != null).map(span -> { + Optional any = spans.stream().filter(span1 -> span1.id().equals(span.parentId())).findAny(); + if (!any.isPresent()) { + log.warn("Span with id [" + span.id() + "] and parent span id [" + span.parentId() + + "] doesn't have a corresponding span with id equal to parent id"); + } + return any; + }).collect(Collectors.toList()); log.info("List of parents and children " + parentChild); then(parentChild.stream().allMatch(Optional::isPresent)).isTrue(); } @@ -169,7 +183,7 @@ public class MessagingApplicationTests extends AbstractIntegrationTest { then(lastHttpSpan.isPresent()).isTrue(); } - @Configuration + @Configuration(proxyBeanMethods = false) public static class IntegrationSpanCollectorConfig { @Bean diff --git a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-test-core/src/main/java/tools/AbstractIntegrationTest.java b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-test-core/src/main/java/tools/AbstractIntegrationTest.java index a249a0f1d..6eee50f8a 100644 --- a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-test-core/src/main/java/tools/AbstractIntegrationTest.java +++ b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-test-core/src/main/java/tools/AbstractIntegrationTest.java @@ -47,6 +47,8 @@ public abstract class AbstractIntegrationTest { } protected Runnable httpMessageWithTraceIdInHeadersIsSuccessfullySent(String endpoint, long traceId, Long spanId) { + log.info("Sending a request with trace id [" + SpanUtil.idToHex(traceId) + "] and span id [" + + SpanUtil.idToHex(spanId) + "]"); return new RequestSendingRunnable(this.restTemplate, endpoint, traceId, spanId); } diff --git a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-test-core/src/main/java/tools/RequestSendingRunnable.java b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-test-core/src/main/java/tools/RequestSendingRunnable.java index 27cbdef42..9ea56f52f 100644 --- a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-test-core/src/main/java/tools/RequestSendingRunnable.java +++ b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-test-core/src/main/java/tools/RequestSendingRunnable.java @@ -61,8 +61,8 @@ public class RequestSendingRunnable implements Runnable { @Override public void run() { - log.info( - String.format("Sending the request to url [%s] with trace id in headers [%d]", this.url, this.traceId)); + log.info(String.format("Sending the request to url [%s] with trace id in headers [%s]", this.url, + SpanUtil.idToHex(this.traceId))); ResponseEntity responseEntity = this.restTemplate.exchange(requestWithTraceId(), String.class); then(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK); log.info(String.format("Received the following response [%s]", responseEntity)); diff --git a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-zipkin/pom.xml b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-zipkin/pom.xml index d29ec3792..f6693989b 100644 --- a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-zipkin/pom.xml +++ b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-zipkin/pom.xml @@ -73,7 +73,7 @@ org.springframework.cloud - spring-cloud-sleuth-core + spring-cloud-sleuth-brave org.springframework.cloud @@ -87,6 +87,12 @@ org.springframework.boot spring-boot-starter-actuator + + com.squareup.okhttp3 + okhttp + ${okhttp.version} + true + org.springframework.boot spring-boot-starter-test diff --git a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-zipkin/src/main/java/sample/SampleBackground.java b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-zipkin/src/main/java/sample/SampleBackground.java index 8e6ffc131..88814f7ac 100644 --- a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-zipkin/src/main/java/sample/SampleBackground.java +++ b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-zipkin/src/main/java/sample/SampleBackground.java @@ -18,9 +18,8 @@ package sample; import java.util.Random; -import brave.Tracer; - import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; diff --git a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-zipkin/src/main/java/sample/SampleController.java b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-zipkin/src/main/java/sample/SampleController.java index 08a29068f..1908ea34c 100644 --- a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-zipkin/src/main/java/sample/SampleController.java +++ b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-zipkin/src/main/java/sample/SampleController.java @@ -19,13 +19,13 @@ package sample; import java.util.Random; import java.util.concurrent.Callable; -import brave.Span; -import brave.Tracer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.context.ApplicationListener; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -99,7 +99,7 @@ public class SampleController implements ApplicationListenerorg.springframework.cloud spring-cloud-sleuth-core + + org.springframework.cloud + spring-cloud-sleuth-brave + org.springframework.boot spring-boot-starter-aop diff --git a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample/src/main/java/sample/SampleBackground.java b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample/src/main/java/sample/SampleBackground.java index cc9bf7730..a94ff89d5 100644 --- a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample/src/main/java/sample/SampleBackground.java +++ b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample/src/main/java/sample/SampleBackground.java @@ -18,11 +18,11 @@ package sample; import java.util.Random; -import brave.Tracer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; diff --git a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample/src/main/java/sample/SampleController.java b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample/src/main/java/sample/SampleController.java index 36fd17c67..4a8375c52 100644 --- a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample/src/main/java/sample/SampleController.java +++ b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample/src/main/java/sample/SampleController.java @@ -19,13 +19,13 @@ package sample; import java.util.Random; import java.util.concurrent.Callable; -import brave.Span; -import brave.Tracer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.context.ApplicationListener; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -102,7 +102,7 @@ public class SampleController implements ApplicationListenerorg.springframework.cloud spring-cloud-sleuth-core + + org.springframework.cloud + spring-cloud-sleuth-brave + + true + + + org.springframework.cloud + spring-cloud-sleuth-otel + + true + + + io.opentelemetry + opentelemetry-exporters-zipkin + true + org.springframework spring-web @@ -71,6 +88,12 @@ micrometer-core true + + com.squareup.okhttp3 + okhttp + ${okhttp.version} + true + io.zipkin.zipkin2 zipkin diff --git a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfiguration.java b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfiguration.java index f5b93f6d5..780165832 100644 --- a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfiguration.java +++ b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfiguration.java @@ -16,17 +16,9 @@ package org.springframework.cloud.sleuth.zipkin2; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.LinkedHashSet; -import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import brave.Tag; -import brave.TracingCustomizer; -import brave.handler.SpanHandler; import io.micrometer.core.instrument.MeterRegistry; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -37,7 +29,6 @@ import zipkin2.reporter.InMemoryReporterMetrics; import zipkin2.reporter.Reporter; import zipkin2.reporter.ReporterMetrics; import zipkin2.reporter.Sender; -import zipkin2.reporter.brave.ZipkinSpanHandler; import zipkin2.reporter.metrics.micrometer.MicrometerReporterMetrics; import org.springframework.beans.factory.annotation.Autowired; @@ -59,7 +50,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.core.env.Environment; -import org.springframework.lang.Nullable; import org.springframework.web.client.RestTemplate; /** @@ -82,27 +72,10 @@ import org.springframework.web.client.RestTemplate; @AutoConfigureBefore(TraceAutoConfiguration.class) @AutoConfigureAfter(name = "org.springframework.cloud.autoconfigure.RefreshAutoConfiguration") @Import(ZipkinSenderConfigurationImportSelector.class) -// public because the constant REPORTER_BEAN_NAME was documented public class ZipkinAutoConfiguration { private static final Log log = LogFactory.getLog(ZipkinAutoConfiguration.class); - /** - * Sort Zipkin Handlers last, so that redactions etc happen prior. - */ - static final Comparator SPAN_HANDLER_COMPARATOR = (o1, o2) -> { - if (o1 instanceof ZipkinSpanHandler) { - if (o2 instanceof ZipkinSpanHandler) { - return 0; - } - return 1; - } - else if (o2 instanceof ZipkinSpanHandler) { - return -1; - } - return 0; - }; - /** * Zipkin reporter bean name. Name of the bean matters for supporting multiple tracing * systems. @@ -169,48 +142,18 @@ public class ZipkinAutoConfiguration { } } - /** Returns one handler for as many reporters as exist. */ - @Bean - SpanHandler zipkinSpanHandler(@Nullable List> spanReporters, @Nullable Tag errorTag) { - if (spanReporters == null) { - return SpanHandler.NOOP; - } - - LinkedHashSet> reporters = new LinkedHashSet<>(spanReporters); - reporters.remove(Reporter.NOOP); - if (spanReporters.isEmpty()) { - return SpanHandler.NOOP; - } - - Reporter spanReporter = reporters.size() == 1 ? reporters.iterator().next() - : new CompositeSpanReporter(reporters.toArray(new Reporter[0])); - - ZipkinSpanHandler.Builder builder = ZipkinSpanHandler.newBuilder(spanReporter); - if (errorTag != null) { - builder.errorTag(errorTag); - } - return builder.build(); - } - - /** This ensures Zipkin reporters end up after redaction, etc. */ - @Bean - TracingCustomizer reorderZipkinHandlersLast() { - return builder -> { - List configuredSpanHandlers = new ArrayList<>(builder.spanHandlers()); - configuredSpanHandlers.sort(SPAN_HANDLER_COMPARATOR); - builder.clearSpanHandlers(); - for (SpanHandler spanHandler : configuredSpanHandlers) { - builder.addSpanHandler(spanHandler); - } - }; - } - @Bean @ConditionalOnMissingBean public ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer(ZipkinProperties zipkinProperties) { return new DefaultZipkinRestTemplateCustomizer(zipkinProperties); } + @Bean + @ConditionalOnMissingBean + ReporterMetrics sleuthReporterMetrics() { + return new InMemoryReporterMetrics(); + } + @Configuration(proxyBeanMethods = false) @ConditionalOnMissingBean(EndpointLocator.class) @ConditionalOnProperty(value = "spring.zipkin.locator.discovery.enabled", havingValue = "false", @@ -288,12 +231,13 @@ public class ZipkinAutoConfiguration { @Bean @ConditionalOnBean(MeterRegistry.class) + @ConditionalOnClass(name = "zipkin2.reporter.metrics.micrometer.MicrometerReporterMetrics") ReporterMetrics sleuthMicrometerReporterMetrics(MeterRegistry meterRegistry) { return MicrometerReporterMetrics.create(meterRegistry); } @Bean - @ConditionalOnMissingBean(MeterRegistry.class) + @ConditionalOnMissingClass("zipkin2.reporter.metrics.micrometer.MicrometerReporterMetrics") ReporterMetrics sleuthReporterMetrics() { return new InMemoryReporterMetrics(); } @@ -302,47 +246,4 @@ public class ZipkinAutoConfiguration { } - // Zipkin conversion only happens once per mutable span - static final class CompositeSpanReporter implements Reporter { - - final Reporter[] reporters; - - CompositeSpanReporter(Reporter[] reporters) { - this.reporters = reporters; - } - - @Override - public void report(Span span) { - for (Reporter reporter : reporters) { - try { - reporter.report(span); - } - catch (RuntimeException ex) { - // TODO: message lifted from ListReporter: this is probably too much - // for warn level - log.warn("Exception occurred while trying to report the span " + span, ex); - } - } - } - - @Override - public int hashCode() { - return Arrays.hashCode(reporters); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof CompositeSpanReporter)) { - return false; - } - return Arrays.equals(((CompositeSpanReporter) obj).reporters, reporters); - } - - @Override - public String toString() { - return Arrays.toString(reporters); - } - - } - } diff --git a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinBraveAutoConfiguration.java b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinBraveAutoConfiguration.java new file mode 100644 index 000000000..9f4c01e91 --- /dev/null +++ b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinBraveAutoConfiguration.java @@ -0,0 +1,164 @@ +/* + * Copyright 2013-2020 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.zipkin2; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.LinkedHashSet; +import java.util.List; + +import brave.Tag; +import brave.Tracer; +import brave.TracingCustomizer; +import brave.handler.SpanHandler; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import zipkin2.Span; +import zipkin2.reporter.Reporter; +import zipkin2.reporter.brave.ZipkinSpanHandler; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +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; +import org.springframework.web.client.RestTemplate; + +/** + * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration + * Auto-configuration} enables reporting to Zipkin via HTTP. + * + * The {@link ZipkinRestTemplateCustomizer} allows you to customize the + * {@link RestTemplate} that is used to send Spans to Zipkin. Its default implementation - + * {@link DefaultZipkinRestTemplateCustomizer} adds the GZip compression. + * + * @author Spencer Gibb + * @author Tim Ysewyn + * @since 1.0.0 + * @see ZipkinRestTemplateCustomizer + * @see DefaultZipkinRestTemplateCustomizer + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(value = { "spring.sleuth.enabled", "spring.zipkin.enabled" }, matchIfMissing = true) +@AutoConfigureBefore(TraceAutoConfiguration.class) +@ConditionalOnClass(Tracer.class) +@AutoConfigureAfter(ZipkinAutoConfiguration.class) +public class ZipkinBraveAutoConfiguration { + + private static final Log log = LogFactory.getLog(ZipkinBraveAutoConfiguration.class); + + /** + * + * Sort Zipkin Handlers last, so that redactions etc happen prior. + */ + static final Comparator SPAN_HANDLER_COMPARATOR = (o1, o2) -> { + if (o1 instanceof ZipkinSpanHandler) { + if (o2 instanceof ZipkinSpanHandler) { + return 0; + } + return 1; + } + else if (o2 instanceof ZipkinSpanHandler) { + return -1; + } + return 0; + }; + + /** Returns one handler for as many reporters as exist. */ + @Bean + SpanHandler zipkinSpanHandler(@Nullable List> spanReporters, @Nullable Tag errorTag) { + if (spanReporters == null) { + return SpanHandler.NOOP; + } + + LinkedHashSet> reporters = new LinkedHashSet<>(spanReporters); + reporters.remove(Reporter.NOOP); + if (spanReporters.isEmpty()) { + return SpanHandler.NOOP; + } + + Reporter spanReporter = reporters.size() == 1 ? reporters.iterator().next() + : new CompositeSpanReporter(reporters.toArray(new Reporter[0])); + + ZipkinSpanHandler.Builder builder = ZipkinSpanHandler.newBuilder(spanReporter); + if (errorTag != null) { + builder.errorTag(errorTag); + } + return builder.build(); + } + + /** This ensures Zipkin reporters end up after redaction, etc. */ + @Bean + TracingCustomizer reorderZipkinHandlersLast() { + return builder -> { + List configuredSpanHandlers = new ArrayList<>(builder.spanHandlers()); + configuredSpanHandlers.sort(SPAN_HANDLER_COMPARATOR); + builder.clearSpanHandlers(); + for (SpanHandler spanHandler : configuredSpanHandlers) { + builder.addSpanHandler(spanHandler); + } + }; + } + + // Zipkin conversion only happens once per mutable span + static final class CompositeSpanReporter implements Reporter { + + final Reporter[] reporters; + + CompositeSpanReporter(Reporter[] reporters) { + this.reporters = reporters; + } + + @Override + public void report(Span span) { + for (Reporter reporter : reporters) { + try { + reporter.report(span); + } + catch (RuntimeException ex) { + // TODO: message lifted from ListReporter: this is probably too much + // for warn level + log.warn("Exception occurred while trying to report the span " + span, ex); + } + } + } + + @Override + public int hashCode() { + return Arrays.hashCode(reporters); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof CompositeSpanReporter)) { + return false; + } + return Arrays.equals(((CompositeSpanReporter) obj).reporters, reporters); + } + + @Override + public String toString() { + return Arrays.toString(reporters); + } + + } + +} diff --git a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinOtelAutoConfiguration.java b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinOtelAutoConfiguration.java new file mode 100644 index 000000000..9ae0ecde1 --- /dev/null +++ b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinOtelAutoConfiguration.java @@ -0,0 +1,77 @@ +/* + * Copyright 2013-2020 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.zipkin2; + +import io.opentelemetry.exporters.zipkin.ZipkinSpanExporter; +import io.opentelemetry.trace.Tracer; +import zipkin2.reporter.Sender; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +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.autoconfig.TraceAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.util.StringUtils; +import org.springframework.web.client.RestTemplate; + +/** + * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration + * Auto-configuration} enables reporting to Zipkin via HTTP. + * + * The {@link ZipkinRestTemplateCustomizer} allows you to customize the + * {@link RestTemplate} that is used to send Spans to Zipkin. Its default implementation - + * {@link DefaultZipkinRestTemplateCustomizer} adds the GZip compression. + * + * @author Spencer Gibb + * @author Tim Ysewyn + * @since 1.0.0 + * @see ZipkinRestTemplateCustomizer + * @see DefaultZipkinRestTemplateCustomizer + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty(value = { "spring.sleuth.enabled", "spring.zipkin.enabled" }, matchIfMissing = true) +@AutoConfigureBefore(TraceAutoConfiguration.class) +@ConditionalOnClass(Tracer.class) +@AutoConfigureAfter(ZipkinAutoConfiguration.class) +public class ZipkinOtelAutoConfiguration { + + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(ZipkinSpanExporter.class) + static class ZipkinConfiguration { + + @Bean + @ConditionalOnMissingBean + ZipkinSpanExporter otelZipkinSpanExporter(ZipkinProperties zipkinProperties, + @Qualifier(ZipkinAutoConfiguration.SENDER_BEAN_NAME) Sender sender, Environment env) { + return ZipkinSpanExporter.newBuilder().setEndpoint(zipkinProperties.getBaseUrl() + "api/v2/spans") + .setSender(sender).setEncoder(zipkinProperties.getEncoder()) + .setServiceName( + StringUtils.hasText(zipkinProperties.getService().getName()) + ? zipkinProperties.getService().getName() + : env.getProperty("spring.application.name", env.getProperty( + "spring.zipkin.service.name", ZipkinSpanExporter.DEFAULT_SERVICE_NAME))) + .build(); + } + + } + +} diff --git a/spring-cloud-sleuth-zipkin/src/main/resources/META-INF/spring.factories b/spring-cloud-sleuth-zipkin/src/main/resources/META-INF/spring.factories index f2608fbe3..59010d21f 100644 --- a/spring-cloud-sleuth-zipkin/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-sleuth-zipkin/src/main/resources/META-INF/spring.factories @@ -1,3 +1,5 @@ # Auto Configuration org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -org.springframework.cloud.sleuth.zipkin2.ZipkinAutoConfiguration +org.springframework.cloud.sleuth.zipkin2.ZipkinAutoConfiguration,\ +org.springframework.cloud.sleuth.zipkin2.ZipkinBraveAutoConfiguration,\ +org.springframework.cloud.sleuth.zipkin2.ZipkinOtelAutoConfiguration diff --git a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/BraveDefaultEndpointLocatorConfigurationTest.java b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/BraveDefaultEndpointLocatorConfigurationTest.java new file mode 100644 index 000000000..b450a9410 --- /dev/null +++ b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/BraveDefaultEndpointLocatorConfigurationTest.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2020 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.zipkin2; + +/** + * @author Matcin Wielgus + */ +public class BraveDefaultEndpointLocatorConfigurationTest extends DefaultEndpointLocatorConfigurationTest { + + @Override + protected Class emptyConfiguration() { + return BraveEmptyConfiguration.class; + } + + @Override + protected Class configurationWithRegistrationClass() { + return BraveConfigurationWithRegistration.class; + } + + @Override + protected Class configurationWithCustomLocatorClass() { + return BraveConfigurationWithCustomLocator.class; + } + + @Override + protected EndpointLocator locatorFromConfiguration() { + return BraveConfigurationWithCustomLocator.locator; + } + +} diff --git a/tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/instrument/async/DefaultTestAutoConfiguration.java b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/BraveZipkinDiscoveryClientTests.java similarity index 53% rename from tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/instrument/async/DefaultTestAutoConfiguration.java rename to spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/BraveZipkinDiscoveryClientTests.java index 243d8156a..621dc3197 100644 --- a/tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/instrument/async/DefaultTestAutoConfiguration.java +++ b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/BraveZipkinDiscoveryClientTests.java @@ -14,22 +14,28 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.async; +package org.springframework.cloud.sleuth.zipkin2; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +import brave.sampler.Sampler; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; -import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.autoconfig.TraceOtelAutoConfiguration; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@EnableAutoConfiguration(exclude = { LoadBalancerAutoConfiguration.class, JmxAutoConfiguration.class }) -@Configuration -public @interface DefaultTestAutoConfiguration { +@SpringBootTest(classes = BraveZipkinDiscoveryClientTests.TestConfig.class) +public class BraveZipkinDiscoveryClientTests extends ZipkinDiscoveryClientTests { + + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration(exclude = TraceOtelAutoConfiguration.class) + static class TestConfig { + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + } } diff --git a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/DefaultEndpointLocatorConfigurationTest.java b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/DefaultEndpointLocatorConfigurationTest.java index 1b03e2860..a09e37c9c 100644 --- a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/DefaultEndpointLocatorConfigurationTest.java +++ b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/DefaultEndpointLocatorConfigurationTest.java @@ -30,6 +30,8 @@ import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.cloud.commons.util.InetUtils; import org.springframework.cloud.commons.util.InetUtilsProperties; +import org.springframework.cloud.sleuth.brave.autoconfig.TraceBraveAutoConfiguration; +import org.springframework.cloud.sleuth.otel.autoconfig.TraceOtelAutoConfiguration; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -41,7 +43,7 @@ import static org.assertj.core.api.Assertions.assertThat; /** * @author Matcin Wielgus */ -public class DefaultEndpointLocatorConfigurationTest { +public abstract class DefaultEndpointLocatorConfigurationTest { public static final byte[] ADDRESS1234 = { 1, 2, 3, 4 }; @@ -49,31 +51,43 @@ public class DefaultEndpointLocatorConfigurationTest { @Test public void endpointLocatorShouldDefaultToServerPropertiesEndpointLocator() { - ConfigurableApplicationContext ctxt = new SpringApplication(EmptyConfiguration.class) + ConfigurableApplicationContext ctxt = new SpringApplication(emptyConfiguration()) .run("--spring.jmx.enabled=false"); assertThat(ctxt.getBean(EndpointLocator.class)).isInstanceOf(DefaultEndpointLocator.class); ctxt.close(); } + protected Class emptyConfiguration() { + throw new UnsupportedOperationException("Provide configuration"); + } + @Test public void endpointLocatorShouldDefaultToServerPropertiesEndpointLocatorEvenWhenDiscoveryClientPresent() { - ConfigurableApplicationContext ctxt = new SpringApplication(ConfigurationWithRegistration.class) + ConfigurableApplicationContext ctxt = new SpringApplication(configurationWithRegistrationClass()) .run("--spring.jmx.enabled=false"); assertThat(ctxt.getBean(EndpointLocator.class)).isInstanceOf(DefaultEndpointLocator.class); ctxt.close(); } + protected Class configurationWithRegistrationClass() { + throw new UnsupportedOperationException("Provide configuration"); + } + @Test public void endpointLocatorShouldRespectExistingEndpointLocator() { - ConfigurableApplicationContext ctxt = new SpringApplication(ConfigurationWithCustomLocator.class) + ConfigurableApplicationContext ctxt = new SpringApplication(configurationWithCustomLocatorClass()) .run("--spring.jmx.enabled=false"); - assertThat(ctxt.getBean(EndpointLocator.class)).isSameAs(ConfigurationWithCustomLocator.locator); + assertThat(ctxt.getBean(EndpointLocator.class)).isSameAs(locatorFromConfiguration()); ctxt.close(); } + protected Class configurationWithCustomLocatorClass() { + throw new UnsupportedOperationException("Provide configuration"); + } + @Test public void endpointLocatorShouldSetServiceNameToServiceId() { - ConfigurableApplicationContext ctxt = new SpringApplication(ConfigurationWithRegistration.class) + ConfigurableApplicationContext ctxt = new SpringApplication(configurationWithRegistrationClass()) .run("--spring.jmx.enabled=false", "--spring.zipkin.locator.discovery.enabled=true"); assertThat(ctxt.getBean(EndpointLocator.class).local().serviceName()).isEqualTo("from-registration"); ctxt.close(); @@ -81,7 +95,7 @@ public class DefaultEndpointLocatorConfigurationTest { @Test public void endpointLocatorShouldAcceptServiceNameOverride() { - ConfigurableApplicationContext ctxt = new SpringApplication(ConfigurationWithRegistration.class).run( + ConfigurableApplicationContext ctxt = new SpringApplication(configurationWithRegistrationClass()).run( "--spring.jmx.enabled=false", "--spring.zipkin.locator.discovery.enabled=true", "--spring.zipkin.service.name=foo"); assertThat(ctxt.getBean(EndpointLocator.class).local().serviceName()).isEqualTo("foo"); @@ -90,13 +104,17 @@ public class DefaultEndpointLocatorConfigurationTest { @Test public void endpointLocatorShouldRespectExistingEndpointLocatorEvenWhenAskedToBeDiscovery() { - ConfigurableApplicationContext ctxt = new SpringApplication(ConfigurationWithRegistration.class, - ConfigurationWithCustomLocator.class).run("--spring.jmx.enabled=false", + ConfigurableApplicationContext ctxt = new SpringApplication(configurationWithRegistrationClass(), + configurationWithCustomLocatorClass()).run("--spring.jmx.enabled=false", "--spring.zipkin.locator.discovery.enabled=true"); - assertThat(ctxt.getBean(EndpointLocator.class)).isSameAs(ConfigurationWithCustomLocator.locator); + assertThat(ctxt.getBean(EndpointLocator.class)).isSameAs(locatorFromConfiguration()); ctxt.close(); } + protected EndpointLocator locatorFromConfiguration() { + throw new UnsupportedOperationException("Provide the locator"); + } + @Test public void portDefaultsTo8080() throws UnknownHostException { DefaultEndpointLocator locator = new DefaultEndpointLocator(null, new ServerProperties(), this.environment, @@ -164,15 +182,15 @@ public class DefaultEndpointLocatorConfigurationTest { return mocked; } - @Configuration - @EnableAutoConfiguration - public static class EmptyConfiguration { + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration(exclude = TraceOtelAutoConfiguration.class) + public static class BraveEmptyConfiguration { } - @Configuration - @EnableAutoConfiguration - public static class ConfigurationWithRegistration { + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration(exclude = TraceOtelAutoConfiguration.class) + public static class BraveConfigurationWithRegistration { @Bean public Registration getRegistration() { @@ -211,9 +229,69 @@ public class DefaultEndpointLocatorConfigurationTest { } - @Configuration - @EnableAutoConfiguration - public static class ConfigurationWithCustomLocator { + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration(exclude = TraceOtelAutoConfiguration.class) + public static class BraveConfigurationWithCustomLocator { + + static EndpointLocator locator = Mockito.mock(EndpointLocator.class); + + @Bean + public EndpointLocator getEndpointLocator() { + return locator; + } + + } + + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration(exclude = TraceBraveAutoConfiguration.class) + public static class OtelEmptyConfiguration { + + } + + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration(exclude = TraceBraveAutoConfiguration.class) + public static class OtelConfigurationWithRegistration { + + @Bean + public Registration getRegistration() { + return new Registration() { + @Override + public String getServiceId() { + return "from-registration"; + } + + @Override + public String getHost() { + return null; + } + + @Override + public int getPort() { + return 0; + } + + @Override + public boolean isSecure() { + return false; + } + + @Override + public URI getUri() { + return null; + } + + @Override + public Map getMetadata() { + return null; + } + }; + } + + } + + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration(exclude = TraceBraveAutoConfiguration.class) + public static class OtelConfigurationWithCustomLocator { static EndpointLocator locator = Mockito.mock(EndpointLocator.class); diff --git a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/OtelDefaultEndpointLocatorConfigurationTest.java b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/OtelDefaultEndpointLocatorConfigurationTest.java new file mode 100644 index 000000000..fccd32bed --- /dev/null +++ b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/OtelDefaultEndpointLocatorConfigurationTest.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2020 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.zipkin2; + +/** + * @author Matcin Wielgus + */ +public class OtelDefaultEndpointLocatorConfigurationTest extends DefaultEndpointLocatorConfigurationTest { + + @Override + protected Class emptyConfiguration() { + return OtelEmptyConfiguration.class; + } + + @Override + protected Class configurationWithRegistrationClass() { + return OtelConfigurationWithRegistration.class; + } + + @Override + protected Class configurationWithCustomLocatorClass() { + return OtelConfigurationWithCustomLocator.class; + } + + @Override + protected EndpointLocator locatorFromConfiguration() { + return OtelConfigurationWithCustomLocator.locator; + } + +} diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/DefaultTestAutoConfiguration.java b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/OtelZipkinDiscoveryClientTests.java similarity index 51% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/DefaultTestAutoConfiguration.java rename to spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/OtelZipkinDiscoveryClientTests.java index c039967a7..87730dde2 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/DefaultTestAutoConfiguration.java +++ b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/OtelZipkinDiscoveryClientTests.java @@ -14,24 +14,29 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument; +package org.springframework.cloud.sleuth.zipkin2; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; -import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.autoconfig.TraceBraveAutoConfiguration; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@EnableAutoConfiguration(exclude = { LoadBalancerAutoConfiguration.class, JmxAutoConfiguration.class }) -// ,TraceSpringIntegrationAutoConfiguration.class, -// TraceWebSocketAutoConfiguration.class }) -@Configuration -public @interface DefaultTestAutoConfiguration { +@SpringBootTest(classes = OtelZipkinDiscoveryClientTests.TestConfig.class) +public class OtelZipkinDiscoveryClientTests extends ZipkinDiscoveryClientTests { + + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration(exclude = TraceBraveAutoConfiguration.class) + static class TestConfig { + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } } diff --git a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfigurationTests.java b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfigurationTests.java index ad3e2d0a6..611714ba1 100644 --- a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfigurationTests.java +++ b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinAutoConfigurationTests.java @@ -19,6 +19,7 @@ package org.springframework.cloud.sleuth.zipkin2; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import brave.Span; @@ -50,6 +51,7 @@ import zipkin2.reporter.brave.ZipkinSpanHandler; import zipkin2.reporter.kafka.KafkaSender; import zipkin2.reporter.metrics.micrometer.MicrometerReporterMetrics; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; @@ -59,6 +61,7 @@ import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.cloud.sleuth.brave.autoconfig.TraceBraveAutoConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -68,7 +71,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.BDDAssertions.then; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.springframework.cloud.sleuth.zipkin2.ZipkinAutoConfiguration.SPAN_HANDLER_COMPARATOR; +import static org.springframework.cloud.sleuth.zipkin2.ZipkinBraveAutoConfiguration.SPAN_HANDLER_COMPARATOR; /** * Not using {@linkplain SpringBootTest} as we need to change properties per test. @@ -77,8 +80,8 @@ import static org.springframework.cloud.sleuth.zipkin2.ZipkinAutoConfiguration.S */ public class ZipkinAutoConfigurationTests { - private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(ZipkinAutoConfiguration.class)); + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration( + AutoConfigurations.of(ZipkinAutoConfiguration.class, ZipkinBraveAutoConfiguration.class)); public MockWebServer server = new MockWebServer(); @@ -152,20 +155,23 @@ public class ZipkinAutoConfigurationTests { void defaultsToV2Endpoint() throws Exception { this.context = new AnnotationConfigApplicationContext(); environment().setProperty("spring.zipkin.base-url", this.server.url("/").toString()); - this.context.register(ZipkinAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class, - TraceAutoConfiguration.class, Config.class); + this.context.register(ZipkinAutoConfiguration.class, ZipkinBraveAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class, TraceAutoConfiguration.class, + TraceBraveAutoConfiguration.class, Config.class); this.context.refresh(); Span span = this.context.getBean(Tracing.class).tracer().nextSpan().name("foo").tag("foo", "bar").start(); span.finish(); - Awaitility.await().untilAsserted(() -> then(this.server.getRequestCount()).isGreaterThan(1)); - // first request is for health check - this.server.takeRequest(); - // second request is the span one - RecordedRequest request = this.server.takeRequest(); - then(request.getPath()).isEqualTo("/api/v2/spans"); - then(request.getBody().readUtf8()).contains("localEndpoint"); + this.context.getBean(ZipkinAutoConfiguration.REPORTER_BEAN_NAME, AsyncReporter.class).flush(); + Awaitility.await().atMost(250, TimeUnit.MILLISECONDS) + .untilAsserted(() -> then(this.server.getRequestCount()).isGreaterThan(1)); + + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + RecordedRequest request = this.server.takeRequest(1, TimeUnit.SECONDS); + then(request.getPath()).isEqualTo("/api/v2/spans"); + then(request.getBody().readUtf8()).contains("localEndpoint"); + }); } private MockEnvironment environment() { @@ -178,20 +184,22 @@ public class ZipkinAutoConfigurationTests { this.context = new AnnotationConfigApplicationContext(); environment().setProperty("spring.zipkin.base-url", this.server.url("/").toString()); environment().setProperty("spring.zipkin.encoder", "JSON_V1"); - this.context.register(ZipkinAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class, - TraceAutoConfiguration.class, Config.class); + this.context.register(ZipkinAutoConfiguration.class, ZipkinBraveAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class, TraceAutoConfiguration.class, + TraceBraveAutoConfiguration.class, Config.class); this.context.refresh(); Span span = this.context.getBean(Tracing.class).tracer().nextSpan().name("foo").tag("foo", "bar").start(); span.finish(); - Awaitility.await().untilAsserted(() -> then(this.server.getRequestCount()).isGreaterThan(0)); - // first request is for health check - this.server.takeRequest(); - // second request is the span one - RecordedRequest request = this.server.takeRequest(); - then(request.getPath()).isEqualTo("/api/v1/spans"); - then(request.getBody().readUtf8()).contains("binaryAnnotations"); + Awaitility.await().atMost(250, TimeUnit.MILLISECONDS) + .untilAsserted(() -> then(this.server.getRequestCount()).isGreaterThan(0)); + + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + RecordedRequest request = this.server.takeRequest(1, TimeUnit.SECONDS); + then(request.getPath()).isEqualTo("/api/v1/spans"); + then(request.getBody().readUtf8()).contains("binaryAnnotations"); + }); } @Test @@ -281,8 +289,9 @@ public class ZipkinAutoConfigurationTests { public void supportsMultipleReporters() throws Exception { this.context = new AnnotationConfigApplicationContext(); environment().setProperty("spring.zipkin.base-url", this.server.url("/").toString()); - this.context.register(ZipkinAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class, - TraceAutoConfiguration.class, Config.class, MultipleReportersConfig.class); + this.context.register(ZipkinAutoConfiguration.class, ZipkinBraveAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class, TraceAutoConfiguration.class, + TraceBraveAutoConfiguration.class, Config.class, MultipleReportersConfig.class); this.context.refresh(); then(this.context.getBeansOfType(Sender.class)).hasSize(2); @@ -297,23 +306,26 @@ public class ZipkinAutoConfigurationTests { span.finish(); - Awaitility.await().untilAsserted(() -> then(this.server.getRequestCount()).isGreaterThan(1)); - // first request is for health check - this.server.takeRequest(); - // second request is the span one - RecordedRequest request = this.server.takeRequest(); - then(request.getPath()).isEqualTo("/api/v2/spans"); - then(request.getBody().readUtf8()).contains("localEndpoint"); + this.context.getBean(ZipkinAutoConfiguration.REPORTER_BEAN_NAME, AsyncReporter.class).flush(); + Awaitility.await().atMost(250, TimeUnit.MILLISECONDS) + .untilAsserted(() -> then(this.server.getRequestCount()).isGreaterThan(1)); + + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + RecordedRequest request = this.server.takeRequest(1, TimeUnit.SECONDS); + then(request.getPath()).isEqualTo("/api/v2/spans"); + then(request.getBody().readUtf8()).contains("localEndpoint"); + }); MultipleReportersConfig.OtherSender sender = this.context.getBean(MultipleReportersConfig.OtherSender.class); - Awaitility.await().untilAsserted(() -> then(sender.isSpanSent()).isTrue()); + Awaitility.await().atMost(250, TimeUnit.MILLISECONDS).untilAsserted(() -> then(sender.isSpanSent()).isTrue()); } @Test public void shouldOverrideDefaultBeans() { this.context = new AnnotationConfigApplicationContext(); - this.context.register(ZipkinAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class, - TraceAutoConfiguration.class, Config.class, MyConfig.class); + this.context.register(ZipkinAutoConfiguration.class, ZipkinBraveAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class, TraceAutoConfiguration.class, + TraceBraveAutoConfiguration.class, Config.class, MyConfig.class); this.context.refresh(); then(this.context.getBeansOfType(Sender.class)).hasSize(1); @@ -326,10 +338,12 @@ public class ZipkinAutoConfigurationTests { span.finish(); - Awaitility.await().untilAsserted(() -> then(this.server.getRequestCount()).isEqualTo(0)); + Awaitility.await().atMost(250, TimeUnit.MILLISECONDS) + .untilAsserted(() -> then(this.server.getRequestCount()).isEqualTo(0)); + this.context.getBean(ZipkinAutoConfiguration.REPORTER_BEAN_NAME, AsyncReporter.class).flush(); MyConfig.MySender sender = this.context.getBean(MyConfig.MySender.class); - Awaitility.await().untilAsserted(() -> then(sender.isSpanSent()).isTrue()); + Awaitility.await().atMost(250, TimeUnit.MILLISECONDS).untilAsserted(() -> then(sender.isSpanSent()).isTrue()); } @Test @@ -400,7 +414,7 @@ public class ZipkinAutoConfigurationTests { }, 200).error()).isInstanceOf(TimeoutException.class).hasMessage("FakeSender{} check() timed out after 200ms"); } - @Configuration + @Configuration(proxyBeanMethods = false) protected static class Config { @Bean @@ -410,7 +424,7 @@ public class ZipkinAutoConfigurationTests { } - @Configuration + @Configuration(proxyBeanMethods = false) protected static class HandlersConfig { @Bean @@ -437,7 +451,7 @@ public class ZipkinAutoConfigurationTests { } - @Configuration + @Configuration(proxyBeanMethods = false) static class WithMeterRegistry { @Bean @@ -447,7 +461,7 @@ public class ZipkinAutoConfigurationTests { } - @Configuration + @Configuration(proxyBeanMethods = false) static class WithReporter { @Bean @@ -457,12 +471,12 @@ public class ZipkinAutoConfigurationTests { } - @Configuration + @Configuration(proxyBeanMethods = false) protected static class MultipleReportersConfig { @Bean - Reporter otherReporter() { - return AsyncReporter.create(otherSender()); + Reporter otherReporter(OtherSender otherSender) { + return AsyncReporter.create(otherSender); } @Bean @@ -505,12 +519,12 @@ public class ZipkinAutoConfigurationTests { // tag::override_default_beans[] - @Configuration + @Configuration(proxyBeanMethods = false) protected static class MyConfig { @Bean(ZipkinAutoConfiguration.REPORTER_BEAN_NAME) - Reporter myReporter() { - return AsyncReporter.create(mySender()); + Reporter myReporter(@Qualifier(ZipkinAutoConfiguration.SENDER_BEAN_NAME) MySender mySender) { + return AsyncReporter.create(mySender); } @Bean(ZipkinAutoConfiguration.SENDER_BEAN_NAME) diff --git a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinDiscoveryClientTests.java b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinDiscoveryClientTests.java index 529d7e134..a92c2afaf 100644 --- a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinDiscoveryClientTests.java +++ b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinDiscoveryClientTests.java @@ -20,75 +20,55 @@ import java.io.IOException; import java.net.URI; import java.util.Map; -import brave.Span; -import brave.Tracing; -import brave.sampler.Sampler; import okhttp3.mockwebserver.MockWebServer; import org.awaitility.Awaitility; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest; import org.springframework.cloud.client.loadbalancer.Request; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; import static org.assertj.core.api.BDDAssertions.then; -@SpringBootTest(classes = ZipkinDiscoveryClientTests.Config.class, - properties = { "spring.zipkin.baseUrl=https://zipkin/", "spring.zipkin.sender.type=web" // override - // default - // priority - // which - // picks - // rabbit - // due - // to - // classpath - }) -public class ZipkinDiscoveryClientTests { - - public static MockWebServer ZIPKIN_RULE = new MockWebServer(); - - @BeforeAll - static void setup() throws IOException { - ZIPKIN_RULE.start(); - } - - @AfterAll - static void clean() throws IOException { - ZIPKIN_RULE.close(); - } +@ContextConfiguration(classes = ZipkinDiscoveryClientTests.Config.class) +@TestPropertySource(properties = { "spring.zipkin.baseUrl=https://zipkin/", "spring.zipkin.sender.type=web" }) +public abstract class ZipkinDiscoveryClientTests { @Autowired - Tracing tracing; + MockWebServer mockWebServer; + + @Autowired + Tracer tracer; @Test public void shouldUseDiscoveryClientToFindZipkinUrlIfPresent() throws Exception { - Span span = this.tracing.tracer().nextSpan().name("foo").start(); + Span span = this.tracer.nextSpan().name("foo").start(); - span.finish(); + span.end(); - Awaitility.await().untilAsserted(() -> then(ZIPKIN_RULE.getRequestCount()).isGreaterThan(0)); + Awaitility.await().untilAsserted(() -> then(mockWebServer.getRequestCount()).isGreaterThan(0)); } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration static class Config { - @Bean - Sampler sampler() { - return Sampler.ALWAYS_SAMPLE; + @Bean(initMethod = "start", destroyMethod = "close") + MockWebServer mockWebServer() { + return new MockWebServer(); } @Bean - LoadBalancerClient loadBalancerClient() { + LoadBalancerClient loadBalancerClient(MockWebServer mockWebServer) { return new LoadBalancerClient() { @Override public T execute(String serviceId, LoadBalancerRequest request) throws IOException { @@ -125,7 +105,7 @@ public class ZipkinDiscoveryClientTests { @Override public int getPort() { - return ZIPKIN_RULE.url("/").port(); + return mockWebServer.url("/").port(); } @Override @@ -135,7 +115,7 @@ public class ZipkinDiscoveryClientTests { @Override public URI getUri() { - return ZIPKIN_RULE.url("/").uri(); + return mockWebServer.url("/").uri(); } @Override diff --git a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/sender/RestTemplateSenderTest.java b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/sender/RestTemplateSenderTest.java index 7a218fc03..5c9156175 100644 --- a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/sender/RestTemplateSenderTest.java +++ b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/sender/RestTemplateSenderTest.java @@ -17,6 +17,7 @@ package org.springframework.cloud.sleuth.zipkin2.sender; import java.io.IOException; +import java.util.concurrent.TimeUnit; import java.util.stream.Stream; import okhttp3.mockwebserver.MockResponse; @@ -77,7 +78,7 @@ public class RestTemplateSenderTest { send(SPAN).execute(); - RecordedRequest request = this.server.takeRequest(); + RecordedRequest request = this.server.takeRequest(1, TimeUnit.SECONDS); assertThat(request.getHeader("Content-Type")).isEqualTo("application/x-protobuf"); // proto3 encoding of ListOfSpan is simply a repeated span entry diff --git a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinRestTemplateSenderConfigurationTest.java b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinRestTemplateSenderConfigurationTest.java index f3d75355a..20c8a6fc3 100644 --- a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinRestTemplateSenderConfigurationTest.java +++ b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/sender/ZipkinRestTemplateSenderConfigurationTest.java @@ -101,11 +101,11 @@ public class ZipkinRestTemplateSenderConfigurationTest { assertThat(uri.toString()).isEqualTo(URI.create(zipkinProperties.getBaseUrl()).toString()); } - @Configuration + @Configuration(proxyBeanMethods = false) @ConditionalOnClass(LoadBalancerClient.class) static class MyDiscoveryClientZipkinUrlExtractorConfiguration { - @Configuration + @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(value = "spring.zipkin.discovery-client-enabled", havingValue = "true", matchIfMissing = true) static class ZipkinClientLoadBalancedConfiguration { @@ -121,7 +121,7 @@ public class ZipkinRestTemplateSenderConfigurationTest { } - @Configuration + @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(value = "spring.zipkin.discovery-client-enabled", havingValue = "false") static class ZipkinClientNoOpConfiguration { diff --git a/spring-cloud-starter-zipkin/pom.xml b/spring-cloud-starter-sleuth-otel/pom.xml similarity index 66% rename from spring-cloud-starter-zipkin/pom.xml rename to spring-cloud-starter-sleuth-otel/pom.xml index a82079aa6..30762434e 100644 --- a/spring-cloud-starter-zipkin/pom.xml +++ b/spring-cloud-starter-sleuth-otel/pom.xml @@ -25,20 +25,32 @@ 3.0.0-SNAPSHOT .. - spring-cloud-starter-zipkin - Spring Cloud Starter Zipkin - Spring Cloud Starter Zipkin + spring-cloud-starter-sleuth-otel + Spring Cloud Starter Sleuth with OpenTelemetry + Spring Cloud Starter Sleuth with OpenTelemetry ${basedir}/../.. org.springframework.cloud - spring-cloud-starter-sleuth + spring-cloud-starter + + + org.springframework.boot + spring-boot-starter-aop org.springframework.cloud - spring-cloud-sleuth-zipkin + spring-cloud-sleuth-otel + + + io.opentelemetry + opentelemetry-extension-auto-annotations + + + io.opentelemetry + opentelemetry-extension-trace-propagators diff --git a/spring-cloud-starter-sleuth/pom.xml b/spring-cloud-starter-sleuth/pom.xml index aaf5c8a1b..c62bb5fab 100644 --- a/spring-cloud-starter-sleuth/pom.xml +++ b/spring-cloud-starter-sleuth/pom.xml @@ -26,8 +26,8 @@ .. spring-cloud-starter-sleuth - spring-cloud-starter-sleuth - Spring Cloud Starter + Spring Cloud Starter Sleuth with Brave + Spring Cloud Starter Sleuth with Brave ${basedir}/../.. @@ -42,7 +42,7 @@ org.springframework.cloud - spring-cloud-sleuth-core + spring-cloud-sleuth-brave diff --git a/tests/brave/pom.xml b/tests/brave/pom.xml new file mode 100644 index 000000000..3110c2d7b --- /dev/null +++ b/tests/brave/pom.xml @@ -0,0 +1,69 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-tests-brave + pom + Spring Cloud Sleuth Brave Tests + Spring Cloud Sleuth Brave Tests + + + org.springframework.cloud + spring-cloud-sleuth-tests + 3.0.0-SNAPSHOT + .. + + + + spring-cloud-sleuth-instrumentation-annotation-tests + spring-cloud-sleuth-instrumentation-async-tests + spring-cloud-sleuth-instrumentation-baggage-tests + spring-cloud-sleuth-instrumentation-circuitbreaker-tests + spring-cloud-sleuth-instrumentation-feign-tests + spring-cloud-sleuth-instrumentation-gateway-tests + spring-cloud-sleuth-instrumentation-grpc-tests + spring-cloud-sleuth-instrumentation-lettuce-tests + spring-cloud-sleuth-instrumentation-messaging-tests + spring-cloud-sleuth-instrumentation-mvc-tests + spring-cloud-sleuth-instrumentation-quartz-tests + spring-cloud-sleuth-instrumentation-reactor-tests + spring-cloud-sleuth-instrumentation-rxjava-tests + spring-cloud-sleuth-instrumentation-scheduling-tests + spring-cloud-sleuth-instrumentation-webflux-tests + + + + + + + + maven-deploy-plugin + + true + + + + + + + diff --git a/tests/spring-cloud-sleuth-instrumentation-webflux-tests/pom.xml b/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/pom.xml similarity index 78% rename from tests/spring-cloud-sleuth-instrumentation-webflux-tests/pom.xml rename to tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/pom.xml index 7e06c5665..fd9072f9b 100644 --- a/tests/spring-cloud-sleuth-instrumentation-webflux-tests/pom.xml +++ b/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/pom.xml @@ -22,14 +22,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - spring-cloud-sleuth-instrumentation-webflux-tests + spring-cloud-sleuth-instrumentation-annotation-tests jar - Spring Cloud Sleuth WebFlux Instrumentation Tests - Spring Cloud Sleuth WebFlux Instrumentation Tests + Spring Cloud Sleuth Brave Annotation Instrumentation Tests + Spring Cloud Sleuth Brave Annotation Instrumentation Tests org.springframework.cloud - spring-cloud-sleuth-tests + spring-cloud-sleuth-tests-brave 3.0.0-SNAPSHOT .. @@ -51,18 +51,24 @@ + + org.springframework.cloud + spring-cloud-sleuth-tests-common + ${project.version} + org.springframework.boot - spring-boot-starter-actuator - true + spring-boot-starter-aop org.springframework.boot spring-boot-starter-webflux + test org.springframework.cloud - spring-cloud-starter-sleuth + spring-cloud-sleuth-brave + test org.springframework.boot @@ -79,11 +85,6 @@ awaitility test - - org.apache.commons - commons-lang3 - 3.8.1 - diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/brave/annotation/NullSpanTagAnnotationHandlerTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/brave/annotation/NullSpanTagAnnotationHandlerTests.java new file mode 100644 index 000000000..f570e2412 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/brave/annotation/NullSpanTagAnnotationHandlerTests.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2020 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.brave.annotation; + +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = NullSpanTagAnnotationHandlerTests.Config.class) +public class NullSpanTagAnnotationHandlerTests + extends org.springframework.cloud.sleuth.annotation.NullSpanTagAnnotationHandlerTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/brave/annotation/SleuthSpanCreatorAspectFluxTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/brave/annotation/SleuthSpanCreatorAspectFluxTests.java new file mode 100644 index 000000000..951d275a3 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/brave/annotation/SleuthSpanCreatorAspectFluxTests.java @@ -0,0 +1,61 @@ +/* + * Copyright 2013-2020 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.brave.annotation; + +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.brave.bridge.BraveTraceContext; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = SleuthSpanCreatorAspectFluxTests.Config.class) +public class SleuthSpanCreatorAspectFluxTests + extends org.springframework.cloud.sleuth.annotation.SleuthSpanCreatorAspectFluxTests { + + @Override + public TraceContext traceContext() { + return BraveTraceContext + .fromBrave(brave.propagation.TraceContext.newBuilder().traceId(1).spanId(2).sampled(true).build()); + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/brave/annotation/SleuthSpanCreatorAspectMonoTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/brave/annotation/SleuthSpanCreatorAspectMonoTests.java new file mode 100644 index 000000000..a66d81a22 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/brave/annotation/SleuthSpanCreatorAspectMonoTests.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2020 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.brave.annotation; + +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = SleuthSpanCreatorAspectMonoTests.Config.class) +public class SleuthSpanCreatorAspectMonoTests + extends org.springframework.cloud.sleuth.annotation.SleuthSpanCreatorAspectMonoTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/brave/annotation/SleuthSpanCreatorAspectNegativeTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/brave/annotation/SleuthSpanCreatorAspectNegativeTests.java new file mode 100644 index 000000000..f82d314c7 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/brave/annotation/SleuthSpanCreatorAspectNegativeTests.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2020 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.brave.annotation; + +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = SleuthSpanCreatorAspectNegativeTests.Config.class) +public class SleuthSpanCreatorAspectNegativeTests + extends org.springframework.cloud.sleuth.annotation.SleuthSpanCreatorAspectNegativeTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/brave/annotation/SleuthSpanCreatorAspectTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/brave/annotation/SleuthSpanCreatorAspectTests.java new file mode 100644 index 000000000..c623c2b31 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/brave/annotation/SleuthSpanCreatorAspectTests.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2020 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.brave.annotation; + +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = SleuthSpanCreatorAspectTests.Config.class) +public class SleuthSpanCreatorAspectTests + extends org.springframework.cloud.sleuth.annotation.SleuthSpanCreatorAspectTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/brave/annotation/SleuthSpanCreatorCircularDependencyTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/brave/annotation/SleuthSpanCreatorCircularDependencyTests.java new file mode 100644 index 000000000..7f365bf88 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/brave/annotation/SleuthSpanCreatorCircularDependencyTests.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2020 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.brave.annotation; + +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = SleuthSpanCreatorCircularDependencyTests.Config.class) +public class SleuthSpanCreatorCircularDependencyTests + extends org.springframework.cloud.sleuth.annotation.SleuthSpanCreatorCircularDependencyTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/brave/annotation/SpanTagAnnotationHandlerTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/brave/annotation/SpanTagAnnotationHandlerTests.java new file mode 100644 index 000000000..90c5c1ae8 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/brave/annotation/SpanTagAnnotationHandlerTests.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2020 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.brave.annotation; + +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = SpanTagAnnotationHandlerTests.Config.class) +public class SpanTagAnnotationHandlerTests + extends org.springframework.cloud.sleuth.annotation.SpanTagAnnotationHandlerTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/resources/application.yml b/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/resources/application.yml new file mode 100644 index 000000000..caf171df7 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/resources/application.yml @@ -0,0 +1,5 @@ +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE + +spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.cloud.gateway.config.GatewayAutoConfiguration, org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration, org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration \ No newline at end of file diff --git a/tests/spring-cloud-sleuth-instrumentation-async-tests/pom.xml b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/pom.xml similarity index 80% rename from tests/spring-cloud-sleuth-instrumentation-async-tests/pom.xml rename to tests/brave/spring-cloud-sleuth-instrumentation-async-tests/pom.xml index 743aff658..c83cfcfb2 100644 --- a/tests/spring-cloud-sleuth-instrumentation-async-tests/pom.xml +++ b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/pom.xml @@ -24,12 +24,12 @@ spring-cloud-sleuth-instrumentation-async-tests jar - Spring Cloud Sleuth Async Instrumentation Tests - Spring Cloud Sleuth Async Instrumentation Tests + Spring Cloud Sleuth Brave Async Instrumentation Tests + Spring Cloud Sleuth Brave Async Instrumentation Tests org.springframework.cloud - spring-cloud-sleuth-tests + spring-cloud-sleuth-tests-brave 3.0.0-SNAPSHOT .. @@ -51,6 +51,11 @@ + + ${project.groupId} + spring-cloud-sleuth-tests-common + ${project.version} + org.springframework.boot spring-boot-starter-web @@ -59,6 +64,11 @@ org.springframework.cloud spring-cloud-starter-sleuth + + org.springframework.cloud + spring-cloud-sleuth-brave + test + org.springframework.boot spring-boot-starter-test diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/AsyncDisabledTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/AsyncDisabledTests.java new file mode 100644 index 000000000..0b3519a54 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/AsyncDisabledTests.java @@ -0,0 +1,24 @@ +/* + * Copyright 2013-2020 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.brave.instrument.async; + +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +public class AsyncDisabledTests extends org.springframework.cloud.sleuth.instrument.async.AsyncDisabledTests { + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/LazyTraceThreadPoolTaskSchedulerTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/LazyTraceThreadPoolTaskSchedulerTests.java new file mode 100644 index 000000000..2cd3c17e3 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/LazyTraceThreadPoolTaskSchedulerTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2020 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.brave.instrument.async; + +import org.springframework.cloud.sleuth.brave.BraveTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +public class LazyTraceThreadPoolTaskSchedulerTests + extends org.springframework.cloud.sleuth.instrument.async.LazyTraceThreadPoolTaskSchedulerTests { + + BraveTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new BraveTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/TraceAsyncAspectTest.java b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/TraceAsyncAspectTest.java new file mode 100644 index 000000000..262b597c7 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/TraceAsyncAspectTest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2013-2020 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.brave.instrument.async; + +import org.springframework.cloud.sleuth.brave.BraveTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +/** + * @author Marcin Grzejszczak + */ +public class TraceAsyncAspectTest extends org.springframework.cloud.sleuth.instrument.async.TraceAsyncAspectTest { + + BraveTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new BraveTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncIntegrationTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/TraceAsyncIntegrationTests.java similarity index 72% rename from tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncIntegrationTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/TraceAsyncIntegrationTests.java index c03f378c5..9fe4da341 100644 --- a/tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncIntegrationTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/TraceAsyncIntegrationTests.java @@ -14,19 +14,23 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.async; +package org.springframework.cloud.sleuth.brave.instrument.async; +import brave.Span; import brave.SpanCustomizer; +import brave.Tracer; import brave.handler.MutableSpan; import brave.handler.SpanHandler; import brave.propagation.CurrentTraceContext; -import brave.propagation.CurrentTraceContext.Scope; import brave.propagation.TraceContext; import brave.test.IntegrationTestSpanHandler; import org.junit.ClassRule; import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; 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.SpanName; import org.springframework.context.annotation.Bean; @@ -42,6 +46,8 @@ import static org.assertj.core.api.Assertions.assertThat; @DirtiesContext // flakey otherwise public class TraceAsyncIntegrationTests { + private static final Logger log = LoggerFactory.getLogger(TraceAsyncIntegrationTests.class); + @ClassRule public static IntegrationTestSpanHandler spans = new IntegrationTestSpanHandler(); @@ -53,12 +59,17 @@ public class TraceAsyncIntegrationTests { @Autowired CurrentTraceContext currentTraceContext; + @Autowired + Tracer tracer; + @Test public void should_set_span_on_an_async_annotated_method() { - try (Scope ws = currentTraceContext.maybeScope(context)) { + Span parent = tracer.joinSpan(context); + try (Tracer.SpanInScope ws = tracer.withSpanInScope(parent.start())) { + log.info("HELLO"); asyncLogic.invokeAsync(); - MutableSpan span = takeDesirableSpan(); + MutableSpan span = takeDesirableSpan("invoke-async"); assertThat(span.name()).isEqualTo("invoke-async"); assertThat(span.containsAnnotation("@Async")).isTrue(); assertThat(span.tags()).containsEntry("class", "AsyncLogic").containsEntry("method", "invokeAsync"); @@ -66,14 +77,20 @@ public class TraceAsyncIntegrationTests { // continues the trace assertThat(span.traceId()).isEqualTo(context.traceIdString()); } + finally { + parent.finish(); + } + } @Test public void should_set_span_with_custom_method_on_an_async_annotated_method() { - try (Scope ws = currentTraceContext.maybeScope(context)) { + Span parent = tracer.joinSpan(context); + try (Tracer.SpanInScope ws = tracer.withSpanInScope(parent.start())) { + log.info("HELLO"); asyncLogic.invokeAsync_customName(); - MutableSpan span = takeDesirableSpan(); + MutableSpan span = takeDesirableSpan("foo"); assertThat(span.name()).isEqualTo("foo"); assertThat(span.containsAnnotation("@Async")).isTrue(); assertThat(span.tags()).containsEntry("class", "AsyncLogic").containsEntry("method", @@ -82,19 +99,26 @@ public class TraceAsyncIntegrationTests { // continues the trace assertThat(span.traceId()).isEqualTo(context.traceIdString()); } + finally { + parent.finish(); + } } // Sleuth adds spans named "async" with no tags when an executor is used. // We don't want that one. - MutableSpan takeDesirableSpan() { + MutableSpan takeDesirableSpan(String name) { MutableSpan span1 = spans.takeLocalSpan(); MutableSpan span2 = spans.takeLocalSpan(); - return span1.name().equals("async") ? span2 : span1; + log.info("Two last spans [" + span2 + "] and [" + span1 + "]"); + MutableSpan span = span1 != null && name.equals(span1.name()) ? span1 + : span2 != null && name.equals(span2.name()) ? span2 : null; + assertThat(span).as("No span with name <> was found", name).isNotNull(); + return span; } - @DefaultTestAutoConfiguration + @EnableAutoConfiguration @EnableAsync - @Configuration + @Configuration(proxyBeanMethods = false) static class TraceAsyncITestConfiguration { @Bean @@ -111,6 +135,8 @@ public class TraceAsyncIntegrationTests { static class AsyncLogic { + private static final Logger log = LoggerFactory.getLogger(AsyncLogic.class); + final SpanCustomizer customizer; AsyncLogic(SpanCustomizer customizer) { @@ -120,12 +146,14 @@ public class TraceAsyncIntegrationTests { @Async public void invokeAsync() { customizer.annotate("@Async"); // proves the handler is in scope + log.info("HELLO ASYNC"); } @Async @SpanName("foo") public void invokeAsync_customName() { customizer.annotate("@Async"); // proves the handler is in scope + log.info("HELLO ASYNC CUSTOM NAME"); } } diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/TraceAsyncListenableTaskExecutorTest.java b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/TraceAsyncListenableTaskExecutorTest.java new file mode 100644 index 000000000..e3a7d1763 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/TraceAsyncListenableTaskExecutorTest.java @@ -0,0 +1,38 @@ +/* + * Copyright 2013-2020 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.brave.instrument.async; + +import org.springframework.cloud.sleuth.brave.BraveTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +/** + * @author Marcin Grzejszczak + */ +public class TraceAsyncListenableTaskExecutorTest + extends org.springframework.cloud.sleuth.instrument.async.TraceAsyncListenableTaskExecutorTest { + + BraveTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new BraveTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/TraceCallableTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/TraceCallableTests.java new file mode 100644 index 000000000..940a92289 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/TraceCallableTests.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2020 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.brave.instrument.async; + +import org.springframework.cloud.sleuth.brave.BraveTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +public class TraceCallableTests extends org.springframework.cloud.sleuth.instrument.async.TraceCallableTests { + + BraveTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new BraveTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/TraceRunnableTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/TraceRunnableTests.java new file mode 100644 index 000000000..aabd21051 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/TraceRunnableTests.java @@ -0,0 +1,42 @@ +/* + * Copyright 2013-2020 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.brave.instrument.async; + +import org.assertj.core.api.BDDAssertions; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.brave.BraveTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +public class TraceRunnableTests extends org.springframework.cloud.sleuth.instrument.async.TraceRunnableTests { + + BraveTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new BraveTestTracing(); + } + return this.testTracing; + } + + @Override + protected void assertThatThereIsNoParentId(Span secondSpan) { + BDDAssertions.then(secondSpan.context().parentId()).as("saved span as remnant of first span").isNull(); + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/TraceableExecutorServiceTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/TraceableExecutorServiceTests.java new file mode 100644 index 000000000..f8c1b19ed --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/TraceableExecutorServiceTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2020 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.brave.instrument.async; + +import org.springframework.cloud.sleuth.brave.BraveTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +public class TraceableExecutorServiceTests + extends org.springframework.cloud.sleuth.instrument.async.TraceableExecutorServiceTests { + + BraveTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new BraveTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/TraceableScheduledExecutorServiceTest.java b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/TraceableScheduledExecutorServiceTest.java new file mode 100644 index 000000000..43449a92b --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/TraceableScheduledExecutorServiceTest.java @@ -0,0 +1,38 @@ +/* + * Copyright 2013-2020 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.brave.instrument.async; + +import org.springframework.cloud.sleuth.brave.BraveTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +/** + * @author Marcin Grzejszczak + */ +public class TraceableScheduledExecutorServiceTest + extends org.springframework.cloud.sleuth.instrument.async.TraceableScheduledExecutorServiceTest { + + BraveTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new BraveTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/instrument/async/issues/issue1212/GH1212Tests.java b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/issues/issue1212/GH1212Tests.java similarity index 90% rename from tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/instrument/async/issues/issue1212/GH1212Tests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/issues/issue1212/GH1212Tests.java index 53c341569..c6bad280e 100644 --- a/tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/instrument/async/issues/issue1212/GH1212Tests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/issues/issue1212/GH1212Tests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.async.issues.issue1212; +package org.springframework.cloud.sleuth.brave.instrument.async.issues.issue1212; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; @@ -95,7 +95,7 @@ public class GH1212Tests { @SpringBootConfiguration @EnableAutoConfiguration @EnableAsync - static class App { + public static class App { @Bean AsyncComponent asyncComponent() { @@ -104,7 +104,7 @@ public class GH1212Tests { } - static class AsyncComponent { + public static class AsyncComponent { @Async public CompletableFuture asyncMethod() { @@ -117,8 +117,8 @@ public class GH1212Tests { /* * Configuration with a single Executor named `taskExecutor` */ - @Configuration - static class DefaultTaskExecutorConfig { + @Configuration(proxyBeanMethods = false) + public static class DefaultTaskExecutorConfig { @Bean(name = AsyncExecutionAspectSupport.DEFAULT_TASK_EXECUTOR_BEAN_NAME) public Executor taskExecutor() { @@ -130,8 +130,8 @@ public class GH1212Tests { /* * Configuration with a single TaskExecutor */ - @Configuration - static class SingleTaskExecutorConfig { + @Configuration(proxyBeanMethods = false) + public static class SingleTaskExecutorConfig { @Bean // there's the task @@ -146,8 +146,8 @@ public class GH1212Tests { * Configuration with a multiple TaskExecutors --> Spring won't pick any unless one * is @Primary */ - @Configuration - static class MultipleTaskExecutorConfig { + @Configuration(proxyBeanMethods = false) + public static class MultipleTaskExecutorConfig { @Bean public TaskExecutor multipleTaskExecutor1() { @@ -164,8 +164,8 @@ public class GH1212Tests { /* * Configuration where a custom AsyncConfigurer is provided */ - @Configuration - static class CustomAsyncConfigurerConfig { + @Configuration(proxyBeanMethods = false) + public static class CustomAsyncConfigurerConfig { @Bean public AsyncConfigurer customAsyncConfigurer() { diff --git a/tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/instrument/async/issues/issue410/Issue410Tests.java b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/issues/issue410/Issue410Tests.java similarity index 99% rename from tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/instrument/async/issues/issue410/Issue410Tests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/issues/issue410/Issue410Tests.java index 784655cf5..0f241766f 100644 --- a/tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/instrument/async/issues/issue410/Issue410Tests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/issues/issue410/Issue410Tests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.async.issues.issue410; +package org.springframework.cloud.sleuth.brave.instrument.async.issues.issue410; import java.util.Date; import java.util.concurrent.CompletableFuture; @@ -244,7 +244,7 @@ public class Issue410Tests { } -@Configuration +@Configuration(proxyBeanMethods = false) @EnableAsync class AppConfig { diff --git a/tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/instrument/async/issues/issue546/Issue546Tests.java b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/issues/issue546/Issue546Tests.java similarity index 98% rename from tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/instrument/async/issues/issue546/Issue546Tests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/issues/issue546/Issue546Tests.java index a1456a0ed..d3afe82ee 100644 --- a/tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/instrument/async/issues/issue546/Issue546Tests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/async/issues/issue546/Issue546Tests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.async.issues.issue546; +package org.springframework.cloud.sleuth.brave.instrument.async.issues.issue546; import brave.Tracing; import org.apache.commons.logging.Log; diff --git a/tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/resources/application.yml b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/resources/application.yml similarity index 56% rename from tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/resources/application.yml rename to tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/resources/application.yml index f1e4a6a92..9ed06adbb 100644 --- a/tests/spring-cloud-sleuth-instrumentation-async-tests/src/test/resources/application.yml +++ b/tests/brave/spring-cloud-sleuth-instrumentation-async-tests/src/test/resources/application.yml @@ -1,3 +1,3 @@ logging.level.org.springframework.cloud: DEBUG logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR -logging.level.org.springframework.cloud.sleuth.instrument.web.client.feign: TRACE \ No newline at end of file +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE \ No newline at end of file diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/pom.xml b/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/pom.xml new file mode 100644 index 000000000..70fd407d7 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/pom.xml @@ -0,0 +1,93 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-instrumentation-baggage-tests + jar + Spring Cloud Sleuth Brave Baggage Instrumentation Tests + Spring Cloud Sleuth Brave Baggage Instrumentation Tests + + + org.springframework.cloud + spring-cloud-sleuth-tests-brave + 3.0.0-SNAPSHOT + .. + + + + true + + + + + + + maven-deploy-plugin + + true + + + + + + + + org.springframework.cloud + spring-cloud-sleuth-tests-common + ${project.version} + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.integration + spring-integration-core + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.cloud + spring-cloud-sleuth-brave + test + + + org.springframework.boot + spring-boot-starter-test + test + + + io.zipkin.brave + brave-tests + test + + + org.awaitility + awaitility + test + + + + diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/brave/baggage/BaggageEntryTagSpanHandlerTest.java b/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/brave/baggage/BaggageEntryTagSpanHandlerTest.java new file mode 100644 index 000000000..8750e0699 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/brave/baggage/BaggageEntryTagSpanHandlerTest.java @@ -0,0 +1,57 @@ +/* + * Copyright 2013-2020 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.brave.baggage; + +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +/** + * @author Taras Danylchuk + */ +@SpringBootTest(// WebEnvironment.NONE will not read a Yaml profile + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = BaggageEntryTagSpanHandlerTest.Config.class) +public class BaggageEntryTagSpanHandlerTest + extends org.springframework.cloud.sleuth.baggage.BaggageEntryTagSpanHandlerTest { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/brave/baggage/MultipleHopsIntegrationTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/brave/baggage/MultipleHopsIntegrationTests.java new file mode 100644 index 000000000..70a74089f --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/brave/baggage/MultipleHopsIntegrationTests.java @@ -0,0 +1,88 @@ +/* + * Copyright 2013-2020 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.brave.baggage; + +import brave.baggage.BaggageField; +import brave.baggage.BaggagePropagationConfig; +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.brave.bridge.BraveTraceContext; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +import static java.util.Arrays.asList; +import static org.assertj.core.api.BDDAssertions.then; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@SpringBootTest(webEnvironment = RANDOM_PORT) +@ContextConfiguration(classes = MultipleHopsIntegrationTests.Config.class) +public class MultipleHopsIntegrationTests + extends org.springframework.cloud.sleuth.baggage.multiple.MultipleHopsIntegrationTests { + + static final BaggageField REQUEST_ID = BaggageField.create("x-vcap-request-id"); + static final BaggageField COUNTRY_CODE = BaggageField.create("country-code"); + + @Override + protected void assertSpanNames() { + then(this.spans).extracting(FinishedSpan::name).containsAll(asList("GET /greeting", "send")); + } + + @Override + protected void assertBaggage(Span initialSpan) { + // set with baggage api + then(this.application.allSpans()).as("All have request ID") + .allMatch(span -> "f4308d05-2228-4468-80f6-92a8377ba193" + .equals(REQUEST_ID.getValue(BraveTraceContext.toBrave(span.context())))); + + // baz is not tagged in the initial span, only downstream! + then(this.application.allSpans()).as("All downstream have country-code") + .filteredOn(span -> !span.equals(initialSpan)) + .allMatch(span -> "FO".equals(COUNTRY_CODE.getValue(BraveTraceContext.toBrave(span.context())))); + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + @Bean + BaggagePropagationConfig notInProperties() { + return BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create("bar")); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/resources/application.yml b/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/resources/application.yml new file mode 100644 index 000000000..caf171df7 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/resources/application.yml @@ -0,0 +1,5 @@ +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE + +spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.cloud.gateway.config.GatewayAutoConfiguration, org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration, org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration \ No newline at end of file diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/pom.xml b/tests/brave/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/pom.xml new file mode 100644 index 000000000..25c03d501 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/pom.xml @@ -0,0 +1,89 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-instrumentation-circuitbreaker-tests + jar + Spring Cloud Sleuth Brave Circuitbreaker Instrumentation Tests + Spring Cloud Sleuth Brave Circuitbreaker Instrumentation Tests + + + org.springframework.cloud + spring-cloud-sleuth-tests-brave + 3.0.0-SNAPSHOT + .. + + + + true + + + + + + + maven-deploy-plugin + + true + + + + + + + + org.springframework.cloud + spring-cloud-sleuth-tests-common + ${project.version} + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.cloud + spring-cloud-starter-circuitbreaker-resilience4j + + + org.springframework.cloud + spring-cloud-sleuth-brave + test + + + org.springframework.boot + spring-boot-starter-test + test + + + io.zipkin.brave + brave-tests + test + + + org.awaitility + awaitility + test + + + + diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/circuitbreaker/CircuitBreakerIntegrationTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/circuitbreaker/CircuitBreakerIntegrationTests.java new file mode 100644 index 000000000..32ef9540d --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/circuitbreaker/CircuitBreakerIntegrationTests.java @@ -0,0 +1,60 @@ +/* + * Copyright 2013-2020 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.brave.instrument.circuitbreaker; + +import brave.sampler.Sampler; +import org.assertj.core.api.BDDAssertions; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = CircuitBreakerIntegrationTests.Config.class) +public class CircuitBreakerIntegrationTests + extends org.springframework.cloud.sleuth.instrument.circuitbreaker.CircuitBreakerIntegrationTests { + + @Override + public void assertException(FinishedSpan finishedSpan) { + BDDAssertions.then(finishedSpan.tags().get("error")).contains("boom"); + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/circuitbreaker/CircuitBreakerTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/circuitbreaker/CircuitBreakerTests.java new file mode 100644 index 000000000..9878181ca --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/circuitbreaker/CircuitBreakerTests.java @@ -0,0 +1,43 @@ +/* + * Copyright 2013-2020 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.brave.instrument.circuitbreaker; + +import org.assertj.core.api.BDDAssertions; + +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.brave.BraveTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +public class CircuitBreakerTests + extends org.springframework.cloud.sleuth.instrument.circuitbreaker.CircuitBreakerTests { + + BraveTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new BraveTestTracing(); + } + return this.testTracing; + } + + @Override + public void additionalAssertions(FinishedSpan finishedSpan) { + BDDAssertions.then(finishedSpan.tags().get("error")).contains("boom2"); + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/src/test/resources/application.yml b/tests/brave/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/src/test/resources/application.yml new file mode 100644 index 000000000..caf171df7 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/src/test/resources/application.yml @@ -0,0 +1,5 @@ +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE + +spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.cloud.gateway.config.GatewayAutoConfiguration, org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration, org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration \ No newline at end of file diff --git a/tests/spring-cloud-sleuth-instrumentation-feign-tests/pom.xml b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/pom.xml similarity index 76% rename from tests/spring-cloud-sleuth-instrumentation-feign-tests/pom.xml rename to tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/pom.xml index 9b3f9c385..b3534ddc5 100644 --- a/tests/spring-cloud-sleuth-instrumentation-feign-tests/pom.xml +++ b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/pom.xml @@ -24,12 +24,12 @@ spring-cloud-sleuth-instrumentation-feign-tests jar - Spring Cloud Sleuth Feign Instrumentation Tests - Spring Cloud Sleuth Feign Instrumentation Tests + Spring Cloud Sleuth Brave Feign Instrumentation Tests + Spring Cloud Sleuth Brave Feign Instrumentation Tests org.springframework.cloud - spring-cloud-sleuth-tests + spring-cloud-sleuth-tests-brave 3.0.0-SNAPSHOT .. @@ -51,10 +51,31 @@ + + ${project.groupId} + spring-cloud-sleuth-tests-common + ${project.version} + org.springframework.boot spring-boot-starter-web + + com.squareup.okhttp3 + mockwebserver + + + com.squareup.okhttp3 + okhttp + + 4.8.0 + compile + + + org.springframework.cloud + spring-cloud-sleuth-brave + test + org.springframework.cloud spring-cloud-starter-sleuth diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/FeignRetriesTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/FeignRetriesTests.java new file mode 100644 index 000000000..bdaf170bb --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/FeignRetriesTests.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2020 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.brave.instrument.feign; + +import java.io.IOException; + +import org.assertj.core.api.BDDAssertions; + +import org.springframework.cloud.sleuth.brave.BraveIntegrationTestTracing; +import org.springframework.cloud.sleuth.brave.BraveTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +public class FeignRetriesTests extends org.springframework.cloud.sleuth.instrument.web.client.feign.FeignRetriesTests { + + BraveTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new BraveIntegrationTestTracing(); + } + return this.testTracing; + } + + @Override + public void assertException() { + BDDAssertions.then(tracerTest().handler().reportedSpans().get(0).error()).isInstanceOf(IOException.class); + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/TraceFeignAspectTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/TraceFeignAspectTests.java new file mode 100644 index 000000000..8bc1a3b48 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/TraceFeignAspectTests.java @@ -0,0 +1,39 @@ +/* + * Copyright 2013-2020 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.brave.instrument.feign; + +import org.springframework.cloud.sleuth.brave.BraveIntegrationTestTracing; +import org.springframework.cloud.sleuth.brave.BraveTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +/** + * @author Marcin Grzejszczak + */ +public class TraceFeignAspectTests + extends org.springframework.cloud.sleuth.instrument.web.client.feign.TraceFeignAspectTests { + + BraveTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new BraveIntegrationTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/TracingFeignClientTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/TracingFeignClientTests.java new file mode 100644 index 000000000..361241ac9 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/TracingFeignClientTests.java @@ -0,0 +1,42 @@ +/* + * Copyright 2013-2020 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.brave.instrument.feign; + +import org.assertj.core.api.BDDAssertions; + +import org.springframework.cloud.sleuth.brave.BraveTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +public class TracingFeignClientTests + extends org.springframework.cloud.sleuth.instrument.web.client.feign.TracingFeignClientTests { + + BraveTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new BraveTestTracing(); + } + return this.testTracing; + } + + @Override + public void assertException(RuntimeException error) { + BDDAssertions.then(this.tracerTest().handler().reportedSpans().get(0).error()).isSameAs(error); + } + +} diff --git a/tests/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/instrument/feign/issues/issue1125/ManuallyCreatedLoadBalancerFeignClientTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/issues/issue1125/ManuallyCreatedLoadBalancerFeignClientTests.java similarity index 94% rename from tests/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/instrument/feign/issues/issue1125/ManuallyCreatedLoadBalancerFeignClientTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/issues/issue1125/ManuallyCreatedLoadBalancerFeignClientTests.java index 5a2b34221..030c09b4a 100644 --- a/tests/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/instrument/feign/issues/issue1125/ManuallyCreatedLoadBalancerFeignClientTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/issues/issue1125/ManuallyCreatedLoadBalancerFeignClientTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.feign.issues.issue1125; +package org.springframework.cloud.sleuth.brave.instrument.feign.issues.issue1125; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -95,14 +95,14 @@ public class ManuallyCreatedLoadBalancerFeignClientTests { } -@Configuration +@Configuration(proxyBeanMethods = false) @EnableAutoConfiguration @EnableFeignClients class Application { @Bean - public Client client(LoadBalancerClient blockingLoadBalancerClient) { - return new MyBlockingClient(new MyDelegateClient(), blockingLoadBalancerClient); + public Client client(LoadBalancerClient blockingLoadBalancerClient, LoadBalancerProperties properties) { + return new MyBlockingClient(new MyDelegateClient(), blockingLoadBalancerClient, properties); } @Bean @@ -125,8 +125,8 @@ class Application { class MyBlockingClient extends FeignBlockingLoadBalancerClient { - MyBlockingClient(Client delegate, LoadBalancerClient loadBalancerClient) { - super(delegate, loadBalancerClient, new LoadBalancerProperties()); + MyBlockingClient(Client delegate, LoadBalancerClient loadBalancerClient, LoadBalancerProperties properties) { + super(delegate, loadBalancerClient, properties); } boolean wasCalled; diff --git a/tests/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/instrument/feign/issues/issue1125delegates/ManuallyCreatedDelegateLoadBalancerFeignClientTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/issues/issue1125delegates/ManuallyCreatedDelegateLoadBalancerFeignClientTests.java similarity index 97% rename from tests/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/instrument/feign/issues/issue1125delegates/ManuallyCreatedDelegateLoadBalancerFeignClientTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/issues/issue1125delegates/ManuallyCreatedDelegateLoadBalancerFeignClientTests.java index f2f7be677..7886fd62f 100644 --- a/tests/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/instrument/feign/issues/issue1125delegates/ManuallyCreatedDelegateLoadBalancerFeignClientTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/issues/issue1125delegates/ManuallyCreatedDelegateLoadBalancerFeignClientTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.feign.issues.issue1125delegates; +package org.springframework.cloud.sleuth.brave.instrument.feign.issues.issue1125delegates; import java.nio.charset.StandardCharsets; import java.util.HashMap; @@ -102,7 +102,7 @@ public class ManuallyCreatedDelegateLoadBalancerFeignClientTests { } -@Configuration +@Configuration(proxyBeanMethods = false) @EnableAutoConfiguration @Import(FeignClientsConfiguration.class) class Application { diff --git a/tests/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/instrument/feign/issues/issue307/Issue307Tests.java b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/issues/issue307/Issue307Tests.java similarity index 97% rename from tests/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/instrument/feign/issues/issue307/Issue307Tests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/issues/issue307/Issue307Tests.java index bbd270932..3f0b12123 100644 --- a/tests/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/instrument/feign/issues/issue307/Issue307Tests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/issues/issue307/Issue307Tests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.feign.issues.issue307; +package org.springframework.cloud.sleuth.brave.instrument.feign.issues.issue307; import java.util.ArrayList; import java.util.List; diff --git a/tests/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/instrument/feign/issues/issue362/Issue362Tests.java b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/issues/issue362/Issue362Tests.java similarity index 95% rename from tests/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/instrument/feign/issues/issue362/Issue362Tests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/issues/issue362/Issue362Tests.java index e6a3399f1..5c4ab8c73 100644 --- a/tests/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/instrument/feign/issues/issue362/Issue362Tests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/issues/issue362/Issue362Tests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.feign.issues.issue362; +package org.springframework.cloud.sleuth.brave.instrument.feign.issues.issue362; import java.io.IOException; import java.util.Date; @@ -42,6 +42,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpStatus; @@ -124,11 +125,11 @@ public class Issue362Tests { } -@Configuration +@Configuration(proxyBeanMethods = false) @EnableAutoConfiguration( // spring boot test will otherwise instrument the client and server with the // same bean factory which isn't expected - excludeName = "org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration") + exclude = TraceWebServletAutoConfiguration.class) @EnableFeignClients(basePackageClasses = { SleuthTestController.class }) class Application { @@ -170,7 +171,7 @@ class FeignComponentAsserter { } -@Configuration +@Configuration(proxyBeanMethods = false) class CustomConfig { @Bean diff --git a/tests/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/instrument/feign/issues/issue393/Issue393Tests.java b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/issues/issue393/Issue393Tests.java similarity index 94% rename from tests/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/instrument/feign/issues/issue393/Issue393Tests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/issues/issue393/Issue393Tests.java index 835f4ae75..e32fbfd80 100644 --- a/tests/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/instrument/feign/issues/issue393/Issue393Tests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/issues/issue393/Issue393Tests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.feign.issues.issue393; +package org.springframework.cloud.sleuth.brave.instrument.feign.issues.issue393; import java.util.stream.Collectors; @@ -32,6 +32,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.ResponseEntity; @@ -88,11 +89,11 @@ public class Issue393Tests { } -@Configuration +@Configuration(proxyBeanMethods = false) @EnableAutoConfiguration( // spring boot test will otherwise instrument the client and server with the // same bean factory which isn't expected - excludeName = "org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration") + exclude = TraceWebServletAutoConfiguration.class) @EnableFeignClients @EnableDiscoveryClient class Application { diff --git a/tests/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/instrument/feign/issues/issue502/Issue502Tests.java b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/issues/issue502/Issue502Tests.java similarity index 96% rename from tests/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/instrument/feign/issues/issue502/Issue502Tests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/issues/issue502/Issue502Tests.java index 088899532..acdfed24e 100644 --- a/tests/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/instrument/feign/issues/issue502/Issue502Tests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/feign/issues/issue502/Issue502Tests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.feign.issues.issue502; +package org.springframework.cloud.sleuth.brave.instrument.feign.issues.issue502; import java.nio.charset.StandardCharsets; import java.util.HashMap; @@ -85,7 +85,7 @@ public class Issue502Tests { } -@Configuration +@Configuration(proxyBeanMethods = false) @EnableAutoConfiguration @EnableFeignClients class Application { diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/resources/application.yml b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/resources/application.yml new file mode 100644 index 000000000..b3b1e54dd --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-feign-tests/src/test/resources/application.yml @@ -0,0 +1,3 @@ +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE diff --git a/tests/spring-cloud-sleuth-instrumentation-rpc-tests/pom.xml b/tests/brave/spring-cloud-sleuth-instrumentation-gateway-tests/pom.xml similarity index 74% rename from tests/spring-cloud-sleuth-instrumentation-rpc-tests/pom.xml rename to tests/brave/spring-cloud-sleuth-instrumentation-gateway-tests/pom.xml index b1bc2e183..f60006b9b 100644 --- a/tests/spring-cloud-sleuth-instrumentation-rpc-tests/pom.xml +++ b/tests/brave/spring-cloud-sleuth-instrumentation-gateway-tests/pom.xml @@ -22,14 +22,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - spring-cloud-sleuth-instrumentation-rpc-tests + spring-cloud-sleuth-instrumentation-gateway-tests jar - Spring Cloud Sleuth RPC Instrumentation Tests - Spring Cloud Sleuth RPC Instrumentation Tests + Spring Cloud Sleuth Brave Gateway Instrumentation Tests + Spring Cloud Sleuth Brave Gateway Instrumentation Tests org.springframework.cloud - spring-cloud-sleuth-tests + spring-cloud-sleuth-tests-brave 3.0.0-SNAPSHOT .. @@ -52,12 +52,18 @@ - org.springframework.boot - spring-boot-starter-web + org.springframework.cloud + spring-cloud-sleuth-tests-common + ${project.version} org.springframework.cloud - spring-cloud-starter-sleuth + spring-cloud-starter-gateway + + + org.springframework.cloud + spring-cloud-sleuth-brave + test org.springframework.boot diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-gateway-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/TraceRequestHttpHeadersFilterTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-gateway-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/TraceRequestHttpHeadersFilterTests.java new file mode 100644 index 000000000..53b239856 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-gateway-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/TraceRequestHttpHeadersFilterTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web.client; + +import org.springframework.cloud.sleuth.brave.BraveTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +public class TraceRequestHttpHeadersFilterTests + extends org.springframework.cloud.sleuth.instrument.web.client.TraceRequestHttpHeadersFilterTests { + + BraveTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new BraveTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-gateway-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/TraceResponseHttpHeadersFilterTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-gateway-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/TraceResponseHttpHeadersFilterTests.java new file mode 100644 index 000000000..817d8a847 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-gateway-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/TraceResponseHttpHeadersFilterTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web.client; + +import org.springframework.cloud.sleuth.brave.BraveTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +public class TraceResponseHttpHeadersFilterTests + extends org.springframework.cloud.sleuth.instrument.web.client.TraceResponseHttpHeadersFilterTests { + + BraveTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new BraveTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-gateway-tests/src/test/resources/application.yml b/tests/brave/spring-cloud-sleuth-instrumentation-gateway-tests/src/test/resources/application.yml new file mode 100644 index 000000000..caf171df7 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-gateway-tests/src/test/resources/application.yml @@ -0,0 +1,5 @@ +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE + +spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.cloud.gateway.config.GatewayAutoConfiguration, org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration, org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration \ No newline at end of file diff --git a/tests/spring-cloud-sleuth-instrumentation-grpc-tests/pom.xml b/tests/brave/spring-cloud-sleuth-instrumentation-grpc-tests/pom.xml similarity index 88% rename from tests/spring-cloud-sleuth-instrumentation-grpc-tests/pom.xml rename to tests/brave/spring-cloud-sleuth-instrumentation-grpc-tests/pom.xml index 6d70ae19a..692e8dee1 100644 --- a/tests/spring-cloud-sleuth-instrumentation-grpc-tests/pom.xml +++ b/tests/brave/spring-cloud-sleuth-instrumentation-grpc-tests/pom.xml @@ -24,12 +24,12 @@ spring-cloud-sleuth-instrumentation-grpc-tests jar - Spring Cloud Sleuth Grpc Instrumentation Tests - Spring Cloud Sleuth Grpc Instrumentation Tests + Spring Cloud Sleuth Brave Grpc Instrumentation Tests + Spring Cloud Sleuth Brave Grpc Instrumentation Tests org.springframework.cloud - spring-cloud-sleuth-tests + spring-cloud-sleuth-tests-brave 3.0.0-SNAPSHOT .. @@ -59,6 +59,11 @@ org.springframework.cloud spring-cloud-starter-sleuth + + org.springframework.cloud + spring-cloud-sleuth-brave + test + org.springframework.boot spring-boot-starter-test diff --git a/tests/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/GrpcTracingIntegrationTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/grpc/GrpcTracingIntegrationTests.java similarity index 98% rename from tests/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/GrpcTracingIntegrationTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/grpc/GrpcTracingIntegrationTests.java index c136c20f0..578543e6c 100644 --- a/tests/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/GrpcTracingIntegrationTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/grpc/GrpcTracingIntegrationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.grpc; +package org.springframework.cloud.sleuth.brave.instrument.grpc; import java.util.List; import java.util.concurrent.TimeUnit; @@ -125,7 +125,7 @@ public class GrpcTracingIntegrationTests { } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration @Import(HelloGrpcService.class) public static class TestConfiguration { diff --git a/tests/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloReply.java b/tests/brave/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloReply.java similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloReply.java rename to tests/brave/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloReply.java diff --git a/tests/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloReplyOrBuilder.java b/tests/brave/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloReplyOrBuilder.java similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloReplyOrBuilder.java rename to tests/brave/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloReplyOrBuilder.java diff --git a/tests/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloRequest.java b/tests/brave/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloRequest.java similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloRequest.java rename to tests/brave/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloRequest.java diff --git a/tests/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloRequestOrBuilder.java b/tests/brave/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloRequestOrBuilder.java similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloRequestOrBuilder.java rename to tests/brave/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloRequestOrBuilder.java diff --git a/tests/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloServiceGrpc.java b/tests/brave/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloServiceGrpc.java similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloServiceGrpc.java rename to tests/brave/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloServiceGrpc.java diff --git a/tests/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloServiceOuterClass.java b/tests/brave/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloServiceOuterClass.java similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloServiceOuterClass.java rename to tests/brave/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/grpc/stubs/HelloServiceOuterClass.java diff --git a/tests/spring-cloud-sleuth-instrumentation-feign-tests/src/test/resources/application.yml b/tests/brave/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/resources/application.yml similarity index 56% rename from tests/spring-cloud-sleuth-instrumentation-feign-tests/src/test/resources/application.yml rename to tests/brave/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/resources/application.yml index ff5a978ef..9ed06adbb 100644 --- a/tests/spring-cloud-sleuth-instrumentation-feign-tests/src/test/resources/application.yml +++ b/tests/brave/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/resources/application.yml @@ -1,3 +1,3 @@ logging.level.org.springframework.cloud: DEBUG logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR -logging.level.org.springframework.cloud.sleuth.instrument.web.client.feign: TRACE +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE \ No newline at end of file diff --git a/tests/spring-cloud-sleuth-instrumentation-lettuce-tests/pom.xml b/tests/brave/spring-cloud-sleuth-instrumentation-lettuce-tests/pom.xml similarity index 86% rename from tests/spring-cloud-sleuth-instrumentation-lettuce-tests/pom.xml rename to tests/brave/spring-cloud-sleuth-instrumentation-lettuce-tests/pom.xml index dd6a29f1d..4e53a5237 100644 --- a/tests/spring-cloud-sleuth-instrumentation-lettuce-tests/pom.xml +++ b/tests/brave/spring-cloud-sleuth-instrumentation-lettuce-tests/pom.xml @@ -24,12 +24,12 @@ spring-cloud-sleuth-instrumentation-lettuce-tests jar - Spring Cloud Sleuth Lettuce Instrumentation Tests - Spring Cloud Sleuth Lettuce Instrumentation Tests + Spring Cloud Sleuth Brave Lettuce Instrumentation Tests + Spring Cloud Sleuth Brave Lettuce Instrumentation Tests org.springframework.cloud - spring-cloud-sleuth-tests + spring-cloud-sleuth-tests-brave 3.0.0-SNAPSHOT .. @@ -59,6 +59,11 @@ org.springframework.cloud spring-cloud-starter-sleuth + + org.springframework.cloud + spring-cloud-sleuth-brave + test + org.springframework.boot spring-boot-starter-test diff --git a/tests/spring-cloud-sleuth-instrumentation-lettuce-tests/src/test/java/org/springframework/cloud/sleuth/instrument/redis/TraceRedisAutoConfigurationTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-lettuce-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/redis/TraceRedisAutoConfigurationTests.java similarity index 96% rename from tests/spring-cloud-sleuth-instrumentation-lettuce-tests/src/test/java/org/springframework/cloud/sleuth/instrument/redis/TraceRedisAutoConfigurationTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-lettuce-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/redis/TraceRedisAutoConfigurationTests.java index 167c21783..90a053db3 100644 --- a/tests/spring-cloud-sleuth-instrumentation-lettuce-tests/src/test/java/org/springframework/cloud/sleuth/instrument/redis/TraceRedisAutoConfigurationTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-lettuce-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/redis/TraceRedisAutoConfigurationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.redis; +package org.springframework.cloud.sleuth.brave.instrument.redis; import io.lettuce.core.resource.ClientResources; import org.junit.jupiter.api.Test; @@ -48,7 +48,7 @@ public class TraceRedisAutoConfigurationTests { then(this.clientResources.tracing().isEnabled()).isTrue(); } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration protected static class Config { diff --git a/tests/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/resources/application.yml b/tests/brave/spring-cloud-sleuth-instrumentation-lettuce-tests/src/test/resources/application.yml similarity index 56% rename from tests/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/resources/application.yml rename to tests/brave/spring-cloud-sleuth-instrumentation-lettuce-tests/src/test/resources/application.yml index f1e4a6a92..9ed06adbb 100644 --- a/tests/spring-cloud-sleuth-instrumentation-grpc-tests/src/test/resources/application.yml +++ b/tests/brave/spring-cloud-sleuth-instrumentation-lettuce-tests/src/test/resources/application.yml @@ -1,3 +1,3 @@ logging.level.org.springframework.cloud: DEBUG logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR -logging.level.org.springframework.cloud.sleuth.instrument.web.client.feign: TRACE \ No newline at end of file +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE \ No newline at end of file diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/activemq-data/localhost/KahaDB/db.data b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/activemq-data/localhost/KahaDB/db.data similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/activemq-data/localhost/KahaDB/db.data rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/activemq-data/localhost/KahaDB/db.data diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/activemq-data/localhost/KahaDB/db.redo b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/activemq-data/localhost/KahaDB/db.redo similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/activemq-data/localhost/KahaDB/db.redo rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/activemq-data/localhost/KahaDB/db.redo diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/activemq-data/localhost/KahaDB/lock b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/activemq-data/localhost/KahaDB/lock similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/activemq-data/localhost/KahaDB/lock rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/activemq-data/localhost/KahaDB/lock diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/pom.xml b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/pom.xml similarity index 88% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/pom.xml rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/pom.xml index 0436fd2d2..f32873c79 100644 --- a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/pom.xml +++ b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/pom.xml @@ -25,12 +25,12 @@ https://www.w3.org/2001/XMLSchema-instance "> spring-cloud-sleuth-instrumentation-messaging-tests jar - Spring Cloud Sleuth Messaging Instrumentation Tests - Spring Cloud Sleuth Messaging Instrumentation Tests + Spring Cloud Sleuth Brave Messaging Instrumentation Tests + Spring Cloud Sleuth Brave Messaging Instrumentation Tests org.springframework.cloud - spring-cloud-sleuth-tests + spring-cloud-sleuth-tests-brave 3.0.0-SNAPSHOT .. @@ -52,14 +52,24 @@ https://www.w3.org/2001/XMLSchema-instance "> + + org.springframework.cloud + spring-cloud-sleuth-tests-common + ${project.version} + org.springframework.boot - spring-boot-starter-web + spring-boot-starter-aop org.springframework.cloud spring-cloud-starter-sleuth + + org.springframework.cloud + spring-cloud-sleuth-brave + test + org.springframework.cloud spring-cloud-stream @@ -112,33 +122,27 @@ https://www.w3.org/2001/XMLSchema-instance "> javax.jms javax.jms-api - true org.springframework spring-jms - true org.springframework.integration spring-integration-core - true org.springframework.amqp spring-rabbit - true org.springframework.kafka spring-kafka - true org.apache.kafka kafka-streams - true org.springframework.boot @@ -160,7 +164,6 @@ https://www.w3.org/2001/XMLSchema-instance "> org.springframework.boot spring-boot-starter-websocket - true diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessagingAutoConfiguration1664Tests.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TraceMessagingAutoConfiguration1664Tests.java similarity index 96% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessagingAutoConfiguration1664Tests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TraceMessagingAutoConfiguration1664Tests.java index ce8c95713..4678c53ca 100644 --- a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessagingAutoConfiguration1664Tests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TraceMessagingAutoConfiguration1664Tests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.messaging; +package org.springframework.cloud.sleuth.brave.instrument.messaging; import brave.Tracer; import brave.handler.SpanHandler; @@ -57,7 +57,7 @@ public class TraceMessagingAutoConfiguration1664Tests { then(this.mySleuthKafka1664Aspect.adapterWrapped).isTrue(); } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration protected static class Config { diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessagingAutoConfigurationTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TraceMessagingAutoConfigurationTests.java similarity index 94% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessagingAutoConfigurationTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TraceMessagingAutoConfigurationTests.java index e4acb4511..4166bee9b 100644 --- a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessagingAutoConfigurationTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TraceMessagingAutoConfigurationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.messaging; +package org.springframework.cloud.sleuth.brave.instrument.messaging; import brave.Tracer; import brave.handler.SpanHandler; @@ -42,6 +42,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; +import org.springframework.cloud.sleuth.brave.autoconfig.TraceBraveAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.kafka.annotation.KafkaListener; @@ -143,11 +144,12 @@ public class TraceMessagingAutoConfigurationTests { } private ApplicationContextRunner contextRunner(String... propertyValues) { - return new ApplicationContextRunner().withPropertyValues(propertyValues).withConfiguration( - AutoConfigurations.of(TraceAutoConfiguration.class, TraceMessagingAutoConfiguration.class)); + return new ApplicationContextRunner().withPropertyValues(propertyValues) + .withConfiguration(AutoConfigurations.of(TraceBraveAutoConfiguration.class, + TraceAutoConfiguration.class, TraceMessagingAutoConfiguration.class)); } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration protected static class Config { @@ -249,7 +251,7 @@ class TestSleuthJmsBeanPostProcessor extends TracingConnectionFactoryBeanPostPro } -@Configuration +@Configuration(proxyBeanMethods = false) class ProducerSamplerConfig { static final SamplerFunction INSTANCE = request -> null; @@ -261,7 +263,7 @@ class ProducerSamplerConfig { } -@Configuration +@Configuration(proxyBeanMethods = false) class ConsumerSamplerConfig { static final SamplerFunction INSTANCE = request -> null; diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TraceWebSocketAutoConfigurationTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TraceWebSocketAutoConfigurationTests.java new file mode 100644 index 000000000..5b3748e78 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TraceWebSocketAutoConfigurationTests.java @@ -0,0 +1,56 @@ +/* + * Copyright 2013-2020 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.brave.instrument.messaging; + +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +/** + * @author Marcin Grzejszczak + */ +@SpringBootTest +@ContextConfiguration(classes = TraceWebSocketAutoConfigurationTests.Config.class) +public class TraceWebSocketAutoConfigurationTests + extends org.springframework.cloud.sleuth.instrument.messaging.TraceWebSocketAutoConfigurationTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TracingChannelInterceptorTest.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TracingChannelInterceptorTest.java new file mode 100644 index 000000000..e3c4878e8 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TracingChannelInterceptorTest.java @@ -0,0 +1,91 @@ +/* + * Copyright 2013-2020 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.brave.instrument.messaging; + +import java.util.List; +import java.util.Map; + +import brave.Tracing; +import brave.propagation.B3Propagation; +import brave.propagation.TraceContext; +import org.junit.jupiter.api.Test; + +import org.springframework.cloud.sleuth.brave.BraveTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; +import org.springframework.messaging.support.MessageBuilder; +import org.springframework.messaging.support.NativeMessageHeaderAccessor; + +import static brave.propagation.B3Propagation.Format.SINGLE; +import static brave.propagation.B3SingleFormat.parseB3SingleFormat; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.messaging.support.NativeMessageHeaderAccessor.NATIVE_HEADERS; + +public class TracingChannelInterceptorTest + extends org.springframework.cloud.sleuth.instrument.messaging.TracingChannelInterceptorTest { + + BraveTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new BraveTestTracing() { + @Override + public Tracing.Builder tracingBuilder() { + return super.tracingBuilder() + .propagationFactory(B3Propagation.newFactoryBuilder().injectFormat(SINGLE).build()); + } + }; + this.testTracing.reset(); + } + return this.testTracing; + } + + @Test + public void producerConsidersOldSpanIds_nativeHeaders() { + channel.addInterceptor(producerSideOnly(this.interceptor)); + + NativeMessageHeaderAccessor accessor = new NativeMessageHeaderAccessor() { + }; + + accessor.setNativeHeader("b3", "000000000000000a-000000000000000b-1-000000000000000a"); + + this.channel.send(MessageBuilder.withPayload("foo").copyHeaders(accessor.toMessageHeaders()).build()); + + TraceContext receiveContext = parseB3SingleFormat( + ((List) ((Map) this.channel.receive().getHeaders().get(NATIVE_HEADERS)).get("b3")).get(0).toString()) + .context(); + assertThat(receiveContext.parentIdString()).isEqualTo("000000000000000b"); + } + + /** + * If the producer is acting on an un-processed message (ex via a polling consumer), + * it should look at trace headers when there is no span in scope, and use that as the + * parent context. + */ + @Test + public void producerConsidersOldSpanIds() { + this.channel.addInterceptor(producerSideOnly(this.interceptor)); + + this.channel + .send(MessageBuilder.withPayload("foo").setHeader("b3", "000000000000000a-000000000000000b-1").build()); + + TraceContext receiveContext = parseB3SingleFormat(this.channel.receive().getHeaders().get("b3", String.class)) + .context(); + assertThat(receiveContext.parentIdString()).isEqualTo("000000000000000b"); + } + +} diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/ITTracingChannelInterceptorTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/ITTracingChannelInterceptorTests.java similarity index 92% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/ITTracingChannelInterceptorTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/ITTracingChannelInterceptorTests.java index 96caba1a9..9554b84d6 100644 --- a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/ITTracingChannelInterceptorTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/ITTracingChannelInterceptorTests.java @@ -24,7 +24,6 @@ import javax.annotation.PreDestroy; import brave.Span; import brave.Tracer; import brave.Tracing; -import brave.handler.SpanHandler; import brave.propagation.StrictCurrentTraceContext; import brave.test.TestSpanHandler; import org.junit.jupiter.api.AfterEach; @@ -129,14 +128,14 @@ public class ITTracingChannelInterceptorTests implements MessageHandler { assertThat(MessageHeaderAccessor.getAccessor(this.message, MessageHeaderAccessor.class)).isNull(); } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration static class App { ExecutorService service = Executors.newSingleThreadExecutor(); @Bean - SpanHandler testSpanHandler() { + TestSpanHandler testSpanHandler() { return new TestSpanHandler(); } @@ -146,14 +145,13 @@ public class ITTracingChannelInterceptorTests implements MessageHandler { } @Bean - Tracing tracing() { - return Tracing.newBuilder().currentTraceContext(currentTraceContext()).addSpanHandler(testSpanHandler()) - .build(); + Tracing tracing(StrictCurrentTraceContext currentTraceContext, TestSpanHandler spanHandler) { + return Tracing.newBuilder().currentTraceContext(currentTraceContext).addSpanHandler(spanHandler).build(); } @Bean - Tracer tracer() { - return tracing().tracer(); + Tracer tracer(Tracing tracing) { + return tracing.tracer(); } @Bean @@ -172,8 +170,8 @@ public class ITTracingChannelInterceptorTests implements MessageHandler { } @Bean - public MessagingTemplate messagingTemplate() { - return new MessagingTemplate(directChannel()); + public MessagingTemplate messagingTemplate(DirectChannel directChannel) { + return new MessagingTemplate(directChannel); } } diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/JmsTracingConfigurationTest.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/JmsTracingConfigurationTest.java similarity index 98% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/JmsTracingConfigurationTest.java rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/JmsTracingConfigurationTest.java index 25aee6097..9de90e78c 100644 --- a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/JmsTracingConfigurationTest.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/JmsTracingConfigurationTest.java @@ -132,7 +132,7 @@ public class JmsTracingConfigurationTest { } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableJms static class SimpleJmsListenerConfiguration implements JmsListenerConfigurer { @@ -163,7 +163,7 @@ public class JmsTracingConfigurationTest { } -@Configuration +@Configuration(proxyBeanMethods = false) @EnableAutoConfiguration(exclude = KafkaAutoConfiguration.class) class JmsTestTracingConfiguration { diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/SleuthKafkaStreamsConfigurationTest.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/SleuthKafkaStreamsConfigurationTest.java similarity index 98% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/SleuthKafkaStreamsConfigurationTest.java rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/SleuthKafkaStreamsConfigurationTest.java index c5fb5a299..3d6c99392 100644 --- a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/SleuthKafkaStreamsConfigurationTest.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/SleuthKafkaStreamsConfigurationTest.java @@ -44,7 +44,7 @@ public class SleuthKafkaStreamsConfigurationTest { then(streamsBuilderFactoryBean.clientSupplierInvoked).isTrue(); } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration protected static class Config { diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/StreamFunctionAdapterTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/StreamFunctionAdapterTests.java similarity index 96% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/StreamFunctionAdapterTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/StreamFunctionAdapterTests.java index f9fbb90c5..f9f80a1f2 100644 --- a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/StreamFunctionAdapterTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/StreamFunctionAdapterTests.java @@ -20,6 +20,7 @@ import java.util.function.Function; import brave.Span; import brave.test.TestSpanHandler; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,6 +55,7 @@ public class StreamFunctionAdapterTests { OutputDestination outputDestination; @Test + @Disabled("TODO: Waiting for Oleg to fix this") void should_instrument_a_simple_message_to_message_function() { assertThat(tracingChannelInterceptor).as("Ensure that we're doing instrumentation via function wrapper") .isNull(); @@ -71,7 +73,7 @@ public class StreamFunctionAdapterTests { Span.Kind.PRODUCER); } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration @ImportAutoConfiguration(TestChannelBinderConfiguration.class) static class Config { diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/StreamMessageOperatorsTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/StreamMessageOperatorsTests.java similarity index 87% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/StreamMessageOperatorsTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/StreamMessageOperatorsTests.java index ec4e13800..7f17912a7 100644 --- a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/StreamMessageOperatorsTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/StreamMessageOperatorsTests.java @@ -21,13 +21,13 @@ import java.util.Map; import java.util.function.Function; import brave.Span; -import brave.Tracing; import brave.test.TestSpanHandler; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import reactor.core.publisher.Flux; +import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; @@ -76,7 +76,7 @@ public class StreamMessageOperatorsTests { Span.Kind.PRODUCER); } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration @ImportAutoConfiguration(TestChannelBinderConfiguration.class) static class Config { @@ -87,8 +87,8 @@ public class StreamMessageOperatorsTests { } @Bean - SimpleReactiveManualFunction simpleFunction(Tracing tracing) { - return new SimpleReactiveManualFunction(tracing); + SimpleReactiveManualFunction simpleFunction(BeanFactory beanFactory) { + return new SimpleReactiveManualFunction(beanFactory); } } @@ -99,27 +99,27 @@ class SimpleReactiveManualFunction implements Function>, Fl private static final Logger log = LoggerFactory.getLogger(SimpleReactiveManualFunction.class); - private final Tracing tracing; + private final BeanFactory beanFactory; - SimpleReactiveManualFunction(Tracing tracing) { - this.tracing = tracing; + SimpleReactiveManualFunction(BeanFactory beanFactory) { + this.beanFactory = beanFactory; } @Override public Flux> apply(Flux> input) { - return input.map(message -> (MessagingSleuthOperators.asFunction(this.tracing, message)) - .andThen(msg -> MessagingSleuthOperators.withSpanInScope(this.tracing, msg, stringMessage -> { + return input.map(message -> (MessagingSleuthOperators.asFunction(this.beanFactory, message)) + .andThen(msg -> MessagingSleuthOperators.withSpanInScope(this.beanFactory, msg, stringMessage -> { log.info("Hello from simple manual [{}]", stringMessage.getPayload()); return stringMessage; - })).andThen(msg -> MessagingSleuthOperators.afterMessageHandled(this.tracing, msg, null)) + })).andThen(msg -> MessagingSleuthOperators.afterMessageHandled(this.beanFactory, msg, null)) .andThen(msg -> { - MessagingSleuthOperators.withSpanInScope(this.tracing, msg, stringMessage -> { + MessagingSleuthOperators.withSpanInScope(this.beanFactory, msg, stringMessage -> { log.info("Here we may do some processing"); }); Map headers = new HashMap<>(msg.getHeaders()); headers.put("destination", "specialDestination"); return MessageBuilder.createMessage(msg.getPayload().toUpperCase(), new MessageHeaders(headers)); - }).andThen(msg -> MessagingSleuthOperators.handleOutputMessage(this.tracing, msg)).apply(message)); + }).andThen(msg -> MessagingSleuthOperators.handleOutputMessage(this.beanFactory, msg)).apply(message)); } } diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceContextPropagationChannelInterceptorTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceContextPropagationChannelInterceptorTests.java similarity index 98% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceContextPropagationChannelInterceptorTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceContextPropagationChannelInterceptorTests.java index 40a3e6802..1605fbfe1 100644 --- a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceContextPropagationChannelInterceptorTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceContextPropagationChannelInterceptorTests.java @@ -90,7 +90,7 @@ public class TraceContextPropagationChannelInterceptorTests { assertThat(extracted.spanIdString()).as("spanId was equal to parent's id").isNotEqualTo(expectedSpanId); } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration static class App { diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceStreamChannelInterceptorTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceStreamChannelInterceptorTests.java similarity index 98% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceStreamChannelInterceptorTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceStreamChannelInterceptorTests.java index 23bc965b4..065684cf1 100644 --- a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceStreamChannelInterceptorTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceStreamChannelInterceptorTests.java @@ -92,7 +92,7 @@ public class TraceStreamChannelInterceptorTests { assertThat(extracted.spanIdString()).as("spanId was equal to parent's id").isNotEqualTo(expectedSpanId); } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration @ImportAutoConfiguration(TestChannelBinderConfiguration.class) static class App { diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/CustomExecutorConfig.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/CustomExecutorConfig.java similarity index 97% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/CustomExecutorConfig.java rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/CustomExecutorConfig.java index 24c5077cd..51a9636a1 100644 --- a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/CustomExecutorConfig.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/CustomExecutorConfig.java @@ -27,7 +27,7 @@ import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @EnableAsync -@Configuration +@Configuration(proxyBeanMethods = false) public class CustomExecutorConfig extends AsyncConfigurerSupport { @Autowired diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloSpringIntegration.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloSpringIntegration.java similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloSpringIntegration.java rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloSpringIntegration.java diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloWorldImpl.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloWorldImpl.java similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloWorldImpl.java rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloWorldImpl.java diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloWorldRestController.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloWorldRestController.java similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloWorldRestController.java rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloWorldRestController.java diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/Issue943Tests.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/Issue943Tests.java similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/Issue943Tests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/Issue943Tests.java diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/MessagingGateway.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/MessagingGateway.java similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/MessagingGateway.java rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/MessagingGateway.java diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/util/SpanUtil.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/util/SpanUtil.java similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/util/SpanUtil.java rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/util/SpanUtil.java diff --git a/tests/spring-cloud-sleuth-instrumentation-lettuce-tests/src/test/resources/application.yml b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/application.yml similarity index 56% rename from tests/spring-cloud-sleuth-instrumentation-lettuce-tests/src/test/resources/application.yml rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/application.yml index f1e4a6a92..9ed06adbb 100644 --- a/tests/spring-cloud-sleuth-instrumentation-lettuce-tests/src/test/resources/application.yml +++ b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/application.yml @@ -1,3 +1,3 @@ logging.level.org.springframework.cloud: DEBUG logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR -logging.level.org.springframework.cloud.sleuth.instrument.web.client.feign: TRACE \ No newline at end of file +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE \ No newline at end of file diff --git a/spring-cloud-sleuth-core/src/test/resources/beans/applicationContext.xml b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/beans/applicationContext.xml similarity index 100% rename from spring-cloud-sleuth-core/src/test/resources/beans/applicationContext.xml rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/beans/applicationContext.xml diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/logback.xml b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/logback.xml similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/logback.xml rename to tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/logback.xml diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/pom.xml b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/pom.xml new file mode 100644 index 000000000..13a22027e --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/pom.xml @@ -0,0 +1,113 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-instrumentation-mvc-tests + jar + Spring Cloud Sleuth Brave Mvc Instrumentation Tests + Spring Cloud Sleuth Brave Mvc Instrumentation Tests + + + org.springframework.cloud + spring-cloud-sleuth-tests-brave + 3.0.0-SNAPSHOT + .. + + + + true + + + + + + + maven-deploy-plugin + + true + + + + + + + + ${project.groupId} + spring-cloud-sleuth-tests-common + ${project.version} + + + org.springframework.boot + spring-boot-starter-aop + + + org.apache.httpcomponents + httpclient + + + com.squareup.okhttp3 + mockwebserver + + + com.squareup.okhttp3 + okhttp + + 4.8.0 + compile + + + org.springframework.boot + spring-boot-starter-actuator + true + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.cloud + spring-cloud-sleuth-brave + test + + + org.springframework.boot + spring-boot-starter-test + test + + + io.zipkin.brave + brave-tests + test + + + org.awaitility + awaitility + test + + + + diff --git a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/AbstractMvcIntegrationTest.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/AbstractMvcIntegrationTest.java similarity index 97% rename from tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/AbstractMvcIntegrationTest.java rename to tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/AbstractMvcIntegrationTest.java index 28d9dd12f..99ef78cbb 100644 --- a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/AbstractMvcIntegrationTest.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/AbstractMvcIntegrationTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web; +package org.springframework.cloud.sleuth.brave.instrument.web; import org.junit.jupiter.api.BeforeEach; diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/HttpServerParserTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/HttpServerParserTests.java new file mode 100644 index 000000000..196ab4aea --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/HttpServerParserTests.java @@ -0,0 +1,52 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web; + +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = HttpServerParserTests.Config.class) +public class HttpServerParserTests extends org.springframework.cloud.sleuth.instrument.web.HttpServerParserTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/IgnoreAutoConfiguredSkipPatternsIntegrationTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/IgnoreAutoConfiguredSkipPatternsIntegrationTests.java new file mode 100644 index 000000000..fec902c66 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/IgnoreAutoConfiguredSkipPatternsIntegrationTests.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web; + +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = IgnoreAutoConfiguredSkipPatternsIntegrationTests.Config.class) +public class IgnoreAutoConfiguredSkipPatternsIntegrationTests + extends org.springframework.cloud.sleuth.instrument.web.IgnoreAutoConfiguredSkipPatternsIntegrationTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithBasePath.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithBasePath.java new file mode 100644 index 000000000..92ccd050a --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithBasePath.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web; + +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = SkipEndPointsIntegrationTestsWithContextPathWithBasePath.Config.class) +public class SkipEndPointsIntegrationTestsWithContextPathWithBasePath extends + org.springframework.cloud.sleuth.instrument.web.SkipEndPointsIntegrationTestsWithContextPathWithBasePath { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath.java new file mode 100644 index 000000000..2397cd928 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web; + +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath.Config.class) +public class SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath extends + org.springframework.cloud.sleuth.instrument.web.SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath.java new file mode 100644 index 000000000..b1877e501 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web; + +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath.Config.class) +public class SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath extends + org.springframework.cloud.sleuth.instrument.web.SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath.java new file mode 100644 index 000000000..1265662f9 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web; + +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath.Config.class) +public class SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath extends + org.springframework.cloud.sleuth.instrument.web.SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceAsyncIntegrationTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceAsyncIntegrationTests.java similarity index 98% rename from tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceAsyncIntegrationTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceAsyncIntegrationTests.java index 95b165c26..ffbc6807b 100644 --- a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceAsyncIntegrationTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceAsyncIntegrationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web; +package org.springframework.cloud.sleuth.brave.instrument.web; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicReference; @@ -168,7 +168,7 @@ public class TraceAsyncIntegrationTests { @EnableAutoConfiguration @EnableAsync - @Configuration + @Configuration(proxyBeanMethods = false) static class TraceAsyncITestConfiguration { @Bean diff --git a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceCustomFilterResponseInjectorTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceCustomFilterResponseInjectorTests.java similarity index 97% rename from tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceCustomFilterResponseInjectorTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceCustomFilterResponseInjectorTests.java index 334fcb650..f1b007730 100644 --- a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceCustomFilterResponseInjectorTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceCustomFilterResponseInjectorTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web; +package org.springframework.cloud.sleuth.brave.instrument.web; import java.io.IOException; import java.net.URI; @@ -81,7 +81,7 @@ public class TraceCustomFilterResponseInjectorTests { then(responseEntity.getHeaders()).containsKey("b3").as("Trace headers must be present in response headers"); } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration static class Config implements ApplicationListener { diff --git a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterIntegrationTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceFilterIntegrationTests.java similarity index 95% rename from tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterIntegrationTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceFilterIntegrationTests.java index 0b2e19982..b394b9bda 100644 --- a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterIntegrationTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceFilterIntegrationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web; +package org.springframework.cloud.sleuth.brave.instrument.web; import java.io.IOException; import java.util.Optional; @@ -33,7 +33,6 @@ import brave.Tracer; import brave.handler.MutableSpan; import brave.handler.SpanHandler; import brave.sampler.Sampler; -import brave.servlet.TracingFilter; import brave.test.TestSpanHandler; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -46,6 +45,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration; +import org.springframework.cloud.sleuth.instrument.web.servlet.TracingFilter; import org.springframework.cloud.sleuth.util.SpanUtil; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -71,6 +72,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest(classes = TraceFilterIntegrationTests.Config.class) public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest { + static final String MVC_CONTROLLER_CLASS_KEY = "mvc.controller.class"; + static final String MVC_CONTROLLER_METHOD_KEY = "mvc.controller.method"; + private static Span span; @Autowired @@ -97,8 +101,7 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest { then(this.spans).hasSize(1); MutableSpan span = this.spans.get(0); - then(span.tags()).containsKey(TraceWebFilter.MVC_CONTROLLER_CLASS_KEY) - .containsKey(TraceWebFilter.MVC_CONTROLLER_METHOD_KEY); + then(span.tags()).containsKey(MVC_CONTROLLER_CLASS_KEY).containsKey(MVC_CONTROLLER_METHOD_KEY); then(this.tracer.currentSpan()).isNull(); } @@ -280,7 +283,7 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest { } @EnableAutoConfiguration - @Configuration + @Configuration(proxyBeanMethods = false) protected static class Config { private static final Log log = LogFactory.getLog(Config.class); @@ -337,7 +340,7 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest { } - @Configuration + @Configuration(proxyBeanMethods = false) static class ManagementServer { @Bean diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceFilterTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceFilterTests.java new file mode 100644 index 000000000..5e39d2bd4 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceFilterTests.java @@ -0,0 +1,117 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web; + +import java.util.regex.Pattern; + +import brave.http.HttpTracing; +import org.assertj.core.api.BDDAssertions; +import org.junit.jupiter.api.Test; + +import org.springframework.cloud.sleuth.api.http.HttpServerHandler; +import org.springframework.cloud.sleuth.brave.BraveTestTracing; +import org.springframework.cloud.sleuth.brave.bridge.http.BraveHttpServerHandler; +import org.springframework.cloud.sleuth.instrument.web.servlet.TracingFilter; +import org.springframework.cloud.sleuth.test.TestTracingAware; +import org.springframework.cloud.sleuth.test.TracerAware; +import org.springframework.http.HttpMethod; +import org.springframework.mock.web.MockServletContext; + +/** + * @author Spencer Gibb + */ +public class TraceFilterTests extends org.springframework.cloud.sleuth.instrument.web.TraceFilterTests { + + BraveTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new BraveTestTracing(); + } + return this.testTracing; + } + + @Override + public HttpServerHandler httpServerHandler() { + HttpTracing httpTracing = this.testTracing.httpTracingBuilder() + .serverSampler(new SkipPatternHttpServerSampler(() -> Pattern.compile(""))).build(); + return new BraveHttpServerHandler(brave.http.HttpServerHandler.create(httpTracing)); + } + + @Test + public void createsChildFromHeadersWhenJoinUnsupported() throws Exception { + this.request = builder().header("b3", "0000000000000014-000000000000000a") + .buildRequest(new MockServletContext()); + TracerAware aware = tracerTest().tracing(); + BraveTestTracing braveTestTracing = ((BraveTestTracing) aware); + braveTestTracing.tracingBuilder(braveTestTracing.tracingBuilder().supportsJoin(false)).reset(); + + TracingFilter.create(aware.currentTraceContext(), httpServerHandler()).doFilter(this.request, this.response, + this.filterChain); + + BDDAssertions.then(tracerTest().tracing().tracer().currentSpan()).isNull(); + BDDAssertions.then(tracerTest().handler()).hasSize(1); + BDDAssertions.then(tracerTest().handler().get(0).parentId()).isEqualTo("000000000000000a"); + } + + @Test + public void samplesASpanDebugFlagWithInterceptor() throws Exception { + this.request = builder().header("b3", "d").buildRequest(new MockServletContext()); + + neverSampleFilter().doFilter(this.request, this.response, this.filterChain); + + BDDAssertions.then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("GET"); + } + + @Test + public void shouldNotStoreHttpStatusCodeWhenResponseCodeHasNotYetBeenSet() throws Exception { + this.response.setStatus(0); + this.filter.doFilter(this.request, this.response, this.filterChain); + + BDDAssertions.then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).tags()).doesNotContainKey("http.status_code"); + } + + @Test + public void samplesASpanRegardlessOfTheSamplerWhenDebugIsPresent() throws Exception { + this.request = builder().header("b3", "d").buildRequest(new MockServletContext()); + + neverSampleFilter().doFilter(this.request, this.response, this.filterChain); + + BDDAssertions.then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).isNotEmpty(); + } + + @Test + public void startsNewTraceWithParentIdInHeaders() throws Exception { + this.request = builder().header("b3", "0000000000000002-0000000000000003-1-000000000000000a") + .buildRequest(new MockServletContext()); + + this.filter.doFilter(this.request, this.response, this.filterChain); + + BDDAssertions.then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).spanId()).isEqualTo("0000000000000003"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("http.path", "/").containsEntry("http.method", + HttpMethod.GET.toString()); + } + +} diff --git a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterWebIntegrationMultipleFiltersTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceFilterWebIntegrationMultipleFiltersTests.java similarity index 98% rename from tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterWebIntegrationMultipleFiltersTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceFilterWebIntegrationMultipleFiltersTests.java index dce8532d0..eeacaa712 100644 --- a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterWebIntegrationMultipleFiltersTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceFilterWebIntegrationMultipleFiltersTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web; +package org.springframework.cloud.sleuth.brave.instrument.web; import java.io.IOException; import java.util.concurrent.Executor; @@ -102,7 +102,7 @@ public class TraceFilterWebIntegrationMultipleFiltersTests { } @EnableAutoConfiguration - @Configuration + @Configuration(proxyBeanMethods = false) public static class Config { // issue #550 diff --git a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterWebIntegrationTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceFilterWebIntegrationTests.java similarity index 94% rename from tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterWebIntegrationTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceFilterWebIntegrationTests.java index 7bf045a1f..d17863743 100644 --- a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterWebIntegrationTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceFilterWebIntegrationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web; +package org.springframework.cloud.sleuth.brave.instrument.web; import java.io.IOException; import java.util.Arrays; @@ -49,6 +49,10 @@ import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.OutputCaptureExtension; import org.springframework.cloud.sleuth.annotation.ContinueSpan; import org.springframework.cloud.sleuth.annotation.SpanTag; +import org.springframework.cloud.sleuth.instrument.web.HttpClientRequestParser; +import org.springframework.cloud.sleuth.instrument.web.HttpServerRequestParser; +import org.springframework.cloud.sleuth.instrument.web.HttpServerSampler; +import org.springframework.cloud.sleuth.instrument.web.SkipPatternProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @@ -165,8 +169,8 @@ public class TraceFilterWebIntegrationTests { @EnableAutoConfiguration( // spring boot test will otherwise instrument the client and server with the // same bean factory which isn't expected - excludeName = "org.springframework.cloud.sleuth.instrument.web.client.TraceWebClientAutoConfiguration") - @Configuration + excludeName = "org.springframework.cloud.sleuth.brave.instrument.web.client.TraceWebClientAutoConfiguration") + @Configuration(proxyBeanMethods = false) public static class Config { @Bean diff --git a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceWebDisabledTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceWebDisabledTests.java similarity index 91% rename from tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceWebDisabledTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceWebDisabledTests.java index b7312daa7..9ce9dfbf4 100644 --- a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceWebDisabledTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceWebDisabledTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web; +package org.springframework.cloud.sleuth.brave.instrument.web; import org.junit.jupiter.api.Test; @@ -33,7 +33,7 @@ public class TraceWebDisabledTests { } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration public static class Config { diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/MultipleAsyncRestTemplateTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/MultipleAsyncRestTemplateTests.java new file mode 100644 index 000000000..84608ae62 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/MultipleAsyncRestTemplateTests.java @@ -0,0 +1,56 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web.client; + +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +/** + * @author Marcin Grzejszczak + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = MultipleAsyncRestTemplateTests.Config.class) +public class MultipleAsyncRestTemplateTests + extends org.springframework.cloud.sleuth.instrument.web.client.MultipleAsyncRestTemplateTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/RestTemplateTraceAspectIntegrationTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/RestTemplateTraceAspectIntegrationTests.java similarity index 93% rename from tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/RestTemplateTraceAspectIntegrationTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/RestTemplateTraceAspectIntegrationTests.java index 94680a846..6e1fc0083 100644 --- a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/RestTemplateTraceAspectIntegrationTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/RestTemplateTraceAspectIntegrationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web.client; +package org.springframework.cloud.sleuth.brave.instrument.web.client; import java.util.Collections; import java.util.concurrent.Callable; @@ -27,7 +27,6 @@ import brave.handler.MutableSpan; import brave.handler.SpanHandler; import brave.propagation.CurrentTraceContext; import brave.sampler.Sampler; -import brave.spring.web.TracingAsyncClientHttpRequestInterceptor; import brave.test.TestSpanHandler; import org.awaitility.Awaitility; import org.junit.jupiter.api.AfterEach; @@ -37,6 +36,9 @@ import org.junit.jupiter.api.Test; 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.api.http.HttpClientHandler; +import org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration; +import org.springframework.cloud.sleuth.instrument.web.mvc.TracingAsyncClientHttpRequestInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; import org.springframework.core.env.Environment; @@ -176,7 +178,7 @@ public class RestTemplateTraceAspectIntegrationTests { @EnableAutoConfiguration( // spring boot test will otherwise instrument the client and server with the // same bean factory which isn't expected - excludeName = "org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration") + exclude = TraceWebServletAutoConfiguration.class) @Import(AspectTestingController.class) public static class Config { @@ -191,10 +193,12 @@ public class RestTemplateTraceAspectIntegrationTests { } @Bean - public AsyncRestTemplate asyncRestTemplate(Tracing tracing) { + public AsyncRestTemplate asyncRestTemplate( + org.springframework.cloud.sleuth.api.CurrentTraceContext currentTraceContext, + HttpClientHandler httpClientHandler) { AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate(); - asyncRestTemplate.setInterceptors( - Collections.singletonList(TracingAsyncClientHttpRequestInterceptor.create(tracing))); + asyncRestTemplate.setInterceptors(Collections.singletonList( + TracingAsyncClientHttpRequestInterceptor.create(currentTraceContext, httpClientHandler))); return asyncRestTemplate; } diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/TraceRestTemplateInterceptorIntegrationTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/TraceRestTemplateInterceptorIntegrationTests.java new file mode 100644 index 000000000..57c9e11a0 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/TraceRestTemplateInterceptorIntegrationTests.java @@ -0,0 +1,38 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web.client; + +import org.springframework.cloud.sleuth.brave.BraveTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +/** + * @author Marcin Grzejszczak + */ +public class TraceRestTemplateInterceptorIntegrationTests + extends org.springframework.cloud.sleuth.instrument.web.client.TraceRestTemplateInterceptorIntegrationTests { + + BraveTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new BraveTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/TraceRestTemplateInterceptorTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/TraceRestTemplateInterceptorTests.java new file mode 100644 index 000000000..2556c17e9 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/TraceRestTemplateInterceptorTests.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web.client; + +import java.util.Map; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.brave.BraveTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +import static org.assertj.core.api.BDDAssertions.then; + +/** + * @author Dave Syer + * + */ +public class TraceRestTemplateInterceptorTests + extends org.springframework.cloud.sleuth.instrument.web.client.TraceRestTemplateInterceptorTests { + + BraveTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new BraveTestTracing(); + } + return this.testTracing; + } + + @Override + public void assertThatParentSpanIdSet(Span span, Map headers) { + then(headers.get("X-B3-ParentSpanId")).isEqualTo(span.context().spanId()); + } + +} diff --git a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebAsyncClientAutoConfigurationTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/TraceWebAsyncClientAutoConfigurationTests.java similarity index 95% rename from tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebAsyncClientAutoConfigurationTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/TraceWebAsyncClientAutoConfigurationTests.java index 5114cab20..0dba8011d 100644 --- a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebAsyncClientAutoConfigurationTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/TraceWebAsyncClientAutoConfigurationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web.client; +package org.springframework.cloud.sleuth.brave.instrument.web.client; import java.util.ArrayList; import java.util.concurrent.ExecutionException; @@ -35,6 +35,7 @@ import org.junit.jupiter.api.Test; 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.instrument.web.TraceWebServletAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @@ -122,8 +123,8 @@ public class TraceWebAsyncClientAutoConfigurationTests { @EnableAutoConfiguration( // spring boot test will otherwise instrument the client and server with the // same bean factory which isn't expected - excludeName = "org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration") - @Configuration + exclude = TraceWebServletAutoConfiguration.class) + @Configuration(proxyBeanMethods = false) public static class TestConfiguration { @Bean diff --git a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/exceptionresolver/Issue585Tests.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/exceptionresolver/Issue585Tests.java similarity index 98% rename from tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/exceptionresolver/Issue585Tests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/exceptionresolver/Issue585Tests.java index 1386a6cd9..8d03d2ab3 100644 --- a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/exceptionresolver/Issue585Tests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/exceptionresolver/Issue585Tests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web.client.exceptionresolver; +package org.springframework.cloud.sleuth.brave.instrument.web.client.exceptionresolver; import java.time.Instant; diff --git a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/view/Issue469.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/view/Issue469.java similarity index 92% rename from tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/view/Issue469.java rename to tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/view/Issue469.java index cd630af3a..71855f9bb 100644 --- a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/view/Issue469.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/view/Issue469.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web.view; +package org.springframework.cloud.sleuth.brave.instrument.web.view; import brave.handler.SpanHandler; import brave.sampler.Sampler; @@ -27,7 +27,7 @@ import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; @EnableAutoConfiguration -@Configuration +@Configuration(proxyBeanMethods = false) public class Issue469 extends WebMvcConfigurerAdapter { @Override diff --git a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/view/Issue469Tests.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/view/Issue469Tests.java similarity index 96% rename from tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/view/Issue469Tests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/view/Issue469Tests.java index 30b4b6bcb..4cbcc94c6 100644 --- a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/view/Issue469Tests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/view/Issue469Tests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web.view; +package org.springframework.cloud.sleuth.brave.instrument.web.view; import brave.test.TestSpanHandler; import org.junit.jupiter.api.Test; diff --git a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/util/SpanUtil.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/util/SpanUtil.java similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/util/SpanUtil.java rename to tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/util/SpanUtil.java diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/resources/application.yml b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/resources/application.yml new file mode 100644 index 000000000..9ed06adbb --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/resources/application.yml @@ -0,0 +1,3 @@ +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE \ No newline at end of file diff --git a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/pom.xml b/tests/brave/spring-cloud-sleuth-instrumentation-quartz-tests/pom.xml similarity index 78% rename from tests/spring-cloud-sleuth-instrumentation-mvc-tests/pom.xml rename to tests/brave/spring-cloud-sleuth-instrumentation-quartz-tests/pom.xml index 92ce92866..372729b77 100644 --- a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/pom.xml +++ b/tests/brave/spring-cloud-sleuth-instrumentation-quartz-tests/pom.xml @@ -22,14 +22,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - spring-cloud-sleuth-instrumentation-mvc-tests + spring-cloud-sleuth-instrumentation-quartz-tests jar - Spring Cloud Sleuth Mvc Instrumentation Tests - Spring Cloud Sleuth Mvc Instrumentation Tests + Spring Cloud Sleuth Brave Quartz Instrumentation Tests + Spring Cloud Sleuth Brave Quartz Instrumentation Tests org.springframework.cloud - spring-cloud-sleuth-tests + spring-cloud-sleuth-tests-brave 3.0.0-SNAPSHOT .. @@ -53,16 +53,17 @@ org.springframework.boot - spring-boot-starter-actuator - true - - - org.springframework.boot - spring-boot-starter-web + spring-boot-starter-quartz org.springframework.cloud - spring-cloud-starter-sleuth + spring-cloud-sleuth-tests-common + ${project.version} + + + org.springframework.cloud + spring-cloud-sleuth-brave + test org.springframework.boot diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-quartz-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/quartz/TracingJobListenerTest.java b/tests/brave/spring-cloud-sleuth-instrumentation-quartz-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/quartz/TracingJobListenerTest.java new file mode 100644 index 000000000..825e17d6d --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-quartz-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/quartz/TracingJobListenerTest.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2020 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.brave.instrument.quartz; + +import org.springframework.cloud.sleuth.brave.BraveIntegrationTestTracing; +import org.springframework.cloud.sleuth.brave.BraveTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +public class TracingJobListenerTest extends org.springframework.cloud.sleuth.instrument.quartz.TracingJobListenerTest { + + BraveTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new BraveIntegrationTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-quartz-tests/src/test/resources/application.yml b/tests/brave/spring-cloud-sleuth-instrumentation-quartz-tests/src/test/resources/application.yml new file mode 100644 index 000000000..caf171df7 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-quartz-tests/src/test/resources/application.yml @@ -0,0 +1,5 @@ +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE + +spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.cloud.gateway.config.GatewayAutoConfiguration, org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration, org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration \ No newline at end of file diff --git a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/pom.xml b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/pom.xml similarity index 88% rename from tests/spring-cloud-sleuth-instrumentation-reactor-tests/pom.xml rename to tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/pom.xml index ce06e6c79..3c8ce77e8 100644 --- a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/pom.xml +++ b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/pom.xml @@ -24,12 +24,12 @@ spring-cloud-sleuth-instrumentation-reactor-tests jar - Spring Cloud Sleuth Reactor Instrumentation Tests - Spring Cloud Sleuth Reactor Instrumentation Tests + Spring Cloud Sleuth Brave Reactor Instrumentation Tests + Spring Cloud Sleuth Brave Reactor Instrumentation Tests org.springframework.cloud - spring-cloud-sleuth-tests + spring-cloud-sleuth-tests-brave 3.0.0-SNAPSHOT .. @@ -59,6 +59,11 @@ org.springframework.cloud spring-cloud-starter-sleuth + + org.springframework.cloud + spring-cloud-sleuth-brave + test + io.zipkin.brave brave-instrumentation-http-tests diff --git a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/FlowsScopePassingSpanSubscriberTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/FlowsScopePassingSpanSubscriberTests.java similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/FlowsScopePassingSpanSubscriberTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/FlowsScopePassingSpanSubscriberTests.java diff --git a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/Issue866Configuration.java b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/Issue866Configuration.java similarity index 98% rename from tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/Issue866Configuration.java rename to tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/Issue866Configuration.java index 4ecbb4a93..ee7d18cd5 100644 --- a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/Issue866Configuration.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/Issue866Configuration.java @@ -28,7 +28,7 @@ import org.springframework.context.annotation.Configuration; /** * @author Marcin Grzejszczak */ -@Configuration +@Configuration(proxyBeanMethods = false) public class Issue866Configuration { private static final Log log = LogFactory.getLog(Issue866Configuration.class); diff --git a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/ScopePassingSpanSubscriberSpringBootTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/ScopePassingSpanSubscriberSpringBootTests.java similarity index 99% rename from tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/ScopePassingSpanSubscriberSpringBootTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/ScopePassingSpanSubscriberSpringBootTests.java index 311830181..43e2427db 100644 --- a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/ScopePassingSpanSubscriberSpringBootTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/ScopePassingSpanSubscriberSpringBootTests.java @@ -176,7 +176,7 @@ public class ScopePassingSpanSubscriberSpringBootTests { } @EnableAutoConfiguration - @Configuration + @Configuration(proxyBeanMethods = false) static class Config { @Bean diff --git a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/ScopePassingSpanSubscriberTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/ScopePassingSpanSubscriberTests.java similarity index 84% rename from tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/ScopePassingSpanSubscriberTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/ScopePassingSpanSubscriberTests.java index c5d968251..5804a9c1b 100644 --- a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/ScopePassingSpanSubscriberTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/ScopePassingSpanSubscriberTests.java @@ -19,10 +19,7 @@ package org.springframework.cloud.sleuth.instrument.reactor; import java.util.Objects; import java.util.function.Function; -import brave.propagation.CurrentTraceContext; -import brave.propagation.CurrentTraceContext.Scope; import brave.propagation.StrictCurrentTraceContext; -import brave.propagation.TraceContext; import org.assertj.core.presentation.StandardRepresentation; import org.junit.After; import org.junit.Before; @@ -37,6 +34,10 @@ import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; import reactor.util.context.Context; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.brave.bridge.BraveCurrentTraceContext; +import org.springframework.cloud.sleuth.brave.bridge.BraveTraceContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import static org.assertj.core.api.Assertions.assertThat; @@ -58,11 +59,15 @@ public class ScopePassingSpanSubscriberTests { StandardRepresentation.registerFormatterForType(ScopePassingSpanSubscriber.class, Objects::toString); } - StrictCurrentTraceContext currentTraceContext = StrictCurrentTraceContext.create(); + StrictCurrentTraceContext traceContext = StrictCurrentTraceContext.create(); - TraceContext context = TraceContext.newBuilder().traceId(1).spanId(1).sampled(true).build(); + CurrentTraceContext currentTraceContext = BraveCurrentTraceContext.fromBrave(traceContext); - TraceContext context2 = TraceContext.newBuilder().traceId(1).spanId(2).sampled(true).build(); + TraceContext context = BraveTraceContext + .fromBrave(brave.propagation.TraceContext.newBuilder().traceId(1).spanId(1).sampled(true).build()); + + TraceContext context2 = BraveTraceContext + .fromBrave(brave.propagation.TraceContext.newBuilder().traceId(1).spanId(2).sampled(true).build()); Subscriber assertNotScopePassingSpanSubscriber = new CoreSubscriber() { @Override @@ -124,7 +129,7 @@ public class ScopePassingSpanSubscriberTests { @After public void close() { springContext.close(); - currentTraceContext.close(); + traceContext.close(); } @Test @@ -144,7 +149,7 @@ public class ScopePassingSpanSubscriberTests { ScopePassingSpanSubscriber subscriber = new ScopePassingSpanSubscriber<>(null, initial, this.currentTraceContext, context); - then(initial).isSameAs(subscriber.currentContext()); + then(initial.get(TraceContext.class)).isSameAs(subscriber.currentContext().get(TraceContext.class)); } @Test @@ -157,7 +162,7 @@ public class ScopePassingSpanSubscriberTests { @Test public void should_put_current_span_to_context() { - try (Scope ws = this.currentTraceContext.newScope(context2)) { + try (CurrentTraceContext.Scope ws = this.currentTraceContext.newScope(context2)) { CoreSubscriber subscriber = new ScopePassingSpanSubscriber<>(new BaseSubscriber() { }, Context.empty(), currentTraceContext, context); @@ -173,7 +178,7 @@ public class ScopePassingSpanSubscriberTests { Function, ? extends Publisher> transformer = scopePassingSpanOperator( this.springContext); - try (Scope ws = this.currentTraceContext.newScope(context)) { + try (CurrentTraceContext.Scope ws = this.currentTraceContext.newScope(context)) { transformer.apply(Mono.just(1)).subscribe(assertNotScopePassingSpanSubscriber); @@ -192,7 +197,7 @@ public class ScopePassingSpanSubscriberTests { Function, ? extends Publisher> transformer = scopePassingSpanOperator( this.springContext); - try (Scope ws = this.currentTraceContext.newScope(context)) { + try (CurrentTraceContext.Scope ws = this.currentTraceContext.newScope(context)) { transformer.apply(Mono.just(1).hide()).subscribe(assertScopePassingSpanSubscriber); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/TraceReactorAutoConfigurationAccessorConfiguration.java b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/TraceReactorAutoConfigurationAccessorConfiguration.java similarity index 100% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/TraceReactorAutoConfigurationAccessorConfiguration.java rename to tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/TraceReactorAutoConfigurationAccessorConfiguration.java diff --git a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/sample/FlatMapTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/sample/FlatMapTests.java similarity index 92% rename from tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/sample/FlatMapTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/sample/FlatMapTests.java index 7eb48f2da..9541c402a 100644 --- a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/sample/FlatMapTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/sample/FlatMapTests.java @@ -23,7 +23,6 @@ import java.util.stream.Collectors; import brave.Span; import brave.Tracer; -import brave.Tracing; import brave.handler.MutableSpan; import brave.handler.SpanHandler; import brave.sampler.Sampler; @@ -43,6 +42,8 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.brave.bridge.BraveSpan; import org.springframework.cloud.sleuth.instrument.reactor.Issue866Configuration; import org.springframework.cloud.sleuth.instrument.reactor.TraceReactorAutoConfigurationAccessorConfiguration; import org.springframework.cloud.sleuth.instrument.web.WebFluxSleuthOperators; @@ -182,7 +183,7 @@ public class FlatMapTests { return traceIdOfFlatMap.get(0); } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration static class TestConfiguration { @@ -239,37 +240,40 @@ public class FlatMapTests { } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration static class TestManualConfiguration { brave.Span spanInFoo; @Bean - RouterFunction handlers(Tracing tracing, ManualRequestSender requestSender) { + RouterFunction handlers(org.springframework.cloud.sleuth.api.Tracer tracing, + CurrentTraceContext currentTraceContext, ManualRequestSender requestSender) { return route(GET("/noFlatMap"), request -> { ServerWebExchange exchange = request.exchange(); - WebFluxSleuthOperators.withSpanInScope(tracing, exchange, () -> LOGGER.info("noFlatMap")); + WebFluxSleuthOperators.withSpanInScope(tracing, currentTraceContext, exchange, + () -> LOGGER.info("noFlatMap")); Flux one = requestSender.getAll().map(String::length); return ServerResponse.ok().body(one, Integer.class); }).andRoute(GET("/withFlatMap"), request -> { ServerWebExchange exchange = request.exchange(); - WebFluxSleuthOperators.withSpanInScope(tracing, exchange, () -> LOGGER.info("withFlatMap")); + WebFluxSleuthOperators.withSpanInScope(tracing, currentTraceContext, exchange, + () -> LOGGER.info("withFlatMap")); Flux one = requestSender.getAll().map(String::length); Flux response = one .flatMap(size -> requestSender.getAll().doOnEach(sig -> WebFluxSleuthOperators .withSpanInScope(sig.getContext(), () -> LOGGER.info(sig.getContext().toString())))) .map(string -> { - WebFluxSleuthOperators.withSpanInScope(tracing, exchange, + WebFluxSleuthOperators.withSpanInScope(tracing, currentTraceContext, exchange, () -> LOGGER.info("WHATEVER YEAH")); return string.length(); }); return ServerResponse.ok().body(response, Integer.class); }).andRoute(GET("/foo"), request -> { ServerWebExchange exchange = request.exchange(); - WebFluxSleuthOperators.withSpanInScope(tracing, exchange, () -> { + WebFluxSleuthOperators.withSpanInScope(tracing, currentTraceContext, exchange, () -> { LOGGER.info("foo"); - this.spanInFoo = tracing.tracer().currentSpan(); + this.spanInFoo = BraveSpan.toBrave(tracing.currentSpan()); }); return ServerResponse.ok().body(Flux.just(1), Integer.class); }); diff --git a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/sample/ManualRequestSender.java b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/sample/ManualRequestSender.java similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/sample/ManualRequestSender.java rename to tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/sample/ManualRequestSender.java diff --git a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/sample/RequestSender.java b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/sample/RequestSender.java similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/sample/RequestSender.java rename to tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/sample/RequestSender.java diff --git a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/ITSpringConfiguredReactorClient.java b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/ITSpringConfiguredReactorClient.java similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/ITSpringConfiguredReactorClient.java rename to tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/ITSpringConfiguredReactorClient.java diff --git a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/ReactorNettyHttpClientBraveTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/ReactorNettyHttpClientBraveTests.java similarity index 96% rename from tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/ReactorNettyHttpClientBraveTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/ReactorNettyHttpClientBraveTests.java index c54445a93..6a5cc831e 100644 --- a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/ReactorNettyHttpClientBraveTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/ReactorNettyHttpClientBraveTests.java @@ -37,7 +37,7 @@ public class ReactorNettyHttpClientBraveTests extends ITSpringConfiguredReactorC /** * This borrows hooks from - * {@code org.springframework.cloud.sleuth.instrument.reactor.TraceReactorAutoConfiguration} + * {@code org.springframework.cloud.sleuth.brave.instrument.reactor.TraceReactorAutoConfiguration} * to ensure that the invocation trace context is set in scope for hooks like * {@link Subscriber#onNext}. * diff --git a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TestHttpCallbackSubscriber.java b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TestHttpCallbackSubscriber.java similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TestHttpCallbackSubscriber.java rename to tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TestHttpCallbackSubscriber.java diff --git a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/WebClientBraveTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/WebClientBraveTests.java similarity index 98% rename from tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/WebClientBraveTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/WebClientBraveTests.java index 67ead3e9c..2ac762209 100644 --- a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/WebClientBraveTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/WebClientBraveTests.java @@ -81,7 +81,7 @@ public class WebClientBraveTests extends ITSpringConfiguredReactorClient { public void readsRequestAtResponseTime() { } - @Configuration + @Configuration(proxyBeanMethods = false) static class WebClientConfiguration { /** diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/resources/application.yml b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/resources/application.yml new file mode 100644 index 000000000..9ed06adbb --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/resources/application.yml @@ -0,0 +1,3 @@ +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE \ No newline at end of file diff --git a/tests/spring-cloud-sleuth-instrumentation-rxjava-tests/pom.xml b/tests/brave/spring-cloud-sleuth-instrumentation-rxjava-tests/pom.xml similarity index 85% rename from tests/spring-cloud-sleuth-instrumentation-rxjava-tests/pom.xml rename to tests/brave/spring-cloud-sleuth-instrumentation-rxjava-tests/pom.xml index 9e5bc6c2b..a8a411e39 100644 --- a/tests/spring-cloud-sleuth-instrumentation-rxjava-tests/pom.xml +++ b/tests/brave/spring-cloud-sleuth-instrumentation-rxjava-tests/pom.xml @@ -24,12 +24,12 @@ spring-cloud-sleuth-instrumentation-rxjava-tests jar - Spring Cloud Sleuth RxJava Instrumentation Tests - Spring Cloud Sleuth RxJava Instrumentation Tests + Spring Cloud Sleuth Brave RxJava Instrumentation Tests + Spring Cloud Sleuth Brave RxJava Instrumentation Tests org.springframework.cloud - spring-cloud-sleuth-tests + spring-cloud-sleuth-tests-brave 3.0.0-SNAPSHOT .. @@ -55,6 +55,11 @@ org.springframework.cloud spring-cloud-starter-sleuth + + org.springframework.cloud + spring-cloud-sleuth-brave + test + org.springframework.boot spring-boot-starter-test diff --git a/tests/spring-cloud-sleuth-instrumentation-rxjava-tests/src/test/java/org/springframework/cloud/sleuth/instrument/rxjava/SleuthRxJavaSchedulersHookTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-rxjava-tests/src/test/java/org/springframework/cloud/sleuth/instrument/rxjava/SleuthRxJavaSchedulersHookTests.java similarity index 96% rename from tests/spring-cloud-sleuth-instrumentation-rxjava-tests/src/test/java/org/springframework/cloud/sleuth/instrument/rxjava/SleuthRxJavaSchedulersHookTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-rxjava-tests/src/test/java/org/springframework/cloud/sleuth/instrument/rxjava/SleuthRxJavaSchedulersHookTests.java index d6278b2a2..a5c44f863 100644 --- a/tests/spring-cloud-sleuth-instrumentation-rxjava-tests/src/test/java/org/springframework/cloud/sleuth/instrument/rxjava/SleuthRxJavaSchedulersHookTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-rxjava-tests/src/test/java/org/springframework/cloud/sleuth/instrument/rxjava/SleuthRxJavaSchedulersHookTests.java @@ -26,7 +26,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; -import brave.Tracer; import brave.Tracing; import brave.propagation.StrictCurrentTraceContext; import brave.test.TestSpanHandler; @@ -39,6 +38,9 @@ import rx.plugins.RxJavaObservableExecutionHook; import rx.plugins.RxJavaPlugins; import rx.plugins.RxJavaSchedulersHook; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.brave.bridge.BraveTracer; + import static org.assertj.core.api.BDDAssertions.then; /** @@ -57,7 +59,7 @@ public class SleuthRxJavaSchedulersHookTests { Tracing tracing = Tracing.newBuilder().currentTraceContext(this.currentTraceContext).addSpanHandler(this.spans) .build(); - Tracer tracer = this.tracing.tracer(); + Tracer tracer = BraveTracer.fromBrave(this.tracing.tracer()); @AfterEach public void clean() { diff --git a/tests/spring-cloud-sleuth-instrumentation-rxjava-tests/src/test/java/org/springframework/cloud/sleuth/instrument/rxjava/SleuthRxJavaTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-rxjava-tests/src/test/java/org/springframework/cloud/sleuth/instrument/rxjava/SleuthRxJavaTests.java similarity index 98% rename from tests/spring-cloud-sleuth-instrumentation-rxjava-tests/src/test/java/org/springframework/cloud/sleuth/instrument/rxjava/SleuthRxJavaTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-rxjava-tests/src/test/java/org/springframework/cloud/sleuth/instrument/rxjava/SleuthRxJavaTests.java index e010f0b31..eeeb3c565 100644 --- a/tests/spring-cloud-sleuth-instrumentation-rxjava-tests/src/test/java/org/springframework/cloud/sleuth/instrument/rxjava/SleuthRxJavaTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-rxjava-tests/src/test/java/org/springframework/cloud/sleuth/instrument/rxjava/SleuthRxJavaTests.java @@ -95,7 +95,7 @@ public class SleuthRxJavaTests { then(this.spans.get(0).name()).isEqualTo("current_span"); } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration public static class TestConfig { diff --git a/tests/spring-cloud-sleuth-instrumentation-rxjava-tests/src/test/resources/application.yml b/tests/brave/spring-cloud-sleuth-instrumentation-rxjava-tests/src/test/resources/application.yml similarity index 74% rename from tests/spring-cloud-sleuth-instrumentation-rxjava-tests/src/test/resources/application.yml rename to tests/brave/spring-cloud-sleuth-instrumentation-rxjava-tests/src/test/resources/application.yml index 3739ee3ab..2ffa52976 100644 --- a/tests/spring-cloud-sleuth-instrumentation-rxjava-tests/src/test/resources/application.yml +++ b/tests/brave/spring-cloud-sleuth-instrumentation-rxjava-tests/src/test/resources/application.yml @@ -1,6 +1,6 @@ logging.level.org.springframework.cloud: DEBUG logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR -logging.level.org.springframework.cloud.sleuth.instrument.web.client.feign: TRACE +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE # comma separated list of matchers spring.sleuth.rxjava.schedulers.ignoredthreads: HystixMetricPoller,^MyCustomThread.*$,^RxComputation.*$ \ No newline at end of file diff --git a/tests/spring-cloud-sleuth-instrumentation-scheduling-tests/pom.xml b/tests/brave/spring-cloud-sleuth-instrumentation-scheduling-tests/pom.xml similarity index 84% rename from tests/spring-cloud-sleuth-instrumentation-scheduling-tests/pom.xml rename to tests/brave/spring-cloud-sleuth-instrumentation-scheduling-tests/pom.xml index ebf4da03c..16b1f91cc 100644 --- a/tests/spring-cloud-sleuth-instrumentation-scheduling-tests/pom.xml +++ b/tests/brave/spring-cloud-sleuth-instrumentation-scheduling-tests/pom.xml @@ -24,12 +24,12 @@ spring-cloud-sleuth-instrumentation-scheduling-tests jar - Spring Cloud Sleuth Scheduling Instrumentation Tests - Spring Cloud Sleuth Scheduling Instrumentation Tests + Spring Cloud Sleuth Brave Scheduling Instrumentation Tests + Spring Cloud Sleuth Brave Scheduling Instrumentation Tests org.springframework.cloud - spring-cloud-sleuth-tests + spring-cloud-sleuth-tests-brave 3.0.0-SNAPSHOT .. @@ -55,6 +55,11 @@ org.springframework.cloud spring-cloud-starter-sleuth + + org.springframework.cloud + spring-cloud-sleuth-brave + test + org.springframework.boot spring-boot-starter-test diff --git a/tests/spring-cloud-sleuth-instrumentation-scheduling-tests/src/test/java/org/springframework/cloud/sleuth/instrument/scheduling/TracingOnScheduledTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-scheduling-tests/src/test/java/org/springframework/cloud/sleuth/instrument/scheduling/TracingOnScheduledTests.java similarity index 97% rename from tests/spring-cloud-sleuth-instrumentation-scheduling-tests/src/test/java/org/springframework/cloud/sleuth/instrument/scheduling/TracingOnScheduledTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-scheduling-tests/src/test/java/org/springframework/cloud/sleuth/instrument/scheduling/TracingOnScheduledTests.java index 707175ffd..6f5ded90f 100644 --- a/tests/spring-cloud-sleuth-instrumentation-scheduling-tests/src/test/java/org/springframework/cloud/sleuth/instrument/scheduling/TracingOnScheduledTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-scheduling-tests/src/test/java/org/springframework/cloud/sleuth/instrument/scheduling/TracingOnScheduledTests.java @@ -131,7 +131,7 @@ public class TracingOnScheduledTests { } -@Configuration +@Configuration(proxyBeanMethods = false) @EnableAutoConfiguration @EnableScheduling class ScheduledTestConfiguration { @@ -212,11 +212,9 @@ class TestBeanWithScheduledMethodThatThrowsAnException { this.tracing = tracing; } - @Scheduled(fixedDelay = 1L) + @Scheduled(fixedDelay = 100L) public void scheduledMethod() { - log.info("Running the scheduled method"); this.span = this.tracing.tracer().currentSpan(); - log.info("Stored the span " + this.span + " as current span"); this.executed.set(true); throw new RuntimeException("HELLO"); } @@ -248,7 +246,7 @@ class TestBeanWithScheduledMethodToBeIgnored { this.tracing = tracing; } - @Scheduled(fixedDelay = 1000L) + @Scheduled(fixedDelay = 100L) public void scheduledMethodToIgnore() { this.span = this.tracing.tracer().currentSpan(); this.executed.set(true); diff --git a/tests/spring-cloud-sleuth-instrumentation-scheduling-tests/src/test/resources/application.yml b/tests/brave/spring-cloud-sleuth-instrumentation-scheduling-tests/src/test/resources/application.yml similarity index 69% rename from tests/spring-cloud-sleuth-instrumentation-scheduling-tests/src/test/resources/application.yml rename to tests/brave/spring-cloud-sleuth-instrumentation-scheduling-tests/src/test/resources/application.yml index fcce7321b..a85d3e0c8 100644 --- a/tests/spring-cloud-sleuth-instrumentation-scheduling-tests/src/test/resources/application.yml +++ b/tests/brave/spring-cloud-sleuth-instrumentation-scheduling-tests/src/test/resources/application.yml @@ -1,5 +1,5 @@ logging.level.org.springframework.cloud: DEBUG logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR -logging.level.org.springframework.cloud.sleuth.instrument.web.client.feign: TRACE +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE spring.sleuth.scheduled.skipPattern: "^org.*TestBeanWithScheduledMethodToBeIgnored$" \ No newline at end of file diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/pom.xml b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/pom.xml new file mode 100644 index 000000000..2633d5891 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/pom.xml @@ -0,0 +1,109 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-instrumentation-webflux-tests + jar + Spring Cloud Sleuth Brave WebFlux Instrumentation Tests + Spring Cloud Sleuth Brave WebFlux Instrumentation Tests + + + org.springframework.cloud + spring-cloud-sleuth-tests-brave + 3.0.0-SNAPSHOT + .. + + + + true + + + + + + + maven-deploy-plugin + + true + + + + + + + + ${project.groupId} + spring-cloud-sleuth-tests-common + ${project.version} + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + org.springframework.cloud + spring-cloud-sleuth-brave + test + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.boot + spring-boot-starter-test + test + + + io.zipkin.brave + brave-tests + test + + + org.awaitility + awaitility + test + + + + diff --git a/tests/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/GH1102Tests.java b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/GH1102Tests.java similarity index 97% rename from tests/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/GH1102Tests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/GH1102Tests.java index c710a510e..874acd03f 100644 --- a/tests/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/GH1102Tests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/GH1102Tests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web; +package org.springframework.cloud.sleuth.brave.instrument.web; import brave.ScopedSpan; import brave.Tracer; @@ -76,7 +76,7 @@ public class GH1102Tests { } @EnableAutoConfiguration - @Configuration + @Configuration(proxyBeanMethods = false) static class WebConfig { @Bean diff --git a/tests/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceWebFluxTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceWebFluxTests.java similarity index 98% rename from tests/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceWebFluxTests.java rename to tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceWebFluxTests.java index 89f573aee..0295116eb 100644 --- a/tests/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceWebFluxTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceWebFluxTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web; +package org.springframework.cloud.sleuth.brave.instrument.web; import brave.Span; import brave.Tracer; @@ -157,7 +157,7 @@ public class TraceWebFluxTests { return exchange.block(); } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration static class Config { diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/HttpClientBeanPostProcessorTest.java b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/HttpClientBeanPostProcessorTest.java new file mode 100644 index 000000000..5b4fd9384 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/HttpClientBeanPostProcessorTest.java @@ -0,0 +1,31 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web.client; + +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.brave.bridge.BraveTraceContext; + +public class HttpClientBeanPostProcessorTest + extends org.springframework.cloud.sleuth.instrument.web.client.HttpClientBeanPostProcessorTest { + + @Override + public TraceContext traceContext() { + return BraveTraceContext + .fromBrave(brave.propagation.TraceContext.newBuilder().traceId(1).spanId(2).sampled(true).build()); + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/MergedFactory.java b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/MergedFactory.java new file mode 100644 index 000000000..9b313383f --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/MergedFactory.java @@ -0,0 +1,59 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web.client; + +import java.util.List; + +import brave.Span; +import brave.internal.collect.Lists; +import brave.internal.propagation.StringPropagationAdapter; +import brave.propagation.B3Propagation; +import brave.propagation.Propagation; +import brave.propagation.TraceContext; + +public class MergedFactory extends Propagation.Factory implements Propagation { + + final Propagation single = B3Propagation.newFactoryBuilder() + .injectFormat(Span.Kind.CLIENT, B3Propagation.Format.SINGLE).build().get(); + + final Propagation multi = B3Propagation.newFactoryBuilder() + .injectFormat(Span.Kind.CLIENT, B3Propagation.Format.MULTI).build().get(); + + @Override + public List keys() { + return Lists.concat(single.keys(), multi.keys()); + } + + @Override + public TraceContext.Injector injector(Setter setter) { + return (traceContext, request) -> { + single.injector(setter).inject(traceContext, request); + multi.injector(setter).inject(traceContext, request); + }; + } + + @Override + public TraceContext.Extractor extractor(Getter getter) { + return multi.extractor(getter); + } + + @Override + public Propagation create(KeyFactory keyFactory) { + return StringPropagationAdapter.create(this, keyFactory); + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/ReactorNettyHttpClientSpringBootTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/ReactorNettyHttpClientSpringBootTests.java new file mode 100644 index 000000000..54a3d47c3 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/ReactorNettyHttpClientSpringBootTests.java @@ -0,0 +1,79 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web.client; + +import brave.Span; +import brave.propagation.B3Propagation; +import brave.propagation.Propagation; +import brave.sampler.Sampler; +import org.assertj.core.api.Assertions; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.brave.bridge.BraveTraceContext; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +@ContextConfiguration(classes = ReactorNettyHttpClientSpringBootTests.Config.class) +public class ReactorNettyHttpClientSpringBootTests + extends org.springframework.cloud.sleuth.instrument.web.client.ReactorNettyHttpClientSpringBootTests { + + @Override + public TraceContext traceContext() { + return BraveTraceContext + .fromBrave(brave.propagation.TraceContext.newBuilder().traceId(1).spanId(2).sampled(true).build()); + } + + @Override + public void assertSingleB3Header(String b3SingleHeaderReadByServer, FinishedSpan clientSpan, TraceContext parent) { + Assertions.assertThat(b3SingleHeaderReadByServer) + .isEqualTo(parent.traceId() + "-" + clientSpan.spanId() + "-1-" + parent.spanId()); + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + Propagation.Factory propagationFactory() { + return B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE) + .injectFormat(Span.Kind.CLIENT, B3Propagation.Format.SINGLE) + .injectFormat(Span.Kind.SERVER, B3Propagation.Format.SINGLE).build(); + } + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.IntegrationTestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.IntegrationTestSpanHandler braveTestSpanHandler() { + return new brave.test.IntegrationTestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/WebClientCustomParserTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/WebClientCustomParserTests.java new file mode 100644 index 000000000..06ddd42f5 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/WebClientCustomParserTests.java @@ -0,0 +1,61 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web.client; + +import brave.propagation.Propagation; +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@SpringBootTest(webEnvironment = RANDOM_PORT) +@ContextConfiguration(classes = WebClientCustomParserTests.Config.class) +public class WebClientCustomParserTests + extends org.springframework.cloud.sleuth.instrument.web.client.integration.parser.WebClientCustomParserTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + Propagation.Factory sleuthFactory() { + return new MergedFactory(); + } + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/WebClientDiscoveryExceptionTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/WebClientDiscoveryExceptionTests.java new file mode 100644 index 000000000..bff80af24 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/WebClientDiscoveryExceptionTests.java @@ -0,0 +1,55 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web.client; + +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@SpringBootTest(webEnvironment = RANDOM_PORT) +@ContextConfiguration(classes = WebClientDiscoveryExceptionTests.Config.class) +public class WebClientDiscoveryExceptionTests extends + org.springframework.cloud.sleuth.instrument.web.client.discoveryexception.WebClientDiscoveryExceptionTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/WebClientExceptionTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/WebClientExceptionTests.java new file mode 100644 index 000000000..1192c7df1 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/WebClientExceptionTests.java @@ -0,0 +1,54 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web.client; + +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@SpringBootTest(classes = { WebClientExceptionTests.Config.class, + org.springframework.cloud.sleuth.instrument.web.client.exception.WebClientExceptionTests.TestConfiguration.class }, + properties = { "spring.application.name=exceptionservice" }, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class WebClientExceptionTests + extends org.springframework.cloud.sleuth.instrument.web.client.exception.WebClientExceptionTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/WebClientNotSampledTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/WebClientNotSampledTests.java new file mode 100644 index 000000000..c33d8993f --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/WebClientNotSampledTests.java @@ -0,0 +1,70 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web.client; + +import java.util.Map; + +import brave.propagation.Propagation; +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.ContextConfiguration; + +import static org.assertj.core.api.BDDAssertions.then; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@SpringBootTest(webEnvironment = RANDOM_PORT) +@ContextConfiguration(classes = WebClientNotSampledTests.Config.class) +public class WebClientNotSampledTests + extends org.springframework.cloud.sleuth.instrument.web.client.integration.notsampled.WebClientNotSampledTests { + + @Override + public void assertB3SingleNotSampled(ResponseEntity> response) { + then(response.getBody().get("b3")).isNotNull().contains("-0-"); // not sampled + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + Propagation.Factory sleuthFactory() { + return new MergedFactory(); + } + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.NEVER_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/WebClientTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/WebClientTests.java new file mode 100644 index 000000000..70b7365fc --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/client/WebClientTests.java @@ -0,0 +1,61 @@ +/* + * Copyright 2013-2020 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.brave.instrument.web.client; + +import brave.propagation.Propagation; +import brave.sampler.Sampler; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@SpringBootTest(webEnvironment = RANDOM_PORT) +@ContextConfiguration(classes = WebClientTests.Config.class) +public class WebClientTests + extends org.springframework.cloud.sleuth.instrument.web.client.integration.sampled.WebClientTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + Propagation.Factory sleuthFactory() { + return new MergedFactory(); + } + + @Bean + TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) { + return new BraveTestSpanHandler(testSpanHandler); + } + + @Bean + Sampler alwaysSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + brave.test.TestSpanHandler braveTestSpanHandler() { + return new brave.test.TestSpanHandler(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/util/SpanUtil.java b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/util/SpanUtil.java new file mode 100644 index 000000000..ce959c2d3 --- /dev/null +++ b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/brave/util/SpanUtil.java @@ -0,0 +1,55 @@ +/* + * Copyright 2013-2020 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.brave.util; + +/** + * @author Marcin Grzejszczak + * @since + */ +public final class SpanUtil { + + private SpanUtil() { + throw new IllegalStateException("Can't instantiate a utility class"); + } + + static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + // Represents given long id as 16-character lower-hex string + public static String idToHex(long id) { + char[] data = new char[16]; + writeHexLong(data, 0, id); + return new String(data); + } + + // Inspired by {@code okio.Buffer.writeLong} + static void writeHexLong(char[] data, int pos, long v) { + writeHexByte(data, pos + 0, (byte) ((v >>> 56L) & 0xff)); + writeHexByte(data, pos + 2, (byte) ((v >>> 48L) & 0xff)); + writeHexByte(data, pos + 4, (byte) ((v >>> 40L) & 0xff)); + writeHexByte(data, pos + 6, (byte) ((v >>> 32L) & 0xff)); + writeHexByte(data, pos + 8, (byte) ((v >>> 24L) & 0xff)); + writeHexByte(data, pos + 10, (byte) ((v >>> 16L) & 0xff)); + writeHexByte(data, pos + 12, (byte) ((v >>> 8L) & 0xff)); + writeHexByte(data, pos + 14, (byte) (v & 0xff)); + } + + static void writeHexByte(char[] data, int pos, byte b) { + data[pos + 0] = HEX_DIGITS[(b >> 4) & 0xf]; + data[pos + 1] = HEX_DIGITS[b & 0xf]; + } + +} diff --git a/tests/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/resources/logback.xml b/tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/resources/logback.xml similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/resources/logback.xml rename to tests/brave/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/resources/logback.xml diff --git a/tests/common/pom.xml b/tests/common/pom.xml new file mode 100644 index 000000000..86e50d1f5 --- /dev/null +++ b/tests/common/pom.xml @@ -0,0 +1,144 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-tests-common + jar + Spring Cloud Sleuth Tests Common + Spring Cloud Sleuth Tests Common + + + org.springframework.cloud + spring-cloud-sleuth-tests + 3.0.0-SNAPSHOT + .. + + + + true + + + + + org.springframework.cloud + spring-cloud-sleuth-core + + + org.springframework.boot + spring-boot-starter-test + compile + + + org.awaitility + awaitility + compile + + + com.squareup.okhttp3 + mockwebserver + true + + + org.springframework.integration + spring-integration-core + true + + + org.springframework.boot + spring-boot-starter-websocket + true + + + org.springframework.boot + spring-boot-starter-actuator + true + + + org.springframework.boot + spring-boot-starter-web + true + + + org.springframework.boot + spring-boot-starter-webflux + true + + + org.springframework.cloud + spring-cloud-starter-gateway + true + + + org.springframework.cloud + spring-cloud-starter-circuitbreaker-resilience4j + true + + + org.springframework.boot + spring-boot-starter-quartz + true + + + org.springframework.cloud + spring-cloud-starter-openfeign + true + + + io.github.openfeign + feign-okhttp + true + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + true + + + org.apache.httpcomponents + httpclient + true + + + org.springframework.cloud + spring-cloud-sleuth-brave + true + + + io.zipkin.brave + brave-tests + true + + + org.springframework.cloud + spring-cloud-sleuth-otel + true + + + io.opentelemetry + opentelemetry-extension-trace-propagators + true + + + + + diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/NullSpanTagAnnotationHandlerTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/annotation/NullSpanTagAnnotationHandlerTests.java similarity index 90% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/NullSpanTagAnnotationHandlerTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/annotation/NullSpanTagAnnotationHandlerTests.java index c79f64b5a..438aed44b 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/NullSpanTagAnnotationHandlerTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/annotation/NullSpanTagAnnotationHandlerTests.java @@ -19,23 +19,21 @@ package org.springframework.cloud.sleuth.annotation; import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import brave.sampler.Sampler; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; -@SpringBootTest(classes = NullSpanTagAnnotationHandlerTests.TestConfiguration.class) - -public class NullSpanTagAnnotationHandlerTests { +@ContextConfiguration(classes = NullSpanTagAnnotationHandlerTests.TestConfiguration.class) +public abstract class NullSpanTagAnnotationHandlerTests { @Autowired BeanFactory beanFactory; @@ -91,20 +89,15 @@ public class NullSpanTagAnnotationHandlerTests { } } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration - protected static class TestConfiguration { + public static class TestConfiguration { @Bean public TagValueResolver tagValueResolver() { return parameter -> null; } - @Bean - Sampler alwaysSampler() { - return Sampler.ALWAYS_SAMPLE; - } - } protected class AnnotationMockClass { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectFluxTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectFluxTests.java similarity index 54% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectFluxTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectFluxTests.java index f6cf112b6..665d830bf 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectFluxTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectFluxTests.java @@ -19,18 +19,11 @@ package org.springframework.cloud.sleuth.annotation; import java.util.Iterator; import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; -import brave.Span; -import brave.Tracer; -import brave.handler.SpanHandler; -import brave.propagation.CurrentTraceContext; -import brave.propagation.CurrentTraceContext.Scope; -import brave.propagation.TraceContext; -import brave.sampler.Sampler; -import brave.test.TestSpanHandler; -import org.apache.commons.lang.StringUtils; +import org.assertj.core.api.BDDAssertions; import org.awaitility.Awaitility; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -40,17 +33,22 @@ import reactor.util.context.Context; 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.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.test.TestSpanHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; import static org.assertj.core.api.BDDAssertions.then; import static org.springframework.cloud.sleuth.annotation.SleuthSpanCreatorAspectFluxTests.TestBean.TEST_STRING1; import static org.springframework.cloud.sleuth.annotation.SleuthSpanCreatorAspectFluxTests.TestBean.TEST_STRING2; -@SpringBootTest(classes = SleuthSpanCreatorAspectFluxTests.TestConfiguration.class) - -public class SleuthSpanCreatorAspectFluxTests { +@ContextConfiguration(classes = SleuthSpanCreatorAspectFluxTests.TestConfiguration.class) +public abstract class SleuthSpanCreatorAspectFluxTests { @Autowired TestBeanInterface testBean; @@ -64,21 +62,18 @@ public class SleuthSpanCreatorAspectFluxTests { @Autowired TestSpanHandler spans; - TraceContext context = TraceContext.newBuilder().traceId(1).spanId(1).sampled(true).build(); + TraceContext context = traceContext(); - private static String toHexString(Long value) { - then(value).isNotNull(); - return StringUtils.leftPad(Long.toHexString(value), 16, '0'); - } + public abstract TraceContext traceContext(); - protected static Long id(Tracer tracer) { + protected static String id(Tracer tracer) { if (tracer.currentSpan() == null) { throw new IllegalStateException("Current Span is supposed to have a value!"); } return tracer.currentSpan().context().spanId(); } - protected static Long id(Context context, Tracer tracer) { + protected static String id(Context context, Tracer tracer) { if (context.hasKey(TraceContext.class)) { return context.get(TraceContext.class).spanId(); } @@ -93,15 +88,15 @@ public class SleuthSpanCreatorAspectFluxTests { @Test public void newSpan_shouldContinueExistingTrace() { - try (Scope scope = this.currentTraceContext.newScope(context)) { + try (CurrentTraceContext.Scope scope = this.currentTraceContext.newScope(context)) { Flux flux = this.testBean.testMethod(); verifyNoSpansUntilFluxComplete(flux); } - Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).traceId()).isEqualTo(context.traceIdString()); - then(this.spans.get(0).parentId()).isEqualTo(context.spanIdString()); + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).traceId()).isEqualTo(context.traceId()); + BDDAssertions.then(this.spans.get(0).parentId()).isEqualTo(context.spanId()); }); } @@ -111,11 +106,11 @@ public class SleuthSpanCreatorAspectFluxTests { verifyNoSpansUntilFluxComplete(flux); - Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("test-method"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("test-method"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -125,11 +120,11 @@ public class SleuthSpanCreatorAspectFluxTests { verifyNoSpansUntilFluxComplete(flux); - Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("test-method2"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("test-method2"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -139,11 +134,11 @@ public class SleuthSpanCreatorAspectFluxTests { verifyNoSpansUntilFluxComplete(flux); - Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method3"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method3"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -153,11 +148,11 @@ public class SleuthSpanCreatorAspectFluxTests { verifyNoSpansUntilFluxComplete(flux); - Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method4"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method4"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -169,12 +164,12 @@ public class SleuthSpanCreatorAspectFluxTests { // end::execution[] verifyNoSpansUntilFluxComplete(flux); - Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method5"); - then(this.spans.get(0).tags()).containsEntry("testTag", "test"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method5"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("testTag", "test"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -184,12 +179,12 @@ public class SleuthSpanCreatorAspectFluxTests { verifyNoSpansUntilFluxComplete(flux); - Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method6"); - then(this.spans.get(0).tags()).containsEntry("testTag6", "test"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method6"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("testTag6", "test"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -199,11 +194,11 @@ public class SleuthSpanCreatorAspectFluxTests { verifyNoSpansUntilFluxComplete(flux); - Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method8"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method8"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -213,12 +208,13 @@ public class SleuthSpanCreatorAspectFluxTests { verifyNoSpansUntilFluxComplete(flux); - Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method9"); - then(this.spans.get(0).tags()).containsEntry("class", "TestBean").containsEntry("method", "testMethod9"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method9"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("class", "TestBean").containsEntry("method", + "testMethod9"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -226,23 +222,23 @@ public class SleuthSpanCreatorAspectFluxTests { public void shouldContinueSpanWithLogWhenAnnotationOnInterfaceMethod() { Span span = this.tracer.nextSpan().name("foo"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { Flux flux = this.testBean.testMethod10("test"); verifyNoSpansUntilFluxComplete(flux); } finally { - span.finish(); + span.end(); } - Awaitility.await().untilAsserted(() -> { - then(spans).hasSize(1); - then(spans.get(0).name()).isEqualTo("foo"); - then(spans.get(0).tags()).containsEntry("customTestTag10", "test"); - then(spans.get(0).annotations().stream().map(Map.Entry::getValue).collect(Collectors.toList())) + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + BDDAssertions.then(spans).hasSize(1); + BDDAssertions.then(spans.get(0).name()).isEqualTo("foo"); + BDDAssertions.then(spans.get(0).tags()).containsEntry("customTestTag10", "test"); + BDDAssertions.then(spans.get(0).events().stream().map(Map.Entry::getValue).collect(Collectors.toList())) .contains("customTest.before", "customTest.after"); - then(spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -251,14 +247,15 @@ public class SleuthSpanCreatorAspectFluxTests { Flux flux = this.testBean.testMethod10("test"); verifyNoSpansUntilFluxComplete(flux); - Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("test-method10"); - then(this.spans.get(0).tags()).containsEntry("customTestTag10", "test"); - then(this.spans.get(0).annotations().stream().map(Map.Entry::getValue).collect(Collectors.toList())) + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("test-method10"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("customTestTag10", "test"); + BDDAssertions + .then(this.spans.get(0).events().stream().map(Map.Entry::getValue).collect(Collectors.toList())) .contains("customTest.before", "customTest.after"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -266,23 +263,24 @@ public class SleuthSpanCreatorAspectFluxTests { public void shouldContinueSpanWhenKeyIsUsedOnSpanTagWhenAnnotationOnInterfaceMethod() { Span span = this.tracer.nextSpan().name("foo"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { Flux flux = this.testBean.testMethod10_v2("test"); verifyNoSpansUntilFluxComplete(flux); } finally { - span.finish(); + span.end(); } - Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("foo"); - then(this.spans.get(0).tags()).containsEntry("customTestTag10", "test"); - then(this.spans.get(0).annotations().stream().map(Map.Entry::getValue).collect(Collectors.toList())) + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("foo"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("customTestTag10", "test"); + BDDAssertions + .then(this.spans.get(0).events().stream().map(Map.Entry::getValue).collect(Collectors.toList())) .contains("customTest.before", "customTest.after"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -290,25 +288,26 @@ public class SleuthSpanCreatorAspectFluxTests { public void shouldContinueSpanWithLogWhenAnnotationOnClassMethod() { Span span = this.tracer.nextSpan().name("foo"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { // tag::continue_span_execution[] Flux flux = this.testBean.testMethod11("test"); // end::continue_span_execution[] verifyNoSpansUntilFluxComplete(flux); } finally { - span.finish(); + span.end(); } - Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("foo"); - then(this.spans.get(0).tags()).containsEntry("class", "TestBean").containsEntry("method", "testMethod11") - .containsEntry("customTestTag11", "test"); - then(this.spans.get(0).annotations().stream().map(Map.Entry::getValue).collect(Collectors.toList())) + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("foo"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("class", "TestBean") + .containsEntry("method", "testMethod11").containsEntry("customTestTag11", "test"); + BDDAssertions + .then(this.spans.get(0).events().stream().map(Map.Entry::getValue).collect(Collectors.toList())) .contains("customTest.before", "customTest.after"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -317,20 +316,21 @@ public class SleuthSpanCreatorAspectFluxTests { try { Flux flux = this.testBean.testMethod12("test"); - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); flux.toIterable().iterator().next(); } catch (RuntimeException ignored) { } - Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("test-method12"); - then(this.spans.get(0).tags()).containsEntry("testTag12", "test"); - then(this.spans.get(0).error()).hasMessageContaining("test exception 12"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + BDDAssertions.then(this.spans).hasSize(1); + FinishedSpan finishedSpan = this.spans.get(0); + BDDAssertions.then(finishedSpan.name()).isEqualTo("test-method12"); + BDDAssertions.then(finishedSpan.tags()).containsEntry("testTag12", "test"); + BDDAssertions.then(finishedSpan.error()).hasMessageContaining("test exception 12"); + BDDAssertions.then(finishedSpan.endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -338,11 +338,11 @@ public class SleuthSpanCreatorAspectFluxTests { public void shouldAddErrorTagWhenExceptionOccurredInContinueSpan() { Span span = this.tracer.nextSpan().name("foo"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { // tag::continue_span_execution[] Flux flux = this.testBean.testMethod13(); - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); flux.toIterable().iterator().next(); // end::continue_span_execution[] @@ -350,17 +350,18 @@ public class SleuthSpanCreatorAspectFluxTests { catch (RuntimeException ignored) { } finally { - span.finish(); + span.end(); } - Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("foo"); - then(this.spans.get(0).error()).hasMessageContaining("test exception 13"); - then(this.spans.get(0).annotations().stream().map(Map.Entry::getValue).collect(Collectors.toList())) + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("foo"); + BDDAssertions.then(this.spans.get(0).error()).hasMessageContaining("test exception 13"); + BDDAssertions + .then(this.spans.get(0).events().stream().map(Map.Entry::getValue).collect(Collectors.toList())) .contains("testMethod13.before", "testMethod13.afterFailure", "testMethod13.after"); - then(spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -369,47 +370,47 @@ public class SleuthSpanCreatorAspectFluxTests { Flux flux = this.testBean.testMethod7(); verifyNoSpansUntilFluxComplete(flux); - Awaitility.await().untilAsserted(() -> { - then(this.spans).isEmpty(); - then(this.tracer.currentSpan()).isNull(); + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + BDDAssertions.then(this.spans).isEmpty(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @Test public void shouldReturnNewSpanFromTraceContext() { - Flux flux = this.testBean.newSpanInTraceContext(); - Long newSpanId = flux.blockFirst(); + Flux flux = this.testBean.newSpanInTraceContext(); + String newSpanId = flux.blockFirst(); - Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("span-in-trace-context"); - then(this.spans.get(0).id()).isEqualTo(toHexString(newSpanId)); - then(this.tracer.currentSpan()).isNull(); + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("span-in-trace-context"); + BDDAssertions.then(this.spans.get(0).spanId()).isEqualTo(newSpanId); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @Test public void shouldReturnNewSpanFromSubscriberContext() { - Flux flux = this.testBean.newSpanInSubscriberContext(); - Long newSpanId = flux.blockFirst(); + Flux flux = this.testBean.newSpanInSubscriberContext(); + String newSpanId = flux.blockFirst(); - Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("span-in-subscriber-context"); - then(this.spans.get(0).id()).isEqualTo(toHexString(newSpanId)); - then(this.tracer.currentSpan()).isNull(); + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("span-in-subscriber-context"); + BDDAssertions.then(this.spans.get(0).spanId()).isEqualTo(newSpanId); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } private void verifyNoSpansUntilFluxComplete(Flux flux) { Iterator iterator = flux.toIterable().iterator(); - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); this.testBean.proceed(); String result1 = iterator.next(); then(result1).isEqualTo(TEST_STRING1); - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); this.testBean.proceed(); String result2 = iterator.next(); @@ -470,10 +471,10 @@ public class SleuthSpanCreatorAspectFluxTests { Flux testMethod14(String param); @NewSpan(name = "spanInTraceContext") - Flux newSpanInTraceContext(); + Flux newSpanInTraceContext(); @NewSpan(name = "spanInSubscriberContext") - Flux newSpanInSubscriberContext(); + Flux newSpanInSubscriberContext(); void proceed(); @@ -591,36 +592,26 @@ public class SleuthSpanCreatorAspectFluxTests { } @Override - public Flux newSpanInTraceContext() { + public Flux newSpanInTraceContext() { return Flux.defer(() -> Flux.just(id(this.tracer))); } @Override - public Flux newSpanInSubscriberContext() { + public Flux newSpanInSubscriberContext() { return Mono.subscriberContext().flatMapMany(context -> Flux.just(id(context, this.tracer))); } } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration - protected static class TestConfiguration { + public static class TestConfiguration { @Bean public TestBeanInterface testBean(Tracer tracer) { return new TestBean(tracer); } - @Bean - SpanHandler testSpanHandler() { - return new TestSpanHandler(); - } - - @Bean - Sampler alwaysSampler() { - return Sampler.ALWAYS_SAMPLE; - } - } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectMonoTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectMonoTests.java similarity index 61% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectMonoTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectMonoTests.java index 11966a9b1..6b0f1596e 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectMonoTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectMonoTests.java @@ -21,14 +21,7 @@ import java.util.Objects; import java.util.stream.Collector; import java.util.stream.Collectors; -import javax.annotation.concurrent.NotThreadSafe; - -import brave.Span; -import brave.Tracer; -import brave.handler.MutableSpan; -import brave.handler.SpanHandler; -import brave.sampler.Sampler; -import brave.test.TestSpanHandler; +import org.assertj.core.api.BDDAssertions; import org.awaitility.Awaitility; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -36,20 +29,20 @@ import reactor.core.publisher.Mono; 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.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.test.TestSpanHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; import static org.assertj.core.api.BDDAssertions.then; import static org.springframework.cloud.sleuth.annotation.SleuthSpanCreatorAspectMonoTests.TestBean.TEST_STRING; -import static org.springframework.test.annotation.DirtiesContext.MethodMode.BEFORE_METHOD; import static reactor.core.publisher.Mono.just; -@SpringBootTest(classes = SleuthSpanCreatorAspectMonoTests.TestConfiguration.class) -@DirtiesContext(methodMode = BEFORE_METHOD) -@NotThreadSafe -public class SleuthSpanCreatorAspectMonoTests { +@ContextConfiguration(classes = SleuthSpanCreatorAspectMonoTests.TestConfiguration.class) +public abstract class SleuthSpanCreatorAspectMonoTests { @Autowired TestBeanInterface testBean; @@ -67,7 +60,7 @@ public class SleuthSpanCreatorAspectMonoTests { if (tracer.currentSpan() == null) { throw new IllegalStateException("Current Span is supposed to have a value!"); } - return tracer.currentSpan().context().spanIdString(); + return tracer.currentSpan().context().spanId(); } @BeforeEach @@ -79,15 +72,15 @@ public class SleuthSpanCreatorAspectMonoTests { public void shouldCreateSpanWhenAnnotationOnInterfaceMethod() { Mono mono = this.testBean.testMethod(); - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); mono.block(); Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("test-method"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("test-method"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -95,15 +88,15 @@ public class SleuthSpanCreatorAspectMonoTests { public void shouldCreateSpanWhenAnnotationOnClassMethod() { Mono mono = this.testBean.testMethod2(); - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); mono.block(); Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("test-method2"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("test-method2"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -111,16 +104,16 @@ public class SleuthSpanCreatorAspectMonoTests { public void shouldCreateSpanWithCustomNameWhenAnnotationOnClassMethod() { Mono mono = this.testBean.testMethod3(); - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); String result = mono.block(); Awaitility.await().untilAsserted(() -> { then(result).isEqualTo(TEST_STRING); - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method3"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method3"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -128,15 +121,15 @@ public class SleuthSpanCreatorAspectMonoTests { public void shouldCreateSpanWithCustomNameWhenAnnotationOnInterfaceMethod() { Mono mono = this.testBean.testMethod4(); - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); mono.block(); Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method4"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method4"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -146,16 +139,16 @@ public class SleuthSpanCreatorAspectMonoTests { Mono mono = this.testBean.testMethod5("test"); // end::execution[] - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); mono.block(); Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method5"); - then(this.spans.get(0).tags()).containsEntry("testTag", "test"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method5"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("testTag", "test"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -163,16 +156,16 @@ public class SleuthSpanCreatorAspectMonoTests { public void shouldCreateSpanWithTagWhenAnnotationOnClassMethod() { Mono mono = this.testBean.testMethod6("test"); - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); mono.block(); Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method6"); - then(this.spans.get(0).tags()).containsEntry("testTag6", "test"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method6"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("testTag6", "test"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -180,15 +173,15 @@ public class SleuthSpanCreatorAspectMonoTests { public void shouldCreateSpanWithLogWhenAnnotationOnInterfaceMethod() { Mono mono = this.testBean.testMethod8("test"); - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); mono.block(); Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method8"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method8"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -196,16 +189,17 @@ public class SleuthSpanCreatorAspectMonoTests { public void shouldCreateSpanWithLogWhenAnnotationOnClassMethod() { Mono mono = this.testBean.testMethod9("test"); - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); mono.block(); Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method9"); - then(this.spans.get(0).tags()).containsEntry("class", "TestBean").containsEntry("method", "testMethod9"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method9"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("class", "TestBean").containsEntry("method", + "testMethod9"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -213,25 +207,26 @@ public class SleuthSpanCreatorAspectMonoTests { public void shouldContinueSpanWithLogWhenAnnotationOnInterfaceMethod() { Span span = this.tracer.nextSpan().name("foo"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { Mono mono = this.testBean.testMethod10("test"); - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); mono.block(); } finally { - span.finish(); + span.end(); } Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("foo"); - then(this.spans.get(0).tags()).containsEntry("customTestTag10", "test"); - then(this.spans.get(0).annotations().stream().map(Map.Entry::getValue).collect(Collectors.toList())) + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("foo"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("customTestTag10", "test"); + BDDAssertions + .then(this.spans.get(0).events().stream().map(Map.Entry::getValue).collect(Collectors.toList())) .contains("customTest.before", "customTest.after"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -240,13 +235,14 @@ public class SleuthSpanCreatorAspectMonoTests { this.testBean.testMethod10("test").block(); Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("test-method10"); - then(this.spans.get(0).tags()).containsEntry("customTestTag10", "test"); - then(this.spans.get(0).annotations().stream().map(Map.Entry::getValue).collect(Collectors.toList())) + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("test-method10"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("customTestTag10", "test"); + BDDAssertions + .then(this.spans.get(0).events().stream().map(Map.Entry::getValue).collect(Collectors.toList())) .contains("customTest.before", "customTest.after"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -254,25 +250,26 @@ public class SleuthSpanCreatorAspectMonoTests { public void shouldContinueSpanWhenKeyIsUsedOnSpanTagWhenAnnotationOnInterfaceMethod() { Span span = this.tracer.nextSpan().name("foo"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { Mono mono = this.testBean.testMethod10_v2("test"); - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); mono.block(); } finally { - span.finish(); + span.end(); } Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("foo"); - then(this.spans.get(0).tags()).containsEntry("customTestTag10", "test"); - then(this.spans.get(0).annotations().stream().map(Map.Entry::getValue).collect(Collectors.toList())) + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("foo"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("customTestTag10", "test"); + BDDAssertions + .then(this.spans.get(0).events().stream().map(Map.Entry::getValue).collect(Collectors.toList())) .contains("customTest.before", "customTest.after"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -280,27 +277,28 @@ public class SleuthSpanCreatorAspectMonoTests { public void shouldContinueSpanWithLogWhenAnnotationOnClassMethod() { Span span = this.tracer.nextSpan().name("foo"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { // tag::continue_span_execution[] Mono mono = this.testBean.testMethod11("test"); // end::continue_span_execution[] - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); mono.block(); } finally { - span.finish(); + span.end(); } Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("foo"); - then(this.spans.get(0).tags()).containsEntry("class", "TestBean").containsEntry("method", "testMethod11") - .containsEntry("customTestTag11", "test"); - then(this.spans.get(0).annotations().stream().map(Map.Entry::getValue).collect(Collectors.toList())) + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("foo"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("class", "TestBean") + .containsEntry("method", "testMethod11").containsEntry("customTestTag11", "test"); + BDDAssertions + .then(this.spans.get(0).events().stream().map(Map.Entry::getValue).collect(Collectors.toList())) .contains("customTest.before", "customTest.after"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -309,7 +307,7 @@ public class SleuthSpanCreatorAspectMonoTests { try { Mono mono = this.testBean.testMethod12("test"); - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); mono.block(); } @@ -317,12 +315,12 @@ public class SleuthSpanCreatorAspectMonoTests { } Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("test-method12"); - then(this.spans.get(0).tags()).containsEntry("testTag12", "test"); - then(this.spans.get(0).error()).hasMessageContaining("test exception 12"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("test-method12"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("testTag12", "test"); + BDDAssertions.then(this.spans.get(0).error()).hasMessageContaining("test exception 12"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -330,11 +328,11 @@ public class SleuthSpanCreatorAspectMonoTests { public void shouldAddErrorTagWhenExceptionOccurredInContinueSpan() { Span span = this.tracer.nextSpan().name("foo"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { // tag::continue_span_execution[] Mono mono = this.testBean.testMethod13(); - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); mono.block(); // end::continue_span_execution[] @@ -342,17 +340,18 @@ public class SleuthSpanCreatorAspectMonoTests { catch (RuntimeException ignored) { } finally { - span.finish(); + span.end(); } Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("foo"); - then(this.spans.get(0).error()).hasMessageContaining("test exception 13"); - then(this.spans.get(0).annotations().stream().map(Map.Entry::getValue).collect(Collectors.toList())) + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("foo"); + BDDAssertions.then(this.spans.get(0).error()).hasMessageContaining("test exception 13"); + BDDAssertions + .then(this.spans.get(0).events().stream().map(Map.Entry::getValue).collect(Collectors.toList())) .contains("testMethod13.before", "testMethod13.afterFailure", "testMethod13.after"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -362,8 +361,8 @@ public class SleuthSpanCreatorAspectMonoTests { mono.block(); Awaitility.await().untilAsserted(() -> { - then(this.spans).isEmpty(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).isEmpty(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -371,15 +370,15 @@ public class SleuthSpanCreatorAspectMonoTests { public void shouldReturnNewSpanFromTraceContext() { Mono mono = this.testBean.newSpanInTraceContext(); - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); String newSpanId = mono.block(); Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("span-in-trace-context"); - then(this.spans.get(0).id()).isEqualTo(newSpanId); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("span-in-trace-context"); + BDDAssertions.then(this.spans.get(0).spanId()).isEqualTo(newSpanId); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -387,7 +386,7 @@ public class SleuthSpanCreatorAspectMonoTests { public void shouldReturnNewSpanFromTraceContextOuter() { Mono, String>> mono = this.testBeanOuter.outerNewSpanInTraceContext(); - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); Pair, String> pair = mono.block(); String outerSpanIdBefore = pair.getFirst().getFirst(); @@ -396,17 +395,17 @@ public class SleuthSpanCreatorAspectMonoTests { then(outerSpanIdBefore).isNotEqualTo(innerSpanId); Awaitility.await().untilAsserted(() -> { - MutableSpan outerSpan = spans.spans().stream() + FinishedSpan outerSpan = spans.reportedSpans().stream() .filter(span -> span.name().equals("outer-span-in-trace-context")).findFirst() .orElseThrow(() -> new AssertionError("No span with name [outer-span-in-trace-context] found")); - then(outerSpan.name()).isEqualTo("outer-span-in-trace-context"); - then(outerSpan.id()).isEqualTo(outerSpanIdBefore); - MutableSpan innerSpan = spans.spans().stream().filter(span -> span.name().equals("span-in-trace-context")) - .findFirst() + BDDAssertions.then(outerSpan.name()).isEqualTo("outer-span-in-trace-context"); + BDDAssertions.then(outerSpan.spanId()).isEqualTo(outerSpanIdBefore); + FinishedSpan innerSpan = spans.reportedSpans().stream() + .filter(span -> span.name().equals("span-in-trace-context")).findFirst() .orElseThrow(() -> new AssertionError("No span with name [span-in-trace-context] found")); - then(innerSpan.name()).isEqualTo("span-in-trace-context"); - then(innerSpan.id()).isEqualTo(innerSpanId); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(innerSpan.name()).isEqualTo("span-in-trace-context"); + BDDAssertions.then(innerSpan.spanId()).isEqualTo(innerSpanId); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -414,15 +413,15 @@ public class SleuthSpanCreatorAspectMonoTests { public void shouldReturnNewSpanFromSubscriberContext() { Mono mono = this.testBean.newSpanInSubscriberContext(); - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); String newSpanId = mono.block(); Awaitility.await().untilAsserted(() -> { - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("span-in-subscriber-context"); - then(this.spans.get(0).id()).isEqualTo(newSpanId); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("span-in-subscriber-context"); + BDDAssertions.then(this.spans.get(0).spanId()).isEqualTo(newSpanId); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -430,7 +429,7 @@ public class SleuthSpanCreatorAspectMonoTests { public void shouldReturnNewSpanFromSubscriberContextOuter() { Mono, String>> mono = this.testBeanOuter.outerNewSpanInSubscriberContext(); - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); Pair, String> pair = mono.block(); String outerSpanIdBefore = pair.getFirst().getFirst(); @@ -439,17 +438,17 @@ public class SleuthSpanCreatorAspectMonoTests { then(outerSpanIdBefore).isNotEqualTo(innerSpanId); Awaitility.await().untilAsserted(() -> { - MutableSpan outerSpan = spans.spans().stream() + FinishedSpan outerSpan = spans.reportedSpans().stream() .filter(span -> span.name().equals("outer-span-in-subscriber-context")).findFirst().orElseThrow( () -> new AssertionError("No span with name [outer-span-in-subscriber-context] found")); - then(outerSpan.name()).isEqualTo("outer-span-in-subscriber-context"); - then(outerSpan.id()).isEqualTo(outerSpanIdBefore); - MutableSpan innerSpan = spans.spans().stream() + BDDAssertions.then(outerSpan.name()).isEqualTo("outer-span-in-subscriber-context"); + BDDAssertions.then(outerSpan.spanId()).isEqualTo(outerSpanIdBefore); + FinishedSpan innerSpan = spans.reportedSpans().stream() .filter(span -> span.name().equals("span-in-subscriber-context")).findFirst() .orElseThrow(() -> new AssertionError("No span with name [span-in-subscriber-context] found")); - then(innerSpan.name()).isEqualTo("span-in-subscriber-context"); - then(innerSpan.id()).isEqualTo(innerSpanId); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(innerSpan.name()).isEqualTo("span-in-subscriber-context"); + BDDAssertions.then(innerSpan.spanId()).isEqualTo(innerSpanId); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); }); } @@ -638,30 +637,20 @@ public class SleuthSpanCreatorAspectMonoTests { } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration - protected static class TestConfiguration { + public static class TestConfiguration { @Bean - public TestBeanInterface testBean(Tracer tracer) { + TestBeanInterface testBean(Tracer tracer) { return new TestBean(tracer); } @Bean - public TestBeanOuter testBeanOuter(Tracer tracer, TestBeanInterface testBean) { + TestBeanOuter testBeanOuter(Tracer tracer, TestBeanInterface testBean) { return new TestBeanOuter(tracer, testBean); } - @Bean - SpanHandler testSpanHandler() { - return new TestSpanHandler(); - } - - @Bean - Sampler alwaysSampler() { - return Sampler.ALWAYS_SAMPLE; - } - } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectNegativeTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectNegativeTests.java similarity index 76% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectNegativeTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectNegativeTests.java index 6ac0fcda0..d8252082c 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectNegativeTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectNegativeTests.java @@ -16,22 +16,19 @@ package org.springframework.cloud.sleuth.annotation; -import brave.handler.SpanHandler; -import brave.sampler.Sampler; -import brave.test.TestSpanHandler; +import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; 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.test.TestSpanHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; -import static org.assertj.core.api.BDDAssertions.then; - -@SpringBootTest(classes = SleuthSpanCreatorAspectNegativeTests.TestConfiguration.class) -public class SleuthSpanCreatorAspectNegativeTests { +@ContextConfiguration(classes = SleuthSpanCreatorAspectNegativeTests.TestConfiguration.class) +public abstract class SleuthSpanCreatorAspectNegativeTests { @Autowired NotAnnotatedTestBeanInterface testBean; @@ -51,15 +48,15 @@ public class SleuthSpanCreatorAspectNegativeTests { public void shouldNotCallAdviceForNotAnnotatedBean() { this.testBean.testMethod(); - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); } @Test public void shouldCallAdviceForAnnotatedBean() throws Throwable { this.annotatedTestBean.testMethod(); - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("test-method"); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("test-method"); } protected interface NotAnnotatedTestBeanInterface { @@ -133,30 +130,20 @@ public class SleuthSpanCreatorAspectNegativeTests { } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration - protected static class TestConfiguration { + public static class TestConfiguration { @Bean - SpanHandler testSpanHandler() { - return new TestSpanHandler(); - } - - @Bean - public NotAnnotatedTestBeanInterface testBean() { + NotAnnotatedTestBeanInterface testBean() { return new NotAnnotatedTestBean(); } @Bean - public TestBeanInterface annotatedTestBean() { + TestBeanInterface annotatedTestBean() { return new TestBean(); } - @Bean - public Sampler sampler() { - return Sampler.ALWAYS_SAMPLE; - } - } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectTests.java similarity index 52% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectTests.java index c9465e894..f20372197 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorAspectTests.java @@ -19,28 +19,21 @@ package org.springframework.cloud.sleuth.annotation; import java.util.Map; import java.util.stream.Collectors; -import brave.Span; -import brave.Tracer; -import brave.handler.SpanHandler; -import brave.sampler.Sampler; -import brave.test.TestSpanHandler; +import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; 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.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.test.TestSpanHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; -import static org.assertj.core.api.BDDAssertions.then; -import static org.springframework.test.annotation.DirtiesContext.MethodMode.BEFORE_METHOD; - -@SpringBootTest(classes = SleuthSpanCreatorAspectTests.TestConfiguration.class) - -@DirtiesContext(methodMode = BEFORE_METHOD) -public class SleuthSpanCreatorAspectTests { +@ContextConfiguration(classes = SleuthSpanCreatorAspectTests.TestConfiguration.class) +public abstract class SleuthSpanCreatorAspectTests { @Autowired TestBeanInterface testBean; @@ -60,40 +53,40 @@ public class SleuthSpanCreatorAspectTests { public void shouldCreateSpanWhenAnnotationOnInterfaceMethod() { this.testBean.testMethod(); - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("test-method"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("test-method"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } @Test public void shouldCreateSpanWhenAnnotationOnClassMethod() { this.testBean.testMethod2(); - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("test-method2"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("test-method2"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } @Test public void shouldCreateSpanWithCustomNameWhenAnnotationOnClassMethod() { this.testBean.testMethod3(); - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method3"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method3"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } @Test public void shouldCreateSpanWithCustomNameWhenAnnotationOnInterfaceMethod() { this.testBean.testMethod4(); - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method4"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method4"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } @Test @@ -102,119 +95,120 @@ public class SleuthSpanCreatorAspectTests { this.testBean.testMethod5("test"); // end::execution[] - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method5"); - then(this.spans.get(0).tags()).containsEntry("testTag", "test"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method5"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("testTag", "test"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } @Test public void shouldCreateSpanWithTagWhenAnnotationOnClassMethod() { this.testBean.testMethod6("test"); - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method6"); - then(this.spans.get(0).tags()).containsEntry("testTag6", "test"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method6"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("testTag6", "test"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } @Test public void shouldCreateSpanWithLogWhenAnnotationOnInterfaceMethod() { this.testBean.testMethod8("test"); - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method8"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method8"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } @Test public void shouldCreateSpanWithLogWhenAnnotationOnClassMethod() { this.testBean.testMethod9("test"); - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method9"); - then(this.spans.get(0).tags()).containsEntry("class", "TestBean").containsEntry("method", "testMethod9"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("custom-name-on-test-method9"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("class", "TestBean").containsEntry("method", + "testMethod9"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } @Test public void shouldContinueSpanWithLogWhenAnnotationOnInterfaceMethod() { Span span = this.tracer.nextSpan().name("foo"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { this.testBean.testMethod10("test"); } finally { - span.finish(); + span.end(); } - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("foo"); - then(this.spans.get(0).tags()).containsEntry("customTestTag10", "test"); - then(this.spans.get(0).annotations().stream().map(Map.Entry::getValue).collect(Collectors.toList())) + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("foo"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("customTestTag10", "test"); + BDDAssertions.then(this.spans.get(0).events().stream().map(Map.Entry::getValue).collect(Collectors.toList())) .contains("customTest.before", "customTest.after"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } @Test public void shouldStartAndCloseSpanOnContinueSpanIfSpanNotSet() { this.testBean.testMethod10("test"); - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("test-method10"); - then(this.spans.get(0).tags()).containsEntry("customTestTag10", "test"); - then(this.spans.get(0).annotations().stream().map(Map.Entry::getValue).collect(Collectors.toList())) + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("test-method10"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("customTestTag10", "test"); + BDDAssertions.then(this.spans.get(0).events().stream().map(Map.Entry::getValue).collect(Collectors.toList())) .contains("customTest.before", "customTest.after"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } @Test public void shouldContinueSpanWhenKeyIsUsedOnSpanTagWhenAnnotationOnInterfaceMethod() { Span span = this.tracer.nextSpan().name("foo"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { this.testBean.testMethod10_v2("test"); } finally { - span.finish(); + span.end(); } - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("foo"); - then(this.spans.get(0).tags()).containsEntry("customTestTag10", "test"); - then(this.spans.get(0).annotations().stream().map(Map.Entry::getValue).collect(Collectors.toList())) + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("foo"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("customTestTag10", "test"); + BDDAssertions.then(this.spans.get(0).events().stream().map(Map.Entry::getValue).collect(Collectors.toList())) .contains("customTest.before", "customTest.after"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } @Test public void shouldContinueSpanWithLogWhenAnnotationOnClassMethod() { Span span = this.tracer.nextSpan().name("foo"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { // tag::continue_span_execution[] this.testBean.testMethod11("test"); // end::continue_span_execution[] } finally { - span.finish(); + span.end(); } - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("foo"); - then(this.spans.get(0).tags()).containsEntry("class", "TestBean").containsEntry("method", "testMethod11") - .containsEntry("customTestTag11", "test"); - then(this.spans.get(0).annotations().stream().map(Map.Entry::getValue).collect(Collectors.toList())) + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("foo"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("class", "TestBean") + .containsEntry("method", "testMethod11").containsEntry("customTestTag11", "test"); + BDDAssertions.then(this.spans.get(0).events().stream().map(Map.Entry::getValue).collect(Collectors.toList())) .contains("customTest.before", "customTest.after"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } @Test @@ -225,19 +219,19 @@ public class SleuthSpanCreatorAspectTests { catch (RuntimeException ignored) { } - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("test-method12"); - then(this.spans.get(0).tags()).containsEntry("testTag12", "test"); - then(this.spans.get(0).error()).hasMessageContaining("test exception 12"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("test-method12"); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("testTag12", "test"); + BDDAssertions.then(this.spans.get(0).error()).hasMessageContaining("test exception 12"); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } @Test public void shouldAddErrorTagWhenExceptionOccurredInContinueSpan() { Span span = this.tracer.nextSpan().name("foo"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { // tag::continue_span_execution[] this.testBean.testMethod13(); // end::continue_span_execution[] @@ -245,24 +239,24 @@ public class SleuthSpanCreatorAspectTests { catch (RuntimeException ignored) { } finally { - span.finish(); + span.end(); } - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("foo"); - then(this.spans.get(0).error()).hasMessageContaining("test exception 13"); - then(this.spans.get(0).annotations().stream().map(Map.Entry::getValue).collect(Collectors.toList())) + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).name()).isEqualTo("foo"); + BDDAssertions.then(this.spans.get(0).error()).hasMessageContaining("test exception 13"); + BDDAssertions.then(this.spans.get(0).events().stream().map(Map.Entry::getValue).collect(Collectors.toList())) .contains("testMethod13.before", "testMethod13.afterFailure", "testMethod13.after"); - then(this.spans.get(0).finishTimestamp()).isNotZero(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans.get(0).endTimestamp()).isNotZero(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } @Test public void shouldNotCreateSpanWhenNotAnnotated() { this.testBean.testMethod7(); - then(this.spans).isEmpty(); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).isEmpty(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } protected interface TestBeanInterface { @@ -391,25 +385,15 @@ public class SleuthSpanCreatorAspectTests { } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration - protected static class TestConfiguration { + public static class TestConfiguration { @Bean - public TestBeanInterface testBean() { + TestBeanInterface testBean() { return new TestBean(); } - @Bean - SpanHandler testSpanHandler() { - return new TestSpanHandler(); - } - - @Bean - Sampler alwaysSampler() { - return Sampler.ALWAYS_SAMPLE; - } - } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorCircularDependencyTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorCircularDependencyTests.java similarity index 74% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorCircularDependencyTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorCircularDependencyTests.java index e4e38f703..f6a287966 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorCircularDependencyTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/annotation/SleuthSpanCreatorCircularDependencyTests.java @@ -16,18 +16,16 @@ package org.springframework.cloud.sleuth.annotation; -import brave.handler.SpanHandler; -import brave.test.TestSpanHandler; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; -@SpringBootTest(classes = SleuthSpanCreatorCircularDependencyTests.TestConfiguration.class) -public class SleuthSpanCreatorCircularDependencyTests { +@ContextConfiguration(classes = SleuthSpanCreatorCircularDependencyTests.TestConfiguration.class) +public abstract class SleuthSpanCreatorCircularDependencyTests { @Test public void contextLoads() throws Exception { @@ -55,22 +53,17 @@ public class SleuthSpanCreatorCircularDependencyTests { } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration - protected static class TestConfiguration { + public static class TestConfiguration { @Bean - SpanHandler testSpanHandler() { - return new TestSpanHandler(); - } - - @Bean - public Service1 service1() { + Service1 service1() { return new Service1(); } @Bean - public Service2 service2() { + Service2 service2() { return new Service2(); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SpanTagAnnotationHandlerTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/annotation/SpanTagAnnotationHandlerTests.java similarity index 91% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SpanTagAnnotationHandlerTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/annotation/SpanTagAnnotationHandlerTests.java index 7b7a396e5..a7615fb6e 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/annotation/SpanTagAnnotationHandlerTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/annotation/SpanTagAnnotationHandlerTests.java @@ -19,23 +19,21 @@ package org.springframework.cloud.sleuth.annotation; import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import brave.sampler.Sampler; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; -@SpringBootTest(classes = SpanTagAnnotationHandlerTests.TestConfiguration.class) - -public class SpanTagAnnotationHandlerTests { +@ContextConfiguration(classes = SpanTagAnnotationHandlerTests.TestConfiguration.class) +public abstract class SpanTagAnnotationHandlerTests { @Autowired BeanFactory beanFactory; @@ -90,9 +88,9 @@ public class SpanTagAnnotationHandlerTests { } } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration - protected static class TestConfiguration { + public static class TestConfiguration { // tag::custom_resolver[] @Bean(name = "myCustomTagValueResolver") @@ -101,11 +99,6 @@ public class SpanTagAnnotationHandlerTests { } // end::custom_resolver[] - @Bean - Sampler alwaysSampler() { - return Sampler.ALWAYS_SAMPLE; - } - } protected class AnnotationMockClass { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/baggage/BaggageTagSpanHandlerTest.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/baggage/BaggageEntryTagSpanHandlerTest.java similarity index 53% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/baggage/BaggageTagSpanHandlerTest.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/baggage/BaggageEntryTagSpanHandlerTest.java index 4de90a0a3..0f7c19fc3 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/baggage/BaggageTagSpanHandlerTest.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/baggage/BaggageEntryTagSpanHandlerTest.java @@ -16,35 +16,30 @@ package org.springframework.cloud.sleuth.baggage; -import brave.ScopedSpan; -import brave.Tracer; -import brave.baggage.BaggageField; -import brave.handler.SpanHandler; -import brave.propagation.TraceContext; -import brave.test.TestSpanHandler; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Bean; +import org.springframework.cloud.sleuth.api.BaggageEntry; +import org.springframework.cloud.sleuth.api.ScopedSpan; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.test.TestSpanHandler; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.ActiveProfiles; - -import static org.assertj.core.api.Assertions.assertThat; +import org.springframework.test.context.ContextConfiguration; /** * @author Taras Danylchuk */ -@SpringBootTest( - // WebEnvironment.NONE will not read a Yaml profile - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = BaggageTagSpanHandlerTest.Config.class) +@ContextConfiguration(classes = BaggageEntryTagSpanHandlerTest.TestConfig.class) @ActiveProfiles("baggage") // application-baggage.yml -public class BaggageTagSpanHandlerTest { +public abstract class BaggageEntryTagSpanHandlerTest { - static final BaggageField COUNTRY_CODE = BaggageField.create("country-code"); - static final BaggageField REQUEST_ID = BaggageField.create("x-vcap-request-id"); + BaggageEntry countryCode; + + BaggageEntry requestId; @Autowired private Tracer tracer; @@ -58,29 +53,26 @@ public class BaggageTagSpanHandlerTest { public void setUp() { this.spans.clear(); this.span = this.tracer.startScopedSpan("my-scoped-span"); - TraceContext context = this.span.context(); - COUNTRY_CODE.updateValue(context, "FO"); - REQUEST_ID.updateValue(context, "f4308d05-2228-4468-80f6-92a8377ba193"); + this.countryCode = this.tracer.createBaggage("country-code"); + this.countryCode.set("FO"); + this.requestId = this.tracer.createBaggage("x-vcap-request-id"); + this.requestId.set("f4308d05-2228-4468-80f6-92a8377ba193"); } @Test public void shouldReportWithBaggageInTags() { - this.span.finish(); + this.span.end(); - assertThat(this.spans).hasSize(1); - assertThat(this.spans.get(0).tags()).hasSize(1) // REQUEST_ID is not in the - // tag-fields - .containsEntry(COUNTRY_CODE.name(), "FO"); + Assertions.assertThat(this.spans).hasSize(1); + Assertions.assertThat(this.spans.get(0).tags()).hasSize(1) // REQUEST_ID is not in + // the + // tag-fields + .containsEntry(countryCode.name(), "FO"); } @EnableAutoConfiguration - @Configuration - static class Config { - - @Bean - public SpanHandler testSpanHandler() { - return new TestSpanHandler(); - } + @Configuration(proxyBeanMethods = false) + static class TestConfig { } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/multiple/DemoApplication.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/baggage/multiple/DemoApplication.java similarity index 87% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/multiple/DemoApplication.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/baggage/multiple/DemoApplication.java index a0a72a7d0..695a12c04 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/multiple/DemoApplication.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/baggage/multiple/DemoApplication.java @@ -14,18 +14,18 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.multiple; +package org.springframework.cloud.sleuth.baggage.multiple; import java.util.Arrays; import java.util.List; -import brave.Span; -import brave.Tags; -import brave.Tracer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.sleuth.api.BaggageEntry; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.http.HttpHeaders; import org.springframework.integration.annotation.Aggregator; import org.springframework.integration.annotation.Gateway; @@ -40,7 +40,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import static org.springframework.cloud.sleuth.instrument.multiple.MultipleHopsIntegrationTests.COUNTRY_CODE; +import static org.springframework.cloud.sleuth.baggage.multiple.MultipleHopsIntegrationTests.COUNTRY_CODE; @MessagingGateway(name = "greeter") interface Sender { @@ -78,7 +78,10 @@ public class DemoApplication { this.httpSpan = this.tracer.currentSpan(); // tag what was propagated - Tags.BAGGAGE_FIELD.tag(COUNTRY_CODE, httpSpan); + BaggageEntry baggageEntry = this.tracer.getBaggage(COUNTRY_CODE); + if (baggageEntry != null && baggageEntry.get() != null) { + this.httpSpan.tag(COUNTRY_CODE, baggageEntry.get()); + } return new Greeting(message); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/multiple/MultipleHopsIntegrationTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/baggage/multiple/MultipleHopsIntegrationTests.java similarity index 51% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/multiple/MultipleHopsIntegrationTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/baggage/multiple/MultipleHopsIntegrationTests.java index da6a20aa7..1d42e4845 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/multiple/MultipleHopsIntegrationTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/baggage/multiple/MultipleHopsIntegrationTests.java @@ -14,38 +14,36 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.multiple; +package org.springframework.cloud.sleuth.baggage.multiple; import java.net.URI; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; -import brave.Span; -import brave.Tags; -import brave.Tracer; -import brave.Tracer.SpanInScope; -import brave.baggage.BaggageField; -import brave.baggage.BaggagePropagationConfig; -import brave.baggage.BaggagePropagationConfig.SingleBaggageField; -import brave.handler.MutableSpan; -import brave.handler.SpanHandler; -import brave.sampler.Sampler; -import brave.test.TestSpanHandler; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.test.TestSpanHandler; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.RequestEntity; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; import org.springframework.web.client.RestTemplate; import static java.util.Arrays.asList; @@ -56,29 +54,32 @@ import static org.assertj.core.api.BDDAssertions.then; import static org.awaitility.Awaitility.await; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; -@SpringBootTest(classes = MultipleHopsIntegrationTests.Config.class, webEnvironment = RANDOM_PORT, - properties = { "spring.sleuth.baggage.remote-fields=x-vcap-request-id,country-code", - "spring.sleuth.baggage.local-fields=bp", "spring.sleuth.integration.enabled=true" }) -public class MultipleHopsIntegrationTests { +@SpringBootTest(webEnvironment = RANDOM_PORT) +@ContextConfiguration(classes = MultipleHopsIntegrationTests.TestConfig.class) +@TestPropertySource(properties = { "spring.sleuth.baggage.remote-fields=x-vcap-request-id,country-code", + "spring.sleuth.baggage.local-fields=bp", "spring.sleuth.integration.enabled=true" }) +public abstract class MultipleHopsIntegrationTests { - static final BaggageField REQUEST_ID = BaggageField.create("x-vcap-request-id"); - static final BaggageField BUSINESS_PROCESS = BaggageField.create("bp"); - static final BaggageField COUNTRY_CODE = BaggageField.create("country-code"); + protected static final String REQUEST_ID = "x-vcap-request-id"; + + protected static final String BUSINESS_PROCESS = "bp"; + + protected static final String COUNTRY_CODE = "country-code"; @Autowired Tracer tracer; @Autowired - TestSpanHandler spans; + protected TestSpanHandler spans; @Autowired RestTemplate restTemplate; @Autowired - Config config; + TestConfig testConfig; @Autowired - DemoApplication application; + protected DemoApplication application; @BeforeEach public void setup() { @@ -87,67 +88,74 @@ public class MultipleHopsIntegrationTests { @Test public void should_prepare_spans_for_export() { - this.restTemplate.getForObject("http://localhost:" + this.config.port + "/greeting", String.class); + this.restTemplate.getForObject("http://localhost:" + this.testConfig.port + "/greeting", String.class); await().atMost(5, SECONDS).untilAsserted(() -> { then(this.spans).hasSize(14); }); - then(this.spans).extracting(MutableSpan::name).containsAll(asList("GET /greeting", "send")); - then(this.spans).extracting(MutableSpan::kind) + assertSpanNames(); + then(this.spans).extracting(FinishedSpan::kind) // no server kind due to test constraints .containsAll(asList(Span.Kind.CONSUMER, Span.Kind.PRODUCER, Span.Kind.SERVER)); - then(this.spans.spans().stream().map(span -> span.tags().get("channel")).filter(Objects::nonNull).distinct() - .collect(toList())).hasSize(3).containsAll(asList("words", "counts", "greetings")); + then(this.spans.reportedSpans().stream().map(span -> span.tags().get("channel")).filter(Objects::nonNull) + .distinct().collect(toList())).hasSize(3).containsAll(asList("words", "counts", "greetings")); + } + + protected void assertSpanNames() { + throw new UnsupportedOperationException("Implement this assertion"); } @Test public void should_propagate_the_baggage() { - // tag::baggage[] Span initialSpan = this.tracer.nextSpan().name("span").start(); - BUSINESS_PROCESS.updateValue(initialSpan.context(), "ALM"); - COUNTRY_CODE.updateValue(initialSpan.context(), "FO"); - // end::baggage[] - - try (SpanInScope ws = this.tracer.withSpanInScope(initialSpan)) { + System.out.println("FOO: " + initialSpan.context().traceId()); + // tag::baggage[] + try (Tracer.SpanInScope ws = this.tracer.withSpan(initialSpan)) { + this.tracer.createBaggage(BUSINESS_PROCESS).set("ALM"); + this.tracer.createBaggage(COUNTRY_CODE).set("FO"); + // end::baggage[] // tag::baggage_tag[] - Tags.BAGGAGE_FIELD.tag(BUSINESS_PROCESS, initialSpan); + initialSpan.tag(BUSINESS_PROCESS, "ALM"); // end::baggage_tag[] // set request ID in a header not with the api explicitly HttpHeaders headers = new HttpHeaders(); - headers.put(REQUEST_ID.name(), Collections.singletonList("f4308d05-2228-4468-80f6-92a8377ba193")); + headers.put(REQUEST_ID, Collections.singletonList("f4308d05-2228-4468-80f6-92a8377ba193")); RequestEntity requestEntity = new RequestEntity(headers, HttpMethod.GET, - URI.create("http://localhost:" + this.config.port + "/greeting")); + URI.create("http://localhost:" + this.testConfig.port + "/greeting")); this.restTemplate.exchange(requestEntity, String.class); } finally { - initialSpan.finish(); + initialSpan.end(); } await().atMost(5, SECONDS).untilAsserted(() -> { then(this.spans).isNotEmpty(); }); - List withBagTags = this.spans.spans().stream() - .filter(s -> s.tags().containsKey(BUSINESS_PROCESS.name())).collect(toList()); + List withBagTags = this.spans.reportedSpans().stream() + .filter(s -> s.tags().containsKey(BUSINESS_PROCESS)).collect(toList()); // set with tag api then(withBagTags).as("only initialSpan was bag tagged").hasSize(1); - assertThat(withBagTags.get(0).tags()).containsEntry(BUSINESS_PROCESS.name(), "ALM"); + assertThat(withBagTags.get(0).tags()).containsEntry(BUSINESS_PROCESS, "ALM"); - // set with baggage api - then(this.application.allSpans()).as("All have request ID") - .allMatch(span -> "f4308d05-2228-4468-80f6-92a8377ba193".equals(REQUEST_ID.getValue(span.context()))); + Set traceIds = this.application.allSpans().stream().map(s -> s.context().traceId()) + .collect(Collectors.toSet()); + then(traceIds).hasSize(1); + then(traceIds.iterator().next()).as("All have same trace ID").isEqualTo(initialSpan.context().traceId()); + assertBaggage(initialSpan); - // baz is not tagged in the initial span, only downstream! - then(this.application.allSpans()).as("All downstream have country-code") - .filteredOn(span -> !span.equals(initialSpan)) - .allMatch(span -> "FO".equals(COUNTRY_CODE.getValue(span.context()))); } - @Configuration - @SpringBootApplication(exclude = JmxAutoConfiguration.class) - public static class Config implements ApplicationListener { + protected void assertBaggage(Span initialSpan) { + throw new UnsupportedOperationException("Implement this assertion"); + } + + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration(exclude = JmxAutoConfiguration.class) + @Import(DemoApplication.class) + public static class TestConfig implements ApplicationListener { int port; @@ -156,26 +164,11 @@ public class MultipleHopsIntegrationTests { this.port = event.getSource().getPort(); } - @Bean - BaggagePropagationConfig notInProperties() { - return SingleBaggageField.remote(BaggageField.create("bar")); - } - @Bean RestTemplate restTemplate() { return new RestTemplate(); } - @Bean - SpanHandler testSpanHandler() { - return new TestSpanHandler(); - } - - @Bean - Sampler defaultTraceSampler() { - return Sampler.ALWAYS_SAMPLE; - } - } } diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/brave/BraveIntegrationTestTracing.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/brave/BraveIntegrationTestTracing.java new file mode 100644 index 000000000..28617fe70 --- /dev/null +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/brave/BraveIntegrationTestTracing.java @@ -0,0 +1,45 @@ +/* + * Copyright 2013-2020 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.brave; + +import brave.Tracing; +import brave.test.IntegrationTestSpanHandler; + +import org.springframework.cloud.sleuth.test.TestSpanHandler; + +public class BraveIntegrationTestTracing extends BraveTestTracing { + + IntegrationTestSpanHandler spanHandler; + + @Override + public Tracing.Builder tracingBuilder() { + return super.tracingBuilder().addSpanHandler(initSpanHandler()); + } + + @Override + public TestSpanHandler handler() { + return new BraveTestSpanHandler(spans, initSpanHandler()); + } + + private IntegrationTestSpanHandler initSpanHandler() { + if (this.spanHandler == null) { + this.spanHandler = new IntegrationTestSpanHandler(); + } + return this.spanHandler; + } + +} diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/brave/BraveTestSpanHandler.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/brave/BraveTestSpanHandler.java new file mode 100644 index 000000000..3bf992d46 --- /dev/null +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/brave/BraveTestSpanHandler.java @@ -0,0 +1,89 @@ +/* + * Copyright 2013-2020 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.brave; + +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +import brave.test.IntegrationTestSpanHandler; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.brave.bridge.BraveFinishedSpan; +import org.springframework.cloud.sleuth.test.TestSpanHandler; + +public class BraveTestSpanHandler implements TestSpanHandler { + + final brave.test.TestSpanHandler spans; + + final IntegrationTestSpanHandler integrationSpans; + + public BraveTestSpanHandler(brave.test.TestSpanHandler spans) { + this.spans = spans; + this.integrationSpans = null; + } + + public BraveTestSpanHandler(IntegrationTestSpanHandler integrationSpans) { + this.spans = null; + this.integrationSpans = integrationSpans; + } + + public BraveTestSpanHandler(brave.test.TestSpanHandler spans, IntegrationTestSpanHandler integrationSpans) { + this.spans = spans; + this.integrationSpans = integrationSpans; + } + + @Override + public List reportedSpans() { + return this.spans.spans().stream().map(BraveFinishedSpan::new).collect(Collectors.toList()); + } + + @Override + public FinishedSpan takeLocalSpan() { + return new BraveFinishedSpan(this.integrationSpans.takeLocalSpan()); + } + + @Override + public void clear() { + if (this.spans != null) { + this.spans.clear(); + } + } + + @Override + public FinishedSpan takeRemoteSpan(Span.Kind kind) { + return new BraveFinishedSpan(this.integrationSpans.takeRemoteSpan(brave.Span.Kind.valueOf(kind.name()))); + } + + @Override + public FinishedSpan takeRemoteSpanWithError(Span.Kind kind) { + return new BraveFinishedSpan( + this.integrationSpans.takeRemoteSpanWithError(brave.Span.Kind.valueOf(kind.name()))); + } + + @Override + public FinishedSpan get(int index) { + return new BraveFinishedSpan(this.spans.get(index)); + } + + @Override + public Iterator iterator() { + return reportedSpans().iterator(); + } + +} diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/brave/BraveTestTracing.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/brave/BraveTestTracing.java new file mode 100644 index 000000000..eed13434e --- /dev/null +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/brave/BraveTestTracing.java @@ -0,0 +1,158 @@ +/* + * Copyright 2013-2020 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.brave; + +import java.io.Closeable; + +import brave.Tracing; +import brave.handler.SpanHandler; +import brave.http.HttpTracing; +import brave.propagation.StrictScopeDecorator; +import brave.propagation.ThreadLocalCurrentTraceContext; +import brave.sampler.Sampler; + +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; +import org.springframework.cloud.sleuth.api.http.HttpRequestParser; +import org.springframework.cloud.sleuth.api.http.HttpServerHandler; +import org.springframework.cloud.sleuth.api.propagation.Propagator; +import org.springframework.cloud.sleuth.brave.bridge.BraveCurrentTraceContext; +import org.springframework.cloud.sleuth.brave.bridge.BravePropagator; +import org.springframework.cloud.sleuth.brave.bridge.BraveTracer; +import org.springframework.cloud.sleuth.brave.bridge.http.BraveHttpClientHandler; +import org.springframework.cloud.sleuth.brave.bridge.http.BraveHttpRequestParser; +import org.springframework.cloud.sleuth.brave.bridge.http.BraveHttpServerHandler; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.cloud.sleuth.test.TestTracingAssertions; +import org.springframework.cloud.sleuth.test.TestTracingAware; +import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; +import org.springframework.cloud.sleuth.test.TracerAware; + +public class BraveTestTracing implements TracerAware, TestTracingAware, TestTracingAwareSupplier, Closeable { + + brave.test.TestSpanHandler spans = new brave.test.TestSpanHandler(); + + Sampler sampler = Sampler.ALWAYS_SAMPLE; + + ThreadLocalCurrentTraceContext context = ThreadLocalCurrentTraceContext.newBuilder() + .addScopeDecorator(StrictScopeDecorator.create()).build(); + + Tracing.Builder builder = tracingBuilder(); + + Tracing tracing = builder.build(); + + public Tracing.Builder tracingBuilder() { + Tracing.Builder builder = Tracing.newBuilder().currentTraceContext(context).sampler(this.sampler) + .addSpanHandler(spanHandler()); + this.builder = builder; + return builder; + } + + public BraveTestTracing tracingBuilder(Tracing.Builder builder) { + this.builder = builder; + return this; + } + + brave.Tracer tracer = this.tracing.tracer(); + + HttpTracing httpTracing = httpTracingBuilder().build(); + + public HttpTracing.Builder httpTracingBuilder() { + return HttpTracing.newBuilder(this.tracing); + } + + @Override + public Tracer tracer() { + return BraveTracer.fromBrave(this.tracer); + } + + @Override + public TracerAware sampler(TraceSampler sampler) { + this.sampler = sampler == TraceSampler.ON ? Sampler.ALWAYS_SAMPLE : Sampler.NEVER_SAMPLE; + this.builder = tracingBuilder(); + reset(); + return this; + } + + public void reset() { + this.tracing = this.builder.build(); + this.tracer = this.tracing.tracer(); + this.httpTracing = httpTracingBuilder().build(); + } + + SpanHandler spanHandler() { + return this.spans; + } + + @Override + public CurrentTraceContext currentTraceContext() { + return BraveCurrentTraceContext.fromBrave(this.context); + } + + @Override + public Propagator propagator() { + return new BravePropagator(this.tracing); + } + + @Override + public HttpServerHandler httpServerHandler() { + return new BraveHttpServerHandler(brave.http.HttpServerHandler.create(this.httpTracing)); + } + + @Override + public TracerAware clientRequestParser(HttpRequestParser httpRequestParser) { + reset(); + this.httpTracing = this.httpTracing.toBuilder() + .clientRequestParser(BraveHttpRequestParser.toBrave(httpRequestParser)).build(); + return this; + } + + @Override + public HttpClientHandler httpClientHandler() { + return new BraveHttpClientHandler(brave.http.HttpClientHandler.create(this.httpTracing)); + } + + @Override + public TracerAware tracing() { + return this; + } + + @Override + public TestSpanHandler handler() { + return new BraveTestSpanHandler(this.spans); + } + + @Override + public TestTracingAssertions assertions() { + return new BraveTestTracingAssertions(); + } + + @Override + public TestTracingAware tracerTest() { + return this; + } + + @Override + public void close() { + this.spans.clear(); + this.context.clear(); + handler().clear(); + this.sampler = Sampler.ALWAYS_SAMPLE; + } + +} diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/brave/BraveTestTracingAssertions.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/brave/BraveTestTracingAssertions.java new file mode 100644 index 000000000..9740a69cf --- /dev/null +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/brave/BraveTestTracingAssertions.java @@ -0,0 +1,31 @@ +/* + * Copyright 2013-2020 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.brave; + +import org.assertj.core.api.BDDAssertions; + +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.test.TestTracingAssertions; + +public class BraveTestTracingAssertions implements TestTracingAssertions { + + @Override + public void assertThatNoParentPresent(FinishedSpan finishedSpan) { + BDDAssertions.then(finishedSpan.parentId()).isNull(); + } + +} diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/AsyncDisabledTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/AsyncDisabledTests.java similarity index 80% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/AsyncDisabledTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/AsyncDisabledTests.java index 8ea379a0f..a2c9da652 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/AsyncDisabledTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/AsyncDisabledTests.java @@ -20,24 +20,24 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; -import brave.propagation.CurrentTraceContext; +import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; -import static org.assertj.core.api.BDDAssertions.then; - -@SpringBootTest(classes = AsyncDisabledTests.ConfigureThreadPoolTaskScheduler.class, - webEnvironment = SpringBootTest.WebEnvironment.NONE, properties = "spring.sleuth.scheduled.enabled=false") -public class AsyncDisabledTests { +@ContextConfiguration(classes = AsyncDisabledTests.ConfigureThreadPoolTaskScheduler.class) +@TestPropertySource(properties = "spring.sleuth.scheduled.enabled=false") +public abstract class AsyncDisabledTests { @Autowired CurrentTraceContext currentTraceContext; @@ -54,10 +54,10 @@ public class AsyncDisabledTests { public void should_not_wrap_scheduler() throws InterruptedException { BlockingQueue spans = new LinkedBlockingQueue<>(); this.executor.execute(() -> spans.add(this.currentTraceContext.get() != null)); - then(spans.take()).isFalse(); + BDDAssertions.then(spans.take()).isFalse(); } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration @EnableAsync static class ConfigureThreadPoolTaskScheduler { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskSchedulerTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskSchedulerTests.java similarity index 96% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskSchedulerTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskSchedulerTests.java index b3edf3065..1942d513a 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskSchedulerTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceThreadPoolTaskSchedulerTests.java @@ -23,8 +23,6 @@ import java.util.concurrent.Callable; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadFactory; -import brave.Tracing; -import brave.propagation.StrictCurrentTraceContext; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -35,17 +33,15 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.BeanFactory; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; +import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; import org.springframework.scheduling.Trigger; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.util.ErrorHandler; @ExtendWith(MockitoExtension.class) -public class LazyTraceThreadPoolTaskSchedulerTests { - - StrictCurrentTraceContext currentTraceContext = StrictCurrentTraceContext.create(); - - Tracing tracing = Tracing.newBuilder().currentTraceContext(currentTraceContext).build(); +public abstract class LazyTraceThreadPoolTaskSchedulerTests implements TestTracingAwareSupplier { @Mock(lenient = true) BeanFactory beanFactory; @@ -63,12 +59,10 @@ public class LazyTraceThreadPoolTaskSchedulerTests { @AfterEach public void close() { this.executor.shutdown(); - this.tracing.close(); - this.currentTraceContext.close(); } BeanFactory beanFactory() { - BDDMockito.given(this.beanFactory.getBean(Tracing.class)).willReturn(this.tracing); + BDDMockito.given(this.beanFactory.getBean(Tracer.class)).willReturn(tracerTest().tracing().tracer()); BDDMockito.given(this.beanFactory.getBean(SpanNamer.class)).willReturn(new DefaultSpanNamer()); SleuthContextListenerAccessor.set(this.beanFactory, true); return this.beanFactory; diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListenerAccessor.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListenerAccessor.java similarity index 100% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListenerAccessor.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/SleuthContextListenerAccessor.java diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncAspectTest.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncAspectTest.java similarity index 67% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncAspectTest.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncAspectTest.java index 5f88b9f41..7b5706f5c 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncAspectTest.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncAspectTest.java @@ -16,31 +16,21 @@ package org.springframework.cloud.sleuth.instrument.async; -import brave.Tracing; -import brave.propagation.StrictCurrentTraceContext; -import brave.test.TestSpanHandler; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.reflect.MethodSignature; import org.assertj.core.api.BDDAssertions; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.BDDMockito; import org.mockito.Mockito; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; +import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; /** * @author Marcin Grzejszczak */ -public class TraceAsyncAspectTest { - - StrictCurrentTraceContext currentTraceContext = StrictCurrentTraceContext.create(); - - TestSpanHandler spans = new TestSpanHandler(); - - Tracing tracing = Tracing.newBuilder().currentTraceContext(this.currentTraceContext).addSpanHandler(this.spans) - .build(); +public abstract class TraceAsyncAspectTest implements TestTracingAwareSupplier { ProceedingJoinPoint point = Mockito.mock(ProceedingJoinPoint.class); @@ -53,16 +43,10 @@ public class TraceAsyncAspectTest { BDDMockito.given(this.point.getTarget()).willReturn(""); } - @AfterEach - public void close() { - this.tracing.close(); - this.currentTraceContext.close(); - } - // Issue#926 @Test public void should_work() throws Throwable { - TraceAsyncAspect asyncAspect = new TraceAsyncAspect(this.tracing.tracer(), new DefaultSpanNamer()) { + TraceAsyncAspect asyncAspect = new TraceAsyncAspect(tracerTest().tracing().tracer(), new DefaultSpanNamer()) { @Override String name(ProceedingJoinPoint pjp) { return "foo-bar"; @@ -71,9 +55,9 @@ public class TraceAsyncAspectTest { asyncAspect.traceBackgroundThread(this.point); - BDDAssertions.then(this.spans).hasSize(1); - BDDAssertions.then(this.spans.get(0).name()).isEqualTo("foo-bar"); - BDDAssertions.then(this.spans.get(0).finishTimestamp()).isPositive(); + BDDAssertions.then(tracerTest().handler().reportedSpans()).hasSize(1); + BDDAssertions.then(tracerTest().handler().reportedSpans().get(0).name()).isEqualTo("foo-bar"); + BDDAssertions.then(tracerTest().handler().reportedSpans().get(0).endTimestamp()).isPositive(); } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncListenableTaskExecutorTest.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncListenableTaskExecutorTest.java similarity index 58% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncListenableTaskExecutorTest.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncListenableTaskExecutorTest.java index 24dbf480d..9d1667311 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncListenableTaskExecutorTest.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncListenableTaskExecutorTest.java @@ -20,50 +20,37 @@ import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import brave.Span; -import brave.Tracer; -import brave.Tracing; -import brave.propagation.StrictCurrentTraceContext; import org.assertj.core.api.BDDAssertions; import org.awaitility.Awaitility; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; +import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; import org.springframework.core.task.AsyncListenableTaskExecutor; import org.springframework.core.task.SimpleAsyncTaskExecutor; /** * @author Marcin Grzejszczak */ -public class TraceAsyncListenableTaskExecutorTest { +public abstract class TraceAsyncListenableTaskExecutorTest implements TestTracingAwareSupplier { AsyncListenableTaskExecutor delegate = new SimpleAsyncTaskExecutor(); - StrictCurrentTraceContext currentTraceContext = StrictCurrentTraceContext.create(); - - Tracing tracing = Tracing.newBuilder().currentTraceContext(currentTraceContext).build(); - - Tracer tracer = this.tracing.tracer(); - TraceAsyncListenableTaskExecutor traceAsyncListenableTaskExecutor = new TraceAsyncListenableTaskExecutor( - this.delegate, this.tracing); - - @AfterEach - public void close() { - this.tracing.close(); - this.currentTraceContext.close(); - } + this.delegate, tracerTest().tracing().tracer(), new DefaultSpanNamer()); @Test public void should_submit_listenable_trace_runnable() throws Exception { AtomicBoolean executed = new AtomicBoolean(); - Span span = this.tracer.nextSpan().name("foo"); + Span span = tracerTest().tracing().tracer().nextSpan().name("foo"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { - this.traceAsyncListenableTaskExecutor.submitListenable(aRunnable(this.tracing, executed)).get(); + try (Tracer.SpanInScope ws = tracerTest().tracing().tracer().withSpan(span.start())) { + this.traceAsyncListenableTaskExecutor.submitListenable(aRunnable(executed)).get(); } finally { - span.finish(); + span.end(); } BDDAssertions.then(executed.get()).isTrue(); @@ -71,14 +58,14 @@ public class TraceAsyncListenableTaskExecutorTest { @Test public void should_submit_listenable_trace_callable() throws Exception { - Span span = this.tracer.nextSpan().name("foo"); + Span span = tracerTest().tracing().tracer().nextSpan().name("foo"); Span spanFromListenable; - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { - spanFromListenable = this.traceAsyncListenableTaskExecutor.submitListenable(aCallable(this.tracing)).get(); + try (Tracer.SpanInScope ws = tracerTest().tracing().tracer().withSpan(span.start())) { + spanFromListenable = this.traceAsyncListenableTaskExecutor.submitListenable(aCallable()).get(); } finally { - span.finish(); + span.end(); } BDDAssertions.then(spanFromListenable).isNotNull(); @@ -87,13 +74,13 @@ public class TraceAsyncListenableTaskExecutorTest { @Test public void should_execute_a_trace_runnable() throws Exception { AtomicBoolean executed = new AtomicBoolean(); - Span span = this.tracer.nextSpan().name("foo"); + Span span = tracerTest().tracing().tracer().nextSpan().name("foo"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { - this.traceAsyncListenableTaskExecutor.execute(aRunnable(this.tracing, executed)); + try (Tracer.SpanInScope ws = tracerTest().tracing().tracer().withSpan(span.start())) { + this.traceAsyncListenableTaskExecutor.execute(aRunnable(executed)); } finally { - span.finish(); + span.end(); } Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { @@ -104,13 +91,13 @@ public class TraceAsyncListenableTaskExecutorTest { @Test public void should_execute_with_timeout_a_trace_runnable() throws Exception { AtomicBoolean executed = new AtomicBoolean(); - Span span = this.tracer.nextSpan().name("foo"); + Span span = tracerTest().tracing().tracer().nextSpan().name("foo"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { - this.traceAsyncListenableTaskExecutor.execute(aRunnable(this.tracing, executed), 1L); + try (Tracer.SpanInScope ws = tracerTest().tracing().tracer().withSpan(span.start())) { + this.traceAsyncListenableTaskExecutor.execute(aRunnable(executed), 1L); } finally { - span.finish(); + span.end(); } Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { @@ -120,14 +107,14 @@ public class TraceAsyncListenableTaskExecutorTest { @Test public void should_submit_trace_callable() throws Exception { - Span span = this.tracer.nextSpan().name("foo"); + Span span = tracerTest().tracing().tracer().nextSpan().name("foo"); Span spanFromListenable; - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { - spanFromListenable = this.traceAsyncListenableTaskExecutor.submit(aCallable(this.tracing)).get(); + try (Tracer.SpanInScope ws = tracerTest().tracing().tracer().withSpan(span.start())) { + spanFromListenable = this.traceAsyncListenableTaskExecutor.submit(aCallable()).get(); } finally { - span.finish(); + span.end(); } BDDAssertions.then(spanFromListenable).isNotNull(); @@ -136,13 +123,13 @@ public class TraceAsyncListenableTaskExecutorTest { @Test public void should_submit_trace_runnable() throws Exception { AtomicBoolean executed = new AtomicBoolean(); - Span span = this.tracer.nextSpan().name("foo"); + Span span = tracerTest().tracing().tracer().nextSpan().name("foo"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { - this.traceAsyncListenableTaskExecutor.submit(aRunnable(this.tracing, executed)).get(); + try (Tracer.SpanInScope ws = tracerTest().tracing().tracer().withSpan(span.start())) { + this.traceAsyncListenableTaskExecutor.submit(aRunnable(executed)).get(); } finally { - span.finish(); + span.end(); } Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { @@ -150,15 +137,15 @@ public class TraceAsyncListenableTaskExecutorTest { }); } - Runnable aRunnable(Tracing tracing, AtomicBoolean executed) { + Runnable aRunnable(AtomicBoolean executed) { return () -> { - BDDAssertions.then(tracing.tracer().currentSpan()).isNotNull(); + BDDAssertions.then(tracerTest().tracing().tracer().currentSpan()).isNotNull(); executed.set(true); }; } - Callable aCallable(Tracing tracing) { - return () -> tracing.tracer().currentSpan(); + Callable aCallable() { + return () -> tracerTest().tracing().tracer().currentSpan(); } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceCallableTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceCallableTests.java similarity index 67% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceCallableTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceCallableTests.java index de50a2ac1..7a1ccbd55 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceCallableTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceCallableTests.java @@ -20,41 +20,26 @@ import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import brave.Span; -import brave.Tracer; -import brave.Tracing; -import brave.propagation.StrictCurrentTraceContext; -import brave.test.TestSpanHandler; +import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.cloud.sleuth.SpanName; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; - -import static org.assertj.core.api.BDDAssertions.then; +import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; @ExtendWith(MockitoExtension.class) -public class TraceCallableTests { +public abstract class TraceCallableTests implements TestTracingAwareSupplier { ExecutorService executor = Executors.newSingleThreadExecutor(); - StrictCurrentTraceContext currentTraceContext = StrictCurrentTraceContext.create(); - - TestSpanHandler spans = new TestSpanHandler(); - - Tracing tracing = Tracing.newBuilder().currentTraceContext(this.currentTraceContext).addSpanHandler(this.spans) - .build(); - - Tracer tracer = this.tracing.tracer(); - @AfterEach public void clean() { this.executor.shutdown(); - this.tracing.close(); - this.spans.clear(); - this.currentTraceContext.close(); } @Test @@ -63,7 +48,7 @@ public class TraceCallableTests { Span secondSpan = whenCallableGetsSubmitted(thatRetrievesTraceFromThreadLocal()); - then(secondSpan.context().traceId()).isNotEqualTo(firstSpan.context().traceId()); + BDDAssertions.then(secondSpan.context().traceId()).isNotEqualTo(firstSpan.context().traceId()); } @Test @@ -72,45 +57,47 @@ public class TraceCallableTests { Span secondSpan = whenNonTraceableCallableGetsSubmitted(thatRetrievesTraceFromThreadLocal()); - then(secondSpan).isNull(); + BDDAssertions.then(secondSpan).isNull(); } @Test public void should_remove_parent_span_from_thread_local_after_finishing_work() throws Exception { - Span parent = this.tracer.nextSpan().name("http:parent"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(parent)) { + Span parent = tracerTest().tracing().tracer().nextSpan().name("http:parent"); + try (Tracer.SpanInScope ws = tracerTest().tracing().tracer().withSpan(parent)) { Span child = givenCallableGetsSubmitted(thatRetrievesTraceFromThreadLocal()); - then(parent).as("parent").isNotNull(); - then(child.context().parentId()).isEqualTo(parent.context().spanId()); + BDDAssertions.then(parent).as("parent").isNotNull(); + BDDAssertions.then(child.context().parentId()).isEqualTo(parent.context().spanId()); } - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(tracerTest().tracing().tracer().currentSpan()).isNull(); Span secondSpan = whenNonTraceableCallableGetsSubmitted(thatRetrievesTraceFromThreadLocal()); - then(secondSpan).isNull(); + BDDAssertions.then(secondSpan).isNull(); } @Test public void should_take_name_of_span_from_span_name_annotation() throws Exception { whenATraceKeepingCallableGetsSubmitted(); - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("some-callable-name-from-annotation"); + BDDAssertions.then(tracerTest().handler().reportedSpans()).hasSize(1); + BDDAssertions.then(tracerTest().handler().reportedSpans().get(0).name()) + .isEqualTo("some-callable-name-from-annotation"); } @Test public void should_take_name_of_span_from_to_string_if_span_name_annotation_is_missing() throws Exception { whenCallableGetsSubmitted(thatRetrievesTraceFromThreadLocal()); - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("some-callable-name-from-to-string"); + BDDAssertions.then(tracerTest().handler().reportedSpans()).hasSize(1); + BDDAssertions.then(tracerTest().handler().reportedSpans().get(0).name()) + .isEqualTo("some-callable-name-from-to-string"); } private Callable thatRetrievesTraceFromThreadLocal() { return new Callable() { @Override public Span call() throws Exception { - return TraceCallableTests.this.tracer.currentSpan(); + return tracerTest().tracing().tracer().currentSpan(); } @Override @@ -127,13 +114,14 @@ public class TraceCallableTests { private Span whenCallableGetsSubmitted(Callable callable) throws InterruptedException, java.util.concurrent.ExecutionException { - return this.executor.submit(new TraceCallable<>(this.tracing, new DefaultSpanNamer(), callable)).get(); + return this.executor + .submit(new TraceCallable<>(tracerTest().tracing().tracer(), new DefaultSpanNamer(), callable)).get(); } private Span whenATraceKeepingCallableGetsSubmitted() throws InterruptedException, java.util.concurrent.ExecutionException { - return this.executor - .submit(new TraceCallable<>(this.tracing, new DefaultSpanNamer(), new TraceKeepingCallable())).get(); + return this.executor.submit(new TraceCallable<>(tracerTest().tracing().tracer(), new DefaultSpanNamer(), + new TraceKeepingCallable(tracerTest().tracing().tracer()))).get(); } private Span whenNonTraceableCallableGetsSubmitted(Callable callable) @@ -146,9 +134,15 @@ public class TraceCallableTests { public Span span; + private final Tracer tracer; + + TraceKeepingCallable(Tracer tracer) { + this.tracer = tracer; + } + @Override public Span call() throws Exception { - this.span = Tracing.currentTracer().currentSpan(); + this.span = this.tracer.currentSpan(); return this.span; } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnableTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnableTests.java similarity index 71% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnableTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnableTests.java index d07d01085..0f2fa2f41 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnableTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnableTests.java @@ -20,41 +20,26 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; -import brave.Span; -import brave.Tracer; -import brave.Tracing; -import brave.propagation.StrictCurrentTraceContext; -import brave.test.TestSpanHandler; +import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.cloud.sleuth.SpanName; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; - -import static org.assertj.core.api.BDDAssertions.then; +import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; @ExtendWith(MockitoExtension.class) -public class TraceRunnableTests { +public abstract class TraceRunnableTests implements TestTracingAwareSupplier { ExecutorService executor = Executors.newSingleThreadExecutor(); - StrictCurrentTraceContext currentTraceContext = StrictCurrentTraceContext.create(); - - TestSpanHandler spans = new TestSpanHandler(); - - Tracing tracing = Tracing.newBuilder().currentTraceContext(this.currentTraceContext).addSpanHandler(this.spans) - .build(); - - Tracer tracer = this.tracing.tracer(); - @AfterEach public void clean() { this.executor.shutdown(); - this.tracing.close(); - this.spans.clear(); - this.currentTraceContext.close(); } @Test @@ -63,18 +48,22 @@ public class TraceRunnableTests { TraceKeepingRunnable traceKeepingRunnable = runnableThatRetrievesTraceFromThreadLocal(); givenRunnableGetsSubmitted(traceKeepingRunnable); Span firstSpan = traceKeepingRunnable.span; - then(firstSpan).as("first span").isNotNull(); + BDDAssertions.then(firstSpan).as("first span").isNotNull(); // when whenRunnableGetsSubmitted(traceKeepingRunnable); // then Span secondSpan = traceKeepingRunnable.span; - then(secondSpan.context().traceId()).as("second span id").isNotEqualTo(firstSpan.context().traceId()) - .as("first span id"); + BDDAssertions.then(secondSpan.context().traceId()).as("second span id") + .isNotEqualTo(firstSpan.context().traceId()).as("first span id"); // and - then(secondSpan.context().parentId()).as("saved span as remnant of first span").isNull(); + assertThatThereIsNoParentId(secondSpan); + } + + protected void assertThatThereIsNoParentId(Span secondSpan) { + throw new UnsupportedOperationException("Implement this assertion"); } @Test @@ -83,14 +72,14 @@ public class TraceRunnableTests { TraceKeepingRunnable traceKeepingRunnable = runnableThatRetrievesTraceFromThreadLocal(); givenRunnableGetsSubmitted(traceKeepingRunnable); Span firstSpan = traceKeepingRunnable.span; - then(firstSpan).as("expected span").isNotNull(); + BDDAssertions.then(firstSpan).as("expected span").isNotNull(); // when whenNonTraceableRunnableGetsSubmitted(traceKeepingRunnable); // then Span secondSpan = traceKeepingRunnable.span; - then(secondSpan).as("unexpected span").isNull(); + BDDAssertions.then(secondSpan).as("unexpected span").isNull(); } @Test @@ -99,8 +88,9 @@ public class TraceRunnableTests { whenRunnableGetsSubmitted(traceKeepingRunnable); - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("some-runnable-name-from-annotation"); + BDDAssertions.then(tracerTest().handler().reportedSpans()).hasSize(1); + BDDAssertions.then(tracerTest().handler().reportedSpans().get(0).name()) + .isEqualTo("some-runnable-name-from-annotation"); } @Test @@ -110,12 +100,13 @@ public class TraceRunnableTests { whenRunnableGetsSubmitted(runnable); - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("some-runnable-name-from-to-string"); + BDDAssertions.then(tracerTest().handler().reportedSpans()).hasSize(1); + BDDAssertions.then(tracerTest().handler().reportedSpans().get(0).name()) + .isEqualTo("some-runnable-name-from-to-string"); } private TraceKeepingRunnable runnableThatRetrievesTraceFromThreadLocal() { - return new TraceKeepingRunnable(this.tracer); + return new TraceKeepingRunnable(tracerTest().tracing().tracer()); } private void givenRunnableGetsSubmitted(Runnable runnable) throws Exception { @@ -123,7 +114,8 @@ public class TraceRunnableTests { } private void whenRunnableGetsSubmitted(Runnable runnable) throws Exception { - this.executor.submit(new TraceRunnable(this.tracing, new DefaultSpanNamer(), runnable)).get(); + this.executor.submit(new TraceRunnable(tracerTest().tracing().tracer(), new DefaultSpanNamer(), runnable)) + .get(); } private void whenNonTraceableRunnableGetsSubmitted(Runnable runnable) throws Exception { @@ -134,7 +126,7 @@ public class TraceRunnableTests { return new Runnable() { @Override public void run() { - span.set(TraceRunnableTests.this.tracer.currentSpan()); + span.set(tracerTest().tracing().tracer().currentSpan()); } @Override diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorServiceTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorServiceTests.java similarity index 84% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorServiceTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorServiceTests.java index c3cfcb3d7..28d86c935 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorServiceTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorServiceTests.java @@ -28,12 +28,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import brave.ScopedSpan; -import brave.Tracer; -import brave.Tracing; -import brave.propagation.StrictCurrentTraceContext; -import brave.propagation.TraceContext; -import brave.test.TestSpanHandler; import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -47,13 +41,17 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.BeanFactory; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.ScopedSpan; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; +import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; import static java.util.stream.Collectors.toList; -import static org.assertj.core.api.BDDAssertions.then; @ExtendWith(MockitoExtension.class) -public class TraceableExecutorServiceTests { +public abstract class TraceableExecutorServiceTests implements TestTracingAwareSupplier { private static int TOTAL_THREADS = 10; @@ -64,22 +62,16 @@ public class TraceableExecutorServiceTests { ExecutorService traceManagerableExecutorService; - StrictCurrentTraceContext currentTraceContext = StrictCurrentTraceContext.create(); - - TestSpanHandler spans = new TestSpanHandler(); - - Tracing tracing = Tracing.newBuilder().currentTraceContext(this.currentTraceContext).addSpanHandler(this.spans) - .build(); - - Tracer tracer = this.tracing.tracer(); - SpanVerifyingRunnable spanVerifyingRunnable = new SpanVerifyingRunnable(); + Tracer tracer = tracerTest().tracing().tracer(); + + CurrentTraceContext currentTraceContext = tracerTest().tracing().currentTraceContext(); + @BeforeEach public void setup() { this.traceManagerableExecutorService = new TraceableExecutorService(beanFactory(true), this.executorService, "foo"); - this.spans.clear(); this.spanVerifyingRunnable.clear(); } @@ -87,8 +79,6 @@ public class TraceableExecutorServiceTests { public void tearDown() { this.traceManagerableExecutorService.shutdown(); this.executorService.shutdown(); - this.tracing.close(); - this.currentTraceContext.close(); } @Test @@ -99,11 +89,12 @@ public class TraceableExecutorServiceTests { CompletableFuture.allOf(runnablesExecutedViaTraceManagerableExecutorService()).get(); } finally { - span.finish(); + span.end(); } - then(this.spanVerifyingRunnable.traceIds.stream().distinct().collect(toList())).hasSize(1); - then(this.spanVerifyingRunnable.spanIds.stream().distinct().collect(toList())).hasSize(TOTAL_THREADS); + BDDAssertions.then(this.spanVerifyingRunnable.traceIds.stream().distinct().collect(toList())).hasSize(1); + BDDAssertions.then(this.spanVerifyingRunnable.spanIds.stream().distinct().collect(toList())) + .hasSize(TOTAL_THREADS); } @Test @@ -170,7 +161,7 @@ public class TraceableExecutorServiceTests { private List callables() { List list = new ArrayList<>(); - list.add(new TraceCallable<>(this.tracing, new DefaultSpanNamer(), () -> "foo")); + list.add(new TraceCallable<>(this.tracer, new DefaultSpanNamer(), () -> "foo")); list.add((Callable) () -> "bar"); return list; } @@ -188,8 +179,8 @@ public class TraceableExecutorServiceTests { "calculateTax")); // end::completablefuture[] - then(completableFuture.get()).isEqualTo(1_000_000L); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(completableFuture.get()).isEqualTo(1_000_000L); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } @Test @@ -202,8 +193,8 @@ public class TraceableExecutorServiceTests { return 1_000_000L; }, new TraceableExecutorService(beanFactory, executorService, "calculateTax")); - then(completableFuture.get()).isEqualTo(1_000_000L); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(completableFuture.get()).isEqualTo(1_000_000L); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } private CompletableFuture[] runnablesExecutedViaTraceManagerableExecutorService() { @@ -215,7 +206,8 @@ public class TraceableExecutorServiceTests { } BeanFactory beanFactory(boolean refreshed) { - BDDMockito.given(this.beanFactory.getBean(Tracing.class)).willReturn(this.tracing); + BDDMockito.given(this.beanFactory.getBean(org.springframework.cloud.sleuth.api.Tracer.class)) + .willReturn(this.tracer); BDDMockito.given(this.beanFactory.getBean(SpanNamer.class)).willReturn(new DefaultSpanNamer()); SleuthContextListenerAccessor.set(this.beanFactory, refreshed); return this.beanFactory; @@ -223,9 +215,9 @@ public class TraceableExecutorServiceTests { class SpanVerifyingRunnable implements Runnable { - Queue traceIds = new ConcurrentLinkedQueue<>(); + Queue traceIds = new ConcurrentLinkedQueue<>(); - Queue spanIds = new ConcurrentLinkedQueue<>(); + Queue spanIds = new ConcurrentLinkedQueue<>(); @Override public void run() { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorServiceTest.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorServiceTest.java similarity index 70% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorServiceTest.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorServiceTest.java index 966610f72..9f87c902c 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorServiceTest.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorServiceTest.java @@ -21,36 +21,28 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; -import brave.Tracing; -import brave.propagation.StrictCurrentTraceContext; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentMatcher; +import org.mockito.ArgumentMatchers; import org.mockito.BDDMockito; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.BeanFactory; import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.internal.DefaultSpanNamer; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.BDDMockito.then; -import static org.mockito.Mockito.never; +import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; /** * @author Marcin Grzejszczak */ @ExtendWith(MockitoExtension.class) -public class TraceableScheduledExecutorServiceTest { - - StrictCurrentTraceContext currentTraceContext = StrictCurrentTraceContext.create(); - - Tracing tracing = Tracing.newBuilder().currentTraceContext(this.currentTraceContext).build(); +public abstract class TraceableScheduledExecutorServiceTest implements TestTracingAwareSupplier { @Mock(lenient = true) BeanFactory beanFactory; @@ -66,46 +58,40 @@ public class TraceableScheduledExecutorServiceTest { beanFactory(); } - @AfterEach - public void close() { - this.tracing.close(); - this.currentTraceContext.close(); - } - @Test public void should_schedule_a_trace_runnable() throws Exception { this.traceableScheduledExecutorService.schedule(aRunnable(), 1L, TimeUnit.DAYS); - then(this.scheduledExecutorService).should().schedule( - BDDMockito.argThat(matcher(Runnable.class, instanceOf(TraceRunnable.class))), anyLong(), - any(TimeUnit.class)); + BDDMockito.then(this.scheduledExecutorService).should().schedule( + BDDMockito.argThat(matcher(Runnable.class, instanceOf(TraceRunnable.class))), + ArgumentMatchers.anyLong(), ArgumentMatchers.any(TimeUnit.class)); } @Test public void should_schedule_a_trace_callable() throws Exception { this.traceableScheduledExecutorService.schedule(aCallable(), 1L, TimeUnit.DAYS); - then(this.scheduledExecutorService).should().schedule( - BDDMockito.argThat(matcher(Callable.class, instanceOf(TraceCallable.class))), anyLong(), - any(TimeUnit.class)); + BDDMockito.then(this.scheduledExecutorService).should().schedule( + BDDMockito.argThat(matcher(Callable.class, instanceOf(TraceCallable.class))), + ArgumentMatchers.anyLong(), ArgumentMatchers.any(TimeUnit.class)); } @Test public void should_schedule_at_fixed_rate_a_trace_runnable() throws Exception { this.traceableScheduledExecutorService.scheduleAtFixedRate(aRunnable(), 1L, 1L, TimeUnit.DAYS); - then(this.scheduledExecutorService).should().scheduleAtFixedRate( - BDDMockito.argThat(matcher(Runnable.class, instanceOf(TraceRunnable.class))), anyLong(), anyLong(), - any(TimeUnit.class)); + BDDMockito.then(this.scheduledExecutorService).should().scheduleAtFixedRate( + BDDMockito.argThat(matcher(Runnable.class, instanceOf(TraceRunnable.class))), + ArgumentMatchers.anyLong(), ArgumentMatchers.anyLong(), ArgumentMatchers.any(TimeUnit.class)); } @Test public void should_schedule_with_fixed_delay_a_trace_runnable() throws Exception { this.traceableScheduledExecutorService.scheduleWithFixedDelay(aRunnable(), 1L, 1L, TimeUnit.DAYS); - then(this.scheduledExecutorService).should().scheduleWithFixedDelay( - BDDMockito.argThat(matcher(Runnable.class, instanceOf(TraceRunnable.class))), anyLong(), anyLong(), - any(TimeUnit.class)); + BDDMockito.then(this.scheduledExecutorService).should().scheduleWithFixedDelay( + BDDMockito.argThat(matcher(Runnable.class, instanceOf(TraceRunnable.class))), + ArgumentMatchers.anyLong(), ArgumentMatchers.anyLong(), ArgumentMatchers.any(TimeUnit.class)); } @Test @@ -113,9 +99,9 @@ public class TraceableScheduledExecutorServiceTest { SleuthContextListenerAccessor.set(this.beanFactory, false); this.traceableScheduledExecutorService.schedule(aRunnable(), 1L, TimeUnit.DAYS); - then(this.scheduledExecutorService).should(never()).schedule( - BDDMockito.argThat(matcher(Runnable.class, instanceOf(TraceRunnable.class))), anyLong(), - any(TimeUnit.class)); + BDDMockito.then(this.scheduledExecutorService).should(Mockito.never()).schedule( + BDDMockito.argThat(matcher(Runnable.class, instanceOf(TraceRunnable.class))), + ArgumentMatchers.anyLong(), ArgumentMatchers.any(TimeUnit.class)); } @Test @@ -123,9 +109,9 @@ public class TraceableScheduledExecutorServiceTest { SleuthContextListenerAccessor.set(this.beanFactory, false); this.traceableScheduledExecutorService.schedule(aCallable(), 1L, TimeUnit.DAYS); - then(this.scheduledExecutorService).should(never()).schedule( - BDDMockito.argThat(matcher(Callable.class, instanceOf(TraceCallable.class))), anyLong(), - any(TimeUnit.class)); + BDDMockito.then(this.scheduledExecutorService).should(Mockito.never()).schedule( + BDDMockito.argThat(matcher(Callable.class, instanceOf(TraceCallable.class))), + ArgumentMatchers.anyLong(), ArgumentMatchers.any(TimeUnit.class)); } @Test @@ -133,9 +119,9 @@ public class TraceableScheduledExecutorServiceTest { SleuthContextListenerAccessor.set(this.beanFactory, false); this.traceableScheduledExecutorService.scheduleAtFixedRate(aRunnable(), 1L, 1L, TimeUnit.DAYS); - then(this.scheduledExecutorService).should(never()).scheduleAtFixedRate( - BDDMockito.argThat(matcher(Runnable.class, instanceOf(TraceRunnable.class))), anyLong(), anyLong(), - any(TimeUnit.class)); + BDDMockito.then(this.scheduledExecutorService).should(Mockito.never()).scheduleAtFixedRate( + BDDMockito.argThat(matcher(Runnable.class, instanceOf(TraceRunnable.class))), + ArgumentMatchers.anyLong(), ArgumentMatchers.anyLong(), ArgumentMatchers.any(TimeUnit.class)); } @Test @@ -143,9 +129,9 @@ public class TraceableScheduledExecutorServiceTest { SleuthContextListenerAccessor.set(this.beanFactory, false); this.traceableScheduledExecutorService.scheduleWithFixedDelay(aRunnable(), 1L, 1L, TimeUnit.DAYS); - then(this.scheduledExecutorService).should(never()).scheduleWithFixedDelay( - BDDMockito.argThat(matcher(Runnable.class, instanceOf(TraceRunnable.class))), anyLong(), anyLong(), - any(TimeUnit.class)); + BDDMockito.then(this.scheduledExecutorService).should(Mockito.never()).scheduleWithFixedDelay( + BDDMockito.argThat(matcher(Runnable.class, instanceOf(TraceRunnable.class))), + ArgumentMatchers.anyLong(), ArgumentMatchers.anyLong(), ArgumentMatchers.any(TimeUnit.class)); } Predicate instanceOf(Class clazz) { @@ -166,7 +152,7 @@ public class TraceableScheduledExecutorServiceTest { } BeanFactory beanFactory() { - BDDMockito.given(this.beanFactory.getBean(Tracing.class)).willReturn(this.tracing); + BDDMockito.given(this.beanFactory.getBean(Tracer.class)).willReturn(tracerTest().tracing().tracer()); BDDMockito.given(this.beanFactory.getBean(SpanNamer.class)).willReturn(new DefaultSpanNamer()); SleuthContextListenerAccessor.set(this.beanFactory, true); return this.beanFactory; diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/CircuitBreakerIntegrationTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/CircuitBreakerIntegrationTests.java similarity index 63% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/CircuitBreakerIntegrationTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/CircuitBreakerIntegrationTests.java index cafb38c43..66366d87b 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/CircuitBreakerIntegrationTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/CircuitBreakerIntegrationTests.java @@ -18,29 +18,25 @@ package org.springframework.cloud.sleuth.instrument.circuitbreaker; import java.util.concurrent.atomic.AtomicReference; -import brave.ScopedSpan; -import brave.Span; -import brave.Tracer; -import brave.handler.MutableSpan; -import brave.handler.SpanHandler; -import brave.sampler.Sampler; -import brave.test.TestSpanHandler; import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory; import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; +import org.springframework.cloud.sleuth.api.ScopedSpan; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.test.TestSpanHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; -import static org.assertj.core.api.BDDAssertions.then; - -@SpringBootTest(classes = CircuitBreakerIntegrationTests.Config.class) -public class CircuitBreakerIntegrationTests { +@ContextConfiguration(classes = CircuitBreakerIntegrationTests.TestConfig.class) +public abstract class CircuitBreakerIntegrationTests { @Autowired TestSpanHandler spans; @@ -66,11 +62,11 @@ public class CircuitBreakerIntegrationTests { // when Span span = this.factory.create("name").run(tracer::currentSpan); - then(span).isNotNull(); - then(scopedSpan.context().traceIdString()).isEqualTo(span.context().traceIdString()); + BDDAssertions.then(span).isNotNull(); + BDDAssertions.then(scopedSpan.context().traceId()).isEqualTo(span.context().traceId()); } finally { - scopedSpan.finish(); + scopedSpan.end(); } } @@ -92,43 +88,37 @@ public class CircuitBreakerIntegrationTests { throw new IllegalStateException("boom2"); })).isInstanceOf(IllegalStateException.class).hasMessageContaining("boom2"); - then(this.spans).hasSize(2); - then(scopedSpan.context().traceIdString()).isEqualTo(first.get().context().traceIdString()); - then(scopedSpan.context().traceIdString()).isEqualTo(second.get().context().traceIdString()); - then(first.get().context().spanIdString()).isNotEqualTo(second.get().context().spanIdString()); + BDDAssertions.then(this.spans).hasSize(2); + BDDAssertions.then(scopedSpan.context().traceId()).isEqualTo(first.get().context().traceId()); + BDDAssertions.then(scopedSpan.context().traceId()).isEqualTo(second.get().context().traceId()); + BDDAssertions.then(first.get().context().spanId()).isNotEqualTo(second.get().context().spanId()); - MutableSpan reportedSpan = this.spans.get(0); - then(reportedSpan.name()).contains("CircuitBreakerIntegrationTests"); - then(reportedSpan.tags().get("error")).contains("boom"); + FinishedSpan finishedSpan = this.spans.get(0); + BDDAssertions.then(finishedSpan.name()).contains("CircuitBreakerIntegrationTests"); + assertException(finishedSpan); - reportedSpan = this.spans.get(1); - then(reportedSpan.name()).contains("CircuitBreakerIntegrationTests"); - then(reportedSpan.tags().get("error")).contains("boom2"); + finishedSpan = this.spans.get(1); + BDDAssertions.then(finishedSpan.name()).contains("CircuitBreakerIntegrationTests"); + assertException(finishedSpan); } finally { - scopedSpan.finish(); + scopedSpan.end(); } } - @Configuration - @EnableAutoConfiguration - static class Config { + public void assertException(FinishedSpan finishedSpan) { + throw new UnsupportedOperationException("Implement this assertion"); + } - @Bean - SpanHandler testSpanHandler() { - return new TestSpanHandler(); - } + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration + public static class TestConfig { @Bean Resilience4JCircuitBreakerFactory resilience4JCircuitBreakerFactory() { return new Resilience4JCircuitBreakerFactory(); } - @Bean - Sampler sampler() { - return Sampler.ALWAYS_SAMPLE; - } - } } diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/CircuitBreakerTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/CircuitBreakerTests.java new file mode 100644 index 000000000..e427f7ef5 --- /dev/null +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/CircuitBreakerTests.java @@ -0,0 +1,91 @@ +/* + * Copyright 2013-2020 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.circuitbreaker; + +import java.util.concurrent.atomic.AtomicReference; + +import org.assertj.core.api.BDDAssertions; +import org.junit.jupiter.api.Test; + +import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory; +import org.springframework.cloud.sleuth.api.ScopedSpan; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; + +public abstract class CircuitBreakerTests implements TestTracingAwareSupplier { + + @Test + public void should_pass_tracing_information_when_using_circuit_breaker() { + // given + Tracer tracer = tracerTest().tracing().tracer(); + ScopedSpan scopedSpan = null; + try { + scopedSpan = tracer.startScopedSpan("start"); + // when + Span span = new Resilience4JCircuitBreakerFactory().create("name") + .run(new TraceSupplier<>(tracerTest().tracing().tracer(), tracer::currentSpan)); + + BDDAssertions.then(span).isNotNull(); + BDDAssertions.then(scopedSpan.context().traceId()).isEqualTo(span.context().traceId()); + } + finally { + scopedSpan.end(); + } + } + + @Test + public void should_pass_tracing_information_when_using_circuit_breaker_with_fallback() { + // given + Tracer tracer = tracerTest().tracing().tracer(); + AtomicReference first = new AtomicReference<>(); + AtomicReference second = new AtomicReference<>(); + ScopedSpan scopedSpan = null; + try { + scopedSpan = tracer.startScopedSpan("start"); + // when + BDDAssertions.thenThrownBy(() -> new Resilience4JCircuitBreakerFactory().create("name") + .run(new TraceSupplier<>(tracerTest().tracing().tracer(), () -> { + first.set(tracer.currentSpan()); + throw new IllegalStateException("boom"); + }), new TraceFunction<>(tracerTest().tracing().tracer(), throwable -> { + second.set(tracer.currentSpan()); + throw new IllegalStateException("boom2"); + }))).isInstanceOf(IllegalStateException.class).hasMessageContaining("boom2"); + + BDDAssertions.then(tracerTest().handler().reportedSpans()).hasSize(2); + BDDAssertions.then(first.get()).isNotNull(); + BDDAssertions.then(second.get()).isNotNull(); + BDDAssertions.then(scopedSpan.context().traceId()).isEqualTo(first.get().context().traceId()); + BDDAssertions.then(scopedSpan.context().traceId()).isEqualTo(second.get().context().traceId()); + BDDAssertions.then(first.get().context().spanId()).isNotEqualTo(second.get().context().spanId()); + + FinishedSpan finishedSpan = tracerTest().handler().reportedSpans().get(1); + BDDAssertions.then(finishedSpan.name()).contains("CircuitBreakerTests"); + additionalAssertions(finishedSpan); + } + finally { + scopedSpan.end(); + } + } + + public void additionalAssertions(FinishedSpan finishedSpan) { + throw new UnsupportedOperationException("Assert errors"); + } + +} diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceWebSocketAutoConfigurationTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceWebSocketAutoConfigurationTests.java similarity index 73% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceWebSocketAutoConfigurationTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceWebSocketAutoConfigurationTests.java index 0e9cec9a7..c4d224b85 100644 --- a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceWebSocketAutoConfigurationTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceWebSocketAutoConfigurationTests.java @@ -16,45 +16,42 @@ package org.springframework.cloud.sleuth.instrument.messaging; -import brave.sampler.Sampler; +import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.test.context.ContextConfiguration; import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer; import org.springframework.web.socket.config.annotation.DelegatingWebSocketMessageBrokerConfiguration; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; -import static org.assertj.core.api.BDDAssertions.then; - /** * @author Marcin Grzejszczak */ -@SpringBootTest(classes = TraceWebSocketAutoConfigurationTests.Config.class) -public class TraceWebSocketAutoConfigurationTests { +@ContextConfiguration(classes = TraceWebSocketAutoConfigurationTests.TestConfig.class) +public abstract class TraceWebSocketAutoConfigurationTests { @Autowired DelegatingWebSocketMessageBrokerConfiguration delegatingWebSocketMessageBrokerConfiguration; @Test public void should_register_interceptors_for_all_channels() { - then(this.delegatingWebSocketMessageBrokerConfiguration.clientInboundChannel().getInterceptors()) + BDDAssertions.then(this.delegatingWebSocketMessageBrokerConfiguration.clientInboundChannel().getInterceptors()) .hasAtLeastOneElementOfType(TracingChannelInterceptor.class); - then(this.delegatingWebSocketMessageBrokerConfiguration.clientOutboundChannel().getInterceptors()) + BDDAssertions.then(this.delegatingWebSocketMessageBrokerConfiguration.clientOutboundChannel().getInterceptors()) .hasAtLeastOneElementOfType(TracingChannelInterceptor.class); - then(this.delegatingWebSocketMessageBrokerConfiguration.brokerChannel().getInterceptors()) + BDDAssertions.then(this.delegatingWebSocketMessageBrokerConfiguration.brokerChannel().getInterceptors()) .hasAtLeastOneElementOfType(TracingChannelInterceptor.class); } @EnableAutoConfiguration - @Configuration + @Configuration(proxyBeanMethods = false) @EnableWebSocketMessageBroker - public static class Config extends AbstractWebSocketMessageBrokerConfigurer { + public static class TestConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { @@ -67,11 +64,6 @@ public class TraceWebSocketAutoConfigurationTests { registry.addEndpoint("/hello").withSockJS(); } - @Bean - Sampler testSampler() { - return Sampler.ALWAYS_SAMPLE; - } - } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptorTest.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptorTest.java similarity index 71% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptorTest.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptorTest.java index db64342fa..a6bf02e33 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptorTest.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptorTest.java @@ -21,20 +21,16 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import brave.Span; -import brave.Tracing; -import brave.handler.MutableSpan; -import brave.propagation.B3Propagation; -import brave.propagation.StrictCurrentTraceContext; -import brave.propagation.TraceContext; -import brave.test.TestSpanHandler; +import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import org.springframework.amqp.support.AmqpHeaders; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.channel.QueueChannel; -import org.springframework.kafka.support.KafkaHeaders; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; @@ -46,34 +42,24 @@ import org.springframework.messaging.support.ErrorMessage; import org.springframework.messaging.support.ExecutorChannelInterceptor; import org.springframework.messaging.support.ExecutorSubscribableChannel; import org.springframework.messaging.support.MessageBuilder; -import org.springframework.messaging.support.NativeMessageHeaderAccessor; -import static brave.propagation.B3Propagation.Format.SINGLE; -import static brave.propagation.B3SingleFormat.parseB3SingleFormat; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.messaging.support.NativeMessageHeaderAccessor.NATIVE_HEADERS; -public class TracingChannelInterceptorTest { +public abstract class TracingChannelInterceptorTest implements TestTracingAwareSupplier { - StrictCurrentTraceContext currentTraceContext = StrictCurrentTraceContext.create(); + protected ChannelInterceptor interceptor = TracingChannelInterceptor.create(tracerTest().tracing().tracer(), + tracerTest().tracing().propagator(), new SleuthIntegrationMessagingProperties()); - TestSpanHandler spans = new TestSpanHandler(); + protected TestSpanHandler spans = tracerTest().handler(); - Tracing tracing = Tracing.newBuilder().currentTraceContext(this.currentTraceContext) - // SINGLE_NO_PARENT more appropriate for messaging, but we check parent - // hereTraceMessageHeaders - .propagationFactory(B3Propagation.newFactoryBuilder().injectFormat(SINGLE).build()) - .addSpanHandler(this.spans).build(); + protected QueueChannel channel = new QueueChannel(); - ChannelInterceptor interceptor = TracingChannelInterceptor.create(tracing, new SleuthMessagingProperties()); + protected DirectChannel directChannel = new DirectChannel(); - QueueChannel channel = new QueueChannel(); + protected Message message; - DirectChannel directChannel = new DirectChannel(); - - Message message; - - MessageHandler handler = new MessageHandler() { + protected MessageHandler handler = new MessageHandler() { @Override public void handleMessage(Message msg) throws MessagingException { TracingChannelInterceptorTest.this.message = msg; @@ -82,8 +68,7 @@ public class TracingChannelInterceptorTest { @AfterEach public void close() { - this.tracing.close(); - this.currentTraceContext.close(); + tracerTest().close(); } @Test @@ -101,7 +86,7 @@ public class TracingChannelInterceptorTest { this.channel.send(MessageBuilder.withPayload("foo").build()); assertThat(this.channel.receive().getHeaders()).containsKey("b3"); - assertThat(this.spans).hasSize(1).extracting(MutableSpan::kind).containsExactly(Span.Kind.PRODUCER); + assertThat(this.spans).hasSize(1).extracting(FinishedSpan::kind).containsExactly(Span.Kind.PRODUCER); } @Test @@ -112,7 +97,7 @@ public class TracingChannelInterceptorTest { assertThat(this.message).isNotNull(); assertThat(this.message.getHeaders()).containsKeys("b3", "nativeHeaders"); - assertThat(this.spans).extracting(MutableSpan::kind).contains(Span.Kind.CONSUMER, Span.Kind.PRODUCER); + assertThat(this.spans).extracting(FinishedSpan::kind).contains(Span.Kind.CONSUMER, Span.Kind.PRODUCER); } @Test @@ -124,40 +109,6 @@ public class TracingChannelInterceptorTest { assertThat((Map) this.channel.receive().getHeaders().get(NATIVE_HEADERS)).containsOnlyKeys("b3"); } - /** - * If the producer is acting on an un-processed message (ex via a polling consumer), - * it should look at trace headers when there is no span in scope, and use that as the - * parent context. - */ - @Test - public void producerConsidersOldSpanIds() { - this.channel.addInterceptor(producerSideOnly(this.interceptor)); - - this.channel - .send(MessageBuilder.withPayload("foo").setHeader("b3", "000000000000000a-000000000000000b-1").build()); - - TraceContext receiveContext = parseB3SingleFormat(this.channel.receive().getHeaders().get("b3", String.class)) - .context(); - assertThat(receiveContext.parentIdString()).isEqualTo("000000000000000b"); - } - - @Test - public void producerConsidersOldSpanIds_nativeHeaders() { - this.channel.addInterceptor(producerSideOnly(this.interceptor)); - - NativeMessageHeaderAccessor accessor = new NativeMessageHeaderAccessor() { - }; - - accessor.setNativeHeader("b3", "000000000000000a-000000000000000b-1-000000000000000a"); - - this.channel.send(MessageBuilder.withPayload("foo").copyHeaders(accessor.toMessageHeaders()).build()); - - TraceContext receiveContext = parseB3SingleFormat( - ((List) ((Map) this.channel.receive().getHeaders().get(NATIVE_HEADERS)).get("b3")).get(0).toString()) - .context(); - assertThat(receiveContext.parentIdString()).isEqualTo("000000000000000b"); - } - /** * We have to inject headers on a polling receive as any future processor will come * later. @@ -169,7 +120,7 @@ public class TracingChannelInterceptorTest { this.channel.send(MessageBuilder.withPayload("foo").build()); assertThat(this.channel.receive().getHeaders()).containsKeys("b3", "nativeHeaders"); - assertThat(this.spans).hasSize(1).extracting(MutableSpan::kind).containsExactly(Span.Kind.CONSUMER); + assertThat(this.spans).hasSize(1).extracting(FinishedSpan::kind).containsExactly(Span.Kind.CONSUMER); } @Test @@ -191,7 +142,7 @@ public class TracingChannelInterceptorTest { channel.send(MessageBuilder.withPayload("foo").build()); assertThat(messages.get(0).getHeaders()).doesNotContainKeys("b3", "nativeHeaders"); - assertThat(this.spans).extracting(MutableSpan::kind).containsExactly(Span.Kind.CONSUMER, null); + assertThat(this.spans).extracting(FinishedSpan::kind).containsExactly(Span.Kind.CONSUMER, null); } /** @@ -230,7 +181,7 @@ public class TracingChannelInterceptorTest { this.channel.send(MessageBuilder.withPayload("foo").build()); this.channel.receive(); - assertThat(this.spans).extracting(MutableSpan::kind).containsExactlyInAnyOrder(Span.Kind.CONSUMER, + assertThat(this.spans).extracting(FinishedSpan::kind).containsExactlyInAnyOrder(Span.Kind.CONSUMER, Span.Kind.PRODUCER); } @@ -243,7 +194,7 @@ public class TracingChannelInterceptorTest { channel.send(MessageBuilder.withPayload("foo").build()); - assertThat(this.spans).extracting(MutableSpan::kind).containsExactly(Span.Kind.CONSUMER, null, + assertThat(this.spans).extracting(FinishedSpan::kind).containsExactly(Span.Kind.CONSUMER, null, Span.Kind.PRODUCER); } @@ -266,10 +217,12 @@ public class TracingChannelInterceptorTest { assertThat(this.message).isNotNull(); // Parse fails if trace or span ID are missing - TraceContext context = parseB3SingleFormat(this.message.getHeaders().get("b3", String.class)).context(); - - assertThat(context.traceIdString()).isEqualTo("000000000000000a"); - assertThat(context.spanIdString()).isNotEqualTo("000000000000000a"); + String b3 = this.message.getHeaders().get("b3", String.class); + // b3 can be traceid-spanid-sampled(-parentid) + // the latter is not yet supported in otel + B3Context b3Context = new B3Context(b3); + assertThat(b3Context.traceId).endsWith("000000000000000a"); + assertThat(b3Context.spanId).doesNotEndWith("000000000000000a"); assertThat(this.spans).hasSize(2); assertThat(this.message.getHeaders().getReplyChannel()).isSameAs(errorsReplyChannel); assertThat(this.message.getHeaders().getErrorChannel()).isSameAs(errorsReplyChannel); @@ -301,9 +254,12 @@ public class TracingChannelInterceptorTest { this.message = this.channel.receive(); - TraceContext receiveContext = parseB3SingleFormat(this.message.getHeaders().get("b3", String.class)).context(); - assertThat(receiveContext.traceIdString()).isEqualTo("000000000000000a"); - assertThat(receiveContext.spanIdString()).isNotEqualTo("000000000000000a"); + String b3 = this.message.getHeaders().get("b3", String.class); + // b3 can be traceid-spanid-sampled(-parentid) + // the latter is not yet supported in otel + B3Context b3Context = new B3Context(b3); + assertThat(b3Context.traceId).endsWith("000000000000000a"); + assertThat(b3Context.spanId).doesNotEndWith("000000000000000a"); assertThat(this.spans).hasSize(2); } @@ -315,10 +271,10 @@ public class TracingChannelInterceptorTest { channel.subscribe(messages::add); Map headers = new HashMap<>(); - headers.put(KafkaHeaders.MESSAGE_KEY, "hello"); + headers.put("kafka_messageKey", "hello"); channel.send(MessageBuilder.createMessage("foo", new MessageHeaders(headers))); - assertThat(this.spans).extracting(MutableSpan::remoteServiceName).contains("kafka"); + assertThat(this.spans).extracting(FinishedSpan::remoteServiceName).contains("kafka"); } @Test @@ -329,10 +285,10 @@ public class TracingChannelInterceptorTest { channel.subscribe(messages::add); Map headers = new HashMap<>(); - headers.put(AmqpHeaders.RECEIVED_ROUTING_KEY, "hello"); + headers.put("amqp_receivedRoutingKey", "hello"); channel.send(MessageBuilder.createMessage("foo", new MessageHeaders(headers))); - assertThat(this.spans).extracting(MutableSpan::remoteServiceName).contains("rabbitmq"); + assertThat(this.spans).extracting(FinishedSpan::remoteServiceName).contains("rabbitmq"); } @Test @@ -345,10 +301,10 @@ public class TracingChannelInterceptorTest { Map headers = new HashMap<>(); channel.send(MessageBuilder.createMessage("foo", new MessageHeaders(headers))); - assertThat(this.spans).extracting(MutableSpan::remoteServiceName).containsOnly("broker", null); + assertThat(this.spans).extracting(FinishedSpan::remoteServiceName).containsOnly("broker", null); } - ChannelInterceptor producerSideOnly(ChannelInterceptor delegate) { + public ChannelInterceptor producerSideOnly(ChannelInterceptor delegate) { return new ChannelInterceptorAdapter() { @Override public Message preSend(Message message, MessageChannel channel) { @@ -395,3 +351,31 @@ public class TracingChannelInterceptorTest { } } + +class B3Context { + + public String traceId; + + public String spanId; + + public String sampled; + + public String parentSpanId; + + B3Context(String b3Header) { + BDDAssertions.then(b3Header).isNotEmpty(); + String[] split = b3Header.split("-"); + if (split.length == 4) { + this.traceId = split[0]; + this.spanId = split[1]; + this.sampled = split[2]; + this.parentSpanId = split[3]; + } + else { + this.traceId = split[0]; + this.spanId = split[1]; + this.sampled = split[2]; + } + } + +} diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/quartz/TracingJobListenerTest.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/quartz/TracingJobListenerTest.java similarity index 74% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/quartz/TracingJobListenerTest.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/quartz/TracingJobListenerTest.java index 3b8d9f06e..ea76a5854 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/quartz/TracingJobListenerTest.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/quartz/TracingJobListenerTest.java @@ -20,17 +20,11 @@ import java.util.HashMap; import java.util.Properties; import java.util.concurrent.CompletableFuture; -import brave.Tracer.SpanInScope; -import brave.Tracing; -import brave.handler.MutableSpan; -import brave.propagation.Propagation.Setter; -import brave.propagation.StrictCurrentTraceContext; -import brave.test.IntegrationTestSpanHandler; -import org.junit.Rule; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.quartz.Job; +import org.quartz.JobBuilder; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; @@ -40,6 +34,7 @@ import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.Trigger.CompletedExecutionInstruction; +import org.quartz.TriggerBuilder; import org.quartz.TriggerKey; import org.quartz.TriggerListener; import org.quartz.impl.StdSchedulerFactory; @@ -47,9 +42,12 @@ import org.quartz.listeners.JobListenerSupport; import org.quartz.listeners.TriggerListenerSupport; import org.quartz.utils.StringKeyDirtyFlagMap; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; + import static org.assertj.core.api.Assertions.assertThat; -import static org.quartz.JobBuilder.newJob; -import static org.quartz.TriggerBuilder.newTrigger; import static org.springframework.cloud.sleuth.instrument.quartz.TracingJobListener.CONTEXT_SPAN_IN_SCOPE_KEY; import static org.springframework.cloud.sleuth.instrument.quartz.TracingJobListener.CONTEXT_SPAN_KEY; import static org.springframework.cloud.sleuth.instrument.quartz.TracingJobListener.TRIGGER_TAG_KEY; @@ -57,10 +55,7 @@ import static org.springframework.cloud.sleuth.instrument.quartz.TracingJobListe /** * @author Branden Cash */ -public class TracingJobListenerTest { - - @Rule - public IntegrationTestSpanHandler spanHandler = new IntegrationTestSpanHandler(); +public abstract class TracingJobListenerTest implements TestTracingAwareSupplier { private static final JobKey SUCCESSFUL_JOB_KEY = new JobKey("SuccessfulJob"); @@ -74,19 +69,16 @@ public class TracingJobListenerTest { private CompletableFuture completableJob; - private StrictCurrentTraceContext currentTraceContext = StrictCurrentTraceContext.create(); - - private Tracing tracing = Tracing.newBuilder().addSpanHandler(spanHandler).currentTraceContext(currentTraceContext) - .build(); - @BeforeEach public void setUp() throws Exception { - listener = new TracingJobListener(tracing); + listener = new TracingJobListener(tracerTest().tracing().tracer(), tracerTest().tracing().propagator()); completableJob = new CompleteableTriggerListener(); scheduler = createScheduler(getClass().getSimpleName(), 1); - scheduler.addJob(newJob(ExceptionalJob.class).withIdentity(EXCEPTIONAL_JOB_KEY).storeDurably().build(), true); - scheduler.addJob(newJob(SuccessfulJob.class).withIdentity(SUCCESSFUL_JOB_KEY).storeDurably().build(), true); + scheduler.addJob( + JobBuilder.newJob(ExceptionalJob.class).withIdentity(EXCEPTIONAL_JOB_KEY).storeDurably().build(), true); + scheduler.addJob(JobBuilder.newJob(SuccessfulJob.class).withIdentity(SUCCESSFUL_JOB_KEY).storeDurably().build(), + true); scheduler.getListenerManager().addTriggerListener(listener); scheduler.getListenerManager().addJobListener(listener); @@ -98,8 +90,6 @@ public class TracingJobListenerTest { @AfterEach public void tearDown() throws Exception { this.scheduler.shutdown(true); - this.tracing.close(); - this.currentTraceContext.close(); } @Test @@ -114,25 +104,26 @@ public class TracingJobListenerTest { @Test public void should_complete_span_when_job_is_successful() throws Exception { // given - Trigger trigger = newTrigger().forJob(SUCCESSFUL_JOB_KEY).startNow().build(); + Trigger trigger = TriggerBuilder.newTrigger().forJob(SUCCESSFUL_JOB_KEY).startNow().build(); // when runJob(trigger); // expect - spanHandler.takeLocalSpan(); + tracerTest().handler().takeLocalSpan(); } @Test public void should_have_span_with_proper_name_and_tag_when_job_is_successful() throws Exception { // given - Trigger trigger = newTrigger().withIdentity(TRIGGER_KEY).forJob(SUCCESSFUL_JOB_KEY).startNow().build(); + Trigger trigger = TriggerBuilder.newTrigger().withIdentity(TRIGGER_KEY).forJob(SUCCESSFUL_JOB_KEY).startNow() + .build(); // when runJob(trigger); // expect - MutableSpan span = spanHandler.takeLocalSpan(); + FinishedSpan span = tracerTest().handler().takeLocalSpan(); assertThat(span.name()).isEqualToIgnoringCase(SUCCESSFUL_JOB_KEY.toString()); assertThat(span.tags().get(TRIGGER_TAG_KEY)).isEqualToIgnoringCase(TRIGGER_KEY.toString()); } @@ -140,33 +131,33 @@ public class TracingJobListenerTest { @Test public void should_complete_span_when_job_throws_exception() throws Exception { // given - Trigger trigger = newTrigger().forJob(EXCEPTIONAL_JOB_KEY).startNow().build(); + Trigger trigger = TriggerBuilder.newTrigger().forJob(EXCEPTIONAL_JOB_KEY).startNow().build(); // when runJob(trigger); // expect - spanHandler.takeLocalSpan(); + tracerTest().handler().takeLocalSpan(); } @Test public void should_complete_span_when_job_is_vetoed() throws Exception { // given scheduler.getListenerManager().addTriggerListener(new VetoJobTriggerListener()); - Trigger trigger = newTrigger().forJob(SUCCESSFUL_JOB_KEY).startNow().build(); + Trigger trigger = TriggerBuilder.newTrigger().forJob(SUCCESSFUL_JOB_KEY).startNow().build(); // when runJob(trigger); // expect - spanHandler.takeLocalSpan(); + tracerTest().handler().takeLocalSpan(); } @Test public void should_not_complete_span_when_context_is_modified_to_remove_keys() throws Exception { // given scheduler.getListenerManager().addJobListener(new ContextModifyingJobListener()); - Trigger trigger = newTrigger().forJob(EXCEPTIONAL_JOB_KEY).startNow().build(); + Trigger trigger = TriggerBuilder.newTrigger().forJob(EXCEPTIONAL_JOB_KEY).startNow().build(); // when runJob(trigger); @@ -179,16 +170,16 @@ public class TracingJobListenerTest { // given JobDataMap data = new JobDataMap(); addSpanToJobData(data); - Trigger trigger = newTrigger().forJob(SUCCESSFUL_JOB_KEY).usingJobData(data).startNow().build(); + Trigger trigger = TriggerBuilder.newTrigger().forJob(SUCCESSFUL_JOB_KEY).usingJobData(data).startNow().build(); // when runJob(trigger); // expect - MutableSpan parent = spanHandler.takeLocalSpan(); - MutableSpan child = spanHandler.takeLocalSpan(); - assertThat(parent.parentId()).isNull(); - assertThat(child.parentId()).isEqualTo(parent.id()); + FinishedSpan parent = tracerTest().handler().takeLocalSpan(); + FinishedSpan child = tracerTest().handler().takeLocalSpan(); + tracerTest().assertions().assertThatNoParentPresent(parent); + assertThat(child.parentId()).isEqualTo(parent.spanId()); } @Test @@ -197,16 +188,17 @@ public class TracingJobListenerTest { // given JobDataMap data = new JobDataMap(new HashMap()); addSpanToJobData(data); - Trigger trigger = newTrigger().forJob(SUCCESSFUL_JOB_KEY).usingJobData(data).startNow().build(); + Trigger trigger = TriggerBuilder.newTrigger().forJob(SUCCESSFUL_JOB_KEY).usingJobData(data).startNow().build(); // when runJob(trigger); // expect - MutableSpan parent = spanHandler.takeLocalSpan(); - MutableSpan child = spanHandler.takeLocalSpan(); - assertThat(parent.parentId()).isNull(); - assertThat(child.parentId()).isEqualTo(parent.id()); + FinishedSpan parent = tracerTest().handler().takeLocalSpan(); + FinishedSpan child = tracerTest().handler().takeLocalSpan(); + tracerTest().assertions().assertThatNoParentPresent(parent); + assertThat(child).isNotNull(); + assertThat(child.parentId()).isEqualTo(parent.spanId()); } void runJob(Trigger trigger) throws SchedulerException { @@ -224,13 +216,13 @@ public class TracingJobListenerTest { } void addSpanToJobData(JobDataMap data) { - brave.Span span = tracing.tracer().nextSpan().start(); - try (SpanInScope spanInScope = tracing.tracer().withSpanInScope(span)) { - tracing.propagation().injector((Setter) StringKeyDirtyFlagMap::put) - .inject(tracing.currentTraceContext().get(), data); + Span span = tracerTest().tracing().tracer().nextSpan().start(); + try (Tracer.SpanInScope spanInScope = tracerTest().tracing().tracer().withSpan(span)) { + tracerTest().tracing().propagator().inject(tracerTest().tracing().currentTraceContext().get(), data, + StringKeyDirtyFlagMap::put); } finally { - span.finish(); + span.end(); } } diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerParserTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerParserTests.java new file mode 100644 index 000000000..d6727b4c2 --- /dev/null +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerParserTests.java @@ -0,0 +1,159 @@ +/* + * Copyright 2013-2020 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.util.Map; +import java.util.stream.Collectors; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.assertj.core.api.BDDAssertions; +import org.awaitility.Awaitility; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.http.HttpRequestParser; +import org.springframework.cloud.sleuth.api.http.HttpResponseParser; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +import static org.assertj.core.api.BDDAssertions.then; + +@ContextConfiguration(classes = HttpServerParserTests.TestConfiguration.class) +public abstract class HttpServerParserTests { + + @Autowired + TestSpanHandler spans; + + @Autowired + Tracer tracer; + + @LocalServerPort + int port; + + @Autowired + FooController fooController; + + @AfterEach + @BeforeEach + public void close() { + this.spans.clear(); + this.fooController.clear(); + } + + @Test + public void should_set_tags_via_server_parsers() { + BDDAssertions.then(new RestTemplate().getForObject("http://localhost:" + this.port + "/hello", String.class)) + .isEqualTo("hello"); + + Awaitility.await() + .untilAsserted(() -> then(serverSideTags()).containsEntry("ServerRequest", "Tag") + .containsEntry("ServerRequestServlet", "GET").containsEntry("ServerResponse", "Tag") + .containsEntry("ServerResponseServlet", "200")); + } + + @NotNull + protected Map serverSideTags() { + return spans.reportedSpans().stream().filter(f -> f.kind().equals(Span.Kind.SERVER)) + .flatMap(f -> f.tags().entrySet().stream()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration(exclude = JmxAutoConfiguration.class) + @Import(ServerParserConfiguration.class) + public static class TestConfiguration { + + @Bean + FooController fooController() { + return new FooController(); + } + + } + + @RestController + public static class FooController { + + Span span; + + @RequestMapping(value = "/hello", method = RequestMethod.GET) + public String hello() { + return "hello"; + } + + public Span getSpan() { + return this.span; + } + + public void clear() { + this.span = null; + } + + } + + // tag::server_parser_config[] + @Configuration(proxyBeanMethods = false) + public static class ServerParserConfiguration { + + @Bean(name = HttpServerRequestParser.NAME) + HttpRequestParser myHttpRequestParser() { + return (request, context, span) -> { + // Span customization + span.tag("ServerRequest", "Tag"); + Object unwrap = request.unwrap(); + if (unwrap instanceof HttpServletRequest) { + HttpServletRequest req = (HttpServletRequest) unwrap; + // Span customization + span.tag("ServerRequestServlet", req.getMethod()); + } + }; + } + + @Bean(name = HttpServerResponseParser.NAME) + HttpResponseParser myHttpResponseParser() { + return (response, context, span) -> { + // Span customization + span.tag("ServerResponse", "Tag"); + Object unwrap = response.unwrap(); + if (unwrap instanceof HttpServletResponse) { + HttpServletResponse resp = (HttpServletResponse) unwrap; + // Span customization + span.tag("ServerResponseServlet", String.valueOf(resp.getStatus())); + } + }; + } + + } + // end::server_parser_config[] + +} diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/IgnoreAutoConfiguredSkipPatternsIntegrationTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/IgnoreAutoConfiguredSkipPatternsIntegrationTests.java similarity index 64% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/IgnoreAutoConfiguredSkipPatternsIntegrationTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/IgnoreAutoConfiguredSkipPatternsIntegrationTests.java index 833d5b6f4..841cb05b8 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/IgnoreAutoConfiguredSkipPatternsIntegrationTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/IgnoreAutoConfiguredSkipPatternsIntegrationTests.java @@ -16,33 +16,28 @@ package org.springframework.cloud.sleuth.instrument.web; -import brave.Tracer; -import brave.handler.SpanHandler; -import brave.sampler.Sampler; -import brave.test.TestSpanHandler; +import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.server.LocalServerPort; -import org.springframework.cloud.sleuth.DisableSecurity; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.test.TestSpanHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; -import static org.assertj.core.api.BDDAssertions.then; - -@SpringBootTest(classes = IgnoreAutoConfiguredSkipPatternsIntegrationTests.Config.class, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, - properties = { "management.endpoints.web.exposure.include:*", "server.servlet.context-path:/context-path", - "spring.sleuth.web.ignoreAutoConfiguredSkipPatterns:true" }) -public class IgnoreAutoConfiguredSkipPatternsIntegrationTests { +@ContextConfiguration(classes = IgnoreAutoConfiguredSkipPatternsIntegrationTests.TestConfig.class) +@TestPropertySource(properties = { "management.endpoints.web.exposure.include:*", + "server.servlet.context-path:/context-path", "spring.sleuth.web.ignoreAutoConfiguredSkipPatterns:true" }) +public abstract class IgnoreAutoConfiguredSkipPatternsIntegrationTests { @Autowired TestSpanHandler spans; @@ -64,31 +59,39 @@ public class IgnoreAutoConfiguredSkipPatternsIntegrationTests { new RestTemplate().getForObject("http://localhost:" + this.port + "/context-path/actuator/health", String.class); - then(this.tracer.currentSpan()).isNull(); - then(this.spans).hasSize(1); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); } @Test public void should_sample_non_actuator_endpoint_when_override_pattern_is_true() { new RestTemplate().getForObject("http://localhost:" + this.port + "/context-path/something", String.class); - then(this.tracer.currentSpan()).isNull(); - then(this.spans).hasSize(1); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); } @Test public void should_not_sample_default_skip_patterns_when_override_pattern_is_true() { new RestTemplate().getForObject("http://localhost:" + this.port + "/context-path/index.html", String.class); - then(this.tracer.currentSpan()).isNull(); - then(this.spans).hasSize(0); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(0); + } + + @EnableAutoConfiguration + @Configuration(proxyBeanMethods = false) + public static class TestConfig { + + @Bean + TestRestController testRestController() { + return new TestRestController(); + } + } - @EnableAutoConfiguration(exclude = RabbitAutoConfiguration.class) - @Configuration - @DisableSecurity @RestController - public static class Config { + public static class TestRestController { @GetMapping("something") void doNothing() { @@ -98,16 +101,6 @@ public class IgnoreAutoConfiguredSkipPatternsIntegrationTests { void html() { } - @Bean - SpanHandler testSpanHandler() { - return new TestSpanHandler(); - } - - @Bean - Sampler sampler() { - return Sampler.ALWAYS_SAMPLE; - } - } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithBasePath.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithBasePath.java similarity index 71% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithBasePath.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithBasePath.java index c3a9aad9e..2dc7e384c 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithBasePath.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithBasePath.java @@ -16,32 +16,28 @@ package org.springframework.cloud.sleuth.instrument.web; -import brave.Tracer; -import brave.handler.SpanHandler; -import brave.sampler.Sampler; -import brave.test.TestSpanHandler; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.server.LocalServerPort; -import org.springframework.cloud.sleuth.DisableSecurity; -import org.springframework.context.annotation.Bean; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.test.TestSpanHandler; import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import static org.assertj.core.api.BDDAssertions.then; -@SpringBootTest(classes = SkipEndPointsIntegrationTestsWithContextPathWithBasePath.Config.class, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, +@ContextConfiguration(classes = SkipEndPointsIntegrationTestsWithContextPathWithBasePath.TestConfig.class) +@TestPropertySource( properties = { "management.endpoints.web.exposure.include:*", "server.servlet.context-path:/context-path" }) -public class SkipEndPointsIntegrationTestsWithContextPathWithBasePath { +public abstract class SkipEndPointsIntegrationTestsWithContextPathWithBasePath { @Autowired TestSpanHandler spans; @@ -75,26 +71,15 @@ public class SkipEndPointsIntegrationTestsWithContextPathWithBasePath { then(this.spans).hasSize(1); } - @EnableAutoConfiguration(exclude = RabbitAutoConfiguration.class) - @Configuration - @DisableSecurity + @EnableAutoConfiguration + @Configuration(proxyBeanMethods = false) @RestController - public static class Config { + public static class TestConfig { @GetMapping("something") void doNothing() { } - @Bean - SpanHandler testSpanHandler() { - return new TestSpanHandler(); - } - - @Bean - Sampler sampler() { - return Sampler.ALWAYS_SAMPLE; - } - } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath.java similarity index 72% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath.java index a155d2127..67ea27728 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath.java @@ -16,33 +16,28 @@ package org.springframework.cloud.sleuth.instrument.web; -import brave.Tracer; -import brave.handler.SpanHandler; -import brave.sampler.Sampler; -import brave.test.TestSpanHandler; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.server.LocalServerPort; -import org.springframework.cloud.sleuth.DisableSecurity; -import org.springframework.context.annotation.Bean; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.test.TestSpanHandler; import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import static org.assertj.core.api.BDDAssertions.then; -@SpringBootTest(classes = SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath.Config.class, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, - properties = { "management.endpoints.web.exposure.include:*", "server.servlet.context-path:/context-path", - "management.endpoints.web.base-path:/" }) -public class SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath { +@ContextConfiguration(classes = SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath.TestConfig.class) +@TestPropertySource(properties = { "management.endpoints.web.exposure.include:*", + "server.servlet.context-path:/context-path", "management.endpoints.web.base-path:/" }) +public abstract class SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath { @LocalServerPort int port; @@ -91,11 +86,10 @@ public class SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath { then(this.spans).hasSize(0); } - @EnableAutoConfiguration(exclude = RabbitAutoConfiguration.class) - @Configuration - @DisableSecurity + @EnableAutoConfiguration + @Configuration(proxyBeanMethods = false) @RestController - public static class Config { + public static class TestConfig { @GetMapping("something") void doNothing() { @@ -109,16 +103,6 @@ public class SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath { void metrics() { } - @Bean - SpanHandler testSpanHandler() { - return new TestSpanHandler(); - } - - @Bean - Sampler sampler() { - return Sampler.ALWAYS_SAMPLE; - } - } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath.java similarity index 75% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath.java index 85977c14e..9ef0dc75b 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath.java @@ -16,32 +16,29 @@ package org.springframework.cloud.sleuth.instrument.web; -import brave.Tracer; -import brave.handler.SpanHandler; -import brave.sampler.Sampler; -import brave.test.TestSpanHandler; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.server.LocalServerPort; -import org.springframework.cloud.sleuth.DisableSecurity; -import org.springframework.context.annotation.Bean; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.test.TestSpanHandler; import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import static org.assertj.core.api.BDDAssertions.then; -@SpringBootTest(classes = SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath.Config.class, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, - properties = { "management.endpoints.web.exposure.include:*" }) -public class SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath { +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath.TestConfig.class) +@TestPropertySource(properties = { "management.endpoints.web.exposure.include:*" }) +public abstract class SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath { @LocalServerPort int port; @@ -90,11 +87,10 @@ public class SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath { then(this.spans).hasSize(0); } - @EnableAutoConfiguration(exclude = RabbitAutoConfiguration.class) - @Configuration - @DisableSecurity + @EnableAutoConfiguration + @Configuration(proxyBeanMethods = false) @RestController - public static class Config { + public static class TestConfig { @GetMapping("something") void doNothing() { @@ -108,16 +104,6 @@ public class SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath { void metrics() { } - @Bean - SpanHandler testSpanHandler() { - return new TestSpanHandler(); - } - - @Bean - Sampler sampler() { - return Sampler.ALWAYS_SAMPLE; - } - } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath.java similarity index 77% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath.java index 7c2c365e4..6bde36a64 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath.java @@ -16,32 +16,30 @@ package org.springframework.cloud.sleuth.instrument.web; -import brave.Tracer; -import brave.handler.SpanHandler; -import brave.sampler.Sampler; -import brave.test.TestSpanHandler; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.server.LocalServerPort; -import org.springframework.cloud.sleuth.DisableSecurity; -import org.springframework.context.annotation.Bean; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.test.TestSpanHandler; import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import static org.assertj.core.api.BDDAssertions.then; -@SpringBootTest(classes = SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath.Config.class, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath.TestConfig.class) +@TestPropertySource( properties = { "management.endpoints.web.exposure.include:*", "management.endpoints.web.base-path:/" }) -public class SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath { +public abstract class SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath { @LocalServerPort int port; @@ -90,11 +88,10 @@ public class SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath { then(this.spans).hasSize(0); } - @EnableAutoConfiguration(exclude = RabbitAutoConfiguration.class) - @Configuration - @DisableSecurity + @EnableAutoConfiguration + @Configuration(proxyBeanMethods = false) @RestController - public static class Config { + public static class TestConfig { @GetMapping("something") void doNothing() { @@ -108,16 +105,6 @@ public class SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath { void metrics() { } - @Bean - SpanHandler testSpanHandler() { - return new TestSpanHandler(); - } - - @Bean - Sampler sampler() { - return Sampler.ALWAYS_SAMPLE; - } - } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java similarity index 54% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java index 0dd076fc2..443681cda 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java @@ -17,25 +17,22 @@ package org.springframework.cloud.sleuth.instrument.web; import java.util.concurrent.atomic.AtomicReference; -import java.util.regex.Pattern; import javax.servlet.Filter; -import brave.Span; -import brave.Tracer; -import brave.Tracing; -import brave.http.HttpClientParser; -import brave.http.HttpServerParser; -import brave.http.HttpTracing; -import brave.propagation.StrictScopeDecorator; -import brave.propagation.ThreadLocalCurrentTraceContext; -import brave.sampler.Sampler; -import brave.servlet.TracingFilter; -import brave.test.TestSpanHandler; +import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.http.HttpServerHandler; +import org.springframework.cloud.sleuth.instrument.web.servlet.TracingFilter; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; +import org.springframework.cloud.sleuth.test.TracerAware; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -52,27 +49,22 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder /** * @author Spencer Gibb */ -public class TraceFilterTests { +public abstract class TraceFilterTests implements TestTracingAwareSupplier { - TestSpanHandler spans = new TestSpanHandler(); + protected Tracer tracer = tracerTest().tracing().tracer(); - Tracing tracing = Tracing.newBuilder().currentTraceContext( - ThreadLocalCurrentTraceContext.newBuilder().addScopeDecorator(StrictScopeDecorator.create()).build()) - .addSpanHandler(this.spans).build(); + protected CurrentTraceContext currentTraceContext = tracerTest().tracing().currentTraceContext(); - Tracer tracer = this.tracing.tracer(); + protected Filter filter = TracingFilter.create(this.currentTraceContext, + tracerTest().tracing().httpServerHandler()); - HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing).clientParser(new HttpClientParser()) - .serverParser(new HttpServerParser()) - .serverSampler(new SkipPatternHttpServerSampler(() -> Pattern.compile(""))).build(); + protected TestSpanHandler spans = tracerTest().handler(); - Filter filter = TracingFilter.create(this.httpTracing); + protected MockHttpServletRequest request; - MockHttpServletRequest request; + protected MockHttpServletResponse response; - MockHttpServletResponse response; - - MockFilterChain filterChain; + protected MockFilterChain filterChain; @BeforeEach public void init() { @@ -88,7 +80,7 @@ public class TraceFilterTests { @AfterEach public void cleanup() { - Tracing.current().close(); + tracerTest().close(); } @Test @@ -97,56 +89,26 @@ public class TraceFilterTests { neverSampleFilter().doFilter(this.request, this.response, this.filterChain); - then(Tracing.current().tracer().currentSpan()).isNull(); - then(this.spans).isEmpty(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).isEmpty(); } - private Filter neverSampleFilter() { - Tracing tracing = Tracing.newBuilder() - .currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder() - .addScopeDecorator(StrictScopeDecorator.create()).build()) - .addSpanHandler(this.spans).sampler(Sampler.NEVER_SAMPLE).supportsJoin(false).build(); - HttpTracing httpTracing = HttpTracing.newBuilder(tracing).clientParser(new HttpClientParser()) - .serverParser(new HttpServerParser()) - .serverSampler(new SkipPatternHttpServerSampler(() -> Pattern.compile(""))).build(); - return TracingFilter.create(httpTracing); + protected Filter neverSampleFilter() { + return TracingFilter.create(tracerTest().tracing().currentTraceContext(), + tracerTest().tracing().sampler(TracerAware.TraceSampler.OFF).httpServerHandler()); } @Test public void startsNewTrace() throws Exception { this.filter.doFilter(this.request, this.response, this.filterChain); - then(this.spans).hasSize(1); - then(this.spans.get(0).tags()).containsEntry("http.path", "/").containsEntry("http.method", + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("http.path", "/").containsEntry("http.method", HttpMethod.GET.toString()); // we don't check for status_code anymore cause Brave doesn't support it oob // .containsEntry("http.status_code", "200") } - @Test - public void shouldNotStoreHttpStatusCodeWhenResponseCodeHasNotYetBeenSet() throws Exception { - this.response.setStatus(0); - this.filter.doFilter(this.request, this.response, this.filterChain); - - then(Tracing.current().tracer().currentSpan()).isNull(); - then(this.spans).hasSize(1); - then(this.spans.get(0).tags()).doesNotContainKey("http.status_code"); - } - - @Test - public void startsNewTraceWithParentIdInHeaders() throws Exception { - this.request = builder().header("b3", "0000000000000002-0000000000000003-1-000000000000000a") - .buildRequest(new MockServletContext()); - - this.filter.doFilter(this.request, this.response, this.filterChain); - - then(Tracing.current().tracer().currentSpan()).isNull(); - then(this.spans).hasSize(1); - then(this.spans.get(0).id()).isEqualTo("0000000000000003"); - then(this.spans.get(0).tags()).containsEntry("http.path", "/").containsEntry("http.method", - HttpMethod.GET.toString()); - } - @Test public void continuesATraceWhenSpanNotSampled() throws Exception { AtomicReference span = new AtomicReference<>(); @@ -155,11 +117,12 @@ public class TraceFilterTests { this.filter.doFilter(this.request, this.response, (req, resp) -> { this.filterChain.doFilter(req, resp); - span.set(this.tracing.tracer().currentSpan()); + span.set(tracerTest().tracing().tracer().currentSpan()); }); - then(Tracing.current().tracer().currentSpan()).isNull(); - then(span.get().context().traceIdString()).isEqualTo("0000000000000014"); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(span.get().context().traceId()) + .isEqualTo(tracerTest().assertions().or128Bit("0000000000000014")); } @Test @@ -168,7 +131,7 @@ public class TraceFilterTests { this.filter.doFilter(this.request, this.response, this.filterChain); - then(Tracing.current().tracer().currentSpan()).isNull(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } @Test @@ -178,8 +141,8 @@ public class TraceFilterTests { this.filter.doFilter(this.request, this.response, this.filterChain); - then(Tracing.current().tracer().currentSpan()).isNull(); - then(this.spans).hasSize(1); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); } @Test @@ -187,7 +150,7 @@ public class TraceFilterTests { Span span = this.tracer.nextSpan().name("http:foo"); this.response.setStatus(404); - then(Tracing.current().tracer().currentSpan()).isNull(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); this.filter.doFilter(this.request, this.response, this.filterChain); } @@ -198,26 +161,11 @@ public class TraceFilterTests { this.filter.doFilter(this.request, this.response, this.filterChain); - then(Tracing.current().tracer().currentSpan()).isNull(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); verifyParentSpanHttpTags(); } - @Test - public void createsChildFromHeadersWhenJoinUnsupported() throws Exception { - Tracing tracing = Tracing.newBuilder() - .currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder() - .addScopeDecorator(StrictScopeDecorator.create()).build()) - .addSpanHandler(this.spans).supportsJoin(false).build(); - HttpTracing httpTracing = HttpTracing.create(tracing); - this.request = builder().header("b3", "0000000000000014-000000000000000a") - .buildRequest(new MockServletContext()); - - TracingFilter.create(httpTracing).doFilter(this.request, this.response, this.filterChain); - - then(Tracing.current().tracer().currentSpan()).isNull(); - then(this.spans).hasSize(1); - then(this.spans.get(0).parentId()).isEqualTo("000000000000000a"); - } + public abstract HttpServerHandler httpServerHandler(); @Test public void shouldAnnotateSpanWithErrorWhenExceptionIsThrown() throws Exception { @@ -238,10 +186,10 @@ public class TraceFilterTests { assertThat(e.getMessage()).isEqualTo("Planned"); } - then(Tracing.current().tracer().currentSpan()).isNull(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); verifyParentSpanHttpTags(); - then(this.spans).hasSize(1); - then(this.spans.get(0).tags()).containsEntry("error", "Planned"); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).error()).hasMessageContaining("Planned"); } @Test @@ -250,7 +198,7 @@ public class TraceFilterTests { this.response.setStatus(404); - then(Tracing.current().tracer().currentSpan()).isNull(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); this.filter.doFilter(this.request, this.response, this.filterChain); } @@ -262,8 +210,8 @@ public class TraceFilterTests { this.filter.doFilter(this.request, this.response, this.filterChain); - then(Tracing.current().tracer().currentSpan()).isNull(); - then(this.spans).hasSize(1); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); } @Test @@ -274,8 +222,8 @@ public class TraceFilterTests { this.filter.doFilter(this.request, this.response, this.filterChain); - then(Tracing.current().tracer().currentSpan()).isNull(); - then(this.spans).hasSize(1); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); } @Test @@ -284,8 +232,8 @@ public class TraceFilterTests { this.filter.doFilter(this.request, this.response, this.filterChain); - then(Tracing.current().tracer().currentSpan()).isNull(); - then(this.spans).isNotEmpty(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).isNotEmpty(); then(this.response.getStatus()).isEqualTo(HttpStatus.OK.value()); } @@ -295,21 +243,11 @@ public class TraceFilterTests { this.filter.doFilter(this.request, this.response, this.filterChain); - then(Tracing.current().tracer().currentSpan()).isNull(); - then(this.spans).isNotEmpty(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).isNotEmpty(); then(this.response.getStatus()).isEqualTo(HttpStatus.OK.value()); } - @Test - public void samplesASpanRegardlessOfTheSamplerWhenDebugIsPresent() throws Exception { - this.request = builder().header("b3", "d").buildRequest(new MockServletContext()); - - neverSampleFilter().doFilter(this.request, this.response, this.filterChain); - - then(Tracing.current().tracer().currentSpan()).isNull(); - then(this.spans).isNotEmpty(); - } - @SuppressWarnings("Duplicates") @Test public void usesSamplingMechanismWhenIncomingTraceIsMalformed() throws Exception { @@ -317,8 +255,8 @@ public class TraceFilterTests { neverSampleFilter().doFilter(this.request, this.response, this.filterChain); - then(Tracing.current().tracer().currentSpan()).isNull(); - then(this.spans).isEmpty(); + BDDAssertions.then(tracerTest().tracing().tracer().currentSpan()).isNull(); + BDDAssertions.then(tracerTest().handler()).isEmpty(); } // #668 @@ -329,28 +267,17 @@ public class TraceFilterTests { this.filter.doFilter(this.request, this.response, this.filterChain); - then(Tracing.current().tracer().currentSpan()).isNull(); - then(this.spans).hasSize(1); - then(this.spans.get(0).tags()).containsEntry("http.path", "/").containsEntry("http.method", + BDDAssertions.then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.spans).hasSize(1); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("http.path", "/").containsEntry("http.method", HttpMethod.GET.toString()); // we don't check for status_code anymore cause Brave doesn't support it oob // .containsEntry("http.status_code", "295") } - @Test - public void samplesASpanDebugFlagWithInterceptor() throws Exception { - this.request = builder().header("b3", "d").buildRequest(new MockServletContext()); - - neverSampleFilter().doFilter(this.request, this.response, this.filterChain); - - then(Tracing.current().tracer().currentSpan()).isNull(); - then(this.spans).hasSize(1); - then(this.spans.get(0).name()).isEqualTo("GET"); - } - public void verifyParentSpanHttpTags() { - then(this.spans).isNotEmpty(); - then(this.spans.get(0).tags()).containsEntry("http.path", "/").containsEntry("http.method", + BDDAssertions.then(this.spans).isNotEmpty(); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("http.path", "/").containsEntry("http.method", HttpMethod.GET.toString()); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/HttpClientBeanPostProcessorTest.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/HttpClientBeanPostProcessorTest.java similarity index 80% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/HttpClientBeanPostProcessorTest.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/HttpClientBeanPostProcessorTest.java index 80cff31a9..5dbd927aa 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/HttpClientBeanPostProcessorTest.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/HttpClientBeanPostProcessorTest.java @@ -18,8 +18,8 @@ package org.springframework.cloud.sleuth.instrument.web.client; import java.util.function.BiConsumer; -import brave.propagation.TraceContext; import io.netty.bootstrap.Bootstrap; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -31,13 +31,12 @@ import reactor.core.publisher.SynchronousSink; import reactor.core.scheduler.Schedulers; import reactor.netty.Connection; +import org.springframework.cloud.sleuth.api.TraceContext; import org.springframework.cloud.sleuth.instrument.web.client.HttpClientBeanPostProcessor.PendingSpan; import org.springframework.cloud.sleuth.instrument.web.client.HttpClientBeanPostProcessor.TracingMapConnect; -import static org.assertj.core.api.Assertions.assertThat; - @ExtendWith(MockitoExtension.class) -public class HttpClientBeanPostProcessorTest { +public abstract class HttpClientBeanPostProcessorTest { @Mock Connection connection; @@ -45,7 +44,9 @@ public class HttpClientBeanPostProcessorTest { @Mock Bootstrap bootstrap; - TraceContext traceContext = TraceContext.newBuilder().traceId(1).spanId(2).sampled(true).build(); + TraceContext traceContext = traceContext(); + + public abstract TraceContext traceContext(); @BeforeEach public void setup() { @@ -62,8 +63,8 @@ public class HttpClientBeanPostProcessorTest { .handle(new BiConsumer>() { @Override public void accept(Connection t, SynchronousSink ctx) { - assertThat(ctx.currentContext().get(TraceContext.class)).isSameAs(traceContext); - assertThat(ctx.currentContext().get(PendingSpan.class)).isNotNull(); + Assertions.assertThat(ctx.currentContext().get(TraceContext.class)).isSameAs(traceContext); + Assertions.assertThat(ctx.currentContext().get(PendingSpan.class)).isNotNull(); } }); @@ -79,8 +80,8 @@ public class HttpClientBeanPostProcessorTest { .handle(new BiConsumer>() { @Override public void accept(Connection t, SynchronousSink ctx) { - assertThat(ctx.currentContext().getOrEmpty(TraceContext.class)).isEmpty(); - assertThat(ctx.currentContext().get(PendingSpan.class)).isNotNull(); + Assertions.assertThat(ctx.currentContext().getOrEmpty(TraceContext.class)).isEmpty(); + Assertions.assertThat(ctx.currentContext().get(PendingSpan.class)).isNotNull(); } }); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/MultipleAsyncRestTemplateTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/MultipleAsyncRestTemplateTests.java similarity index 83% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/MultipleAsyncRestTemplateTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/MultipleAsyncRestTemplateTests.java index d60ab0c56..7da689f84 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/MultipleAsyncRestTemplateTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/MultipleAsyncRestTemplateTests.java @@ -22,11 +22,9 @@ import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import brave.Span; -import brave.Tracer; -import brave.sampler.Sampler; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.assertj.core.api.BDDAssertions; import org.awaitility.Awaitility; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -36,8 +34,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; import org.springframework.cloud.sleuth.instrument.async.LazyTraceExecutor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -54,6 +53,7 @@ import org.springframework.scheduling.annotation.AsyncConfigurerSupport; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.AsyncRestTemplate; @@ -63,13 +63,11 @@ import static org.assertj.core.api.BDDAssertions.then; /** * @author Marcin Grzejszczak */ -@SpringBootTest( - classes = { MultipleAsyncRestTemplateTests.Config.class, - MultipleAsyncRestTemplateTests.CustomExecutorConfig.class, - MultipleAsyncRestTemplateTests.ControllerConfig.class }, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = { MultipleAsyncRestTemplateTests.TestConfig.class, + MultipleAsyncRestTemplateTests.CustomExecutorConfig.class, + MultipleAsyncRestTemplateTests.ControllerConfig.class }) @DirtiesContext -public class MultipleAsyncRestTemplateTests { +public abstract class MultipleAsyncRestTemplateTests { private static final Log log = LogFactory.getLog(MultipleAsyncRestTemplateTests.class); @@ -101,16 +99,16 @@ public class MultipleAsyncRestTemplateTests { @Test public void should_pass_tracing_context_with_custom_async_client() throws Exception { Span span = this.tracer.nextSpan().name("foo"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { String result = this.asyncRestTemplate.getForEntity("http://localhost:" + this.port + "/foo", String.class) .get().getBody(); - then(span.context().traceIdString()).isEqualTo(result); + BDDAssertions.then(span.context().traceId()).isEqualTo(result); } finally { - span.finish(); + span.end(); } - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } @Test @@ -118,20 +116,20 @@ public class MultipleAsyncRestTemplateTests { then(this.executor).isNotNull(); then(this.wrappedExecutor).isInstanceOf(LazyTraceExecutor.class); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } @Test public void should_inject_traced_executor_that_passes_tracing_context() throws Exception { Span span = this.tracer.nextSpan().name("foo"); AtomicBoolean executed = new AtomicBoolean(false); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { this.wrappedExecutor.execute(() -> { Span currentSpan = this.tracer.currentSpan(); log.info("Current span " + currentSpan); - then(currentSpan).isNotNull(); - long currentTraceId = currentSpan.context().traceId(); - long initialTraceId = span.context().traceId(); + BDDAssertions.then(currentSpan).isNotNull(); + String currentTraceId = currentSpan.context().traceId(); + String initialTraceId = span.context().traceId(); log.info("Hello from runnable before trace id check. Initial [" + initialTraceId + "] current [" + currentTraceId + "]"); then(currentTraceId).isEqualTo(initialTraceId); @@ -140,19 +138,19 @@ public class MultipleAsyncRestTemplateTests { }); } finally { - span.finish(); + span.end(); } Awaitility.await().atMost(10L, TimeUnit.SECONDS).untilAsserted(() -> { then(executed.get()).isTrue(); }); - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } - // tag::custom_async_rest_template[] - @Configuration @EnableAutoConfiguration - static class Config { + // tag::custom_async_rest_template[] + @Configuration(proxyBeanMethods = false) + public static class TestConfig { @Bean(name = "customAsyncRestTemplate") public AsyncRestTemplate traceAsyncRestTemplate() { @@ -175,12 +173,12 @@ public class MultipleAsyncRestTemplateTests { // end::custom_async_rest_template[] // tag::custom_executor[] - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration @EnableAsync // add the infrastructure role to ensure that the bean gets auto-proxied @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - static class CustomExecutorConfig extends AsyncConfigurerSupport { + public static class CustomExecutorConfig extends AsyncConfigurerSupport { @Autowired BeanFactory beanFactory; @@ -201,19 +199,14 @@ public class MultipleAsyncRestTemplateTests { } // end::custom_executor[] - @Configuration - static class ControllerConfig { + @Configuration(proxyBeanMethods = false) + public static class ControllerConfig { @Bean MyRestController myRestController(Tracer tracer) { return new MyRestController(tracer); } - @Bean - Sampler sampler() { - return Sampler.ALWAYS_SAMPLE; - } - } } @@ -255,7 +248,7 @@ class MyRestController { @RequestMapping("/foo") String foo() { - return this.tracer.currentSpan().context().traceIdString(); + return this.tracer.currentSpan().context().traceId(); } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/ReactorNettyHttpClientSpringBootTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/ReactorNettyHttpClientSpringBootTests.java similarity index 64% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/ReactorNettyHttpClientSpringBootTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/ReactorNettyHttpClientSpringBootTests.java index 7d7f158fb..e1881a933 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/ReactorNettyHttpClientSpringBootTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/ReactorNettyHttpClientSpringBootTests.java @@ -16,17 +16,8 @@ package org.springframework.cloud.sleuth.instrument.web.client; -import brave.handler.MutableSpan; -import brave.handler.SpanHandler; -import brave.propagation.B3SinglePropagation; -import brave.propagation.CurrentTraceContext; -import brave.propagation.CurrentTraceContext.Scope; -import brave.propagation.Propagation; -import brave.propagation.TraceContext; -import brave.sampler.Sampler; -import brave.test.IntegrationTestSpanHandler; import io.netty.handler.codec.http.HttpResponseStatus; -import org.junit.ClassRule; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import reactor.core.publisher.Flux; @@ -39,15 +30,16 @@ import reactor.netty.http.server.HttpServer; 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.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.test.TestSpanHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; import org.springframework.web.reactive.function.client.WebClient; -import static brave.Span.Kind.CLIENT; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - /** * This tests {@link HttpClient} instrumentation performed by * {@link HttpClientBeanPostProcessor}, as wired by auto-configuration. @@ -57,12 +49,8 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * care should be taken to also test that integration. For example, it would be easy to * create duplicate client spans for the same request. */ -@SpringBootTest(classes = ReactorNettyHttpClientSpringBootTests.TestConfiguration.class, - webEnvironment = SpringBootTest.WebEnvironment.NONE) -public class ReactorNettyHttpClientSpringBootTests { - - @ClassRule - public static IntegrationTestSpanHandler spanHandler = new IntegrationTestSpanHandler(); +@ContextConfiguration(classes = ReactorNettyHttpClientSpringBootTests.TestConfiguration.class) +public abstract class ReactorNettyHttpClientSpringBootTests { DisposableServer disposableServer; @@ -72,27 +60,33 @@ public class ReactorNettyHttpClientSpringBootTests { @Autowired CurrentTraceContext currentTraceContext; - TraceContext context = TraceContext.newBuilder().traceId(1).spanId(1).sampled(true).build(); + @Autowired + TestSpanHandler handler; + + TraceContext parent = traceContext(); @AfterEach public void tearDown() { if (disposableServer != null) { disposableServer.disposeNow(); } + this.handler.clear(); } + public abstract TraceContext traceContext(); + @Test public void shouldRecordRemoteEndpoint() throws Exception { disposableServer = HttpServer.create().port(0).handle((in, out) -> out.sendString(Flux.just("foo"))).bindNow(); HttpClientResponse response = httpClient.port(disposableServer.port()).get().uri("/").response().block(); - assertThat(response.status()).isEqualTo(HttpResponseStatus.OK); + Assertions.assertThat(response.status()).isEqualTo(HttpResponseStatus.OK); - MutableSpan clientSpan = spanHandler.takeRemoteSpan(CLIENT); + FinishedSpan clientSpan = this.handler.takeRemoteSpan(Span.Kind.CLIENT); - assertThat(clientSpan.remoteIp()).isNotNull(); - assertThat(clientSpan.remotePort()).isNotZero(); + Assertions.assertThat(clientSpan.remoteIp()).isNotNull(); + Assertions.assertThat(clientSpan.remotePort()).isNotZero(); } @Test @@ -102,15 +96,17 @@ public class ReactorNettyHttpClientSpringBootTests { .handle((in, out) -> out.sendString(Flux.just(in.requestHeaders().get("b3")))).bindNow(); String b3SingleHeaderReadByServer; - try (Scope ws = currentTraceContext.newScope(context)) { + try (CurrentTraceContext.Scope ws = currentTraceContext.newScope(parent)) { b3SingleHeaderReadByServer = httpClient.port(disposableServer.port()).get().uri("/").responseContent() .aggregate().asString().block(); } - MutableSpan clientSpan = spanHandler.takeRemoteSpan(CLIENT); + FinishedSpan clientSpan = this.handler.takeRemoteSpan(Span.Kind.CLIENT); + assertSingleB3Header(b3SingleHeaderReadByServer, clientSpan, parent); + } - assertThat(b3SingleHeaderReadByServer) - .isEqualTo(context.traceIdString() + "-" + clientSpan.id() + "-1-" + context.spanIdString()); + public void assertSingleB3Header(String b3SingleHeaderReadByServer, FinishedSpan clientSpan, TraceContext parent) { + throw new UnsupportedOperationException("Implement this assertion"); } @Test @@ -124,9 +120,10 @@ public class ReactorNettyHttpClientSpringBootTests { String b3SingleHeaderReadByServer = request.block(); - MutableSpan clientSpan = spanHandler.takeRemoteSpan(CLIENT); + FinishedSpan clientSpan = this.handler.takeRemoteSpan(Span.Kind.CLIENT); - assertThat(b3SingleHeaderReadByServer).isEqualTo(clientSpan.traceId() + "-" + clientSpan.id() + "-1"); + Assertions.assertThat(b3SingleHeaderReadByServer) + .isEqualTo(clientSpan.traceId() + "-" + clientSpan.spanId() + "-1"); } @Test @@ -138,29 +135,14 @@ public class ReactorNettyHttpClientSpringBootTests { Mono request = httpClient.port(disposableServer.port()).get().uri("/").responseContent().aggregate() .asString(); - assertThatThrownBy(request::block).hasCauseInstanceOf(PrematureCloseException.class); + Assertions.assertThatThrownBy(request::block).hasCauseInstanceOf(PrematureCloseException.class); - spanHandler.takeRemoteSpanWithError(CLIENT); + this.handler.takeRemoteSpanWithError(Span.Kind.CLIENT); } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration - static class TestConfiguration { - - @Bean - Propagation.Factory propagationFactory() { - return B3SinglePropagation.FACTORY; - } - - @Bean - Sampler sampler() { - return Sampler.ALWAYS_SAMPLE; - } - - @Bean - SpanHandler testSpanHandler() { - return spanHandler; - } + public static class TestConfiguration { @Bean HttpClient reactorHttpClient() { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRequestHttpHeadersFilterTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRequestHttpHeadersFilterTests.java similarity index 81% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRequestHttpHeadersFilterTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRequestHttpHeadersFilterTests.java index 692b09683..d4dede6e6 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRequestHttpHeadersFilterTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRequestHttpHeadersFilterTests.java @@ -18,41 +18,24 @@ package org.springframework.cloud.sleuth.instrument.web.client; import java.util.Arrays; import java.util.Collections; +import java.util.List; -import brave.Tracing; -import brave.http.HttpTracing; -import brave.propagation.StrictCurrentTraceContext; -import brave.test.TestSpanHandler; import org.assertj.core.api.BDDAssertions; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter; +import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; import org.springframework.http.HttpHeaders; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.web.server.MockServerWebExchange; // This test uses B3 multi format as it is the default for client propagation -public class TraceRequestHttpHeadersFilterTests { - - StrictCurrentTraceContext currentTraceContext = StrictCurrentTraceContext.create(); - - TestSpanHandler spans = new TestSpanHandler(); - - Tracing tracing = Tracing.newBuilder().currentTraceContext(this.currentTraceContext).addSpanHandler(this.spans) - .build(); - - HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing).build(); - - @AfterEach - public void close() { - this.tracing.close(); - this.currentTraceContext.close(); - } +public abstract class TraceRequestHttpHeadersFilterTests implements TestTracingAwareSupplier { @Test public void should_override_span_tracing_headers() { - HttpHeadersFilter filter = TraceRequestHttpHeadersFilter.create(this.httpTracing); + HttpHeadersFilter filter = TraceRequestHttpHeadersFilter.create(tracerTest().tracing().tracer(), + tracerTest().tracing().httpClientHandler(), tracerTest().tracing().propagator()); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.set("X-Hello", "World"); httpHeaders.set("X-B3-TraceId", "52f112af7472aff0"); @@ -63,9 +46,9 @@ public class TraceRequestHttpHeadersFilterTests { HttpHeaders filteredHeaders = filter.filter(requestHeaders(httpHeaders), exchange); // we want to continue the trace - BDDAssertions.then(filteredHeaders.get("X-B3-TraceId")).isEqualTo(httpHeaders.get("X-B3-TraceId")); + BDDAssertions.then(high(filteredHeaders.get("X-B3-TraceId"))).isEqualTo(high(httpHeaders.get("X-B3-TraceId"))); // but we want to have a new span id - BDDAssertions.then(filteredHeaders.get("X-B3-SpanId")).isNotEqualTo(httpHeaders.get("X-B3-SpanId")); + BDDAssertions.then(high(filteredHeaders.get("X-B3-SpanId"))).isNotEqualTo(high(httpHeaders.get("X-B3-SpanId"))); BDDAssertions.then(filteredHeaders.get("X-Hello")).isEqualTo(Collections.singletonList("World")); BDDAssertions.then(filteredHeaders.get("X-Hello-Request")) .isEqualTo(Collections.singletonList("Request World")); @@ -75,7 +58,8 @@ public class TraceRequestHttpHeadersFilterTests { @Test public void should_override_span_tracing_headers_when_using_b3() { - HttpHeadersFilter filter = TraceRequestHttpHeadersFilter.create(this.httpTracing); + HttpHeadersFilter filter = TraceRequestHttpHeadersFilter.create(tracerTest().tracing().tracer(), + tracerTest().tracing().httpClientHandler(), tracerTest().tracing().propagator()); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.set("X-Hello", "World"); httpHeaders.set("B3", "1111111111111111-1111111111111111"); @@ -85,11 +69,11 @@ public class TraceRequestHttpHeadersFilterTests { HttpHeaders filteredHeaders = filter.filter(requestHeaders(httpHeaders), exchange); // we want to continue the trace - BDDAssertions.then(filteredHeaders.get("X-B3-TraceId")) - .isEqualTo(Collections.singletonList("1111111111111111")); + BDDAssertions.then(high(filteredHeaders.get("X-B3-TraceId"))) + .isEqualTo(high(Collections.singletonList("1111111111111111"))); // but we want to have a new span id - BDDAssertions.then(filteredHeaders.get("X-B3-SpanId")) - .isNotEqualTo(Collections.singletonList("1111111111111111")); + BDDAssertions.then(high(filteredHeaders.get("X-B3-SpanId"))) + .isNotEqualTo(high(Collections.singletonList("1111111111111111"))); // we don't want to propagate b3 BDDAssertions.then(filteredHeaders.get("B3")).isNullOrEmpty(); BDDAssertions.then(filteredHeaders.get("X-Hello")).isEqualTo(Collections.singletonList("World")); @@ -101,7 +85,8 @@ public class TraceRequestHttpHeadersFilterTests { @Test public void should_set_tracing_headers() { - HttpHeadersFilter filter = TraceRequestHttpHeadersFilter.create(this.httpTracing); + HttpHeadersFilter filter = TraceRequestHttpHeadersFilter.create(tracerTest().tracing().tracer(), + tracerTest().tracing().httpClientHandler(), tracerTest().tracing().propagator()); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.set("X-Hello", "World"); MockServerHttpRequest request = MockServerHttpRequest.post("foo/bar").headers(httpHeaders).build(); @@ -120,7 +105,8 @@ public class TraceRequestHttpHeadersFilterTests { // #1469 @Test public void should_reuse_headers_only_from_input_since_exchange_may_contain_already_ignored_headers() { - HttpHeadersFilter filter = TraceRequestHttpHeadersFilter.create(this.httpTracing); + HttpHeadersFilter filter = TraceRequestHttpHeadersFilter.create(tracerTest().tracing().tracer(), + tracerTest().tracing().httpClientHandler(), tracerTest().tracing().propagator()); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.set("X-Hello", "World"); MockServerHttpRequest request = MockServerHttpRequest.post("foo/bar").headers(httpHeaders).build(); @@ -137,7 +123,8 @@ public class TraceRequestHttpHeadersFilterTests { // #1352 @Test public void should_set_tracing_headers_with_multiple_values() { - HttpHeadersFilter filter = TraceRequestHttpHeadersFilter.create(this.httpTracing); + HttpHeadersFilter filter = TraceRequestHttpHeadersFilter.create(tracerTest().tracing().tracer(), + tracerTest().tracing().httpClientHandler(), tracerTest().tracing().propagator()); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.add("X-Hello-Request", "Request World"); httpHeaders.addAll("X-Hello", Arrays.asList("World1", "World2")); @@ -169,4 +156,13 @@ public class TraceRequestHttpHeadersFilterTests { return headers; } + private String high(List ids) { + BDDAssertions.then(ids).isNotNull().isNotEmpty(); + String id = ids.get(0); + if (id.length() == 32) { + return id.substring(16); + } + return id; + } + } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceResponseHttpHeadersFilterTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceResponseHttpHeadersFilterTests.java similarity index 70% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceResponseHttpHeadersFilterTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceResponseHttpHeadersFilterTests.java index bd7a97852..198ab7dd1 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceResponseHttpHeadersFilterTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceResponseHttpHeadersFilterTests.java @@ -16,39 +16,21 @@ package org.springframework.cloud.sleuth.instrument.web.client; -import brave.Tracing; -import brave.http.HttpTracing; -import brave.propagation.StrictCurrentTraceContext; -import brave.test.TestSpanHandler; import org.assertj.core.api.BDDAssertions; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter; +import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; import org.springframework.http.HttpHeaders; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.web.server.MockServerWebExchange; -public class TraceResponseHttpHeadersFilterTests { - - StrictCurrentTraceContext currentTraceContext = StrictCurrentTraceContext.create(); - - TestSpanHandler spans = new TestSpanHandler(); - - Tracing tracing = Tracing.newBuilder().currentTraceContext(this.currentTraceContext).addSpanHandler(this.spans) - .build(); - - HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing).build(); - - @AfterEach - public void close() { - this.tracing.close(); - this.currentTraceContext.close(); - } +public abstract class TraceResponseHttpHeadersFilterTests implements TestTracingAwareSupplier { @Test public void should_not_report_span_when_no_span_was_present_in_attribute() { - HttpHeadersFilter filter = TraceResponseHttpHeadersFilter.create(this.httpTracing); + HttpHeadersFilter filter = TraceResponseHttpHeadersFilter.create(tracerTest().tracing().tracer(), + tracerTest().tracing().httpClientHandler(), tracerTest().tracing().propagator()); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.set("b3", "52f112af7472aff0-53e6ab6fc5dfee58"); MockServerHttpRequest request = MockServerHttpRequest.post("foo/bar").headers(httpHeaders).build(); @@ -56,21 +38,23 @@ public class TraceResponseHttpHeadersFilterTests { filter.filter(httpHeaders, exchange); - BDDAssertions.then(this.spans).isEmpty(); + BDDAssertions.then(tracerTest().handler().reportedSpans()).isEmpty(); } @Test public void should_report_span_when_span_was_present_in_attribute() { - HttpHeadersFilter filter = TraceResponseHttpHeadersFilter.create(this.httpTracing); + HttpHeadersFilter filter = TraceResponseHttpHeadersFilter.create(tracerTest().tracing().tracer(), + tracerTest().tracing().httpClientHandler(), tracerTest().tracing().propagator()); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.set("b3", "52f112af7472aff0-53e6ab6fc5dfee58"); MockServerHttpRequest request = MockServerHttpRequest.post("foo/bar").headers(httpHeaders).build(); MockServerWebExchange exchange = MockServerWebExchange.builder(request).build(); - exchange.getAttributes().put(TraceResponseHttpHeadersFilter.SPAN_ATTRIBUTE, this.tracing.tracer().nextSpan()); + exchange.getAttributes().put(TraceResponseHttpHeadersFilter.SPAN_ATTRIBUTE, + tracerTest().tracing().tracer().nextSpan()); filter.filter(httpHeaders, exchange); - BDDAssertions.then(this.spans).isNotEmpty(); + BDDAssertions.then(tracerTest().handler().reportedSpans()).isNotEmpty(); } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorIntegrationTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorIntegrationTests.java similarity index 70% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorIntegrationTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorIntegrationTests.java index 018c658bb..f98479b87 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorIntegrationTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorIntegrationTests.java @@ -20,14 +20,6 @@ import java.io.IOException; import java.util.Arrays; import java.util.Map; -import brave.Span; -import brave.Tracer; -import brave.Tracing; -import brave.handler.MutableSpan; -import brave.http.HttpTracing; -import brave.propagation.StrictCurrentTraceContext; -import brave.spring.web.TracingClientHttpRequestInterceptor; -import brave.test.TestSpanHandler; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.SocketPolicy; @@ -36,8 +28,13 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.instrument.web.mvc.TracingClientHttpRequestInterceptor; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; import org.springframework.http.client.ClientHttpRequestFactory; -import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; @@ -46,7 +43,7 @@ import static org.assertj.core.api.Assertions.fail; /** * @author Marcin Grzejszczak */ -public class TraceRestTemplateInterceptorIntegrationTests { +public abstract class TraceRestTemplateInterceptorIntegrationTests implements TestTracingAwareSupplier { public final MockWebServer mockWebServer = new MockWebServer(); @@ -60,36 +57,30 @@ public class TraceRestTemplateInterceptorIntegrationTests { mockWebServer.close(); } - StrictCurrentTraceContext currentTraceContext = StrictCurrentTraceContext.create(); - - TestSpanHandler spans = new TestSpanHandler(); - - Tracing tracing = Tracing.newBuilder().currentTraceContext(this.currentTraceContext).addSpanHandler(this.spans) - .build(); - - Tracer tracer = this.tracing.tracer(); - private RestTemplate template = new RestTemplate(clientHttpRequestFactory()); + Tracer tracer = tracerTest().tracing().tracer(); + + TestSpanHandler spans = tracerTest().handler(); + @BeforeEach public void setup() { - this.template.setInterceptors(Arrays.asList( - TracingClientHttpRequestInterceptor.create(HttpTracing.create(this.tracing)))); + this.template.setInterceptors(Arrays.asList(TracingClientHttpRequestInterceptor + .create(tracerTest().tracing().currentTraceContext(), tracerTest().tracing().httpClientHandler()))); } @AfterEach public void clean() { - this.tracing.close(); - this.currentTraceContext.close(); + tracerTest().close(); } // Issue #198 @Test public void spanRemovedFromThreadUponException() throws IOException { this.mockWebServer.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START)); - Span span = this.tracer.nextSpan().name("new trace"); + Span span = tracerTest().tracing().tracer().nextSpan().name("new trace"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = tracerTest().tracing().tracer().withSpan(span.start())) { this.template.getForEntity("http://localhost:" + this.mockWebServer.getPort() + "/exception", Map.class) .getBody(); fail("should throw an exception"); @@ -98,12 +89,12 @@ public class TraceRestTemplateInterceptorIntegrationTests { BDDAssertions.then(e).hasRootCauseInstanceOf(IOException.class); } finally { - span.finish(); + span.end(); } // 1 span "new race", 1 span "rest template" BDDAssertions.then(this.spans).hasSize(2); - MutableSpan span1 = this.spans.get(0); + FinishedSpan span1 = this.spans.get(0); BDDAssertions.then(span1.error()).hasMessage("Read timed out"); BDDAssertions.then(span1.kind()).isEqualTo(Span.Kind.CLIENT); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceRestTemplateInterceptorTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorTests.java similarity index 62% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceRestTemplateInterceptorTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorTests.java index 2a8996f59..2333db4ff 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceRestTemplateInterceptorTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorTests.java @@ -14,27 +14,24 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web; +package org.springframework.cloud.sleuth.instrument.web.client; import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import brave.Span; -import brave.Tracer; -import brave.Tracing; -import brave.http.HttpClientParser; -import brave.http.HttpRequestParser; -import brave.http.HttpTags; -import brave.http.HttpTracing; -import brave.propagation.StrictCurrentTraceContext; -import brave.sampler.Sampler; -import brave.spring.web.TracingClientHttpRequestInterceptor; -import brave.test.TestSpanHandler; +import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; +import org.springframework.cloud.sleuth.instrument.web.mvc.TracingClientHttpRequestInterceptor; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; +import org.springframework.cloud.sleuth.test.TracerAware; import org.springframework.http.HttpHeaders; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.test.web.client.MockMvcClientHttpRequestFactory; @@ -52,16 +49,7 @@ import static org.assertj.core.api.BDDAssertions.then; * @author Dave Syer * */ -public class TraceRestTemplateInterceptorTests { - - StrictCurrentTraceContext currentTraceContext = StrictCurrentTraceContext.create(); - - TestSpanHandler spans = new TestSpanHandler(); - - Tracing tracing = Tracing.newBuilder().currentTraceContext(this.currentTraceContext).addSpanHandler(this.spans) - .build(); - - Tracer tracer = this.tracing.tracer(); +public abstract class TraceRestTemplateInterceptorTests implements TestTracingAwareSupplier { private TestController testController = new TestController(); @@ -69,20 +57,23 @@ public class TraceRestTemplateInterceptorTests { private RestTemplate template = new RestTemplate(new MockMvcClientHttpRequestFactory(this.mockMvc)); + Tracer tracer = tracerTest().tracing().tracer(); + + TestSpanHandler spans = tracerTest().handler(); + @BeforeEach public void setup() { - setInterceptors(HttpTracing.create(this.tracing)); + setInterceptors(tracerTest().tracing().httpClientHandler()); } - private void setInterceptors(HttpTracing httpTracing) { - this.template.setInterceptors( - Arrays.asList(TracingClientHttpRequestInterceptor.create(httpTracing))); + private void setInterceptors(HttpClientHandler httpClientHandler) { + this.template.setInterceptors(Arrays.asList(TracingClientHttpRequestInterceptor + .create(tracerTest().tracing().currentTraceContext(), httpClientHandler))); } @AfterEach public void clean() { - this.tracing.close(); - this.currentTraceContext.close(); + tracerTest().close(); } @Test @@ -100,59 +91,61 @@ public class TraceRestTemplateInterceptorTests { Span span = this.tracer.nextSpan().name("new trace"); Map headers; - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { headers = this.template.getForEntity("/", Map.class).getBody(); } finally { - span.finish(); + span.end(); } // Default inject format for client spans is B3 multi - then(headers.get("X-B3-TraceId")).isEqualTo(span.context().traceIdString()); - then(headers.get("X-B3-SpanId")).isNotEqualTo(span.context().spanIdString()); - then(headers.get("X-B3-ParentSpanId")).isEqualTo(span.context().spanIdString()); + then(headers.get("X-B3-TraceId")).isEqualTo(span.context().traceId()); + then(headers.get("X-B3-SpanId")).isNotEqualTo(span.context().spanId()); + assertThatParentSpanIdSet(span, headers); + } + + public void assertThatParentSpanIdSet(Span span, Map headers) { + throw new UnsupportedOperationException("Implement this assertion"); } // Issue #290 @Test public void requestHeadersAddedWhenTracing() { - setInterceptors(HttpTracing.newBuilder(this.tracing).clientRequestParser((request, context, span) -> { - HttpTags.URL.tag(request, context, span); - HttpRequestParser.DEFAULT.parse(request, context, span); - }).build()); + setInterceptors(tracerTest().tracing() + .clientRequestParser((request, context, span) -> span.tag("http.url", request.url()) + .tag("http.method", request.method()).tag("http.path", request.path()).name(request.method())) + .httpClientHandler()); Span span = this.tracer.nextSpan().name("new trace"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { this.template.getForEntity("/foo?a=b", Map.class); } finally { - span.finish(); + span.end(); } - then(this.spans).isNotEmpty(); - then(this.spans.get(0).tags()).containsEntry("http.url", "/foo?a=b").containsEntry("http.path", "/foo") - .containsEntry("http.method", "GET"); + BDDAssertions.then(this.spans).isNotEmpty(); + BDDAssertions.then(this.spans.get(0).tags()).containsEntry("http.url", "/foo?a=b") + .containsEntry("http.path", "/foo").containsEntry("http.method", "GET"); } @Test public void notSampledHeaderAddedWhenNotSampled() { - this.tracing.close(); - this.tracing = Tracing.newBuilder().currentTraceContext(this.currentTraceContext).addSpanHandler(this.spans) - .sampler(Sampler.NEVER_SAMPLE).build(); this.template.setInterceptors(Arrays.asList( - TracingClientHttpRequestInterceptor.create(HttpTracing.create(tracing)))); + TracingClientHttpRequestInterceptor.create(tracerTest().tracing().currentTraceContext(), + tracerTest().tracing().sampler(TracerAware.TraceSampler.OFF).httpClientHandler()))); + this.spans = tracerTest().handler(); - Span span = tracing.tracer().nextSpan().name("new trace"); - Map headers; + Span span = tracerTest().tracing().tracer().nextSpan().name("new trace"); - try (Tracer.SpanInScope ws = tracing.tracer().withSpanInScope(span.start())) { - headers = this.template.getForEntity("/", Map.class).getBody(); + try (Tracer.SpanInScope ws = tracerTest().tracing().tracer().withSpan(span.start())) { + this.template.getForEntity("/", Map.class).getBody(); } finally { - span.finish(); + span.end(); } - then(this.spans).isEmpty(); + BDDAssertions.then(this.spans).isEmpty(); } // issue #198 @@ -160,7 +153,7 @@ public class TraceRestTemplateInterceptorTests { public void spanRemovedFromThreadUponException() { Span span = this.tracer.nextSpan().name("new trace"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { this.template.getForEntity("/exception", Map.class).getBody(); fail("should throw an exception"); } @@ -168,28 +161,27 @@ public class TraceRestTemplateInterceptorTests { then(e).hasMessageStartingWith("500 Internal Server Error"); } finally { - span.finish(); + span.end(); } - then(this.tracer.currentSpan()).isNull(); + BDDAssertions.then(this.tracer.currentSpan()).isNull(); } @Test public void createdSpanNameHasOnlyPrintableAsciiCharactersForNonEncodedURIWithNonAsciiChars() { - setInterceptors(HttpTracing.newBuilder(this.tracing).clientParser(new HttpClientParser()).build()); Span span = this.tracer.nextSpan().name("new trace"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { this.template.getForEntity("/cas~fs~划", Map.class).getBody(); } catch (Exception e) { } finally { - span.finish(); + span.end(); } - then(this.spans).hasSize(2); + BDDAssertions.then(this.spans).hasSize(2); } @RestController diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/discoveryexception/WebClientDiscoveryExceptionTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/discoveryexception/WebClientDiscoveryExceptionTests.java similarity index 79% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/discoveryexception/WebClientDiscoveryExceptionTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/discoveryexception/WebClientDiscoveryExceptionTests.java index 068b53065..39f25e1f0 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/discoveryexception/WebClientDiscoveryExceptionTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/discoveryexception/WebClientDiscoveryExceptionTests.java @@ -22,11 +22,6 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import brave.Span; -import brave.Tracer; -import brave.handler.SpanHandler; -import brave.sampler.Sampler; -import brave.test.TestSpanHandler; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -34,31 +29,33 @@ import reactor.core.publisher.Flux; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; -import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration; +import org.springframework.cloud.sleuth.test.TestSpanHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.ResponseEntity; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.client.RestTemplate; import static org.assertj.core.api.BDDAssertions.then; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; -@SpringBootTest(classes = { WebClientDiscoveryExceptionTests.TestConfiguration.class }, webEnvironment = RANDOM_PORT) -@TestPropertySource(properties = { "spring.application.name=exceptionservice" }) +@ContextConfiguration(classes = WebClientDiscoveryExceptionTests.TestConfiguration.class) +@TestPropertySource(properties = "spring.application.name=exceptionservice") @DirtiesContext -public class WebClientDiscoveryExceptionTests { +public abstract class WebClientDiscoveryExceptionTests { @Autowired TestFeignInterfaceWithException testFeignInterfaceWithException; @@ -83,20 +80,20 @@ public class WebClientDiscoveryExceptionTests { throws IOException, InterruptedException { Span span = this.tracer.nextSpan().name("new trace"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span.start())) { provider.get(this); Assertions.fail("should throw an exception"); } catch (RuntimeException e) { } finally { - span.finish(); + span.end(); } // hystrix commands should finish at this point Thread.sleep(200); - then(this.spans.spans().stream().filter(span1 -> span1.kind() == Span.Kind.CLIENT).findFirst().get().error()) - .isNotNull(); + then(this.spans.reportedSpans().stream().filter(span1 -> span1.kind() == Span.Kind.CLIENT).findFirst().get() + .error()).isNotNull(); } @Test @@ -126,10 +123,8 @@ public class WebClientDiscoveryExceptionTests { } - @Configuration - @EnableAutoConfiguration( - excludeName = "org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration", - exclude = EurekaClientAutoConfiguration.class) + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration(exclude = TraceWebServletAutoConfiguration.class) @EnableDiscoveryClient @EnableFeignClients @LoadBalancerClient("exceptionservice") @@ -141,16 +136,6 @@ public class WebClientDiscoveryExceptionTests { return new RestTemplate(); } - @Bean - Sampler alwaysSampler() { - return Sampler.ALWAYS_SAMPLE; - } - - @Bean - SpanHandler testSpanHandler() { - return new TestSpanHandler(); - } - @Bean ServiceInstanceListSupplier serviceInstanceListSupplier() { return new ServiceInstanceListSupplier() { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/exception/WebClientExceptionTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/exception/WebClientExceptionTests.java similarity index 80% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/exception/WebClientExceptionTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/exception/WebClientExceptionTests.java index c009db210..f9ce61a0b 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/exception/WebClientExceptionTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/exception/WebClientExceptionTests.java @@ -18,33 +18,34 @@ package org.springframework.cloud.sleuth.instrument.web.client.exception; import java.io.IOException; import java.util.Map; +import java.util.concurrent.TimeUnit; import java.util.stream.Stream; -import brave.Span; -import brave.Tracer; -import brave.Tracing; -import brave.handler.SpanHandler; -import brave.sampler.Sampler; -import brave.test.TestSpanHandler; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.awaitility.Awaitility; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.test.TestSpanHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.http.ResponseEntity; import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.client.RestTemplate; @@ -52,9 +53,9 @@ import org.springframework.web.client.RestTemplate; import static org.assertj.core.api.Assertions.fail; import static org.assertj.core.api.BDDAssertions.then; -@SpringBootTest(classes = { WebClientExceptionTests.TestConfiguration.class }, - properties = { "spring.application.name=exceptionservice" }, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = WebClientExceptionTests.TestConfiguration.class) +@TestPropertySource(properties = "spring.application.name=exceptionservice") +@DirtiesContext public class WebClientExceptionTests { private static final Log log = LogFactory.getLog(WebClientExceptionTests.class); @@ -67,7 +68,7 @@ public class WebClientExceptionTests { RestTemplate template; @Autowired - Tracing tracer; + Tracer tracer; @Autowired TestSpanHandler spans; @@ -81,9 +82,9 @@ public class WebClientExceptionTests { @ParameterizedTest @MethodSource("parametersForShouldCloseSpanUponException") public void shouldCloseSpanUponException(ResponseEntityProvider provider) throws IOException { - Span span = this.tracer.tracer().nextSpan().name("new trace").start(); + Span span = this.tracer.nextSpan().name("new trace").start(); - try (Tracer.SpanInScope ws = this.tracer.tracer().withSpanInScope(span)) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span)) { log.info("Started new span " + span); provider.get(this); fail("should throw an exception"); @@ -92,12 +93,14 @@ public class WebClientExceptionTests { // SleuthAssertions.then(e).hasRootCauseInstanceOf(IOException.class); } finally { - span.finish(); + span.end(); } - then(this.tracer.tracer().currentSpan()).isNull(); - then(this.spans).isNotEmpty(); - then(this.spans.get(0).error()).isNotNull(); + then(this.tracer.currentSpan()).isNull(); + Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { + then(this.spans).isNotEmpty(); + then(this.spans.get(0).error()).isNotNull(); + }); } static Stream parametersForShouldCloseSpanUponException() { @@ -122,7 +125,7 @@ public class WebClientExceptionTests { } - @Configuration + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration @EnableFeignClients @LoadBalancerClient(value = "exceptionservice", @@ -138,19 +141,9 @@ public class WebClientExceptionTests { return new RestTemplate(clientHttpRequestFactory); } - @Bean - Sampler alwaysSampler() { - return Sampler.ALWAYS_SAMPLE; - } - - @Bean - SpanHandler testSpanHandler() { - return new TestSpanHandler(); - } - } - @Configuration + @Configuration(proxyBeanMethods = false) public static class ExceptionServiceLoadBalancerClientConfiguration { @Bean diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRetriesTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRetriesTests.java similarity index 67% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRetriesTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRetriesTests.java index 40e627ec7..a603c515d 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRetriesTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRetriesTests.java @@ -21,11 +21,6 @@ import java.nio.charset.Charset; import java.util.HashMap; import java.util.concurrent.atomic.AtomicInteger; -import brave.Span; -import brave.Tracing; -import brave.http.HttpTracing; -import brave.propagation.StrictCurrentTraceContext; -import brave.test.TestSpanHandler; import feign.Client; import feign.Feign; import feign.FeignException; @@ -34,6 +29,8 @@ import feign.RequestLine; import feign.RequestTemplate; import feign.Response; import okhttp3.mockwebserver.MockWebServer; +import org.assertj.core.api.Assertions; +import org.assertj.core.api.BDDAssertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -43,15 +40,16 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.BeanFactory; - -import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; -import static org.assertj.core.api.BDDAssertions.then; +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; +import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; /** * @author Marcin Grzejszczak */ @ExtendWith(MockitoExtension.class) -public class FeignRetriesTests { +public abstract class FeignRetriesTests implements TestTracingAwareSupplier { public final MockWebServer server = new MockWebServer(); @@ -68,25 +66,13 @@ public class FeignRetriesTests { @Mock(lenient = true) BeanFactory beanFactory; - StrictCurrentTraceContext currentTraceContext = StrictCurrentTraceContext.create(); - - TestSpanHandler spans = new TestSpanHandler(); - - Tracing tracing = Tracing.newBuilder().currentTraceContext(this.currentTraceContext).addSpanHandler(this.spans) - .build(); - - HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing).build(); - @BeforeEach @AfterEach public void setup() { - BDDMockito.given(this.beanFactory.getBean(HttpTracing.class)).willReturn(this.httpTracing); - } - - @AfterEach - public void close() { - this.tracing.close(); - this.currentTraceContext.close(); + BDDMockito.given(this.beanFactory.getBean(CurrentTraceContext.class)) + .willReturn(tracerTest().tracing().currentTraceContext()); + BDDMockito.given(this.beanFactory.getBean(HttpClientHandler.class)) + .willReturn(tracerTest().tracing().httpClientHandler()); } @Test @@ -96,12 +82,12 @@ public class FeignRetriesTests { }; String url = "http://localhost:" + this.server.getPort(); - TestInterface api = Feign.builder().client(new TracingFeignClient(this.httpTracing, client)) - .target(TestInterface.class, url); + TestInterface api = Feign.builder().client(new TracingFeignClient(tracerTest().tracing().currentTraceContext(), + tracerTest().tracing().httpClientHandler(), client)).target(TestInterface.class, url); try { api.decodedPost(); - failBecauseExceptionWasNotThrown(FeignException.class); + Assertions.failBecauseExceptionWasNotThrown(FeignException.class); } catch (FeignException e) { } @@ -125,16 +111,21 @@ public class FeignRetriesTests { .build(); } }; - TestInterface api = Feign.builder().client(new TracingFeignClient(this.httpTracing, (request, options) -> { - atomicInteger.incrementAndGet(); - return client.execute(request, options); - })).target(TestInterface.class, url); + TestInterface api = Feign.builder().client(new TracingFeignClient(tracerTest().tracing().currentTraceContext(), + tracerTest().tracing().httpClientHandler(), (request, options) -> { + atomicInteger.incrementAndGet(); + return client.execute(request, options); + })).target(TestInterface.class, url); - then(api.decodedPost()).isEqualTo("OK"); + BDDAssertions.then(api.decodedPost()).isEqualTo("OK"); // request interception should take place only twice (1st request & 2nd retry) - then(atomicInteger.get()).isEqualTo(2); - then(this.spans.get(0).error()).isInstanceOf(IOException.class); - then(this.spans.get(1).kind()).isEqualTo(Span.Kind.CLIENT); + BDDAssertions.then(atomicInteger.get()).isEqualTo(2); + assertException(); + BDDAssertions.then(tracerTest().handler().reportedSpans().get(1).kind()).isEqualTo(Span.Kind.CLIENT); + } + + public void assertException() { + throw new UnsupportedOperationException("Implement this assertion"); } interface TestInterface { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignAspectTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignAspectTests.java similarity index 66% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignAspectTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignAspectTests.java index 4714b80ba..e7eaad042 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignAspectTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignAspectTests.java @@ -16,29 +16,24 @@ package org.springframework.cloud.sleuth.instrument.web.client.feign; -import brave.Tracing; -import brave.http.HttpTracing; -import brave.propagation.StrictCurrentTraceContext; import feign.Client; import org.aspectj.lang.ProceedingJoinPoint; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.BDDMockito; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.BeanFactory; - -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; +import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; /** * @author Marcin Grzejszczak */ @ExtendWith(MockitoExtension.class) -public class TraceFeignAspectTests { +public abstract class TraceFeignAspectTests implements TestTracingAwareSupplier { @Mock BeanFactory beanFactory; @@ -49,12 +44,6 @@ public class TraceFeignAspectTests { @Mock ProceedingJoinPoint pjp; - StrictCurrentTraceContext currentTraceContext = StrictCurrentTraceContext.create(); - - Tracing tracing = Tracing.newBuilder().currentTraceContext(currentTraceContext).build(); - - HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing).build(); - TraceFeignAspect traceFeignAspect; @BeforeEach @@ -67,28 +56,23 @@ public class TraceFeignAspectTests { }; } - @AfterEach - public void close() { - this.tracing.close(); - this.currentTraceContext.close(); - } - @Test public void should_wrap_feign_client_in_trace_representation() throws Throwable { - given(this.pjp.getTarget()).willReturn(this.client); + BDDMockito.given(this.pjp.getTarget()).willReturn(this.client); this.traceFeignAspect.feignClientWasCalled(this.pjp); - verify(this.pjp, never()).proceed(); + Mockito.verify(this.pjp, Mockito.never()).proceed(); } @Test public void should_not_wrap_traced_feign_client_in_trace_representation() throws Throwable { - given(this.pjp.getTarget()).willReturn(new TracingFeignClient(this.httpTracing, this.client)); + BDDMockito.given(this.pjp.getTarget()).willReturn(new TracingFeignClient( + tracerTest().tracing().currentTraceContext(), tracerTest().tracing().httpClientHandler(), this.client)); this.traceFeignAspect.feignClientWasCalled(this.pjp); - verify(this.pjp).proceed(); + Mockito.verify(this.pjp).proceed(); } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignClientTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignClientTests.java similarity index 67% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignClientTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignClientTests.java index 989e55504..a5c2459d2 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignClientTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignClientTests.java @@ -19,18 +19,11 @@ package org.springframework.cloud.sleuth.instrument.web.client.feign; import java.io.IOException; import java.util.HashMap; -import brave.Span; -import brave.Tracer; -import brave.Tracing; -import brave.http.HttpTracing; -import brave.propagation.StrictCurrentTraceContext; -import brave.test.TestSpanHandler; import feign.Client; import feign.Request; import feign.RequestTemplate; import org.assertj.core.api.BDDAssertions; import org.junit.Assert; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -40,14 +33,16 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.stubbing.Answer; -import static org.assertj.core.api.BDDAssertions.then; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; /** * @author Marcin Grzejszczak * @author Hash.Jang */ @ExtendWith(MockitoExtension.class) -public class TracingFeignClientTests { +public abstract class TracingFeignClientTests implements TestTracingAwareSupplier { RequestTemplate requestTemplate = new RequestTemplate(); @@ -56,16 +51,6 @@ public class TracingFeignClientTests { Request.Options options = new Request.Options(); - StrictCurrentTraceContext currentTraceContext = StrictCurrentTraceContext.create(); - - TestSpanHandler spans = new TestSpanHandler(); - - Tracing tracing = Tracing.newBuilder().currentTraceContext(currentTraceContext).addSpanHandler(spans).build(); - - Tracer tracer = this.tracing.tracer(); - - HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing).build(); - @Mock Client client; @@ -73,47 +58,46 @@ public class TracingFeignClientTests { @BeforeEach public void setup() { - this.traceFeignClient = TracingFeignClient.create(this.httpTracing, this.client); - } - - @AfterEach - public void close() { - this.tracing.close(); - this.currentTraceContext.close(); + this.traceFeignClient = TracingFeignClient.create(tracerTest().tracing().currentTraceContext(), + tracerTest().tracing().httpClientHandler(), this.client); } @Test public void should_log_cr_when_response_successful() throws IOException { - Span span = this.tracer.nextSpan().name("foo"); + Span span = tracerTest().tracing().tracer().nextSpan().name("foo"); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = tracerTest().tracing().tracer().withSpan(span.start())) { this.traceFeignClient.execute(this.request, this.options); } finally { - span.finish(); + span.end(); } - then(spans.get(0).kind()).isEqualTo(Span.Kind.CLIENT); + BDDAssertions.then(tracerTest().handler().reportedSpans().get(0).kind()).isEqualTo(Span.Kind.CLIENT); } @Test public void should_log_error_when_exception_thrown() throws IOException { RuntimeException error = new RuntimeException("exception has occurred"); - Span span = this.tracer.nextSpan().name("foo"); + Span span = tracerTest().tracing().tracer().nextSpan().name("foo"); BDDMockito.given(this.client.execute(BDDMockito.any(), BDDMockito.any())).willThrow(error); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) { + try (Tracer.SpanInScope ws = tracerTest().tracing().tracer().withSpan(span.start())) { this.traceFeignClient.execute(this.request, this.options); BDDAssertions.fail("Exception should have been thrown"); } catch (Exception e) { } finally { - span.finish(); + span.end(); } - then(this.spans.get(0).kind()).isEqualTo(Span.Kind.CLIENT); - then(this.spans.get(0).error()).isSameAs(error); + BDDAssertions.then(this.tracerTest().handler().reportedSpans().get(0).kind()).isEqualTo(Span.Kind.CLIENT); + assertException(error); + } + + public void assertException(RuntimeException error) { + throw new UnsupportedOperationException("Implement this assertion"); } @Test diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/integration/notsampled/WebClientNotSampledTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/integration/notsampled/WebClientNotSampledTests.java new file mode 100644 index 000000000..3d39cd4b4 --- /dev/null +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/integration/notsampled/WebClientNotSampledTests.java @@ -0,0 +1,190 @@ +/* + * Copyright 2013-2020 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.client.integration.notsampled; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; +import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +import static org.assertj.core.api.BDDAssertions.then; + +@ContextConfiguration(classes = WebClientNotSampledTests.TestConfiguration.class) +@TestPropertySource( + properties = { "spring.application.name=fooservice", "spring.sleuth.web.client.skip-pattern=/skip.*" }) +@DirtiesContext +public abstract class WebClientNotSampledTests { + + @Autowired + TestFeignInterface testFeignInterface; + + @Autowired + @LoadBalanced + RestTemplate template; + + @Autowired + TestSpanHandler spans; + + @Autowired + Tracer tracer; + + @LocalServerPort + int port; + + @Autowired + FooController fooController; + + @AfterEach + @BeforeEach + public void close() { + this.spans.clear(); + this.fooController.clear(); + } + + @ParameterizedTest + @MethodSource("parametersForShouldPropagateNotSamplingHeader") + @SuppressWarnings("unchecked") + public void shouldPropagateNotSamplingHeader(ResponseEntityProvider provider) { + Span span = this.tracer.nextSpan().name("foo").start(); + + try (Tracer.SpanInScope ws = this.tracer.withSpan(span)) { + ResponseEntity> response = provider.get(this); + + assertB3SingleNotSampled(response); + } + finally { + span.end(); + } + + then(this.spans).isEmpty(); + then(this.tracer.currentSpan()).isNull(); + } + + public void assertB3SingleNotSampled(ResponseEntity> response) { + throw new UnsupportedOperationException("Implement this assertion"); + } + + static Stream parametersForShouldPropagateNotSamplingHeader() throws Exception { + return Stream.of((ResponseEntityProvider) (tests) -> tests.testFeignInterface.headers(), + (ResponseEntityProvider) (tests) -> tests.template.getForEntity("http://fooservice/", Map.class)); + } + + @FeignClient("fooservice") + public interface TestFeignInterface { + + @RequestMapping(method = RequestMethod.GET, value = "/") + ResponseEntity> headers(); + + } + + @FunctionalInterface + interface ResponseEntityProvider { + + @SuppressWarnings("rawtypes") + ResponseEntity get(WebClientNotSampledTests webClientTests); + + } + + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration(exclude = { TraceWebServletAutoConfiguration.class, JmxAutoConfiguration.class }) + @EnableFeignClients + @LoadBalancerClient(value = "fooservice", configuration = SimpleLoadBalancerClientConfiguration.class) + public static class TestConfiguration { + + @Bean + FooController fooController() { + return new FooController(); + } + + @LoadBalanced + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } + + } + + @RestController + public static class FooController { + + Span span; + + @RequestMapping("/") + public Map home(@RequestHeader HttpHeaders headers) { + Map map = new HashMap<>(); + for (String key : headers.keySet()) { + map.put(key, headers.getFirst(key)); + } + return map; + } + + public Span getSpan() { + return this.span; + } + + public void clear() { + this.span = null; + } + + } + + @Configuration(proxyBeanMethods = false) + public static class SimpleLoadBalancerClientConfiguration { + + @Value("${local.server.port}") + private int port = 0; + + @Bean + public ServiceInstanceListSupplier serviceInstanceListSupplier(Environment env) { + return ServiceInstanceListSupplier.fixed(env).instance(this.port, "fooservice").build(); + } + + } + +} diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/integration/parser/WebClientCustomParserTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/integration/parser/WebClientCustomParserTests.java new file mode 100644 index 000000000..5870e4c82 --- /dev/null +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/integration/parser/WebClientCustomParserTests.java @@ -0,0 +1,191 @@ +/* + * Copyright 2013-2020 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.client.integration.parser; + +import java.util.Map; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; +import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.http.HttpRequestParser; +import org.springframework.cloud.sleuth.api.http.HttpResponseParser; +import org.springframework.cloud.sleuth.instrument.web.HttpClientRequestParser; +import org.springframework.cloud.sleuth.instrument.web.HttpClientResponseParser; +import org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.core.env.Environment; +import org.springframework.http.ResponseEntity; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import static org.assertj.core.api.BDDAssertions.then; + +@ContextConfiguration(classes = WebClientCustomParserTests.TestConfiguration.class) +@TestPropertySource( + properties = { "spring.application.name=fooservice", "spring.sleuth.web.client.skip-pattern=/skip.*" }) +@DirtiesContext +public abstract class WebClientCustomParserTests { + + @Autowired + TestFeignInterface testFeignInterface; + + @Autowired + TestSpanHandler spans; + + @Autowired + Tracer tracer; + + @LocalServerPort + int port; + + @Autowired + FooController fooController; + + @AfterEach + @BeforeEach + public void close() { + this.spans.clear(); + this.fooController.clear(); + } + + @Test + public void should_set_tags_via_server_and_client_parsers() { + this.testFeignInterface.getTraceId(); + Map clientSideTags = spans.reportedSpans().stream() + .filter(f -> f.kind().equals(Span.Kind.CLIENT)).flatMap(f -> f.tags().entrySet().stream()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + then(clientSideTags).containsEntry("ClientRequest", "Tag").containsEntry("ClientRequestFeign", "GET") + .containsEntry("ClientResponse", "Tag").containsEntry("ClientResponseFeign", "200"); + } + + @FeignClient("fooservice") + public interface TestFeignInterface { + + @RequestMapping(method = RequestMethod.GET, value = "/traceid") + ResponseEntity getTraceId(); + + } + + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration(exclude = { TraceWebServletAutoConfiguration.class, JmxAutoConfiguration.class }) + @EnableFeignClients + @LoadBalancerClient(value = "fooservice", configuration = SimpleLoadBalancerClientConfiguration.class) + @Import(ClientParserConfiguration.class) + public static class TestConfiguration { + + @Bean + FooController fooController() { + return new FooController(); + } + + } + + @Configuration(proxyBeanMethods = false) + public static class SimpleLoadBalancerClientConfiguration { + + @Value("${local.server.port}") + private int port = 0; + + @Bean + public ServiceInstanceListSupplier serviceInstanceListSupplier(Environment env) { + return ServiceInstanceListSupplier.fixed(env).instance(this.port, "fooservice").build(); + } + + } + + @RestController + public static class FooController { + + Span span; + + @RequestMapping(value = "/traceid", method = RequestMethod.GET) + public String traceId(@RequestHeader("b3") String b3Single) { + then(b3Single).isNotEmpty(); + return b3Single; + } + + public Span getSpan() { + return this.span; + } + + public void clear() { + this.span = null; + } + + } + + // tag::client_parser_config[] + @Configuration(proxyBeanMethods = false) + public static class ClientParserConfiguration { + + // example for Feign + @Bean(name = HttpClientRequestParser.NAME) + HttpRequestParser myHttpClientRequestParser() { + return (request, context, span) -> { + // Span customization + span.name(request.method()); + span.tag("ClientRequest", "Tag"); + Object unwrap = request.unwrap(); + if (unwrap instanceof feign.Request) { + feign.Request req = (feign.Request) unwrap; + // Span customization + span.tag("ClientRequestFeign", req.httpMethod().name()); + } + }; + } + + // example for Feign + @Bean(name = HttpClientResponseParser.NAME) + HttpResponseParser myHttpClientResponseParser() { + return (response, context, span) -> { + // Span customization + span.tag("ClientResponse", "Tag"); + Object unwrap = response.unwrap(); + if (unwrap instanceof feign.Response) { + feign.Response resp = (feign.Response) unwrap; + // Span customization + span.tag("ClientResponseFeign", String.valueOf(resp.status())); + } + }; + } + + } + // end::client_parser_config[] + +} diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/integration/WebClientTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/integration/sampled/WebClientTests.java similarity index 70% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/integration/WebClientTests.java rename to tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/integration/sampled/WebClientTests.java index bab90ab7c..be023555b 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/integration/WebClientTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/integration/sampled/WebClientTests.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package org.springframework.cloud.sleuth.instrument.web.client.integration; +package org.springframework.cloud.sleuth.instrument.web.client.integration.sampled; +import java.time.Duration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @@ -28,27 +28,8 @@ import java.util.stream.Stream; import javax.servlet.http.HttpServletRequest; -import brave.Span; -import brave.Tracer; -import brave.Tracing; -import brave.baggage.BaggagePropagation; -import brave.handler.MutableSpan; -import brave.handler.SpanHandler; -import brave.propagation.B3Propagation; -import brave.propagation.B3SingleFormat; -import brave.propagation.SamplingFlags; -import brave.propagation.TraceContextOrSamplingFlags; -import brave.sampler.Sampler; -import brave.test.TestSpanHandler; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.concurrent.FutureCallback; -import org.apache.http.impl.client.BasicResponseHandler; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; -import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; import org.awaitility.Awaitility; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -62,26 +43,30 @@ import reactor.core.publisher.BaseSubscriber; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.boot.web.client.RestTemplateCustomizer; import org.springframework.boot.web.server.LocalServerPort; import org.springframework.boot.web.servlet.error.ErrorAttributes; import org.springframework.cloud.client.loadbalancer.LoadBalanced; -import org.springframework.cloud.gateway.config.GatewayAutoConfiguration; -import org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration; import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration; +import org.springframework.cloud.sleuth.test.TestSpanHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; @@ -92,17 +77,14 @@ import org.springframework.web.client.RestTemplate; import org.springframework.web.reactive.function.client.UnknownHttpStatusCodeException; import org.springframework.web.reactive.function.client.WebClient; -import static brave.Span.Kind.CLIENT; -import static brave.propagation.B3Propagation.Format.SINGLE_NO_PARENT; import static org.assertj.core.api.Assertions.fail; import static org.assertj.core.api.BDDAssertions.then; -@SpringBootTest(classes = WebClientTests.TestConfiguration.class, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = WebClientTests.TestConfiguration.class) @TestPropertySource( properties = { "spring.application.name=fooservice", "spring.sleuth.web.client.skip-pattern=/skip.*" }) @DirtiesContext -public class WebClientTests { +public abstract class WebClientTests { private static final Log log = LogFactory.getLog(WebClientTests.class); @@ -119,12 +101,6 @@ public class WebClientTests { @Autowired WebClient.Builder webClientBuilder; - @Autowired - HttpClientBuilder httpClientBuilder; // #845 - - @Autowired - HttpAsyncClientBuilder httpAsyncClientBuilder; // #845 - @Autowired TestSpanHandler spans; @@ -163,8 +139,9 @@ public class WebClientTests { Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAsserted(() -> { then(getHeader(response, "b3")).isNull(); then(this.spans).isNotEmpty(); - Optional noTraceSpan = this.spans.spans().stream().filter( - span -> "GET".equals(span.name()) && !span.tags().isEmpty() && span.tags().containsKey("http.path")) + Optional noTraceSpan = this.spans.reportedSpans().stream() + .filter(span -> span.name().contains("GET") && !span.tags().isEmpty() + && span.tags().containsKey("http.path")) .findFirst(); then(noTraceSpan.isPresent()).isTrue(); then(noTraceSpan.get().tags()).containsEntry("http.path", "/notrace").containsEntry("http.method", "GET"); @@ -202,38 +179,13 @@ public class WebClientTests { String.class)); } - @ParameterizedTest - @MethodSource("parametersForShouldPropagateNotSamplingHeader") - @SuppressWarnings("unchecked") - public void shouldPropagateNotSamplingHeader(ResponseEntityProvider provider) { - Span span = this.tracer.nextSpan(TraceContextOrSamplingFlags.create(SamplingFlags.NOT_SAMPLED)).name("foo") - .start(); - - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) { - ResponseEntity> response = provider.get(this); - - then(response.getBody().get("b3")).isNotNull().endsWith("-0"); // not sampled - } - finally { - span.finish(); - } - - then(this.spans).isEmpty(); - then(this.tracer.currentSpan()).isNull(); - } - - static Stream parametersForShouldPropagateNotSamplingHeader() throws Exception { - return Stream.of((ResponseEntityProvider) (tests) -> tests.testFeignInterface.headers(), - (ResponseEntityProvider) (tests) -> tests.template.getForEntity("http://fooservice/", Map.class)); - } - @ParameterizedTest @MethodSource("parametersForShouldAttachTraceIdWhenCallingAnotherService") @SuppressWarnings("unchecked") public void shouldAttachTraceIdWhenCallingAnotherService(ResponseEntityProvider provider) { Span span = this.tracer.nextSpan().name("foo").start(); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span)) { ResponseEntity response = provider.get(this); // https://github.com/spring-cloud/spring-cloud-sleuth/issues/327 @@ -241,80 +193,28 @@ public class WebClientTests { then(getHeader(response, "b3")).isNull(); } finally { - span.finish(); + span.end(); } then(this.tracer.currentSpan()).isNull(); then(this.spans).isNotEmpty(); } - @Test - @SuppressWarnings("unchecked") - public void shouldAttachTraceIdWhenCallingAnotherServiceForHttpClient() throws Exception { - Span span = this.tracer.nextSpan().name("foo").start(); - - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) { - String response = this.httpClientBuilder.build().execute(new HttpGet("http://localhost:" + this.port), - new BasicResponseHandler()); - - then(response).isNotEmpty(); - } - - then(this.tracer.currentSpan()).isNull(); - then(this.spans).isNotEmpty().extracting("traceId", String.class).containsOnly(span.context().traceIdString()); - then(this.spans).extracting("kind.name").contains("CLIENT"); - } - - @Test - @SuppressWarnings("unchecked") - public void shouldAttachTraceIdWhenCallingAnotherServiceForAsyncHttpClient() throws Exception { - Span span = this.tracer.nextSpan().name("foo").start(); - - CloseableHttpAsyncClient client = this.httpAsyncClientBuilder.build(); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) { - client.start(); - Future future = client.execute(new HttpGet("http://localhost:" + this.port), - new FutureCallback() { - @Override - public void completed(HttpResponse result) { - - } - - @Override - public void failed(Exception ex) { - - } - - @Override - public void cancelled() { - - } - }); - then(future.get()).isNotNull(); - } - finally { - client.close(); - } - - then(this.tracer.currentSpan()).isNull(); - then(this.spans).isNotEmpty().extracting("traceId", String.class).containsOnly(span.context().traceIdString()); - then(this.spans).extracting("kind.name").contains("CLIENT"); - } - @Test @SuppressWarnings("unchecked") public void shouldAttachTraceIdWhenCallingAnotherServiceViaWebClient() { Span span = this.tracer.nextSpan().name("foo").start(); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span)) { this.webClient.get().uri("http://localhost:" + this.port + "/traceid").retrieve().bodyToMono(String.class) - .block(); + .block(Duration.ofMillis(100)); } finally { - span.finish(); + span.end(); } then(this.tracer.currentSpan()).isNull(); - then(this.spans).isNotEmpty().extracting("kind.name").contains("CLIENT"); + then(this.spans.reportedSpans().stream().filter(r -> r.kind() != null).map(r -> r.kind().name()) + .collect(Collectors.toList())).isNotEmpty().contains("CLIENT"); } @Test @@ -322,19 +222,20 @@ public class WebClientTests { public void shouldWorkWhenCustomStatusCodeIsReturned() { Span span = this.tracer.nextSpan().name("foo").start(); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span)) { this.webClient.get().uri("http://localhost:" + this.port + "/issue1462").retrieve().bodyToMono(String.class) - .block(); + .block(Duration.ofSeconds(1)); } catch (UnknownHttpStatusCodeException ex) { } finally { - span.finish(); + span.end(); } then(this.tracer.currentSpan()).isNull(); - then(this.spans).isNotEmpty().extracting("kind.name").contains("CLIENT"); + then(this.spans.reportedSpans().stream().filter(r -> r.kind() != null).map(r -> r.kind().name()) + .collect(Collectors.toList())).isNotEmpty().contains("CLIENT"); } /** @@ -357,11 +258,12 @@ public class WebClientTests { @Test public void shouldRespectSkipPattern() { - this.webClient.get().uri("http://localhost:" + this.port + "/skip").retrieve().bodyToMono(String.class).block(); + this.webClient.get().uri("http://localhost:" + this.port + "/skip").retrieve().bodyToMono(String.class) + .block(Duration.ofSeconds(1)); then(this.spans).isEmpty(); this.webClient.get().uri("http://localhost:" + this.port + "/doNotSkip").retrieve().bodyToMono(String.class) - .block(); + .block(Duration.ofSeconds(1)); then(this.spans).isNotEmpty(); } @@ -376,11 +278,11 @@ public class WebClientTests { public void shouldAttachTraceIdWhenUsingFeignClientWithoutResponseBody(ResponseEntityProvider provider) { Span span = this.tracer.nextSpan().name("foo").start(); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span)) { provider.get(this); } finally { - span.finish(); + span.end(); } then(this.tracer.currentSpan()).isNull(); @@ -403,18 +305,19 @@ public class WebClientTests { } then(this.tracer.currentSpan()).isNull(); - Optional storedSpan = this.spans.spans().stream() + Optional storedSpan = this.spans.reportedSpans().stream() .filter(span -> "404".equals(span.tags().get("http.status_code"))).findFirst(); then(storedSpan.isPresent()).isTrue(); - this.spans.spans().stream().forEach(span -> { - int initialSize = span.annotations().size(); - int distinctSize = span.annotations().stream().map(Map.Entry::getValue).distinct() - .collect(Collectors.toList()).size(); - log.info("logs " + span.annotations()); + this.spans.reportedSpans().stream().forEach(span -> { + int initialSize = span.events().size(); + int distinctSize = span.events().stream().map(Map.Entry::getValue).distinct().collect(Collectors.toList()) + .size(); + log.info("logs " + span.events()); then(initialSize).as("there are no duplicate log entries").isEqualTo(distinctSize); }); - then(this.spans).isNotEmpty().extracting("kind.name").contains("CLIENT"); + then(this.spans.reportedSpans().stream().filter(r -> r.kind() != null).map(r -> r.kind().name()) + .collect(Collectors.toList())).isNotEmpty().contains("CLIENT"); } @Test @@ -429,13 +332,13 @@ public class WebClientTests { public void should_wrap_rest_template_builders() { Span span = this.tracer.nextSpan().name("foo").start(); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span)) { RestTemplate template = this.restTemplateBuilder.build(); template.getForObject("http://localhost:" + this.port + "/traceid", String.class); } finally { - span.finish(); + span.end(); } then(this.tracer.currentSpan()).isNull(); then(this.customizer.isExecuted()).isTrue(); @@ -447,16 +350,16 @@ public class WebClientTests { Span span = this.tracer.nextSpan().name("foo").start(); AtomicReference traceId = new AtomicReference<>(); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) { + try (Tracer.SpanInScope ws = this.tracer.withSpan(span)) { this.webClientBuilder.filter((request, exchange) -> { traceId.set(request.headers().getFirst("b3")); return exchange.exchange(request); }).build().get().uri("http://localhost:" + this.port + "/traceid").retrieve().bodyToMono(String.class) - .block(); + .block(Duration.ofMillis(100)); } finally { - span.finish(); + span.end(); } then(traceId).doesNotHaveValue(null); } @@ -491,21 +394,12 @@ public class WebClientTests { } - @Configuration - @EnableAutoConfiguration( - excludeName = "org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration", - exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class }) + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration(exclude = { TraceWebServletAutoConfiguration.class, JmxAutoConfiguration.class }) @EnableFeignClients @LoadBalancerClient(value = "fooservice", configuration = SimpleLoadBalancerClientConfiguration.class) public static class TestConfiguration { - @Bean - BaggagePropagation.FactoryBuilder baggagePropagationFactoryBuilder() { - // Use b3 single format as it is less verbose - return BaggagePropagation.newFactoryBuilder( - B3Propagation.newFactoryBuilder().injectFormat(CLIENT, SINGLE_NO_PARENT).build()); - } - @Bean FooController fooController() { return new FooController(); @@ -523,18 +417,8 @@ public class WebClientTests { } @Bean - Sampler testSampler() { - return Sampler.ALWAYS_SAMPLE; - } - - @Bean - TestErrorController testErrorController(ErrorAttributes errorAttributes, Tracing tracer) { - return new TestErrorController(errorAttributes, tracer.tracer()); - } - - @Bean - SpanHandler testSpanHandler() { - return new TestSpanHandler(); + TestErrorController testErrorController(ErrorAttributes errorAttributes, Tracer tracer) { + return new TestErrorController(errorAttributes, tracer); } @Bean @@ -609,8 +493,7 @@ public class WebClientTests { @RequestMapping(value = "/traceid", method = RequestMethod.GET) public String traceId(@RequestHeader("b3") String b3Single) { - TraceContextOrSamplingFlags traceContext = B3SingleFormat.parseB3SingleFormat(b3Single); - then(traceContext.context()).isNotNull(); + then(b3Single).isNotEmpty(); return b3Single; } @@ -625,8 +508,7 @@ public class WebClientTests { @RequestMapping("/noresponse") public void noResponse(@RequestHeader("b3") String b3Single) { - TraceContextOrSamplingFlags traceContext = B3SingleFormat.parseB3SingleFormat(b3Single); - then(traceContext.context()).isNotNull(); + then(b3Single).isNotEmpty(); } public Span getSpan() { @@ -644,7 +526,6 @@ public class WebClientTests { @RequestMapping(value = "/issue1462", method = RequestMethod.GET) public ResponseEntity issue1462() { - System.out.println("GOT IT"); return ResponseEntity.status(499).body("issue1462"); } @@ -655,7 +536,7 @@ public class WebClientTests { } - @Configuration + @Configuration(proxyBeanMethods = false) public static class SimpleLoadBalancerClientConfiguration { @Value("${local.server.port}") diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/otel/OtelTestSpanHandler.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/otel/OtelTestSpanHandler.java new file mode 100644 index 000000000..d1037412f --- /dev/null +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/otel/OtelTestSpanHandler.java @@ -0,0 +1,125 @@ +/* + * Copyright 2013-2020 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.otel; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.trace.ReadWriteSpan; +import io.opentelemetry.sdk.trace.ReadableSpan; +import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import org.jetbrains.annotations.NotNull; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.otel.bridge.OtelFinishedSpan; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.cloud.sleuth.test.TestSpanHandler; + +public class OtelTestSpanHandler implements TestSpanHandler, SpanProcessor, SpanExporter { + + private final ArrayListSpanProcessor spanProcessor; + + public OtelTestSpanHandler(ArrayListSpanProcessor spanProcessor) { + this.spanProcessor = spanProcessor; + } + + @Override + public List reportedSpans() { + return spanProcessor.spans().stream().map(OtelFinishedSpan::new).collect(Collectors.toList()); + } + + @Override + public FinishedSpan takeLocalSpan() { + return new OtelFinishedSpan(spanProcessor.takeLocalSpan()); + } + + @Override + public void clear() { + spanProcessor.clear(); + } + + @Override + public FinishedSpan takeRemoteSpan(Span.Kind kind) { + return reportedSpans().stream().filter(s -> s.kind().name().equals(kind.name())).findFirst() + .orElseThrow(() -> new AssertionError("No span with kind [" + kind.name() + "] found.")); + } + + @Override + public FinishedSpan takeRemoteSpanWithError(Span.Kind kind) { + return reportedSpans().stream().filter(s -> s.kind().name().equals(kind.name()) && s.error() != null) + .findFirst() + .orElseThrow(() -> new AssertionError("No span with kind [" + kind.name() + "] and error found.")); + } + + @Override + public FinishedSpan get(int index) { + return reportedSpans().get(index); + } + + @NotNull + @Override + public Iterator iterator() { + return reportedSpans().iterator(); + } + + @Override + public void onStart(ReadWriteSpan span) { + spanProcessor.onStart(span); + } + + @Override + public boolean isStartRequired() { + return spanProcessor.isStartRequired(); + } + + @Override + public void onEnd(ReadableSpan span) { + spanProcessor.onEnd(span); + } + + @Override + public boolean isEndRequired() { + return spanProcessor.isEndRequired(); + } + + @Override + public CompletableResultCode export(Collection spans) { + return spanProcessor.export(spans); + } + + @Override + public CompletableResultCode flush() { + return spanProcessor.flush(); + } + + @Override + public CompletableResultCode shutdown() { + return spanProcessor.shutdown(); + } + + @Override + public CompletableResultCode forceFlush() { + return spanProcessor.forceFlush(); + } + +} diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/otel/OtelTestTracing.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/otel/OtelTestTracing.java new file mode 100644 index 000000000..013a7db36 --- /dev/null +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/otel/OtelTestTracing.java @@ -0,0 +1,167 @@ +/* + * Copyright 2013-2020 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.otel; + +import java.io.Closeable; +import java.util.regex.Pattern; + +import io.opentelemetry.OpenTelemetry; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.context.propagation.DefaultContextPropagators; +import io.opentelemetry.extensions.trace.propagation.B3Propagator; +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; +import io.opentelemetry.sdk.trace.TracerSdkProvider; +import io.opentelemetry.sdk.trace.config.TraceConfig; +import org.jetbrains.annotations.NotNull; + +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.SamplerFunction; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; +import org.springframework.cloud.sleuth.api.http.HttpRequestParser; +import org.springframework.cloud.sleuth.api.http.HttpServerHandler; +import org.springframework.cloud.sleuth.api.propagation.Propagator; +import org.springframework.cloud.sleuth.autoconfig.SleuthBaggageProperties; +import org.springframework.cloud.sleuth.otel.bridge.OtelBaggageManager; +import org.springframework.cloud.sleuth.otel.bridge.OtelCurrentTraceContext; +import org.springframework.cloud.sleuth.otel.bridge.OtelPropagator; +import org.springframework.cloud.sleuth.otel.bridge.OtelTracer; +import org.springframework.cloud.sleuth.otel.bridge.http.OtelHttpClientHandler; +import org.springframework.cloud.sleuth.otel.bridge.http.OtelHttpServerHandler; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.cloud.sleuth.test.TestTracingAssertions; +import org.springframework.cloud.sleuth.test.TestTracingAware; +import org.springframework.cloud.sleuth.test.TestTracingAwareSupplier; +import org.springframework.cloud.sleuth.test.TracerAware; +import org.springframework.context.ApplicationEventPublisher; + +public class OtelTestTracing implements TracerAware, TestTracingAware, TestTracingAwareSupplier, Closeable { + + ArrayListSpanProcessor spanProcessor = new ArrayListSpanProcessor(); + + ContextPropagators defaultContextPropagators = OpenTelemetry.getPropagators(); + + ContextPropagators contextPropagators = contextPropagators(); + + Sampler sampler = Samplers.alwaysOn(); + + HttpRequestParser clientRequestParser; + + io.opentelemetry.trace.Tracer tracer = otelTracer(); + + CurrentTraceContext currentTraceContext = new OtelCurrentTraceContext(this.tracer, publisher()); + + OtelBaggageManager otelBaggageManager = new OtelBaggageManager(this.tracer, OpenTelemetry.getBaggageManager(), + new SleuthBaggageProperties(), publisher()); + + io.opentelemetry.trace.Tracer otelTracer() { + TracerSdkProvider provider = TracerSdkProvider.builder().build(); + provider.addSpanProcessor(this.spanProcessor); + OpenTelemetry.setPropagators(this.contextPropagators); + provider.updateActiveTraceConfig(TraceConfig.getDefault().toBuilder().setSampler(this.sampler).build()); + return provider.get("org.springframework.cloud.sleuth"); + } + + @NotNull + protected ContextPropagators contextPropagators() { + return DefaultContextPropagators.builder().addTextMapPropagator(B3Propagator.getMultipleHeaderPropagator()) + .addTextMapPropagator(B3Propagator.getSingleHeaderPropagator()).build(); + } + + private void reset() { + this.tracer = otelTracer(); + this.currentTraceContext = new OtelCurrentTraceContext(this.tracer, publisher()); + } + + @Override + public TracerAware sampler(TraceSampler sampler) { + this.sampler = sampler == TraceSampler.ON ? Samplers.alwaysOn() : Samplers.alwaysOff(); + return this; + } + + @Override + public TracerAware tracing() { + return this; + } + + @Override + public TestSpanHandler handler() { + return new OtelTestSpanHandler(this.spanProcessor); + } + + @Override + public TestTracingAssertions assertions() { + return new OtelTestTracingAssertions(); + } + + @Override + public void close() { + this.spanProcessor.clear(); + OpenTelemetry.setPropagators(this.defaultContextPropagators); + this.sampler = Samplers.alwaysOn(); + } + + @Override + public TestTracingAware tracerTest() { + return this; + } + + @Override + public Tracer tracer() { + reset(); + return OtelTracer.fromOtel(this.tracer, this.otelBaggageManager); + } + + @Override + public CurrentTraceContext currentTraceContext() { + reset(); + return new OtelCurrentTraceContext(this.tracer, publisher()); + } + + @Override + public Propagator propagator() { + reset(); + return new OtelPropagator(this.contextPropagators, this.tracer); + } + + @Override + public HttpServerHandler httpServerHandler() { + reset(); + return new OtelHttpServerHandler(this.tracer, null, null, () -> Pattern.compile("")); + } + + @Override + public TracerAware clientRequestParser(HttpRequestParser httpRequestParser) { + this.clientRequestParser = httpRequestParser; + return this; + } + + @Override + public HttpClientHandler httpClientHandler() { + reset(); + return new OtelHttpClientHandler(this.tracer, this.clientRequestParser, null, SamplerFunction.alwaysSample()); + } + + ApplicationEventPublisher publisher() { + return event -> { + + }; + } + +} diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/otel/OtelTestTracingAssertions.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/otel/OtelTestTracingAssertions.java new file mode 100644 index 000000000..a320dba31 --- /dev/null +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/otel/OtelTestTracingAssertions.java @@ -0,0 +1,36 @@ +/* + * Copyright 2013-2020 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.otel; + +import org.assertj.core.api.BDDAssertions; + +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.test.TestTracingAssertions; + +public class OtelTestTracingAssertions implements TestTracingAssertions { + + @Override + public void assertThatNoParentPresent(FinishedSpan finishedSpan) { + BDDAssertions.then(Long.valueOf(finishedSpan.parentId())).isEqualTo(0L); + } + + @Override + public String or128Bit(String id) { + return "0000000000000000" + id; + } + +} diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/test/TestSpanHandler.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/test/TestSpanHandler.java new file mode 100644 index 000000000..d64669cc2 --- /dev/null +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/test/TestSpanHandler.java @@ -0,0 +1,38 @@ +/* + * Copyright 2013-2020 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.test; + +import java.util.List; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; + +public interface TestSpanHandler extends Iterable { + + List reportedSpans(); + + FinishedSpan takeLocalSpan(); + + void clear(); + + FinishedSpan takeRemoteSpan(Span.Kind kind); + + FinishedSpan takeRemoteSpanWithError(Span.Kind kind); + + FinishedSpan get(int index); + +} diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/test/TestTracingAssertions.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/test/TestTracingAssertions.java new file mode 100644 index 000000000..6df09b6bf --- /dev/null +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/test/TestTracingAssertions.java @@ -0,0 +1,29 @@ +/* + * Copyright 2013-2020 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.test; + +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; + +public interface TestTracingAssertions { + + void assertThatNoParentPresent(FinishedSpan finishedSpan); + + default String or128Bit(String id) { + return id; + } + +} diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/test/TestTracingAware.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/test/TestTracingAware.java new file mode 100644 index 000000000..526572d45 --- /dev/null +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/test/TestTracingAware.java @@ -0,0 +1,32 @@ +/* + * Copyright 2013-2020 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.test; + +import java.io.Closeable; + +public interface TestTracingAware extends Closeable { + + TracerAware tracing(); + + TestSpanHandler handler(); + + TestTracingAssertions assertions(); + + @Override + void close(); + +} diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/test/TestTracingAwareSupplier.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/test/TestTracingAwareSupplier.java new file mode 100644 index 000000000..50b1da1b7 --- /dev/null +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/test/TestTracingAwareSupplier.java @@ -0,0 +1,30 @@ +/* + * Copyright 2013-2020 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.test; + +import org.junit.jupiter.api.AfterEach; + +public interface TestTracingAwareSupplier { + + TestTracingAware tracerTest(); + + @AfterEach + default void cleanUpTracing() { + tracerTest().close(); + } + +} diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/test/TracerAware.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/test/TracerAware.java new file mode 100644 index 000000000..3f1f7d8d9 --- /dev/null +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/test/TracerAware.java @@ -0,0 +1,48 @@ +/* + * Copyright 2013-2020 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.test; + +import org.springframework.cloud.sleuth.api.CurrentTraceContext; +import org.springframework.cloud.sleuth.api.Tracer; +import org.springframework.cloud.sleuth.api.http.HttpClientHandler; +import org.springframework.cloud.sleuth.api.http.HttpRequestParser; +import org.springframework.cloud.sleuth.api.http.HttpServerHandler; +import org.springframework.cloud.sleuth.api.propagation.Propagator; + +public interface TracerAware { + + Tracer tracer(); + + TracerAware sampler(TraceSampler sampler); + + CurrentTraceContext currentTraceContext(); + + Propagator propagator(); + + HttpServerHandler httpServerHandler(); + + TracerAware clientRequestParser(HttpRequestParser httpRequestParser); + + HttpClientHandler httpClientHandler(); + + enum TraceSampler { + + ON, OFF + + } + +} diff --git a/tests/common/src/main/resources/application-baggage.yml b/tests/common/src/main/resources/application-baggage.yml new file mode 100644 index 000000000..0b0c68731 --- /dev/null +++ b/tests/common/src/main/resources/application-baggage.yml @@ -0,0 +1,9 @@ +spring: + sleuth: + baggage: + foo: bar + remoteFields: + - country-code + - x-vcap-request-id + tagFields: + - country-code diff --git a/tests/otel/pom.xml b/tests/otel/pom.xml new file mode 100644 index 000000000..1530db10f --- /dev/null +++ b/tests/otel/pom.xml @@ -0,0 +1,67 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-otel-tests + pom + Spring Cloud Sleuth Otel Tests + Spring Cloud Sleuth Otel Tests + + + org.springframework.cloud + spring-cloud-sleuth-tests + 3.0.0-SNAPSHOT + .. + + + + spring-cloud-sleuth-instrumentation-annotation-tests + spring-cloud-sleuth-instrumentation-async-tests + spring-cloud-sleuth-instrumentation-baggage-tests + spring-cloud-sleuth-instrumentation-circuitbreaker-tests + spring-cloud-sleuth-instrumentation-feign-tests + spring-cloud-sleuth-instrumentation-gateway-tests + spring-cloud-sleuth-instrumentation-messaging-tests + spring-cloud-sleuth-instrumentation-mvc-tests + spring-cloud-sleuth-instrumentation-quartz-tests + spring-cloud-sleuth-instrumentation-reactor-tests + spring-cloud-sleuth-instrumentation-rxjava-tests + spring-cloud-sleuth-instrumentation-scheduling-tests + spring-cloud-sleuth-instrumentation-webflux-tests + + + + + + + + maven-deploy-plugin + + true + + + + + + + diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/pom.xml b/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/pom.xml new file mode 100644 index 000000000..0bd4a2c4a --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/pom.xml @@ -0,0 +1,85 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-instrumentation-annotation-otel-tests + jar + Spring Cloud Sleuth Otel Annotation Instrumentation Tests + Spring Cloud Sleuth Otel Annotation Instrumentation Tests + + + org.springframework.cloud + spring-cloud-sleuth-otel-tests + 3.0.0-SNAPSHOT + .. + + + + true + + + + + + + maven-deploy-plugin + + true + + + + + + + + org.springframework.cloud + spring-cloud-sleuth-tests-common + ${project.version} + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.boot + spring-boot-starter-webflux + test + + + org.springframework.cloud + spring-cloud-sleuth-otel + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.awaitility + awaitility + test + + + + diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/otel/annotation/NullSpanTagAnnotationHandlerTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/otel/annotation/NullSpanTagAnnotationHandlerTests.java new file mode 100644 index 000000000..824e2923d --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/otel/annotation/NullSpanTagAnnotationHandlerTests.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2020 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.otel.annotation; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = NullSpanTagAnnotationHandlerTests.Config.class) +public class NullSpanTagAnnotationHandlerTests + extends org.springframework.cloud.sleuth.annotation.NullSpanTagAnnotationHandlerTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/otel/annotation/SleuthSpanCreatorAspectFluxTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/otel/annotation/SleuthSpanCreatorAspectFluxTests.java new file mode 100644 index 000000000..01f42d1d3 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/otel/annotation/SleuthSpanCreatorAspectFluxTests.java @@ -0,0 +1,62 @@ +/* + * Copyright 2013-2020 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.otel.annotation; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; +import io.opentelemetry.trace.SpanContext; +import io.opentelemetry.trace.SpanId; +import io.opentelemetry.trace.TraceFlags; +import io.opentelemetry.trace.TraceId; +import io.opentelemetry.trace.TraceState; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.bridge.OtelTraceContext; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = SleuthSpanCreatorAspectFluxTests.Config.class) +public class SleuthSpanCreatorAspectFluxTests + extends org.springframework.cloud.sleuth.annotation.SleuthSpanCreatorAspectFluxTests { + + @Override + public TraceContext traceContext() { + return OtelTraceContext.fromOtel(SpanContext.create(TraceId.fromLongs(1L, 0L), SpanId.fromLong(2L), + TraceFlags.getSampled(), TraceState.builder().build())); + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/otel/annotation/SleuthSpanCreatorAspectMonoTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/otel/annotation/SleuthSpanCreatorAspectMonoTests.java new file mode 100644 index 000000000..a3075e556 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/otel/annotation/SleuthSpanCreatorAspectMonoTests.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2020 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.otel.annotation; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = SleuthSpanCreatorAspectMonoTests.Config.class) +public class SleuthSpanCreatorAspectMonoTests + extends org.springframework.cloud.sleuth.annotation.SleuthSpanCreatorAspectMonoTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/otel/annotation/SleuthSpanCreatorAspectNegativeTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/otel/annotation/SleuthSpanCreatorAspectNegativeTests.java new file mode 100644 index 000000000..dc994af3c --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/otel/annotation/SleuthSpanCreatorAspectNegativeTests.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2020 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.otel.annotation; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = SleuthSpanCreatorAspectNegativeTests.Config.class) +public class SleuthSpanCreatorAspectNegativeTests + extends org.springframework.cloud.sleuth.annotation.SleuthSpanCreatorAspectNegativeTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/otel/annotation/SleuthSpanCreatorAspectTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/otel/annotation/SleuthSpanCreatorAspectTests.java new file mode 100644 index 000000000..01d6b0036 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/otel/annotation/SleuthSpanCreatorAspectTests.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2020 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.otel.annotation; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = SleuthSpanCreatorAspectTests.Config.class) +public class SleuthSpanCreatorAspectTests + extends org.springframework.cloud.sleuth.annotation.SleuthSpanCreatorAspectTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/otel/annotation/SleuthSpanCreatorCircularDependencyTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/otel/annotation/SleuthSpanCreatorCircularDependencyTests.java new file mode 100644 index 000000000..54ec6fdea --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/otel/annotation/SleuthSpanCreatorCircularDependencyTests.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2020 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.otel.annotation; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = SleuthSpanCreatorCircularDependencyTests.Config.class) +public class SleuthSpanCreatorCircularDependencyTests + extends org.springframework.cloud.sleuth.annotation.SleuthSpanCreatorCircularDependencyTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/otel/annotation/SpanTagAnnotationHandlerTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/otel/annotation/SpanTagAnnotationHandlerTests.java new file mode 100644 index 000000000..5c0b53ade --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/java/org/springframework/cloud/sleuth/otel/annotation/SpanTagAnnotationHandlerTests.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2020 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.otel.annotation; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = SpanTagAnnotationHandlerTests.Config.class) +public class SpanTagAnnotationHandlerTests + extends org.springframework.cloud.sleuth.annotation.SpanTagAnnotationHandlerTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/resources/application.yml b/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/resources/application.yml new file mode 100644 index 000000000..caf171df7 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-annotation-tests/src/test/resources/application.yml @@ -0,0 +1,5 @@ +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE + +spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.cloud.gateway.config.GatewayAutoConfiguration, org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration, org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration \ No newline at end of file diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/pom.xml b/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/pom.xml new file mode 100644 index 000000000..654abc456 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/pom.xml @@ -0,0 +1,92 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-instrumentation-async-otel-tests + jar + Spring Cloud Sleuth Otel Async Instrumentation Tests + Spring Cloud Sleuth Otel Async Instrumentation Tests + + + org.springframework.cloud + spring-cloud-sleuth-otel-tests + 3.0.0-SNAPSHOT + .. + + + + true + + + + + + + maven-deploy-plugin + + true + + + + + + + + ${project.groupId} + spring-cloud-sleuth-tests-common + ${project.version} + + + org.springframework.boot + spring-boot-starter-web + + + io.opentelemetry + opentelemetry-extension-trace-propagators + + + org.springframework.cloud + spring-cloud-sleuth-core + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.cloud + spring-cloud-sleuth-otel + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.awaitility + awaitility + test + + + + diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/AsyncDisabledTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/AsyncDisabledTests.java new file mode 100644 index 000000000..2d144519e --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/AsyncDisabledTests.java @@ -0,0 +1,24 @@ +/* + * Copyright 2013-2020 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.otel.instrument.async; + +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +public class AsyncDisabledTests extends org.springframework.cloud.sleuth.instrument.async.AsyncDisabledTests { + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/LazyTraceThreadPoolTaskSchedulerTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/LazyTraceThreadPoolTaskSchedulerTests.java new file mode 100644 index 000000000..9fbb9a0d8 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/LazyTraceThreadPoolTaskSchedulerTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2020 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.otel.instrument.async; + +import org.springframework.cloud.sleuth.otel.OtelTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +public class LazyTraceThreadPoolTaskSchedulerTests + extends org.springframework.cloud.sleuth.instrument.async.LazyTraceThreadPoolTaskSchedulerTests { + + OtelTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new OtelTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/TraceAsyncAspectTest.java b/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/TraceAsyncAspectTest.java new file mode 100644 index 000000000..5441f3ef4 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/TraceAsyncAspectTest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2013-2020 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.otel.instrument.async; + +import org.springframework.cloud.sleuth.otel.OtelTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +/** + * @author Marcin Grzejszczak + */ +public class TraceAsyncAspectTest extends org.springframework.cloud.sleuth.instrument.async.TraceAsyncAspectTest { + + OtelTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new OtelTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/TraceAsyncListenableTaskExecutorTest.java b/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/TraceAsyncListenableTaskExecutorTest.java new file mode 100644 index 000000000..ccdd9904f --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/TraceAsyncListenableTaskExecutorTest.java @@ -0,0 +1,38 @@ +/* + * Copyright 2013-2020 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.otel.instrument.async; + +import org.springframework.cloud.sleuth.otel.OtelTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +/** + * @author Marcin Grzejszczak + */ +public class TraceAsyncListenableTaskExecutorTest + extends org.springframework.cloud.sleuth.instrument.async.TraceAsyncListenableTaskExecutorTest { + + OtelTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new OtelTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/TraceCallableTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/TraceCallableTests.java new file mode 100644 index 000000000..37bc6ac43 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/TraceCallableTests.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2020 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.otel.instrument.async; + +import org.springframework.cloud.sleuth.otel.OtelTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +public class TraceCallableTests extends org.springframework.cloud.sleuth.instrument.async.TraceCallableTests { + + OtelTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new OtelTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/TraceRunnableTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/TraceRunnableTests.java new file mode 100644 index 000000000..64f4bc2dc --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/TraceRunnableTests.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2020 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.otel.instrument.async; + +import io.opentelemetry.trace.DefaultSpan; +import org.assertj.core.api.BDDAssertions; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.otel.OtelTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +public class TraceRunnableTests extends org.springframework.cloud.sleuth.instrument.async.TraceRunnableTests { + + OtelTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new OtelTestTracing(); + } + return this.testTracing; + } + + @Override + protected void assertThatThereIsNoParentId(Span secondSpan) { + BDDAssertions.then(secondSpan.context().parentId()).as("saved span as remnant of first span") + .isEqualTo(DefaultSpan.getInvalid().getContext().getSpanIdAsHexString()); + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/TraceableExecutorServiceTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/TraceableExecutorServiceTests.java new file mode 100644 index 000000000..501a4ec76 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/TraceableExecutorServiceTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2020 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.otel.instrument.async; + +import org.springframework.cloud.sleuth.otel.OtelTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +public class TraceableExecutorServiceTests + extends org.springframework.cloud.sleuth.instrument.async.TraceableExecutorServiceTests { + + OtelTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new OtelTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/TraceableScheduledExecutorServiceTest.java b/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/TraceableScheduledExecutorServiceTest.java new file mode 100644 index 000000000..91d833350 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/async/TraceableScheduledExecutorServiceTest.java @@ -0,0 +1,38 @@ +/* + * Copyright 2013-2020 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.otel.instrument.async; + +import org.springframework.cloud.sleuth.otel.OtelTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +/** + * @author Marcin Grzejszczak + */ +public class TraceableScheduledExecutorServiceTest + extends org.springframework.cloud.sleuth.instrument.async.TraceableScheduledExecutorServiceTest { + + OtelTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new OtelTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/resources/application.yml b/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/resources/application.yml new file mode 100644 index 000000000..9ed06adbb --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-async-tests/src/test/resources/application.yml @@ -0,0 +1,3 @@ +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE \ No newline at end of file diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/pom.xml b/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/pom.xml new file mode 100644 index 000000000..7418faf9e --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/pom.xml @@ -0,0 +1,92 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-instrumentation-baggage-otel-tests + jar + Spring Cloud Sleuth Otel Baggage Instrumentation Tests + Spring Cloud Sleuth Otel Baggage Instrumentation Tests + + + org.springframework.cloud + spring-cloud-sleuth-otel-tests + 3.0.0-SNAPSHOT + .. + + + + true + + + + + + + maven-deploy-plugin + + true + + + + + + + + org.springframework.cloud + spring-cloud-sleuth-tests-common + ${project.version} + + + io.opentelemetry + opentelemetry-extension-trace-propagators + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.integration + spring-integration-core + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.cloud + spring-cloud-sleuth-otel + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.awaitility + awaitility + test + + + + diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/otel/baggage/BaggageEntryTagSpanHandlerTest.java b/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/otel/baggage/BaggageEntryTagSpanHandlerTest.java new file mode 100644 index 000000000..47cb033ef --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/otel/baggage/BaggageEntryTagSpanHandlerTest.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2020 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.otel.baggage; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +/** + * @author Taras Danylchuk + */ +@SpringBootTest(// WebEnvironment.NONE will not read a Yaml profile + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = BaggageEntryTagSpanHandlerTest.Config.class) +public class BaggageEntryTagSpanHandlerTest + extends org.springframework.cloud.sleuth.baggage.BaggageEntryTagSpanHandlerTest { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/otel/baggage/MultipleHopsIntegrationTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/otel/baggage/MultipleHopsIntegrationTests.java new file mode 100644 index 000000000..ab0e1b4d1 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/otel/baggage/MultipleHopsIntegrationTests.java @@ -0,0 +1,95 @@ +/* + * Copyright 2013-2020 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.otel.baggage; + +import java.util.Queue; +import java.util.concurrent.LinkedBlockingQueue; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.bridge.OtelBaggageEntry; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +import static java.util.Arrays.asList; +import static org.assertj.core.api.BDDAssertions.then; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@SpringBootTest(webEnvironment = RANDOM_PORT) +@ContextConfiguration(classes = MultipleHopsIntegrationTests.Config.class) +public class MultipleHopsIntegrationTests + extends org.springframework.cloud.sleuth.baggage.multiple.MultipleHopsIntegrationTests { + + @Autowired + MyBaggageChangedListener myBaggageChangedListener; + + // TODO: Why do we have empty names here + @Override + protected void assertSpanNames() { + then(this.spans).extracting(FinishedSpan::name).containsAll(asList("HTTP GET", "handle", "send")); + } + + @Override + protected void assertBaggage(Span initialSpan) { + then(this.myBaggageChangedListener.baggageChanged).as("All have request ID") + .filteredOn(b -> b.name.equals(REQUEST_ID)) + .allMatch(event -> "f4308d05-2228-4468-80f6-92a8377ba193".equals(event.value)); + then(this.myBaggageChangedListener.baggageChanged).as("All have request ID") + .filteredOn(b -> b.name.equals(COUNTRY_CODE)).allMatch(event -> "FO".equals(event.value)); + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + @Bean + MyBaggageChangedListener myBaggageChangedListener() { + return new MyBaggageChangedListener(); + } + + } + +} + +class MyBaggageChangedListener implements ApplicationListener { + + Queue baggageChanged = new LinkedBlockingQueue<>(); + + @Override + public void onApplicationEvent(OtelBaggageEntry.BaggageChanged event) { + this.baggageChanged.add(event); + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/resources/application.yml b/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/resources/application.yml new file mode 100644 index 000000000..caf171df7 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/resources/application.yml @@ -0,0 +1,5 @@ +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE + +spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.cloud.gateway.config.GatewayAutoConfiguration, org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration, org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration \ No newline at end of file diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/pom.xml b/tests/otel/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/pom.xml new file mode 100644 index 000000000..1afb9ceb5 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/pom.xml @@ -0,0 +1,88 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-instrumentation-circuitbreaker-otel-tests + jar + Spring Cloud Sleuth Otel Circuitbreaker Instrumentation Tests + Spring Cloud Sleuth Otel Circuitbreaker Instrumentation Tests + + + org.springframework.cloud + spring-cloud-sleuth-otel-tests + 3.0.0-SNAPSHOT + .. + + + + true + + + + + + + maven-deploy-plugin + + true + + + + + + + + org.springframework.cloud + spring-cloud-sleuth-tests-common + ${project.version} + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.cloud + spring-cloud-starter-circuitbreaker-resilience4j + + + io.opentelemetry + opentelemetry-extension-trace-propagators + + + org.springframework.cloud + spring-cloud-sleuth-otel + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.awaitility + awaitility + test + + + + diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/circuitbreaker/CircuitBreakerIntegrationTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/circuitbreaker/CircuitBreakerIntegrationTests.java new file mode 100644 index 000000000..0d27054fe --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/circuitbreaker/CircuitBreakerIntegrationTests.java @@ -0,0 +1,60 @@ +/* + * Copyright 2013-2020 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.otel.instrument.circuitbreaker; + +import io.opentelemetry.common.AttributeKey; +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; +import org.assertj.core.api.BDDAssertions; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.bridge.OtelFinishedSpan; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = CircuitBreakerIntegrationTests.Config.class) +public class CircuitBreakerIntegrationTests + extends org.springframework.cloud.sleuth.instrument.circuitbreaker.CircuitBreakerIntegrationTests { + + @Override + public void assertException(FinishedSpan finishedSpan) { + OtelFinishedSpan.AssertingThrowable throwable = (OtelFinishedSpan.AssertingThrowable) finishedSpan.error(); + String msg = throwable.attributes.get(AttributeKey.stringKey("exception.message")); + BDDAssertions.then(msg).contains("boom"); + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/circuitbreaker/CircuitBreakerTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/circuitbreaker/CircuitBreakerTests.java new file mode 100644 index 000000000..1c86dc71a --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/circuitbreaker/CircuitBreakerTests.java @@ -0,0 +1,47 @@ +/* + * Copyright 2013-2020 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.otel.instrument.circuitbreaker; + +import io.opentelemetry.common.AttributeKey; +import org.assertj.core.api.BDDAssertions; + +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.otel.OtelTestTracing; +import org.springframework.cloud.sleuth.otel.bridge.OtelFinishedSpan; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +public class CircuitBreakerTests + extends org.springframework.cloud.sleuth.instrument.circuitbreaker.CircuitBreakerTests { + + OtelTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new OtelTestTracing(); + } + return this.testTracing; + } + + @Override + public void additionalAssertions(FinishedSpan finishedSpan) { + OtelFinishedSpan.AssertingThrowable throwable = (OtelFinishedSpan.AssertingThrowable) finishedSpan.error(); + String msg = throwable.attributes.get(AttributeKey.stringKey("exception.message")); + BDDAssertions.then(msg).contains("boom2"); + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/src/test/resources/application.yml b/tests/otel/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/src/test/resources/application.yml new file mode 100644 index 000000000..caf171df7 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-circuitbreaker-tests/src/test/resources/application.yml @@ -0,0 +1,5 @@ +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE + +spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.cloud.gateway.config.GatewayAutoConfiguration, org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration, org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration \ No newline at end of file diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-feign-tests/pom.xml b/tests/otel/spring-cloud-sleuth-instrumentation-feign-tests/pom.xml new file mode 100644 index 000000000..2af1ccf82 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-feign-tests/pom.xml @@ -0,0 +1,120 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-instrumentation-feign-otel-tests + jar + Spring Cloud Sleuth Otel Feign Instrumentation Tests + Spring Cloud Sleuth Otel Feign Instrumentation Tests + + + org.springframework.cloud + spring-cloud-sleuth-otel-tests + 3.0.0-SNAPSHOT + .. + + + + true + + + + + + + maven-deploy-plugin + + true + + + + + + + + ${project.groupId} + spring-cloud-sleuth-tests-common + ${project.version} + + + org.springframework.boot + spring-boot-starter-web + + + io.opentelemetry + opentelemetry-extension-trace-propagators + + + com.squareup.okhttp3 + mockwebserver + + + com.squareup.okhttp3 + okhttp + + 4.8.0 + compile + + + org.springframework.cloud + spring-cloud-sleuth-otel + test + + + org.springframework.cloud + spring-cloud-sleuth-core + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + org.springframework.cloud + spring-cloud-starter-circuitbreaker-resilience4j + + + io.github.openfeign + feign-okhttp + true + + + org.springframework.boot + spring-boot-starter-test + test + + + org.awaitility + awaitility + test + + + + diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/feign/FeignRetriesTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/feign/FeignRetriesTests.java new file mode 100644 index 000000000..ad54bce20 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/feign/FeignRetriesTests.java @@ -0,0 +1,48 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web.client.feign; + +import java.io.IOException; + +import io.opentelemetry.common.AttributeKey; +import org.assertj.core.api.BDDAssertions; + +import org.springframework.cloud.sleuth.otel.OtelTestTracing; +import org.springframework.cloud.sleuth.otel.bridge.OtelFinishedSpan; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +public class FeignRetriesTests extends org.springframework.cloud.sleuth.instrument.web.client.feign.FeignRetriesTests { + + OtelTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new OtelTestTracing(); + } + return this.testTracing; + } + + @Override + public void assertException() { + OtelFinishedSpan.AssertingThrowable throwable = (OtelFinishedSpan.AssertingThrowable) this.tracerTest() + .handler().reportedSpans().get(0).error(); + String type = throwable.attributes.get(AttributeKey.stringKey("exception.type")); + BDDAssertions.then(type).contains(IOException.class.getSimpleName()); + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/feign/TraceFeignAspectTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/feign/TraceFeignAspectTests.java new file mode 100644 index 000000000..ee475d329 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/feign/TraceFeignAspectTests.java @@ -0,0 +1,38 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web.client.feign; + +import org.springframework.cloud.sleuth.otel.OtelTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +/** + * @author Marcin Grzejszczak + */ +public class TraceFeignAspectTests + extends org.springframework.cloud.sleuth.instrument.web.client.feign.TraceFeignAspectTests { + + OtelTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new OtelTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/feign/TracingFeignClientTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/feign/TracingFeignClientTests.java new file mode 100644 index 000000000..a9376946a --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-feign-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/feign/TracingFeignClientTests.java @@ -0,0 +1,47 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web.client.feign; + +import io.opentelemetry.common.AttributeKey; +import org.assertj.core.api.BDDAssertions; + +import org.springframework.cloud.sleuth.otel.OtelTestTracing; +import org.springframework.cloud.sleuth.otel.bridge.OtelFinishedSpan; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +public class TracingFeignClientTests + extends org.springframework.cloud.sleuth.instrument.web.client.feign.TracingFeignClientTests { + + OtelTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new OtelTestTracing(); + } + return this.testTracing; + } + + @Override + public void assertException(RuntimeException error) { + OtelFinishedSpan.AssertingThrowable throwable = (OtelFinishedSpan.AssertingThrowable) this.tracerTest() + .handler().reportedSpans().get(0).error(); + String message = throwable.attributes.get(AttributeKey.stringKey("exception.message")); + BDDAssertions.then(message).isEqualTo(error.getMessage()); + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-feign-tests/src/test/resources/application.yml b/tests/otel/spring-cloud-sleuth-instrumentation-feign-tests/src/test/resources/application.yml new file mode 100644 index 000000000..b3b1e54dd --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-feign-tests/src/test/resources/application.yml @@ -0,0 +1,3 @@ +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-gateway-tests/pom.xml b/tests/otel/spring-cloud-sleuth-instrumentation-gateway-tests/pom.xml new file mode 100644 index 000000000..64cdd3aa0 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-gateway-tests/pom.xml @@ -0,0 +1,84 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-instrumentation-gateway-otel-tests + jar + Spring Cloud Sleuth Otel Gateway Instrumentation Tests + Spring Cloud Sleuth Otel Gateway Instrumentation Tests + + + org.springframework.cloud + spring-cloud-sleuth-otel-tests + 3.0.0-SNAPSHOT + .. + + + + true + + + + + + + maven-deploy-plugin + + true + + + + + + + + org.springframework.cloud + spring-cloud-sleuth-tests-common + ${project.version} + + + org.springframework.cloud + spring-cloud-starter-gateway + + + io.opentelemetry + opentelemetry-extension-trace-propagators + + + org.springframework.cloud + spring-cloud-sleuth-otel + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.awaitility + awaitility + test + + + + diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-gateway-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/TraceRequestHttpHeadersFilterTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-gateway-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/TraceRequestHttpHeadersFilterTests.java new file mode 100644 index 000000000..e1d49e5f7 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-gateway-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/TraceRequestHttpHeadersFilterTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web.client; + +import org.springframework.cloud.sleuth.otel.OtelTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +public class TraceRequestHttpHeadersFilterTests + extends org.springframework.cloud.sleuth.instrument.web.client.TraceRequestHttpHeadersFilterTests { + + OtelTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new OtelTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-gateway-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/TraceResponseHttpHeadersFilterTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-gateway-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/TraceResponseHttpHeadersFilterTests.java new file mode 100644 index 000000000..781236868 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-gateway-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/TraceResponseHttpHeadersFilterTests.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web.client; + +import org.springframework.cloud.sleuth.otel.OtelTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +public class TraceResponseHttpHeadersFilterTests + extends org.springframework.cloud.sleuth.instrument.web.client.TraceResponseHttpHeadersFilterTests { + + OtelTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new OtelTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-gateway-tests/src/test/resources/application.yml b/tests/otel/spring-cloud-sleuth-instrumentation-gateway-tests/src/test/resources/application.yml new file mode 100644 index 000000000..caf171df7 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-gateway-tests/src/test/resources/application.yml @@ -0,0 +1,5 @@ +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE + +spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.cloud.gateway.config.GatewayAutoConfiguration, org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration, org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration \ No newline at end of file diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/activemq-data/localhost/KahaDB/db.data b/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/activemq-data/localhost/KahaDB/db.data new file mode 100644 index 000000000..3ba33e62e Binary files /dev/null and b/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/activemq-data/localhost/KahaDB/db.data differ diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/activemq-data/localhost/KahaDB/db.redo b/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/activemq-data/localhost/KahaDB/db.redo new file mode 100644 index 000000000..14e640f13 Binary files /dev/null and b/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/activemq-data/localhost/KahaDB/db.redo differ diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/activemq-data/localhost/KahaDB/lock b/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/activemq-data/localhost/KahaDB/lock new file mode 100644 index 000000000..60d95e53f Binary files /dev/null and b/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/activemq-data/localhost/KahaDB/lock differ diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/pom.xml b/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/pom.xml new file mode 100644 index 000000000..6d1a3b072 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/pom.xml @@ -0,0 +1,121 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-instrumentation-messaging-otel-tests + jar + Spring Cloud Sleuth Otel Messaging Instrumentation Tests + Spring Cloud Sleuth Otel Messaging Instrumentation Tests + + + org.springframework.cloud + spring-cloud-sleuth-otel-tests + 3.0.0-SNAPSHOT + .. + + + + true + + + + + + + maven-deploy-plugin + + true + + + + + + + + org.springframework.cloud + spring-cloud-sleuth-tests-common + ${project.version} + + + org.springframework.boot + spring-boot-starter-web + + + io.opentelemetry + opentelemetry-extension-trace-propagators + + + org.springframework.cloud + spring-cloud-sleuth-core + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.cloud + spring-cloud-sleuth-otel + test + + + org.springframework.cloud + spring-cloud-stream + jar + + + org.springframework.cloud + spring-cloud-stream + test-jar + test + test-binder + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + org.awaitility + awaitility + test + + + + org.springframework.integration + spring-integration-core + true + + + org.springframework.boot + spring-boot-starter-websocket + true + + + + diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/messaging/TraceWebSocketAutoConfigurationTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/messaging/TraceWebSocketAutoConfigurationTests.java new file mode 100644 index 000000000..297eb6f56 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/messaging/TraceWebSocketAutoConfigurationTests.java @@ -0,0 +1,52 @@ +/* + * Copyright 2013-2020 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.otel.instrument.messaging; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +/** + * @author Marcin Grzejszczak + */ +@SpringBootTest +@ContextConfiguration(classes = TraceWebSocketAutoConfigurationTests.Config.class) +public class TraceWebSocketAutoConfigurationTests + extends org.springframework.cloud.sleuth.instrument.messaging.TraceWebSocketAutoConfigurationTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/messaging/TracingChannelInterceptorTest.java b/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/messaging/TracingChannelInterceptorTest.java new file mode 100644 index 000000000..ed5486584 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/messaging/TracingChannelInterceptorTest.java @@ -0,0 +1,66 @@ +/* + * Copyright 2013-2020 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.otel.instrument.messaging; + +import java.util.Collections; +import java.util.List; + +import javax.annotation.Nullable; + +import io.grpc.Context; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.context.propagation.DefaultContextPropagators; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.extensions.trace.propagation.B3Propagator; + +import org.springframework.cloud.sleuth.otel.OtelTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +public class TracingChannelInterceptorTest + extends org.springframework.cloud.sleuth.instrument.messaging.TracingChannelInterceptorTest { + + OtelTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new OtelTestTracing() { + @Override + protected ContextPropagators contextPropagators() { + return DefaultContextPropagators.builder().addTextMapPropagator(new TextMapPropagator() { + @Override + public List fields() { + return Collections.singletonList("b3"); + } + + @Override + public void inject(Context context, @Nullable C c, Setter setter) { + B3Propagator.getSingleHeaderPropagator().inject(context, c, setter); + } + + @Override + public Context extract(Context context, C c, Getter getter) { + return B3Propagator.getSingleHeaderPropagator().extract(context, c, getter); + } + }).build(); + } + }; + } + return this.testTracing; + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/util/SpanUtil.java b/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/util/SpanUtil.java new file mode 100644 index 000000000..8e8c2f4ff --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/util/SpanUtil.java @@ -0,0 +1,55 @@ +/* + * Copyright 2013-2020 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.otel.instrument.util; + +/** + * @author Marcin Grzejszczak + * @since + */ +public final class SpanUtil { + + static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + private SpanUtil() { + throw new IllegalStateException("Can't instantiate a utility class"); + } + + // Represents given long id as 16-character lower-hex string + public static String idToHex(long id) { + char[] data = new char[16]; + writeHexLong(data, 0, id); + return new String(data); + } + + // Inspired by {@code okio.Buffer.writeLong} + static void writeHexLong(char[] data, int pos, long v) { + writeHexByte(data, pos + 0, (byte) ((v >>> 56L) & 0xff)); + writeHexByte(data, pos + 2, (byte) ((v >>> 48L) & 0xff)); + writeHexByte(data, pos + 4, (byte) ((v >>> 40L) & 0xff)); + writeHexByte(data, pos + 6, (byte) ((v >>> 32L) & 0xff)); + writeHexByte(data, pos + 8, (byte) ((v >>> 24L) & 0xff)); + writeHexByte(data, pos + 10, (byte) ((v >>> 16L) & 0xff)); + writeHexByte(data, pos + 12, (byte) ((v >>> 8L) & 0xff)); + writeHexByte(data, pos + 14, (byte) (v & 0xff)); + } + + static void writeHexByte(char[] data, int pos, byte b) { + data[pos + 0] = HEX_DIGITS[(b >> 4) & 0xf]; + data[pos + 1] = HEX_DIGITS[b & 0xf]; + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/application.yml b/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/application.yml new file mode 100644 index 000000000..9ed06adbb --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/application.yml @@ -0,0 +1,3 @@ +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE \ No newline at end of file diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/beans/applicationContext.xml b/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/beans/applicationContext.xml similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/beans/applicationContext.xml rename to tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/beans/applicationContext.xml diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/logback.xml b/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/logback.xml new file mode 100644 index 000000000..a7061973e --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/logback.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/pom.xml b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/pom.xml new file mode 100644 index 000000000..98d15ee58 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/pom.xml @@ -0,0 +1,112 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-instrumentation-mvc-otel-tests + jar + Spring Cloud Sleuth Otel Mvc Instrumentation Tests + Spring Cloud Sleuth Otel Mvc Instrumentation Tests + + + org.springframework.cloud + spring-cloud-sleuth-otel-tests + 3.0.0-SNAPSHOT + .. + + + + true + + + + + + + maven-deploy-plugin + + true + + + + + + + + ${project.groupId} + spring-cloud-sleuth-tests-common + ${project.version} + + + io.opentelemetry + opentelemetry-extension-trace-propagators + + + org.apache.httpcomponents + httpclient + + + com.squareup.okhttp3 + mockwebserver + + + com.squareup.okhttp3 + okhttp + + 4.8.0 + compile + + + org.springframework.boot + spring-boot-starter-actuator + true + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.cloud + spring-cloud-sleuth-core + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.cloud + spring-cloud-sleuth-otel + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.awaitility + awaitility + test + + + + diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/HttpServerParserTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/HttpServerParserTests.java new file mode 100644 index 000000000..3d27d7438 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/HttpServerParserTests.java @@ -0,0 +1,48 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = HttpServerParserTests.Config.class) +public class HttpServerParserTests extends org.springframework.cloud.sleuth.instrument.web.HttpServerParserTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/IgnoreAutoConfiguredSkipPatternsIntegrationTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/IgnoreAutoConfiguredSkipPatternsIntegrationTests.java new file mode 100644 index 000000000..318197390 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/IgnoreAutoConfiguredSkipPatternsIntegrationTests.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = IgnoreAutoConfiguredSkipPatternsIntegrationTests.Config.class) +public class IgnoreAutoConfiguredSkipPatternsIntegrationTests + extends org.springframework.cloud.sleuth.instrument.web.IgnoreAutoConfiguredSkipPatternsIntegrationTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithBasePath.java b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithBasePath.java new file mode 100644 index 000000000..5bc2aaa01 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithBasePath.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = SkipEndPointsIntegrationTestsWithContextPathWithBasePath.Config.class) +public class SkipEndPointsIntegrationTestsWithContextPathWithBasePath extends + org.springframework.cloud.sleuth.instrument.web.SkipEndPointsIntegrationTestsWithContextPathWithBasePath { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath.java b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath.java new file mode 100644 index 000000000..1d42483a4 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath.Config.class) +public class SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath extends + org.springframework.cloud.sleuth.instrument.web.SkipEndPointsIntegrationTestsWithContextPathWithoutBasePath { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath.java b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath.java new file mode 100644 index 000000000..9946c5783 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath.Config.class) +public class SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath extends + org.springframework.cloud.sleuth.instrument.web.SkipEndPointsIntegrationTestsWithoutContextPathWithBasePath { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath.java b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath.java new file mode 100644 index 000000000..b6491afbe --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath.Config.class) +public class SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath extends + org.springframework.cloud.sleuth.instrument.web.SkipEndPointsIntegrationTestsWithoutContextPathWithoutBasePath { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/TraceFilterTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/TraceFilterTests.java new file mode 100644 index 000000000..9edac7b4d --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/TraceFilterTests.java @@ -0,0 +1,43 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web; + +import org.springframework.cloud.sleuth.api.http.HttpServerHandler; +import org.springframework.cloud.sleuth.otel.OtelTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +/** + * @author Spencer Gibb + */ +public class TraceFilterTests extends org.springframework.cloud.sleuth.instrument.web.TraceFilterTests { + + OtelTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new OtelTestTracing(); + } + return this.testTracing; + } + + @Override + public HttpServerHandler httpServerHandler() { + return tracerTest().tracing().httpServerHandler(); + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/MultipleAsyncRestTemplateTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/MultipleAsyncRestTemplateTests.java new file mode 100644 index 000000000..63be7f5f0 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/MultipleAsyncRestTemplateTests.java @@ -0,0 +1,52 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web.client; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +/** + * @author Marcin Grzejszczak + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ContextConfiguration(classes = MultipleAsyncRestTemplateTests.Config.class) +public class MultipleAsyncRestTemplateTests + extends org.springframework.cloud.sleuth.instrument.web.client.MultipleAsyncRestTemplateTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/TraceRestTemplateInterceptorIntegrationTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/TraceRestTemplateInterceptorIntegrationTests.java new file mode 100644 index 000000000..d37337129 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/TraceRestTemplateInterceptorIntegrationTests.java @@ -0,0 +1,38 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web.client; + +import org.springframework.cloud.sleuth.otel.OtelTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +/** + * @author Marcin Grzejszczak + */ +public class TraceRestTemplateInterceptorIntegrationTests + extends org.springframework.cloud.sleuth.instrument.web.client.TraceRestTemplateInterceptorIntegrationTests { + + OtelTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new OtelTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/TraceRestTemplateInterceptorTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/TraceRestTemplateInterceptorTests.java new file mode 100644 index 000000000..1595c0ba9 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/TraceRestTemplateInterceptorTests.java @@ -0,0 +1,47 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web.client; + +import java.util.Map; + +import org.springframework.cloud.sleuth.api.Span; +import org.springframework.cloud.sleuth.otel.OtelTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +/** + * @author Dave Syer + * + */ +public class TraceRestTemplateInterceptorTests + extends org.springframework.cloud.sleuth.instrument.web.client.TraceRestTemplateInterceptorTests { + + OtelTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new OtelTestTracing(); + } + return this.testTracing; + } + + @Override + public void assertThatParentSpanIdSet(Span span, Map headers) { + + } + +} diff --git a/tests/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/util/SpanUtil.java b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/util/SpanUtil.java similarity index 100% rename from tests/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/util/SpanUtil.java rename to tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/util/SpanUtil.java index cc5fa1e5f..38c88aff1 100644 --- a/tests/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/util/SpanUtil.java +++ b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/util/SpanUtil.java @@ -22,12 +22,12 @@ package org.springframework.cloud.sleuth.util; */ public final class SpanUtil { + static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + private SpanUtil() { throw new IllegalStateException("Can't instantiate a utility class"); } - static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - // Represents given long id as 16-character lower-hex string public static String idToHex(long id) { char[] data = new char[16]; diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/resources/application.yml b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/resources/application.yml new file mode 100644 index 000000000..9ed06adbb --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/resources/application.yml @@ -0,0 +1,3 @@ +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE \ No newline at end of file diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-quartz-tests/pom.xml b/tests/otel/spring-cloud-sleuth-instrumentation-quartz-tests/pom.xml new file mode 100644 index 000000000..0c090c4ce --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-quartz-tests/pom.xml @@ -0,0 +1,84 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-instrumentation-quartz-otel-tests + jar + Spring Cloud Sleuth Otel Quartz Instrumentation Tests + Spring Cloud Sleuth Otel Quartz Instrumentation Tests + + + org.springframework.cloud + spring-cloud-sleuth-otel-tests + 3.0.0-SNAPSHOT + .. + + + + true + + + + + + + maven-deploy-plugin + + true + + + + + + + + org.springframework.boot + spring-boot-starter-quartz + + + io.opentelemetry + opentelemetry-extension-trace-propagators + + + org.springframework.cloud + spring-cloud-sleuth-tests-common + ${project.version} + + + org.springframework.cloud + spring-cloud-sleuth-otel + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.awaitility + awaitility + test + + + + diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-quartz-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/quartz/TracingJobListenerTest.java b/tests/otel/spring-cloud-sleuth-instrumentation-quartz-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/quartz/TracingJobListenerTest.java new file mode 100644 index 000000000..40c89d042 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-quartz-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/quartz/TracingJobListenerTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2020 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.otel.instrument.quartz; + +import org.springframework.cloud.sleuth.otel.OtelTestTracing; +import org.springframework.cloud.sleuth.test.TestTracingAware; + +public class TracingJobListenerTest extends org.springframework.cloud.sleuth.instrument.quartz.TracingJobListenerTest { + + OtelTestTracing testTracing; + + @Override + public TestTracingAware tracerTest() { + if (this.testTracing == null) { + this.testTracing = new OtelTestTracing(); + } + return this.testTracing; + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-quartz-tests/src/test/resources/application.yml b/tests/otel/spring-cloud-sleuth-instrumentation-quartz-tests/src/test/resources/application.yml new file mode 100644 index 000000000..caf171df7 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-quartz-tests/src/test/resources/application.yml @@ -0,0 +1,5 @@ +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE + +spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.cloud.gateway.config.GatewayAutoConfiguration, org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration, org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration \ No newline at end of file diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-reactor-tests/pom.xml b/tests/otel/spring-cloud-sleuth-instrumentation-reactor-tests/pom.xml new file mode 100644 index 000000000..5a7555b77 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-reactor-tests/pom.xml @@ -0,0 +1,102 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-instrumentation-reactor-otel-tests + jar + Spring Cloud Sleuth Otel Reactor Instrumentation Tests + Spring Cloud Sleuth Otel Reactor Instrumentation Tests + + + org.springframework.cloud + spring-cloud-sleuth-otel-tests + 3.0.0-SNAPSHOT + .. + + + + true + + + + + + + maven-deploy-plugin + + true + + + + + + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework.cloud + spring-cloud-sleuth-core + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.cloud + spring-cloud-sleuth-otel + test + + + io.opentelemetry + opentelemetry-extension-trace-propagators + + + org.springframework.boot + spring-boot-starter-test + test + + + org.awaitility + awaitility + test + + + io.projectreactor + reactor-core + true + + + io.projectreactor.netty + reactor-netty-http + true + + + org.reactivestreams + reactive-streams + true + + + + diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/resources/application.yml b/tests/otel/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/resources/application.yml new file mode 100644 index 000000000..9ed06adbb --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/resources/application.yml @@ -0,0 +1,3 @@ +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE \ No newline at end of file diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-rxjava-tests/pom.xml b/tests/otel/spring-cloud-sleuth-instrumentation-rxjava-tests/pom.xml new file mode 100644 index 000000000..d1225da14 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-rxjava-tests/pom.xml @@ -0,0 +1,88 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-instrumentation-rxjava-otel-tests + jar + Spring Cloud Sleuth Otel RxJava Instrumentation Tests + Spring Cloud Sleuth Otel RxJava Instrumentation Tests + + + org.springframework.cloud + spring-cloud-sleuth-otel-tests + 3.0.0-SNAPSHOT + .. + + + + true + + + + + + + maven-deploy-plugin + + true + + + + + + + + org.springframework.cloud + spring-cloud-sleuth-core + + + io.opentelemetry + opentelemetry-extension-trace-propagators + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.cloud + spring-cloud-sleuth-otel + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.awaitility + awaitility + test + + + io.reactivex + rxjava + true + + + + diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-rxjava-tests/src/test/resources/application.yml b/tests/otel/spring-cloud-sleuth-instrumentation-rxjava-tests/src/test/resources/application.yml new file mode 100644 index 000000000..2ffa52976 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-rxjava-tests/src/test/resources/application.yml @@ -0,0 +1,6 @@ +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE + +# comma separated list of matchers +spring.sleuth.rxjava.schedulers.ignoredthreads: HystixMetricPoller,^MyCustomThread.*$,^RxComputation.*$ \ No newline at end of file diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-scheduling-tests/pom.xml b/tests/otel/spring-cloud-sleuth-instrumentation-scheduling-tests/pom.xml new file mode 100644 index 000000000..ce23beca7 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-scheduling-tests/pom.xml @@ -0,0 +1,83 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-instrumentation-scheduling-otel-tests + jar + Spring Cloud Sleuth Otel Scheduling Instrumentation Tests + Spring Cloud Sleuth Otel Scheduling Instrumentation Tests + + + org.springframework.cloud + spring-cloud-sleuth-otel-tests + 3.0.0-SNAPSHOT + .. + + + + true + + + + + + + maven-deploy-plugin + + true + + + + + + + + org.springframework.cloud + spring-cloud-sleuth-core + + + io.opentelemetry + opentelemetry-extension-trace-propagators + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.cloud + spring-cloud-sleuth-otel + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.awaitility + awaitility + test + + + + diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-scheduling-tests/src/test/resources/application.yml b/tests/otel/spring-cloud-sleuth-instrumentation-scheduling-tests/src/test/resources/application.yml new file mode 100644 index 000000000..a85d3e0c8 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-scheduling-tests/src/test/resources/application.yml @@ -0,0 +1,5 @@ +logging.level.org.springframework.cloud: DEBUG +logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR +logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE + +spring.sleuth.scheduled.skipPattern: "^org.*TestBeanWithScheduledMethodToBeIgnored$" \ No newline at end of file diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/pom.xml b/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/pom.xml new file mode 100644 index 000000000..3c416cc69 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/pom.xml @@ -0,0 +1,108 @@ + + + + + 4.0.0 + + spring-cloud-sleuth-instrumentation-webflux-otel-tests + jar + Spring Cloud Sleuth Otel WebFlux Instrumentation Tests + Spring Cloud Sleuth Otel WebFlux Instrumentation Tests + + + org.springframework.cloud + spring-cloud-sleuth-otel-tests + 3.0.0-SNAPSHOT + .. + + + + true + + + + + + + maven-deploy-plugin + + true + + + + + + + + ${project.groupId} + spring-cloud-sleuth-tests-common + ${project.version} + + + org.springframework.boot + spring-boot-starter-actuator + + + io.opentelemetry + opentelemetry-extension-trace-propagators + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + org.springframework.cloud + spring-cloud-sleuth-otel + test + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.cloud + spring-cloud-sleuth-core + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.boot + spring-boot-starter-test + test + + + org.awaitility + awaitility + test + + + + diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/HttpClientBeanPostProcessorTest.java b/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/HttpClientBeanPostProcessorTest.java new file mode 100644 index 000000000..e60dd55d1 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/HttpClientBeanPostProcessorTest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web.client; + +import io.opentelemetry.trace.SpanContext; +import io.opentelemetry.trace.SpanId; +import io.opentelemetry.trace.TraceFlags; +import io.opentelemetry.trace.TraceId; +import io.opentelemetry.trace.TraceState; + +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.otel.bridge.OtelTraceContext; + +public class HttpClientBeanPostProcessorTest + extends org.springframework.cloud.sleuth.instrument.web.client.HttpClientBeanPostProcessorTest { + + @Override + public TraceContext traceContext() { + return OtelTraceContext.fromOtel(SpanContext.create(TraceId.fromLongs(1L, 0L), SpanId.fromLong(2L), + TraceFlags.getSampled(), TraceState.builder().build())); + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/ReactorNettyHttpClientSpringBootTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/ReactorNettyHttpClientSpringBootTests.java new file mode 100644 index 000000000..2990641c4 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/ReactorNettyHttpClientSpringBootTests.java @@ -0,0 +1,79 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web.client; + +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.extensions.trace.propagation.B3Propagator; +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; +import io.opentelemetry.trace.SpanContext; +import io.opentelemetry.trace.SpanId; +import io.opentelemetry.trace.TraceFlags; +import io.opentelemetry.trace.TraceId; +import io.opentelemetry.trace.TraceState; +import org.assertj.core.api.Assertions; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.api.TraceContext; +import org.springframework.cloud.sleuth.api.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.bridge.OtelTraceContext; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +@ContextConfiguration(classes = ReactorNettyHttpClientSpringBootTests.Config.class) +@TestPropertySource(properties = "spring.sleuth.otel.propagation.type=custom") +public class ReactorNettyHttpClientSpringBootTests + extends org.springframework.cloud.sleuth.instrument.web.client.ReactorNettyHttpClientSpringBootTests { + + @Override + public TraceContext traceContext() { + return OtelTraceContext.fromOtel(SpanContext.create(TraceId.fromLongs(1L, 0L), SpanId.fromLong(2L), + TraceFlags.getSampled(), TraceState.builder().build())); + } + + @Override + public void assertSingleB3Header(String b3SingleHeaderReadByServer, FinishedSpan clientSpan, TraceContext parent) { + Assertions.assertThat(b3SingleHeaderReadByServer) + .isEqualTo(parent.traceId() + "-" + clientSpan.spanId() + "-1"); + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + TextMapPropagator otelTextMapPropagator() { + return B3Propagator.getSingleHeaderPropagator(); + } + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/WebClientCustomParserTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/WebClientCustomParserTests.java new file mode 100644 index 000000000..0a511a486 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/WebClientCustomParserTests.java @@ -0,0 +1,51 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web.client; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@SpringBootTest(webEnvironment = RANDOM_PORT) +@ContextConfiguration(classes = WebClientCustomParserTests.Config.class) +public class WebClientCustomParserTests + extends org.springframework.cloud.sleuth.instrument.web.client.integration.parser.WebClientCustomParserTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/WebClientDiscoveryExceptionTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/WebClientDiscoveryExceptionTests.java new file mode 100644 index 000000000..cdbc28679 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/WebClientDiscoveryExceptionTests.java @@ -0,0 +1,51 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web.client; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@SpringBootTest(webEnvironment = RANDOM_PORT) +@ContextConfiguration(classes = WebClientDiscoveryExceptionTests.Config.class) +public class WebClientDiscoveryExceptionTests extends + org.springframework.cloud.sleuth.instrument.web.client.discoveryexception.WebClientDiscoveryExceptionTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/WebClientExceptionTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/WebClientExceptionTests.java new file mode 100644 index 000000000..e391dd54d --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/WebClientExceptionTests.java @@ -0,0 +1,50 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web.client; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@SpringBootTest(classes = { WebClientExceptionTests.Config.class, + org.springframework.cloud.sleuth.instrument.web.client.exception.WebClientExceptionTests.TestConfiguration.class }, + properties = { "spring.application.name=exceptionservice" }, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class WebClientExceptionTests + extends org.springframework.cloud.sleuth.instrument.web.client.exception.WebClientExceptionTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/WebClientNotSampledTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/WebClientNotSampledTests.java new file mode 100644 index 000000000..c18fe65ce --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/WebClientNotSampledTests.java @@ -0,0 +1,60 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web.client; + +import java.util.Map; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.ContextConfiguration; + +import static org.assertj.core.api.BDDAssertions.then; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@SpringBootTest(webEnvironment = RANDOM_PORT) +@ContextConfiguration(classes = WebClientNotSampledTests.Config.class) +public class WebClientNotSampledTests + extends org.springframework.cloud.sleuth.instrument.web.client.integration.notsampled.WebClientNotSampledTests { + + @Override + public void assertB3SingleNotSampled(ResponseEntity> response) { + then(response.getBody().get("b3")).isNotNull().endsWith("-0"); // not sampled + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOff(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/WebClientTests.java b/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/WebClientTests.java new file mode 100644 index 000000000..f293a6cad --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/instrument/web/client/WebClientTests.java @@ -0,0 +1,51 @@ +/* + * Copyright 2013-2020 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.otel.instrument.web.client; + +import io.opentelemetry.sdk.trace.Sampler; +import io.opentelemetry.sdk.trace.Samplers; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.otel.OtelTestSpanHandler; +import org.springframework.cloud.sleuth.otel.exporter.ArrayListSpanProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@SpringBootTest(webEnvironment = RANDOM_PORT) +@ContextConfiguration(classes = WebClientTests.Config.class) +public class WebClientTests + extends org.springframework.cloud.sleuth.instrument.web.client.integration.sampled.WebClientTests { + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + OtelTestSpanHandler testSpanHandlerSupplier() { + return new OtelTestSpanHandler(new ArrayListSpanProcessor()); + } + + @Bean + Sampler alwaysSampler() { + return Samplers.alwaysOn(); + } + + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/util/SpanUtil.java b/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/util/SpanUtil.java new file mode 100644 index 000000000..1107ce0cd --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/otel/util/SpanUtil.java @@ -0,0 +1,55 @@ +/* + * Copyright 2013-2020 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.brave.util; + +/** + * @author Marcin Grzejszczak + * @since + */ +public final class SpanUtil { + + static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + private SpanUtil() { + throw new IllegalStateException("Can't instantiate a utility class"); + } + + // Represents given long id as 16-character lower-hex string + public static String idToHex(long id) { + char[] data = new char[16]; + writeHexLong(data, 0, id); + return new String(data); + } + + // Inspired by {@code okio.Buffer.writeLong} + static void writeHexLong(char[] data, int pos, long v) { + writeHexByte(data, pos + 0, (byte) ((v >>> 56L) & 0xff)); + writeHexByte(data, pos + 2, (byte) ((v >>> 48L) & 0xff)); + writeHexByte(data, pos + 4, (byte) ((v >>> 40L) & 0xff)); + writeHexByte(data, pos + 6, (byte) ((v >>> 32L) & 0xff)); + writeHexByte(data, pos + 8, (byte) ((v >>> 24L) & 0xff)); + writeHexByte(data, pos + 10, (byte) ((v >>> 16L) & 0xff)); + writeHexByte(data, pos + 12, (byte) ((v >>> 8L) & 0xff)); + writeHexByte(data, pos + 14, (byte) (v & 0xff)); + } + + static void writeHexByte(char[] data, int pos, byte b) { + data[pos + 0] = HEX_DIGITS[(b >> 4) & 0xf]; + data[pos + 1] = HEX_DIGITS[b & 0xf]; + } + +} diff --git a/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/resources/logback.xml b/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/resources/logback.xml new file mode 100644 index 000000000..17279c080 --- /dev/null +++ b/tests/otel/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/resources/logback.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + diff --git a/tests/pom.xml b/tests/pom.xml index e8880a175..bfd0d6904 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -35,17 +35,9 @@ - spring-cloud-sleuth-instrumentation-async-tests - spring-cloud-sleuth-instrumentation-grpc-tests - spring-cloud-sleuth-instrumentation-messaging-tests - spring-cloud-sleuth-instrumentation-reactor-tests - 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 + common + brave + otel diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/application.yml b/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/application.yml deleted file mode 100644 index f1e4a6a92..000000000 --- a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/resources/application.yml +++ /dev/null @@ -1,3 +0,0 @@ -logging.level.org.springframework.cloud: DEBUG -logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR -logging.level.org.springframework.cloud.sleuth.instrument.web.client.feign: TRACE \ No newline at end of file diff --git a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/resources/application.yml b/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/resources/application.yml deleted file mode 100644 index f1e4a6a92..000000000 --- a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/resources/application.yml +++ /dev/null @@ -1,3 +0,0 @@ -logging.level.org.springframework.cloud: DEBUG -logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR -logging.level.org.springframework.cloud.sleuth.instrument.web.client.feign: TRACE \ No newline at end of file diff --git a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/TraceReactorAutoConfigurationAccessorConfiguration.java b/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/TraceReactorAutoConfigurationAccessorConfiguration.java deleted file mode 100644 index eb34632c3..000000000 --- a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/TraceReactorAutoConfigurationAccessorConfiguration.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2013-2020 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.reactor; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import reactor.core.publisher.Hooks; -import reactor.core.scheduler.Schedulers; - -import org.springframework.context.ConfigurableApplicationContext; - -import static org.springframework.cloud.sleuth.instrument.reactor.TraceReactorAutoConfiguration.SLEUTH_REACTOR_EXECUTOR_SERVICE_KEY; -import static org.springframework.cloud.sleuth.instrument.reactor.TraceReactorAutoConfiguration.TraceReactorConfiguration.SLEUTH_TRACE_REACTOR_KEY; - -/** - * @author Marcin Grzejszczak - */ -public final class TraceReactorAutoConfigurationAccessorConfiguration { - - private TraceReactorAutoConfigurationAccessorConfiguration() { - throw new IllegalStateException("Can't instantiate a utility class"); - } - - private static final Log log = LogFactory.getLog(TraceReactorAutoConfigurationAccessorConfiguration.class); - - public static void close() { - if (log.isTraceEnabled()) { - log.trace("Cleaning up hooks"); - } - Hooks.resetOnEachOperator(SLEUTH_TRACE_REACTOR_KEY); - Hooks.resetOnLastOperator(SLEUTH_TRACE_REACTOR_KEY); - Schedulers.removeExecutorServiceDecorator(SLEUTH_REACTOR_EXECUTOR_SERVICE_KEY); - } - - public static void setup(ConfigurableApplicationContext context) { - if (log.isTraceEnabled()) { - log.trace("Setting up hooks"); - } - HookRegisteringBeanDefinitionRegistryPostProcessor.setupHooks(context); - } - -} diff --git a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/resources/application.yml b/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/resources/application.yml deleted file mode 100644 index f1e4a6a92..000000000 --- a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/resources/application.yml +++ /dev/null @@ -1,3 +0,0 @@ -logging.level.org.springframework.cloud: DEBUG -logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR -logging.level.org.springframework.cloud.sleuth.instrument.web.client.feign: TRACE \ No newline at end of file