Adds support for Kotlin Coroutines (#1977)
* Added Kotlin Coroutine support * Added docs, polished code and added one more case for reactor context
This commit is contained in:
committed by
GitHub
parent
75b40bb884
commit
7c4d8b1479
@@ -1,28 +1,33 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = crlf
|
||||
insert_final_newline = true
|
||||
|
||||
[*.java]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
continuation_indent_size = 8
|
||||
|
||||
[*.groovy]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
continuation_indent_size = 8
|
||||
|
||||
[*.xml]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
continuation_indent_size = 8
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.yaml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = crlf
|
||||
insert_final_newline = true
|
||||
|
||||
[*.java]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
continuation_indent_size = 8
|
||||
|
||||
[*.kt]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
continuation_indent_size = 8
|
||||
|
||||
[*.groovy]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
continuation_indent_size = 8
|
||||
|
||||
[*.xml]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
continuation_indent_size = 8
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.yaml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<version>${build-helper-maven-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>generate-sources</phase>
|
||||
|
||||
@@ -729,3 +729,10 @@ This feature is available for all tracer implementations.
|
||||
|
||||
We're instrumenting the `Session` repositories that wraps all operations in a span.
|
||||
In order to disable this instrumentation set `spring.sleuth.session.enabled` to `false`.
|
||||
|
||||
[[sleuth-kotlin-integration]]
|
||||
== Kotlin Coroutines
|
||||
|
||||
This feature is available for all tracer implementations.
|
||||
|
||||
We're adding Kotlin Coroutines that allow you to retrieve the current span via the `Tracer` bean. You can either pass the bean to the Kotlin Coroutine context via `Tracer.asContextElement()` method execution or if you have Reactor Kotlin Coroutine integration on the classpath, we will retrieve it from Reactor's context. To retrieve the current span you can call the `currentSpan()` method within the Kotlin Coroutine.
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -88,6 +88,7 @@
|
||||
<datasource-proxy.version>1.7</datasource-proxy.version>
|
||||
<tomcat-jdbc.version>10.0.6</tomcat-jdbc.version>
|
||||
<commons-dbcp2.version>2.8.0</commons-dbcp2.version>
|
||||
<kotlin.version>1.5.10</kotlin.version>
|
||||
|
||||
<!-- Until we switch it to true in sc-build -->
|
||||
<javadoc.failOnError>true</javadoc.failOnError>
|
||||
@@ -98,6 +99,7 @@
|
||||
<brave-propagation-aws.version>0.21.3</brave-propagation-aws.version>
|
||||
<archunit-junit5.version>0.14.1</archunit-junit5.version>
|
||||
<testcontainers.version>1.15.3</testcontainers.version>
|
||||
<build-helper-maven-plugin.version>3.2.0</build-helper-maven-plugin.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
/*
|
||||
* 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.actuate;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.cloud.sleuth.exporter.FinishedSpan;
|
||||
|
||||
/**
|
||||
* Writes finished spans in a provided format.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public interface FinishedSpanWriter<T> {
|
||||
|
||||
/**
|
||||
* Writes the spans in a given format to String.
|
||||
* @param format format in which spans should be stored
|
||||
* @param spans spans to store
|
||||
* @return string representation of spans or {@code null} if {@link TextOutputFormat}
|
||||
* is not supported.
|
||||
*/
|
||||
T write(TextOutputFormat format, List<FinishedSpan> spans);
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.actuate;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.cloud.sleuth.exporter.FinishedSpan;
|
||||
|
||||
/**
|
||||
* Writes finished spans in a provided format.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public interface FinishedSpanWriter<T> {
|
||||
|
||||
/**
|
||||
* Writes the spans in a given format to String.
|
||||
* @param format format in which spans should be stored
|
||||
* @param spans spans to store
|
||||
* @return string representation of spans or {@code null} if {@link TextOutputFormat}
|
||||
* is not supported.
|
||||
*/
|
||||
T write(TextOutputFormat format, List<FinishedSpan> spans);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,87 +1,87 @@
|
||||
/*
|
||||
* 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.actuate;
|
||||
|
||||
import brave.handler.SpanHandler;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.Producible;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.autoconfig.brave.ConditionalOnBraveEnabled;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
* Auto-configuration} for Sleuth actuator endpoint.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.1.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(value = "spring.sleuth.enabled", matchIfMissing = true)
|
||||
@ConditionalOnAvailableEndpoint(endpoint = TracesScrapeEndpoint.class)
|
||||
@AutoConfigureBefore(BraveAutoConfiguration.class)
|
||||
@EnableConfigurationProperties(SleuthActuatorProperties.class)
|
||||
@ConditionalOnClass(Producible.class)
|
||||
public class TraceSleuthActuatorAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
BufferingSpanReporter sleuthBufferingSpanReporter(SleuthActuatorProperties sleuthActuatorProperties) {
|
||||
return new BufferingSpanReporter(sleuthActuatorProperties.getCapacity());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
TracesScrapeEndpoint sleuthTracesScrapeEndpoint(BufferingSpanReporter bufferingSpanReporter,
|
||||
FinishedSpanWriter finishedSpanWriter) {
|
||||
return new TracesScrapeEndpoint(bufferingSpanReporter, finishedSpanWriter);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(brave.Tracer.class)
|
||||
@ConditionalOnBraveEnabled
|
||||
static class BraveActuatorConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
FinishedSpanWriter sleuthBraveFinishedSpanWriter() {
|
||||
return new BraveFinishedSpanWriter();
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to register at least one {@link SpanHandler} for sampling to hook in.
|
||||
* If there are no span handlers all spans will be noop and no spans will get
|
||||
* reported.
|
||||
* @return a noop span handler
|
||||
*/
|
||||
@Bean
|
||||
SpanHandler sleuthBraveCustomSpanHandler() {
|
||||
return new SpanHandler() {
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.actuate;
|
||||
|
||||
import brave.handler.SpanHandler;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.Producible;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.autoconfig.brave.ConditionalOnBraveEnabled;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
* Auto-configuration} for Sleuth actuator endpoint.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.1.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(value = "spring.sleuth.enabled", matchIfMissing = true)
|
||||
@ConditionalOnAvailableEndpoint(endpoint = TracesScrapeEndpoint.class)
|
||||
@AutoConfigureBefore(BraveAutoConfiguration.class)
|
||||
@EnableConfigurationProperties(SleuthActuatorProperties.class)
|
||||
@ConditionalOnClass(Producible.class)
|
||||
public class TraceSleuthActuatorAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
BufferingSpanReporter sleuthBufferingSpanReporter(SleuthActuatorProperties sleuthActuatorProperties) {
|
||||
return new BufferingSpanReporter(sleuthActuatorProperties.getCapacity());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
TracesScrapeEndpoint sleuthTracesScrapeEndpoint(BufferingSpanReporter bufferingSpanReporter,
|
||||
FinishedSpanWriter finishedSpanWriter) {
|
||||
return new TracesScrapeEndpoint(bufferingSpanReporter, finishedSpanWriter);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(brave.Tracer.class)
|
||||
@ConditionalOnBraveEnabled
|
||||
static class BraveActuatorConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
FinishedSpanWriter sleuthBraveFinishedSpanWriter() {
|
||||
return new BraveFinishedSpanWriter();
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to register at least one {@link SpanHandler} for sampling to hook in.
|
||||
* If there are no span handlers all spans will be noop and no spans will get
|
||||
* reported.
|
||||
* @return a noop span handler
|
||||
*/
|
||||
@Bean
|
||||
SpanHandler sleuthBraveCustomSpanHandler() {
|
||||
return new SpanHandler() {
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
/*
|
||||
* 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.actuate;
|
||||
|
||||
import org.assertj.core.api.BDDAssertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
|
||||
class TraceSleuthActuatorAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withPropertyValues("management.endpoints.web.exposure.include=traces")
|
||||
.withConfiguration(AutoConfigurations.of(TraceSleuthActuatorAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void should_register_actuator_when_management_endpoint_included() {
|
||||
this.contextRunner.run(context -> BDDAssertions.then(context).hasSingleBean(TracesScrapeEndpoint.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_register_actuator_when_endpoint_disabled() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.traces.enabled=false")
|
||||
.run(context -> BDDAssertions.then(context).doesNotHaveBean(TracesScrapeEndpoint.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_register_actuator_when_sleuth_disabled() {
|
||||
this.contextRunner.withPropertyValues("spring.sleuth.enabled=false")
|
||||
.run(context -> BDDAssertions.then(context).doesNotHaveBean(TracesScrapeEndpoint.class));
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.actuate;
|
||||
|
||||
import org.assertj.core.api.BDDAssertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
|
||||
class TraceSleuthActuatorAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withPropertyValues("management.endpoints.web.exposure.include=traces")
|
||||
.withConfiguration(AutoConfigurations.of(TraceSleuthActuatorAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void should_register_actuator_when_management_endpoint_included() {
|
||||
this.contextRunner.run(context -> BDDAssertions.then(context).hasSingleBean(TracesScrapeEndpoint.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_register_actuator_when_endpoint_disabled() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.traces.enabled=false")
|
||||
.run(context -> BDDAssertions.then(context).doesNotHaveBean(TracesScrapeEndpoint.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_register_actuator_when_sleuth_disabled() {
|
||||
this.contextRunner.withPropertyValues("spring.sleuth.enabled=false")
|
||||
.run(context -> BDDAssertions.then(context).doesNotHaveBean(TracesScrapeEndpoint.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -212,6 +212,26 @@
|
||||
<artifactId>spring-session-data-redis</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor.kotlin</groupId>
|
||||
<artifactId>reactor-kotlin-extensions</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-reflect</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlinx</groupId>
|
||||
<artifactId>kotlinx-coroutines-reactor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>p6spy</groupId>
|
||||
@@ -246,6 +266,78 @@
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>${build-helper-maven-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>add-source</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>add-source</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>src/main/kotlin</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>add-test-source</id>
|
||||
<phase>generate-test-sources</phase>
|
||||
<goals>
|
||||
<goal>add-test-source</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>src/test/kotlin</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<args>
|
||||
<arg>-Xjsr305=strict</arg>
|
||||
</args>
|
||||
<compilerPlugins>
|
||||
<plugin>spring</plugin>
|
||||
</compilerPlugins>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-allopen</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>fast</id>
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.kotlin;
|
||||
|
||||
import kotlin.coroutines.CoroutineContext;
|
||||
import kotlin.jvm.functions.Function2;
|
||||
import kotlinx.coroutines.ThreadContextElement;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanAndScope;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
/**
|
||||
* {@link ThreadContextElement} for synchronizing a {@link SpanAndScope} across coroutine
|
||||
* suspension and resumption.
|
||||
*
|
||||
* Inspired by OpenTelemetry's KotlinContextElement.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*/
|
||||
class KotlinContextElement implements ThreadContextElement<SpanAndScope> {
|
||||
|
||||
static final CoroutineContext.Key<KotlinContextElement> KEY = new CoroutineContext.Key<KotlinContextElement>() {
|
||||
};
|
||||
|
||||
private final Span span;
|
||||
|
||||
private final Tracer tracer;
|
||||
|
||||
KotlinContextElement(Tracer tracer) {
|
||||
this.tracer = tracer;
|
||||
this.span = tracer.currentSpan();
|
||||
}
|
||||
|
||||
Span getSpan() {
|
||||
return this.span;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CoroutineContext.Key<?> getKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("MustBeClosedChecker")
|
||||
public SpanAndScope updateThreadContext(CoroutineContext coroutineContext) {
|
||||
Tracer.SpanInScope spanInScope = this.tracer.withSpan(this.span);
|
||||
return new SpanAndScope(this.span, spanInScope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreThreadContext(CoroutineContext coroutineContext, SpanAndScope spanAndScope) {
|
||||
spanAndScope.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CoroutineContext plus(CoroutineContext coroutineContext) {
|
||||
return CoroutineContext.DefaultImpls.plus(this, coroutineContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> R fold(R initial, Function2<? super R, ? super CoroutineContext.Element, ? extends R> operation) {
|
||||
return CoroutineContext.Element.DefaultImpls.fold(this, initial, operation);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <E extends CoroutineContext.Element> E get(CoroutineContext.Key<E> key) {
|
||||
return CoroutineContext.Element.DefaultImpls.get(this, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CoroutineContext minusKey(CoroutineContext.Key<?> key) {
|
||||
return CoroutineContext.Element.DefaultImpls.minusKey(this, key);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.kotlin
|
||||
|
||||
import kotlinx.coroutines.reactor.ReactorContext
|
||||
import org.springframework.cloud.sleuth.CurrentTraceContext
|
||||
import org.springframework.cloud.sleuth.Span
|
||||
import org.springframework.cloud.sleuth.TraceContext
|
||||
import org.springframework.cloud.sleuth.Tracer
|
||||
import org.springframework.util.ClassUtils
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.reflect.jvm.internal.impl.load.kotlin.KotlinClassFinder
|
||||
|
||||
/**
|
||||
* Returns a [CoroutineContext] which will make this [Context] current when resuming a coroutine
|
||||
* and restores the previous [Context] on suspension.
|
||||
*
|
||||
* Inspired by OpenTelemetry's asContextElement.
|
||||
* @since 3.1.0
|
||||
*/
|
||||
fun Tracer.asContextElement(): CoroutineContext {
|
||||
return KotlinContextElement(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Span] in this [CoroutineContext] if present, or null otherwise.
|
||||
*
|
||||
* Inspired by OpenTelemetry's asContextElement.
|
||||
* @since 3.1.0
|
||||
*/
|
||||
fun CoroutineContext.currentSpan(): Span? {
|
||||
val element = get(KotlinContextElement.KEY)
|
||||
if (element is KotlinContextElement) {
|
||||
return element.span
|
||||
}
|
||||
if (!ClassUtils.isPresent("kotlinx.coroutines.reactor.ReactorContext", null)) {
|
||||
return null
|
||||
}
|
||||
val reactorContext = get(ReactorContext.Key)
|
||||
if (reactorContext != null) {
|
||||
if (reactorContext.context.hasKey(Span::class.java)) {
|
||||
return reactorContext.context.get(Span::class.java)
|
||||
}
|
||||
else if (reactorContext.context.hasKey(TraceContext::class.java) && reactorContext.context.hasKey(Tracer::class.java) && reactorContext.context.hasKey(CurrentTraceContext::class.java)) {
|
||||
val traceContext = reactorContext.context.get(TraceContext::class.java)
|
||||
reactorContext.context.get(CurrentTraceContext::class.java).maybeScope(traceContext).use {
|
||||
return reactorContext.context.get(Tracer::class.java).currentSpan()
|
||||
}
|
||||
}
|
||||
else if (reactorContext.context.hasKey(Tracer::class.java)) {
|
||||
return reactorContext.context.get(Tracer::class.java).currentSpan()
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.kotlin
|
||||
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.reactor.ReactorContext
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.assertj.core.api.BDDAssertions.then
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.boot.test.context.FilteredClassLoader
|
||||
import org.springframework.cloud.sleuth.CurrentTraceContext
|
||||
import org.springframework.cloud.sleuth.Span
|
||||
import org.springframework.cloud.sleuth.TraceContext
|
||||
import org.springframework.cloud.sleuth.Tracer
|
||||
import org.springframework.cloud.sleuth.tracer.SimpleCurrentTraceContext
|
||||
import org.springframework.cloud.sleuth.tracer.SimpleTracer
|
||||
import reactor.util.context.Context
|
||||
|
||||
internal class AsContextElementKtTests {
|
||||
|
||||
@Test
|
||||
fun `should return current span from context`(): Unit = runBlocking {
|
||||
val simpleTracer = SimpleTracer()
|
||||
val nextSpan = simpleTracer.nextSpan().start()
|
||||
var spanInGlobalScopeLaunch: Span? = null
|
||||
var spanInGlobalScopeAsync: Span? = null
|
||||
val asContextElement = simpleTracer.asContextElement()
|
||||
|
||||
GlobalScope.launch(asContextElement) {
|
||||
spanInGlobalScopeLaunch = coroutineContext.currentSpan()
|
||||
}
|
||||
GlobalScope.async(asContextElement) {
|
||||
spanInGlobalScopeAsync = coroutineContext.currentSpan()
|
||||
}.await()
|
||||
|
||||
then(spanInGlobalScopeLaunch).isSameAs(nextSpan)
|
||||
then(spanInGlobalScopeAsync).isSameAs(nextSpan)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return span from coroutine context when KotlinContextElement present`(): Unit = runBlocking {
|
||||
val simpleTracer = SimpleTracer()
|
||||
val nextSpan = simpleTracer.nextSpan().start()
|
||||
val element = KotlinContextElement(simpleTracer)
|
||||
|
||||
then(element.currentSpan()).isSameAs(nextSpan)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return null from coroutine context when KotlinContextElement and Reactor extensions are missing`(): Unit = runBlocking {
|
||||
val contextClassLoader = Thread.currentThread().contextClassLoader
|
||||
try {
|
||||
Thread.currentThread().contextClassLoader = FilteredClassLoader("kotlinx.coroutines.reactor.ReactorContext")
|
||||
then(coroutineContext.currentSpan()).isNull()
|
||||
} finally {
|
||||
Thread.currentThread().contextClassLoader = contextClassLoader
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return Span from Reactor extensions when KotlinContextElement missing`(): Unit = runBlocking {
|
||||
val simpleTracer = SimpleTracer()
|
||||
val nextSpan = simpleTracer.nextSpan().start()
|
||||
val reactorContext = ReactorContext(Context.of(Span::class.java, nextSpan))
|
||||
|
||||
then(reactorContext.currentSpan()).isSameAs(nextSpan);
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return Span from Reactor extensions CurrentTraceContext when KotlinContextElement missing and there is TraceContext in Reactor context`(): Unit = runBlocking {
|
||||
val currentTraceContext = SimpleCurrentTraceContext()
|
||||
val simpleTracer = SimpleTracer()
|
||||
val nextSpan = simpleTracer.nextSpan().start()
|
||||
val reactorContext = ReactorContext(Context.of(Tracer::class.java, simpleTracer, CurrentTraceContext::class.java, currentTraceContext, TraceContext::class.java, nextSpan.context()))
|
||||
|
||||
then(reactorContext.currentSpan()).isSameAs(nextSpan);
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return Span from Reactor extensions Tracer when KotlinContextElement missing and there is no Span in context`(): Unit = runBlocking {
|
||||
val simpleTracer = SimpleTracer()
|
||||
val nextSpan = simpleTracer.nextSpan().start()
|
||||
val reactorContext = ReactorContext(Context.of(Tracer::class.java, simpleTracer))
|
||||
|
||||
then(reactorContext.currentSpan()).isSameAs(nextSpan);
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return null when no span is found`(): Unit = runBlocking {
|
||||
val reactorContext = ReactorContext(Context.empty())
|
||||
|
||||
then(reactorContext.currentSpan()).isNull()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -48,6 +48,7 @@
|
||||
<module>spring-cloud-sleuth-instrumentation-grpc-tests</module>
|
||||
<module>spring-cloud-sleuth-instrumentation-jdbc-tests</module>
|
||||
<module>spring-cloud-sleuth-instrumentation-kafka-tests</module>
|
||||
<module>spring-cloud-sleuth-instrumentation-kotlin-tests</module>
|
||||
<module>spring-cloud-sleuth-instrumentation-lettuce-tests</module>
|
||||
<module>spring-cloud-sleuth-instrumentation-messaging-tests</module>
|
||||
<module>spring-cloud-sleuth-instrumentation-mvc-tests</module>
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
<?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-kotlin-tests</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Cloud Sleuth Brave Kotlin Instrumentation Tests</name>
|
||||
<description>Spring Cloud Sleuth Brave Kotlin 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>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>${build-helper-maven-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>add-source</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>add-source</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>src/main/kotlin</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>add-test-source</id>
|
||||
<phase>generate-test-sources</phase>
|
||||
<goals>
|
||||
<goal>add-test-source</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>src/test/kotlin</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<args>
|
||||
<arg>-Xjsr305=strict</arg>
|
||||
</args>
|
||||
<compilerPlugins>
|
||||
<plugin>spring</plugin>
|
||||
</compilerPlugins>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-allopen</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-tests-common</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</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>io.zipkin.brave</groupId>
|
||||
<artifactId>brave-tests</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.awaitility</groupId>
|
||||
<artifactId>awaitility</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.projectreactor.kotlin</groupId>
|
||||
<artifactId>reactor-kotlin-extensions</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-reflect</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlinx</groupId>
|
||||
<artifactId>kotlinx-coroutines-reactor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.kotlin
|
||||
|
||||
import brave.sampler.Sampler
|
||||
import brave.test.TestSpanHandler
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@ContextConfiguration
|
||||
class SleuthCoroutinesApplicationTests : org.springframework.cloud.sleuth.kotlin.SleuthCoroutinesApplicationTests() {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
internal class Config {
|
||||
@Bean
|
||||
fun testSpanHandlerSupplier(testSpanHandler: TestSpanHandler?): org.springframework.cloud.sleuth.test.TestSpanHandler {
|
||||
return BraveTestSpanHandler(testSpanHandler)
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun alwaysSampler(): Sampler {
|
||||
return Sampler.ALWAYS_SAMPLE
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun braveTestSpanHandler(): TestSpanHandler {
|
||||
return TestSpanHandler()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,6 +260,87 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.projectreactor.kotlin</groupId>
|
||||
<artifactId>reactor-kotlin-extensions</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-reflect</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlinx</groupId>
|
||||
<artifactId>kotlinx-coroutines-reactor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>${build-helper-maven-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>add-source</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>add-source</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>src/main/kotlin</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<args>
|
||||
<arg>-Xjsr305=strict</arg>
|
||||
</args>
|
||||
<compilerPlugins>
|
||||
<plugin>spring</plugin>
|
||||
</compilerPlugins>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-allopen</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.kotlin
|
||||
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.assertj.core.api.BDDAssertions.then
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
import org.springframework.boot.test.context.TestConfiguration
|
||||
import org.springframework.boot.web.server.LocalServerPort
|
||||
import org.springframework.cloud.sleuth.Span
|
||||
import org.springframework.cloud.sleuth.Tracer
|
||||
import org.springframework.cloud.sleuth.instrument.kotlin.asContextElement
|
||||
import org.springframework.cloud.sleuth.instrument.kotlin.currentSpan
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import org.springframework.web.client.RestTemplate
|
||||
import kotlin.coroutines.coroutineContext
|
||||
|
||||
@ContextConfiguration(classes = [SleuthCoroutinesApplicationTests.ControllerTestConfig::class])
|
||||
abstract class SleuthCoroutinesApplicationTests {
|
||||
|
||||
@LocalServerPort
|
||||
var serverPort: Int = 0
|
||||
|
||||
@Autowired
|
||||
lateinit var tracer: Tracer
|
||||
|
||||
@Autowired
|
||||
lateinit var webController: WebController
|
||||
|
||||
@Autowired
|
||||
lateinit var restTemplate: RestTemplate
|
||||
|
||||
@Test
|
||||
fun should_pass_tracing_context_within_coroutines(): Unit = runBlocking {
|
||||
val nextSpan = tracer.nextSpan()
|
||||
val traceId = nextSpan.context().traceId()
|
||||
tracer.withSpan(nextSpan.start()).use { withSpan ->
|
||||
val response = restTemplate.getForObject(applicationUrl(), String::class.java)
|
||||
// span in the controller is set
|
||||
then(webController.spanInController).isNotNull
|
||||
then(webController.spanInController?.context()?.traceId()).isEqualTo(traceId)
|
||||
// span in global scope launch is set
|
||||
then(webController.spanInGlobalScopeLaunch).isNotNull
|
||||
then(webController.spanInGlobalScopeLaunch?.context()?.traceId()).isEqualTo(traceId)
|
||||
// span in global async scope is set
|
||||
then(webController.spanInGlobalScopeAsync).isNotNull
|
||||
then(webController.spanInGlobalScopeAsync?.context()?.traceId()).isEqualTo(traceId)
|
||||
// trace id got propagated
|
||||
then(response).isEqualTo(traceId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun applicationUrl() = "http://localhost:$serverPort/hello"
|
||||
|
||||
@TestConfiguration
|
||||
@EnableAutoConfiguration
|
||||
internal class ControllerTestConfig {
|
||||
@Bean
|
||||
fun restTemplate(): RestTemplate = RestTemplate()
|
||||
|
||||
@Bean
|
||||
fun webController(tracer: Tracer): WebController = WebController(tracer)
|
||||
}
|
||||
}
|
||||
|
||||
@RestController
|
||||
class WebController(val tracer: Tracer) {
|
||||
val log: Logger = LoggerFactory.getLogger(this::class.java)
|
||||
var spanInController: Span? = null
|
||||
var spanInGlobalScopeLaunch: Span? = null
|
||||
var spanInGlobalScopeAsync: Span? = null
|
||||
|
||||
@GetMapping("/hello")
|
||||
suspend fun hello(): String? {
|
||||
spanInController = coroutineContext.currentSpan()
|
||||
GlobalScope.launch(tracer.asContextElement()) {
|
||||
log.info("in Coroutines context (launch) - current span {}", coroutineContext.currentSpan())
|
||||
spanInGlobalScopeLaunch = coroutineContext.currentSpan()
|
||||
}
|
||||
GlobalScope.async(tracer.asContextElement()) {
|
||||
log.info("in Coroutines context (async)- current span {}", coroutineContext.currentSpan())
|
||||
spanInGlobalScopeAsync = coroutineContext.currentSpan()
|
||||
}.await()
|
||||
return coroutineContext.currentSpan()?.context()?.traceId()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user