diff --git a/packages/opentelemetry-core/src/context/context.ts b/packages/opentelemetry-core/src/context/context.ts index c3a6dc33f50..1c1fe4cdbef 100644 --- a/packages/opentelemetry-core/src/context/context.ts +++ b/packages/opentelemetry-core/src/context/context.ts @@ -84,3 +84,33 @@ export function getParentSpanContext( ): SpanContext | undefined { return getActiveSpan(context)?.context() || getExtractedSpanContext(context); } + +/** + * Shared key for indicating if instrumentation should be suppressed beyond + * this current scope. + */ +export const SUPPRESS_INSTRUMENTATION_KEY = Context.createKey( + 'OpenTelemetry Context Key SUPPRESS_INSTRUMENTATION' +) + +/** + * Set whether or not instrumentation should be suppressed beyond + * this current scope. + * + * @param context context to set the suppress instrumentation value on. + * @param shouldSuppress value to set. + */ +export function setSuppressInstrumentation(context: Context, shouldSuppress: boolean): Context { + return context.setValue(SUPPRESS_INSTRUMENTATION_KEY, shouldSuppress) +} + +/** + * Return current suppress instrumentation value for the given context, + * if it exists. + * + * @param context context check for the suppress instrumentation value. + */ +export function getSuppressInstrumentation(context: Context): boolean | undefined { + const value = context.getValue(SUPPRESS_INSTRUMENTATION_KEY) as boolean + return value +} \ No newline at end of file diff --git a/packages/opentelemetry-core/test/context/context.test.ts b/packages/opentelemetry-core/test/context/context.test.ts new file mode 100644 index 00000000000..7328d8d957e --- /dev/null +++ b/packages/opentelemetry-core/test/context/context.test.ts @@ -0,0 +1,69 @@ +/* + * 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 { + SUPPRESS_INSTRUMENTATION_KEY, + setSuppressInstrumentation, + getSuppressInstrumentation, +} from '../../src/context/context'; +import { Context } from '@opentelemetry/api'; + + +describe('Context Helpers', () => { + describe('setSuppressInstrumentation', () => { + it('should set suppress instrumentation value', () => { + const expectedValue = true + const context = setSuppressInstrumentation(Context.ROOT_CONTEXT, expectedValue) + + const value = context.getValue(SUPPRESS_INSTRUMENTATION_KEY) + const boolValue = value as boolean + + assert.equal(boolValue, expectedValue) + }) + }) + + describe('getSuppressInstrumentation', () => { + it('should get value as bool', () => { + const expectedValue = false + const context = Context.ROOT_CONTEXT.setValue(SUPPRESS_INSTRUMENTATION_KEY, expectedValue) + + const value = getSuppressInstrumentation(context) + + assert.equal(value, expectedValue) + }) + + it('should handle null values', () => { + const expectedValue = null + const context = Context.ROOT_CONTEXT.setValue(SUPPRESS_INSTRUMENTATION_KEY, expectedValue) + + const value = getSuppressInstrumentation(context) + + assert.equal(value, expectedValue) + }) + + it('should handle undefined values', () => { + const expectedValue = undefined + const context = Context.ROOT_CONTEXT.setValue(SUPPRESS_INSTRUMENTATION_KEY, expectedValue) + + const value = getSuppressInstrumentation(context) + + assert.equal(value, expectedValue) + }) + }) +}) + diff --git a/packages/opentelemetry-tracing/src/Tracer.ts b/packages/opentelemetry-tracing/src/Tracer.ts index 694829dda03..2dc355fb1a4 100644 --- a/packages/opentelemetry-tracing/src/Tracer.ts +++ b/packages/opentelemetry-tracing/src/Tracer.ts @@ -25,6 +25,7 @@ import { randomSpanId, randomTraceId, setActiveSpan, + getSuppressInstrumentation } from '@opentelemetry/core'; import { Resource } from '@opentelemetry/resources'; import { BasicTracerProvider } from './BasicTracerProvider'; @@ -69,6 +70,12 @@ export class Tracer implements api.Tracer { options: api.SpanOptions = {}, context = api.context.active() ): api.Span { + const suppressInstrumentation = getSuppressInstrumentation(context) + if (suppressInstrumentation) { + this.logger.debug('Instrumentation suppressed, returning NoOp Span'); + return api.NOOP_SPAN; + } + const parentContext = getParent(options, context); const spanId = randomSpanId(); let traceId; @@ -81,6 +88,7 @@ export class Tracer implements api.Tracer { traceId = parentContext.traceId; traceState = parentContext.traceState; } + const spanKind = options.kind ?? api.SpanKind.INTERNAL; const links = options.links ?? []; const attributes = { ...this._defaultAttributes, ...options.attributes }; diff --git a/packages/opentelemetry-tracing/test/Tracer.test.ts b/packages/opentelemetry-tracing/test/Tracer.test.ts index a65b00ce966..cbd6d233e6c 100644 --- a/packages/opentelemetry-tracing/test/Tracer.test.ts +++ b/packages/opentelemetry-tracing/test/Tracer.test.ts @@ -15,13 +15,14 @@ */ import * as assert from 'assert'; -import { NoopSpan, Sampler, SamplingDecision } from '@opentelemetry/api'; +import { NoopSpan, Sampler, SamplingDecision, Context, NOOP_SPAN } from '@opentelemetry/api'; import { BasicTracerProvider, Tracer, Span } from '../src'; import { InstrumentationLibrary, NoopLogger, AlwaysOnSampler, AlwaysOffSampler, + setSuppressInstrumentation } from '@opentelemetry/core'; describe('Tracer', () => { @@ -94,4 +95,20 @@ describe('Tracer', () => { assert.strictEqual(lib.name, 'default'); assert.strictEqual(lib.version, '0.0.1'); }); + + it('should return cached no-op span when suppressInstrumentation true', (done) => { + const tracer = new Tracer( + { name: 'default', version: '0.0.1' }, + { sampler: new TestSampler() }, + tracerProvider + ); + + const context = setSuppressInstrumentation(Context.ROOT_CONTEXT, true) + const span = tracer.startSpan('span3', undefined, context); + + assert.equal(span, NOOP_SPAN); + span.end(); + + done() + }); });