Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add composite propagator #821

Merged
Prev Previous commit
Next Next commit
chore: lint
  • Loading branch information
dyladan committed Feb 28, 2020
commit b4c5e4a06d016227b8a2a244290a86d09ede5252
39 changes: 18 additions & 21 deletions packages/opentelemetry-core/src/context/propagation/composite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,25 @@ import { Carrier, Context, HttpTextFormat } from '@opentelemetry/api';

/** Combines multiple propagators into a single propagator. */
export class CompositePropagator implements HttpTextFormat {
private _propagators: HttpTextFormat[];
constructor(...propagators: HttpTextFormat[]) {
this._propagators = propagators;
}
private _propagators: HttpTextFormat[];
constructor(...propagators: HttpTextFormat[]) {
this._propagators = propagators;
}

inject(context: Context, carrier: Carrier) {
for (const propagator of this._propagators) {
try {
propagator.inject(context, carrier);
} catch { }
}
inject(context: Context, carrier: Carrier) {
for (const propagator of this._propagators) {
try {
propagator.inject(context, carrier);
} catch {}
}
}

extract(context: Context, carrier: Carrier): Context {
return this._propagators.reduce(
(ctx, propagator) => {
try {
return propagator.extract(ctx, carrier)
} catch { }
return ctx;
},
context
);
}
extract(context: Context, carrier: Carrier): Context {
return this._propagators.reduce((ctx, propagator) => {
try {
return propagator.extract(ctx, carrier);
} catch {}
return ctx;
}, context);
}
}
221 changes: 131 additions & 90 deletions packages/opentelemetry-core/test/context/composite.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,108 +16,149 @@

import { Context } from '@opentelemetry/scope-base';
import * as assert from 'assert';
import { CompositePropagator, HttpTraceContext, randomSpanId, randomTraceId } from '../../src';
import { setExtractedSpanContext, getExtractedSpanContext } from '../../src/context/context';
import { B3Format, X_B3_SPAN_ID, X_B3_TRACE_ID, X_B3_SAMPLED } from '../../src/context/propagation/B3Format';
import { TRACE_PARENT_HEADER, TRACE_STATE_HEADER } from '../../src/context/propagation/HttpTraceContext';
import {
CompositePropagator,
HttpTraceContext,
randomSpanId,
randomTraceId,
} from '../../src';
import {
setExtractedSpanContext,
getExtractedSpanContext,
} from '../../src/context/context';
import {
B3Format,
X_B3_SPAN_ID,
X_B3_TRACE_ID,
X_B3_SAMPLED,
} from '../../src/context/propagation/B3Format';
import {
TRACE_PARENT_HEADER,
TRACE_STATE_HEADER,
} from '../../src/context/propagation/HttpTraceContext';
import { TraceState } from '../../src/trace/TraceState';
import { HttpTextFormat, SpanContext } from '@opentelemetry/api';

describe('Composite Propagator', () => {
let traceId: string;
let spanId: string;
let traceId: string;
let spanId: string;

beforeEach(() => {
traceId = randomTraceId();
spanId = randomSpanId();
});

describe('inject', () => {
let carrier: { [key: string]: unknown };
let spanContext: SpanContext;
let ctxWithSpanContext: Context;

beforeEach(() => {
traceId = randomTraceId();
spanId = randomSpanId();
carrier = {};
spanContext = {
spanId,
traceId,
traceFlags: 1,
traceState: new TraceState('foo=bar'),
};
ctxWithSpanContext = setExtractedSpanContext(
Context.ROOT_CONTEXT,
spanContext
);
});

it('should inject context using all configured propagators', () => {
const composite = new CompositePropagator(
new B3Format(),
new HttpTraceContext()
);
composite.inject(ctxWithSpanContext, carrier);

assert.strictEqual(carrier[X_B3_TRACE_ID], traceId);
assert.strictEqual(carrier[X_B3_SPAN_ID], spanId);
assert.strictEqual(carrier[X_B3_SAMPLED], 1);
assert.strictEqual(
carrier[TRACE_PARENT_HEADER],
`00-${traceId}-${spanId}-01`
);
assert.strictEqual(carrier[TRACE_STATE_HEADER], 'foo=bar');
});

describe('inject', () => {
let carrier: { [key: string]: unknown };
let spanContext: SpanContext;
let ctxWithSpanContext: Context;

beforeEach(() => {
carrier = {};
spanContext = {
spanId,
traceId,
traceFlags: 1,
traceState: new TraceState("foo=bar")
};
ctxWithSpanContext = setExtractedSpanContext(Context.ROOT_CONTEXT, spanContext)
});

it('should inject context using all configured propagators', () => {
const composite = new CompositePropagator(new B3Format(), new HttpTraceContext());
composite.inject(ctxWithSpanContext, carrier);

assert.strictEqual(carrier[X_B3_TRACE_ID], traceId)
assert.strictEqual(carrier[X_B3_SPAN_ID], spanId)
assert.strictEqual(carrier[X_B3_SAMPLED], 1)
assert.strictEqual(carrier[TRACE_PARENT_HEADER], `00-${traceId}-${spanId}-01`)
assert.strictEqual(carrier[TRACE_STATE_HEADER], 'foo=bar')
});

it('should not throw', () => {
const composite = new CompositePropagator(new ThrowingPropagator(), new HttpTraceContext());
composite.inject(ctxWithSpanContext, carrier);

assert.strictEqual(carrier[TRACE_PARENT_HEADER], `00-${traceId}-${spanId}-01`);
});
it('should not throw', () => {
const composite = new CompositePropagator(
new ThrowingPropagator(),
new HttpTraceContext()
);
composite.inject(ctxWithSpanContext, carrier);

assert.strictEqual(
carrier[TRACE_PARENT_HEADER],
`00-${traceId}-${spanId}-01`
);
});
});

describe('extract', () => {
let carrier: { [key: string]: unknown };

describe('extract', () => {
let carrier: { [key: string]: unknown };

beforeEach(() => {
carrier = {
[X_B3_TRACE_ID]: traceId,
[X_B3_SPAN_ID]: spanId,
[X_B3_SAMPLED]: 1,
[TRACE_PARENT_HEADER]: `00-${traceId}-${spanId}-01`,
[TRACE_STATE_HEADER]: 'foo=bar',
}
});

it('should extract context using all configured propagators', () => {
const composite = new CompositePropagator(new B3Format(), new HttpTraceContext());
const spanContext = getExtractedSpanContext(composite.extract(Context.ROOT_CONTEXT, carrier));

if (!spanContext) {
throw new Error("no extracted context");
}

assert.strictEqual(spanContext.traceId, traceId)
assert.strictEqual(spanContext.spanId, spanId)
assert.strictEqual(spanContext.traceFlags, 1)
assert.strictEqual(spanContext.isRemote, true)
assert.strictEqual(spanContext.traceState!.get("foo"), "bar")
});

it('should not throw', () => {
const composite = new CompositePropagator(new ThrowingPropagator(), new HttpTraceContext());
const spanContext = getExtractedSpanContext(composite.extract(Context.ROOT_CONTEXT, carrier));

if (!spanContext) {
throw new Error("no extracted context");
}

assert.strictEqual(spanContext.traceId, traceId)
assert.strictEqual(spanContext.spanId, spanId)
assert.strictEqual(spanContext.traceFlags, 1)
assert.strictEqual(spanContext.isRemote, true)
assert.strictEqual(spanContext.traceState!.get("foo"), "bar")
});
})
beforeEach(() => {
carrier = {
[X_B3_TRACE_ID]: traceId,
[X_B3_SPAN_ID]: spanId,
[X_B3_SAMPLED]: 1,
[TRACE_PARENT_HEADER]: `00-${traceId}-${spanId}-01`,
[TRACE_STATE_HEADER]: 'foo=bar',
};
});

it('should extract context using all configured propagators', () => {
const composite = new CompositePropagator(
new B3Format(),
new HttpTraceContext()
);
const spanContext = getExtractedSpanContext(
composite.extract(Context.ROOT_CONTEXT, carrier)
);

if (!spanContext) {
throw new Error('no extracted context');
}

assert.strictEqual(spanContext.traceId, traceId);
assert.strictEqual(spanContext.spanId, spanId);
assert.strictEqual(spanContext.traceFlags, 1);
assert.strictEqual(spanContext.isRemote, true);
assert.strictEqual(spanContext.traceState!.get('foo'), 'bar');
});

it('should not throw', () => {
const composite = new CompositePropagator(
new ThrowingPropagator(),
new HttpTraceContext()
);
const spanContext = getExtractedSpanContext(
composite.extract(Context.ROOT_CONTEXT, carrier)
);

if (!spanContext) {
throw new Error('no extracted context');
}

assert.strictEqual(spanContext.traceId, traceId);
assert.strictEqual(spanContext.spanId, spanId);
assert.strictEqual(spanContext.traceFlags, 1);
assert.strictEqual(spanContext.isRemote, true);
assert.strictEqual(spanContext.traceState!.get('foo'), 'bar');
});
});
});

class ThrowingPropagator implements HttpTextFormat {
inject(context: Context, carrier: unknown) {
throw new Error('this propagator throws')
}
inject(context: Context, carrier: unknown) {
throw new Error('this propagator throws');
}

extract(context: Context, carrier: unknown): Context {
throw new Error('This propagator throws')
}
extract(context: Context, carrier: unknown): Context {
throw new Error('This propagator throws');
}
}