Skip to content

Commit

Permalink
1911 b3 propagator debug flag (#2038)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
jarebudev and anuraaga authored Nov 12, 2020
1 parent c177dac commit d4583db
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -28,33 +28,37 @@ public <C> Optional<Context> extract(
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))));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
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;
Expand All @@ -27,20 +26,15 @@ public <C> Optional<Context> extract(
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:
Expand All @@ -50,25 +44,34 @@ private static <C> SpanContext getSpanContextFromSingleHeader(
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))));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ public <C> void inject(Context context, C carrier, TextMapPropagator.Setter<C> s

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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,12 @@ public <C> void inject(Context context, C carrier, TextMapPropagator.Setter<C> s
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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -599,4 +601,96 @@ void extract_emptyCarrier() {
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);
}
}

0 comments on commit d4583db

Please sign in to comment.