Improvement with Reactor instrumentation
- adds Spring Cloud Function instrumentation - adds Operators to manually provide instrumentation for Fluxes - introduces Manual instrumentation mode for Reactor TODO: Documentation (will add it soon) related gh-1684
This commit is contained in:
@@ -25,16 +25,23 @@
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
<artifactId>benchmarks</artifactId>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.4.0-M1</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<main.basedir>${project.basedir}/..</main.basedir>
|
||||
<jmh.version>1.22</jmh.version>
|
||||
<maven-shade-plugin.version>3.2.1</maven-shade-plugin.version>
|
||||
<sonar.skip>true</sonar.skip>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<spring-boot.version>2.4.0-M1</spring-boot.version>
|
||||
<brave.version>5.12.3</brave.version>
|
||||
<okhttp.version>3.14.6</okhttp.version>
|
||||
<microbenchmark-runner.version>0.2.0.RELEASE</microbenchmark-runner.version>
|
||||
<jmh.version>1.21</jmh.version>
|
||||
<spring-cloud-stream.version>3.1.0-SNAPSHOT</spring-cloud-stream.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@@ -47,10 +54,9 @@
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- Import dependency management from Spring Boot -->
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-dependencies</artifactId>
|
||||
<version>${spring-cloud-stream.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -60,7 +66,7 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-cloud-sleuth-core</artifactId>
|
||||
<artifactId>spring-cloud-starter-sleuth</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -91,22 +97,36 @@
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>3.14.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-core</artifactId>
|
||||
<version>1.3</version>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-stream-kafka</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream</artifactId>
|
||||
<type>test-jar</type>
|
||||
<scope>compile</scope>
|
||||
<classifier>test-binder</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-core</artifactId>
|
||||
<version>${jmh.version}</version>
|
||||
<groupId>com.github.mp911de.microbenchmark-runner</groupId>
|
||||
<artifactId>microbenchmark-runner-junit5</artifactId>
|
||||
<version>${microbenchmark-runner.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.mp911de.microbenchmark-runner</groupId>
|
||||
<artifactId>microbenchmark-runner-extras</artifactId>
|
||||
<version>${microbenchmark-runner.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-generator-annprocess</artifactId>
|
||||
@@ -122,7 +142,6 @@
|
||||
<dependency>
|
||||
<groupId>io.zipkin.brave</groupId>
|
||||
<artifactId>brave-instrumentation-httpclient</artifactId>
|
||||
<version>${brave.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
@@ -140,24 +159,15 @@
|
||||
<target>${maven.compiler.target}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<!--skip deploy (this is just a test module) -->
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-install-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
@@ -229,75 +239,6 @@
|
||||
</pluginRepositories>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>jmh</id>
|
||||
<activation>
|
||||
<activeByDefault>false</activeByDefault>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>${maven-shade-plugin.version}</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<configuration>
|
||||
<keepDependenciesWithProvidedScope>true
|
||||
</keepDependenciesWithProvidedScope>
|
||||
<createDependencyReducedPom>true</createDependencyReducedPom>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<finalName>benchmarks</finalName>
|
||||
<transformers>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/spring.handlers</resource>
|
||||
</transformer>
|
||||
<transformer
|
||||
implementation="org.springframework.boot.maven.PropertiesMergingResourceTransformer">
|
||||
<resource>META-INF/spring.factories</resource>
|
||||
</transformer>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/spring.schemas</resource>
|
||||
</transformer>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>org.openjdk.jmh.Main</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
<createDependencyReducedPom>false
|
||||
</createDependencyReducedPom>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>jmeter</id>
|
||||
<activation>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -56,13 +56,15 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@SpringBootApplication
|
||||
@RestController
|
||||
@EnableAsync
|
||||
public class SleuthBenchmarkingSpringApp
|
||||
implements ApplicationListener<ServletWebServerInitializedEvent> {
|
||||
public class SleuthBenchmarkingSpringApp implements ApplicationListener<ServletWebServerInitializedEvent> {
|
||||
|
||||
private static final Log log = LogFactory.getLog(SleuthBenchmarkingSpringApp.class);
|
||||
|
||||
public final ExecutorService pool = Executors.newWorkStealingPool();
|
||||
|
||||
/**
|
||||
* Port of the app.
|
||||
*/
|
||||
public int port;
|
||||
|
||||
@Autowired(required = false)
|
||||
@@ -109,11 +111,9 @@ public class SleuthBenchmarkingSpringApp
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ServletWebServerFactory servletContainer(
|
||||
@Value("${server.port:0}") int serverPort) {
|
||||
public ServletWebServerFactory servletContainer(@Value("${server.port:0}") int serverPort) {
|
||||
log.info("Starting container at port [" + serverPort + "]");
|
||||
return new TomcatServletWebServerFactory(
|
||||
serverPort == 0 ? SocketUtils.findAvailableTcpPort() : serverPort);
|
||||
return new TomcatServletWebServerFactory(serverPort == 0 ? SocketUtils.findAvailableTcpPort() : serverPort);
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* 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.benchmarks.app.stream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
import brave.Tracing;
|
||||
import brave.propagation.TraceContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.sleuth.instrument.messaging.MessagingSleuthOperator;
|
||||
import org.springframework.cloud.sleuth.instrument.web.WebFluxSleuthOperators;
|
||||
import org.springframework.cloud.stream.binder.test.InputDestination;
|
||||
import org.springframework.cloud.stream.binder.test.OutputDestination;
|
||||
import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootApplication
|
||||
@Import(TestChannelBinderConfiguration.class)
|
||||
public class SleuthBenchmarkingStreamApplication {
|
||||
|
||||
public static void main(String[] args) throws InterruptedException, IOException {
|
||||
// System.setProperty("spring.sleuth.enabled", "false");
|
||||
// System.setProperty("spring.sleuth.reactor.instrumentation-type",
|
||||
// "DECORATE_ON_EACH");
|
||||
// System.setProperty("spring.sleuth.reactor.instrumentation-type",
|
||||
// "DECORATE_ON_LAST");
|
||||
// System.setProperty("spring.sleuth.reactor.instrumentation-type", "MANUAL");
|
||||
System.setProperty("spring.sleuth.reactor.instrumentation-type", "MANUAL");
|
||||
System.setProperty("spring.sleuth.function.type", "simple");
|
||||
ConfigurableApplicationContext context = SpringApplication.run(SleuthBenchmarkingStreamApplication.class, args);
|
||||
System.out.println("PRess any key to continue");
|
||||
System.in.read();
|
||||
for (int i = 0; i < 1; i++) {
|
||||
InputDestination input = context.getBean(InputDestination.class);
|
||||
input.send(MessageBuilder.withPayload("hello".getBytes())
|
||||
.setHeader("b3", "4883117762eb9420-4883117762eb9420-1").build());
|
||||
System.out.println("Retrieving the message for tests");
|
||||
OutputDestination output = context.getBean(OutputDestination.class);
|
||||
Message<byte[]> message = output.receive(200L);
|
||||
System.out.println("Got the message from output");
|
||||
assertThat(message).isNotNull();
|
||||
System.out.println("Message is not null");
|
||||
assertThat(message.getPayload()).isEqualTo("HELLO".getBytes());
|
||||
System.out.println("Payload is HELLO");
|
||||
String b3 = message.getHeaders().get("b3", String.class);
|
||||
System.out.println("Checking the b3 header [" + b3 + "]");
|
||||
assertThat(b3).startsWith("4883117762eb9420");
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
ExecutorService sleuthExecutorService() {
|
||||
return Executors.newCachedThreadPool();
|
||||
}
|
||||
|
||||
@Bean(name = "myFlux")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.function.type", havingValue = "simple")
|
||||
public Function<String, String> nonReactiveSimpleSleuthFunction() {
|
||||
System.out.println("simple_function");
|
||||
return new SimpleFunction();
|
||||
}
|
||||
|
||||
@Bean(name = "myFlux")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.function.type", havingValue = "reactive_simple")
|
||||
public Function<Flux<String>, Flux<String>> reactiveSimpleSleuthFunction() {
|
||||
System.out.println("simple_reactive_function");
|
||||
return new SimpleReactiveFunction();
|
||||
}
|
||||
|
||||
@Bean(name = "myFlux")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.function.type", havingValue = "simple_manual")
|
||||
public Function<Message<String>, Message<String>> nonReactiveSimpleManualSleuthFunction(Tracing tracing) {
|
||||
System.out.println("simple_manual_function");
|
||||
return new SimpleManualFunction(tracing);
|
||||
}
|
||||
|
||||
@Bean(name = "myFlux")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.function.type", havingValue = "reactive_simple_manual")
|
||||
public Function<Flux<Message<String>>, Flux<Message<String>>> reactiveSimpleManualSleuthFunction(Tracing tracing) {
|
||||
System.out.println("simple_reactive_manual_function");
|
||||
return new SimpleReactiveManualFunction(tracing);
|
||||
}
|
||||
|
||||
@Bean(name = "myFlux")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.nonreactive.function.enabled", havingValue = "true")
|
||||
public Function<String, String> nonReactiveSleuthFunction(ExecutorService executorService) {
|
||||
System.out.println("no sleuth non reactive function");
|
||||
return new SleuthNonReactiveFunction(executorService);
|
||||
}
|
||||
|
||||
@Bean(name = "myFlux")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.function.type", havingValue = "DECORATE_ON_EACH",
|
||||
matchIfMissing = true)
|
||||
public Function<Flux<String>, Flux<String>> onEachFunction() {
|
||||
System.out.println("on each function");
|
||||
return new SleuthFunction();
|
||||
}
|
||||
|
||||
@Bean(name = "myFlux")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.function.type", havingValue = "DECORATE_ON_LAST")
|
||||
public Function<Flux<String>, Flux<String>> onLastFunction() {
|
||||
System.out.println("on last function");
|
||||
return new SleuthFunction();
|
||||
}
|
||||
|
||||
@Bean(name = "myFlux")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.function.type", havingValue = "MANUAL")
|
||||
public Function<Flux<String>, Flux<String>> manualFunction() {
|
||||
System.out.println("manual function");
|
||||
return new SleuthManualFunction();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SimpleFunction implements Function<String, String> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SimpleFunction.class);
|
||||
|
||||
@Override
|
||||
public String apply(String input) {
|
||||
log.info("Hello from simple [{}]", input);
|
||||
return input.toUpperCase();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SimpleReactiveFunction implements Function<Flux<String>, Flux<String>> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SimpleReactiveFunction.class);
|
||||
|
||||
@Override
|
||||
public Flux<String> apply(Flux<String> input) {
|
||||
return input.doOnNext(s -> log.info("Hello from simple [{}]", s)).map(String::toUpperCase);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SimpleManualFunction implements Function<Message<String>, Message<String>> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SimpleFunction.class);
|
||||
|
||||
private final Tracing tracing;
|
||||
|
||||
SimpleManualFunction(Tracing tracing) {
|
||||
this.tracing = tracing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<String> apply(Message<String> input) {
|
||||
return (MessagingSleuthOperator.asFunction(this.tracing, input)
|
||||
.andThen(msg -> MessagingSleuthOperator.withSpanInScope(this.tracing, msg, stringMessage -> {
|
||||
log.info("Hello from simple manual [{}]", stringMessage.getPayload());
|
||||
return stringMessage;
|
||||
})).andThen(msg -> MessagingSleuthOperator.afterMessageHandled(this.tracing, msg, null))
|
||||
.andThen(msg -> MessagingSleuthOperator.handleOutputMessage(this.tracing, msg))
|
||||
.andThen(msg -> MessageBuilder.createMessage(msg.getPayload().toUpperCase(), msg.getHeaders()))
|
||||
.andThen(msg -> MessagingSleuthOperator.afterMessageHandled(this.tracing, msg, null)).apply(input));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SimpleReactiveManualFunction implements Function<Flux<Message<String>>, Flux<Message<String>>> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SimpleReactiveFunction.class);
|
||||
|
||||
private final Tracing tracing;
|
||||
|
||||
SimpleReactiveManualFunction(Tracing tracing) {
|
||||
this.tracing = tracing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Message<String>> apply(Flux<Message<String>> input) {
|
||||
return input
|
||||
.map(message -> (MessagingSleuthOperator.asFunction(this.tracing, message))
|
||||
.andThen(msg -> MessagingSleuthOperator.withSpanInScope(this.tracing, msg, stringMessage -> {
|
||||
log.info("Hello from simple manual [{}]", stringMessage.getPayload());
|
||||
return stringMessage;
|
||||
})).andThen(msg -> MessagingSleuthOperator.afterMessageHandled(this.tracing, msg, null))
|
||||
.andThen(msg -> MessageBuilder.createMessage(msg.getPayload().toUpperCase(), msg.getHeaders()))
|
||||
.andThen(msg -> MessagingSleuthOperator.handleOutputMessage(this.tracing, msg))
|
||||
.apply(message));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SleuthNonReactiveFunction implements Function<String, String> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SleuthNonReactiveFunction.class);
|
||||
|
||||
private final ExecutorService executorService;
|
||||
|
||||
SleuthNonReactiveFunction(ExecutorService executorService) {
|
||||
this.executorService = executorService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(String input) {
|
||||
log.info("Got a message");
|
||||
try {
|
||||
return this.executorService.submit(() -> {
|
||||
log.info("Logging [{}] from a new thread", input);
|
||||
return input.toUpperCase();
|
||||
}).get(20, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SleuthFunction implements Function<Flux<String>, Flux<String>> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SleuthFunction.class);
|
||||
|
||||
@Override
|
||||
public Flux<String> apply(Flux<String> input) {
|
||||
return input.doOnEach(signal -> log.info("Got a message"))
|
||||
.flatMap(s -> Mono.delay(Duration.ofMillis(1), Schedulers.newParallel("foo")).map(aLong -> {
|
||||
log.info("Logging [{}] from flat map", s);
|
||||
return s.toUpperCase();
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SleuthManualFunction implements Function<Flux<String>, Flux<String>> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SleuthManualFunction.class);
|
||||
|
||||
@Override
|
||||
public Flux<String> apply(Flux<String> input) {
|
||||
return input.doOnEach(WebFluxSleuthOperators.withSpanInScope(() -> log.info("Got a message"))).flatMap(s -> Mono
|
||||
.subscriberContext().delayElement(Duration.ofMillis(1), Schedulers.newParallel("foo")).map(ctx -> {
|
||||
WebFluxSleuthOperators.withSpanInScope(ctx, () -> log.info("Logging [{}] from flat map", s));
|
||||
return s.toUpperCase();
|
||||
})).doOnEach(signal -> {
|
||||
WebFluxSleuthOperators.withSpanInScope(signal.getContext(), () -> 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");
|
||||
log.info("Assertions passed");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,13 +16,18 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.benchmarks.app.webflux;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import brave.sampler.Sampler;
|
||||
import brave.handler.SpanHandler;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import brave.propagation.TraceContext;
|
||||
import brave.sampler.Sampler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
@@ -31,9 +36,12 @@ 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.instrument.web.SkipPatternProvider;
|
||||
import org.springframework.cloud.sleuth.instrument.web.WebFluxSleuthOperators;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.SocketUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@@ -42,17 +50,18 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@RestController
|
||||
public class SleuthBenchmarkingSpringWebFluxApp
|
||||
implements ApplicationListener<ReactiveWebServerInitializedEvent> {
|
||||
public class SleuthBenchmarkingSpringWebFluxApp implements ApplicationListener<ReactiveWebServerInitializedEvent> {
|
||||
|
||||
private static final Log log = LogFactory
|
||||
.getLog(SleuthBenchmarkingSpringWebFluxApp.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(SleuthBenchmarkingSpringWebFluxApp.class);
|
||||
|
||||
/**
|
||||
* Port to set.
|
||||
*/
|
||||
public int port;
|
||||
|
||||
public static void main(String... args) {
|
||||
new SpringApplicationBuilder(SleuthBenchmarkingSpringWebFluxApp.class)
|
||||
.web(WebApplicationType.REACTIVE).application().run(args);
|
||||
new SpringApplicationBuilder(SleuthBenchmarkingSpringWebFluxApp.class).web(WebApplicationType.REACTIVE)
|
||||
.application().run(args);
|
||||
}
|
||||
|
||||
@RequestMapping("/foo")
|
||||
@@ -71,11 +80,9 @@ public class SleuthBenchmarkingSpringWebFluxApp
|
||||
}
|
||||
|
||||
@Bean
|
||||
NettyReactiveWebServerFactory nettyReactiveWebServerFactory(
|
||||
@Value("${server.port:0}") int serverPort) {
|
||||
NettyReactiveWebServerFactory nettyReactiveWebServerFactory(@Value("${server.port:0}") int serverPort) {
|
||||
log.info("Starting container at port [" + serverPort + "]");
|
||||
return new NettyReactiveWebServerFactory(
|
||||
serverPort == 0 ? SocketUtils.findAvailableTcpPort() : serverPort);
|
||||
return new NettyReactiveWebServerFactory(serverPort == 0 ? SocketUtils.findAvailableTcpPort() : serverPort);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -90,4 +97,54 @@ public class SleuthBenchmarkingSpringWebFluxApp
|
||||
this.port = event.getWebServer().getPort();
|
||||
}
|
||||
|
||||
@GetMapping("/simple")
|
||||
public Mono<String> simple() {
|
||||
return Mono.just("hello").map(String::toUpperCase);
|
||||
}
|
||||
|
||||
@GetMapping("/complexNoSleuth")
|
||||
public Mono<String> complexNoSleuth() {
|
||||
return Flux.range(1, 10).map(String::valueOf).collect(Collectors.toList())
|
||||
.doOnEach(signal -> log.info("Got a request"))
|
||||
.flatMap(s -> Mono.delay(Duration.ofMillis(1), Schedulers.newParallel("foo")).map(aLong -> {
|
||||
log.info("Logging [{}] from flat map", s);
|
||||
return "";
|
||||
}));
|
||||
}
|
||||
|
||||
@GetMapping("/complex")
|
||||
public Mono<String> complex() {
|
||||
return Flux.range(1, 10).map(String::valueOf).collect(Collectors.toList())
|
||||
.doOnEach(signal -> log.info("Got a request"))
|
||||
.flatMap(s -> Mono.delay(Duration.ofMillis(1), Schedulers.newParallel("foo")).map(aLong -> {
|
||||
log.info("Logging [{}] from flat map", s);
|
||||
return "";
|
||||
})).doOnEach(signal -> {
|
||||
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");
|
||||
log.info("Assertions passed");
|
||||
});
|
||||
}
|
||||
|
||||
@GetMapping("/complexManual")
|
||||
public Mono<String> complexManual() {
|
||||
return Flux.range(1, 10).map(String::valueOf).collect(Collectors.toList())
|
||||
.doOnEach(WebFluxSleuthOperators.withSpanInScope(() -> log.info("Got a request")))
|
||||
.flatMap(s -> Mono.subscriberContext().delayElement(Duration.ofMillis(1), Schedulers.newParallel("foo"))
|
||||
.map(ctx -> {
|
||||
WebFluxSleuthOperators.withSpanInScope(ctx,
|
||||
() -> log.info("Logging [{}] from flat map", s));
|
||||
return "";
|
||||
}))
|
||||
.doOnEach(signal -> {
|
||||
WebFluxSleuthOperators.withSpanInScope(signal.getContext(), () -> 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");
|
||||
log.info("Assertions passed");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.benchmarks.jmh.benchmarks;
|
||||
|
||||
public class SpringWebFluxOnLastBenchmark extends SpringWebFluxBenchmarks {
|
||||
|
||||
@Override
|
||||
protected String[] runArgs() {
|
||||
return new String[] { "--spring.jmx.enabled=false",
|
||||
"--spring.application.name=defaultTraceContextWithOnLastOperator",
|
||||
"--spring.sleuth.enabled=true",
|
||||
"--spring.sleuth.reactor.on-each-operator=false" };
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
logging.level:
|
||||
org.springframework: ERROR
|
||||
org.springframework.cloud.sleuth.benchmarks: INFO
|
||||
org.springframework.sleuth: ERROR
|
||||
brave: ERROR
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.benchmarks.jmh.benchmarks;
|
||||
package org.springframework.cloud.sleuth.benchmarks.jmh;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
@@ -57,15 +57,13 @@ public class ProcessLauncherState {
|
||||
this.args.add(count++, "-Djava.security.egd=file:/dev/./urandom");
|
||||
this.args.add(count++, "-XX:TieredStopAtLevel=1"); // zoom
|
||||
if (System.getProperty("bench.args") != null) {
|
||||
this.args.addAll(count++,
|
||||
Arrays.asList(System.getProperty("bench.args").split(" ")));
|
||||
this.args.addAll(count++, Arrays.asList(System.getProperty("bench.args").split(" ")));
|
||||
}
|
||||
this.length = args.length;
|
||||
this.home = new File(dir);
|
||||
}
|
||||
|
||||
protected static String output(InputStream inputStream, String marker)
|
||||
throws IOException {
|
||||
protected static String output(InputStream inputStream, String marker) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
BufferedReader br = null;
|
||||
br = new BufferedReader(new InputStreamReader(inputStream));
|
||||
@@ -100,8 +98,7 @@ public class ProcessLauncherState {
|
||||
|
||||
public void after() throws Exception {
|
||||
if (started != null && started.isAlive()) {
|
||||
System.err.println(
|
||||
"Stopped " + mainClass + ": " + started.destroyForcibly().waitFor());
|
||||
System.err.println("Stopped " + mainClass + ": " + started.destroyForcibly().waitFor());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.benchmarks.jmh;
|
||||
|
||||
import org.openjdk.jmh.runner.Runner;
|
||||
import org.openjdk.jmh.runner.RunnerException;
|
||||
import org.openjdk.jmh.runner.options.Options;
|
||||
import org.openjdk.jmh.runner.options.OptionsBuilder;
|
||||
|
||||
public class RunSleuthJmhBenchmarksFromIde {
|
||||
|
||||
// Convenience main entry-point for testing from IDE
|
||||
public static void main(String[] args) throws RunnerException {
|
||||
Options opt = new OptionsBuilder()
|
||||
.include(RunSleuthJmhBenchmarksFromIde.class.getPackage().getName()
|
||||
+ ".benchmarks.*")
|
||||
.build();
|
||||
|
||||
new Runner(opt).run();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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.benchmarks.jmh;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import brave.Tracing;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.TearDown;
|
||||
|
||||
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.stream.binder.test.InputDestination;
|
||||
import org.springframework.cloud.stream.binder.test.OutputDestination;
|
||||
import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@Disabled
|
||||
public class SampleTests {
|
||||
|
||||
@Test
|
||||
public void testStream() throws Exception {
|
||||
for (BenchmarkContext.Instrumentation value : BenchmarkContext.Instrumentation.values()) {
|
||||
run(value);
|
||||
}
|
||||
// run(BenchmarkContext.Instrumentation.sleuthReactiveSimpleManual);
|
||||
}
|
||||
|
||||
private void run(BenchmarkContext.Instrumentation value) throws Exception {
|
||||
BenchmarkContext context = new BenchmarkContext();
|
||||
System.out.println("\n\n\n\n WILL WORK WITH [" + value + "]\n\n\n\n");
|
||||
context.instrumentation = value;
|
||||
context.setup();
|
||||
|
||||
try {
|
||||
context.run(value);
|
||||
}
|
||||
finally {
|
||||
context.clean();
|
||||
}
|
||||
System.out.println("\n\n FINISHED WITH [" + value + "]\n\n\n\n");
|
||||
}
|
||||
|
||||
public static class BenchmarkContext {
|
||||
|
||||
volatile ConfigurableApplicationContext applicationContext;
|
||||
volatile InputDestination input;
|
||||
volatile OutputDestination output;
|
||||
@Param
|
||||
private Instrumentation instrumentation;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
this.applicationContext = initContext();
|
||||
this.input = this.applicationContext.getBean(InputDestination.class);
|
||||
this.output = this.applicationContext.getBean(OutputDestination.class);
|
||||
}
|
||||
|
||||
protected ConfigurableApplicationContext initContext() {
|
||||
SpringApplication application = new SpringApplicationBuilder(SleuthBenchmarkingStreamApplication.class)
|
||||
.web(WebApplicationType.REACTIVE).application();
|
||||
return application.run(runArgs());
|
||||
}
|
||||
|
||||
protected String[] runArgs() {
|
||||
List<String> strings = new ArrayList<>();
|
||||
strings.addAll(Arrays.asList("--spring.jmx.enabled=false",
|
||||
"--spring.application.name=defaultTraceContextForStream" + instrumentation.name()));
|
||||
strings.addAll(instrumentation.entires.stream().map(s -> "--" + s).collect(Collectors.toList()));
|
||||
return strings.toArray(new String[0]);
|
||||
}
|
||||
|
||||
void run(Instrumentation value) {
|
||||
System.out.println("Sending the message to input");
|
||||
input.send(MessageBuilder.withPayload("hello".getBytes())
|
||||
.setHeader("b3", "4883117762eb9420-4883117762eb9420-1").build());
|
||||
System.out.println("Retrieving the message for tests");
|
||||
Message<byte[]> message = output.receive(200L);
|
||||
System.out.println("Got the message from output");
|
||||
assertThat(message).isNotNull();
|
||||
System.out.println("Message is not null");
|
||||
assertThat(message.getPayload()).isEqualTo("HELLO".getBytes());
|
||||
System.out.println("Payload is HELLO");
|
||||
if (!value.toString().toLowerCase().contains("nosleuth")) {
|
||||
String b3 = message.getHeaders().get("b3", String.class);
|
||||
System.out.println("Checking the b3 header [" + b3 + "]");
|
||||
assertThat(b3).startsWith("4883117762eb9420");
|
||||
}
|
||||
}
|
||||
|
||||
@TearDown
|
||||
public void clean() throws Exception {
|
||||
Tracing current = Tracing.current();
|
||||
if (current != null) {
|
||||
current.close();
|
||||
}
|
||||
try {
|
||||
this.applicationContext.close();
|
||||
}
|
||||
catch (Exception ig) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public enum Instrumentation {
|
||||
|
||||
noSleuthSimple("spring.sleuth.enabled=false,spring.sleuth.function.type=simple");
|
||||
|
||||
private Set<String> entires = new HashSet<>();
|
||||
|
||||
Instrumentation(String key, String value) {
|
||||
this.entires.add(key + "=" + value);
|
||||
}
|
||||
|
||||
Instrumentation(String commaSeparated) {
|
||||
this.entires.addAll(StringUtils.commaDelimitedListToSet(commaSeparated));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(TestChannelBinderConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -14,10 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.benchmarks.jmh.benchmarks;
|
||||
package org.springframework.cloud.sleuth.benchmarks.jmh.mvc;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import jmh.mbr.junit5.Microbenchmark;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
@@ -37,13 +38,18 @@ import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
@Measurement(iterations = 5)
|
||||
@Warmup(iterations = 10)
|
||||
@Fork(3)
|
||||
@Measurement(iterations = 5, time = 1)
|
||||
@Warmup(iterations = 10, time = 1)
|
||||
@Fork(2)
|
||||
@BenchmarkMode(Mode.SampleTime)
|
||||
@OutputTimeUnit(TimeUnit.MICROSECONDS)
|
||||
@Threads(Threads.MAX)
|
||||
public class AnnotationBenchmarks {
|
||||
@Microbenchmark
|
||||
public class AnnotationBenchmarksTests {
|
||||
|
||||
static {
|
||||
System.setProperty("jmh.mbr.report.publishTo", "csv:annotation.csv");
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void manuallyCreatedSpans(BenchmarkContext context) throws Exception {
|
||||
@@ -64,9 +70,8 @@ public class AnnotationBenchmarks {
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
this.withSleuth = new SpringApplication(SleuthBenchmarkingSpringApp.class)
|
||||
.run("--spring.jmx.enabled=false",
|
||||
"--spring.application.name=withSleuth");
|
||||
this.withSleuth = new SpringApplication(SleuthBenchmarkingSpringApp.class).run("--spring.jmx.enabled=false",
|
||||
"--spring.application.name=withSleuth");
|
||||
this.sleuth = this.withSleuth.getBean(SleuthBenchmarkingSpringApp.class);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -14,10 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.benchmarks.jmh.benchmarks;
|
||||
package org.springframework.cloud.sleuth.benchmarks.jmh.mvc;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import jmh.mbr.junit5.Microbenchmark;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
@@ -37,13 +38,18 @@ import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
@Measurement(iterations = 5)
|
||||
@Warmup(iterations = 10)
|
||||
@Fork(3)
|
||||
@Measurement(iterations = 5, time = 1)
|
||||
@Warmup(iterations = 10, time = 1)
|
||||
@Fork(2)
|
||||
@BenchmarkMode(Mode.SampleTime)
|
||||
@OutputTimeUnit(TimeUnit.MICROSECONDS)
|
||||
@Threads(Threads.MAX)
|
||||
public class AsyncBenchmarks {
|
||||
@Microbenchmark
|
||||
public class AsyncBenchmarksTests {
|
||||
|
||||
static {
|
||||
System.setProperty("jmh.mbr.report.publishTo", "csv:async.csv");
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void asyncMethodWithoutSleuth(BenchmarkContext context) throws Exception {
|
||||
@@ -68,18 +74,13 @@ public class AsyncBenchmarks {
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
this.withSleuth = new SpringApplication(SleuthBenchmarkingSpringApp.class)
|
||||
.run("--spring.jmx.enabled=false",
|
||||
"--spring.application.name=withSleuth");
|
||||
this.withoutSleuth = new SpringApplication(SleuthBenchmarkingSpringApp.class)
|
||||
.run("--spring.jmx.enabled=false",
|
||||
"--spring.application.name=withoutSleuth",
|
||||
"--spring.sleuth.enabled=false",
|
||||
"--spring.sleuth.async.enabled=false");
|
||||
this.tracedAsyncMethodHavingBean = this.withSleuth
|
||||
.getBean(SleuthBenchmarkingSpringApp.class);
|
||||
this.untracedAsyncMethodHavingBean = this.withoutSleuth
|
||||
.getBean(SleuthBenchmarkingSpringApp.class);
|
||||
this.withSleuth = new SpringApplication(SleuthBenchmarkingSpringApp.class).run("--spring.jmx.enabled=false",
|
||||
"--spring.application.name=withSleuth");
|
||||
this.withoutSleuth = new SpringApplication(SleuthBenchmarkingSpringApp.class).run(
|
||||
"--spring.jmx.enabled=false", "--spring.application.name=withoutSleuth",
|
||||
"--spring.sleuth.enabled=false", "--spring.sleuth.async.enabled=false");
|
||||
this.tracedAsyncMethodHavingBean = this.withSleuth.getBean(SleuthBenchmarkingSpringApp.class);
|
||||
this.untracedAsyncMethodHavingBean = this.withoutSleuth.getBean(SleuthBenchmarkingSpringApp.class);
|
||||
}
|
||||
|
||||
@TearDown
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.benchmarks.jmh.benchmarks;
|
||||
package org.springframework.cloud.sleuth.benchmarks.jmh.mvc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Callable;
|
||||
@@ -28,6 +28,7 @@ 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;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
@@ -62,17 +63,21 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@Warmup(iterations = 10)
|
||||
@Warmup(iterations = 5)
|
||||
@BenchmarkMode(Mode.SampleTime)
|
||||
@OutputTimeUnit(TimeUnit.MICROSECONDS)
|
||||
@Threads(Threads.MAX)
|
||||
public class HttpFilterBenchmarks {
|
||||
@Microbenchmark
|
||||
public class HttpFilterBenchmarksTests {
|
||||
|
||||
static {
|
||||
System.setProperty("jmh.mbr.report.publishTo", "csv:http_filter.csv");
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@Measurement(iterations = 5, time = 1)
|
||||
@Fork(3)
|
||||
public void filterWithoutSleuth(BenchmarkContext context)
|
||||
throws IOException, ServletException {
|
||||
@Fork(2)
|
||||
public void filterWithoutSleuth(BenchmarkContext context) throws IOException, ServletException {
|
||||
MockHttpServletRequest request = builder().buildRequest(new MockServletContext());
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
@@ -82,9 +87,8 @@ public class HttpFilterBenchmarks {
|
||||
|
||||
@Benchmark
|
||||
@Measurement(iterations = 5, time = 1)
|
||||
@Fork(3)
|
||||
public void filterWithSleuth(BenchmarkContext context)
|
||||
throws ServletException, IOException {
|
||||
@Fork(2)
|
||||
public void filterWithSleuth(BenchmarkContext context) throws ServletException, IOException {
|
||||
MockHttpServletRequest request = builder().buildRequest(new MockServletContext());
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
@@ -107,12 +111,10 @@ public class HttpFilterBenchmarks {
|
||||
}
|
||||
|
||||
private MockHttpServletRequestBuilder builder() {
|
||||
return get("/").accept(MediaType.APPLICATION_JSON).header("User-Agent",
|
||||
"MockMvc");
|
||||
return get("/").accept(MediaType.APPLICATION_JSON).header("User-Agent", "MockMvc");
|
||||
}
|
||||
|
||||
private void performRequest(MockMvc mockMvc, String url, String expectedResult)
|
||||
throws Exception {
|
||||
private void performRequest(MockMvc mockMvc, String url, String expectedResult) throws Exception {
|
||||
MvcResult mvcResult = mockMvc.perform(get("/" + url)).andExpect(status().isOk())
|
||||
.andExpect(request().asyncStarted()).andReturn();
|
||||
|
||||
@@ -135,16 +137,12 @@ public class HttpFilterBenchmarks {
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
this.withSleuth = new SpringApplication(SleuthBenchmarkingSpringApp.class)
|
||||
.run("--spring.jmx.enabled=false",
|
||||
"--spring.application.name=withSleuth");
|
||||
this.withSleuth = new SpringApplication(SleuthBenchmarkingSpringApp.class).run("--spring.jmx.enabled=false",
|
||||
"--spring.application.name=withSleuth");
|
||||
this.tracingFilter = this.withSleuth.getBean(TracingFilter.class);
|
||||
this.mockMvcForTracedController = MockMvcBuilders
|
||||
.standaloneSetup(
|
||||
this.withSleuth.getBean(SleuthBenchmarkingSpringApp.class))
|
||||
.build();
|
||||
this.mockMvcForUntracedController = MockMvcBuilders
|
||||
.standaloneSetup(new VanillaController()).build();
|
||||
.standaloneSetup(this.withSleuth.getBean(SleuthBenchmarkingSpringApp.class)).build();
|
||||
this.mockMvcForUntracedController = MockMvcBuilders.standaloneSetup(new VanillaController()).build();
|
||||
}
|
||||
|
||||
@TearDown
|
||||
@@ -162,8 +160,8 @@ public class HttpFilterBenchmarks {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.benchmarks.jmh.benchmarks;
|
||||
package org.springframework.cloud.sleuth.benchmarks.jmh.mvc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
@@ -23,6 +23,7 @@ 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;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
@@ -49,24 +50,26 @@ import static org.assertj.core.api.BDDAssertions.then;
|
||||
/**
|
||||
* We're checking how much overhead does the instrumentation of the RestTemplate take
|
||||
*/
|
||||
@Measurement(iterations = 5)
|
||||
@Warmup(iterations = 10)
|
||||
@Fork(3)
|
||||
@Measurement(iterations = 5, time = 1)
|
||||
@Warmup(iterations = 10, time = 1)
|
||||
@Fork(2)
|
||||
@BenchmarkMode(Mode.SampleTime)
|
||||
@OutputTimeUnit(TimeUnit.MICROSECONDS)
|
||||
@Threads(Threads.MAX)
|
||||
public class RestTemplateBenchmark {
|
||||
@Microbenchmark
|
||||
public class RestTemplateBenchmarkTests {
|
||||
|
||||
@Benchmark
|
||||
public void syncEndpointWithoutSleuth(BenchmarkContext context)
|
||||
throws IOException, ServletException {
|
||||
then(context.untracedTemplate.getForObject("/foo", String.class))
|
||||
.isEqualTo("foo");
|
||||
static {
|
||||
System.setProperty("jmh.mbr.report.publishTo", "csv:rest_template.csv");
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void syncEndpointWithSleuth(BenchmarkContext context)
|
||||
throws ServletException, IOException {
|
||||
public void syncEndpointWithoutSleuth(BenchmarkContext context) throws IOException, ServletException {
|
||||
then(context.untracedTemplate.getForObject("/foo", String.class)).isEqualTo("foo");
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void syncEndpointWithSleuth(BenchmarkContext context) throws ServletException, IOException {
|
||||
then(context.tracedTemplate.getForObject("/foo", String.class)).isEqualTo("foo");
|
||||
}
|
||||
|
||||
@@ -83,18 +86,14 @@ public class RestTemplateBenchmark {
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
new SpringApplication(SleuthBenchmarkingSpringApp.class).run(
|
||||
"--spring.jmx.enabled=false", "--spring.application.name=withSleuth");
|
||||
this.mockMvc = MockMvcBuilders
|
||||
.standaloneSetup(
|
||||
this.withSleuth.getBean(SleuthBenchmarkingSpringApp.class))
|
||||
new SpringApplication(SleuthBenchmarkingSpringApp.class).run("--spring.jmx.enabled=false",
|
||||
"--spring.application.name=withSleuth");
|
||||
this.mockMvc = MockMvcBuilders.standaloneSetup(this.withSleuth.getBean(SleuthBenchmarkingSpringApp.class))
|
||||
.build();
|
||||
this.tracedTemplate = new RestTemplate(
|
||||
new MockMvcClientHttpRequestFactory(this.mockMvc));
|
||||
this.tracedTemplate.setInterceptors(Collections.singletonList(
|
||||
this.withSleuth.getBean(TracingClientHttpRequestInterceptor.class)));
|
||||
this.untracedTemplate = new RestTemplate(
|
||||
new MockMvcClientHttpRequestFactory(this.mockMvc));
|
||||
this.tracedTemplate = new RestTemplate(new MockMvcClientHttpRequestFactory(this.mockMvc));
|
||||
this.tracedTemplate.setInterceptors(
|
||||
Collections.singletonList(this.withSleuth.getBean(TracingClientHttpRequestInterceptor.class)));
|
||||
this.untracedTemplate = new RestTemplate(new MockMvcClientHttpRequestFactory(this.mockMvc));
|
||||
}
|
||||
|
||||
@TearDown
|
||||
@@ -14,8 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.benchmarks.jmh.benchmarks;
|
||||
package org.springframework.cloud.sleuth.benchmarks.jmh.mvc;
|
||||
|
||||
import jmh.mbr.junit5.Microbenchmark;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
@@ -27,11 +28,18 @@ 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;
|
||||
|
||||
@Measurement(iterations = 5)
|
||||
@Warmup(iterations = 1)
|
||||
@Fork(value = 2, warmups = 0)
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
public class StartupBenchmark {
|
||||
@Microbenchmark
|
||||
public class StartupBenchmarkTests {
|
||||
|
||||
static {
|
||||
System.setProperty("jmh.mbr.report.publishTo", "csv:startup.csv");
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void withAnnotations(ApplicationState state) throws Exception {
|
||||
@@ -46,25 +54,21 @@ public class StartupBenchmark {
|
||||
|
||||
@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.run();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void withoutScheduled(ApplicationState state) throws Exception {
|
||||
state.setExtraArgs("--spring.sleuth.scheduled.enabled=false",
|
||||
"--spring.sleuth.async.enabled=false",
|
||||
state.setExtraArgs("--spring.sleuth.scheduled.enabled=false", "--spring.sleuth.async.enabled=false",
|
||||
"--spring.sleuth.annotation.enabled=false");
|
||||
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");
|
||||
state.setExtraArgs("--spring.sleuth.web.enabled=false", "--spring.sleuth.scheduled.enabled=false",
|
||||
"--spring.sleuth.async.enabled=false", "--spring.sleuth.annotation.enabled=false");
|
||||
state.run();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* 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.benchmarks.jmh.stream;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import brave.Tracing;
|
||||
import jmh.mbr.junit5.Microbenchmark;
|
||||
import org.junit.platform.commons.annotation.Testable;
|
||||
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.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;
|
||||
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.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.sleuth.benchmarks.app.stream.SleuthBenchmarkingStreamApplication;
|
||||
import org.springframework.cloud.stream.binder.test.InputDestination;
|
||||
import org.springframework.cloud.stream.binder.test.OutputDestination;
|
||||
import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@Measurement(iterations = 5, time = 1)
|
||||
@Warmup(iterations = 10, time = 1)
|
||||
@Fork(2)
|
||||
@BenchmarkMode(Mode.SampleTime)
|
||||
@OutputTimeUnit(TimeUnit.MILLISECONDS)
|
||||
@Microbenchmark
|
||||
public class MicroBenchmarkStreamTests {
|
||||
|
||||
static {
|
||||
System.setProperty("jmh.mbr.report.publishTo", "csv:stream.csv");
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@Testable
|
||||
public void testStream(BenchmarkContext context) throws Exception {
|
||||
context.run();
|
||||
}
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
public static class BenchmarkContext {
|
||||
|
||||
volatile ConfigurableApplicationContext applicationContext;
|
||||
|
||||
volatile InputDestination input;
|
||||
|
||||
volatile OutputDestination output;
|
||||
|
||||
@Param
|
||||
private Instrumentation instrumentation;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
this.applicationContext = initContext();
|
||||
this.input = this.applicationContext.getBean(InputDestination.class);
|
||||
this.output = this.applicationContext.getBean(OutputDestination.class);
|
||||
}
|
||||
|
||||
protected ConfigurableApplicationContext initContext() {
|
||||
SpringApplication application = new SpringApplicationBuilder(SleuthBenchmarkingStreamApplication.class)
|
||||
.web(WebApplicationType.REACTIVE).application();
|
||||
return application.run(runArgs());
|
||||
}
|
||||
|
||||
protected String[] runArgs() {
|
||||
List<String> strings = new ArrayList<>();
|
||||
strings.addAll(Arrays.asList("--spring.jmx.enabled=false",
|
||||
"--spring.application.name=defaultTraceContextForStream" + instrumentation.name()));
|
||||
strings.addAll(instrumentation.entires.stream().map(s -> "--" + s).collect(Collectors.toList()));
|
||||
return strings.toArray(new String[0]);
|
||||
}
|
||||
|
||||
void run() {
|
||||
// System.out.println("Sending the message to input");
|
||||
input.send(MessageBuilder.withPayload("hello".getBytes())
|
||||
.setHeader("b3", "4883117762eb9420-4883117762eb9420-1").build());
|
||||
// System.out.println("Retrieving the message for tests");
|
||||
Message<byte[]> message = output.receive(200L);
|
||||
// System.out.println("Got the message from output");
|
||||
assertThat(message).isNotNull();
|
||||
// System.out.println("Message is not null");
|
||||
assertThat(message.getPayload()).isEqualTo("HELLO".getBytes());
|
||||
// System.out.println("Payload is HELLO");
|
||||
if (!instrumentation.toString().toLowerCase().contains("nosleuth")) {
|
||||
String b3 = message.getHeaders().get("b3", String.class);
|
||||
// System.out.println("Checking the b3 header [" + b3 + "]");
|
||||
assertThat(b3).startsWith("4883117762eb9420");
|
||||
}
|
||||
}
|
||||
|
||||
@TearDown
|
||||
public void clean() throws Exception {
|
||||
Tracing current = Tracing.current();
|
||||
if (current != null) {
|
||||
current.close();
|
||||
}
|
||||
try {
|
||||
this.applicationContext.close();
|
||||
}
|
||||
catch (Exception ig) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public enum Instrumentation {
|
||||
|
||||
noSleuthSimple("spring.sleuth.enabled=false,spring.sleuth.function.type=simple"), sleuthSimple(
|
||||
"spring.sleuth.reactor.instrumentation-type=MANUAL,spring.sleuth.function.type=simple"), noSleuthReactiveSimple(
|
||||
"spring.sleuth.enabled=false,spring.sleuth.function.type=reactive_simple"), sleuthReactiveSimpleOnEach(
|
||||
"spring.sleuth.reactor.instrumentation-type=DECORATE_ON_EACH,spring.sleuth.integration.enabled=true,spring.sleuth.function.type=DECORATE_ON_EACH"),
|
||||
// This won't work with messaging
|
||||
// sleuthReactiveSimpleOnLast("spring.sleuth.reactor.instrumentation-type=DECORATE_ON_LAST,spring.sleuth.function.type=DECORATE_ON_LAST"),
|
||||
// NO FUNCTION, NO INTEGRATION, MANUAL OPERATORS
|
||||
sleuthSimpleManual(
|
||||
"spring.sleuth.function.enabled=false,spring.sleuth.integration.enabled=false,spring.sleuth.function.type=simple_manual"), sleuthReactiveSimpleManual(
|
||||
"spring.sleuth.function.enabled=false,spring.sleuth.integration.enabled=false,spring.sleuth.function.type=reactive_simple_manual"),
|
||||
// NO FUNCTION - OLD INTEGRATION STYLE
|
||||
sleuthSimpleNoFunctionInstrumentationManual(
|
||||
"spring.sleuth.function.type=simple_manual,spring.sleuth.function.enabled=false,spring.sleuth.integration.enabled=true,spring.sleuth.reactor.instrumentation-type=MANUAL"), sleuthReactiveSimpleNoFunctionInstrumentationManual(
|
||||
"spring.sleuth.function.type=reactive_simple_manual,spring.sleuth.function.enabled=false,spring.sleuth.integration.enabled=true,spring.sleuth.reactor.instrumentation-type=MANUAL");
|
||||
|
||||
private Set<String> entires = new HashSet<>();
|
||||
|
||||
Instrumentation(String key, String value) {
|
||||
this.entires.add(key + "=" + value);
|
||||
}
|
||||
|
||||
Instrumentation(String commaSeparated) {
|
||||
this.entires.addAll(StringUtils.commaDelimitedListToSet(commaSeparated));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(TestChannelBinderConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.benchmarks.jmh.webflux;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import brave.Tracing;
|
||||
import jmh.mbr.junit5.Microbenchmark;
|
||||
import org.junit.platform.commons.annotation.Testable;
|
||||
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.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;
|
||||
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.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.sleuth.benchmarks.app.webflux.SleuthBenchmarkingSpringWebFluxApp;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
@Measurement(iterations = 5, time = 1)
|
||||
@Warmup(iterations = 10, time = 1)
|
||||
@Fork(2)
|
||||
@BenchmarkMode(Mode.SampleTime)
|
||||
@OutputTimeUnit(TimeUnit.MILLISECONDS)
|
||||
@Microbenchmark
|
||||
public class MicroBenchmarkHttpTests {
|
||||
|
||||
static {
|
||||
System.setProperty("jmh.mbr.report.publishTo", "csv:http.csv");
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@Testable
|
||||
public void test(BenchmarkContext context) throws Exception {
|
||||
context.run();
|
||||
}
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
public static class BenchmarkContext {
|
||||
|
||||
volatile ConfigurableApplicationContext applicationContext;
|
||||
|
||||
volatile WebTestClient webTestClient;
|
||||
|
||||
@Param
|
||||
private Instrumentation instrumentation;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
this.applicationContext = initContext();
|
||||
this.webTestClient = WebTestClient.bindToApplicationContext(applicationContext).build();
|
||||
}
|
||||
|
||||
protected ConfigurableApplicationContext initContext() {
|
||||
SpringApplication application = new SpringApplicationBuilder(SleuthBenchmarkingSpringWebFluxApp.class)
|
||||
.web(WebApplicationType.REACTIVE).application();
|
||||
return application.run(runArgs());
|
||||
}
|
||||
|
||||
protected String[] runArgs() {
|
||||
return new String[] { "--spring.jmx.enabled=false",
|
||||
"--spring.application.name=defaultTraceContext" + instrumentation.name(),
|
||||
"--" + instrumentation.key + "=" + instrumentation.value };
|
||||
}
|
||||
|
||||
void run() {
|
||||
this.webTestClient.get().uri(this.instrumentation.url).header("X-B3-TraceId", "4883117762eb9420")
|
||||
.header("X-B3-SpanId", "4883117762eb9420").exchange().expectStatus().isOk();
|
||||
}
|
||||
|
||||
@TearDown
|
||||
public void clean() throws Exception {
|
||||
Tracing current = Tracing.current();
|
||||
if (current != null) {
|
||||
current.close();
|
||||
}
|
||||
try {
|
||||
this.applicationContext.close();
|
||||
}
|
||||
catch (Exception ig) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public enum Instrumentation {
|
||||
|
||||
noSleuthSimple("spring.sleuth.enabled", "false", "/simple"), sleuthSimpleManual(
|
||||
"spring.sleuth.reactor.instrumentation-type", "MANUAL",
|
||||
"/simple"), noSleuthComplex("spring.sleuth.enabled", "false", "/complexNoSleuth"), onEachComplex(
|
||||
"spring.sleuth.reactor.instrumentation-type", "DECORATE_ON_EACH",
|
||||
"/complex"), onLastComplex("spring.sleuth.reactor.instrumentation-type", "DECORATE_ON_LAST",
|
||||
"/complex"), onManualComplex("spring.sleuth.reactor.instrumentation-type", "MANUAL",
|
||||
"/complexManual");
|
||||
|
||||
private String key;
|
||||
|
||||
private String value;
|
||||
|
||||
private String url;
|
||||
|
||||
Instrumentation(String key, String value, String url) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.benchmarks.jmh.benchmarks;
|
||||
package org.springframework.cloud.sleuth.benchmarks.jmh.webflux;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -26,6 +26,7 @@ import brave.httpclient.TracingHttpClientBuilder;
|
||||
import brave.propagation.CurrentTraceContext;
|
||||
import brave.propagation.TraceContext;
|
||||
import brave.sampler.Sampler;
|
||||
import jmh.mbr.junit5.Microbenchmark;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
@@ -55,28 +56,38 @@ import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
@Measurement(iterations = 5, time = 1)
|
||||
@Warmup(iterations = 10, time = 1)
|
||||
@Fork(3)
|
||||
@Fork(2)
|
||||
@BenchmarkMode(Mode.SampleTime)
|
||||
@OutputTimeUnit(TimeUnit.MICROSECONDS)
|
||||
@Threads(2)
|
||||
@State(Scope.Benchmark)
|
||||
public class SpringWebFluxBenchmarks {
|
||||
@Microbenchmark
|
||||
public class SpringWebFluxBenchmarksTests {
|
||||
|
||||
static final SpanHandler FAKE_SPAN_HANDLER = new SpanHandler() {
|
||||
// intentionally anonymous to prevent logging fallback on NOOP
|
||||
};
|
||||
protected static TraceContext defaultTraceContext = TraceContext.newBuilder().traceIdHigh(333L).traceId(444L)
|
||||
.spanId(3).sampled(true).build();
|
||||
|
||||
static {
|
||||
System.setProperty("jmh.mbr.report.publishTo", "csv:webflux.csv");
|
||||
}
|
||||
|
||||
protected static TraceContext defaultTraceContext = TraceContext.newBuilder()
|
||||
.traceIdHigh(333L).traceId(444L).spanId(3).sampled(true).build();
|
||||
protected ConfigurableApplicationContext applicationContext;
|
||||
|
||||
protected SleuthBenchmarkingSpringWebFluxApp springWebFluxApp;
|
||||
|
||||
CloseableHttpClient client;
|
||||
|
||||
CloseableHttpClient tracedClient;
|
||||
|
||||
CloseableHttpClient unsampledClient;
|
||||
|
||||
private String baseUrl;
|
||||
|
||||
public static void main(String[] args) throws RunnerException {
|
||||
Options opt = new OptionsBuilder()
|
||||
.include(".*" + SpringWebFluxBenchmarks.class.getSimpleName() + ".*")
|
||||
Options opt = new OptionsBuilder().include(".*" + SpringWebFluxBenchmarksTests.class.getSimpleName() + ".*")
|
||||
.build();
|
||||
|
||||
new Runner(opt).run();
|
||||
@@ -87,8 +98,7 @@ public class SpringWebFluxBenchmarks {
|
||||
}
|
||||
|
||||
protected CloseableHttpClient newClient(HttpTracing httpTracing) {
|
||||
return TracingHttpClientBuilder.create(httpTracing).disableAutomaticRetries()
|
||||
.build();
|
||||
return TracingHttpClientBuilder.create(httpTracing).disableAutomaticRetries().build();
|
||||
}
|
||||
|
||||
protected CloseableHttpClient newClient() {
|
||||
@@ -107,21 +117,18 @@ public class SpringWebFluxBenchmarks {
|
||||
public void setup() {
|
||||
ConfigurableApplicationContext context = initContext();
|
||||
this.applicationContext = context;
|
||||
this.springWebFluxApp = this.applicationContext
|
||||
.getBean(SleuthBenchmarkingSpringWebFluxApp.class);
|
||||
this.springWebFluxApp = this.applicationContext.getBean(SleuthBenchmarkingSpringWebFluxApp.class);
|
||||
baseUrl = "http://127.0.0.1:" + springWebFluxApp.port + "/foo";
|
||||
client = newClient();
|
||||
tracedClient = newClient(HttpTracing
|
||||
.create(Tracing.newBuilder().addSpanHandler(FAKE_SPAN_HANDLER).build()));
|
||||
unsampledClient = newClient(HttpTracing.create(Tracing.newBuilder()
|
||||
.sampler(Sampler.NEVER_SAMPLE).addSpanHandler(FAKE_SPAN_HANDLER).build()));
|
||||
tracedClient = newClient(HttpTracing.create(Tracing.newBuilder().addSpanHandler(FAKE_SPAN_HANDLER).build()));
|
||||
unsampledClient = newClient(HttpTracing
|
||||
.create(Tracing.newBuilder().sampler(Sampler.NEVER_SAMPLE).addSpanHandler(FAKE_SPAN_HANDLER).build()));
|
||||
postSetUp();
|
||||
}
|
||||
|
||||
protected ConfigurableApplicationContext initContext() {
|
||||
SpringApplication application = new SpringApplicationBuilder(
|
||||
SleuthBenchmarkingSpringWebFluxApp.class).web(WebApplicationType.REACTIVE)
|
||||
.application();
|
||||
SpringApplication application = new SpringApplicationBuilder(SleuthBenchmarkingSpringWebFluxApp.class)
|
||||
.web(WebApplicationType.REACTIVE).application();
|
||||
customSpringApplication(application);
|
||||
return application.run(runArgs());
|
||||
}
|
||||
@@ -134,8 +141,7 @@ public class SpringWebFluxBenchmarks {
|
||||
}
|
||||
|
||||
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",
|
||||
"--spring.sleuth.enabled=true" };
|
||||
}
|
||||
|
||||
@@ -171,8 +177,7 @@ public class SpringWebFluxBenchmarks {
|
||||
|
||||
@Benchmark
|
||||
public void tracedClient_get_resumeTrace() throws Exception {
|
||||
try (CurrentTraceContext.Scope scope = Tracing.current().currentTraceContext()
|
||||
.newScope(defaultTraceContext)) {
|
||||
try (CurrentTraceContext.Scope scope = Tracing.current().currentTraceContext().newScope(defaultTraceContext)) {
|
||||
get(tracedClient);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -14,8 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.benchmarks.jmh.benchmarks;
|
||||
package org.springframework.cloud.sleuth.benchmarks.jmh.webflux;
|
||||
|
||||
import jmh.mbr.junit5.Microbenchmark;
|
||||
import org.openjdk.jmh.runner.Runner;
|
||||
import org.openjdk.jmh.runner.RunnerException;
|
||||
import org.openjdk.jmh.runner.options.Options;
|
||||
@@ -24,21 +25,23 @@ import org.openjdk.jmh.runner.options.OptionsBuilder;
|
||||
/**
|
||||
* @author alvin
|
||||
*/
|
||||
public class WithOutReactorSleuthSpringWebFluxBenchmarks extends SpringWebFluxBenchmarks {
|
||||
@Microbenchmark
|
||||
public class WithOutReactorSleuthSpringWebFluxBenchmarksTests extends SpringWebFluxBenchmarksTests {
|
||||
|
||||
static {
|
||||
System.setProperty("jmh.mbr.report.publishTo", "csv:webflux_no_reactor_instrumentation.csv");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws RunnerException {
|
||||
Options opt = new OptionsBuilder().include(
|
||||
".*" + WithOutReactorSleuthSpringWebFluxBenchmarks.class.getSimpleName()
|
||||
+ ".*")
|
||||
.build();
|
||||
Options opt = new OptionsBuilder()
|
||||
.include(".*" + WithOutReactorSleuthSpringWebFluxBenchmarksTests.class.getSimpleName() + ".*").build();
|
||||
|
||||
new Runner(opt).run();
|
||||
}
|
||||
|
||||
@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",
|
||||
"--spring.sleuth.enabled=true", "--spring.sleuth.reactor.enabled=false"
|
||||
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -14,8 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.benchmarks.jmh.benchmarks;
|
||||
package org.springframework.cloud.sleuth.benchmarks.jmh.webflux;
|
||||
|
||||
import jmh.mbr.junit5.Microbenchmark;
|
||||
import org.openjdk.jmh.runner.Runner;
|
||||
import org.openjdk.jmh.runner.RunnerException;
|
||||
import org.openjdk.jmh.runner.options.Options;
|
||||
@@ -24,20 +25,23 @@ import org.openjdk.jmh.runner.options.OptionsBuilder;
|
||||
/**
|
||||
* @author alvin
|
||||
*/
|
||||
public class WithOutSleuthSpringWebFluxBenchmarks extends SpringWebFluxBenchmarks {
|
||||
@Microbenchmark
|
||||
public class WithOutSleuthSpringWebFluxBenchmarksTests extends SpringWebFluxBenchmarksTests {
|
||||
|
||||
static {
|
||||
System.setProperty("jmh.mbr.report.publishTo", "csv:webflux_no_sleuth_instrumentation.csv");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws RunnerException {
|
||||
Options opt = new OptionsBuilder().include(
|
||||
".*" + WithOutSleuthSpringWebFluxBenchmarks.class.getSimpleName() + ".*")
|
||||
.build();
|
||||
Options opt = new OptionsBuilder()
|
||||
.include(".*" + WithOutSleuthSpringWebFluxBenchmarksTests.class.getSimpleName() + ".*").build();
|
||||
|
||||
new Runner(opt).run();
|
||||
}
|
||||
|
||||
@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",
|
||||
"--spring.sleuth.enabled=false" };
|
||||
}
|
||||
|
||||
16
pom.xml
16
pom.xml
@@ -174,6 +174,13 @@
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-dependencies</artifactId>
|
||||
<version>${spring-cloud-function.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-openfeign-dependencies</artifactId>
|
||||
@@ -181,6 +188,13 @@
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<!--<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-bom</artifactId>
|
||||
<version>${reactor.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>-->
|
||||
<!-- Spock -->
|
||||
<dependency>
|
||||
<groupId>org.spockframework</groupId>
|
||||
@@ -241,9 +255,11 @@
|
||||
<spring-cloud-gateway.version>3.0.0-SNAPSHOT</spring-cloud-gateway.version>
|
||||
<spring-cloud-circuitbreaker.version>2.0.0-SNAPSHOT</spring-cloud-circuitbreaker.version>
|
||||
<spring-cloud-stream.version>3.1.0-SNAPSHOT</spring-cloud-stream.version>
|
||||
<spring-cloud-function.version>3.1.0-SNAPSHOT</spring-cloud-function.version>
|
||||
<spring-cloud-netflix.version>3.0.0-SNAPSHOT</spring-cloud-netflix.version>
|
||||
<spring-cloud-openfeign.version>3.0.0-SNAPSHOT</spring-cloud-openfeign.version>
|
||||
<brave.version>5.12.3</brave.version>
|
||||
<!-- <reactor.version>2020.0.0-SNAPSHOT</reactor.version>-->
|
||||
<spring-security-boot-autoconfigure.version>2.3.1.RELEASE</spring-security-boot-autoconfigure.version>
|
||||
<disable.nohttp.checks>false</disable.nohttp.checks>
|
||||
<okhttp.version>3.14.6</okhttp.version>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Running JMH Benchmarks"
|
||||
./mvnw clean install -DskipTests --projects benchmarks --also-make -Pbenchmarks,jmh
|
||||
java -Djmh.ignoreLock=true -jar benchmarks/target/benchmarks.jar org.springframework.cloud.sleuth.benchmarks.jmh.* -rf csv -rff jmh-result.csv | tee target/benchmarks.log
|
||||
./mvnw clean verify -Djmh.mbr.report.publishTo=csv:http.csv -pl benchmarks -Pbenchmarks
|
||||
# java -Djmh.ignoreLock=true -jar benchmarks/target/benchmarks.jar org.springframework.cloud.sleuth.benchmarks.jmh.* -rf csv -rff jmh-result.csv | tee target/benchmarks.log
|
||||
|
||||
@@ -85,6 +85,11 @@
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-gateway</artifactId>
|
||||
@@ -100,6 +105,11 @@
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-context</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-core</artifactId>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -221,7 +221,7 @@ class TraceBaggageConfiguration {
|
||||
* {@link SpanHandler} beans, even if {@link SpanHandler#NOOP}, can trigger
|
||||
* {@code org.springframework.cloud.sleuth.sampler.SamplerCondition}
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Conditional(BaggageTagSpanHandlerCondition.class)
|
||||
@EnableConfigurationProperties(SleuthBaggageProperties.class)
|
||||
static class BaggageTagSpanHandlerConfiguration {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* 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.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import brave.propagation.TraceContextOrSamplingFlags;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
* Messaging helpers to manually parse and inject spans. We're treating message headers as
|
||||
* a context that gets passed through.
|
||||
*
|
||||
* The {@code forInputMessage} factory methods will retrieve the tracing context from the
|
||||
* message headers and set up a a child span in the header under key
|
||||
* {@link Span#getClass()} name. If you need to continue it or tag it, it's enough to
|
||||
* retrieve it from the headers.
|
||||
*
|
||||
* The first messaging span (the one that was first found in the input message) is present
|
||||
* under the {@code traceHandlerParentSpan} header key.
|
||||
*
|
||||
* When calling the {@code toOutputMessage} factory method, we will
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public final class MessagingSleuthOperator {
|
||||
|
||||
private static final Log log = LogFactory.getLog(MessagingSleuthOperator.class);
|
||||
|
||||
private MessagingSleuthOperator() {
|
||||
throw new IllegalStateException("You can't instantiate a utility class");
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a span wrapped operation for an input message.
|
||||
* @param tracing - tracing bean
|
||||
* @param message - message to wrap
|
||||
* @param withSpanInScope - an operation that will be wrapped in a span and the span
|
||||
* will be reported at the end
|
||||
* @param <T> - type of payload
|
||||
* @return message with tracing context
|
||||
*/
|
||||
public static <T> Message<T> forInputMessage(Tracing tracing, Message<T> message,
|
||||
Consumer<Message<T>> withSpanInScope) {
|
||||
TraceMessageHandler traceMessageHandler = TraceMessageHandler
|
||||
.forNonSpringIntegration(tracing);
|
||||
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())) {
|
||||
withSpanInScope.accept(wrappedInputMessage.msg);
|
||||
}
|
||||
catch (Exception e) {
|
||||
t = e;
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
traceMessageHandler.afterMessageHandled(wrappedInputMessage.childSpan, t);
|
||||
}
|
||||
return wrappedInputMessage.msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the input message and returns a message with a header containing a span.
|
||||
* @param tracing - tracing bean
|
||||
* @param message - input message to process
|
||||
* @param <T> - payload type
|
||||
* @return message with tracing context
|
||||
*/
|
||||
public static <T> Message<T> forInputMessage(Tracing tracing, Message<T> message) {
|
||||
TraceMessageHandler traceMessageHandler = TraceMessageHandler
|
||||
.forNonSpringIntegration(tracing);
|
||||
MessageAndSpans wrappedInputMessage = traceMessageHandler
|
||||
.wrapInputMessage(message, "");
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Wrapped input msg " + wrappedInputMessage);
|
||||
}
|
||||
return wrappedInputMessage.msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function converting an input message to a message with tracing headers.
|
||||
* @param tracing - tracing bean
|
||||
* @param inputMessage - input message to process
|
||||
* @param <T> input message type
|
||||
* @return function representation of input message with tracing context
|
||||
*/
|
||||
public static <T> Function<Message<T>, Message<T>> asFunction(Tracing tracing,
|
||||
Message<T> inputMessage) {
|
||||
return stringMessage -> MessagingSleuthOperator.forInputMessage(tracing,
|
||||
inputMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves tracing information from message headers.
|
||||
* @param tracing - tracing bean
|
||||
* @param message - message to process
|
||||
* @param <T> - payload type
|
||||
* @return span retrieved from message or {@code null} if there was no span
|
||||
*/
|
||||
public static <T> Span spanFromMessage(Tracing tracing, Message<T> message) {
|
||||
TraceMessageHandler traceMessageHandler = TraceMessageHandler
|
||||
.forNonSpringIntegration(tracing);
|
||||
Span span = traceMessageHandler.spanFromMessage(message);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Found the following span in message " + span);
|
||||
}
|
||||
return span;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves tracing information from message headers and applies the operation.
|
||||
* @param tracing - tracing bean
|
||||
* @param message - message to process
|
||||
* @param withSpanInScope - an operation that will be wrapped in a span but will not
|
||||
* be reported
|
||||
* @param <T> - payload type
|
||||
*/
|
||||
public static <T> void withSpanInScope(Tracing tracing, Message<T> message,
|
||||
Consumer<Message<T>> withSpanInScope) {
|
||||
Span span = spanFromMessage(tracing, message);
|
||||
Tracer tracer = tracing.tracer();
|
||||
try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
|
||||
withSpanInScope.accept(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves tracing information from message headers and applies the operation.
|
||||
* @param tracing - tracing bean
|
||||
* @param message - message to process
|
||||
* @param withSpanInScope - an operation that will be wrapped in a span but will not
|
||||
* be reported
|
||||
* @param <T> - payload type
|
||||
* @return a message with tracing headers.
|
||||
*/
|
||||
public static <T> Message<T> withSpanInScope(Tracing tracing, Message<T> message,
|
||||
Function<Message<T>, Message<T>> withSpanInScope) {
|
||||
Span span = spanFromMessage(tracing, message);
|
||||
Tracer tracer = tracing.tracer();
|
||||
try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
|
||||
return withSpanInScope.apply(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an output message with tracing 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 <T> - message payload
|
||||
* @return instrumented message
|
||||
*/
|
||||
public static <T> Message<T> handleOutputMessage(Tracing tracing,
|
||||
Message<T> message) {
|
||||
return handleOutputMessage(tracing, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an output message with tracing 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 throwable - exception that took place while processing the message
|
||||
* @param <T> - message payload
|
||||
* @return instrumented message
|
||||
*/
|
||||
public static <T> Message<T> handleOutputMessage(Tracing tracing, Message<T> message,
|
||||
Throwable throwable) {
|
||||
TraceMessageHandler traceMessageHandler = TraceMessageHandler
|
||||
.forNonSpringIntegration(tracing);
|
||||
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");
|
||||
return message;
|
||||
}
|
||||
MessageAndSpan messageAndSpan = traceMessageHandler.wrapOutputMessage(message,
|
||||
TraceContextOrSamplingFlags.create(span.context()),
|
||||
String.valueOf(message.getHeaders().getOrDefault("destination", "")));
|
||||
traceMessageHandler.afterMessageHandled(messageAndSpan.span, throwable);
|
||||
return messageAndSpan.msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports the span stored in the message.
|
||||
* @param tracing - tracing bean
|
||||
* @param message - message with tracing context
|
||||
* @param ex - potential exception that took place while processing
|
||||
* @param <T> - message payload
|
||||
* @return instrumented message
|
||||
*/
|
||||
public static <T> Message<T> afterMessageHandled(Tracing tracing, Message<T> message,
|
||||
Throwable ex) {
|
||||
TraceMessageHandler traceMessageHandler = TraceMessageHandler
|
||||
.forNonSpringIntegration(tracing);
|
||||
Span span = traceMessageHandler.spanFromMessage(message);
|
||||
traceMessageHandler.afterMessageHandled(span, ex);
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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.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;
|
||||
|
||||
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.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.autoconfig.TraceAutoConfiguration;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
* Auto-configuration} to enable tracing via Spring Cloud Function.
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(value = "spring.sleuth.function.enabled", matchIfMissing = true)
|
||||
@ConditionalOnBean(Tracing.class)
|
||||
@ConditionalOnClass({ Tracer.class, FunctionAroundWrapper.class })
|
||||
@AutoConfigureAfter(TraceAutoConfiguration.class)
|
||||
class TraceFunctionAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
TraceFunctionAroundWrapper traceFunctionAroundWrapper(Environment environment,
|
||||
Tracing tracing) {
|
||||
return new TraceFunctionAroundWrapper(environment, tracing);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TraceFunctionAroundWrapper extends FunctionAroundWrapper
|
||||
implements ApplicationListener<RefreshScopeRefreshedEvent> {
|
||||
|
||||
private static final Log log = LogFactory.getLog(TraceFunctionAroundWrapper.class);
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
private final Tracing tracing;
|
||||
|
||||
final Map<String, String> functionToDestinationCache = new ConcurrentHashMap<>();
|
||||
|
||||
TraceFunctionAroundWrapper(Environment environment, Tracing tracing) {
|
||||
this.environment = environment;
|
||||
this.tracing = tracing;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doApply(Message<byte[]> message,
|
||||
SimpleFunctionRegistry.FunctionInvocationWrapper targetFunction) {
|
||||
TraceMessageHandler traceMessageHandler = TraceMessageHandler
|
||||
.forNonSpringIntegration(this.tracing);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Will retrieve the tracing headers from the message");
|
||||
}
|
||||
MessageAndSpans wrappedInputMessage = traceMessageHandler
|
||||
.wrapInputMessage(message, inputDestination(targetFunction));
|
||||
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())) {
|
||||
result = targetFunction.apply(wrappedInputMessage.msg);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throwable = e;
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
traceMessageHandler.afterMessageHandled(wrappedInputMessage.childSpan,
|
||||
throwable);
|
||||
}
|
||||
Message msgResult = toMessage(result);
|
||||
MessageAndSpan wrappedOutputMessage = traceMessageHandler.wrapOutputMessage(
|
||||
msgResult,
|
||||
TraceContextOrSamplingFlags
|
||||
.create(wrappedInputMessage.parentSpan.context()),
|
||||
outputDestination(targetFunction));
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Wrapped output msg " + wrappedOutputMessage);
|
||||
}
|
||||
traceMessageHandler.afterMessageHandled(wrappedOutputMessage.span, null);
|
||||
return wrappedOutputMessage.msg;
|
||||
}
|
||||
|
||||
private Message toMessage(Object result) {
|
||||
if (!(result instanceof Message)) {
|
||||
return MessageBuilder.withPayload(result).build();
|
||||
}
|
||||
return (Message) result;
|
||||
}
|
||||
|
||||
private String inputDestination(
|
||||
SimpleFunctionRegistry.FunctionInvocationWrapper targetFunction) {
|
||||
String functionDefinition = targetFunction.getFunctionDefinition();
|
||||
return functionToDestinationCache
|
||||
.computeIfAbsent(functionDefinition,
|
||||
s -> this.environment.getProperty(
|
||||
"spring.cloud.stream.bindings." + s + "-in-0.destination",
|
||||
s));
|
||||
}
|
||||
|
||||
private String outputDestination(
|
||||
SimpleFunctionRegistry.FunctionInvocationWrapper targetFunction) {
|
||||
String functionDefinition = targetFunction.getFunctionDefinition();
|
||||
return functionToDestinationCache.computeIfAbsent(functionDefinition,
|
||||
s -> this.environment.getProperty(
|
||||
"spring.cloud.stream.bindings." + s + "-out-0.destination", s));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Context refreshed, will reset the cache");
|
||||
}
|
||||
functionToDestinationCache.clear();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* 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.ArrayList;
|
||||
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.cloud.sleuth.internal.SpanNameUtil;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.support.ErrorMessage;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.messaging.support.MessageHeaderAccessor;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
// TODO: Duplicates a lot from TraceChannelInterceptor, need to figure out how to merge the two
|
||||
class TraceMessageHandler {
|
||||
|
||||
private static final Log log = LogFactory.getLog(TraceMessageHandler.class);
|
||||
|
||||
/**
|
||||
* Using the literal "broker" until we come up with a better solution.
|
||||
*
|
||||
* <p>
|
||||
* If the message originated from a binder (consumer binding), there will be different
|
||||
* headers present (e.g. "KafkaHeaders.RECEIVED_TOPIC" Vs.
|
||||
* "AmqpHeaders.CONSUMER_QUEUE" (unless the application removes them before sending).
|
||||
* These don't represent the broker, rather a queue, and in any case the heuristics
|
||||
* are not great. At least we might be able to tell if this is rabbit or not (ex how
|
||||
* spring-rabbit works). We need to think this through before making an api, possibly
|
||||
* experimenting.
|
||||
*
|
||||
* <p>
|
||||
* If the app is outbound only (producer), there's no indication of what type the
|
||||
* destination broker is. This may hint at a non-manual solution being overwriting the
|
||||
* remoteServiceName later, similar to how servlet instrumentation lazy set
|
||||
* "http.route".
|
||||
*/
|
||||
private static final String REMOTE_SERVICE_NAME = "broker";
|
||||
|
||||
private static final String TRACE_HANDLER_PARENT_SPAN = "traceHandlerParentSpan";
|
||||
|
||||
private final Tracing tracing;
|
||||
|
||||
private final Tracer tracer;
|
||||
|
||||
private final TraceContext.Injector<MessageHeaderAccessor> injector;
|
||||
|
||||
private final TraceContext.Extractor<MessageHeaderAccessor> extractor;
|
||||
|
||||
private final Function<TraceContext, Span> preSendFunction;
|
||||
|
||||
private final TriConsumer<MessageHeaderAccessor, Span, Span> preSendMessageManipulator;
|
||||
|
||||
private final Function<TraceContext, Span> outputMessageSpanFunction;
|
||||
|
||||
TraceMessageHandler(Tracing tracing, Function<TraceContext, Span> preSendFunction,
|
||||
TriConsumer<MessageHeaderAccessor, Span, Span> preSendMessageManipulator,
|
||||
Function<TraceContext, Span> outputMessageSpanFunction) {
|
||||
this.tracing = tracing;
|
||||
this.tracer = tracing.tracer();
|
||||
this.injector = tracing.propagation().injector(MessageHeaderPropagation.INSTANCE);
|
||||
this.extractor = tracing.propagation()
|
||||
.extractor(MessageHeaderPropagation.INSTANCE);
|
||||
// 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<TraceContext, Span> preSendFunction = ctx -> tracer
|
||||
.nextSpan(TraceContextOrSamplingFlags.create(ctx)).name("handle").start();
|
||||
TriConsumer<MessageHeaderAccessor, Span, Span> preSendMessageManipulator = (
|
||||
headers, parentSpan, childSpan) -> {
|
||||
headers.setHeader("traceHandlerParentSpan", parentSpan);
|
||||
headers.setHeader(Span.class.getName(), childSpan);
|
||||
};
|
||||
Function<TraceContext, Span> postReceiveFunction = ctx -> tracer
|
||||
.nextSpan(TraceContextOrSamplingFlags.create(ctx));
|
||||
return new TraceMessageHandler(tracing, preSendFunction,
|
||||
preSendMessageManipulator, postReceiveFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the given input message with tracing headers and returns a corresponding
|
||||
* span.
|
||||
* @param message - message to wrap
|
||||
* @param destinationName - destination from which the message was received
|
||||
* @return a tuple with the wrapped message and a corresponding span
|
||||
*/
|
||||
MessageAndSpans wrapInputMessage(Message<?> message, String destinationName) {
|
||||
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(consumerSpan, destinationName);
|
||||
consumerSpan.finish();
|
||||
}
|
||||
// create and scope a span for the message processor
|
||||
Span span = this.preSendFunction.apply(consumerSpan.context());
|
||||
// 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);
|
||||
}
|
||||
if (message instanceof ErrorMessage) {
|
||||
return new MessageAndSpans(new ErrorMessage((Throwable) message.getPayload(),
|
||||
headers.getMessageHeaders()), consumerSpan, span);
|
||||
}
|
||||
headers.setImmutable();
|
||||
return new MessageAndSpans(
|
||||
new GenericMessage<>(message.getPayload(), headers.getMessageHeaders()),
|
||||
consumerSpan, span);
|
||||
}
|
||||
|
||||
Span spanFromMessage(Message<?> message) {
|
||||
MessageHeaderAccessor headers = mutableHeaderAccessor(message);
|
||||
Span span = span(headers, Span.class.getName());
|
||||
if (span != null) {
|
||||
return span;
|
||||
}
|
||||
span = span(headers, TRACE_HANDLER_PARENT_SPAN);
|
||||
if (span != null) {
|
||||
return span;
|
||||
}
|
||||
TraceContextOrSamplingFlags extracted = this.extractor.extract(headers);
|
||||
if (extracted == TraceContextOrSamplingFlags.EMPTY) {
|
||||
return null;
|
||||
}
|
||||
return this.tracer.nextSpan(extracted);
|
||||
}
|
||||
|
||||
private void addTags(SpanCustomizer result, String destinationName) {
|
||||
if (StringUtils.hasText(destinationName)) {
|
||||
result.tag("channel", SpanNameUtil.shorten(destinationName));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called either when message got received and processed or message got sent.
|
||||
* @param span - span that corresponds to the given operation
|
||||
* @param ex - an optional exception that occurred while processing / sending.
|
||||
*/
|
||||
void afterMessageHandled(Span span, Throwable ex) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Will finish the current span after message handled " + span);
|
||||
}
|
||||
finishSpan(span, ex);
|
||||
}
|
||||
|
||||
Span parentSpan(Message message) {
|
||||
return span(mutableHeaderAccessor(message), "traceHandlerParentSpan");
|
||||
}
|
||||
|
||||
Span consumerSpan(Message message) {
|
||||
return span(mutableHeaderAccessor(message), Span.class.getName());
|
||||
}
|
||||
|
||||
private Span span(MessageHeaderAccessor headerAccessor, String key) {
|
||||
return headerAccessor.getMessageHeaders().get(key, Span.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the given output message with tracing headers and returns a corresponding
|
||||
* span.
|
||||
* @param message - message to wrap
|
||||
* @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) {
|
||||
Message<?> retrievedMessage = getMessage(message);
|
||||
MessageHeaderAccessor headers = mutableHeaderAccessor(retrievedMessage);
|
||||
Span span = this.outputMessageSpanFunction.apply(parentSpan.context());
|
||||
clearTracingHeaders(headers);
|
||||
this.injector.inject(span.context(), headers);
|
||||
markProducerSpan(headers, span, destinationName);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Created a new span output message " + span);
|
||||
}
|
||||
return new MessageAndSpan(outputMessage(message, retrievedMessage, headers),
|
||||
span);
|
||||
}
|
||||
|
||||
private void markProducerSpan(MessageHeaderAccessor headers, Span span,
|
||||
String destinationName) {
|
||||
if (!span.isNoop()) {
|
||||
span.kind(Span.Kind.PRODUCER).name("send").start();
|
||||
span.remoteServiceName(toRemoteServiceName(headers));
|
||||
addTags(span, destinationName);
|
||||
}
|
||||
}
|
||||
|
||||
private String toRemoteServiceName(MessageHeaderAccessor headers) {
|
||||
for (String key : headers.getMessageHeaders().keySet()) {
|
||||
if (key.startsWith("kafka_")) {
|
||||
return "kafka";
|
||||
}
|
||||
else if (key.startsWith("amqp_")) {
|
||||
return "rabbitmq";
|
||||
}
|
||||
}
|
||||
return REMOTE_SERVICE_NAME;
|
||||
}
|
||||
|
||||
private Message<?> outputMessage(Message<?> originalMessage,
|
||||
Message<?> retrievedMessage, MessageHeaderAccessor additionalHeaders) {
|
||||
MessageHeaderAccessor headers = MessageHeaderAccessor
|
||||
.getMutableAccessor(originalMessage);
|
||||
clearTechnicalTracingHeaders(headers);
|
||||
if (originalMessage instanceof ErrorMessage) {
|
||||
ErrorMessage errorMessage = (ErrorMessage) originalMessage;
|
||||
headers.copyHeaders(MessageHeaderPropagation.propagationHeaders(
|
||||
additionalHeaders.getMessageHeaders(),
|
||||
this.tracing.propagation().keys()));
|
||||
return new ErrorMessage(errorMessage.getPayload(),
|
||||
isWebSockets(headers) ? headers.getMessageHeaders()
|
||||
: new MessageHeaders(headers.getMessageHeaders()),
|
||||
errorMessage.getOriginalMessage());
|
||||
}
|
||||
headers.copyHeaders(additionalHeaders.getMessageHeaders());
|
||||
return new GenericMessage<>(retrievedMessage.getPayload(),
|
||||
isWebSockets(headers) ? headers.getMessageHeaders()
|
||||
: new MessageHeaders(headers.getMessageHeaders()));
|
||||
}
|
||||
|
||||
private boolean isWebSockets(MessageHeaderAccessor headerAccessor) {
|
||||
return headerAccessor.getMessageHeaders().containsKey("stompCommand")
|
||||
|| headerAccessor.getMessageHeaders().containsKey("simpMessageType");
|
||||
}
|
||||
|
||||
private Message<?> getMessage(Message<?> message) {
|
||||
Object payload = message.getPayload();
|
||||
if (payload instanceof MessagingException) {
|
||||
MessagingException e = (MessagingException) payload;
|
||||
Message<?> failedMessage = e.getFailedMessage();
|
||||
return failedMessage != null ? failedMessage : message;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
private MessageHeaderAccessor mutableHeaderAccessor(Message<?> message) {
|
||||
MessageHeaderAccessor headers = MessageHeaderAccessor.getMutableAccessor(message);
|
||||
headers.setLeaveMutable(true);
|
||||
return headers;
|
||||
}
|
||||
|
||||
private void clearTracingHeaders(MessageHeaderAccessor headers) {
|
||||
List<String> keysToRemove = new ArrayList<>(this.tracing.propagation().keys());
|
||||
keysToRemove.add(Span.class.getName());
|
||||
keysToRemove.add("traceHandlerParentSpan");
|
||||
MessageHeaderPropagation.removeAnyTraceHeaders(headers, keysToRemove);
|
||||
}
|
||||
|
||||
private void clearTechnicalTracingHeaders(MessageHeaderAccessor headers) {
|
||||
MessageHeaderPropagation.removeAnyTraceHeaders(headers,
|
||||
Arrays.asList(Span.class.getName(), "traceHandlerParentSpan"));
|
||||
}
|
||||
|
||||
private void finishSpan(Span span, Throwable error) {
|
||||
if (span == null || span.isNoop()) {
|
||||
return;
|
||||
}
|
||||
if (error != null) { // an error occurred, adding error to span
|
||||
String message = error.getMessage();
|
||||
if (message == null) {
|
||||
message = error.getClass().getSimpleName();
|
||||
}
|
||||
span.tag("error", message);
|
||||
}
|
||||
span.finish();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MessageAndSpan {
|
||||
|
||||
final Message msg;
|
||||
|
||||
final Span span;
|
||||
|
||||
MessageAndSpan(Message msg, Span span) {
|
||||
this.msg = msg;
|
||||
this.span = span;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MessageAndSpan{" + "msg=" + this.msg + ", span=" + this.span + '}';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MessageAndSpans {
|
||||
|
||||
final Message msg;
|
||||
|
||||
final Span parentSpan;
|
||||
|
||||
final Span childSpan;
|
||||
|
||||
MessageAndSpans(Message msg, Span parentSpan, Span childSpan) {
|
||||
this.msg = msg;
|
||||
this.parentSpan = parentSpan;
|
||||
this.childSpan = childSpan;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MessageAndSpans{" + "msg=" + msg + ", parentSpan=" + parentSpan
|
||||
+ ", childSpan=" + childSpan + '}';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface TriConsumer<K, V, S> {
|
||||
|
||||
/**
|
||||
* Performs the operation given the specified arguments.
|
||||
* @param k the first input argument
|
||||
* @param v the second input argument
|
||||
* @param s the third input argument
|
||||
*/
|
||||
void accept(K k, V v, S s);
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Contains trace related messaging headers. The deprecated headers contained `-` which
|
||||
* for example in the JMS specs is invalid. That's why the public constants in this class
|
||||
@@ -58,6 +61,9 @@ public final class TraceMessageHeaders {
|
||||
*/
|
||||
public static final String SPAN_FLAGS_NAME = "spanFlags";
|
||||
|
||||
static List<String> HEADERS = Arrays.asList(SAMPLED_NAME, SPAN_FLAGS_NAME,
|
||||
SPAN_ID_NAME, SPAN_NAME_NAME, TRACE_ID_NAME, PARENT_ID_NAME);
|
||||
|
||||
private TraceMessageHeaders() {
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -20,12 +20,18 @@ 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;
|
||||
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.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.function.context.FunctionCatalog;
|
||||
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
|
||||
import org.springframework.cloud.stream.config.BinderFactoryAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.integration.channel.interceptor.GlobalChannelInterceptorWrapper;
|
||||
import org.springframework.integration.config.GlobalChannelInterceptor;
|
||||
@@ -46,8 +52,8 @@ import org.springframework.messaging.support.MessageHeaderAccessor;
|
||||
@AutoConfigureAfter({ TraceAutoConfiguration.class,
|
||||
TraceSpringMessagingAutoConfiguration.class })
|
||||
@OnMessagingEnabled
|
||||
@ConditionalOnProperty(value = "spring.sleuth.integration.enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties(SleuthMessagingProperties.class)
|
||||
@Conditional(TracingChannelInterceptorCondition.class)
|
||||
class TraceSpringIntegrationAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@@ -68,3 +74,34 @@ class TraceSpringIntegrationAutoConfiguration {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final class TracingChannelInterceptorCondition extends AnyNestedCondition {
|
||||
|
||||
private TracingChannelInterceptorCondition() {
|
||||
super(ConfigurationPhase.REGISTER_BEAN);
|
||||
}
|
||||
|
||||
@ConditionalOnMissingClass("org.springframework.cloud.function.context.FunctionCatalog")
|
||||
@ConditionalOnProperty(value = "spring.sleuth.integration.enabled",
|
||||
matchIfMissing = true)
|
||||
static class OnFunctionMissing {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnClass(FunctionCatalog.class)
|
||||
@ConditionalOnBean(BinderFactoryAutoConfiguration.class)
|
||||
@ConditionalOnProperty(value = "spring.sleuth.integration.enabled",
|
||||
matchIfMissing = true)
|
||||
static class OnFunctionPresentAndEnableBinding {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnClass(FunctionCatalog.class)
|
||||
@ConditionalOnMissingBean(BinderFactoryAutoConfiguration.class)
|
||||
@ConditionalOnProperty(value = "spring.sleuth.integration.enabled",
|
||||
havingValue = "true")
|
||||
static class OnFunctionPresentEnableBindingOffAndIntegrationExplicitlyOn {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -24,6 +24,8 @@ import brave.propagation.TraceContext;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
import reactor.core.CoreSubscriber;
|
||||
import reactor.core.Fuseable;
|
||||
import reactor.core.Scannable;
|
||||
@@ -96,13 +98,6 @@ public abstract class ReactorSleuth {
|
||||
return sub;
|
||||
}
|
||||
|
||||
Context context = sub.currentContext();
|
||||
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Spring context [" + springContext + "], Reactor context ["
|
||||
+ context + "], name [" + name(sub) + "]");
|
||||
}
|
||||
|
||||
// Try to get the current trace context bean, lenient when there are problems
|
||||
CurrentTraceContext currentTraceContext = lazyCurrentTraceContext.get();
|
||||
if (currentTraceContext == null) {
|
||||
@@ -118,6 +113,12 @@ public abstract class ReactorSleuth {
|
||||
return sub;
|
||||
}
|
||||
|
||||
Context context = contextWithBeans(springContext, sub);
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Spring context [" + springContext + "], Reactor context ["
|
||||
+ context + "], name [" + name(sub) + "]");
|
||||
}
|
||||
|
||||
TraceContext parent = traceContext(context, currentTraceContext);
|
||||
if (parent == null) {
|
||||
return sub; // no need to scope a null parent
|
||||
@@ -127,11 +128,46 @@ public abstract class ReactorSleuth {
|
||||
log.trace("Creating a scope passing span subscriber with Reactor Context "
|
||||
+ "[" + context + "] and name [" + name(sub) + "]");
|
||||
}
|
||||
// if (runStyle == Scannable.Attr.RunStyle.SYNC) {
|
||||
// return sub;
|
||||
// }
|
||||
return new ScopePassingSpanSubscriber<>(sub, context, currentTraceContext,
|
||||
parent);
|
||||
});
|
||||
}
|
||||
|
||||
private static <T> Context contextWithBeans(
|
||||
ConfigurableApplicationContext springContext, CoreSubscriber<? super T> sub) {
|
||||
Context context = sub.currentContext();
|
||||
if (!context.hasKey(Tracing.class)) {
|
||||
context = context.put(Tracing.class, springContext.getBean(Tracing.class));
|
||||
}
|
||||
if (!context.hasKey(CurrentTraceContext.class)) {
|
||||
context = context.put(CurrentTraceContext.class,
|
||||
springContext.getBean(CurrentTraceContext.class));
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
static <T> Function<? super Publisher<T>, ? extends Publisher<T>> springContextSpanOperator(
|
||||
ConfigurableApplicationContext springContext) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Spring Context passing operator [" + springContext + "]");
|
||||
}
|
||||
return Operators.liftPublisher((p, sub) -> {
|
||||
// We don't scope scalar results as they happen in an instant. This prevents
|
||||
// excessive overhead when using Flux/Mono #just, #empty, #error, etc.
|
||||
if (p instanceof Fuseable.ScalarCallable) {
|
||||
return sub;
|
||||
}
|
||||
if (!springContext.isActive()) {
|
||||
return sub;
|
||||
}
|
||||
final Context context = contextWithBeans(springContext, sub);
|
||||
return new SleuthContextOperator<>(context, sub);
|
||||
});
|
||||
}
|
||||
|
||||
static String name(CoreSubscriber<?> sub) {
|
||||
return Scannable.from(sub).name();
|
||||
}
|
||||
@@ -147,3 +183,54 @@ public abstract class ReactorSleuth {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SleuthContextOperator<T> implements Subscription, CoreSubscriber<T> {
|
||||
|
||||
private final Context context;
|
||||
|
||||
private final Subscriber<? super T> subscriber;
|
||||
|
||||
private Subscription s;
|
||||
|
||||
SleuthContextOperator(Context context, Subscriber<? super T> subscriber) {
|
||||
this.context = context;
|
||||
this.subscriber = subscriber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubscribe(Subscription subscription) {
|
||||
this.s = subscription;
|
||||
this.subscriber.onSubscribe(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void request(long n) {
|
||||
this.s.request(n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
this.s.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T o) {
|
||||
this.subscriber.onNext(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable throwable) {
|
||||
this.subscriber.onError(throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
this.subscriber.onComplete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context currentContext() {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -37,9 +37,14 @@ class SleuthReactorProperties {
|
||||
* 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}
|
||||
*/
|
||||
@Deprecated
|
||||
private boolean decorateOnEach = true;
|
||||
|
||||
private InstrumentationType instrumentationType = InstrumentationType.DECORATE_ON_EACH;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
@@ -48,12 +53,42 @@ class SleuthReactorProperties {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public boolean isDecorateOnEach() {
|
||||
return this.decorateOnEach;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setDecorateOnEach(boolean decorateOnEach) {
|
||||
this.decorateOnEach = decorateOnEach;
|
||||
this.instrumentationType = decorateOnEach ? InstrumentationType.DECORATE_ON_EACH
|
||||
: InstrumentationType.DECORATE_ON_LAST;
|
||||
}
|
||||
|
||||
public InstrumentationType getInstrumentationType() {
|
||||
return this.instrumentationType;
|
||||
}
|
||||
|
||||
public void setInstrumentationType(InstrumentationType instrumentationType) {
|
||||
this.instrumentationType = instrumentationType;
|
||||
}
|
||||
|
||||
public enum InstrumentationType {
|
||||
|
||||
/**
|
||||
* Wraps each operator in a Sleuth representation.
|
||||
*/
|
||||
DECORATE_ON_EACH,
|
||||
|
||||
/**
|
||||
* Wraps only the last operator in Sleuth representation.
|
||||
*/
|
||||
DECORATE_ON_LAST,
|
||||
|
||||
/**
|
||||
* Does not automatically wrap any operators.
|
||||
*/
|
||||
MANUAL;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,11 +16,14 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.reactor;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
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;
|
||||
import reactor.core.publisher.Hooks;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
@@ -45,6 +48,7 @@ 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;
|
||||
|
||||
/**
|
||||
@@ -77,29 +81,6 @@ class TraceReactorAutoConfiguration {
|
||||
@Autowired
|
||||
ConfigurableApplicationContext springContext;
|
||||
|
||||
@PreDestroy
|
||||
public void cleanupHooks() {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Cleaning up hooks");
|
||||
}
|
||||
SleuthReactorProperties reactorProperties = this.springContext
|
||||
.getBean(SleuthReactorProperties.class);
|
||||
if (reactorProperties.isDecorateOnEach()) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Resetting onEach operator instrumentation");
|
||||
}
|
||||
Hooks.resetOnEachOperator(SLEUTH_TRACE_REACTOR_KEY);
|
||||
}
|
||||
else {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Resetting onLast operator instrumentation");
|
||||
}
|
||||
Hooks.resetOnLastOperator(SLEUTH_TRACE_REACTOR_KEY);
|
||||
}
|
||||
Schedulers
|
||||
.removeExecutorServiceDecorator(SLEUTH_REACTOR_EXECUTOR_SERVICE_KEY);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
HookRegisteringBeanDefinitionRegistryPostProcessor traceHookRegisteringBeanDefinitionRegistryPostProcessor(
|
||||
@@ -149,26 +130,32 @@ class HooksRefresher implements ApplicationListener<RefreshScopeRefreshedEvent>
|
||||
}
|
||||
Hooks.resetOnEachOperator(SLEUTH_TRACE_REACTOR_KEY);
|
||||
Hooks.resetOnLastOperator(SLEUTH_TRACE_REACTOR_KEY);
|
||||
if (this.reactorProperties.isDecorateOnEach()) {
|
||||
switch (this.reactorProperties.getInstrumentationType()) {
|
||||
case DECORATE_ON_EACH:
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Decorating onEach operator instrumentation");
|
||||
}
|
||||
Hooks.onEachOperator(SLEUTH_TRACE_REACTOR_KEY,
|
||||
scopePassingSpanOperator(this.context));
|
||||
}
|
||||
else {
|
||||
break;
|
||||
case DECORATE_ON_LAST:
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Decorating onLast operator instrumentation");
|
||||
}
|
||||
Hooks.onLastOperator(SLEUTH_TRACE_REACTOR_KEY,
|
||||
scopePassingSpanOperator(this.context));
|
||||
break;
|
||||
case MANUAL:
|
||||
Hooks.onLastOperator(SLEUTH_TRACE_REACTOR_KEY,
|
||||
springContextSpanOperator(this.context));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class HookRegisteringBeanDefinitionRegistryPostProcessor
|
||||
implements BeanDefinitionRegistryPostProcessor {
|
||||
implements BeanDefinitionRegistryPostProcessor, Closeable {
|
||||
|
||||
private static final Log log = LogFactory
|
||||
.getLog(HookRegisteringBeanDefinitionRegistryPostProcessor.class);
|
||||
@@ -191,21 +178,25 @@ class HookRegisteringBeanDefinitionRegistryPostProcessor
|
||||
|
||||
static void setupHooks(ConfigurableApplicationContext springContext) {
|
||||
ConfigurableEnvironment environment = springContext.getEnvironment();
|
||||
boolean decorateOnEach = environment.getProperty(
|
||||
SleuthReactorProperties.InstrumentationType property = environment.getProperty(
|
||||
"spring.sleuth.reactor.instrumentation-type",
|
||||
SleuthReactorProperties.InstrumentationType.class,
|
||||
SleuthReactorProperties.InstrumentationType.DECORATE_ON_EACH);
|
||||
Boolean decorateOnEach = environment.getProperty(
|
||||
"spring.sleuth.reactor.decorate-on-each", Boolean.class, true);
|
||||
if (decorateOnEach) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Decorating onEach operator instrumentation");
|
||||
}
|
||||
Hooks.onEachOperator(SLEUTH_TRACE_REACTOR_KEY,
|
||||
scopePassingSpanOperator(springContext));
|
||||
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));
|
||||
}
|
||||
else {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Decorating onLast operator instrumentation");
|
||||
}
|
||||
Hooks.onLastOperator(SLEUTH_TRACE_REACTOR_KEY,
|
||||
scopePassingSpanOperator(springContext));
|
||||
else if (property == SleuthReactorProperties.InstrumentationType.DECORATE_ON_EACH) {
|
||||
decorateOnEach(springContext);
|
||||
}
|
||||
else if (property == SleuthReactorProperties.InstrumentationType.DECORATE_ON_LAST) {
|
||||
decorateOnLast(scopePassingSpanOperator(springContext));
|
||||
}
|
||||
else if (property == SleuthReactorProperties.InstrumentationType.MANUAL) {
|
||||
decorateOnLast(springContextSpanOperator(springContext));
|
||||
}
|
||||
Schedulers.setExecutorServiceDecorator(
|
||||
TraceReactorAutoConfiguration.SLEUTH_REACTOR_EXECUTOR_SERVICE_KEY,
|
||||
@@ -214,4 +205,31 @@ class HookRegisteringBeanDefinitionRegistryPostProcessor
|
||||
springContext, scheduledExecutorService));
|
||||
}
|
||||
|
||||
private static void decorateOnLast(
|
||||
Function<? super Publisher<Object>, ? extends Publisher<Object>> function) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Decorating onLast operator instrumentation");
|
||||
}
|
||||
Hooks.onLastOperator(SLEUTH_TRACE_REACTOR_KEY, function);
|
||||
}
|
||||
|
||||
private static void decorateOnEach(ConfigurableApplicationContext springContext) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Decorating onEach operator instrumentation");
|
||||
}
|
||||
Hooks.onEachOperator(SLEUTH_TRACE_REACTOR_KEY,
|
||||
scopePassingSpanOperator(springContext));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Cleaning up hooks");
|
||||
}
|
||||
Hooks.resetOnEachOperator(SLEUTH_TRACE_REACTOR_KEY);
|
||||
Hooks.resetOnLastOperator(SLEUTH_TRACE_REACTOR_KEY);
|
||||
Schedulers.removeExecutorServiceDecorator(
|
||||
TraceReactorAutoConfiguration.SLEUTH_REACTOR_EXECUTOR_SERVICE_KEY);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* 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.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user