Merge pull request #1607 from spring-cloud/deprecate-spring-messaging-headers
Stops propagating Sleuth 1.x spring-messaging headers
This commit is contained in:
7
pom.xml
7
pom.xml
@@ -132,13 +132,6 @@
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- force latest reactor-netty -->
|
||||
<dependency>
|
||||
<groupId>io.projectreactor.netty</groupId>
|
||||
<artifactId>reactor-netty</artifactId>
|
||||
<version>0.9.6.RELEASE</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-dependencies</artifactId>
|
||||
|
||||
@@ -72,11 +72,16 @@ public class TraceBaggageAutoConfiguration {
|
||||
* To override the underlying context format, override this bean and set the delegate
|
||||
* to what you need. {@link BaggagePropagation.FactoryBuilder} will unwrap itself if
|
||||
* no fields are configured.
|
||||
*
|
||||
* <p>
|
||||
* This will use {@link B3Propagation.Format#SINGLE_NO_PARENT} for non-remote spans,
|
||||
* such as for messaging. Note: it will still parse incoming multi-header spans.
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
BaggagePropagation.FactoryBuilder baggagePropagationFactoryBuilder() {
|
||||
return BaggagePropagation.newFactoryBuilder(B3Propagation.FACTORY);
|
||||
return BaggagePropagation.newFactoryBuilder(B3Propagation.newFactoryBuilder()
|
||||
.injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build());
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -19,8 +19,11 @@ package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import brave.propagation.Propagation;
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -45,25 +48,19 @@ enum MessageHeaderPropagation
|
||||
|
||||
private static final Log log = LogFactory.getLog(MessageHeaderPropagation.class);
|
||||
|
||||
private static final Set<String> DEPRECATED_HEADERS = new LinkedHashSet<>();
|
||||
|
||||
private static final Map<String, String> LEGACY_HEADER_MAPPING = new HashMap<>();
|
||||
|
||||
private static final String TRACE_ID_NAME = "X-B3-TraceId";
|
||||
|
||||
private static final String SPAN_ID_NAME = "X-B3-SpanId";
|
||||
|
||||
private static final String PARENT_SPAN_ID_NAME = "X-B3-ParentSpanId";
|
||||
|
||||
private static final String SAMPLED_NAME = "X-B3-Sampled";
|
||||
|
||||
private static final String FLAGS_NAME = "X-B3-Flags";
|
||||
|
||||
static {
|
||||
LEGACY_HEADER_MAPPING.put(TRACE_ID_NAME, TraceMessageHeaders.TRACE_ID_NAME);
|
||||
LEGACY_HEADER_MAPPING.put(SPAN_ID_NAME, TraceMessageHeaders.SPAN_ID_NAME);
|
||||
LEGACY_HEADER_MAPPING.put(PARENT_SPAN_ID_NAME,
|
||||
LEGACY_HEADER_MAPPING.put("X-B3-TraceId", TraceMessageHeaders.TRACE_ID_NAME);
|
||||
LEGACY_HEADER_MAPPING.put("X-B3-SpanId", TraceMessageHeaders.SPAN_ID_NAME);
|
||||
LEGACY_HEADER_MAPPING.put("X-B3-ParentSpanId",
|
||||
TraceMessageHeaders.PARENT_ID_NAME);
|
||||
LEGACY_HEADER_MAPPING.put(SAMPLED_NAME, TraceMessageHeaders.SAMPLED_NAME);
|
||||
LEGACY_HEADER_MAPPING.put(FLAGS_NAME, TraceMessageHeaders.SPAN_FLAGS_NAME);
|
||||
LEGACY_HEADER_MAPPING.put("X-B3-Sampled", TraceMessageHeaders.SAMPLED_NAME);
|
||||
LEGACY_HEADER_MAPPING.put("X-B3-Flags", TraceMessageHeaders.SPAN_FLAGS_NAME);
|
||||
DEPRECATED_HEADERS.addAll(LEGACY_HEADER_MAPPING.keySet());
|
||||
DEPRECATED_HEADERS.addAll(LEGACY_HEADER_MAPPING.values());
|
||||
}
|
||||
|
||||
static Map<String, ?> propagationHeaders(Map<String, ?> headers,
|
||||
@@ -119,8 +116,15 @@ enum MessageHeaderPropagation
|
||||
return nativeAccessor;
|
||||
}
|
||||
|
||||
static final AtomicBoolean LOGGED_PUT_DEPRECATED_HEADER = new AtomicBoolean();
|
||||
|
||||
@Override
|
||||
public void put(MessageHeaderAccessor accessor, String key, String value) {
|
||||
if (DEPRECATED_HEADERS.contains(key)
|
||||
&& LOGGED_PUT_DEPRECATED_HEADER.compareAndSet(false, true)) {
|
||||
log.warn("Please update your code so that it uses the 'b3' header "
|
||||
+ "instead of " + key);
|
||||
}
|
||||
try {
|
||||
doPut(accessor, key, value);
|
||||
}
|
||||
@@ -130,10 +134,6 @@ enum MessageHeaderPropagation
|
||||
+ "] from message", ex);
|
||||
}
|
||||
}
|
||||
String legacyKey = LEGACY_HEADER_MAPPING.get(key);
|
||||
if (legacyKey != null) {
|
||||
doPut(accessor, legacyKey, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void doPut(MessageHeaderAccessor accessor, String key, String value) {
|
||||
@@ -188,7 +188,14 @@ enum MessageHeaderPropagation
|
||||
return null;
|
||||
}
|
||||
|
||||
static final AtomicBoolean LOGGED_GET_DEPRECATED_HEADER = new AtomicBoolean();
|
||||
|
||||
private String doGet(MessageHeaderAccessor accessor, String key) {
|
||||
if (DEPRECATED_HEADERS.contains(key)
|
||||
&& LOGGED_GET_DEPRECATED_HEADER.compareAndSet(false, true)) {
|
||||
log.warn("Please update your code so that it uses the 'b3' header "
|
||||
+ "instead of " + key);
|
||||
}
|
||||
if (accessor instanceof NativeMessageHeaderAccessor) {
|
||||
NativeMessageHeaderAccessor nativeAccessor = (NativeMessageHeaderAccessor) accessor;
|
||||
String result = nativeAccessor.getFirstNativeHeader(key);
|
||||
|
||||
@@ -23,7 +23,9 @@ package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.4
|
||||
* @deprecated Since 3.0 these headers should not be used anymore as "b3" is sent instead
|
||||
*/
|
||||
@Deprecated
|
||||
public final class TraceMessageHeaders {
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.autoconfig;
|
||||
|
||||
import brave.Tracing;
|
||||
import brave.baggage.BaggagePropagation;
|
||||
import brave.propagation.B3Propagation;
|
||||
import brave.propagation.B3SinglePropagation;
|
||||
@@ -41,8 +42,8 @@ public class TraceAutoConfigurationPropagationCustomizationTests {
|
||||
@Test
|
||||
public void stillCreatesDefault() {
|
||||
this.contextRunner.run((context) -> {
|
||||
BDDAssertions.then(context.getBean(Propagation.Factory.class))
|
||||
.isEqualTo(B3Propagation.FACTORY);
|
||||
BDDAssertions.then(context.getBean(Tracing.class).propagation())
|
||||
.isInstanceOf(B3Propagation.class);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -52,8 +53,7 @@ public class TraceAutoConfigurationPropagationCustomizationTests {
|
||||
.withPropertyValues("spring.sleuth.baggage.remote-fields=country-code")
|
||||
.run((context) -> {
|
||||
BDDAssertions.then(context.getBean(Propagation.Factory.class))
|
||||
.hasFieldOrPropertyWithValue("delegate",
|
||||
B3Propagation.FACTORY);
|
||||
.extracting("delegate").isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -61,8 +61,8 @@ public class TraceAutoConfigurationPropagationCustomizationTests {
|
||||
public void defaultValueUsedWhenApplicationNameNotSet() {
|
||||
this.contextRunner.withPropertyValues("spring.application.name=")
|
||||
.run((context) -> {
|
||||
BDDAssertions.then(context.getBean(Propagation.Factory.class))
|
||||
.isEqualTo(B3Propagation.FACTORY);
|
||||
BDDAssertions.then(context.getBean(Tracing.class).propagation())
|
||||
.isInstanceOf(B3Propagation.class);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -56,26 +56,26 @@ public class MessageHeaderPropagationTest
|
||||
@Test
|
||||
public void testGetByteArrayValue() {
|
||||
MessageHeaderAccessor carrier = carrier();
|
||||
carrier.setHeader("X-B3-TraceId", "48485a3953bb6124".getBytes());
|
||||
carrier.setHeader("X-B3-TraceId", "48485a3953bb6124000000".getBytes());
|
||||
String value = MessageHeaderPropagation.INSTANCE.get(carrier, "X-B3-TraceId");
|
||||
assertThat(value).isEqualTo("48485a3953bb6124000000");
|
||||
carrier.setHeader("b3", "48485a3953bb6124-1234".getBytes());
|
||||
carrier.setHeader("b3", "48485a3953bb6124000000-1234".getBytes());
|
||||
String value = MessageHeaderPropagation.INSTANCE.get(carrier, "b3");
|
||||
assertThat(value).isEqualTo("48485a3953bb6124000000-1234");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetStringValue() {
|
||||
MessageHeaderAccessor carrier = carrier();
|
||||
carrier.setHeader("X-B3-TraceId", "48485a3953bb6124");
|
||||
carrier.setHeader("X-B3-TraceId", "48485a3953bb61240000000");
|
||||
String value = MessageHeaderPropagation.INSTANCE.get(carrier, "X-B3-TraceId");
|
||||
assertThat(value).isEqualTo("48485a3953bb61240000000");
|
||||
carrier.setHeader("B3", "48485a3953bb6124-1234");
|
||||
carrier.setHeader("B3", "48485a3953bb61240000000-1234");
|
||||
String value = MessageHeaderPropagation.INSTANCE.get(carrier, "B3");
|
||||
assertThat(value).isEqualTo("48485a3953bb61240000000-1234");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNullValue() {
|
||||
MessageHeaderAccessor carrier = carrier();
|
||||
carrier.setHeader("X-B3-TraceId", "48485a3953bb6124");
|
||||
carrier.setHeader("X-B3-TraceId", "48485a3953bb61240000000");
|
||||
carrier.setHeader("B3", "48485a3953bb6124-1234");
|
||||
carrier.setHeader("B3", "48485a3953bb61240000000-1234");
|
||||
String value = MessageHeaderPropagation.INSTANCE.get(carrier, "non existent key");
|
||||
assertThat(value).isNull();
|
||||
}
|
||||
@@ -85,7 +85,7 @@ public class MessageHeaderPropagationTest
|
||||
MessageHeaderAccessor carrier = carrier();
|
||||
carrier.setHeader(NativeMessageHeaderAccessor.NATIVE_HEADERS,
|
||||
"{spanTraceId=[123], spanId=[456], spanSampled=[0]}");
|
||||
MessageHeaderPropagation.INSTANCE.get(carrier, "X-B3-SpanId");
|
||||
MessageHeaderPropagation.INSTANCE.get(carrier, "b3");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -94,7 +94,7 @@ public class MessageHeaderPropagationTest
|
||||
carrier.setHeader(NativeMessageHeaderAccessor.NATIVE_HEADERS,
|
||||
"{spanTraceId=[123], spanId=[456], spanSampled=[0]}");
|
||||
MessageHeaderPropagation.removeAnyTraceHeaders(carrier,
|
||||
Collections.singletonList("X-B3-SpanId"));
|
||||
Collections.singletonList("b3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -102,7 +102,7 @@ public class MessageHeaderPropagationTest
|
||||
MessageHeaderAccessor carrier = carrier();
|
||||
carrier.setHeader(NativeMessageHeaderAccessor.NATIVE_HEADERS,
|
||||
"{spanTraceId=[123], spanId=[456], spanSampled=[0]}");
|
||||
MessageHeaderPropagation.INSTANCE.put(carrier, "X-B3-SpanId", "1234");
|
||||
MessageHeaderPropagation.INSTANCE.put(carrier, "b3", "1234");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,40 +40,38 @@ public abstract class PropagationSetterTest<C, K> {
|
||||
|
||||
@Test
|
||||
public void set() throws Exception {
|
||||
K key = keyFactory().create("X-B3-TraceId");
|
||||
setter().put(carrier(), key, "48485a3953bb6124");
|
||||
K key = keyFactory().create("b3");
|
||||
setter().put(carrier(), key, "1");
|
||||
|
||||
assertThat(read(carrier(), key)).containsExactly("48485a3953bb6124");
|
||||
assertThat(read(carrier(), key)).containsExactly("1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void set128() throws Exception {
|
||||
K key = keyFactory().create("X-B3-TraceId");
|
||||
setter().put(carrier(), key, "463ac35c9f6413ad48485a3953bb6124");
|
||||
K key = keyFactory().create("b3");
|
||||
setter().put(carrier(), key, "1");
|
||||
|
||||
assertThat(read(carrier(), key))
|
||||
.containsExactly("463ac35c9f6413ad48485a3953bb6124");
|
||||
assertThat(read(carrier(), key)).containsExactly("1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setTwoKeys() throws Exception {
|
||||
K key1 = keyFactory().create("X-B3-TraceId");
|
||||
K key2 = keyFactory().create("X-B3-SpanId");
|
||||
setter().put(carrier(), key1, "463ac35c9f6413ad48485a3953bb6124");
|
||||
setter().put(carrier(), key2, "48485a3953bb6124");
|
||||
K key1 = keyFactory().create("b3");
|
||||
K key2 = keyFactory().create("baggage");
|
||||
setter().put(carrier(), key1, "1");
|
||||
setter().put(carrier(), key2, "country-code=FO");
|
||||
|
||||
assertThat(read(carrier(), key1))
|
||||
.containsExactly("463ac35c9f6413ad48485a3953bb6124");
|
||||
assertThat(read(carrier(), key2)).containsExactly("48485a3953bb6124");
|
||||
assertThat(read(carrier(), key1)).containsExactly("1");
|
||||
assertThat(read(carrier(), key2)).containsExactly("country-code=FO");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reset() throws Exception {
|
||||
K key = keyFactory().create("X-B3-TraceId");
|
||||
setter().put(carrier(), key, "48485a3953bb6124");
|
||||
setter().put(carrier(), key, "463ac35c9f6413ad");
|
||||
K key = keyFactory().create("b3");
|
||||
setter().put(carrier(), key, "0");
|
||||
setter().put(carrier(), key, "1");
|
||||
|
||||
assertThat(read(carrier(), key)).containsExactly("463ac35c9f6413ad");
|
||||
assertThat(read(carrier(), key)).containsExactly("1");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,13 +17,14 @@
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import brave.Tracing;
|
||||
import brave.propagation.B3Propagation;
|
||||
import brave.propagation.StrictCurrentTraceContext;
|
||||
import brave.propagation.TraceContext;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import zipkin2.Span;
|
||||
@@ -45,6 +46,8 @@ import org.springframework.messaging.support.ExecutorSubscribableChannel;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.messaging.support.NativeMessageHeaderAccessor;
|
||||
|
||||
import static brave.propagation.B3Propagation.Format.SINGLE;
|
||||
import static brave.propagation.B3SingleFormat.parseB3SingleFormat;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.messaging.support.NativeMessageHeaderAccessor.NATIVE_HEADERS;
|
||||
|
||||
@@ -55,6 +58,10 @@ public class TracingChannelInterceptorTest {
|
||||
List<Span> spans = new ArrayList<>();
|
||||
|
||||
Tracing tracing = Tracing.newBuilder().currentTraceContext(this.currentTraceContext)
|
||||
// SINGLE_NO_PARENT more appropriate for messaging, but we check parent
|
||||
// hereTraceMessageHeaders
|
||||
.propagationFactory(
|
||||
B3Propagation.newFactoryBuilder().injectFormat(SINGLE).build())
|
||||
.spanReporter(this.spans::add).build();
|
||||
|
||||
ChannelInterceptor interceptor = TracingChannelInterceptor.create(tracing);
|
||||
@@ -92,8 +99,7 @@ public class TracingChannelInterceptorTest {
|
||||
|
||||
this.channel.send(MessageBuilder.withPayload("foo").build());
|
||||
|
||||
assertThat(this.channel.receive().getHeaders()).containsKeys("X-B3-TraceId",
|
||||
"X-B3-SpanId", "X-B3-Sampled", "nativeHeaders");
|
||||
assertThat(this.channel.receive().getHeaders()).containsKey("b3");
|
||||
assertThat(this.spans).hasSize(1).flatExtracting(Span::kind)
|
||||
.containsExactly(Span.Kind.PRODUCER);
|
||||
}
|
||||
@@ -105,8 +111,7 @@ public class TracingChannelInterceptorTest {
|
||||
this.directChannel.send(MessageBuilder.withPayload("foo").build());
|
||||
|
||||
assertThat(this.message).isNotNull();
|
||||
assertThat(this.message.getHeaders()).containsKeys("X-B3-TraceId", "X-B3-SpanId",
|
||||
"X-B3-Sampled", "nativeHeaders");
|
||||
assertThat(this.message.getHeaders()).containsKeys("b3", "nativeHeaders");
|
||||
assertThat(this.spans).flatExtracting(Span::kind).contains(Span.Kind.CONSUMER,
|
||||
Span.Kind.PRODUCER);
|
||||
}
|
||||
@@ -118,8 +123,7 @@ public class TracingChannelInterceptorTest {
|
||||
this.channel.send(MessageBuilder.withPayload("foo").build());
|
||||
|
||||
assertThat((Map) this.channel.receive().getHeaders().get(NATIVE_HEADERS))
|
||||
.containsOnlyKeys("X-B3-TraceId", "X-B3-SpanId", "X-B3-Sampled",
|
||||
"spanTraceId", "spanId", "spanSampled");
|
||||
.containsOnlyKeys("b3");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,12 +136,11 @@ public class TracingChannelInterceptorTest {
|
||||
this.channel.addInterceptor(producerSideOnly(this.interceptor));
|
||||
|
||||
this.channel.send(MessageBuilder.withPayload("foo")
|
||||
.setHeader("X-B3-TraceId", "000000000000000a")
|
||||
.setHeader("X-B3-ParentSpanId", "000000000000000a")
|
||||
.setHeader("X-B3-SpanId", "000000000000000b").build());
|
||||
.setHeader("b3", "000000000000000a-000000000000000b-1").build());
|
||||
|
||||
assertThat(this.channel.receive().getHeaders()).containsEntry("X-B3-ParentSpanId",
|
||||
"000000000000000b");
|
||||
TraceContext receiveContext = parseB3SingleFormat(
|
||||
this.channel.receive().getHeaders().get("b3", String.class)).context();
|
||||
assertThat(receiveContext.parentIdString()).isEqualTo("000000000000000b");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -147,16 +150,16 @@ public class TracingChannelInterceptorTest {
|
||||
NativeMessageHeaderAccessor accessor = new NativeMessageHeaderAccessor() {
|
||||
};
|
||||
|
||||
accessor.setNativeHeader("X-B3-TraceId", "000000000000000a");
|
||||
accessor.setNativeHeader("X-B3-ParentSpanId", "000000000000000a");
|
||||
accessor.setNativeHeader("X-B3-SpanId", "000000000000000b");
|
||||
accessor.setNativeHeader("b3",
|
||||
"000000000000000a-000000000000000b-1-000000000000000a");
|
||||
|
||||
this.channel.send(MessageBuilder.withPayload("foo")
|
||||
.copyHeaders(accessor.toMessageHeaders()).build());
|
||||
|
||||
assertThat((Map) this.channel.receive().getHeaders().get(NATIVE_HEADERS))
|
||||
.containsEntry("X-B3-ParentSpanId",
|
||||
Collections.singletonList("000000000000000b"));
|
||||
TraceContext receiveContext = parseB3SingleFormat(
|
||||
((List) ((Map) this.channel.receive().getHeaders().get(NATIVE_HEADERS))
|
||||
.get("b3")).get(0).toString()).context();
|
||||
assertThat(receiveContext.parentIdString()).isEqualTo("000000000000000b");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,8 +172,8 @@ public class TracingChannelInterceptorTest {
|
||||
|
||||
this.channel.send(MessageBuilder.withPayload("foo").build());
|
||||
|
||||
assertThat(this.channel.receive().getHeaders()).containsKeys("X-B3-TraceId",
|
||||
"X-B3-SpanId", "X-B3-Sampled", "nativeHeaders");
|
||||
assertThat(this.channel.receive().getHeaders()).containsKeys("b3",
|
||||
"nativeHeaders");
|
||||
assertThat(this.spans).hasSize(1).flatExtracting(Span::kind)
|
||||
.containsExactly(Span.Kind.CONSUMER);
|
||||
}
|
||||
@@ -182,8 +185,7 @@ public class TracingChannelInterceptorTest {
|
||||
this.channel.send(MessageBuilder.withPayload("foo").build());
|
||||
|
||||
assertThat((Map) this.channel.receive().getHeaders().get(NATIVE_HEADERS))
|
||||
.containsOnlyKeys("X-B3-TraceId", "X-B3-SpanId", "X-B3-Sampled",
|
||||
"spanTraceId", "spanId", "spanSampled");
|
||||
.containsOnlyKeys("b3");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -195,8 +197,8 @@ public class TracingChannelInterceptorTest {
|
||||
|
||||
channel.send(MessageBuilder.withPayload("foo").build());
|
||||
|
||||
assertThat(messages.get(0).getHeaders()).doesNotContainKeys("X-B3-TraceId",
|
||||
"X-B3-SpanId", "X-B3-Sampled", "nativeHeaders");
|
||||
assertThat(messages.get(0).getHeaders()).doesNotContainKeys("b3",
|
||||
"nativeHeaders");
|
||||
assertThat(this.spans).flatExtracting(Span::kind)
|
||||
.containsExactly(Span.Kind.CONSUMER, null);
|
||||
}
|
||||
@@ -215,8 +217,7 @@ public class TracingChannelInterceptorTest {
|
||||
|
||||
channel.send(MessageBuilder.withPayload("foo").build());
|
||||
|
||||
assertThat(messages.get(0).getHeaders()).doesNotContainKeys("X-B3-TraceId",
|
||||
"X-B3-SpanId", "X-B3-Sampled");
|
||||
assertThat(messages.get(0).getHeaders()).doesNotContainKeys("b3");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -229,7 +230,7 @@ public class TracingChannelInterceptorTest {
|
||||
channel.send(MessageBuilder.withPayload("foo").build());
|
||||
|
||||
assertThat((Map) messages.get(0).getHeaders().get(NATIVE_HEADERS))
|
||||
.doesNotContainKeys("X-B3-TraceId", "X-B3-SpanId", "X-B3-Sampled");
|
||||
.doesNotContainKeys("b3");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -264,27 +265,23 @@ public class TracingChannelInterceptorTest {
|
||||
Map<String, Object> errorChannelHeaders = new HashMap<>();
|
||||
errorChannelHeaders.put(MessageHeaders.REPLY_CHANNEL, errorsReplyChannel);
|
||||
errorChannelHeaders.put(MessageHeaders.ERROR_CHANNEL, errorsReplyChannel);
|
||||
this.channel
|
||||
.send(new ErrorMessage(
|
||||
new MessagingException(MessageBuilder.withPayload("hi")
|
||||
.setHeader(TraceMessageHeaders.TRACE_ID_NAME,
|
||||
"000000000000000a")
|
||||
.setHeader(TraceMessageHeaders.SPAN_ID_NAME,
|
||||
"000000000000000a")
|
||||
.setReplyChannel(deadReplyChannel)
|
||||
.setErrorChannel(deadReplyChannel).build()),
|
||||
errorChannelHeaders));
|
||||
this.channel.send(new ErrorMessage(
|
||||
new MessagingException(MessageBuilder.withPayload("hi")
|
||||
.setHeader("b3", "000000000000000a-000000000000000a")
|
||||
.setReplyChannel(deadReplyChannel)
|
||||
.setErrorChannel(deadReplyChannel).build()),
|
||||
errorChannelHeaders));
|
||||
|
||||
this.message = this.channel.receive();
|
||||
|
||||
assertThat(this.message).isNotNull();
|
||||
String spanId = this.message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME,
|
||||
String.class);
|
||||
assertThat(spanId).isNotNull();
|
||||
String traceId = this.message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME,
|
||||
String.class);
|
||||
assertThat(traceId).isEqualTo("000000000000000a");
|
||||
assertThat(spanId).isNotEqualTo("000000000000000a");
|
||||
|
||||
// Parse fails if trace or span ID are missing
|
||||
TraceContext context = parseB3SingleFormat(
|
||||
this.message.getHeaders().get("b3", String.class)).context();
|
||||
|
||||
assertThat(context.traceIdString()).isEqualTo("000000000000000a");
|
||||
assertThat(context.spanIdString()).isNotEqualTo("000000000000000a");
|
||||
assertThat(this.spans).hasSize(2);
|
||||
assertThat(this.message.getHeaders().getReplyChannel())
|
||||
.isSameAs(errorsReplyChannel);
|
||||
@@ -318,21 +315,16 @@ public class TracingChannelInterceptorTest {
|
||||
public void errorMessageHeadersWithNullPayloadRetained() {
|
||||
this.channel.addInterceptor(this.interceptor);
|
||||
Map<String, Object> errorChannelHeaders = new HashMap<>();
|
||||
errorChannelHeaders.put(TraceMessageHeaders.TRACE_ID_NAME, "000000000000000a");
|
||||
errorChannelHeaders.put(TraceMessageHeaders.SPAN_ID_NAME, "000000000000000a");
|
||||
errorChannelHeaders.put("b3", "000000000000000a-000000000000000a");
|
||||
this.channel.send(new ErrorMessage(new MessagingException("exception"),
|
||||
errorChannelHeaders));
|
||||
|
||||
this.message = this.channel.receive();
|
||||
|
||||
assertThat(this.message).isNotNull();
|
||||
String spanId = this.message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME,
|
||||
String.class);
|
||||
assertThat(spanId).isNotNull();
|
||||
String traceId = this.message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME,
|
||||
String.class);
|
||||
assertThat(traceId).isEqualTo("000000000000000a");
|
||||
assertThat(spanId).isNotEqualTo("000000000000000a");
|
||||
TraceContext receiveContext = parseB3SingleFormat(
|
||||
this.message.getHeaders().get("b3", String.class)).context();
|
||||
assertThat(receiveContext.traceIdString()).isEqualTo("000000000000000a");
|
||||
assertThat(receiveContext.spanIdString()).isNotEqualTo("000000000000000a");
|
||||
assertThat(this.spans).hasSize(2);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.data.MapEntry.entry;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.NONE;
|
||||
@@ -82,10 +83,8 @@ public class BraveTracerTest {
|
||||
|
||||
@Test
|
||||
public void extractTraceContext() {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
map.put("X-B3-TraceId", "0000000000000001");
|
||||
map.put("X-B3-SpanId", "0000000000000002");
|
||||
map.put("X-B3-Sampled", "1");
|
||||
Map<String, String> map = singletonMap("b3",
|
||||
"0000000000000001-0000000000000002-1");
|
||||
|
||||
BraveSpanContext openTracingContext = this.opentracing
|
||||
.extract(Format.Builtin.HTTP_HEADERS, new TextMapAdapter(map));
|
||||
@@ -97,9 +96,7 @@ public class BraveTracerTest {
|
||||
@Test
|
||||
public void extractBaggage() {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
map.put("X-B3-TraceId", "0000000000000001");
|
||||
map.put("X-B3-SpanId", "0000000000000002");
|
||||
map.put("X-B3-Sampled", "1");
|
||||
map.put("b3", "0000000000000001-0000000000000002-1");
|
||||
map.put("country-code", "FO");
|
||||
|
||||
BraveSpanContext openTracingContext = this.opentracing
|
||||
@@ -111,10 +108,8 @@ public class BraveTracerTest {
|
||||
|
||||
@Test
|
||||
public void extractTraceContextTextMap() {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
map.put("X-B3-TraceId", "0000000000000001");
|
||||
map.put("X-B3-SpanId", "0000000000000002");
|
||||
map.put("X-B3-Sampled", "1");
|
||||
Map<String, String> map = singletonMap("b3",
|
||||
"0000000000000001-0000000000000002-1");
|
||||
|
||||
BraveSpanContext openTracingContext = this.opentracing
|
||||
.extract(Format.Builtin.TEXT_MAP, new TextMapAdapter(map));
|
||||
@@ -126,9 +121,7 @@ public class BraveTracerTest {
|
||||
@Test
|
||||
public void extractTraceContextCaseInsensitive() {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
map.put("X-B3-TraceId", "0000000000000001");
|
||||
map.put("x-b3-spanid", "0000000000000002");
|
||||
map.put("x-b3-SaMpLeD", "1");
|
||||
map.put("B3", "0000000000000001-0000000000000002-1");
|
||||
map.put("other", "1");
|
||||
|
||||
BraveSpanContext openTracingContext = this.opentracing
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
|
||||
import org.springframework.cloud.sleuth.util.SpanUtil;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -55,13 +54,6 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
|
||||
*/
|
||||
public class TraceFilterTests {
|
||||
|
||||
static final String PARENT_ID = SpanUtil.idToHex(10L);
|
||||
static final String TRACE_ID_NAME = "X-B3-TraceId";
|
||||
static final String SPAN_ID_NAME = "X-B3-SpanId";
|
||||
static final String PARENT_SPAN_ID_NAME = "X-B3-ParentSpanId";
|
||||
static final String SAMPLED_ID_NAME = "X-B3-Sampled";
|
||||
static final String SPAN_FLAGS = "X-B3-Flags";
|
||||
|
||||
ArrayListSpanReporter reporter = new ArrayListSpanReporter();
|
||||
|
||||
Tracing tracing = Tracing.newBuilder()
|
||||
@@ -152,16 +144,15 @@ public class TraceFilterTests {
|
||||
|
||||
@Test
|
||||
public void startsNewTraceWithParentIdInHeaders() throws Exception {
|
||||
this.request = builder().header(SPAN_ID_NAME, PARENT_ID)
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(2L))
|
||||
.header(PARENT_SPAN_ID_NAME, SpanUtil.idToHex(3L))
|
||||
this.request = builder()
|
||||
.header("b3", "0000000000000002-0000000000000003-1-000000000000000a")
|
||||
.buildRequest(new MockServletContext());
|
||||
|
||||
this.filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
then(Tracing.current().tracer().currentSpan()).isNull();
|
||||
then(this.reporter.getSpans()).hasSize(1);
|
||||
then(this.reporter.getSpans().get(0).id()).isEqualTo(PARENT_ID);
|
||||
then(this.reporter.getSpans().get(0).id()).isEqualTo("0000000000000003");
|
||||
then(this.reporter.getSpans().get(0).tags()).containsEntry("http.path", "/")
|
||||
.containsEntry("http.method", HttpMethod.GET.toString());
|
||||
}
|
||||
@@ -169,10 +160,8 @@ public class TraceFilterTests {
|
||||
@Test
|
||||
public void continuesATraceWhenSpanNotSampled() throws Exception {
|
||||
AtomicReference<Span> span = new AtomicReference<>();
|
||||
this.request = builder().header(SPAN_ID_NAME, PARENT_ID)
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(2L))
|
||||
.header(PARENT_SPAN_ID_NAME, SpanUtil.idToHex(3L))
|
||||
.header(SAMPLED_ID_NAME, 0).buildRequest(new MockServletContext());
|
||||
this.request = builder().header("b3", "0000000000000014-000000000000000a-0")
|
||||
.buildRequest(new MockServletContext());
|
||||
|
||||
this.filter.doFilter(this.request, this.response, (req, resp) -> {
|
||||
this.filterChain.doFilter(req, resp);
|
||||
@@ -180,7 +169,7 @@ public class TraceFilterTests {
|
||||
});
|
||||
|
||||
then(Tracing.current().tracer().currentSpan()).isNull();
|
||||
then(span.get().context().traceIdString()).isEqualTo(SpanUtil.idToHex(2L));
|
||||
then(span.get().context().traceIdString()).isEqualTo("0000000000000014");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -215,8 +204,7 @@ public class TraceFilterTests {
|
||||
|
||||
@Test
|
||||
public void continuesSpanFromHeaders() throws Exception {
|
||||
this.request = builder().header(SPAN_ID_NAME, PARENT_ID)
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(20L))
|
||||
this.request = builder().header("b3", "0000000000000014-000000000000000a")
|
||||
.buildRequest(new MockServletContext());
|
||||
|
||||
this.filter.doFilter(this.request, this.response, this.filterChain);
|
||||
@@ -232,8 +220,7 @@ public class TraceFilterTests {
|
||||
.addScopeDecorator(StrictScopeDecorator.create()).build())
|
||||
.spanReporter(this.reporter).supportsJoin(false).build();
|
||||
HttpTracing httpTracing = HttpTracing.create(tracing);
|
||||
this.request = builder().header(SPAN_ID_NAME, PARENT_ID)
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(20L))
|
||||
this.request = builder().header("b3", "0000000000000014-000000000000000a")
|
||||
.buildRequest(new MockServletContext());
|
||||
|
||||
TracingFilter.create(httpTracing).doFilter(this.request, this.response,
|
||||
@@ -241,13 +228,12 @@ public class TraceFilterTests {
|
||||
|
||||
then(Tracing.current().tracer().currentSpan()).isNull();
|
||||
then(this.reporter.getSpans()).hasSize(1);
|
||||
then(this.reporter.getSpans().get(0).parentId()).isEqualTo(PARENT_ID);
|
||||
then(this.reporter.getSpans().get(0).parentId()).isEqualTo("000000000000000a");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAnnotateSpanWithErrorWhenExceptionIsThrown() throws Exception {
|
||||
this.request = builder().header(SPAN_ID_NAME, PARENT_ID)
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(20L))
|
||||
this.request = builder().header("b3", "0000000000000014-000000000000000a")
|
||||
.buildRequest(new MockServletContext());
|
||||
|
||||
this.filterChain = new MockFilterChain() {
|
||||
@@ -273,8 +259,7 @@ public class TraceFilterTests {
|
||||
|
||||
@Test
|
||||
public void detachesSpanWhenResponseStatusIsNot2xx() throws Exception {
|
||||
this.request = builder().header(SPAN_ID_NAME, PARENT_ID)
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(20L))
|
||||
this.request = builder().header("b3", "14-a")
|
||||
.buildRequest(new MockServletContext());
|
||||
|
||||
this.response.setStatus(404);
|
||||
@@ -285,8 +270,7 @@ public class TraceFilterTests {
|
||||
|
||||
@Test
|
||||
public void closesSpanWhenResponseStatusIs2xx() throws Exception {
|
||||
this.request = builder().header(SPAN_ID_NAME, PARENT_ID)
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(20L))
|
||||
this.request = builder().header("b3", "0000000000000014-000000000000000a")
|
||||
.buildRequest(new MockServletContext());
|
||||
this.response.setStatus(200);
|
||||
|
||||
@@ -298,8 +282,7 @@ public class TraceFilterTests {
|
||||
|
||||
@Test
|
||||
public void closesSpanWhenResponseStatusIs3xx() throws Exception {
|
||||
this.request = builder().header(SPAN_ID_NAME, PARENT_ID)
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(20L))
|
||||
this.request = builder().header("b3", "0000000000000014-000000000000000a")
|
||||
.buildRequest(new MockServletContext());
|
||||
this.response.setStatus(302);
|
||||
|
||||
@@ -311,8 +294,7 @@ public class TraceFilterTests {
|
||||
|
||||
@Test
|
||||
public void returns400IfSpanIsMalformedAndCreatesANewSpan() throws Exception {
|
||||
this.request = builder().header(SPAN_ID_NAME, "asd")
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(20L))
|
||||
this.request = builder().header("b3", "asd")
|
||||
.buildRequest(new MockServletContext());
|
||||
|
||||
this.filter.doFilter(this.request, this.response, this.filterChain);
|
||||
@@ -324,9 +306,7 @@ public class TraceFilterTests {
|
||||
|
||||
@Test
|
||||
public void returns200IfSpanParentIsMalformedAndCreatesANewSpan() throws Exception {
|
||||
this.request = builder().header(SPAN_ID_NAME, PARENT_ID)
|
||||
.header(PARENT_SPAN_ID_NAME, "-")
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(20L))
|
||||
this.request = builder().header("b3", "asd")
|
||||
.buildRequest(new MockServletContext());
|
||||
|
||||
this.filter.doFilter(this.request, this.response, this.filterChain);
|
||||
@@ -337,10 +317,8 @@ public class TraceFilterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void samplesASpanRegardlessOfTheSamplerWhenXB3FlagsIsPresentAndSetTo1()
|
||||
throws Exception {
|
||||
this.request = builder().header(SPAN_FLAGS, 1)
|
||||
.buildRequest(new MockServletContext());
|
||||
public void samplesASpanRegardlessOfTheSamplerWhenDebugIsPresent() throws Exception {
|
||||
this.request = builder().header("b3", "d").buildRequest(new MockServletContext());
|
||||
|
||||
neverSampleFilter().doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
@@ -348,38 +326,10 @@ public class TraceFilterTests {
|
||||
then(this.reporter.getSpans()).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotOverrideTheSampledFlagWhenXB3FlagIsSetToOtherValueThan1()
|
||||
throws Exception {
|
||||
this.request = builder().header(SPAN_FLAGS, 0)
|
||||
.buildRequest(new MockServletContext());
|
||||
|
||||
this.filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
then(Tracing.current().tracer().currentSpan()).isNull();
|
||||
then(this.reporter.getSpans()).isNotEmpty();
|
||||
}
|
||||
|
||||
@SuppressWarnings("Duplicates")
|
||||
@Test
|
||||
public void samplesWhenDebugFlagIsSetTo1AndOnlySpanIdIsSet() throws Exception {
|
||||
this.request = builder().header(SPAN_FLAGS, 1)
|
||||
.header(SPAN_ID_NAME, SpanUtil.idToHex(10L))
|
||||
.buildRequest(new MockServletContext());
|
||||
|
||||
neverSampleFilter().doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
then(Tracing.current().tracer().currentSpan()).isNull();
|
||||
// It is ok to go without a trace ID, if sampling or debug is set
|
||||
then(this.reporter.getSpans()).hasSize(1).extracting("id")
|
||||
.isNotEqualTo(SpanUtil.idToHex(10L));
|
||||
}
|
||||
|
||||
@SuppressWarnings("Duplicates")
|
||||
@Test
|
||||
public void usesSamplingMechanismWhenIncomingTraceIsMalformed() throws Exception {
|
||||
this.request = builder().header(SPAN_FLAGS, 1)
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(10L))
|
||||
this.request = builder().header("b3", "asd")
|
||||
.buildRequest(new MockServletContext());
|
||||
|
||||
neverSampleFilter().doFilter(this.request, this.response, this.filterChain);
|
||||
@@ -407,8 +357,7 @@ public class TraceFilterTests {
|
||||
|
||||
@Test
|
||||
public void samplesASpanDebugFlagWithInterceptor() throws Exception {
|
||||
this.request = builder().header(SPAN_FLAGS, 1)
|
||||
.buildRequest(new MockServletContext());
|
||||
this.request = builder().header("b3", "d").buildRequest(new MockServletContext());
|
||||
|
||||
neverSampleFilter().doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
|
||||
@@ -93,6 +93,7 @@ public class TraceRestTemplateInterceptorTests {
|
||||
Map<String, String> headers = this.template.getForEntity("/", Map.class)
|
||||
.getBody();
|
||||
|
||||
// Default inject format for client spans is B3 multi
|
||||
then(headers.get("X-B3-TraceId")).isNotNull();
|
||||
then(headers.get("X-B3-SpanId")).isNotNull();
|
||||
}
|
||||
@@ -109,6 +110,7 @@ public class TraceRestTemplateInterceptorTests {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
// Default inject format for client spans is B3 multi
|
||||
then(headers.get("X-B3-TraceId"))
|
||||
.isEqualTo(SpanUtil.idToHex(span.context().traceId()));
|
||||
then(headers.get("X-B3-SpanId"))
|
||||
@@ -205,7 +207,8 @@ public class TraceRestTemplateInterceptorTests {
|
||||
@RequestMapping("/")
|
||||
public Map<String, String> home(@RequestHeader HttpHeaders headers) {
|
||||
this.span = TraceRestTemplateInterceptorTests.this.tracer.currentSpan();
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
Map<String, String> map = new HashMap<>();
|
||||
// Default inject format for client spans is B3 multi
|
||||
addHeaders(map, headers, "X-B3-SpanId", "X-B3-TraceId", "X-B3-ParentSpanId");
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||
|
||||
// This test uses B3 multi format as it is the default for client propagation
|
||||
public class TraceRequestHttpHeadersFilterTests {
|
||||
|
||||
StrictCurrentTraceContext currentTraceContext = StrictCurrentTraceContext.create();
|
||||
|
||||
@@ -51,8 +51,7 @@ public class TraceResponseHttpHeadersFilterTests {
|
||||
HttpHeadersFilter filter = TraceResponseHttpHeadersFilter
|
||||
.create(this.httpTracing);
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.set("X-B3-TraceId", "52f112af7472aff0");
|
||||
httpHeaders.set("X-B3-SpanId", "53e6ab6fc5dfee58");
|
||||
httpHeaders.set("b3", "52f112af7472aff0-53e6ab6fc5dfee58");
|
||||
MockServerHttpRequest request = MockServerHttpRequest.post("foo/bar")
|
||||
.headers(httpHeaders).build();
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
@@ -67,8 +66,7 @@ public class TraceResponseHttpHeadersFilterTests {
|
||||
HttpHeadersFilter filter = TraceResponseHttpHeadersFilter
|
||||
.create(this.httpTracing);
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.set("X-B3-TraceId", "52f112af7472aff0");
|
||||
httpHeaders.set("X-B3-SpanId", "53e6ab6fc5dfee58");
|
||||
httpHeaders.set("b3", "52f112af7472aff0-53e6ab6fc5dfee58");
|
||||
MockServerHttpRequest request = MockServerHttpRequest.post("foo/bar")
|
||||
.headers(httpHeaders).build();
|
||||
MockServerWebExchange exchange = MockServerWebExchange.builder(request).build();
|
||||
|
||||
@@ -32,6 +32,9 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import brave.baggage.BaggagePropagation;
|
||||
import brave.propagation.B3Propagation;
|
||||
import brave.propagation.B3SingleFormat;
|
||||
import brave.propagation.SamplingFlags;
|
||||
import brave.propagation.TraceContextOrSamplingFlags;
|
||||
import brave.sampler.Sampler;
|
||||
@@ -90,6 +93,8 @@ import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.reactive.function.client.UnknownHttpStatusCodeException;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static brave.Span.Kind.CLIENT;
|
||||
import static brave.propagation.B3Propagation.Format.SINGLE_NO_PARENT;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
@@ -100,11 +105,6 @@ import static org.assertj.core.api.BDDAssertions.then;
|
||||
@DirtiesContext
|
||||
public class WebClientTests {
|
||||
|
||||
static final String TRACE_ID_NAME = "X-B3-TraceId";
|
||||
static final String SPAN_ID_NAME = "X-B3-SpanId";
|
||||
static final String SAMPLED_NAME = "X-B3-Sampled";
|
||||
static final String PARENT_ID_NAME = "X-B3-ParentSpanId";
|
||||
|
||||
private static final Log log = LogFactory.getLog(WebClientTests.class);
|
||||
|
||||
@Autowired
|
||||
@@ -163,8 +163,7 @@ public class WebClientTests {
|
||||
ResponseEntity<String> response = provider.get(this);
|
||||
|
||||
Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAsserted(() -> {
|
||||
then(getHeader(response, TRACE_ID_NAME)).isNull();
|
||||
then(getHeader(response, SPAN_ID_NAME)).isNull();
|
||||
then(getHeader(response, "b3")).isNull();
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).isNotEmpty();
|
||||
Optional<zipkin2.Span> noTraceSpan = new ArrayList<>(spans).stream()
|
||||
@@ -220,8 +219,7 @@ public class WebClientTests {
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
|
||||
ResponseEntity<Map<String, String>> response = provider.get(this);
|
||||
|
||||
then(response.getBody().get(TRACE_ID_NAME.toLowerCase())).isNotNull();
|
||||
then(response.getBody().get(SAMPLED_NAME.toLowerCase())).isEqualTo("0");
|
||||
then(response.getBody().get("b3")).isNotNull().endsWith("-0"); // not sampled
|
||||
}
|
||||
finally {
|
||||
span.finish();
|
||||
@@ -250,8 +248,7 @@ public class WebClientTests {
|
||||
|
||||
// https://github.com/spring-cloud/spring-cloud-sleuth/issues/327
|
||||
// we don't want to respond with any tracing data
|
||||
then(getHeader(response, SAMPLED_NAME)).isNull();
|
||||
then(getHeader(response, TRACE_ID_NAME)).isNull();
|
||||
then(getHeader(response, "b3")).isNull();
|
||||
}
|
||||
finally {
|
||||
span.finish();
|
||||
@@ -480,7 +477,7 @@ public class WebClientTests {
|
||||
AtomicReference<String> traceId = new AtomicReference<>();
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
|
||||
this.webClientBuilder.filter((request, exchange) -> {
|
||||
traceId.set(request.headers().getFirst("X-B3-SpanId"));
|
||||
traceId.set(request.headers().getFirst("b3"));
|
||||
|
||||
return exchange.exchange(request);
|
||||
}).build().get().uri("http://localhost:" + this.port + "/traceid").retrieve()
|
||||
@@ -531,6 +528,13 @@ public class WebClientTests {
|
||||
configuration = SimpleLoadBalancerClientConfiguration.class)
|
||||
public static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
BaggagePropagation.FactoryBuilder baggagePropagationFactoryBuilder() {
|
||||
// Use b3 single format as it is less verbose
|
||||
return BaggagePropagation.newFactoryBuilder(B3Propagation.newFactoryBuilder()
|
||||
.injectFormat(CLIENT, SINGLE_NO_PARENT).build());
|
||||
}
|
||||
|
||||
@Bean
|
||||
FooController fooController() {
|
||||
return new FooController();
|
||||
@@ -625,26 +629,21 @@ public class WebClientTests {
|
||||
@RestController
|
||||
public static class FooController {
|
||||
|
||||
@Autowired
|
||||
Tracer tracer;
|
||||
|
||||
Span span;
|
||||
|
||||
@RequestMapping(value = "/notrace", method = RequestMethod.GET)
|
||||
public String notrace(
|
||||
@RequestHeader(name = TRACE_ID_NAME, required = false) String traceId) {
|
||||
then(traceId).isNotNull();
|
||||
@RequestHeader(name = "b3", required = false) String b3Single) {
|
||||
then(b3Single).isNotNull();
|
||||
return "OK";
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/traceid", method = RequestMethod.GET)
|
||||
public String traceId(@RequestHeader(TRACE_ID_NAME) String traceId,
|
||||
@RequestHeader(SPAN_ID_NAME) String spanId,
|
||||
@RequestHeader(PARENT_ID_NAME) String parentId) {
|
||||
then(traceId).isNotEmpty();
|
||||
then(parentId).isNotEmpty();
|
||||
then(spanId).isNotEmpty();
|
||||
return traceId;
|
||||
public String traceId(@RequestHeader("b3") String b3Single) {
|
||||
TraceContextOrSamplingFlags traceContext = B3SingleFormat
|
||||
.parseB3SingleFormat(b3Single);
|
||||
then(traceContext.context()).isNotNull();
|
||||
return b3Single;
|
||||
}
|
||||
|
||||
@RequestMapping("/")
|
||||
@@ -657,12 +656,10 @@ public class WebClientTests {
|
||||
}
|
||||
|
||||
@RequestMapping("/noresponse")
|
||||
public void noResponse(@RequestHeader(TRACE_ID_NAME) String traceId,
|
||||
@RequestHeader(SPAN_ID_NAME) String spanId,
|
||||
@RequestHeader(PARENT_ID_NAME) String parentId) {
|
||||
then(traceId).isNotEmpty();
|
||||
then(parentId).isNotEmpty();
|
||||
then(spanId).isNotEmpty();
|
||||
public void noResponse(@RequestHeader("b3") String b3Single) {
|
||||
TraceContextOrSamplingFlags traceContext = B3SingleFormat
|
||||
.parseB3SingleFormat(b3Single);
|
||||
then(traceContext.context()).isNotNull();
|
||||
}
|
||||
|
||||
public Span getSpan() {
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static tools.SpanUtil.idToHex;
|
||||
|
||||
/**
|
||||
* Runnable that will send a request via the provide rest template to the given url. It
|
||||
@@ -39,9 +40,6 @@ import static org.assertj.core.api.BDDAssertions.then;
|
||||
*/
|
||||
public class RequestSendingRunnable implements Runnable {
|
||||
|
||||
static final String TRACE_ID_NAME = "X-B3-TraceId";
|
||||
static final String SPAN_ID_NAME = "X-B3-SpanId";
|
||||
|
||||
private static final Log log = LogFactory.getLog(RequestSendingRunnable.class);
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
@@ -75,8 +73,7 @@ public class RequestSendingRunnable implements Runnable {
|
||||
|
||||
private RequestEntity<Void> requestWithTraceId() {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add(TRACE_ID_NAME, SpanUtil.idToHex(this.traceId));
|
||||
headers.add(SPAN_ID_NAME, SpanUtil.idToHex(this.spanId));
|
||||
headers.add("b3", idToHex(this.traceId) + "-" + idToHex(this.spanId));
|
||||
URI uri = URI.create(this.url);
|
||||
RequestEntity<Void> requestEntity = new RequestEntity<>(headers, HttpMethod.GET,
|
||||
uri);
|
||||
|
||||
@@ -100,10 +100,10 @@ public class ITTracingChannelInterceptorTests implements MessageHandler {
|
||||
// formerly known as TraceChannelInterceptorTest.executableSpanCreation
|
||||
@Test
|
||||
public void propagatesNoopSpan() {
|
||||
this.directChannel.send(
|
||||
MessageBuilder.withPayload("hi").setHeader("X-B3-Sampled", "0").build());
|
||||
this.directChannel
|
||||
.send(MessageBuilder.withPayload("hi").setHeader("b3", "0").build());
|
||||
|
||||
assertThat(this.message.getHeaders()).containsEntry("X-B3-Sampled", "0");
|
||||
assertThat(this.message.getHeaders()).containsEntry("b3", "0");
|
||||
|
||||
assertThat(this.currentSpan.isNoop()).isTrue();
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import brave.propagation.B3SingleFormat;
|
||||
import brave.propagation.TraceContext;
|
||||
import brave.sampler.Sampler;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -80,19 +82,12 @@ public class TraceContextPropagationChannelInterceptorTests {
|
||||
Message<?> message = this.channel.receive(0);
|
||||
assertThat(message).as("message was null").isNotNull();
|
||||
|
||||
String spanId = message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME,
|
||||
String.class);
|
||||
assertThat(spanId).as("spanId was equal to parent's id")
|
||||
String b3 = message.getHeaders().get("b3", String.class);
|
||||
// Trace and Span IDs are implicitly checked
|
||||
TraceContext extracted = B3SingleFormat.parseB3SingleFormat(b3).context();
|
||||
|
||||
assertThat(extracted.spanIdString()).as("spanId was equal to parent's id")
|
||||
.isNotEqualTo(expectedSpanId);
|
||||
|
||||
String traceId = message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME,
|
||||
String.class);
|
||||
assertThat(traceId).as("traceId was null").isNotNull();
|
||||
|
||||
String parentId = message.getHeaders().get(TraceMessageHeaders.PARENT_ID_NAME,
|
||||
String.class);
|
||||
assertThat(parentId).as("parentId was not equal to parent's id")
|
||||
.isEqualTo(this.reporter.getSpans().get(0).id());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
@@ -19,6 +19,8 @@ package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import brave.propagation.B3SingleFormat;
|
||||
import brave.propagation.TraceContext;
|
||||
import brave.sampler.Sampler;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -83,21 +85,12 @@ public class TraceStreamChannelInterceptorTests {
|
||||
Message<?> message = this.channel.receive(0);
|
||||
assertThat(message).as("message was null").isNotNull();
|
||||
|
||||
String spanId = message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME,
|
||||
String.class);
|
||||
assertThat(spanId).as("spanId was equal to parent's id")
|
||||
String b3 = message.getHeaders().get("b3", String.class);
|
||||
// Trace and Span IDs are implicitly checked
|
||||
TraceContext extracted = B3SingleFormat.parseB3SingleFormat(b3).context();
|
||||
|
||||
assertThat(extracted.spanIdString()).as("spanId was equal to parent's id")
|
||||
.isNotEqualTo(expectedSpanId);
|
||||
|
||||
String traceId = message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME,
|
||||
String.class);
|
||||
assertThat(traceId).as("traceId was null").isNotNull();
|
||||
|
||||
String parentId = message.getHeaders().get(TraceMessageHeaders.PARENT_ID_NAME,
|
||||
String.class);
|
||||
// [0] - producer
|
||||
// [1] - http:testsendmessage
|
||||
assertThat(parentId).as("parentId was not equal to parent's id")
|
||||
.isEqualTo(this.reporter.getSpans().get(1).id());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
@@ -28,14 +28,15 @@ import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import brave.Span;
|
||||
import brave.baggage.BaggagePropagation;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.propagation.B3Propagation;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent;
|
||||
import org.springframework.cloud.sleuth.util.SpanUtil;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -49,6 +50,9 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
|
||||
import static brave.Span.Kind.CLIENT;
|
||||
import static brave.propagation.B3Propagation.Format.SINGLE_NO_PARENT;
|
||||
import static brave.propagation.B3SingleFormat.writeB3SingleFormat;
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
|
||||
@@ -57,9 +61,6 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
|
||||
@DirtiesContext
|
||||
public class TraceCustomFilterResponseInjectorTests {
|
||||
|
||||
static final String TRACE_ID_NAME = "X-B3-TraceId";
|
||||
static final String SPAN_ID_NAME = "X-B3-SpanId";
|
||||
|
||||
@Autowired
|
||||
RestTemplate restTemplate;
|
||||
|
||||
@@ -80,7 +81,7 @@ public class TraceCustomFilterResponseInjectorTests {
|
||||
ResponseEntity<Map> responseEntity = this.restTemplate.exchange(requestEntity,
|
||||
Map.class);
|
||||
|
||||
then(responseEntity.getHeaders()).containsKeys(TRACE_ID_NAME, SPAN_ID_NAME)
|
||||
then(responseEntity.getHeaders()).containsKey("b3")
|
||||
.as("Trace headers must be present in response headers");
|
||||
}
|
||||
|
||||
@@ -90,6 +91,13 @@ public class TraceCustomFilterResponseInjectorTests {
|
||||
|
||||
int port;
|
||||
|
||||
@Bean
|
||||
BaggagePropagation.FactoryBuilder baggagePropagationFactoryBuilder() {
|
||||
// Use b3 single format as it is less verbose
|
||||
return BaggagePropagation.newFactoryBuilder(B3Propagation.newFactoryBuilder()
|
||||
.injectFormat(CLIENT, SINGLE_NO_PARENT).build());
|
||||
}
|
||||
|
||||
// tag::configuration[]
|
||||
@Bean
|
||||
HttpResponseInjectingTraceFilter responseInjectingTraceFilter(
|
||||
@@ -129,9 +137,7 @@ public class TraceCustomFilterResponseInjectorTests {
|
||||
FilterChain filterChain) throws IOException, ServletException {
|
||||
HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
Span currentSpan = this.httpTracing.tracing().tracer().currentSpan();
|
||||
response.addHeader("X-B3-TraceId", currentSpan.context().traceIdString());
|
||||
response.addHeader("X-B3-SpanId",
|
||||
SpanUtil.idToHex(currentSpan.context().spanId()));
|
||||
response.addHeader("b3", writeB3SingleFormat(currentSpan.context()));
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
|
||||
@@ -69,12 +69,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
@SpringBootTest(classes = TraceFilterIntegrationTests.Config.class)
|
||||
public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest {
|
||||
|
||||
static final String TRACE_ID_NAME = "X-B3-TraceId";
|
||||
static final String SPAN_ID_NAME = "X-B3-SpanId";
|
||||
static final String SAMPLED_NAME = "X-B3-Sampled";
|
||||
|
||||
private static Log logger = LogFactory.getLog(TraceFilterIntegrationTests.class);
|
||||
|
||||
private static Span span;
|
||||
|
||||
@Autowired
|
||||
@@ -244,72 +238,69 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest {
|
||||
}
|
||||
|
||||
private MvcResult whenSentPingWithTraceId(Long passedTraceId) throws Exception {
|
||||
return sendPingWithTraceId(TRACE_ID_NAME, passedTraceId);
|
||||
return sendPingWithTraceId(passedTraceId);
|
||||
}
|
||||
|
||||
private MvcResult whenSentInfoWithTraceId(Long passedTraceId) throws Exception {
|
||||
return sendRequestWithTraceId("/additionalContextPath/info", TRACE_ID_NAME,
|
||||
passedTraceId);
|
||||
return sendRequestWithTraceId("/additionalContextPath/info", passedTraceId);
|
||||
}
|
||||
|
||||
private MvcResult whenSentFutureWithTraceId(Long passedTraceId) throws Exception {
|
||||
return sendRequestWithTraceId("/future", TRACE_ID_NAME, passedTraceId);
|
||||
return sendRequestWithTraceId("/future", passedTraceId);
|
||||
}
|
||||
|
||||
private MvcResult whenSentDeferredWithTraceId(Long passedTraceId) throws Exception {
|
||||
return sendDeferredWithTraceId(TRACE_ID_NAME, passedTraceId);
|
||||
return sendDeferredWithTraceId(passedTraceId);
|
||||
}
|
||||
|
||||
private MvcResult whenSentToNonExistentEndpointWithTraceId(Long passedTraceId)
|
||||
throws Exception {
|
||||
return sendRequestWithTraceId("/exception/nonExistent", TRACE_ID_NAME,
|
||||
passedTraceId, HttpStatus.NOT_FOUND);
|
||||
return sendRequestWithTraceId("/exception/nonExistent", passedTraceId,
|
||||
HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
private MvcResult whenSentToExceptionThrowingEndpoint(Long passedTraceId)
|
||||
throws Exception {
|
||||
return sendRequestWithTraceId("/throwsException", TRACE_ID_NAME, passedTraceId,
|
||||
return sendRequestWithTraceId("/throwsException", passedTraceId,
|
||||
HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
private MvcResult sendPingWithTraceId(String headerName, Long traceId)
|
||||
throws Exception {
|
||||
return sendRequestWithTraceId("/ping", headerName, traceId);
|
||||
private MvcResult sendPingWithTraceId(Long traceId) throws Exception {
|
||||
return sendRequestWithTraceId("/ping", traceId);
|
||||
}
|
||||
|
||||
private MvcResult sendDeferredWithTraceId(String headerName, Long traceId)
|
||||
throws Exception {
|
||||
return sendRequestWithTraceId("/deferred", headerName, traceId);
|
||||
private MvcResult sendDeferredWithTraceId(Long traceId) throws Exception {
|
||||
return sendRequestWithTraceId("/deferred", traceId);
|
||||
}
|
||||
|
||||
private MvcResult sendRequestWithTraceId(String path, String headerName, Long traceId)
|
||||
throws Exception {
|
||||
private MvcResult sendRequestWithTraceId(String path, Long traceId) throws Exception {
|
||||
return this.mockMvc
|
||||
.perform(MockMvcRequestBuilders.get(path).accept(MediaType.TEXT_PLAIN)
|
||||
.header(headerName, SpanUtil.idToHex(traceId))
|
||||
.header(SPAN_ID_NAME, SpanUtil.idToHex(new Random().nextLong())))
|
||||
.header("b3",
|
||||
SpanUtil.idToHex(traceId) + "-"
|
||||
+ SpanUtil.idToHex(new Random().nextLong())))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
private MvcResult whenSentRequestWithTraceIdAndNoSpanId(Long traceId)
|
||||
throws Exception {
|
||||
return this.mockMvc
|
||||
.perform(MockMvcRequestBuilders.get("/ping").accept(MediaType.TEXT_PLAIN)
|
||||
.header(TRACE_ID_NAME, SpanUtil.idToHex(traceId)))
|
||||
return this.mockMvc.perform(MockMvcRequestBuilders.get("/ping")
|
||||
.accept(MediaType.TEXT_PLAIN).header("b3", SpanUtil.idToHex(traceId)))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
private MvcResult sendRequestWithTraceId(String path, String headerName, Long traceId,
|
||||
HttpStatus status) throws Exception {
|
||||
private MvcResult sendRequestWithTraceId(String path, Long traceId, HttpStatus status)
|
||||
throws Exception {
|
||||
return this.mockMvc
|
||||
.perform(MockMvcRequestBuilders.get(path).accept(MediaType.TEXT_PLAIN)
|
||||
.header(headerName, SpanUtil.idToHex(traceId))
|
||||
.header(SPAN_ID_NAME, SpanUtil.idToHex(new Random().nextLong())))
|
||||
.header("b3",
|
||||
SpanUtil.idToHex(traceId) + "-"
|
||||
+ SpanUtil.idToHex(new Random().nextLong())))
|
||||
.andExpect(status().is(status.value())).andReturn();
|
||||
}
|
||||
|
||||
private boolean notSampledHeaderIsPresent(MvcResult mvcResult) {
|
||||
return "0".equals(mvcResult.getResponse().getHeader(SAMPLED_NAME));
|
||||
return "0".equals(mvcResult.getResponse().getHeader("b3"));
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
|
||||
@@ -52,9 +52,6 @@ public class GH1102Tests {
|
||||
@Autowired
|
||||
TestRetry testRetry;
|
||||
|
||||
@Autowired
|
||||
ArrayListSpanReporter reporter;
|
||||
|
||||
@LocalServerPort
|
||||
int port;
|
||||
|
||||
@@ -73,6 +70,7 @@ public class GH1102Tests {
|
||||
foo.finish();
|
||||
}
|
||||
|
||||
// Default inject format for client spans is B3 multi
|
||||
BDDAssertions.then(this.testRetry.getHttpHeaders().get("x-b3-traceid"))
|
||||
.hasSize(1);
|
||||
}
|
||||
|
||||
@@ -155,8 +155,7 @@ public class TraceWebFluxTests {
|
||||
private ClientResponse whenNonSampledRequestIsSent(int port) {
|
||||
Mono<ClientResponse> exchange = WebClient.create().get()
|
||||
.uri("http://localhost:" + port + "/api/c2/10")
|
||||
.header("X-B3-SpanId", EXPECTED_TRACE_ID)
|
||||
.header("X-B3-TraceId", EXPECTED_TRACE_ID).header("X-B3-Sampled", "0")
|
||||
.header("b3", EXPECTED_TRACE_ID + "-" + EXPECTED_TRACE_ID + "-0")
|
||||
.exchange();
|
||||
return exchange.block();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user