Stops propagating Sleuth 1.x spring-messaging headers

To reduce confusion and overhead, the following custom spring-messaging headers added in Sleuth 1.0 are no longer sent, and a log warning is issued once if they are by outside code.

* spanId
* spanSampled
* spanParentSpanId
* spanTraceId
* spanFlags

Sending the above headers actually increases the headers by up to 10 because they are duplicated in the "native" part of messages. This overhead is extreme especially if messages never leave the process.

The solution is to only send [b3 single format](https://github.com/openzipkin/b3-propagation#single-header), which has been in sleuth since 2.0 and is compatible with JMS. The B3 single format is always parsed and takes precedence, even if multiple headers are sent, so this is a safe change.

Note: Unlike RPC, messaging spans never join with their parent. Better performance is achieved by not propagating the producer's parentId downstream.

Note: Deprecated spring-messaging headers such "spanTraceId" as are still read in Sleuth 3.x. However, they will not be at some point in the future. Please pay attention to the log messages and update your code if you are accidentally using them.
This commit is contained in:
Adrian Cole
2020-04-07 10:58:49 +08:00
parent 01e0c97838
commit b92db4ce51
22 changed files with 233 additions and 316 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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