WIP
This commit is contained in:
@@ -25,10 +25,19 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
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.system.CapturedOutput;
|
||||
import org.springframework.boot.test.system.OutputCaptureExtension;
|
||||
import org.springframework.cloud.gateway.config.GatewayAutoConfiguration;
|
||||
import org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration;
|
||||
import org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.DisableSecurity;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
@@ -62,7 +71,7 @@ public class BraveAutoConfigurationWithDisabledSleuthTests {
|
||||
public void shouldNotContainAnyTracingInfoInTheLogs(CapturedOutput capture) {
|
||||
log.info("hello");
|
||||
|
||||
// prove bootstrap-disabled.yml loaded
|
||||
// prove application-disabled.yml loaded
|
||||
assertThat(applicationName).isEqualTo("disabledapplication");
|
||||
|
||||
// spring.application.name is put in the log format by
|
||||
@@ -71,7 +80,11 @@ public class BraveAutoConfigurationWithDisabledSleuthTests {
|
||||
BDDAssertions.then(capture.toString()).doesNotContain("[disabledapplication");
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class,
|
||||
GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class,
|
||||
MongoAutoConfiguration.class, QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class,
|
||||
RedisAutoConfiguration.class, CassandraAutoConfiguration.class },
|
||||
excludeName = "org.springframework.cloud.gateway.config.GatewayRedisAutoConfiguration")
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@DisableSecurity
|
||||
static class Config {
|
||||
|
||||
@@ -30,8 +30,16 @@ import org.slf4j.MDC;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
|
||||
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;
|
||||
import org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration;
|
||||
|
||||
import static brave.propagation.CurrentTraceContext.Scope.NOOP;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -42,9 +50,12 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
|
||||
properties = { "spring.sleuth.baggage.remote-fields=x-vcap-request-id,country-code",
|
||||
"spring.sleuth.baggage.local-fields=bp", "spring.sleuth.baggage.correlation-fields=country-code,bp",
|
||||
"spring.sleuth.tracer.mode=BRAVE" })
|
||||
"spring.sleuth.tracer.mode=BRAVE", "debug=true" })
|
||||
@SpringBootConfiguration
|
||||
@EnableAutoConfiguration
|
||||
@EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class,
|
||||
GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class,
|
||||
MongoAutoConfiguration.class, QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class, CassandraAutoConfiguration.class },
|
||||
excludeName = "org.springframework.cloud.gateway.config.GatewayRedisAutoConfiguration")
|
||||
public class CorrelationScopeDecoratorTest {
|
||||
|
||||
static final BaggageField COUNTRY_CODE = BaggageField.create("country-code");
|
||||
|
||||
@@ -27,8 +27,16 @@ import brave.test.TestSpanHandler;
|
||||
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.cassandra.CassandraAutoConfiguration;
|
||||
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;
|
||||
import org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.brave.instrument.messaging.ConsumerSampler;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -49,7 +57,10 @@ public class BraveMessagingAutoConfigurationIntegrationTests {
|
||||
then(this.sampler).isNotNull();
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class,
|
||||
GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class,
|
||||
MongoAutoConfiguration.class, QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class, CassandraAutoConfiguration.class },
|
||||
excludeName = "org.springframework.cloud.gateway.config.GatewayRedisAutoConfiguration")
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public static class Config {
|
||||
|
||||
|
||||
@@ -36,8 +36,16 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
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.cassandra.CassandraAutoConfiguration;
|
||||
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;
|
||||
import org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@@ -251,7 +259,10 @@ public class OpenTracingTest {
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableAutoConfiguration
|
||||
@EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class,
|
||||
GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class,
|
||||
MongoAutoConfiguration.class, QuartzAutoConfiguration.class, R2dbcAutoConfiguration.class, CassandraAutoConfiguration.class},
|
||||
excludeName = "org.springframework.cloud.gateway.config.GatewayRedisAutoConfiguration")
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -34,7 +34,7 @@ import org.springframework.stereotype.Service;
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@SpringBootTest(classes = { EndpointWithCyclicDependenciesTests.ClientConfig.class },
|
||||
properties = "spring.sleuth.tracer.mode=BRAVE")
|
||||
properties = { "spring.sleuth.tracer.mode=BRAVE", "spring.main.allow-circular-references=true" })
|
||||
public class EndpointWithCyclicDependenciesTests {
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* 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()
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user