Skip to content

Commit

Permalink
Add getter and setter arguments to propagation API (#827)
Browse files Browse the repository at this point in the history
* feat: add getter and setter arguments to propagation API

* chore: add getter and setter to composite propagator
  • Loading branch information
dyladan committed Mar 5, 2020
1 parent d16c691 commit 5c4c57e
Show file tree
Hide file tree
Showing 17 changed files with 259 additions and 132 deletions.
21 changes: 16 additions & 5 deletions packages/opentelemetry-api/src/api/propagation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
*/

import { Context } from '@opentelemetry/scope-base';
import { Carrier } from '../context/propagation/carrier';
import { defaultGetter, GetterFunction } from '../context/propagation/getter';
import { HttpTextFormat } from '../context/propagation/HttpTextFormat';
import { NOOP_HTTP_TEXT_FORMAT } from '../context/propagation/NoopHttpTextFormat';
import { defaultSetter, SetterFunction } from '../context/propagation/setter';
import { ContextAPI } from './context';

const contextApi = ContextAPI.getInstance();
Expand Down Expand Up @@ -53,19 +54,29 @@ export class PropagationAPI {
* Inject context into a carrier to be propagated inter-process
*
* @param carrier carrier to inject context into
* @param setter Function used to set values on the carrier
* @param context Context carrying tracing data to inject. Defaults to the currently active context.
*/
public inject(carrier: Carrier, context = contextApi.active()): void {
return this._propagator.inject(context, carrier);
public inject<Carrier>(
carrier: Carrier,
setter: SetterFunction<Carrier> = defaultSetter,
context = contextApi.active()
): void {
return this._propagator.inject(context, carrier, setter);
}

/**
* Extract context from a carrier
*
* @param carrier Carrier to extract context from
* @param getter Function used to extract keys from a carrier
* @param context Context which the newly created context will inherit from. Defaults to the currently active context.
*/
public extract(carrier: Carrier, context = contextApi.active()): Context {
return this._propagator.extract(context, carrier);
public extract<Carrier>(
carrier: Carrier,
getter: GetterFunction<Carrier> = defaultGetter,
context = contextApi.active()
): Context {
return this._propagator.extract(context, carrier, getter);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
*/

import { Context } from '@opentelemetry/scope-base';
import { Carrier } from './carrier';
import { SetterFunction } from './setter';
import { GetterFunction } from './getter';

/**
* Injects {@link Context} into and extracts it from carriers that travel
Expand All @@ -37,8 +38,10 @@ export interface HttpTextFormat {
* the wire.
* @param carrier the carrier of propagation fields, such as http request
* headers.
* @param setter a function which accepts a carrier, key, and value, which
* sets the key on the carrier to the value.
*/
inject(context: Context, carrier: Carrier): void;
inject(context: Context, carrier: unknown, setter: SetterFunction): void;

/**
* Given a {@link Context} and a carrier, extract context values from a
Expand All @@ -49,6 +52,8 @@ export interface HttpTextFormat {
* the wire.
* @param carrier the carrier of propagation fields, such as http request
* headers.
* @param getter a function which accepts a carrier and a key, and returns
* the value from the carrier identified by the key.
*/
extract(context: Context, carrier: Carrier): Context;
extract(context: Context, carrier: unknown, getter: GetterFunction): Context;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,16 @@
*/

import { Context } from '@opentelemetry/scope-base';
import { Carrier } from './carrier';
import { HttpTextFormat } from './HttpTextFormat';

/**
* No-op implementations of {@link HttpTextFormat}.
*/
export class NoopHttpTextFormat implements HttpTextFormat {
/** Noop inject function does nothing */
inject(context: Context, carrier: Carrier): void {}
inject(context: Context, carrier: unknown, setter: Function): void {}
/** Noop extract function does nothing and returns the input context */
extract(context: Context, carrier: Carrier): Context {
extract(context: Context, carrier: unknown, getter: Function): Context {
return context;
}
}
Expand Down
31 changes: 31 additions & 0 deletions packages/opentelemetry-api/src/context/propagation/getter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*!
* Copyright 2020, OpenTelemetry 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
*
* https://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.
*/

export type GetterFunction<Carrier = any> = (
carrier: Carrier,
key: string
) => unknown;

/**
* Default getter which just does a simple property access. Returns
* undefined if the key is not set.
*
* @param carrier
* @param key
*/
export function defaultGetter(carrier: any, key: string): unknown {
return carrier[key];
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@
* limitations under the License.
*/

export type Carrier = {
[key: string]: unknown;
};
export type SetterFunction<Carrier = any> = (
carrier: Carrier,
key: string,
value: unknown
) => void;

/**
* Default setter which sets value via direct property access
*
* @param carrier
* @param key
*/
export function defaultSetter(carrier: any, key: string, value: unknown) {
carrier[key] = value;
}
3 changes: 2 additions & 1 deletion packages/opentelemetry-api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@

export * from './common/Logger';
export * from './common/Time';
export * from './context/propagation/carrier';
export * from './context/propagation/getter';
export * from './context/propagation/HttpTextFormat';
export * from './context/propagation/setter';
export * from './correlation_context/CorrelationContext';
export * from './correlation_context/EntryValue';
export * from './metrics/BoundInstrument';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
* limitations under the License.
*/

import { Context } from '@opentelemetry/scope-base';
import * as assert from 'assert';
import { NoopTracer, NOOP_SPAN, SpanKind } from '../../src';
import { Context } from '@opentelemetry/scope-base';
import { defaultGetter } from '../../src/context/propagation/getter';
import { defaultSetter } from '../../src/context/propagation/setter';

describe('NoopTracer', () => {
it('should not crash', () => {
Expand All @@ -38,9 +40,9 @@ describe('NoopTracer', () => {
const httpTextFormat = tracer.getHttpTextFormat();
assert.ok(httpTextFormat);

httpTextFormat.inject(Context.ROOT_CONTEXT, {});
httpTextFormat.inject(Context.ROOT_CONTEXT, {}, defaultSetter);
assert.deepStrictEqual(
httpTextFormat.extract(Context.ROOT_CONTEXT, {}),
httpTextFormat.extract(Context.ROOT_CONTEXT, {}, defaultGetter),
Context.ROOT_CONTEXT
);
});
Expand Down
19 changes: 10 additions & 9 deletions packages/opentelemetry-core/src/context/propagation/B3Format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
*/

import {
Carrier,
Context,
GetterFunction,
HttpTextFormat,
SetterFunction,
TraceFlags,
} from '@opentelemetry/api';
import { getParentSpanContext, setExtractedSpanContext } from '../context';
Expand All @@ -42,29 +43,29 @@ function isValidSpanId(spanId: string): boolean {
* Based on: https://github.com/openzipkin/b3-propagation
*/
export class B3Format implements HttpTextFormat {
inject(context: Context, carrier: Carrier) {
inject(context: Context, carrier: unknown, setter: SetterFunction) {
const spanContext = getParentSpanContext(context);
if (!spanContext) return;

if (
isValidTraceId(spanContext.traceId) &&
isValidSpanId(spanContext.spanId)
) {
carrier[X_B3_TRACE_ID] = spanContext.traceId;
carrier[X_B3_SPAN_ID] = spanContext.spanId;
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) {
carrier[X_B3_SAMPLED] = Number(spanContext.traceFlags);
setter(carrier, X_B3_SAMPLED, Number(spanContext.traceFlags));
}
}
}

extract(context: Context, carrier: Carrier): Context {
const traceIdHeader = carrier[X_B3_TRACE_ID];
const spanIdHeader = carrier[X_B3_SPAN_ID];
const sampledHeader = carrier[X_B3_SAMPLED];
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);
if (!traceIdHeader || !spanIdHeader) return context;
const traceId = Array.isArray(traceIdHeader)
? traceIdHeader[0]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
*/

import {
Carrier,
Context,
GetterFunction,
HttpTextFormat,
SetterFunction,
SpanContext,
TraceFlags,
} from '@opentelemetry/api';
Expand Down Expand Up @@ -63,22 +64,22 @@ export function parseTraceParent(traceParent: string): SpanContext | null {
* https://www.w3.org/TR/trace-context/
*/
export class HttpTraceContext implements HttpTextFormat {
inject(context: Context, carrier: Carrier) {
inject(context: Context, carrier: unknown, setter: SetterFunction) {
const spanContext = getParentSpanContext(context);
if (!spanContext) return;

const traceParent = `${VERSION}-${spanContext.traceId}-${
spanContext.spanId
}-0${Number(spanContext.traceFlags || TraceFlags.UNSAMPLED).toString(16)}`;

carrier[TRACE_PARENT_HEADER] = traceParent;
setter(carrier, TRACE_PARENT_HEADER, traceParent);
if (spanContext.traceState) {
carrier[TRACE_STATE_HEADER] = spanContext.traceState.serialize();
setter(carrier, TRACE_STATE_HEADER, spanContext.traceState.serialize());
}
}

extract(context: Context, carrier: Carrier): Context {
const traceParentHeader = carrier[TRACE_PARENT_HEADER];
extract(context: Context, carrier: unknown, getter: GetterFunction): Context {
const traceParentHeader = getter(carrier, TRACE_PARENT_HEADER);
if (!traceParentHeader) return context;
const traceParent = Array.isArray(traceParentHeader)
? traceParentHeader[0]
Expand All @@ -88,7 +89,7 @@ export class HttpTraceContext implements HttpTextFormat {

spanContext.isRemote = true;

const traceStateHeader = carrier[TRACE_STATE_HEADER];
const traceStateHeader = getter(carrier, TRACE_STATE_HEADER);
if (traceStateHeader) {
// If more than one `tracestate` header is found, we merge them into a
// single header.
Expand Down
16 changes: 11 additions & 5 deletions packages/opentelemetry-core/src/context/propagation/composite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@
* limitations under the License.
*/

import { Carrier, Context, HttpTextFormat, Logger } from '@opentelemetry/api';
import {
Context,
GetterFunction,
HttpTextFormat,
Logger,
SetterFunction,
} from '@opentelemetry/api';
import { NoopLogger } from '../../common/NoopLogger';
import { CompositePropagatorConfig } from './types';

Expand Down Expand Up @@ -42,10 +48,10 @@ export class CompositePropagator implements HttpTextFormat {
* @param context Context to inject
* @param carrier Carrier into which context will be injected
*/
inject(context: Context, carrier: Carrier) {
inject(context: Context, carrier: unknown, setter: SetterFunction) {
for (const propagator of this._propagators) {
try {
propagator.inject(context, carrier);
propagator.inject(context, carrier, setter);
} catch (err) {
this._logger.warn(
`Failed to inject with ${propagator.constructor.name}. Err: ${err.message}`
Expand All @@ -63,10 +69,10 @@ export class CompositePropagator implements HttpTextFormat {
* @param context Context to add values to
* @param carrier Carrier from which to extract context
*/
extract(context: Context, carrier: Carrier): Context {
extract(context: Context, carrier: unknown, getter: GetterFunction): Context {
return this._propagators.reduce((ctx, propagator) => {
try {
return propagator.extract(ctx, carrier);
return propagator.extract(ctx, carrier, getter);
} catch (err) {
this._logger.warn(
`Failed to inject with ${propagator.constructor.name}. Err: ${err.message}`
Expand Down
Loading

0 comments on commit 5c4c57e

Please sign in to comment.