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 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));