1911 b3 propagator debug flag (#2038)

CHANGELOG: Added support for propagating the B3 debug flag

* b3 propagator supports inject/extract of debug flag

* rename context key

* Update extensions/trace-propagators/src/main/java/io/opentelemetry/extension/trace/propagation/B3Propagator.java

Co-authored-by: Anuraag Agrawal <anuraaga@gmail.com>

* changes as per code review comments

* added constants for b3propagator debug propagation

* tidy up

* changed b3 propagator to store a boolean in context

Co-authored-by: Anuraag Agrawal <anuraaga@gmail.com>
This commit is contained in:
jarebudev 2020-11-12 16:15:59 +00:00 committed by GitHub
parent c177dacdbb
commit d4583db62f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 145 additions and 29 deletions

View File

@ -6,6 +6,7 @@
package io.opentelemetry.extension.trace.propagation;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.ContextKey;
import io.opentelemetry.context.propagation.TextMapPropagator;
import java.util.Arrays;
import java.util.Collections;
@ -49,12 +50,17 @@ public class B3Propagator implements TextMapPropagator {
static final String TRACE_ID_HEADER = "X-B3-TraceId";
static final String SPAN_ID_HEADER = "X-B3-SpanId";
static final String SAMPLED_HEADER = "X-B3-Sampled";
static final String DEBUG_HEADER = "X-B3-Flags";
static final String COMBINED_HEADER = "b3";
static final String COMBINED_HEADER_DELIMITER = "-";
static final ContextKey<Boolean> DEBUG_CONTEXT_KEY = ContextKey.named("b3-debug");
static final String MULTI_HEADER_DEBUG = "1";
static final String SINGLE_HEADER_DEBUG = "d";
static final char COMBINED_HEADER_DELIMITER_CHAR = '-';
static final char IS_SAMPLED = '1';
static final char NOT_SAMPLED = '0';
static final char DEBUG_SAMPLED = 'd';
private static final List<String> FIELDS =
Collections.unmodifiableList(

View File

@ -5,12 +5,12 @@
package io.opentelemetry.extension.trace.propagation;
import static io.opentelemetry.extension.trace.propagation.B3Propagator.DEBUG_HEADER;
import static io.opentelemetry.extension.trace.propagation.B3Propagator.SAMPLED_HEADER;
import static io.opentelemetry.extension.trace.propagation.B3Propagator.SPAN_ID_HEADER;
import static io.opentelemetry.extension.trace.propagation.B3Propagator.TRACE_ID_HEADER;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapPropagator;
import java.util.Objects;
@ -28,33 +28,37 @@ final class B3PropagatorExtractorMultipleHeaders implements B3PropagatorExtracto
Context context, C carrier, TextMapPropagator.Getter<C> getter) {
Objects.requireNonNull(carrier, "carrier");
Objects.requireNonNull(getter, "getter");
SpanContext spanContext = getSpanContextFromMultipleHeaders(carrier, getter);
if (!spanContext.isValid()) {
return Optional.empty();
}
return Optional.of(context.with(Span.wrap(spanContext)));
return extractSpanContextFromMultipleHeaders(context, carrier, getter);
}
private static <C> SpanContext getSpanContextFromMultipleHeaders(
C carrier, TextMapPropagator.Getter<C> getter) {
private static <C> Optional<Context> extractSpanContextFromMultipleHeaders(
Context context, C carrier, TextMapPropagator.Getter<C> getter) {
String traceId = getter.get(carrier, TRACE_ID_HEADER);
if (StringUtils.isNullOrEmpty(traceId)) {
return SpanContext.getInvalid();
return Optional.empty();
}
if (!Common.isTraceIdValid(traceId)) {
logger.fine(
"Invalid TraceId in B3 header: " + traceId + "'. Returning INVALID span context.");
return SpanContext.getInvalid();
return Optional.empty();
}
String spanId = getter.get(carrier, SPAN_ID_HEADER);
if (!Common.isSpanIdValid(spanId)) {
logger.fine("Invalid SpanId in B3 header: " + spanId + "'. Returning INVALID span context.");
return SpanContext.getInvalid();
return Optional.empty();
}
// if debug flag is set, then set sampled flag, and also set B3 debug to true in the context
// for onward use by B3 injector
if (B3Propagator.MULTI_HEADER_DEBUG.equals(getter.get(carrier, DEBUG_HEADER))) {
return Optional.of(
context
.with(B3Propagator.DEBUG_CONTEXT_KEY, true)
.with(Span.wrap(Common.buildSpanContext(traceId, spanId, Common.TRUE_INT))));
}
String sampled = getter.get(carrier, SAMPLED_HEADER);
return Common.buildSpanContext(traceId, spanId, sampled);
return Optional.of(context.with(Span.wrap(Common.buildSpanContext(traceId, spanId, sampled))));
}
}

View File

@ -9,7 +9,6 @@ import static io.opentelemetry.extension.trace.propagation.B3Propagator.COMBINED
import static io.opentelemetry.extension.trace.propagation.B3Propagator.COMBINED_HEADER_DELIMITER;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapPropagator;
import java.util.Objects;
@ -27,20 +26,15 @@ final class B3PropagatorExtractorSingleHeader implements B3PropagatorExtractor {
Context context, C carrier, TextMapPropagator.Getter<C> getter) {
Objects.requireNonNull(carrier, "carrier");
Objects.requireNonNull(getter, "getter");
SpanContext spanContext = getSpanContextFromSingleHeader(carrier, getter);
if (!spanContext.isValid()) {
return Optional.empty();
}
return Optional.of(context.with(Span.wrap(spanContext)));
return extractSpanContextFromSingleHeader(context, carrier, getter);
}
@SuppressWarnings("StringSplitter")
private static <C> SpanContext getSpanContextFromSingleHeader(
C carrier, TextMapPropagator.Getter<C> getter) {
private static <C> Optional<Context> extractSpanContextFromSingleHeader(
Context context, C carrier, TextMapPropagator.Getter<C> getter) {
String value = getter.get(carrier, COMBINED_HEADER);
if (StringUtils.isNullOrEmpty(value)) {
return SpanContext.getInvalid();
return Optional.empty();
}
// must have between 2 and 4 hyphen delimited parts:
@ -50,25 +44,34 @@ final class B3PropagatorExtractorSingleHeader implements B3PropagatorExtractor {
if (parts.length < 2 || parts.length > 4) {
logger.fine(
"Invalid combined header '" + COMBINED_HEADER + ". Returning INVALID span context.");
return SpanContext.getInvalid();
return Optional.empty();
}
String traceId = parts[0];
if (!Common.isTraceIdValid(traceId)) {
logger.fine(
"Invalid TraceId in B3 header: " + COMBINED_HEADER + ". Returning INVALID span context.");
return SpanContext.getInvalid();
return Optional.empty();
}
String spanId = parts[1];
if (!Common.isSpanIdValid(spanId)) {
logger.fine(
"Invalid SpanId in B3 header: " + COMBINED_HEADER + ". Returning INVALID span context.");
return SpanContext.getInvalid();
return Optional.empty();
}
String sampled = parts.length >= 3 ? parts[2] : null;
return Common.buildSpanContext(traceId, spanId, sampled);
// if sampled is marked as 'd'ebug, then set sampled flag, and also set B3 debug to true in
// the context for onward use by the B3 injector
if (B3Propagator.SINGLE_HEADER_DEBUG.equals(sampled)) {
return Optional.of(
context
.with(B3Propagator.DEBUG_CONTEXT_KEY, true)
.with(Span.wrap(Common.buildSpanContext(traceId, spanId, Common.TRUE_INT))));
}
return Optional.of(context.with(Span.wrap(Common.buildSpanContext(traceId, spanId, sampled))));
}
}

View File

@ -26,6 +26,11 @@ final class B3PropagatorInjectorMultipleHeaders implements B3PropagatorInjector
String sampled = spanContext.isSampled() ? Common.TRUE_INT : Common.FALSE_INT;
if (Boolean.TRUE.equals(context.get(B3Propagator.DEBUG_CONTEXT_KEY))) {
setter.set(carrier, B3Propagator.DEBUG_HEADER, Common.TRUE_INT);
sampled = Common.TRUE_INT;
}
setter.set(carrier, B3Propagator.TRACE_ID_HEADER, spanContext.getTraceIdAsHexString());
setter.set(carrier, B3Propagator.SPAN_ID_HEADER, spanContext.getSpanIdAsHexString());
setter.set(carrier, B3Propagator.SAMPLED_HEADER, sampled);

View File

@ -44,8 +44,12 @@ final class B3PropagatorInjectorSingleHeader implements B3PropagatorInjector {
System.arraycopy(spanId.toCharArray(), 0, chars, SPAN_ID_OFFSET, SpanId.getHexLength());
chars[SAMPLED_FLAG_OFFSET - 1] = B3Propagator.COMBINED_HEADER_DELIMITER_CHAR;
chars[SAMPLED_FLAG_OFFSET] =
spanContext.isSampled() ? B3Propagator.IS_SAMPLED : B3Propagator.NOT_SAMPLED;
if (Boolean.TRUE.equals(context.get(B3Propagator.DEBUG_CONTEXT_KEY))) {
chars[SAMPLED_FLAG_OFFSET] = B3Propagator.DEBUG_SAMPLED;
} else {
chars[SAMPLED_FLAG_OFFSET] =
spanContext.isSampled() ? B3Propagator.IS_SAMPLED : B3Propagator.NOT_SAMPLED;
}
setter.set(carrier, B3Propagator.COMBINED_HEADER, new String(chars));
}
}

View File

@ -5,7 +5,9 @@
package io.opentelemetry.extension.trace.propagation;
import static io.opentelemetry.extension.trace.propagation.B3Propagator.DEBUG_CONTEXT_KEY;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanContext;
@ -599,4 +601,96 @@ class B3PropagatorTest {
assertThat(getSpanContext(b3Propagator.extract(Context.current(), emptyHeaders, getter)))
.isEqualTo(SpanContext.getInvalid());
}
@Test
void extract_DebugContext_SingleHeader() {
Map<String, String> carrier = new LinkedHashMap<>();
carrier.put(B3Propagator.COMBINED_HEADER, TRACE_ID + "-" + SPAN_ID + "-" + "d");
Context context = b3Propagator.extract(Context.current(), carrier, getter);
assertThat(getSpanContext(context))
.isEqualTo(
SpanContext.createFromRemoteParent(
TRACE_ID, SPAN_ID, SAMPLED_TRACE_OPTIONS, TRACE_STATE_DEFAULT));
assertTrue(context.get(DEBUG_CONTEXT_KEY));
}
@Test
void extract_DebugContext_MultipleHeaders() {
Map<String, String> carrier = new LinkedHashMap<>();
carrier.put(B3Propagator.TRACE_ID_HEADER, TRACE_ID);
carrier.put(B3Propagator.SPAN_ID_HEADER, SPAN_ID);
carrier.put(B3Propagator.DEBUG_HEADER, Common.TRUE_INT);
Context context = b3Propagator.extract(Context.current(), carrier, getter);
assertThat(getSpanContext(context))
.isEqualTo(
SpanContext.createFromRemoteParent(
TRACE_ID, SPAN_ID, SAMPLED_TRACE_OPTIONS, TRACE_STATE_DEFAULT));
assertTrue(context.get(DEBUG_CONTEXT_KEY));
}
@Test
void extract_DebugContext_SampledFalseDebugTrue_MultipleHeaders() {
Map<String, String> carrier = new LinkedHashMap<>();
carrier.put(B3Propagator.TRACE_ID_HEADER, TRACE_ID);
carrier.put(B3Propagator.SPAN_ID_HEADER, SPAN_ID);
carrier.put(B3Propagator.SAMPLED_HEADER, Common.FALSE_INT);
carrier.put(B3Propagator.DEBUG_HEADER, Common.TRUE_INT);
Context context = b3Propagator.extract(Context.current(), carrier, getter);
assertThat(getSpanContext(context))
.isEqualTo(
SpanContext.createFromRemoteParent(
TRACE_ID, SPAN_ID, SAMPLED_TRACE_OPTIONS, TRACE_STATE_DEFAULT));
assertTrue(context.get(DEBUG_CONTEXT_KEY));
}
@Test
void extract_DebugContext_SampledTrueDebugTrue_MultipleHeaders() {
Map<String, String> carrier = new LinkedHashMap<>();
carrier.put(B3Propagator.TRACE_ID_HEADER, TRACE_ID);
carrier.put(B3Propagator.SPAN_ID_HEADER, SPAN_ID);
carrier.put(B3Propagator.SAMPLED_HEADER, Common.TRUE_INT);
carrier.put(B3Propagator.DEBUG_HEADER, Common.TRUE_INT);
Context context = b3Propagator.extract(Context.current(), carrier, getter);
assertThat(getSpanContext(context))
.isEqualTo(
SpanContext.createFromRemoteParent(
TRACE_ID, SPAN_ID, SAMPLED_TRACE_OPTIONS, TRACE_STATE_DEFAULT));
assertTrue(context.get(DEBUG_CONTEXT_KEY));
}
@Test
void inject_DebugContext_MultipleHeaders() {
Map<String, String> carrier = new LinkedHashMap<>();
Context context = Context.current().with(DEBUG_CONTEXT_KEY, true);
b3Propagator.inject(
withSpanContext(
SpanContext.create(TRACE_ID, SPAN_ID, SAMPLED_TRACE_OPTIONS, TRACE_STATE_DEFAULT),
context),
carrier,
setter);
assertThat(carrier).containsEntry(B3Propagator.TRACE_ID_HEADER, TRACE_ID);
assertThat(carrier).containsEntry(B3Propagator.SPAN_ID_HEADER, SPAN_ID);
assertThat(carrier).containsEntry(B3Propagator.SAMPLED_HEADER, Common.TRUE_INT);
assertThat(carrier).containsEntry(B3Propagator.DEBUG_HEADER, Common.TRUE_INT);
}
@Test
void inject_DebugContext_SingleHeader() {
Map<String, String> carrier = new LinkedHashMap<>();
Context context = Context.current().with(DEBUG_CONTEXT_KEY, true);
b3PropagatorSingleHeader.inject(
withSpanContext(
SpanContext.create(TRACE_ID, SPAN_ID, SAMPLED_TRACE_OPTIONS, TRACE_STATE_DEFAULT),
context),
carrier,
setter);
assertThat(carrier)
.containsEntry(
B3Propagator.COMBINED_HEADER,
TRACE_ID + "-" + SPAN_ID + "-" + B3Propagator.SINGLE_HEADER_DEBUG);
}
}