Skip to content

Commit

Permalink
feat(api): propagate spanContext only using API open-telemetry#1456 (o…
Browse files Browse the repository at this point in the history
…pen-telemetry#1527)

Co-authored-by: Bartlomiej Obecny <bobecny@gmail.com>
Co-authored-by: Daniel Dyla <dyladan@users.noreply.github.com>
  • Loading branch information
3 people committed Feb 18, 2021
1 parent f3edd8c commit 651f9f3
Show file tree
Hide file tree
Showing 5 changed files with 270 additions and 5 deletions.
123 changes: 123 additions & 0 deletions api/src/context/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Copyright The 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.
*/

import { Span, SpanContext } from '../';
import { Context, createContextKey } from '@opentelemetry/context-base';

/**
* Active span key
*/
const ACTIVE_SPAN_KEY = createContextKey(
'OpenTelemetry Context Key ACTIVE_SPAN'
);
const EXTRACTED_SPAN_CONTEXT_KEY = createContextKey(
'OpenTelemetry Context Key EXTRACTED_SPAN_CONTEXT'
);
/**
* Shared key for indicating if instrumentation should be suppressed beyond
* this current scope.
*/
const SUPPRESS_INSTRUMENTATION_KEY = createContextKey(
'OpenTelemetry Context Key SUPPRESS_INSTRUMENTATION'
);

/**
* Return the active span if one exists
*
* @param context context to get span from
*/
export function getActiveSpan(context: Context): Span | undefined {
return (context.getValue(ACTIVE_SPAN_KEY) as Span) || undefined;
}

/**
* Set the active span on a context
*
* @param context context to use as parent
* @param span span to set active
*/
export function setActiveSpan(context: Context, span: Span): Context {
return context.setValue(ACTIVE_SPAN_KEY, span);
}

/**
* Get the extracted span context from a context
*
* @param context context to get span context from
*/
export function getExtractedSpanContext(
context: Context
): SpanContext | undefined {
return (
(context.getValue(EXTRACTED_SPAN_CONTEXT_KEY) as SpanContext) || undefined
);
}

/**
* Set the extracted span context on a context
*
* @param context context to set span context on
* @param spanContext span context to set
*/
export function setExtractedSpanContext(
context: Context,
spanContext: SpanContext
): Context {
return context.setValue(EXTRACTED_SPAN_CONTEXT_KEY, spanContext);
}

/**
* Get the span context of the parent span if it exists,
* or the extracted span context if there is no active
* span.
*
* @param context context to get values from
*/
export function getParentSpanContext(
context: Context
): SpanContext | undefined {
return getActiveSpan(context)?.context() || getExtractedSpanContext(context);
}

/**
* Sets value on context to indicate that instrumentation should
* be suppressed beyond this current scope.
*
* @param context context to set the suppress instrumentation value on.
*/
export function suppressInstrumentation(context: Context): Context {
return context.setValue(SUPPRESS_INSTRUMENTATION_KEY, true);
}

/**
* Sets value on context to indicate that instrumentation should
* no-longer be suppressed beyond this current scope.
*
* @param context context to set the suppress instrumentation value on.
*/
export function unsuppressInstrumentation(context: Context): Context {
return context.setValue(SUPPRESS_INSTRUMENTATION_KEY, false);
}

/**
* Return current suppress instrumentation value for the given context,
* if it exists.
*
* @param context context check for the suppress instrumentation value.
*/
export function isInstrumentationSuppressed(context: Context): boolean {
return Boolean(context.getValue(SUPPRESS_INSTRUMENTATION_KEY));
}
1 change: 1 addition & 0 deletions api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
export * from './common/Exception';
export * from './common/Logger';
export * from './common/Time';
export * from './context/context';
export * from './context/propagation/getter';
export * from './context/propagation/TextMapPropagator';
export * from './context/propagation/NoopTextMapPropagator';
Expand Down
31 changes: 27 additions & 4 deletions api/src/trace/NoopTracer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@
* limitations under the License.
*/

import { Span, SpanOptions, Tracer } from '..';
import { NOOP_SPAN } from './NoopSpan';
import { Span, SpanOptions, Tracer, SpanContext } from '..';
import { Context } from '@opentelemetry/context-base';
import { NoopSpan, NOOP_SPAN } from './NoopSpan';
import { isSpanContextValid } from './spancontext-utils';
import { getExtractedSpanContext } from '../context/context';

