aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBogdan Drutu <bdrutu@google.com>2017-12-11 16:12:53 -0800
committerGitHub <noreply@github.com>2017-12-11 16:12:53 -0800
commit381e11c5168e5d31e09ec92840f908b42be647e6 (patch)
treeb4d69bd3f2e5b1c7ef903278be6960420d3aa737
parente2664fb9a41ea54394d4b111052ef2c3ab0d7b0c (diff)
downloadopencensus-java-381e11c5168e5d31e09ec92840f908b42be647e6.tar.gz
Add initial support for b3-propagation headers. (#889)
* Add initial support for b3-propagation headers. * Update tests and throw exception when missing span_id or trace_id. * Cleanup and add more tests. * Update comments. Update tests.
-rw-r--r--api/src/main/java/io/opencensus/trace/SpanId.java25
-rw-r--r--api/src/main/java/io/opencensus/trace/TraceId.java25
-rw-r--r--api/src/test/java/io/opencensus/trace/SpanIdTest.java18
-rw-r--r--api/src/test/java/io/opencensus/trace/TraceIdTest.java19
-rw-r--r--api/src/test/java/io/opencensus/trace/TracerTest.java3
-rw-r--r--impl_core/src/main/java/io/opencensus/implcore/trace/propagation/B3Format.java105
-rw-r--r--impl_core/src/main/java/io/opencensus/implcore/trace/propagation/BinaryFormatImpl.java2
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/trace/propagation/B3FormatTest.java221
8 files changed, 410 insertions, 8 deletions
diff --git a/api/src/main/java/io/opencensus/trace/SpanId.java b/api/src/main/java/io/opencensus/trace/SpanId.java
index 446a228e..a986d013 100644
--- a/api/src/main/java/io/opencensus/trace/SpanId.java
+++ b/api/src/main/java/io/opencensus/trace/SpanId.java
@@ -84,6 +84,22 @@ public final class SpanId implements Comparable<SpanId> {
}
/**
+ * Returns a {@code SpanId} built from a lowercase base16 representation.
+ *
+ * @param src the lowercase base16 representation.
+ * @return a {@code SpanId} built from a lowercase base16 representation.
+ * @throws NullPointerException if {@code src} is null.
+ * @throws IllegalArgumentException if {@code src.length} is not {@code 2 * SpanId.SIZE} OR if the
+ * {@code str} has invalid characters.
+ */
+ public static SpanId fromLowerBase16(String src) {
+ checkArgument(
+ src.length() == 2 * SIZE, "Invalid size: expected %s, got %s", 2 * SIZE, src.length());
+ byte[] bytes = BaseEncoding.base16().lowerCase().decode(src);
+ return new SpanId(bytes);
+ }
+
+ /**
* Generates a new random {@code SpanId}.
*
* @param random The random number generator.
@@ -136,6 +152,15 @@ public final class SpanId implements Comparable<SpanId> {
return !Arrays.equals(bytes, INVALID.bytes);
}
+ /**
+ * Returns the lowercase base16 encoding of this {@code SpanId}.
+ *
+ * @return the lowercase base16 encoding of this {@code SpanId}.
+ */
+ public String toLowerBase16() {
+ return BaseEncoding.base16().lowerCase().encode(bytes);
+ }
+
@Override
public boolean equals(Object obj) {
if (obj == this) {
diff --git a/api/src/main/java/io/opencensus/trace/TraceId.java b/api/src/main/java/io/opencensus/trace/TraceId.java
index 6badf6e5..44b80a66 100644
--- a/api/src/main/java/io/opencensus/trace/TraceId.java
+++ b/api/src/main/java/io/opencensus/trace/TraceId.java
@@ -85,6 +85,22 @@ public final class TraceId implements Comparable<TraceId> {
}
/**
+ * Returns a {@code TraceId} built from a lowercase base16 representation.
+ *
+ * @param src the lowercase base16 representation.
+ * @return a {@code TraceId} built from a lowercase base16 representation.
+ * @throws NullPointerException if {@code src} is null.
+ * @throws IllegalArgumentException if {@code src.length} is not {@code 2 * TraceId.SIZE} OR if
+ * the {@code str} has invalid characters.
+ */
+ public static TraceId fromLowerBase16(String src) {
+ checkArgument(
+ src.length() == 2 * SIZE, "Invalid size: expected %s, got %s", 2 * SIZE, src.length());
+ byte[] bytes = BaseEncoding.base16().lowerCase().decode(src);
+ return new TraceId(bytes);
+ }
+
+ /**
* Generates a new random {@code TraceId}.
*
* @param random the random number generator.
@@ -138,6 +154,15 @@ public final class TraceId implements Comparable<TraceId> {
}
/**
+ * Returns the lowercase base16 encoding of this {@code TraceId}.
+ *
+ * @return the lowercase base16 encoding of this {@code TraceId}.
+ */
+ public String toLowerBase16() {
+ return BaseEncoding.base16().lowerCase().encode(bytes);
+ }
+
+ /**
* Returns the lower 8 bytes of the trace-id as a long value, assuming little-endian order. This
* is used in ProbabilitySampler.
*
diff --git a/api/src/test/java/io/opencensus/trace/SpanIdTest.java b/api/src/test/java/io/opencensus/trace/SpanIdTest.java
index 36226da5..4a5bc2ae 100644
--- a/api/src/test/java/io/opencensus/trace/SpanIdTest.java
+++ b/api/src/test/java/io/opencensus/trace/SpanIdTest.java
@@ -28,7 +28,7 @@ import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class SpanIdTest {
private static final byte[] firstBytes = new byte[] {0, 0, 0, 0, 0, 0, 0, 'a'};
- private static final byte[] secondBytes = new byte[] {(byte) 0xFF, 0, 0, 0, 0, 0, 0, 0};
+ private static final byte[] secondBytes = new byte[] {(byte) 0xFF, 0, 0, 0, 0, 0, 0, 'A'};
private static final SpanId first = SpanId.fromBytes(firstBytes);
private static final SpanId second = SpanId.fromBytes(secondBytes);
@@ -45,6 +45,20 @@ public class SpanIdTest {
}
@Test
+ public void fromLowerBase16() {
+ assertThat(SpanId.fromLowerBase16("0000000000000000")).isEqualTo(SpanId.INVALID);
+ assertThat(SpanId.fromLowerBase16("0000000000000061")).isEqualTo(first);
+ assertThat(SpanId.fromLowerBase16("ff00000000000041")).isEqualTo(second);
+ }
+
+ @Test
+ public void toLowerBase16() {
+ assertThat(SpanId.INVALID.toLowerBase16()).isEqualTo("0000000000000000");
+ assertThat(first.toLowerBase16()).isEqualTo("0000000000000061");
+ assertThat(second.toLowerBase16()).isEqualTo("ff00000000000041");
+ }
+
+ @Test
public void getBytes() {
assertThat(first.getBytes()).isEqualTo(firstBytes);
assertThat(second.getBytes()).isEqualTo(secondBytes);
@@ -71,6 +85,6 @@ public class SpanIdTest {
public void traceId_ToString() {
assertThat(SpanId.INVALID.toString()).contains("0000000000000000");
assertThat(first.toString()).contains("0000000000000061");
- assertThat(second.toString()).contains("ff00000000000000");
+ assertThat(second.toString()).contains("ff00000000000041");
}
}
diff --git a/api/src/test/java/io/opencensus/trace/TraceIdTest.java b/api/src/test/java/io/opencensus/trace/TraceIdTest.java
index 8c1a1004..c8b5dc8f 100644
--- a/api/src/test/java/io/opencensus/trace/TraceIdTest.java
+++ b/api/src/test/java/io/opencensus/trace/TraceIdTest.java
@@ -30,7 +30,7 @@ public class TraceIdTest {
private static final byte[] firstBytes =
new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'a'};
private static final byte[] secondBytes =
- new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'A'};
+ new byte[] {(byte) 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'A'};
private static final TraceId first = TraceId.fromBytes(firstBytes);
private static final TraceId second = TraceId.fromBytes(secondBytes);
@@ -53,6 +53,21 @@ public class TraceIdTest {
}
@Test
+ public void fromLowerBase16() {
+ assertThat(TraceId.fromLowerBase16("00000000000000000000000000000000"))
+ .isEqualTo(TraceId.INVALID);
+ assertThat(TraceId.fromLowerBase16("00000000000000000000000000000061")).isEqualTo(first);
+ assertThat(TraceId.fromLowerBase16("ff000000000000000000000000000041")).isEqualTo(second);
+ }
+
+ @Test
+ public void toLowerBase16() {
+ assertThat(TraceId.INVALID.toLowerBase16()).isEqualTo("00000000000000000000000000000000");
+ assertThat(first.toLowerBase16()).isEqualTo("00000000000000000000000000000061");
+ assertThat(second.toLowerBase16()).isEqualTo("ff000000000000000000000000000041");
+ }
+
+ @Test
public void traceId_CompareTo() {
assertThat(first.compareTo(second)).isGreaterThan(0);
assertThat(second.compareTo(first)).isLessThan(0);
@@ -73,6 +88,6 @@ public class TraceIdTest {
public void traceId_ToString() {
assertThat(TraceId.INVALID.toString()).contains("00000000000000000000000000000000");
assertThat(first.toString()).contains("00000000000000000000000000000061");
- assertThat(second.toString()).contains("00000000000000000000000000000041");
+ assertThat(second.toString()).contains("ff000000000000000000000000000041");
}
}
diff --git a/api/src/test/java/io/opencensus/trace/TracerTest.java b/api/src/test/java/io/opencensus/trace/TracerTest.java
index 7c65742e..8f0ac64f 100644
--- a/api/src/test/java/io/opencensus/trace/TracerTest.java
+++ b/api/src/test/java/io/opencensus/trace/TracerTest.java
@@ -23,9 +23,7 @@ import static org.mockito.Mockito.when;
import io.grpc.Context;
import io.opencensus.common.Scope;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
@@ -38,7 +36,6 @@ import org.mockito.MockitoAnnotations;
public class TracerTest {
private static final Tracer noopTracer = Tracer.getNoopTracer();
private static final String SPAN_NAME = "MySpanName";
- @Rule public ExpectedException thrown = ExpectedException.none();
@Mock private Tracer tracer;
@Mock private SpanBuilder spanBuilder;
@Mock private Span span;
diff --git a/impl_core/src/main/java/io/opencensus/implcore/trace/propagation/B3Format.java b/impl_core/src/main/java/io/opencensus/implcore/trace/propagation/B3Format.java
new file mode 100644
index 00000000..c27234db
--- /dev/null
+++ b/impl_core/src/main/java/io/opencensus/implcore/trace/propagation/B3Format.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2017, OpenCensus 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 io.opencensus.implcore.trace.propagation;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.annotations.VisibleForTesting;
+import io.opencensus.trace.SpanContext;
+import io.opencensus.trace.SpanId;
+import io.opencensus.trace.TraceId;
+import io.opencensus.trace.TraceOptions;
+import io.opencensus.trace.propagation.SpanContextParseException;
+import io.opencensus.trace.propagation.TextFormat;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Implementation of the B3 propagation protocol. See <a
+ * href=https://github.com/openzipkin/b3-propagation>b3-propagation</a>.
+ */
+final class B3Format extends TextFormat {
+ @VisibleForTesting static final String X_B3_TRACE_ID = "X─B3─TraceId";
+ @VisibleForTesting static final String X_B3_SPAN_ID = "X─B3─SpanId";
+ @VisibleForTesting static final String X_B3_PARENT_SPAN_ID = "X─B3─ParentSpanId";
+ @VisibleForTesting static final String X_B3_SAMPLED = "X─B3─Sampled";
+ @VisibleForTesting static final String X_B3_FLAGS = "X-B3-Flags";
+ private static final List<String> FIELDS =
+ Collections.unmodifiableList(
+ Arrays.asList(
+ X_B3_TRACE_ID, X_B3_SPAN_ID, X_B3_PARENT_SPAN_ID, X_B3_SAMPLED, X_B3_FLAGS));
+
+ // Used as the upper TraceId.SIZE hex characters of the traceID. B3-propagation used to send
+ // TraceId.SIZE hex characters (8-bytes traceId) in the past.
+ private static final String UPPER_TRACE_ID = "0000000000000000";
+ // Sampled value via the X_B3_SAMPLED header.
+ private static final String SAMPLED_VALUE = "1";
+ // "Debug" sampled value.
+ private static final String FLAGS_VALUE = "1";
+
+ @Override
+ public List<String> fields() {
+ return FIELDS;
+ }
+
+ @Override
+ public <C> void inject(SpanContext spanContext, C carrier, Setter<C> setter) {
+ checkNotNull(spanContext, "spanContext");
+ checkNotNull(setter, "setter");
+ checkNotNull(carrier, "carrier");
+ setter.put(carrier, X_B3_TRACE_ID, spanContext.getTraceId().toLowerBase16());
+ setter.put(carrier, X_B3_SPAN_ID, spanContext.getSpanId().toLowerBase16());
+ if (spanContext.getTraceOptions().isSampled()) {
+ setter.put(carrier, X_B3_SAMPLED, SAMPLED_VALUE);
+ }
+ }
+
+ @Override
+ public <C> SpanContext extract(C carrier, Getter<C> getter) throws SpanContextParseException {
+ checkNotNull(carrier, "carrier");
+ checkNotNull(getter, "getter");
+ try {
+ TraceId traceId;
+ String traceIdStr = getter.get(carrier, X_B3_TRACE_ID);
+ if (traceIdStr != null) {
+ if (traceIdStr.length() == TraceId.SIZE) {
+ // This is an 8-byte traceID.
+ traceIdStr = UPPER_TRACE_ID + traceIdStr;
+ }
+ traceId = TraceId.fromLowerBase16(traceIdStr);
+ } else {
+ throw new SpanContextParseException("Missing X_B3_TRACE_ID.");
+ }
+ SpanId spanId;
+ String spanIdStr = getter.get(carrier, X_B3_SPAN_ID);
+ if (spanIdStr != null) {
+ spanId = SpanId.fromLowerBase16(spanIdStr);
+ } else {
+ throw new SpanContextParseException("Missing X_B3_SPAN_ID.");
+ }
+ TraceOptions traceOptions = TraceOptions.DEFAULT;
+ if (SAMPLED_VALUE.equals(getter.get(carrier, X_B3_SAMPLED))
+ || FLAGS_VALUE.equals(getter.get(carrier, X_B3_FLAGS))) {
+ traceOptions = TraceOptions.builder().setIsSampled(true).build();
+ }
+ return SpanContext.create(traceId, spanId, traceOptions);
+ } catch (IllegalArgumentException e) {
+ throw new SpanContextParseException("Invalid input.", e);
+ }
+ }
+}
diff --git a/impl_core/src/main/java/io/opencensus/implcore/trace/propagation/BinaryFormatImpl.java b/impl_core/src/main/java/io/opencensus/implcore/trace/propagation/BinaryFormatImpl.java
index 6075c80b..8a4377d3 100644
--- a/impl_core/src/main/java/io/opencensus/implcore/trace/propagation/BinaryFormatImpl.java
+++ b/impl_core/src/main/java/io/opencensus/implcore/trace/propagation/BinaryFormatImpl.java
@@ -59,7 +59,7 @@ import io.opencensus.trace.propagation.SpanContextParseException;
* </ul>
* </ul>
*/
-public final class BinaryFormatImpl extends BinaryFormat {
+final class BinaryFormatImpl extends BinaryFormat {
private static final byte VERSION_ID = 0;
private static final int VERSION_ID_OFFSET = 0;
// The version_id/field_id size in bytes.
diff --git a/impl_core/src/test/java/io/opencensus/implcore/trace/propagation/B3FormatTest.java b/impl_core/src/test/java/io/opencensus/implcore/trace/propagation/B3FormatTest.java
new file mode 100644
index 00000000..45514d06
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/trace/propagation/B3FormatTest.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2017, OpenCensus 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 io.opencensus.implcore.trace.propagation;
+
+import static com.google.common.truth.Truth.assertThat;
+import static io.opencensus.implcore.trace.propagation.B3Format.X_B3_FLAGS;
+import static io.opencensus.implcore.trace.propagation.B3Format.X_B3_PARENT_SPAN_ID;
+import static io.opencensus.implcore.trace.propagation.B3Format.X_B3_SAMPLED;
+import static io.opencensus.implcore.trace.propagation.B3Format.X_B3_SPAN_ID;
+import static io.opencensus.implcore.trace.propagation.B3Format.X_B3_TRACE_ID;
+
+import io.opencensus.trace.SpanContext;
+import io.opencensus.trace.SpanId;
+import io.opencensus.trace.TraceId;
+import io.opencensus.trace.TraceOptions;
+import io.opencensus.trace.propagation.SpanContextParseException;
+import io.opencensus.trace.propagation.TextFormat.Getter;
+import io.opencensus.trace.propagation.TextFormat.Setter;
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link B3Format}. */
+@RunWith(JUnit4.class)
+public class B3FormatTest {
+ private static final String TRACE_ID_BASE16 = "ff000000000000000000000000000041";
+ private static final TraceId TRACE_ID = TraceId.fromLowerBase16(TRACE_ID_BASE16);
+ private static final String TRACE_ID_BASE16_EIGHT_BYTES = "0000000000000041";
+ private static final TraceId TRACE_ID_EIGHT_BYTES =
+ TraceId.fromLowerBase16("0000000000000000" + TRACE_ID_BASE16_EIGHT_BYTES);
+ private static final String SPAN_ID_BASE16 = "ff00000000000041";
+ private static final SpanId SPAN_ID = SpanId.fromLowerBase16(SPAN_ID_BASE16);
+ private static final byte[] TRACE_OPTIONS_BYTES = new byte[] {1};
+ private static final TraceOptions TRACE_OPTIONS = TraceOptions.fromBytes(TRACE_OPTIONS_BYTES);
+ private final B3Format b3Format = new B3Format();
+ @Rule public ExpectedException thrown = ExpectedException.none();
+ private final Setter<Map<String, String>> setter =
+ new Setter<Map<String, String>>() {
+ @Override
+ public void put(Map<String, String> carrier, String key, String value) {
+ carrier.put(key, value);
+ }
+ };
+ private final Getter<Map<String, String>> getter =
+ new Getter<Map<String, String>>() {
+ @Nullable
+ @Override
+ public String get(Map<String, String> carrier, String key) {
+ return carrier.get(key);
+ }
+ };
+
+ @Test
+ public void serialize_SampledContext() {
+ Map<String, String> carrier = new HashMap<String, String>();
+ b3Format.inject(SpanContext.create(TRACE_ID, SPAN_ID, TRACE_OPTIONS), carrier, setter);
+ assertThat(carrier)
+ .containsExactly(
+ X_B3_TRACE_ID, TRACE_ID_BASE16, X_B3_SPAN_ID, SPAN_ID_BASE16, X_B3_SAMPLED, "1");
+ }
+
+ @Test
+ public void serialize_NotSampledContext() {
+ Map<String, String> carrier = new HashMap<String, String>();
+ b3Format.inject(SpanContext.create(TRACE_ID, SPAN_ID, TraceOptions.DEFAULT), carrier, setter);
+ assertThat(carrier)
+ .containsExactly(X_B3_TRACE_ID, TRACE_ID_BASE16, X_B3_SPAN_ID, SPAN_ID_BASE16);
+ }
+
+ @Test
+ public void parseMissingSampledAndMissingFlag() throws SpanContextParseException {
+ Map<String, String> headersNotSampled = new HashMap<String, String>();
+ headersNotSampled.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
+ headersNotSampled.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
+ SpanContext spanContext = SpanContext.create(TRACE_ID, SPAN_ID, TraceOptions.DEFAULT);
+ assertThat(b3Format.extract(headersNotSampled, getter)).isEqualTo(spanContext);
+ }
+
+ @Test
+ public void parseSampled() throws SpanContextParseException {
+ Map<String, String> headersSampled = new HashMap<String, String>();
+ headersSampled.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
+ headersSampled.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
+ headersSampled.put(X_B3_SAMPLED, "1");
+ assertThat(b3Format.extract(headersSampled, getter))
+ .isEqualTo(SpanContext.create(TRACE_ID, SPAN_ID, TRACE_OPTIONS));
+ }
+
+ @Test
+ public void parseZeroSampled() throws SpanContextParseException {
+ Map<String, String> headersNotSampled = new HashMap<String, String>();
+ headersNotSampled.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
+ headersNotSampled.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
+ headersNotSampled.put(X_B3_SAMPLED, "0");
+ assertThat(b3Format.extract(headersNotSampled, getter))
+ .isEqualTo(SpanContext.create(TRACE_ID, SPAN_ID, TraceOptions.DEFAULT));
+ }
+
+ @Test
+ public void parseFlag() throws SpanContextParseException {
+ Map<String, String> headersFlagSampled = new HashMap<String, String>();
+ headersFlagSampled.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
+ headersFlagSampled.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
+ headersFlagSampled.put(X_B3_FLAGS, "1");
+ assertThat(b3Format.extract(headersFlagSampled, getter))
+ .isEqualTo(SpanContext.create(TRACE_ID, SPAN_ID, TRACE_OPTIONS));
+ }
+
+ @Test
+ public void parseZeroFlag() throws SpanContextParseException {
+ Map<String, String> headersFlagNotSampled = new HashMap<String, String>();
+ headersFlagNotSampled.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
+ headersFlagNotSampled.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
+ headersFlagNotSampled.put(X_B3_FLAGS, "0");
+ assertThat(b3Format.extract(headersFlagNotSampled, getter))
+ .isEqualTo(SpanContext.create(TRACE_ID, SPAN_ID, TraceOptions.DEFAULT));
+ }
+
+ @Test
+ public void parseEightBytesTraceId() throws SpanContextParseException {
+ Map<String, String> headersEightBytes = new HashMap<String, String>();
+ headersEightBytes.put(X_B3_TRACE_ID, TRACE_ID_BASE16_EIGHT_BYTES);
+ headersEightBytes.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
+ headersEightBytes.put(X_B3_SAMPLED, "1");
+ assertThat(b3Format.extract(headersEightBytes, getter))
+ .isEqualTo(SpanContext.create(TRACE_ID_EIGHT_BYTES, SPAN_ID, TRACE_OPTIONS));
+ }
+
+ @Test
+ public void parseEightBytesTraceId_NotSampledSpanContext() throws SpanContextParseException {
+ Map<String, String> headersEightBytes = new HashMap<String, String>();
+ headersEightBytes.put(X_B3_TRACE_ID, TRACE_ID_BASE16_EIGHT_BYTES);
+ headersEightBytes.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
+ assertThat(b3Format.extract(headersEightBytes, getter))
+ .isEqualTo(SpanContext.create(TRACE_ID_EIGHT_BYTES, SPAN_ID, TraceOptions.DEFAULT));
+ }
+
+ @Test
+ public void parseInvalidTraceId() throws SpanContextParseException {
+ Map<String, String> invalidHeaders = new HashMap<String, String>();
+ invalidHeaders.put(X_B3_TRACE_ID, "abcdefghijklmnop");
+ invalidHeaders.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
+ thrown.expect(SpanContextParseException.class);
+ thrown.expectMessage("Invalid input.");
+ b3Format.extract(invalidHeaders, getter);
+ }
+
+ @Test
+ public void parseInvalidTraceId_Size() throws SpanContextParseException {
+ Map<String, String> invalidHeaders = new HashMap<String, String>();
+ invalidHeaders.put(X_B3_TRACE_ID, "0123456789abcdef00");
+ invalidHeaders.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
+ thrown.expect(SpanContextParseException.class);
+ thrown.expectMessage("Invalid input.");
+ b3Format.extract(invalidHeaders, getter);
+ }
+
+ @Test
+ public void parseMissingTraceId() throws SpanContextParseException {
+ Map<String, String> invalidHeaders = new HashMap<String, String>();
+ invalidHeaders.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
+ thrown.expect(SpanContextParseException.class);
+ thrown.expectMessage("Missing X_B3_TRACE_ID.");
+ b3Format.extract(invalidHeaders, getter);
+ }
+
+ @Test
+ public void parseInvalidSpanId() throws SpanContextParseException {
+ Map<String, String> invalidHeaders = new HashMap<String, String>();
+ invalidHeaders.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
+ invalidHeaders.put(X_B3_SPAN_ID, "abcdefghijklmnop");
+ thrown.expect(SpanContextParseException.class);
+ thrown.expectMessage("Invalid input.");
+ b3Format.extract(invalidHeaders, getter);
+ }
+
+ @Test
+ public void parseInvalidSpanId_Size() throws SpanContextParseException {
+ Map<String, String> invalidHeaders = new HashMap<String, String>();
+ invalidHeaders.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
+ invalidHeaders.put(X_B3_SPAN_ID, "0123456789abcdef00");
+ thrown.expect(SpanContextParseException.class);
+ thrown.expectMessage("Invalid input.");
+ b3Format.extract(invalidHeaders, getter);
+ }
+
+ @Test
+ public void parseMissingSpanId() throws SpanContextParseException {
+ Map<String, String> invalidHeaders = new HashMap<String, String>();
+ invalidHeaders.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
+ thrown.expect(SpanContextParseException.class);
+ thrown.expectMessage("Missing X_B3_SPAN_ID.");
+ b3Format.extract(invalidHeaders, getter);
+ }
+
+ @Test
+ public void fields_list() {
+ assertThat(b3Format.fields())
+ .containsExactly(
+ X_B3_TRACE_ID, X_B3_SPAN_ID, X_B3_PARENT_SPAN_ID, X_B3_SAMPLED, X_B3_FLAGS);
+ }
+}