Adds R2DBC support (#1946)

fixes gh-1524
This commit is contained in:
Marcin Grzejszczak
2021-05-13 09:12:04 +02:00
committed by GitHub
parent 5db11326b8
commit 181c789020
43 changed files with 1967 additions and 733 deletions

View File

@@ -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`.

View File

@@ -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();
}
}

View File

@@ -133,6 +133,11 @@
<artifactId>rxjava</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-proxy</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>

View File

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

View File

@@ -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<ConnectionFactory, ConnectionFactory> {
private final BeanFactory beanFactory;
private ObjectProvider<ProxyConfig> 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> proxyConfig() {
if (this.proxyConfig == null) {
this.proxyConfig = this.beanFactory.getBeanProvider(ProxyConfig.class);
}
return this.proxyConfig;
}
}

View File

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

View File

@@ -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
}
]
}

View File

@@ -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,\

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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

View File

@@ -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 {
}

View File

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

View File

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

View File

@@ -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

View File

@@ -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

View File

@@ -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 {
}

View File

@@ -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 {
}

View File

@@ -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

View File

@@ -1,375 +1,375 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2013-2018 the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-sleuth-brave</artifactId>
<packaging>jar</packaging>
<name>Spring Cloud Sleuth Brave</name>
<description>Spring Cloud Sleuth Brave</description>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth</artifactId>
<version>3.1.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-instrumentation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.projectreactor.netty</groupId>
<artifactId>reactor-netty-http</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.reactivestreams</groupId>
<artifactId>reactive-streams</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-context</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-streams</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form-spring</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.reactivex</groupId>
<artifactId>rxjava</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<!-- BRAVE -->
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave</artifactId>
<exclusions>
<exclusion>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>io.zipkin.zipkin2</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-context-slf4j</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-messaging</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-rpc</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-spring-rabbit</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-kafka-clients</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-kafka-streams</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-httpclient</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-httpasyncclient</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-jms</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-mongodb</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.aws</groupId>
<artifactId>brave-propagation-aws</artifactId>
</dependency>
<dependency>
<groupId>javax.jms</groupId>
<artifactId>javax.jms-api</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.opentracing.brave</groupId>
<artifactId>brave-opentracing</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpasyncclient</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<optional>true</optional>
</dependency>
<!-- GRPC Optional Dependencies -->
<dependency>
<groupId>io.github.lognet</groupId>
<artifactId>grpc-spring-boot-starter</artifactId>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-grpc</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-metrics-micrometer</artifactId>
<exclusions>
<exclusion>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Instrumentation of Lettuce -->
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<optional>true</optional>
</dependency>
<!-- For Instrumentation of Quartz -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-http-tests</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit-junit5</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>fast</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<forkCount>4</forkCount>
<reuseForks>true</reuseForks>
<argLine>-Xmx1024m -XX:MaxPermSize=256m</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2013-2018 the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-sleuth-brave</artifactId>
<packaging>jar</packaging>
<name>Spring Cloud Sleuth Brave</name>
<description>Spring Cloud Sleuth Brave</description>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth</artifactId>
<version>3.1.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-instrumentation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.projectreactor.netty</groupId>
<artifactId>reactor-netty-http</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.reactivestreams</groupId>
<artifactId>reactive-streams</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-context</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-streams</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form-spring</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.reactivex</groupId>
<artifactId>rxjava</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<!-- BRAVE -->
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave</artifactId>
<exclusions>
<exclusion>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>io.zipkin.zipkin2</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-context-slf4j</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-messaging</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-rpc</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-spring-rabbit</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-kafka-clients</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-kafka-streams</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-httpclient</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-httpasyncclient</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-jms</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-mongodb</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.aws</groupId>
<artifactId>brave-propagation-aws</artifactId>
</dependency>
<dependency>
<groupId>javax.jms</groupId>
<artifactId>javax.jms-api</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.opentracing.brave</groupId>
<artifactId>brave-opentracing</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpasyncclient</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<optional>true</optional>
</dependency>
<!-- GRPC Optional Dependencies -->
<dependency>
<groupId>io.github.lognet</groupId>
<artifactId>grpc-spring-boot-starter</artifactId>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-grpc</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-metrics-micrometer</artifactId>
<exclusions>
<exclusion>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Instrumentation of Lettuce -->
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<optional>true</optional>
</dependency>
<!-- For Instrumentation of Quartz -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-http-tests</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit-junit5</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>fast</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<forkCount>4</forkCount>
<reuseForks>true</reuseForks>
<argLine>-Xmx1024m -XX:MaxPermSize=256m</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

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

View File

@@ -62,6 +62,11 @@
<artifactId>rsocket-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-proxy</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.projectreactor.kafka</groupId>
<artifactId>reactor-kafka</artifactId>

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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<Span> clientSpan = new AtomicReference<>();
listener = new TraceProxyExecutionListener(beanFactory(), connectionFactory) {
@Override
Span clientSpan(QueryExecutionInfo executionInfo, String name) {
Span span = super.clientSpan(executionInfo, name);
clientSpan.set(span);
return span;
}
};
listener.beforeQuery(queryExecutionInfo);
then(queryExecutionInfo.getValueStore().get(Span.class)).isSameAs(clientSpan.get());
}
@Test
void should_annotate_a_span_on_query_result() {
SimpleSpan span = new SimpleSpan();
ValueStore valueStore = ValueStore.create();
valueStore.put(Span.class, span);
MockQueryExecutionInfo queryExecutionInfo = MockQueryExecutionInfo.builder().valueStore(valueStore).build();
listener.eachQueryResult(queryExecutionInfo);
then(span.events).isNotEmpty();
}
@Test
void should_end_span_on_after_query() {
SimpleSpan span = new SimpleSpan().start();
ValueStore valueStore = ValueStore.create();
valueStore.put(Span.class, span);
MockQueryExecutionInfo queryExecutionInfo = MockQueryExecutionInfo.builder().throwable(new RuntimeException())
.valueStore(valueStore).build();
listener.afterQuery(queryExecutionInfo);
then(span.throwable).isNotNull();
then(span.ended).isTrue();
}
private ConnectionFactory connectionFactory() {
return new ConnectionFactory() {
@Override
public Publisher<? extends Connection> create() {
return null;
}
@Override
public ConnectionFactoryMetadata getMetadata() {
return () -> "my-name";
}
};
}
private BeanFactory beanFactory() {
StaticListableBeanFactory beanFactory = new StaticListableBeanFactory();
beanFactory.addBean("tracer", this.simpleTracer);
return beanFactory;
}
}

View File

@@ -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<String> 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;
}

View File

@@ -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<String> events = new ArrayList<>();
Map<String, String> 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;
}
}

View File

@@ -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

View File

@@ -52,6 +52,7 @@
<module>spring-cloud-sleuth-instrumentation-quartz-tests</module>
<module>spring-cloud-sleuth-instrumentation-reactor-tests</module>
<module>spring-cloud-sleuth-instrumentation-rxjava-tests</module>
<module>spring-cloud-sleuth-instrumentation-r2dbc-tests</module>
<module>spring-cloud-sleuth-instrumentation-scheduling-tests</module>
<module>spring-cloud-sleuth-instrumentation-task-tests</module>
<module>spring-cloud-sleuth-instrumentation-webflux-tests</module>

View File

@@ -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

View File

@@ -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 {
}

View File

@@ -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

View File

@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
~
~
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-sleuth-instrumentation-r2dbc-tests</artifactId>
<packaging>jar</packaging>
<name>Spring Cloud Sleuth Brave R2DBC Instrumentation Tests</name>
<description>Spring Cloud Sleuth Brave R2DBC Instrumentation Tests</description>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-tests-brave</artifactId>
<version>3.1.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<properties>
<sonar.skip>true</sonar.skip>
</properties>
<build>
<plugins>
<plugin>
<!--skip deploy -->
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-tests-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-proxy</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-tests</artifactId>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -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();
}
}
}

View File

@@ -0,0 +1 @@
logging.level.org.springframework.cloud: DEBUG

View File

@@ -159,6 +159,16 @@
<artifactId>reactor-kafka</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-proxy</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>

View File

@@ -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<String> traceIds = this.spans.reportedSpans().stream().map(FinishedSpan::getTraceId)
.collect(Collectors.toSet());
then(traceIds).as("There's one traceid").hasSize(1);
Set<String> 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<String> spanNames = this.spans.reportedSpans().stream().map(FinishedSpan::getName)
.collect(Collectors.toList());
List<String> 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;
}
}
}

View File

@@ -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<Void> 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());
}
}

View File

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

View File

@@ -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<ReactiveCustomer, Long> {
Flux<ReactiveCustomer> findByLastName(String lastName);
Mono<ReactiveCustomer> findById(long id);
}

View File

@@ -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<Void> requiresNew() {
return Mono.fromRunnable(() -> log.info("Hello from nested transaction"))
.then(repository.save(new ReactiveCustomer("Hello", "From Propagated Transaction"))).then();
}
}

View File

@@ -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<Void> 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());
}
}

View File

@@ -0,0 +1 @@
CREATE TABLE reactive_customer (id SERIAL PRIMARY KEY, first_name VARCHAR(255), last_name VARCHAR(255));