/**
* No-op implementations of {@link Tracer}.
Expand All @@ -26,8 +29,19 @@ export class NoopTracer implements Tracer {
}

// startSpan starts a noop span.
startSpan(name: string, options?: SpanOptions): Span {
return NOOP_SPAN;
startSpan(name: string, options?: SpanOptions, context?: Context): Span {
const parent = options?.parent;
const parentFromContext = context && getExtractedSpanContext(context);
if (isSpanContext(parent) && isSpanContextValid(parent)) {
return new NoopSpan(parent);
} else if (
isSpanContext(parentFromContext) &&
isSpanContextValid(parentFromContext)
) {
return new NoopSpan(parentFromContext);
} else {
return NOOP_SPAN;
}
}

withSpan<T extends (...args: unknown[]) => ReturnType<T>>(
Expand All @@ -42,4 +56,13 @@ export class NoopTracer implements Tracer {
}
}

function isSpanContext(spanContext: any): spanContext is SpanContext {
return (
typeof spanContext === 'object' &&
typeof spanContext['spanId'] === 'string' &&
typeof spanContext['traceId'] === 'string' &&
typeof spanContext['traceFlags'] === 'number'
);
}

export const NOOP_TRACER = new NoopTracer();
80 changes: 80 additions & 0 deletions api/test/context/context.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright The 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.
*/

import * as assert from 'assert';
import { ROOT_CONTEXT, createContextKey } from '@opentelemetry/context-base';
import {
suppressInstrumentation,
unsuppressInstrumentation,
isInstrumentationSuppressed,
} from '../../src/context/context';

const SUPPRESS_INSTRUMENTATION_KEY = createContextKey(
'OpenTelemetry Context Key SUPPRESS_INSTRUMENTATION'
);

describe('Context Helpers', () => {
describe('suppressInstrumentation', () => {
it('should set suppress to true', () => {
const context = suppressInstrumentation(ROOT_CONTEXT);
assert.deepStrictEqual(isInstrumentationSuppressed(context), true);
});
});

describe('unsuppressInstrumentation', () => {
it('should set suppress to false', () => {
const context = unsuppressInstrumentation(ROOT_CONTEXT);
assert.deepStrictEqual(isInstrumentationSuppressed(context), false);
});
});

describe('isInstrumentationSuppressed', () => {
it('should get value as bool', () => {
const expectedValue = true;
const context = ROOT_CONTEXT.setValue(
SUPPRESS_INSTRUMENTATION_KEY,
expectedValue
);

const value = isInstrumentationSuppressed(context);

assert.equal(value, expectedValue);
});

describe('when suppress instrumentation set to null', () => {
const context = ROOT_CONTEXT.setValue(SUPPRESS_INSTRUMENTATION_KEY, null);

it('should return false', () => {
const value = isInstrumentationSuppressed(context);

assert.equal(value, false);
});
});

describe('when suppress instrumentation set to undefined', () => {
const context = ROOT_CONTEXT.setValue(
SUPPRESS_INSTRUMENTATION_KEY,
undefined
);

it('should return false', () => {
const value = isInstrumentationSuppressed(context);

assert.equal(value, false);
});
});
});
});
40 changes: 39 additions & 1 deletion api/test/noop-implementations/noop-tracer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,15 @@
*/

import * as assert from 'assert';
import { NoopTracer, NOOP_SPAN, SpanKind } from '../../src';
import {
NoopTracer,
NOOP_SPAN,
SpanContext,
SpanKind,
TraceFlags,
context,
setExtractedSpanContext,
} from '../../src';

describe('NoopTracer', () => {
it('should not crash', () => {
Expand Down Expand Up @@ -51,4 +59,34 @@ describe('NoopTracer', () => {
const patchedFn = tracer.bind(fn, NOOP_SPAN);
return patchedFn();
});

it('should propagate valid spanContext on the span (from parent)', () => {
const tracer = new NoopTracer();
const parent: SpanContext = {
traceId: 'd4cda95b652f4a1592b449d5929fda1b',
spanId: '6e0c63257de34c92',
traceFlags: TraceFlags.NONE,
};
const span = tracer.startSpan('test-1', { parent });
assert(span.context().traceId === parent.traceId);
assert(span.context().spanId === parent.spanId);
assert(span.context().traceFlags === parent.traceFlags);
});

it('should propagate valid spanContext on the span (from context)', () => {
const tracer = new NoopTracer();
const parent: SpanContext = {
traceId: 'd4cda95b652f4a1592b449dd92ffda3b',
spanId: '6e0c63ffe4e34c42',
traceFlags: TraceFlags.NONE,
};
const span = tracer.startSpan(
'test-1',
{},
setExtractedSpanContext(context.active(), parent)
);
assert(span.context().traceId === parent.traceId);
assert(span.context().spanId === parent.spanId);
assert(span.context().traceFlags === parent.traceFlags);
});
});

0 comments on commit 651f9f3

Please sign in to comment.