diff --git a/pom.xml b/pom.xml index 8a83b05f1..0bc993b77 100644 --- a/pom.xml +++ b/pom.xml @@ -132,13 +132,6 @@ - - - io.projectreactor.netty - reactor-netty - 0.9.6.RELEASE - true - org.springframework.cloud spring-cloud-sleuth-dependencies diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/baggage/TraceBaggageAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/baggage/TraceBaggageAutoConfiguration.java index 7019cf0df..c108609c5 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/baggage/TraceBaggageAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/baggage/TraceBaggageAutoConfiguration.java @@ -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. + * + *

+ * 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 diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessageHeaderPropagation.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessageHeaderPropagation.java index 4d0fffa9c..517ae3a6b 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessageHeaderPropagation.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessageHeaderPropagation.java @@ -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 DEPRECATED_HEADERS = new LinkedHashSet<>(); + private static final Map 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 propagationHeaders(Map 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); diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessageHeaders.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessageHeaders.java index c90086733..1eb55d4ed 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessageHeaders.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessageHeaders.java @@ -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 { /** diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationPropagationCustomizationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationPropagationCustomizationTests.java index 6dcf250be..98c1e35f7 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationPropagationCustomizationTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfigurationPropagationCustomizationTests.java @@ -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); }); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/MessageHeaderPropagationTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/MessageHeaderPropagationTest.java index 8fef12e81..541e4f481 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/MessageHeaderPropagationTest.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/MessageHeaderPropagationTest.java @@ -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"); } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/PropagationSetterTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/PropagationSetterTest.java index 978d27fc9..cb91b2821 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/PropagationSetterTest.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/PropagationSetterTest.java @@ -40,40 +40,38 @@ public abstract class PropagationSetterTest { @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"); } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptorTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptorTest.java index b2a275f2a..0903497e3 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptorTest.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptorTest.java @@ -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 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 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 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); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/opentracing/BraveTracerTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/opentracing/BraveTracerTest.java index 77d2d6c7c..26cfd89b0 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/opentracing/BraveTracerTest.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/opentracing/BraveTracerTest.java @@ -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 map = new LinkedHashMap<>(); - map.put("X-B3-TraceId", "0000000000000001"); - map.put("X-B3-SpanId", "0000000000000002"); - map.put("X-B3-Sampled", "1"); + Map 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 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 map = new LinkedHashMap<>(); - map.put("X-B3-TraceId", "0000000000000001"); - map.put("X-B3-SpanId", "0000000000000002"); - map.put("X-B3-Sampled", "1"); + Map 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 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 diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java index 9bb9d3172..f3657d089 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java @@ -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 = 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); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceRestTemplateInterceptorTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceRestTemplateInterceptorTests.java index ac48af08d..660971168 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceRestTemplateInterceptorTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceRestTemplateInterceptorTests.java @@ -93,6 +93,7 @@ public class TraceRestTemplateInterceptorTests { Map 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 home(@RequestHeader HttpHeaders headers) { this.span = TraceRestTemplateInterceptorTests.this.tracer.currentSpan(); - Map map = new HashMap(); + Map 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; } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRequestHttpHeadersFilterTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRequestHttpHeadersFilterTests.java index 2dae352d6..cdb5f662d 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRequestHttpHeadersFilterTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRequestHttpHeadersFilterTests.java @@ -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(); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceResponseHttpHeadersFilterTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceResponseHttpHeadersFilterTests.java index 7ae0ede25..222d56002 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceResponseHttpHeadersFilterTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceResponseHttpHeadersFilterTests.java @@ -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(); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/integration/WebClientTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/integration/WebClientTests.java index a9d4abd9c..38d612522 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/integration/WebClientTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/integration/WebClientTests.java @@ -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 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 spans = this.reporter.getSpans(); then(spans).isNotEmpty(); Optional noTraceSpan = new ArrayList<>(spans).stream() @@ -220,8 +219,7 @@ public class WebClientTests { try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) { ResponseEntity> 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 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() { diff --git a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-test-core/src/main/java/tools/RequestSendingRunnable.java b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-test-core/src/main/java/tools/RequestSendingRunnable.java index eae741a9c..373245fea 100644 --- a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-test-core/src/main/java/tools/RequestSendingRunnable.java +++ b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-test-core/src/main/java/tools/RequestSendingRunnable.java @@ -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 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 requestEntity = new RequestEntity<>(headers, HttpMethod.GET, uri); diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/ITTracingChannelInterceptorTests.java b/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/ITTracingChannelInterceptorTests.java index b582ff22f..71c87a90b 100644 --- a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/ITTracingChannelInterceptorTests.java +++ b/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/ITTracingChannelInterceptorTests.java @@ -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(); } diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceContextPropagationChannelInterceptorTests.java b/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceContextPropagationChannelInterceptorTests.java index 9cbd75c06..269a45ffa 100644 --- a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceContextPropagationChannelInterceptorTests.java +++ b/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceContextPropagationChannelInterceptorTests.java @@ -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 diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceStreamChannelInterceptorTests.java b/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceStreamChannelInterceptorTests.java index b4cefbb70..9c1617d59 100644 --- a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceStreamChannelInterceptorTests.java +++ b/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceStreamChannelInterceptorTests.java @@ -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 diff --git a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceCustomFilterResponseInjectorTests.java b/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceCustomFilterResponseInjectorTests.java index f531b0afd..296243663 100644 --- a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceCustomFilterResponseInjectorTests.java +++ b/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceCustomFilterResponseInjectorTests.java @@ -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 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); } diff --git a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterIntegrationTests.java b/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterIntegrationTests.java index 49d3ae960..261d00891 100644 --- a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterIntegrationTests.java +++ b/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterIntegrationTests.java @@ -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 diff --git a/tests/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/GH1102Tests.java b/tests/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/GH1102Tests.java index ea6d437a1..206e2a862 100644 --- a/tests/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/GH1102Tests.java +++ b/tests/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/GH1102Tests.java @@ -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); } diff --git a/tests/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceWebFluxTests.java b/tests/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceWebFluxTests.java index 322a28a18..eca71f507 100644 --- a/tests/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceWebFluxTests.java +++ b/tests/spring-cloud-sleuth-instrumentation-webflux-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceWebFluxTests.java @@ -155,8 +155,7 @@ public class TraceWebFluxTests { private ClientResponse whenNonSampledRequestIsSent(int port) { Mono 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(); }