Skip to content

Commit

Permalink
Propagate b3 parentspanid and debug flag (#1346)
Browse files Browse the repository at this point in the history
  • Loading branch information
srjames90 authored Aug 4, 2020
1 parent 6eb51a2 commit 81d75d2
Show file tree
Hide file tree
Showing 2 changed files with 503 additions and 108 deletions.
134 changes: 108 additions & 26 deletions packages/opentelemetry-core/src/context/propagation/B3Propagator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,19 @@ import { getParentSpanContext, setExtractedSpanContext } from '../context';
export const X_B3_TRACE_ID = 'x-b3-traceid';
export const X_B3_SPAN_ID = 'x-b3-spanid';
export const X_B3_SAMPLED = 'x-b3-sampled';
export const X_B3_PARENT_SPAN_ID = 'x-b3-parentspanid';
export const X_B3_FLAGS = 'x-b3-flags';
export const PARENT_SPAN_ID_KEY = Context.createKey(
'OpenTelemetry Context Key B3 Parent Span Id'
);
export const DEBUG_FLAG_KEY = Context.createKey(
'OpenTelemetry Context Key B3 Debug Flag'
);
const VALID_TRACEID_REGEX = /^([0-9a-f]{16}){1,2}$/i;
const VALID_SPANID_REGEX = /^[0-9a-f]{16}$/i;
const INVALID_ID_REGEX = /^0+$/i;
const VALID_SAMPLED_VALUES = new Set([true, 'true', 'True', '1', 1]);
const VALID_UNSAMPLED_VALUES = new Set([false, 'false', 'False', '0', 0]);

function isValidTraceId(traceId: string): boolean {
return VALID_TRACEID_REGEX.test(traceId) && !INVALID_ID_REGEX.test(traceId);
Expand All @@ -38,6 +48,74 @@ function isValidSpanId(spanId: string): boolean {
return VALID_SPANID_REGEX.test(spanId) && !INVALID_ID_REGEX.test(spanId);
}

function isValidParentSpanID(spanId: string | undefined): boolean {
return spanId === undefined || isValidSpanId(spanId);
}

function isValidSampledValue(sampled: TraceFlags | undefined): boolean {
return sampled === TraceFlags.SAMPLED || sampled === TraceFlags.NONE;
}

function parseHeader(header: unknown) {
return Array.isArray(header) ? header[0] : header;
}

function getHeaderValue(carrier: unknown, getter: GetterFunction, key: string) {
const header = getter(carrier, key);
return parseHeader(header);
}

function getTraceId(carrier: unknown, getter: GetterFunction): string {
const traceId = getHeaderValue(carrier, getter, X_B3_TRACE_ID);
if (typeof traceId === 'string') {
return traceId.padStart(32, '0');
}
return '';
}

function getSpanId(carrier: unknown, getter: GetterFunction): string {
const spanId = getHeaderValue(carrier, getter, X_B3_SPAN_ID);
if (typeof spanId === 'string') {
return spanId;
}
return '';
}

function getParentSpanId(
carrier: unknown,
getter: GetterFunction
): string | undefined {
const spanId = getHeaderValue(carrier, getter, X_B3_PARENT_SPAN_ID);
if (typeof spanId === 'string') {
return spanId;
}
return;
}

function getDebug(
carrier: unknown,
getter: GetterFunction
): string | undefined {
const debug = getHeaderValue(carrier, getter, X_B3_FLAGS);
return debug === '1' ? '1' : undefined;
}

function getTraceFlags(
carrier: unknown,
getter: GetterFunction
): TraceFlags | undefined {
const traceFlags = getHeaderValue(carrier, getter, X_B3_SAMPLED);
const debug = getDebug(carrier, getter);
if (debug === '1' || VALID_SAMPLED_VALUES.has(traceFlags)) {
return TraceFlags.SAMPLED;
}
if (traceFlags === undefined || VALID_UNSAMPLED_VALUES.has(traceFlags)) {
return TraceFlags.NONE;
}
// This indicates to isValidSampledValue that this is not valid
return;
}

/**
* Propagator for the B3 HTTP header format.
* Based on: https://github.com/openzipkin/b3-propagation
Expand All @@ -46,17 +124,27 @@ export class B3Propagator implements HttpTextPropagator {
inject(context: Context, carrier: unknown, setter: SetterFunction) {
const spanContext = getParentSpanContext(context);
if (!spanContext) return;

const parentSpanId = context.getValue(PARENT_SPAN_ID_KEY) as
| undefined
| string;
if (
isValidTraceId(spanContext.traceId) &&
isValidSpanId(spanContext.spanId)
isValidSpanId(spanContext.spanId) &&
isValidParentSpanID(parentSpanId)
) {
const debug = context.getValue(DEBUG_FLAG_KEY);
setter(carrier, X_B3_TRACE_ID, spanContext.traceId);
setter(carrier, X_B3_SPAN_ID, spanContext.spanId);

// We set the header only if there is an existing sampling decision.
// Otherwise we will omit it => Absent.
if (spanContext.traceFlags !== undefined) {
if (parentSpanId) {
setter(carrier, X_B3_PARENT_SPAN_ID, parentSpanId);
}
// According to the B3 spec, if the debug flag is set,
// the sampled flag shouldn't be propagated as well.
if (debug === '1') {
setter(carrier, X_B3_FLAGS, debug);
} else if (spanContext.traceFlags !== undefined) {
// We set the header only if there is an existing sampling decision.
// Otherwise we will omit it => Absent.
setter(
carrier,
X_B3_SAMPLED,
Expand All @@ -69,31 +157,25 @@ export class B3Propagator implements HttpTextPropagator {
}

extract(context: Context, carrier: unknown, getter: GetterFunction): Context {
const traceIdHeader = getter(carrier, X_B3_TRACE_ID);
const spanIdHeader = getter(carrier, X_B3_SPAN_ID);
const sampledHeader = getter(carrier, X_B3_SAMPLED);

const traceIdHeaderValue = Array.isArray(traceIdHeader)
? traceIdHeader[0]
: traceIdHeader;
const spanId = Array.isArray(spanIdHeader) ? spanIdHeader[0] : spanIdHeader;

const options = Array.isArray(sampledHeader)
? sampledHeader[0]
: sampledHeader;

if (typeof traceIdHeaderValue !== 'string' || typeof spanId !== 'string') {
return context;
}

const traceId = traceIdHeaderValue.padStart(32, '0');
const traceId = getTraceId(carrier, getter);
const spanId = getSpanId(carrier, getter);
const parentSpanId = getParentSpanId(carrier, getter);
const traceFlags = getTraceFlags(carrier, getter) as TraceFlags;
const debug = getDebug(carrier, getter);

if (isValidTraceId(traceId) && isValidSpanId(spanId)) {
if (
isValidTraceId(traceId) &&
isValidSpanId(spanId) &&
isValidParentSpanID(parentSpanId) &&
isValidSampledValue(traceFlags)
) {
context = context.setValue(PARENT_SPAN_ID_KEY, parentSpanId);
context = context.setValue(DEBUG_FLAG_KEY, debug);
return setExtractedSpanContext(context, {
traceId,
spanId,
isRemote: true,
traceFlags: isNaN(Number(options)) ? TraceFlags.NONE : Number(options),
traceFlags,
});
}
return context;
Expand Down
Loading

0 comments on commit 81d75d2

Please sign in to comment.