diff --git a/docs/src/main/asciidoc/integrations.adoc b/docs/src/main/asciidoc/integrations.adoc
index 12670a2b0..04f70fe95 100644
--- a/docs/src/main/asciidoc/integrations.adoc
+++ b/docs/src/main/asciidoc/integrations.adoc
@@ -626,3 +626,11 @@ This feature is available for all tracer implementations.
If you have Spring Tx on the classpath we will instrument the `PlatformTransactionManager` and the `ReactiveTransactionManager` to create a span whenever a new transaction is created.
In order to disable this instrumentation set `spring.sleuth.tx.enabled` to `false`.
+
+[[sleuth-r2dbc-integration]]
+== R2DBC
+
+This feature is available for all tracer implementations.
+
+If you have R2DBC Proxy on the classpath we will instrument the `ConnectionFactory`so that it contains a custom `ProxyExecutionListener`.
+In order to disable this instrumentation set `spring.sleuth.r2dbc.enabled` to `false`.
diff --git a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/Span.java b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/Span.java
index 74373c54d..6cc8c84c3 100644
--- a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/Span.java
+++ b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/Span.java
@@ -1,213 +1,222 @@
-/*
- * Copyright 2013-2021 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.cloud.sleuth;
-
-import org.springframework.cloud.sleuth.propagation.Propagator;
-
-/**
- *
- * This API was heavily influenced by Brave. Parts of its documentation were taken
- * directly from Brave.
- *
- * Span is a single unit of work that needs to be started and stopped. Contains timing
- * information and events and tags.
- *
- * @author OpenZipkin Brave Authors
- * @author Marcin Grzejszczak
- * @since 3.0.0
- */
-public interface Span extends SpanCustomizer {
-
- /**
- * @return {@code true} when no recording is done and nothing is reported to an
- * external system. However, this span should still be injected into outgoing
- * requests. Use this flag to avoid performing expensive computation.
- */
- boolean isNoop();
-
- /**
- * @return {@link TraceContext} corresponding to this span.
- */
- TraceContext context();
-
- /**
- * Starts this span.
- * @return this span
- */
- Span start();
-
- /**
- * Sets a name on this span.
- * @param name name to set on the span
- * @return this span
- */
- Span name(String name);
-
- /**
- * Sets an event on this span.
- * @param value event name to set on the span
- * @return this span
- */
- Span event(String value);
-
- /**
- * Sets a tag on this span.
- * @param key tag key
- * @param value tag value
- * @return this span
- */
- Span tag(String key, String value);
-
- /**
- * Records an exception for this span.
- * @param throwable to record
- * @return this span
- */
- Span error(Throwable throwable);
-
- /**
- * Ends the span. The span gets stopped and recorded if not noop.
- */
- void end();
-
- /**
- * Ends the span. The span gets stopped but does not get recorded.
- */
- void abandon();
-
- /**
- * Sets the remote service name for the span.
- * @param remoteServiceName remote service name
- * @return this span
- * @since 3.0.3
- */
- default Span remoteServiceName(String remoteServiceName) {
- return this;
- }
-
- /**
- * Type of span. Can be used to specify additional relationships between spans in
- * addition to a parent/child relationship.
- *
- * Documentation of the enum taken from OpenTelemetry.
- */
- enum Kind {
-
- /**
- * Indicates that the span covers server-side handling of an RPC or other remote
- * request.
- */
- SERVER,
-
- /**
- * Indicates that the span covers the client-side wrapper around an RPC or other
- * remote request.
- */
- CLIENT,
-
- /**
- * Indicates that the span describes producer sending a message to a broker.
- * Unlike client and server, there is no direct critical path latency relationship
- * between producer and consumer spans.
- */
- PRODUCER,
-
- /**
- * Indicates that the span describes consumer receiving a message from a broker.
- * Unlike client and server, there is no direct critical path latency relationship
- * between producer and consumer spans.
- */
- CONSUMER
-
- }
-
- /**
- * In some cases (e.g. when dealing with
- * {@link Propagator#extract(Object, Propagator.Getter)}'s we want to create a span
- * that has not yet been started, yet it's heavily configurable (some options are not
- * possible to be set when a span has already been started). We can achieve that by
- * using a builder.
- *
- * Inspired by OpenZipkin Brave and OpenTelemetry API.
- */
- interface Builder {
-
- /**
- * Sets the parent of the built span.
- * @param context parent's context
- * @return this
- */
- Builder setParent(TraceContext context);
-
- /**
- * Sets no parent of the built span.
- * @return this
- */
- Builder setNoParent();
-
- /**
- * Sets the name of the span.
- * @param name span name
- * @return this
- */
- Builder name(String name);
-
- /**
- * Sets an event on the span.
- * @param value event value
- * @return this
- */
- Builder event(String value);
-
- /**
- * Sets a tag on the span.
- * @param key tag key
- * @param value tag value
- * @return this
- */
- Builder tag(String key, String value);
-
- /**
- * Sets an error on the span.
- * @param throwable error to set
- * @return this
- */
- Builder error(Throwable throwable);
-
- /**
- * Sets the kind on the span.
- * @param spanKind kind of the span
- * @return this
- */
- Builder kind(Span.Kind spanKind);
-
- /**
- * Sets the remote service name for the span.
- * @param remoteServiceName remote service name
- * @return this
- */
- Builder remoteServiceName(String remoteServiceName);
-
- /**
- * Builds and starts the span.
- * @return started span
- */
- Span start();
-
- }
-
-}
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.sleuth;
+
+import org.springframework.cloud.sleuth.propagation.Propagator;
+
+/**
+ *
+ * This API was heavily influenced by Brave. Parts of its documentation were taken
+ * directly from Brave.
+ *
+ * Span is a single unit of work that needs to be started and stopped. Contains timing
+ * information and events and tags.
+ *
+ * @author OpenZipkin Brave Authors
+ * @author Marcin Grzejszczak
+ * @since 3.0.0
+ */
+public interface Span extends SpanCustomizer {
+
+ /**
+ * @return {@code true} when no recording is done and nothing is reported to an
+ * external system. However, this span should still be injected into outgoing
+ * requests. Use this flag to avoid performing expensive computation.
+ */
+ boolean isNoop();
+
+ /**
+ * @return {@link TraceContext} corresponding to this span.
+ */
+ TraceContext context();
+
+ /**
+ * Starts this span.
+ * @return this span
+ */
+ Span start();
+
+ /**
+ * Sets a name on this span.
+ * @param name name to set on the span
+ * @return this span
+ */
+ Span name(String name);
+
+ /**
+ * Sets an event on this span.
+ * @param value event name to set on the span
+ * @return this span
+ */
+ Span event(String value);
+
+ /**
+ * Sets a tag on this span.
+ * @param key tag key
+ * @param value tag value
+ * @return this span
+ */
+ Span tag(String key, String value);
+
+ /**
+ * Records an exception for this span.
+ * @param throwable to record
+ * @return this span
+ */
+ Span error(Throwable throwable);
+
+ /**
+ * Ends the span. The span gets stopped and recorded if not noop.
+ */
+ void end();
+
+ /**
+ * Ends the span. The span gets stopped but does not get recorded.
+ */
+ void abandon();
+
+ /**
+ * Sets the remote service name for the span.
+ * @param remoteServiceName remote service name
+ * @return this span
+ * @since 3.0.3
+ */
+ default Span remoteServiceName(String remoteServiceName) {
+ return this;
+ }
+
+ /**
+ * Type of span. Can be used to specify additional relationships between spans in
+ * addition to a parent/child relationship.
+ *
+ * Documentation of the enum taken from OpenTelemetry.
+ */
+ enum Kind {
+
+ /**
+ * Indicates that the span covers server-side handling of an RPC or other remote
+ * request.
+ */
+ SERVER,
+
+ /**
+ * Indicates that the span covers the client-side wrapper around an RPC or other
+ * remote request.
+ */
+ CLIENT,
+
+ /**
+ * Indicates that the span describes producer sending a message to a broker.
+ * Unlike client and server, there is no direct critical path latency relationship
+ * between producer and consumer spans.
+ */
+ PRODUCER,
+
+ /**
+ * Indicates that the span describes consumer receiving a message from a broker.
+ * Unlike client and server, there is no direct critical path latency relationship
+ * between producer and consumer spans.
+ */
+ CONSUMER
+
+ }
+
+ /**
+ * In some cases (e.g. when dealing with
+ * {@link Propagator#extract(Object, Propagator.Getter)}'s we want to create a span
+ * that has not yet been started, yet it's heavily configurable (some options are not
+ * possible to be set when a span has already been started). We can achieve that by
+ * using a builder.
+ *
+ * Inspired by OpenZipkin Brave and OpenTelemetry API.
+ */
+ interface Builder {
+
+ /**
+ * Sets the parent of the built span.
+ * @param context parent's context
+ * @return this
+ */
+ Builder setParent(TraceContext context);
+
+ /**
+ * Sets no parent of the built span.
+ * @return this
+ */
+ Builder setNoParent();
+
+ /**
+ * Sets the name of the span.
+ * @param name span name
+ * @return this
+ */
+ Builder name(String name);
+
+ /**
+ * Sets an event on the span.
+ * @param value event value
+ * @return this
+ */
+ Builder event(String value);
+
+ /**
+ * Sets a tag on the span.
+ * @param key tag key
+ * @param value tag value
+ * @return this
+ */
+ Builder tag(String key, String value);
+
+ /**
+ * Sets an error on the span.
+ * @param throwable error to set
+ * @return this
+ */
+ Builder error(Throwable throwable);
+
+ /**
+ * Sets the kind on the span.
+ * @param spanKind kind of the span
+ * @return this
+ */
+ Builder kind(Span.Kind spanKind);
+
+ /**
+ * Sets the remote service name for the span.
+ * @param remoteServiceName remote service name
+ * @return this
+ */
+ Builder remoteServiceName(String remoteServiceName);
+
+ /**
+ * Sets the remote URL for the span.
+ * @param remoteUrl remote service name
+ * @return this
+ */
+ default Builder remoteUrl(String remoteUrl) {
+ return this;
+ }
+
+ /**
+ * Builds and starts the span.
+ * @return started span
+ */
+ Span start();
+
+ }
+
+}
diff --git a/spring-cloud-sleuth-autoconfigure/pom.xml b/spring-cloud-sleuth-autoconfigure/pom.xml
index 970d8d5a7..4be621272 100644
--- a/spring-cloud-sleuth-autoconfigure/pom.xml
+++ b/spring-cloud-sleuth-autoconfigure/pom.xml
@@ -133,6 +133,11 @@
rxjava
true
+
+ io.r2dbc
+ r2dbc-proxy
+ true
+
io.github.openfeign
feign-okhttp
diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/r2dbc/TraceConnectionFactoryBeanPostProcessor.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/r2dbc/TraceConnectionFactoryBeanPostProcessor.java
new file mode 100644
index 000000000..cf6cdaf2e
--- /dev/null
+++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/r2dbc/TraceConnectionFactoryBeanPostProcessor.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.sleuth.autoconfig.instrument.r2dbc;
+
+import io.r2dbc.spi.ConnectionFactory;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+
+/**
+ * Adds a tracing listener to the {@link ConnectionFactory}.
+ *
+ * @author Marcin Grzejszczak
+ * @since 3.1.0
+ */
+public class TraceConnectionFactoryBeanPostProcessor implements BeanPostProcessor {
+
+ private final BeanFactory beanFactory;
+
+ public TraceConnectionFactoryBeanPostProcessor(BeanFactory beanFactory) {
+ this.beanFactory = beanFactory;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+ if (bean instanceof ConnectionFactory) {
+ return wrapConnectionFactory((ConnectionFactory) bean);
+ }
+ return bean;
+ }
+
+ ConnectionFactory wrapConnectionFactory(ConnectionFactory bean) {
+ TraceProxyConnectionFactoryWrapper proxyPostProcessor = new TraceProxyConnectionFactoryWrapper(
+ this.beanFactory);
+ return proxyPostProcessor.apply(bean);
+ }
+
+}
diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/r2dbc/TraceProxyConnectionFactoryWrapper.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/r2dbc/TraceProxyConnectionFactoryWrapper.java
new file mode 100644
index 000000000..453e0d851
--- /dev/null
+++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/r2dbc/TraceProxyConnectionFactoryWrapper.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.sleuth.autoconfig.instrument.r2dbc;
+
+import java.util.function.Function;
+
+import io.r2dbc.proxy.ProxyConnectionFactory;
+import io.r2dbc.proxy.callback.ProxyConfig;
+import io.r2dbc.spi.ConnectionFactory;
+
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.cloud.sleuth.instrument.r2dbc.TraceProxyExecutionListener;
+
+class TraceProxyConnectionFactoryWrapper implements Function {
+
+ private final BeanFactory beanFactory;
+
+ private ObjectProvider proxyConfig;
+
+ TraceProxyConnectionFactoryWrapper(BeanFactory beanFactory) {
+ this.beanFactory = beanFactory;
+ }
+
+ @Override
+ public ConnectionFactory apply(ConnectionFactory connectionFactory) {
+ ProxyConnectionFactory.Builder builder = ProxyConnectionFactory.builder(connectionFactory);
+ proxyConfig().ifAvailable(builder::proxyConfig);
+ builder.listener(new TraceProxyExecutionListener(this.beanFactory, connectionFactory));
+ return builder.build();
+ }
+
+ private ObjectProvider proxyConfig() {
+ if (this.proxyConfig == null) {
+ this.proxyConfig = this.beanFactory.getBeanProvider(ProxyConfig.class);
+ }
+ return this.proxyConfig;
+ }
+
+}
diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/r2dbc/TraceR2dbcAutoConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/r2dbc/TraceR2dbcAutoConfiguration.java
new file mode 100644
index 000000000..4e9d1fed1
--- /dev/null
+++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/r2dbc/TraceR2dbcAutoConfiguration.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.sleuth.autoconfig.instrument.r2dbc;
+
+import io.r2dbc.proxy.callback.ProxyConfig;
+
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
+import org.springframework.cloud.sleuth.Tracer;
+import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
+ * Auto-configuration} enables Quartz span information propagation.
+ *
+ * @author Marcin Grzejszczak
+ * @since 3.1.0
+ */
+@Configuration(proxyBeanMethods = false)
+@ConditionalOnClass(ProxyConfig.class)
+@ConditionalOnBean(Tracer.class)
+@ConditionalOnProperty(value = "spring.sleuth.r2dbc.enabled", matchIfMissing = true)
+@AutoConfigureAfter({ BraveAutoConfiguration.class, R2dbcAutoConfiguration.class })
+public class TraceR2dbcAutoConfiguration {
+
+ @Bean
+ static TraceConnectionFactoryBeanPostProcessor traceConnectionFactoryBeanPostProcessor(BeanFactory beanFactory) {
+ return new TraceConnectionFactoryBeanPostProcessor(beanFactory);
+ }
+
+}
diff --git a/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
index 9a894ba23..97669ed28 100644
--- a/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
+++ b/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -172,6 +172,12 @@
"type": "java.lang.Boolean",
"description": "Enable Spring Batch instrumentation.",
"defaultValue": true
+ },
+ {
+ "name": "spring.sleuth.r2dbc.enabled",
+ "type": "java.lang.Boolean",
+ "description": "Enable R2dbc instrumentation.",
+ "defaultValue": true
}
]
}
diff --git a/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/spring.factories
index 7b8848a99..e4f77461c 100644
--- a/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/spring.factories
+++ b/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/spring.factories
@@ -23,6 +23,7 @@ org.springframework.cloud.sleuth.autoconfig.instrument.messaging.TraceSpringInte
org.springframework.cloud.sleuth.autoconfig.instrument.messaging.TraceSpringMessagingAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.messaging.TraceWebSocketAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.rsocket.TraceRSocketAutoConfiguration, \
+org.springframework.cloud.sleuth.autoconfig.instrument.r2dbc.TraceR2dbcAutoConfiguration, \
org.springframework.cloud.sleuth.autoconfig.instrument.tx.TraceTxAutoConfiguration, \
org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.brave.instrument.web.client.BraveWebClientAutoConfiguration,\
diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/rpc/BraveRpcAutoConfigurationIntegrationTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/rpc/BraveRpcAutoConfigurationIntegrationTests.java
index 43c4f9420..0c2ea1e76 100644
--- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/rpc/BraveRpcAutoConfigurationIntegrationTests.java
+++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/rpc/BraveRpcAutoConfigurationIntegrationTests.java
@@ -29,8 +29,10 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
+import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.config.GatewayAutoConfiguration;
import org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration;
@@ -59,7 +61,8 @@ public class BraveRpcAutoConfigurationIntegrationTests {
@EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class,
GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class,
- MongoAutoConfiguration.class, QuartzAutoConfiguration.class })
+ MongoAutoConfiguration.class, QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class,
+ R2dbcDataAutoConfiguration.class })
@Configuration(proxyBeanMethods = false)
public static class Config {
diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/web/client/WebClientTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/web/client/WebClientTests.java
index d874d7544..0e9cf4255 100644
--- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/web/client/WebClientTests.java
+++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/brave/instrument/web/client/WebClientTests.java
@@ -41,6 +41,8 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration;
+import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.cloud.gateway.config.GatewayAutoConfiguration;
@@ -145,8 +147,8 @@ public class WebClientTests {
}
@Configuration(proxyBeanMethods = false)
- @EnableAutoConfiguration(
- exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class })
+ @EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class,
+ R2dbcAutoConfiguration.class, R2dbcDataAutoConfiguration.class })
@DisableSecurity
public static class TestConfiguration {
diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/async/TraceAsyncDefaultAutoConfigurationTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/async/TraceAsyncDefaultAutoConfigurationTests.java
index 18bf5a1dd..4f001fb06 100644
--- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/async/TraceAsyncDefaultAutoConfigurationTests.java
+++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/async/TraceAsyncDefaultAutoConfigurationTests.java
@@ -25,8 +25,10 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
+import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.config.GatewayAutoConfiguration;
import org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration;
@@ -50,7 +52,8 @@ public class TraceAsyncDefaultAutoConfigurationTests {
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class,
GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class,
- MongoAutoConfiguration.class, QuartzAutoConfiguration.class })
+ MongoAutoConfiguration.class, QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class,
+ R2dbcDataAutoConfiguration.class })
static class Config {
@Bean
diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/quartz/TraceQuartzAutoConfigurationTest.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/quartz/TraceQuartzAutoConfigurationTest.java
index 8ed2cd5be..f5f6435f5 100644
--- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/quartz/TraceQuartzAutoConfigurationTest.java
+++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/quartz/TraceQuartzAutoConfigurationTest.java
@@ -27,8 +27,10 @@ import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
+import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.gateway.config.GatewayAutoConfiguration;
import org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration;
@@ -116,7 +118,8 @@ public class TraceQuartzAutoConfigurationTest {
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class,
GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class,
- MongoAutoConfiguration.class, QuartzAutoConfiguration.class })
+ MongoAutoConfiguration.class, QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class,
+ R2dbcDataAutoConfiguration.class })
public static class EnableAutoConfig {
}
diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/r2dbc/TraceConnectionFactoryBeanPostProcessorTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/r2dbc/TraceConnectionFactoryBeanPostProcessorTests.java
new file mode 100644
index 000000000..0e58cafb4
--- /dev/null
+++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/r2dbc/TraceConnectionFactoryBeanPostProcessorTests.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.sleuth.autoconfig.instrument.r2dbc;
+
+import io.r2dbc.spi.ConnectionFactory;
+import org.assertj.core.api.BDDAssertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.BDDMockito;
+
+import org.springframework.beans.factory.support.StaticListableBeanFactory;
+
+class TraceConnectionFactoryBeanPostProcessorTests {
+
+ @Test
+ void should_do_nothing_when_bean_not_connection_factory() {
+ TraceConnectionFactoryBeanPostProcessor processor = new TraceConnectionFactoryBeanPostProcessor(null) {
+ @Override
+ ConnectionFactory wrapConnectionFactory(ConnectionFactory bean) {
+ throw new AssertionError("This method must not be called");
+ }
+ };
+ Object before = new Object();
+
+ Object after = processor.postProcessAfterInitialization(before, "");
+
+ BDDAssertions.then(after).isSameAs(before);
+ }
+
+ @Test
+ void should_modify_the_connection_factory() {
+ TraceConnectionFactoryBeanPostProcessor processor = new TraceConnectionFactoryBeanPostProcessor(
+ new StaticListableBeanFactory());
+ ConnectionFactory before = BDDMockito.mock(ConnectionFactory.class);
+
+ Object after = processor.postProcessAfterInitialization(before, "");
+
+ BDDAssertions.then(after).isNotSameAs(before);
+ }
+
+}
diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/r2dbc/TraceR2dbcAutoConfigurationTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/r2dbc/TraceR2dbcAutoConfigurationTests.java
new file mode 100644
index 000000000..06c87fe45
--- /dev/null
+++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/r2dbc/TraceR2dbcAutoConfigurationTests.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.sleuth.autoconfig.instrument.r2dbc;
+
+import io.r2dbc.proxy.callback.ProxyConfig;
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.FilteredClassLoader;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.cloud.sleuth.autoconfig.TraceNoOpAutoConfiguration;
+
+class TraceR2dbcAutoConfigurationTests {
+
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withPropertyValues("spring.sleuth.noop.enabled=true").withConfiguration(
+ AutoConfigurations.of(TraceNoOpAutoConfiguration.class, TraceR2dbcAutoConfiguration.class));
+
+ @Test
+ void should_register_trace_bean_post_processor() {
+ this.contextRunner.run(
+ context -> Assertions.assertThat(context).hasSingleBean(TraceConnectionFactoryBeanPostProcessor.class));
+ }
+
+ @Test
+ void should_not_create_trace_bean_post_processor_when_no_proxy_on_classpath() {
+ this.contextRunner.withClassLoader(new FilteredClassLoader(ProxyConfig.class)).run(context -> Assertions
+ .assertThat(context).doesNotHaveBean(TraceConnectionFactoryBeanPostProcessor.class));
+ }
+
+}
diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/BraveWebClientAutoConfigurationTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/BraveWebClientAutoConfigurationTests.java
index 4767c32cf..8306face3 100644
--- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/BraveWebClientAutoConfigurationTests.java
+++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/BraveWebClientAutoConfigurationTests.java
@@ -29,8 +29,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
+import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.boot.web.client.RestTemplateCustomizer;
@@ -120,7 +122,8 @@ public class BraveWebClientAutoConfigurationTests {
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class,
GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class,
- MongoAutoConfiguration.class, QuartzAutoConfiguration.class })
+ MongoAutoConfiguration.class, QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class,
+ R2dbcDataAutoConfiguration.class })
static class Config {
// custom builder
diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/GH846Tests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/GH846Tests.java
index 9e0bf7b62..0fd557aad 100644
--- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/GH846Tests.java
+++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/GH846Tests.java
@@ -23,8 +23,10 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
+import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.gateway.config.GatewayAutoConfiguration;
@@ -54,7 +56,8 @@ public class GH846Tests {
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class,
GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class,
- MongoAutoConfiguration.class, QuartzAutoConfiguration.class })
+ MongoAutoConfiguration.class, QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class,
+ R2dbcDataAutoConfiguration.class })
static class App {
@Bean
diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/TraceWebClientDisabledTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/TraceWebClientDisabledTests.java
index 6875a7dc9..43e9d29d9 100644
--- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/TraceWebClientDisabledTests.java
+++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/TraceWebClientDisabledTests.java
@@ -20,8 +20,10 @@ import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
+import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.config.GatewayAutoConfiguration;
import org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration;
@@ -43,7 +45,8 @@ public class TraceWebClientDisabledTests {
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class,
GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class,
- MongoAutoConfiguration.class, QuartzAutoConfiguration.class })
+ MongoAutoConfiguration.class, QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class,
+ R2dbcDataAutoConfiguration.class })
public static class Config {
}
diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/zipkin2/ZipkinSamplerTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/zipkin2/ZipkinSamplerTests.java
index d1dcbb7e8..f3497539d 100644
--- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/zipkin2/ZipkinSamplerTests.java
+++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/zipkin2/ZipkinSamplerTests.java
@@ -22,8 +22,10 @@ import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
+import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.gateway.config.GatewayAutoConfiguration;
@@ -53,7 +55,8 @@ public class ZipkinSamplerTests {
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class,
GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class,
- MongoAutoConfiguration.class, QuartzAutoConfiguration.class })
+ MongoAutoConfiguration.class, QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class,
+ R2dbcDataAutoConfiguration.class })
static class TestConfig {
}
diff --git a/spring-cloud-sleuth-autoconfigure/src/test/resources/application.yml b/spring-cloud-sleuth-autoconfigure/src/test/resources/application.yml
index 7712f8c1c..1e05c6764 100644
--- a/spring-cloud-sleuth-autoconfigure/src/test/resources/application.yml
+++ b/spring-cloud-sleuth-autoconfigure/src/test/resources/application.yml
@@ -1,3 +1,3 @@
-logging.level.org.springframework.cloud: DEBUG
-
-spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.cloud.gateway.config.GatewayAutoConfiguration, org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration, org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration, org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration
+logging.level.org.springframework.cloud: DEBUG
+
+spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.cloud.gateway.config.GatewayAutoConfiguration, org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration, org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration, org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration, org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration, org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration, org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration
diff --git a/spring-cloud-sleuth-brave/pom.xml b/spring-cloud-sleuth-brave/pom.xml
index 1069e4dbe..1d66e9487 100644
--- a/spring-cloud-sleuth-brave/pom.xml
+++ b/spring-cloud-sleuth-brave/pom.xml
@@ -1,375 +1,375 @@
-
-
-
-
- 4.0.0
-
- spring-cloud-sleuth-brave
- jar
- Spring Cloud Sleuth Brave
- Spring Cloud Sleuth Brave
-
-
- org.springframework.cloud
- spring-cloud-sleuth
- 3.1.0-SNAPSHOT
- ..
-
-
-
-
- org.springframework.cloud
- spring-cloud-sleuth-instrumentation
-
-
- org.springframework.boot
- spring-boot-starter-web
- true
-
-
- io.micrometer
- micrometer-core
- true
-
-
- org.springframework.boot
- spring-boot-starter-webflux
- true
-
-
- io.projectreactor
- reactor-core
- true
-
-
- io.projectreactor.netty
- reactor-netty-http
- true
-
-
- org.reactivestreams
- reactive-streams
- true
-
-
- org.springframework.boot
- spring-boot-starter-websocket
- true
-
-
- org.springframework.boot
- spring-boot-configuration-processor
- true
-
-
- org.springframework.boot
- spring-boot-starter-actuator
- true
-
-
- org.springframework.cloud
- spring-cloud-commons
-
-
- org.springframework.cloud
- spring-cloud-stream
- true
-
-
- org.springframework.cloud
- spring-cloud-starter-gateway
- true
-
-
- org.springframework.cloud
- spring-cloud-starter-circuitbreaker-resilience4j
- true
-
-
- org.springframework.cloud
- spring-cloud-starter-openfeign
- true
-
-
- org.springframework.cloud
- spring-cloud-function-context
- true
-
-
- org.springframework.integration
- spring-integration-core
- true
-
-
- org.springframework.amqp
- spring-rabbit
- true
-
-
- org.springframework.kafka
- spring-kafka
- true
-
-
- org.apache.kafka
- kafka-streams
- true
-
-
- org.springframework.boot
- spring-boot-starter-security
- true
-
-
- org.springframework
- spring-context
-
-
- org.springframework.cloud
- spring-cloud-context
- true
-
-
- org.springframework.cloud
- spring-cloud-starter-loadbalancer
- true
-
-
- io.github.openfeign
- feign-core
- true
-
-
- io.github.openfeign.form
- feign-form-spring
- true
-
-
- io.reactivex
- rxjava
- true
-
-
- com.squareup.okhttp3
- okhttp
- ${okhttp.version}
- true
-
-
- org.apache.httpcomponents
- httpclient
- true
-
-
- io.github.openfeign
- feign-okhttp
- true
-
-
- org.springframework.boot
- spring-boot-starter-data-mongodb
- true
-
-
- org.aspectj
- aspectjrt
-
-
-
- io.zipkin.brave
- brave
-
-
- io.zipkin.reporter2
- *
-
-
- io.zipkin.zipkin2
- *
-
-
-
-
- io.zipkin.brave
- brave-context-slf4j
-
-
- io.zipkin.brave
- brave-instrumentation-messaging
-
-
- io.zipkin.brave
- brave-instrumentation-rpc
-
-
- io.zipkin.brave
- brave-instrumentation-spring-rabbit
-
-
- io.zipkin.brave
- brave-instrumentation-kafka-clients
-
-
- io.zipkin.brave
- brave-instrumentation-kafka-streams
-
-
- io.zipkin.brave
- brave-instrumentation-httpclient
-
-
- io.zipkin.brave
- brave-instrumentation-httpasyncclient
-
-
- io.zipkin.brave
- brave-instrumentation-jms
-
-
- io.zipkin.brave
- brave-instrumentation-mongodb
-
-
- io.zipkin.aws
- brave-propagation-aws
-
-
- javax.jms
- javax.jms-api
- true
-
-
- io.opentracing.brave
- brave-opentracing
- true
-
-
- org.apache.httpcomponents
- httpasyncclient
- true
-
-
- org.springframework
- spring-jms
- true
-
-
-
- io.github.lognet
- grpc-spring-boot-starter
- true
-
-
- org.springframework.boot
- spring-boot-starter
-
-
-
-
- io.zipkin.brave
- brave-instrumentation-grpc
- true
-
-
- io.zipkin.reporter2
- zipkin-reporter-metrics-micrometer
-
-
- io.micrometer
- micrometer-core
-
-
-
-
-
- io.lettuce
- lettuce-core
- true
-
-
-
- org.springframework.boot
- spring-boot-starter-quartz
- true
-
-
- org.springframework.boot
- spring-boot-autoconfigure-processor
- true
-
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
- io.zipkin.brave
- brave-instrumentation-http-tests
- test
-
-
- com.squareup.okhttp3
- mockwebserver
- test
-
-
- org.assertj
- assertj-core
- test
-
-
- org.awaitility
- awaitility
- test
-
-
- org.springframework.cloud
- spring-cloud-starter-netflix-eureka-client
- test
-
-
- com.tngtech.archunit
- archunit-junit5
- test
-
-
-
-
-
- fast
-
- false
-
-
-
-
- maven-surefire-plugin
-
- 4
- true
- -Xmx1024m -XX:MaxPermSize=256m
-
-
-
-
-
-
-
-
+
+
+
+
+ 4.0.0
+
+ spring-cloud-sleuth-brave
+ jar
+ Spring Cloud Sleuth Brave
+ Spring Cloud Sleuth Brave
+
+
+ org.springframework.cloud
+ spring-cloud-sleuth
+ 3.1.0-SNAPSHOT
+ ..
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-sleuth-instrumentation
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ true
+
+
+ io.micrometer
+ micrometer-core
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+ true
+
+
+ io.projectreactor
+ reactor-core
+ true
+
+
+ io.projectreactor.netty
+ reactor-netty-http
+ true
+
+
+ org.reactivestreams
+ reactive-streams
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-websocket
+ true
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+ true
+
+
+ org.springframework.cloud
+ spring-cloud-commons
+
+
+ org.springframework.cloud
+ spring-cloud-stream
+ true
+
+
+ org.springframework.cloud
+ spring-cloud-starter-gateway
+ true
+
+
+ org.springframework.cloud
+ spring-cloud-starter-circuitbreaker-resilience4j
+ true
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+ true
+
+
+ org.springframework.cloud
+ spring-cloud-function-context
+ true
+
+
+ org.springframework.integration
+ spring-integration-core
+ true
+
+
+ org.springframework.amqp
+ spring-rabbit
+ true
+
+
+ org.springframework.kafka
+ spring-kafka
+ true
+
+
+ org.apache.kafka
+ kafka-streams
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+ true
+
+
+ org.springframework
+ spring-context
+
+
+ org.springframework.cloud
+ spring-cloud-context
+ true
+
+
+ org.springframework.cloud
+ spring-cloud-starter-loadbalancer
+ true
+
+
+ io.github.openfeign
+ feign-core
+ true
+
+
+ io.github.openfeign.form
+ feign-form-spring
+ true
+
+
+ io.reactivex
+ rxjava
+ true
+
+
+ com.squareup.okhttp3
+ okhttp
+ ${okhttp.version}
+ true
+
+
+ org.apache.httpcomponents
+ httpclient
+ true
+
+
+ io.github.openfeign
+ feign-okhttp
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-data-mongodb
+ true
+
+
+ org.aspectj
+ aspectjrt
+
+
+
+ io.zipkin.brave
+ brave
+
+
+ io.zipkin.reporter2
+ *
+
+
+ io.zipkin.zipkin2
+ *
+
+
+
+
+ io.zipkin.brave
+ brave-context-slf4j
+
+
+ io.zipkin.brave
+ brave-instrumentation-messaging
+
+
+ io.zipkin.brave
+ brave-instrumentation-rpc
+
+
+ io.zipkin.brave
+ brave-instrumentation-spring-rabbit
+
+
+ io.zipkin.brave
+ brave-instrumentation-kafka-clients
+
+
+ io.zipkin.brave
+ brave-instrumentation-kafka-streams
+
+
+ io.zipkin.brave
+ brave-instrumentation-httpclient
+
+
+ io.zipkin.brave
+ brave-instrumentation-httpasyncclient
+
+
+ io.zipkin.brave
+ brave-instrumentation-jms
+
+
+ io.zipkin.brave
+ brave-instrumentation-mongodb
+
+
+ io.zipkin.aws
+ brave-propagation-aws
+
+
+ javax.jms
+ javax.jms-api
+ true
+
+
+ io.opentracing.brave
+ brave-opentracing
+ true
+
+
+ org.apache.httpcomponents
+ httpasyncclient
+ true
+
+
+ org.springframework
+ spring-jms
+ true
+
+
+
+ io.github.lognet
+ grpc-spring-boot-starter
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+
+ io.zipkin.brave
+ brave-instrumentation-grpc
+ true
+
+
+ io.zipkin.reporter2
+ zipkin-reporter-metrics-micrometer
+
+
+ io.micrometer
+ micrometer-core
+
+
+
+
+
+ io.lettuce
+ lettuce-core
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-quartz
+ true
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure-processor
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ io.zipkin.brave
+ brave-instrumentation-http-tests
+ test
+
+
+ com.squareup.okhttp3
+ mockwebserver
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+ org.awaitility
+ awaitility
+ test
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-client
+ test
+
+
+ com.tngtech.archunit
+ archunit-junit5
+ test
+
+
+
+
+
+ fast
+
+ false
+
+
+
+
+ maven-surefire-plugin
+
+ 4
+ true
+ -Xmx1024m -XX:MaxPermSize=256m
+
+
+
+
+
+
+
+
diff --git a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveSpanBuilder.java b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveSpanBuilder.java
index f47a0d12c..e07c0fbdd 100644
--- a/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveSpanBuilder.java
+++ b/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/bridge/BraveSpanBuilder.java
@@ -1,125 +1,144 @@
-/*
- * Copyright 2013-2021 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.cloud.sleuth.brave.bridge;
-
-import brave.Tracer;
-import brave.propagation.TraceContextOrSamplingFlags;
-
-import org.springframework.cloud.sleuth.Span;
-import org.springframework.cloud.sleuth.TraceContext;
-
-/**
- * Brave implementation of a {@link Span.Builder}.
- *
- * @author Marcin Grzejszczak
- * @since 3.0.0
- */
-class BraveSpanBuilder implements Span.Builder {
-
- brave.Span delegate;
-
- TraceContextOrSamplingFlags parentContext;
-
- private final Tracer tracer;
-
- private long startTimestamp;
-
- BraveSpanBuilder(Tracer tracer) {
- this.tracer = tracer;
- }
-
- BraveSpanBuilder(Tracer tracer, TraceContextOrSamplingFlags parentContext) {
- this.tracer = tracer;
- this.parentContext = parentContext;
- }
-
- brave.Span span() {
- if (this.delegate != null) {
- return this.delegate;
- }
- else if (this.parentContext != null) {
- this.delegate = this.tracer.nextSpan(this.parentContext);
- }
- else {
- this.delegate = this.tracer.nextSpan();
- }
- return this.delegate;
- }
-
- @Override
- public Span.Builder setParent(TraceContext context) {
- this.parentContext = TraceContextOrSamplingFlags.create(BraveTraceContext.toBrave(context));
- return this;
- }
-
- @Override
- public Span.Builder setNoParent() {
- return this;
- }
-
- @Override
- public Span.Builder name(String name) {
- span().name(name);
- return this;
- }
-
- @Override
- public Span.Builder event(String value) {
- span().annotate(value);
- return this;
- }
-
- @Override
- public Span.Builder tag(String key, String value) {
- span().tag(key, value);
- return this;
- }
-
- @Override
- public Span.Builder error(Throwable throwable) {
- span().error(throwable);
- return this;
- }
-
- @Override
- public Span.Builder kind(Span.Kind kind) {
- span().kind(kind != null ? brave.Span.Kind.valueOf(kind.toString()) : null);
- return this;
- }
-
- @Override
- public Span.Builder remoteServiceName(String remoteServiceName) {
- span().remoteServiceName(remoteServiceName);
- return this;
- }
-
- @Override
- public Span start() {
- if (this.startTimestamp > 0) {
- span().start(this.startTimestamp);
- }
- else {
- span().start();
- }
- return BraveSpan.fromBrave(this.delegate);
- }
-
- static Span.Builder toBuilder(Tracer tracer, TraceContextOrSamplingFlags context) {
- return new BraveSpanBuilder(tracer, context);
- }
-
-}
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.sleuth.brave.bridge;
+
+import java.net.URI;
+
+import brave.Tracer;
+import brave.propagation.TraceContextOrSamplingFlags;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.cloud.sleuth.Span;
+import org.springframework.cloud.sleuth.TraceContext;
+
+/**
+ * Brave implementation of a {@link Span.Builder}.
+ *
+ * @author Marcin Grzejszczak
+ * @since 3.0.0
+ */
+class BraveSpanBuilder implements Span.Builder {
+
+ private static final Log log = LogFactory.getLog(BraveSpanBuilder.class);
+
+ brave.Span delegate;
+
+ TraceContextOrSamplingFlags parentContext;
+
+ private final Tracer tracer;
+
+ private long startTimestamp;
+
+ BraveSpanBuilder(Tracer tracer) {
+ this.tracer = tracer;
+ }
+
+ BraveSpanBuilder(Tracer tracer, TraceContextOrSamplingFlags parentContext) {
+ this.tracer = tracer;
+ this.parentContext = parentContext;
+ }
+
+ brave.Span span() {
+ if (this.delegate != null) {
+ return this.delegate;
+ }
+ else if (this.parentContext != null) {
+ this.delegate = this.tracer.nextSpan(this.parentContext);
+ }
+ else {
+ this.delegate = this.tracer.nextSpan();
+ }
+ return this.delegate;
+ }
+
+ @Override
+ public Span.Builder setParent(TraceContext context) {
+ this.parentContext = TraceContextOrSamplingFlags.create(BraveTraceContext.toBrave(context));
+ return this;
+ }
+
+ @Override
+ public Span.Builder setNoParent() {
+ return this;
+ }
+
+ @Override
+ public Span.Builder name(String name) {
+ span().name(name);
+ return this;
+ }
+
+ @Override
+ public Span.Builder event(String value) {
+ span().annotate(value);
+ return this;
+ }
+
+ @Override
+ public Span.Builder tag(String key, String value) {
+ span().tag(key, value);
+ return this;
+ }
+
+ @Override
+ public Span.Builder error(Throwable throwable) {
+ span().error(throwable);
+ return this;
+ }
+
+ @Override
+ public Span.Builder kind(Span.Kind kind) {
+ span().kind(kind != null ? brave.Span.Kind.valueOf(kind.toString()) : null);
+ return this;
+ }
+
+ @Override
+ public Span.Builder remoteServiceName(String remoteServiceName) {
+ span().remoteServiceName(remoteServiceName);
+ return this;
+ }
+
+ @Override
+ public Span.Builder remoteUrl(String remoteUrl) {
+ try {
+ URI uri = URI.create(remoteUrl);
+ span().remoteIpAndPort(uri.getHost(), uri.getPort());
+ } catch (Exception e) {
+ if (log.isDebugEnabled()) {
+ log.debug("Failed to parse url [" + remoteUrl + "]. Will not set the value", e);
+ }
+ }
+ return this;
+ }
+
+ @Override
+ public Span start() {
+ if (this.startTimestamp > 0) {
+ span().start(this.startTimestamp);
+ }
+ else {
+ span().start();
+ }
+ return BraveSpan.fromBrave(this.delegate);
+ }
+
+ static Span.Builder toBuilder(Tracer tracer, TraceContextOrSamplingFlags context) {
+ return new BraveSpanBuilder(tracer, context);
+ }
+
+}
diff --git a/spring-cloud-sleuth-instrumentation/pom.xml b/spring-cloud-sleuth-instrumentation/pom.xml
index 7252a853b..f83080117 100644
--- a/spring-cloud-sleuth-instrumentation/pom.xml
+++ b/spring-cloud-sleuth-instrumentation/pom.xml
@@ -62,6 +62,11 @@
rsocket-core
true
+
+ io.r2dbc
+ r2dbc-proxy
+ true
+
io.projectreactor.kafka
reactor-kafka
diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/r2dbc/TraceProxyExecutionListener.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/r2dbc/TraceProxyExecutionListener.java
new file mode 100644
index 000000000..67d23c797
--- /dev/null
+++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/r2dbc/TraceProxyExecutionListener.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.r2dbc;
+
+import io.r2dbc.proxy.core.QueryExecutionInfo;
+import io.r2dbc.proxy.core.QueryInfo;
+import io.r2dbc.proxy.listener.ProxyExecutionListener;
+import io.r2dbc.spi.ConnectionFactory;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.boot.autoconfigure.r2dbc.R2dbcProperties;
+import org.springframework.cloud.sleuth.Span;
+import org.springframework.cloud.sleuth.Tracer;
+import org.springframework.util.StringUtils;
+
+/**
+ * Trace representation of a {@link ProxyExecutionListener}.
+ *
+ * @author Marcin Grzejszczak
+ * @since 3.1.0
+ */
+public class TraceProxyExecutionListener implements ProxyExecutionListener {
+
+ private static final Log log = LogFactory.getLog(TraceProxyExecutionListener.class);
+
+ private final BeanFactory beanFactory;
+
+ private final ConnectionFactory connectionFactory;
+
+ private Tracer tracer;
+
+ public TraceProxyExecutionListener(BeanFactory beanFactory, ConnectionFactory connectionFactory) {
+ this.beanFactory = beanFactory;
+ this.connectionFactory = connectionFactory;
+ }
+
+ @Override
+ public void beforeQuery(QueryExecutionInfo executionInfo) {
+ if (tracer().currentSpan() == null) {
+ return;
+ }
+ String name = this.connectionFactory.getMetadata().getName();
+ Span span = clientSpan(executionInfo, name);
+ if (log.isDebugEnabled()) {
+ log.debug("Created a new child span before query [" + span + "]");
+ }
+ tagQueries(executionInfo, span);
+ executionInfo.getValueStore().put(Span.class, span);
+ }
+
+ Span clientSpan(QueryExecutionInfo executionInfo, String name) {
+ R2dbcProperties r2dbcProperties = this.beanFactory.getBean(R2dbcProperties.class);
+ String url = r2dbcProperties.getUrl();
+ Span.Builder builder = tracer().spanBuilder().kind(Span.Kind.CLIENT).name("query")
+ .remoteServiceName(name)
+ .tag("rd2bc.connection", name).tag("rd2bc.thread", executionInfo.getThreadName());
+ if (StringUtils.hasText(url)) {
+ builder.remoteUrl(url);
+ }
+ return builder.start();
+ }
+
+ private void tagQueries(QueryExecutionInfo executionInfo, Span span) {
+ int i = 0;
+ for (QueryInfo queryInfo : executionInfo.getQueries()) {
+ span.tag("r2dbc.query[" + i + "]", queryInfo.getQuery());
+ i = i + 1;
+ }
+ }
+
+ @Override
+ public void afterQuery(QueryExecutionInfo executionInfo) {
+ Span span = executionInfo.getValueStore().get(Span.class, Span.class);
+ if (span != null) {
+ if (log.isDebugEnabled()) {
+ log.debug("Continued the child span in after query [" + span + "]");
+ }
+ final Throwable throwable = executionInfo.getThrowable();
+ if (throwable != null) {
+ span.error(throwable);
+ }
+ span.end();
+ }
+ }
+
+ @Override
+ public void eachQueryResult(QueryExecutionInfo executionInfo) {
+ Span span = executionInfo.getValueStore().get(Span.class, Span.class);
+ if (span != null) {
+ if (log.isDebugEnabled()) {
+ log.debug("Marking after query result for span [" + span + "]");
+ }
+ span.event("r2dbc.query_result");
+ }
+ }
+
+ private Tracer tracer() {
+ if (this.tracer == null) {
+ this.tracer = this.beanFactory.getBean(Tracer.class);
+ }
+ return this.tracer;
+ }
+
+}
diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/tx/TraceReactiveTransactionManager.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/tx/TraceReactiveTransactionManager.java
index 4a5f8fce2..c8602fcb6 100644
--- a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/tx/TraceReactiveTransactionManager.java
+++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/tx/TraceReactiveTransactionManager.java
@@ -110,7 +110,7 @@ public class TraceReactiveTransactionManager implements ReactiveTransactionManag
}
private Span spanFromContext(TraceContext traceContext) {
- try (CurrentTraceContext.Scope scope = currentTraceContext.maybeScope(traceContext)) {
+ try (CurrentTraceContext.Scope scope = currentTraceContext().maybeScope(traceContext)) {
return tracer().currentSpan();
}
}
diff --git a/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/r2dbc/TraceProxyExecutionListenerTests.java b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/r2dbc/TraceProxyExecutionListenerTests.java
new file mode 100644
index 000000000..3ad277a1c
--- /dev/null
+++ b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/r2dbc/TraceProxyExecutionListenerTests.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.r2dbc;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import io.r2dbc.proxy.core.QueryExecutionInfo;
+import io.r2dbc.proxy.core.ValueStore;
+import io.r2dbc.proxy.test.MockQueryExecutionInfo;
+import io.r2dbc.spi.Connection;
+import io.r2dbc.spi.ConnectionFactory;
+import io.r2dbc.spi.ConnectionFactoryMetadata;
+import org.junit.jupiter.api.Test;
+import org.reactivestreams.Publisher;
+
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.support.StaticListableBeanFactory;
+import org.springframework.cloud.sleuth.Span;
+import org.springframework.cloud.sleuth.tracer.SimpleSpan;
+import org.springframework.cloud.sleuth.tracer.SimpleTracer;
+
+import static org.assertj.core.api.BDDAssertions.then;
+
+class TraceProxyExecutionListenerTests {
+
+ SimpleTracer simpleTracer = new SimpleTracer();
+
+ ConnectionFactory connectionFactory = connectionFactory();
+
+ TraceProxyExecutionListener listener = new TraceProxyExecutionListener(beanFactory(), connectionFactory);
+
+ @Test
+ void should_do_nothing_on_before_query_when_there_was_no_previous_span() {
+ MockQueryExecutionInfo queryExecutionInfo = MockQueryExecutionInfo.empty();
+
+ listener.beforeQuery(queryExecutionInfo);
+
+ then(this.simpleTracer.spans).isEmpty();
+ then(queryExecutionInfo.getValueStore().get(Span.class)).isNull();
+ }
+
+ @Test
+ void should_do_nothing_on_after_query_when_there_was_no_previous_span() {
+ MockQueryExecutionInfo queryExecutionInfo = MockQueryExecutionInfo.empty();
+
+ listener.afterQuery(queryExecutionInfo);
+
+ then(this.simpleTracer.spans).isEmpty();
+ then(queryExecutionInfo.getValueStore().get(Span.class)).isNull();
+ }
+
+ @Test
+ void should_do_nothing_on_each_query_when_there_was_no_previous_span() {
+ MockQueryExecutionInfo queryExecutionInfo = MockQueryExecutionInfo.empty();
+
+ listener.eachQueryResult(queryExecutionInfo);
+
+ then(this.simpleTracer.spans).isEmpty();
+ then(queryExecutionInfo.getValueStore().get(Span.class)).isNull();
+ }
+
+ @Test
+ void should_put_a_child_span_in_value_store_when_a_span_was_already_in_context() {
+ MockQueryExecutionInfo queryExecutionInfo = MockQueryExecutionInfo.empty();
+ this.simpleTracer.nextSpan().start();
+ AtomicReference clientSpan = new AtomicReference<>();
+ listener = new TraceProxyExecutionListener(beanFactory(), connectionFactory) {
+ @Override
+ Span clientSpan(QueryExecutionInfo executionInfo, String name) {
+ Span span = super.clientSpan(executionInfo, name);
+ clientSpan.set(span);
+ return span;
+ }
+ };
+
+ listener.beforeQuery(queryExecutionInfo);
+
+ then(queryExecutionInfo.getValueStore().get(Span.class)).isSameAs(clientSpan.get());
+ }
+
+ @Test
+ void should_annotate_a_span_on_query_result() {
+ SimpleSpan span = new SimpleSpan();
+ ValueStore valueStore = ValueStore.create();
+ valueStore.put(Span.class, span);
+ MockQueryExecutionInfo queryExecutionInfo = MockQueryExecutionInfo.builder().valueStore(valueStore).build();
+
+ listener.eachQueryResult(queryExecutionInfo);
+
+ then(span.events).isNotEmpty();
+ }
+
+ @Test
+ void should_end_span_on_after_query() {
+ SimpleSpan span = new SimpleSpan().start();
+ ValueStore valueStore = ValueStore.create();
+ valueStore.put(Span.class, span);
+ MockQueryExecutionInfo queryExecutionInfo = MockQueryExecutionInfo.builder().throwable(new RuntimeException())
+ .valueStore(valueStore).build();
+
+ listener.afterQuery(queryExecutionInfo);
+
+ then(span.throwable).isNotNull();
+ then(span.ended).isTrue();
+ }
+
+ private ConnectionFactory connectionFactory() {
+ return new ConnectionFactory() {
+ @Override
+ public Publisher extends Connection> create() {
+ return null;
+ }
+
+ @Override
+ public ConnectionFactoryMetadata getMetadata() {
+ return () -> "my-name";
+ }
+ };
+ }
+
+ private BeanFactory beanFactory() {
+ StaticListableBeanFactory beanFactory = new StaticListableBeanFactory();
+ beanFactory.addBean("tracer", this.simpleTracer);
+ return beanFactory;
+ }
+
+}
diff --git a/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/tracer/SimpleSpan.java b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/tracer/SimpleSpan.java
index ad86a5817..ac609ac27 100644
--- a/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/tracer/SimpleSpan.java
+++ b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/tracer/SimpleSpan.java
@@ -16,7 +16,9 @@
package org.springframework.cloud.sleuth.tracer;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.springframework.cloud.sleuth.Span;
@@ -38,6 +40,14 @@ public class SimpleSpan implements Span {
public Throwable throwable;
+ public String remoteServiceName;
+
+ public Span.Kind spanKind;
+
+ public List events = new ArrayList<>();
+
+ public String name;
+
@Override
public boolean isNoop() {
return true;
@@ -56,11 +66,13 @@ public class SimpleSpan implements Span {
@Override
public SimpleSpan name(String name) {
+ this.name = name;
return this;
}
@Override
public SimpleSpan event(String value) {
+ this.events.add(value);
return this;
}
@@ -88,6 +100,7 @@ public class SimpleSpan implements Span {
@Override
public Span remoteServiceName(String remoteServiceName) {
+ this.remoteServiceName = remoteServiceName;
return this;
}
diff --git a/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/tracer/SimpleSpanBuilder.java b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/tracer/SimpleSpanBuilder.java
new file mode 100644
index 000000000..d73faa7fe
--- /dev/null
+++ b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/tracer/SimpleSpanBuilder.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.sleuth.tracer;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.cloud.sleuth.Span;
+import org.springframework.cloud.sleuth.TraceContext;
+
+/**
+ * A noop implementation. Does nothing.
+ *
+ * @author Marcin Grzejszczak
+ * @since 3.0.0
+ */
+class SimpleSpanBuilder implements Span.Builder {
+
+ List events = new ArrayList<>();
+
+ Map tags = new HashMap<>();
+
+ Throwable error;
+
+ Span.Kind spanKind;
+
+ String remoteServiceName;
+
+ String name;
+
+ @Override
+ public Span.Builder setParent(TraceContext context) {
+ return this;
+ }
+
+ @Override
+ public Span.Builder setNoParent() {
+ return this;
+ }
+
+ @Override
+ public Span.Builder name(String name) {
+ this.name = name;
+ return this;
+ }
+
+ @Override
+ public Span.Builder event(String value) {
+ this.events.add(value);
+ return this;
+ }
+
+ @Override
+ public Span.Builder tag(String key, String value) {
+ this.tags.put(key, value);
+ return this;
+ }
+
+ @Override
+ public Span.Builder error(Throwable throwable) {
+ this.error = throwable;
+ return this;
+ }
+
+ @Override
+ public Span.Builder kind(Span.Kind spanKind) {
+ this.spanKind = spanKind;
+ return this;
+ }
+
+ @Override
+ public Span.Builder remoteServiceName(String remoteServiceName) {
+ this.remoteServiceName = remoteServiceName;
+ return this;
+ }
+
+ @Override
+ public Span start() {
+ SimpleSpan span = new SimpleSpan();
+ this.tags.forEach(span::tag);
+ this.events.forEach(span::event);
+ span.remoteServiceName(this.remoteServiceName);
+ span.error(this.error);
+ span.spanKind = this.spanKind;
+ span.name(this.name);
+ return span;
+ }
+
+}
diff --git a/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/tracer/SimpleTracer.java b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/tracer/SimpleTracer.java
index c4cbf633e..d03c15227 100644
--- a/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/tracer/SimpleTracer.java
+++ b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/tracer/SimpleTracer.java
@@ -53,6 +53,13 @@ public class SimpleTracer implements Tracer {
return span;
}
+ public SimpleSpan getLastSpan() {
+ BDDAssertions.then(this.spans).isNotEmpty();
+ SimpleSpan span = this.spans.get(this.spans.size() - 1);
+ BDDAssertions.then(span.started).as("Span must be started").isTrue();
+ return span;
+ }
+
@Override
public SpanInScope withSpan(Span span) {
return new NoOpSpanInScope();
@@ -65,7 +72,10 @@ public class SimpleTracer implements Tracer {
@Override
public Span currentSpan() {
- return new SimpleSpan();
+ if (this.spans.isEmpty()) {
+ return null;
+ }
+ return this.spans.get(spans.size() - 1);
}
@Override
@@ -82,7 +92,7 @@ public class SimpleTracer implements Tracer {
@Override
public Span.Builder spanBuilder() {
- return null;
+ return new SimpleSpanBuilder();
}
@Override
diff --git a/tests/brave/pom.xml b/tests/brave/pom.xml
index 23f1b8daa..110bfbfc2 100644
--- a/tests/brave/pom.xml
+++ b/tests/brave/pom.xml
@@ -52,6 +52,7 @@
spring-cloud-sleuth-instrumentation-quartz-tests
spring-cloud-sleuth-instrumentation-reactor-tests
spring-cloud-sleuth-instrumentation-rxjava-tests
+ spring-cloud-sleuth-instrumentation-r2dbc-tests
spring-cloud-sleuth-instrumentation-scheduling-tests
spring-cloud-sleuth-instrumentation-task-tests
spring-cloud-sleuth-instrumentation-webflux-tests
diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/brave/baggage/MultipleHopsIntegrationTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/brave/baggage/MultipleHopsIntegrationTests.java
index 96dafebfe..f373f8588 100644
--- a/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/brave/baggage/MultipleHopsIntegrationTests.java
+++ b/tests/brave/spring-cloud-sleuth-instrumentation-baggage-tests/src/test/java/org/springframework/cloud/sleuth/brave/baggage/MultipleHopsIntegrationTests.java
@@ -21,9 +21,11 @@ import brave.baggage.BaggagePropagationConfig;
import brave.sampler.Sampler;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration;
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
+import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler;
@@ -72,8 +74,8 @@ public class MultipleHopsIntegrationTests
}
@Configuration(proxyBeanMethods = false)
- @EnableAutoConfiguration(
- exclude = { MongoAutoConfiguration.class, QuartzAutoConfiguration.class, JmxAutoConfiguration.class })
+ @EnableAutoConfiguration(exclude = { MongoAutoConfiguration.class, QuartzAutoConfiguration.class,
+ JmxAutoConfiguration.class, R2dbcAutoConfiguration.class, R2dbcDataAutoConfiguration.class })
static class Config {
@Bean
diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/JmsTracingConfigurationTest.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/JmsTracingConfigurationTest.java
index 781653b0f..b7d5b2105 100644
--- a/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/JmsTracingConfigurationTest.java
+++ b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/JmsTracingConfigurationTest.java
@@ -35,10 +35,12 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration;
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration;
import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
+import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
import org.springframework.boot.jms.XAConnectionFactoryWrapper;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
@@ -166,8 +168,8 @@ public class JmsTracingConfigurationTest {
}
@Configuration(proxyBeanMethods = false)
-@EnableAutoConfiguration(
- exclude = { KafkaAutoConfiguration.class, MongoAutoConfiguration.class, QuartzAutoConfiguration.class })
+@EnableAutoConfiguration(exclude = { KafkaAutoConfiguration.class, MongoAutoConfiguration.class,
+ QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class, R2dbcDataAutoConfiguration.class })
class JmsTestTracingConfiguration {
}
diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloSpringIntegration.java b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloSpringIntegration.java
index 66a907885..734e096d3 100644
--- a/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloSpringIntegration.java
+++ b/tests/brave/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloSpringIntegration.java
@@ -22,15 +22,18 @@ import brave.test.TestSpanHandler;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
+import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ImportResource;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.web.client.RestTemplate;
-@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
+@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
+ R2dbcAutoConfiguration.class, R2dbcDataAutoConfiguration.class })
@ImportResource("classpath:beans/applicationContext.xml")
@EnableIntegration
@EnableAsync
diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-r2dbc-tests/pom.xml b/tests/brave/spring-cloud-sleuth-instrumentation-r2dbc-tests/pom.xml
new file mode 100644
index 000000000..2758b3100
--- /dev/null
+++ b/tests/brave/spring-cloud-sleuth-instrumentation-r2dbc-tests/pom.xml
@@ -0,0 +1,94 @@
+
+
+
+
+ 4.0.0
+
+ spring-cloud-sleuth-instrumentation-r2dbc-tests
+ jar
+ Spring Cloud Sleuth Brave R2DBC Instrumentation Tests
+ Spring Cloud Sleuth Brave R2DBC Instrumentation Tests
+
+
+ org.springframework.cloud
+ spring-cloud-sleuth-tests-brave
+ 3.1.0-SNAPSHOT
+ ..
+
+
+
+ true
+
+
+
+
+
+
+ maven-deploy-plugin
+
+ true
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-sleuth-tests-common
+
+
+ org.springframework.cloud
+ spring-cloud-starter-sleuth
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
+
+ org.springframework.boot
+ spring-boot-starter-data-r2dbc
+
+
+ com.h2database
+ h2
+ runtime
+
+
+ io.r2dbc
+ r2dbc-h2
+ runtime
+
+
+ io.r2dbc
+ r2dbc-proxy
+
+
+ io.zipkin.brave
+ brave-tests
+
+
+ org.awaitility
+ awaitility
+
+
+
+
diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-r2dbc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/r2dbc/R2dbcIntegrationTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-r2dbc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/r2dbc/R2dbcIntegrationTests.java
new file mode 100644
index 000000000..c3e825fd1
--- /dev/null
+++ b/tests/brave/spring-cloud-sleuth-instrumentation-r2dbc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/r2dbc/R2dbcIntegrationTests.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.sleuth.brave.instrument.r2dbc;
+
+import brave.sampler.Sampler;
+
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler;
+import org.springframework.cloud.sleuth.test.TestSpanHandler;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.ContextConfiguration;
+
+@SpringBootTest
+@ContextConfiguration(classes = R2dbcIntegrationTests.Config.class)
+public class R2dbcIntegrationTests extends org.springframework.cloud.sleuth.instrument.r2dbc.R2dbcIntegrationTests {
+
+ @Configuration(proxyBeanMethods = false)
+ static class Config {
+
+ @Bean
+ TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) {
+ return new BraveTestSpanHandler(testSpanHandler);
+ }
+
+ @Bean
+ Sampler alwaysSampler() {
+ return Sampler.ALWAYS_SAMPLE;
+ }
+
+ @Bean
+ brave.test.TestSpanHandler braveTestSpanHandler() {
+ return new brave.test.TestSpanHandler();
+ }
+
+ }
+
+}
diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-r2dbc-tests/src/test/resources/application.yml b/tests/brave/spring-cloud-sleuth-instrumentation-r2dbc-tests/src/test/resources/application.yml
new file mode 100644
index 000000000..be7f4fe29
--- /dev/null
+++ b/tests/brave/spring-cloud-sleuth-instrumentation-r2dbc-tests/src/test/resources/application.yml
@@ -0,0 +1 @@
+logging.level.org.springframework.cloud: DEBUG
diff --git a/tests/common/pom.xml b/tests/common/pom.xml
index 3f6a1176d..f23cd0d2d 100644
--- a/tests/common/pom.xml
+++ b/tests/common/pom.xml
@@ -159,6 +159,16 @@
reactor-kafka
true
+
+ org.springframework.boot
+ spring-boot-starter-data-r2dbc
+ true
+
+
+ io.r2dbc
+ r2dbc-proxy
+ true
+
org.testcontainers
testcontainers
diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/r2dbc/R2dbcIntegrationTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/r2dbc/R2dbcIntegrationTests.java
new file mode 100644
index 000000000..c44d5a086
--- /dev/null
+++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/r2dbc/R2dbcIntegrationTests.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.r2dbc;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import io.r2dbc.spi.ConnectionFactory;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.cloud.sleuth.exporter.FinishedSpan;
+import org.springframework.cloud.sleuth.test.TestSpanHandler;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.dao.DataAccessException;
+import org.springframework.r2dbc.connection.init.ConnectionFactoryInitializer;
+import org.springframework.r2dbc.connection.init.ResourceDatabasePopulator;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestPropertySource;
+
+import static org.assertj.core.api.BDDAssertions.then;
+
+@ContextConfiguration(classes = R2dbcIntegrationTests.TestConfig.class)
+@TestPropertySource(properties = "spring.application.name=MyApplication")
+public abstract class R2dbcIntegrationTests {
+
+ @Autowired
+ TestSpanHandler spans;
+
+ @Test
+ public void should_pass_tracing_information_when_using_r2dbc() {
+ Set traceIds = this.spans.reportedSpans().stream().map(FinishedSpan::getTraceId)
+ .collect(Collectors.toSet());
+ then(traceIds).as("There's one traceid").hasSize(1);
+ Set spanIds = this.spans.reportedSpans().stream().map(FinishedSpan::getSpanId)
+ .collect(Collectors.toSet());
+
+ // 2 transactions - 9 database interactions
+ then(spanIds).as("There are 11 spans").hasSize(11);
+ List spanNames = this.spans.reportedSpans().stream().map(FinishedSpan::getName)
+ .collect(Collectors.toList());
+ List remoteServiceNames = this.spans.reportedSpans().stream().map(FinishedSpan::getRemoteServiceName)
+ .collect(Collectors.toList());
+ then(spanNames.stream().filter("tx"::equalsIgnoreCase).collect(Collectors.toList())).hasSize(2);
+ then(remoteServiceNames.stream().filter("h2"::equalsIgnoreCase).collect(Collectors.toList())).hasSize(9);
+ }
+
+ @Configuration(proxyBeanMethods = false)
+ @EnableAutoConfiguration
+ @ComponentScan
+ public static class TestConfig {
+
+ private static final Logger log = LoggerFactory.getLogger(TestConfig.class);
+
+ @Bean
+ public CommandLineRunner demo(ReactiveNewTransactionService reactiveNewTransactionService) {
+ return (args) -> {
+ try {
+ reactiveNewTransactionService.newTransaction().block(Duration.ofSeconds(50));
+ }
+ catch (DataAccessException e) {
+ log.info("Expected to throw an exception so that we see if rollback works", e);
+ }
+ };
+ }
+
+ @Bean
+ ConnectionFactoryInitializer initializer(ConnectionFactory connectionFactory) {
+ ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer();
+ initializer.setConnectionFactory(connectionFactory);
+ initializer.setDatabasePopulator(new ResourceDatabasePopulator(new ClassPathResource("schema.sql")));
+ return initializer;
+ }
+
+ }
+
+}
diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/r2dbc/ReactiveContinuedTransactionService.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/r2dbc/ReactiveContinuedTransactionService.java
new file mode 100644
index 000000000..70f850115
--- /dev/null
+++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/r2dbc/ReactiveContinuedTransactionService.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.r2dbc;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import reactor.core.publisher.Mono;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+public class ReactiveContinuedTransactionService {
+
+ private static final Logger log = LoggerFactory.getLogger(ReactiveContinuedTransactionService.class);
+
+ private final ReactiveCustomerRepository repository;
+
+ private final ReactiveNestedTransactionService reactiveNestedTransactionService;
+
+ public ReactiveContinuedTransactionService(ReactiveCustomerRepository repository,
+ ReactiveNestedTransactionService reactiveNestedTransactionService) {
+ this.repository = repository;
+ this.reactiveNestedTransactionService = reactiveNestedTransactionService;
+ }
+
+ @Transactional
+ public Mono continuedTransaction() {
+ return Mono.fromRunnable(() -> log.info("Hello from continued transaction")).then(repository.findById(1L))
+ .doOnNext(customer -> {
+ // fetch an individual customer by ID
+ log.info("Customer found with findById(1L):");
+ log.info("--------------------------------");
+ log.info(customer.toString());
+ log.info("");
+ }).doOnNext(customer -> {
+ // fetch customers by last name
+ log.info("Customer found with findByLastName('Bauer'):");
+ log.info("--------------------------------------------");
+ }).flatMapMany(customer -> repository.findByLastName("Bauer"))
+ .doOnNext(cust -> log.info(cust.toString())).then(this.reactiveNestedTransactionService.requiresNew());
+ }
+
+}
diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/r2dbc/ReactiveCustomer.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/r2dbc/ReactiveCustomer.java
new file mode 100644
index 000000000..f3cc7d246
--- /dev/null
+++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/r2dbc/ReactiveCustomer.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.r2dbc;
+
+import org.springframework.data.annotation.Id;
+
+public class ReactiveCustomer {
+
+ @Id
+ private Long id;
+
+ private final String firstName;
+
+ private final String lastName;
+
+ public ReactiveCustomer(String firstName, String lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ public Long getId() {
+ return this.id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getFirstName() {
+ return this.firstName;
+ }
+
+ public String getLastName() {
+ return this.lastName;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Customer[id=%d, firstName='%s', lastName='%s']", id, firstName, lastName);
+ }
+
+}
diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/r2dbc/ReactiveCustomerRepository.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/r2dbc/ReactiveCustomerRepository.java
new file mode 100644
index 000000000..98db76017
--- /dev/null
+++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/r2dbc/ReactiveCustomerRepository.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.r2dbc;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import org.springframework.data.repository.reactive.ReactiveCrudRepository;
+
+public interface ReactiveCustomerRepository extends ReactiveCrudRepository {
+
+ Flux findByLastName(String lastName);
+
+ Mono findById(long id);
+
+}
diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/r2dbc/ReactiveNestedTransactionService.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/r2dbc/ReactiveNestedTransactionService.java
new file mode 100644
index 000000000..992e8fffe
--- /dev/null
+++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/r2dbc/ReactiveNestedTransactionService.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.r2dbc;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import reactor.core.publisher.Mono;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+public class ReactiveNestedTransactionService {
+
+ private static final Logger log = LoggerFactory.getLogger(ReactiveNestedTransactionService.class);
+
+ private final ReactiveCustomerRepository repository;
+
+ public ReactiveNestedTransactionService(ReactiveCustomerRepository repository) {
+ this.repository = repository;
+ }
+
+ @Transactional(propagation = Propagation.REQUIRES_NEW)
+ public Mono requiresNew() {
+ return Mono.fromRunnable(() -> log.info("Hello from nested transaction"))
+ .then(repository.save(new ReactiveCustomer("Hello", "From Propagated Transaction"))).then();
+ }
+
+}
diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/r2dbc/ReactiveNewTransactionService.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/r2dbc/ReactiveNewTransactionService.java
new file mode 100644
index 000000000..71608c9c3
--- /dev/null
+++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/r2dbc/ReactiveNewTransactionService.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.r2dbc;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import reactor.core.publisher.Mono;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+public class ReactiveNewTransactionService {
+
+ private static final Logger log = LoggerFactory.getLogger(ReactiveNewTransactionService.class);
+
+ private final ReactiveCustomerRepository repository;
+
+ private final ReactiveContinuedTransactionService reactiveContinuedTransactionService;
+
+ public ReactiveNewTransactionService(ReactiveCustomerRepository repository,
+ ReactiveContinuedTransactionService reactiveContinuedTransactionService) {
+ this.repository = repository;
+ this.reactiveContinuedTransactionService = reactiveContinuedTransactionService;
+ }
+
+ // 6 database transactions
+ @Transactional
+ public Mono newTransaction() {
+ return Mono.fromRunnable(() -> log.info("Hello from new transaction"))
+ // save a few customers
+ .then(repository.save(new ReactiveCustomer("Jack", "Bauer")))
+ .then(repository.save(new ReactiveCustomer("Chloe", "O'Brian")))
+ .then(repository.save(new ReactiveCustomer("Kim", "Bauer")))
+ .then(repository.save(new ReactiveCustomer("David", "Palmer")))
+ .then(repository.save(new ReactiveCustomer("Michelle", "Dessler"))).doOnNext(reactiveCustomer -> {
+ log.info("Customers found with findAll():");
+ log.info("-------------------------------");
+ }).flatMapMany(reactiveCustomer -> repository.findAll()).doOnNext(cust -> log.info(cust.toString()))
+ .doOnNext(o -> log.info("")).then(this.reactiveContinuedTransactionService.continuedTransaction());
+ }
+
+}
diff --git a/tests/common/src/main/resources/schema.sql b/tests/common/src/main/resources/schema.sql
new file mode 100644
index 000000000..2680487e8
--- /dev/null
+++ b/tests/common/src/main/resources/schema.sql
@@ -0,0 +1 @@
+CREATE TABLE reactive_customer (id SERIAL PRIMARY KEY, first_name VARCHAR(255), last_name VARCHAR(255));