diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/B3Utils.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/B3Utils.java new file mode 100644 index 000000000..4ed81290a --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/B3Utils.java @@ -0,0 +1,145 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.sleuth; + +import java.util.List; +import java.util.Map; + +import org.springframework.util.StringUtils; + +/** + * Utility class to retrieve tracing ids from headers + * + * @author Marcin Grzejszczak + * @since 1.3.5 + */ +public final class B3Utils { + + /** + * Tries to retrieve trace id from b3 header. Falls back to standard header + * if there's nothing there in b3 header + */ + public static String toB3String(Span span) { + String traceId = span.traceIdString(); + String spanId = Span.idToHex(span.getSpanId()); + boolean sampled = span.isExportable(); + List parents = span.getParents(); + StringBuilder b3 = new StringBuilder() + .append(traceId).append("-") + .append(spanId).append("-") + .append(sampled ? Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED); + if (parents != null && !parents.isEmpty()) { + b3 = b3.append("-").append(Span.idToHex(parents.get(0))); + } + return b3.toString(); + } + + /** + * Tries to retrieve trace id from b3 header. Falls back to standard header + * if there's nothing there in b3 header + */ + public static String traceId(String b3HeaderName, + String fallbackHeaderName, Map carrier) { + String b3 = carrier.get(b3HeaderName); + if (StringUtils.hasText(b3)) { + String[] split = b3.split("-"); + if (split.length > 1) { + return split[0]; + } + } + return carrier.get(fallbackHeaderName); + } + + /** + * Tries to retrieve span id from b3 header. Falls back to standard header + * if there's nothing there in b3 header + */ + public static String spanId(String b3HeaderName, + String fallbackHeaderName, Map carrier) { + String b3 = carrier.get(b3HeaderName); + if (StringUtils.hasText(b3)) { + String[] split = b3.split("-"); + if (split.length > 1) { + return split[1]; + } + } + return carrier.get(fallbackHeaderName); + } + + /** + * Tries to retrieve parent span id from b3 header. Falls back to standard header + * if there's nothing there in b3 header + */ + public static String parentSpanId(String b3HeaderName, + String fallbackHeaderName, Map carrier) { + String b3 = carrier.get(b3HeaderName); + if (StringUtils.hasText(b3)) { + String[] split = b3.split("-"); + if (split.length == 4) { + return split[3]; + } + } + return carrier.get(fallbackHeaderName); + } + + /** + * Tries to retrieve sample flag from b3 header. Falls back to standard span flag header + * and sampled header, if there's nothing there in b3 header + */ + public static Sampled sampled(String b3HeaderName, + String fallbackSampledHeaderName, String fallbackFlagsHeaderName, + Map carrier) { + String b3 = carrier.get(b3HeaderName); + if (StringUtils.hasText(b3)) { + String[] split = b3.split("-"); + if (split.length > 2) { + return Sampled.from(split[2]); + } + } + String fallbackFlag = carrier.get(fallbackFlagsHeaderName); + if (Span.SPAN_SAMPLED.equals(fallbackFlag)) { + return Sampled.DEBUG; + } + return Sampled.from(carrier.get(fallbackSampledHeaderName)); + } + + public enum Sampled { + SAMPLED('1'), NOT_SAMPLED('0'), DEBUG('d'); + + final char sampledChar; + + Sampled(char sampledChar) { + this.sampledChar = sampledChar; + } + + static Sampled from(String value) { + if (StringUtils.hasText(value)) { + switch (value) { + case "1" : return SAMPLED; + case "0" : return NOT_SAMPLED; + case "d" : return DEBUG; + } + } + return null; + } + + @Override public String toString() { + return String.valueOf(this.sampledChar); + } + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingExtractor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingExtractor.java index b34fe941a..bba7eeb03 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingExtractor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingExtractor.java @@ -3,10 +3,10 @@ package org.springframework.cloud.sleuth.instrument.messaging; import java.util.Map; import java.util.Random; +import org.springframework.cloud.sleuth.B3Utils; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.SpanTextMap; import org.springframework.cloud.sleuth.util.TextMapUtil; -import org.springframework.util.StringUtils; /** * Default implementation for messaging @@ -62,9 +62,9 @@ public class HeaderBasedMessagingExtractor implements MessagingSpanTextMapExtrac .traceIdHigh(traceId.length() == 32 ? Span.hexToId(traceId, 0) : 0) .traceId(Span.hexToId(traceId)) .spanId(Span.hexToId(spanId(carrier))); - String flags = carrier.get(TraceMessageHeaders.SPAN_FLAGS_NAME); - boolean debug = Span.SPAN_SAMPLED.equals(flags); - boolean spanSampled = Span.SPAN_SAMPLED.equals(sampled(carrier)); + B3Utils.Sampled sampled = sampled(carrier); + boolean debug = sampled == B3Utils.Sampled.DEBUG; + boolean spanSampled = sampled == B3Utils.Sampled.SAMPLED; if (debug) { spanBuilder.exportable(true); } else { @@ -90,36 +90,19 @@ public class HeaderBasedMessagingExtractor implements MessagingSpanTextMapExtrac } private String traceId(Map carrier) { - String b3 = carrier.get(TraceMessageHeaders.B3_NAME); - if (StringUtils.hasText(b3)) { - String[] split = b3.split("-"); - if (split.length == 3) { - return split[0]; - } - } - return carrier.get(TraceMessageHeaders.TRACE_ID_NAME); + return B3Utils.traceId(TraceMessageHeaders.B3_NAME, + TraceMessageHeaders.TRACE_ID_NAME, carrier); } private String spanId(Map carrier) { - String b3 = carrier.get(TraceMessageHeaders.B3_NAME); - if (StringUtils.hasText(b3)) { - String[] split = b3.split("-"); - if (split.length == 3) { - return split[1]; - } - } - return carrier.get(TraceMessageHeaders.SPAN_ID_NAME); + return B3Utils.spanId(TraceMessageHeaders.B3_NAME, + TraceMessageHeaders.SPAN_ID_NAME, carrier); } - private String sampled(Map carrier) { - String b3 = carrier.get(TraceMessageHeaders.B3_NAME); - if (StringUtils.hasText(b3)) { - String[] split = b3.split("-"); - if (split.length == 3) { - return split[2]; - } - } - return carrier.get(TraceMessageHeaders.SAMPLED_NAME); + private B3Utils.Sampled sampled(Map carrier) { + return B3Utils.sampled(TraceMessageHeaders.B3_NAME, + TraceMessageHeaders.SAMPLED_NAME, + TraceMessageHeaders.SPAN_FLAGS_NAME, carrier); } boolean hasHeader(Map message, String name) { @@ -128,7 +111,7 @@ public class HeaderBasedMessagingExtractor implements MessagingSpanTextMapExtrac private void setParentIdIfApplicable(Map carrier, Span.SpanBuilder spanBuilder, String spanParentIdHeader) { - String parentId = carrier.get(spanParentIdHeader); + String parentId = B3Utils.parentSpanId(TraceMessageHeaders.B3_NAME, spanParentIdHeader, carrier); if (parentId != null) { spanBuilder.parent(Span.hexToId(parentId)); } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingInjector.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingInjector.java index 755906baf..bdb0b642f 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingInjector.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingInjector.java @@ -3,6 +3,7 @@ package org.springframework.cloud.sleuth.instrument.messaging; import java.util.List; import java.util.Map; +import org.springframework.cloud.sleuth.B3Utils; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.SpanTextMap; import org.springframework.cloud.sleuth.TraceKeys; @@ -43,8 +44,7 @@ public class HeaderBasedMessagingInjector implements MessagingSpanTextMapInjecto private void addHeaders(Map map, Span span, SpanTextMap textMap) { addHeader(map, textMap, TraceMessageHeaders.TRACE_ID_NAME, span.traceIdString()); addHeader(map, textMap, TraceMessageHeaders.SPAN_ID_NAME, Span.idToHex(span.getSpanId())); - addHeader(map, textMap, TraceMessageHeaders.B3_NAME, span.traceIdString() + "-" + - Span.idToHex(span.getSpanId()) + "-" + (span.isExportable() ? Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED)); + addHeader(map, textMap, TraceMessageHeaders.B3_NAME, B3Utils.toB3String(span)); if (span.isExportable()) { addAnnotations(this.traceKeys, textMap, span); Long parentId = getFirst(span.getParents()); diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanExtractor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanExtractor.java index 70c3f2d85..10137ed76 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanExtractor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanExtractor.java @@ -6,6 +6,7 @@ import java.util.Random; import java.util.regex.Pattern; import org.apache.commons.logging.LogFactory; +import org.springframework.cloud.sleuth.B3Utils; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.SpanTextMap; import org.springframework.util.StringUtils; @@ -82,36 +83,16 @@ public class ZipkinHttpSpanExtractor implements HttpSpanExtractor { } private String traceId(Map carrier) { - String b3 = carrier.get(Span.B3_NAME); - if (StringUtils.hasText(b3)) { - String[] split = b3.split("-"); - if (split.length == 3) { - return split[0]; - } - } - return carrier.get(Span.TRACE_ID_NAME); + return B3Utils.traceId(Span.B3_NAME, Span.TRACE_ID_NAME, carrier); } private String spanId(Map carrier) { - String b3 = carrier.get(Span.B3_NAME); - if (StringUtils.hasText(b3)) { - String[] split = b3.split("-"); - if (split.length == 3) { - return split[1]; - } - } - return carrier.get(Span.SPAN_ID_NAME); + return B3Utils.spanId(Span.B3_NAME, Span.SPAN_ID_NAME, carrier); } - private String sampled(Map carrier) { - String b3 = carrier.get(Span.B3_NAME); - if (StringUtils.hasText(b3)) { - String[] split = b3.split("-"); - if (split.length == 3) { - return split[2]; - } - } - return carrier.get(Span.SAMPLED_NAME); + private B3Utils.Sampled sampled(Map carrier) { + return B3Utils.sampled(Span.B3_NAME, + Span.SAMPLED_NAME, Span.SPAN_FLAGS, carrier); } private String traceIdOrDefaut(Map carrier) { @@ -139,18 +120,18 @@ public class ZipkinHttpSpanExtractor implements HttpSpanExtractor { if (StringUtils.hasText(processId)) { span.processId(processId); } - String parentId = carrier.get(Span.PARENT_ID_NAME); + String parentId = B3Utils.parentSpanId(Span.B3_NAME, Span.PARENT_ID_NAME, carrier); if (parentId != null) { span.parent(Span.hexToId(parentId)); } span.remote(true); - + B3Utils.Sampled sampled = sampled(carrier); boolean skip = this.skipPattern .matcher(carrier.get(ZipkinHttpSpanMapper.URI_HEADER)).matches() - || Span.SPAN_NOT_SAMPLED.equals(sampled(carrier)); + || sampled == B3Utils.Sampled.NOT_SAMPLED; // trace, span id were retrieved from the headers and span is sampled span.shared(!(skip || idToBeGenerated)); - boolean debug = Span.SPAN_SAMPLED.equals(carrier.get(Span.SPAN_FLAGS)); + boolean debug = sampled == B3Utils.Sampled.DEBUG; if (debug) { span.exportable(true); } else if (skip) { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanInjector.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanInjector.java index e7c57708f..37b015ae9 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanInjector.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanInjector.java @@ -2,6 +2,7 @@ package org.springframework.cloud.sleuth.instrument.web; import java.util.Map; +import org.springframework.cloud.sleuth.B3Utils; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.SpanTextMap; import org.springframework.util.StringUtils; @@ -19,7 +20,7 @@ public class ZipkinHttpSpanInjector implements HttpSpanInjector { @Override public void inject(Span span, SpanTextMap map) { Map carrier = SPAN_CARRIER_MAPPER.convert(map); - setHeader(map, carrier, Span.B3_NAME, b3(span)); + setHeader(map, carrier, Span.B3_NAME, B3Utils.toB3String(span)); setHeader(map, carrier, Span.TRACE_ID_NAME, span.traceIdString()); setIdHeader(map, carrier, Span.SPAN_ID_NAME, span.getSpanId()); setHeader(map, carrier, Span.SAMPLED_NAME, span.isExportable() ? Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED); @@ -31,11 +32,6 @@ public class ZipkinHttpSpanInjector implements HttpSpanInjector { } } - private String b3(Span span) { - return span.traceIdString() + "-" + Span.idToHex(span.getSpanId()) + "-" + - (span.isExportable() ? Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED); - } - private String prefixedKey(String key) { if (key.startsWith(Span.SPAN_BAGGAGE_HEADER_PREFIX + ZipkinHttpSpanMapper.HEADER_DELIMITER)) { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/B3UtilsTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/B3UtilsTest.java new file mode 100644 index 000000000..12cd29d94 --- /dev/null +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/B3UtilsTest.java @@ -0,0 +1,177 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.sleuth; + +import java.util.HashMap; +import java.util.Map; + +import org.assertj.core.api.BDDAssertions; +import org.junit.Test; + +/** + * @author Marcin Grzejszczak + */ +public class B3UtilsTest { + + @Test public void should_build_a_b3_string_without_parent_span() { + Span span = Span.builder() + .traceId(1L) + .spanId(2L) + .exportable(true).build(); + + String b3String = B3Utils.toB3String(span); + + BDDAssertions.then(b3String).isEqualTo("0000000000000001-0000000000000002-1"); + } + + @Test public void should_build_a_b3_string_without_parent_span_for_unsampled() { + Span span = Span.builder() + .traceId(1L) + .spanId(2L) + .exportable(false).build(); + + String b3String = B3Utils.toB3String(span); + + BDDAssertions.then(b3String).isEqualTo("0000000000000001-0000000000000002-0"); + } + + @Test public void should_build_a_b3_string_with_parent_span() { + Span span = Span.builder() + .traceId(1L) + .spanId(2L) + .parent(3L) + .exportable(true).build(); + + String b3String = B3Utils.toB3String(span); + + BDDAssertions.then(b3String).isEqualTo("0000000000000001-0000000000000002-1-0000000000000003"); + } + + @Test public void should_read_trace_id_from_b3_string() { + Map map = new HashMap<>(); + map.put("b3", "0000000000000001-0000000000000002-1-0000000000000003"); + + String id = B3Utils.traceId("b3", "fallback", map); + + BDDAssertions.then(id).isEqualTo("0000000000000001"); + } + + @Test public void should_read_trace_id_from_fallback() { + Map map = new HashMap<>(); + map.put("fallback", "0000000000000001"); + + String id = B3Utils.traceId("b3", "fallback", map); + + BDDAssertions.then(id).isEqualTo("0000000000000001"); + } + + @Test public void should_read_span_id_from_b3_string() { + Map map = new HashMap<>(); + map.put("b3", "0000000000000001-0000000000000002-1-0000000000000003"); + + String id = B3Utils.spanId("b3", "fallback", map); + + BDDAssertions.then(id).isEqualTo("0000000000000002"); + } + + @Test public void should_read_span_id_from_fallback() { + Map map = new HashMap<>(); + map.put("fallback", "0000000000000002"); + + String id = B3Utils.spanId("b3", "fallback", map); + + BDDAssertions.then(id).isEqualTo("0000000000000002"); + } + + @Test public void should_read_parent_id_from_b3_string() { + Map map = new HashMap<>(); + map.put("b3", "0000000000000001-0000000000000002-1-0000000000000003"); + + String id = B3Utils.parentSpanId("b3", "fallback", map); + + BDDAssertions.then(id).isEqualTo("0000000000000003"); + } + + @Test public void should_read_parent_id_from_fallback() { + Map map = new HashMap<>(); + map.put("fallback", "0000000000000003"); + + String id = B3Utils.parentSpanId("b3", "fallback", map); + + BDDAssertions.then(id).isEqualTo("0000000000000003"); + } + + @Test public void should_read_sampled_id_from_b3_string() { + Map map = new HashMap<>(); + map.put("b3", "0000000000000001-0000000000000002-1-0000000000000003"); + + B3Utils.Sampled sampled = B3Utils.sampled("b3", "fallbackSampled", + "fallbackFlags", map); + + BDDAssertions.then(sampled).isEqualTo(B3Utils.Sampled.SAMPLED); + } + + @Test public void should_read_not_sampled_id_from_b3_string() { + Map map = new HashMap<>(); + map.put("b3", "0000000000000001-0000000000000002-0-0000000000000003"); + + B3Utils.Sampled sampled = B3Utils.sampled("b3", "fallbackSampled", + "fallbackFlags", map); + + BDDAssertions.then(sampled).isEqualTo(B3Utils.Sampled.NOT_SAMPLED); + } + + @Test public void should_read_debug_sampled_id_from_b3_string() { + Map map = new HashMap<>(); + map.put("b3", "0000000000000001-0000000000000002-d-0000000000000003"); + + B3Utils.Sampled sampled = B3Utils.sampled("b3", "fallbackSampled", + "fallbackFlags", map); + + BDDAssertions.then(sampled).isEqualTo(B3Utils.Sampled.DEBUG); + } + + @Test public void should_read_sampled_id_from_fallback() { + Map map = new HashMap<>(); + map.put("fallbackSampled", "1"); + + B3Utils.Sampled sampled = B3Utils.sampled("b3", "fallbackSampled", + "fallbackFlags", map); + + BDDAssertions.then(sampled).isEqualTo(B3Utils.Sampled.SAMPLED); + } + + @Test public void should_read_not_sampled_id_from_fallback() { + Map map = new HashMap<>(); + map.put("fallbackSampled", "0"); + + B3Utils.Sampled sampled = B3Utils.sampled("b3", "fallbackSampled", + "fallbackFlags", map); + + BDDAssertions.then(sampled).isEqualTo(B3Utils.Sampled.NOT_SAMPLED); + } + + @Test public void should_read_debug_id_from_fallback() { + Map map = new HashMap<>(); + map.put("fallbackFlags", "1"); + + B3Utils.Sampled sampled = B3Utils.sampled("b3", "fallbackSampled", + "fallbackFlags", map); + + BDDAssertions.then(sampled).isEqualTo(B3Utils.Sampled.DEBUG); + } +} \ No newline at end of file diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/assertions/SpanAssert.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/assertions/SpanAssert.java index 62a96d1d0..5e0ac2fc6 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/assertions/SpanAssert.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/assertions/SpanAssert.java @@ -21,6 +21,7 @@ import java.util.Objects; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.assertj.core.api.AbstractAssert; +import org.assertj.core.api.Assertions; import org.springframework.cloud.sleuth.Span; public class SpanAssert extends AbstractAssert { @@ -45,6 +46,18 @@ public class SpanAssert extends AbstractAssert { return this; } + public SpanAssert hasParentSpanIdEqualTo(Long spanId) { + isNotNull(); + Assertions.assertThat(this.actual.getParents()).isNotEmpty(); + Long parentSpanId = this.actual.getParents().get(0); + if (!Objects.equals(parentSpanId, spanId)) { + String message = String.format("Expected span's parent spanId to be <%s> but was <%s>", spanId, parentSpanId); + log.error(message); + failWithMessage(message); + } + return this; + } + public SpanAssert hasSpanIdEqualTo(Long spanId) { isNotNull(); if (!Objects.equals(this.actual.getSpanId(), spanId)) { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingExtractorTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingExtractorTests.java index df44e8318..f731ab3eb 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingExtractorTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingExtractorTests.java @@ -32,7 +32,25 @@ import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then; public class HeaderBasedMessagingExtractorTests { @Test - public void b3HeadersTakePrecedenceOverAnyOtherHeaders() { + public void simpleB3HeadersTakePrecedenceOverAnyOtherHeaders() { + HeaderBasedMessagingExtractor extractor = new HeaderBasedMessagingExtractor(); + SpanTextMap spanTextMap = spanTextMap(); + spanTextMap.put(TraceMessageHeaders.B3_NAME, "0000000000000005-0000000000000004"); + spanTextMap.put(TraceMessageHeaders.SPAN_ID_NAME, Span.idToHex(20L)); + spanTextMap.put(TraceMessageHeaders.TRACE_ID_NAME, Span.idToHex(30L)); + spanTextMap.put(TraceMessageHeaders.SAMPLED_NAME, "1"); + + Span span = extractor.joinTrace(spanTextMap); + + then(span) + .isNotNull() + .hasTraceIdEqualTo(5L) + .hasSpanIdEqualTo(4L) + .isExportable(); + } + + @Test + public void b3HeadersWithSampledTakePrecedenceOverAnyOtherHeaders() { HeaderBasedMessagingExtractor extractor = new HeaderBasedMessagingExtractor(); SpanTextMap spanTextMap = spanTextMap(); spanTextMap.put(TraceMessageHeaders.B3_NAME, "0000000000000005-0000000000000004-1"); @@ -49,6 +67,45 @@ public class HeaderBasedMessagingExtractorTests { .isExportable(); } + @Test + public void b3HeadersWithDebugSampledTakePrecedenceOverAnyOtherHeaders() { + HeaderBasedMessagingExtractor extractor = new HeaderBasedMessagingExtractor(); + SpanTextMap spanTextMap = spanTextMap(); + spanTextMap.put(TraceMessageHeaders.B3_NAME, "0000000000000005-0000000000000004-d"); + spanTextMap.put(TraceMessageHeaders.SPAN_ID_NAME, Span.idToHex(20L)); + spanTextMap.put(TraceMessageHeaders.TRACE_ID_NAME, Span.idToHex(30L)); + spanTextMap.put(TraceMessageHeaders.SAMPLED_NAME, "0"); + spanTextMap.put(TraceMessageHeaders.SPAN_FLAGS_NAME, "0"); + + Span span = extractor.joinTrace(spanTextMap); + + then(span) + .isNotNull() + .hasTraceIdEqualTo(5L) + .hasSpanIdEqualTo(4L) + .isExportable(); + } + + @Test + public void b3HeadersWithParentSpanIdTakePrecedenceOverAnyOtherHeaders() { + HeaderBasedMessagingExtractor extractor = new HeaderBasedMessagingExtractor(); + SpanTextMap spanTextMap = spanTextMap(); + spanTextMap.put(TraceMessageHeaders.B3_NAME, "0000000000000005-0000000000000004-1-0000000000000003"); + spanTextMap.put(TraceMessageHeaders.SPAN_ID_NAME, Span.idToHex(20L)); + spanTextMap.put(TraceMessageHeaders.TRACE_ID_NAME, Span.idToHex(30L)); + spanTextMap.put(TraceMessageHeaders.PARENT_ID_NAME, Span.idToHex(60L)); + spanTextMap.put(TraceMessageHeaders.SAMPLED_NAME, "0"); + + Span span = extractor.joinTrace(spanTextMap); + + then(span) + .isNotNull() + .hasTraceIdEqualTo(5L) + .hasSpanIdEqualTo(4L) + .hasParentSpanIdEqualTo(3L) + .isExportable(); + } + @Test public void legacyHeadersTakePrecedenceOverB3WhenB3IsInvalid() { HeaderBasedMessagingExtractor extractor = new HeaderBasedMessagingExtractor(); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingInjectorTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingInjectorTests.java index 2f558e815..cdb85b3cf 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingInjectorTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingInjectorTests.java @@ -40,7 +40,7 @@ public class HeaderBasedMessagingInjectorTests { injector.inject(span, map); then(map) - .contains(new AbstractMap.SimpleEntry(TraceMessageHeaders.B3_NAME, "0000000000000002-0000000000000001-1")) + .contains(new AbstractMap.SimpleEntry(TraceMessageHeaders.B3_NAME, "0000000000000002-0000000000000001-1-0000000000000003")) .contains(new AbstractMap.SimpleEntry(TraceMessageHeaders.SPAN_ID_NAME, Span.idToHex(10L))) .contains(new AbstractMap.SimpleEntry(TraceMessageHeaders.TRACE_ID_NAME, Span.idToHex(20L))) .contains(new AbstractMap.SimpleEntry(TraceMessageHeaders.PARENT_ID_NAME, Span.idToHex(30L))) diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/HttpServletRequestExtractorTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/HttpServletRequestExtractorTests.java index 56da3c529..7559c9e46 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/HttpServletRequestExtractorTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/HttpServletRequestExtractorTests.java @@ -82,6 +82,60 @@ public class HttpServletRequestExtractorTests { @Test public void should_pick_values_from_b3_if_present() { + BDDMockito.given(this.request.getHeaderNames()) + .willReturn(new Vector<>(Arrays.asList(Span.B3_NAME, Span.TRACE_ID_NAME, + Span.SPAN_ID_NAME, Span.PARENT_ID_NAME, Span.SAMPLED_NAME, + Span.SPAN_FLAGS)).elements()); + BDDMockito.given(this.request.getHeader(Span.TRACE_ID_NAME)) + .willReturn(Span.idToHex(10L)); + BDDMockito.given(this.request.getHeader(Span.SPAN_ID_NAME)) + .willReturn(Span.idToHex(20L)); + BDDMockito.given(this.request.getHeader(Span.PARENT_ID_NAME)) + .willReturn(Span.idToHex(30L)); + BDDMockito.given(this.request.getHeader(Span.SAMPLED_NAME)) + .willReturn(Span.SPAN_SAMPLED); + BDDMockito.given(this.request.getHeader(Span.B3_NAME)) + .willReturn("0000000000000005-0000000000000004"); + + Span span = this.extractor.joinTrace(new HttpServletRequestTextMap(this.request)); + + then(span) + .isNotNull() + .hasTraceIdEqualTo(5L) + .hasSpanIdEqualTo(4L) + .hasParentSpanIdEqualTo(30L) + .isExportable(); + } + + @Test + public void should_pick_values_from_b3_with_debug_flag_if_present() { + BDDMockito.given(this.request.getHeaderNames()) + .willReturn(new Vector<>(Arrays.asList(Span.B3_NAME, Span.TRACE_ID_NAME, + Span.SPAN_ID_NAME, Span.PARENT_ID_NAME, Span.SAMPLED_NAME, + Span.SPAN_FLAGS)).elements()); + BDDMockito.given(this.request.getHeader(Span.TRACE_ID_NAME)) + .willReturn(Span.idToHex(10L)); + BDDMockito.given(this.request.getHeader(Span.SPAN_ID_NAME)) + .willReturn(Span.idToHex(20L)); + BDDMockito.given(this.request.getHeader(Span.PARENT_ID_NAME)) + .willReturn(Span.idToHex(30L)); + BDDMockito.given(this.request.getHeader(Span.SAMPLED_NAME)) + .willReturn(Span.SPAN_NOT_SAMPLED); + BDDMockito.given(this.request.getHeader(Span.B3_NAME)) + .willReturn("0000000000000005-0000000000000004-d"); + + Span span = this.extractor.joinTrace(new HttpServletRequestTextMap(this.request)); + + then(span) + .isNotNull() + .hasTraceIdEqualTo(5L) + .hasSpanIdEqualTo(4L) + .hasParentSpanIdEqualTo(30L) + .isExportable(); + } + + @Test + public void should_pick_values_from_b3_with_sampled_flag_if_present() { BDDMockito.given(this.request.getHeaderNames()) .willReturn(new Vector<>(Arrays.asList(Span.B3_NAME, Span.TRACE_ID_NAME, Span.SPAN_ID_NAME, Span.PARENT_ID_NAME, Span.SAMPLED_NAME, @@ -103,6 +157,34 @@ public class HttpServletRequestExtractorTests { .isNotNull() .hasTraceIdEqualTo(5L) .hasSpanIdEqualTo(4L) + .hasParentSpanIdEqualTo(30L) + .isExportable(); + } + + @Test + public void should_pick_values_from_b3_with_parent_id_if_present() { + BDDMockito.given(this.request.getHeaderNames()) + .willReturn(new Vector<>(Arrays.asList(Span.B3_NAME, Span.TRACE_ID_NAME, + Span.SPAN_ID_NAME, Span.PARENT_ID_NAME, Span.SAMPLED_NAME, + Span.SPAN_FLAGS)).elements()); + BDDMockito.given(this.request.getHeader(Span.TRACE_ID_NAME)) + .willReturn(Span.idToHex(10L)); + BDDMockito.given(this.request.getHeader(Span.SPAN_ID_NAME)) + .willReturn(Span.idToHex(20L)); + BDDMockito.given(this.request.getHeader(Span.PARENT_ID_NAME)) + .willReturn(Span.idToHex(30L)); + BDDMockito.given(this.request.getHeader(Span.SAMPLED_NAME)) + .willReturn(Span.SPAN_NOT_SAMPLED); + BDDMockito.given(this.request.getHeader(Span.B3_NAME)) + .willReturn("0000000000000005-0000000000000004-1-0000000000000006"); + + Span span = this.extractor.joinTrace(new HttpServletRequestTextMap(this.request)); + + then(span) + .isNotNull() + .hasTraceIdEqualTo(5L) + .hasSpanIdEqualTo(4L) + .hasParentSpanIdEqualTo(6L) .isExportable(); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanInjectorTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanInjectorTests.java index e8c83d777..43d7baec6 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanInjectorTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanInjectorTests.java @@ -39,7 +39,7 @@ public class ZipkinHttpSpanInjectorTests { then(map) .contains(new AbstractMap.SimpleEntry(Span.B3_NAME, - "0000000000000002-0000000000000001-1")) + "0000000000000002-0000000000000001-1-0000000000000003")) .contains(new AbstractMap.SimpleEntry(Span.SPAN_ID_NAME, Span.idToHex(10L))) .contains(new AbstractMap.SimpleEntry(Span.TRACE_ID_NAME, Span.idToHex(20L))) .contains(new AbstractMap.SimpleEntry(Span.PARENT_ID_NAME, Span.idToHex(30L)))