From b323afe57cca6b114f400e06c06d1bb120e6a5ad Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Wed, 26 Feb 2020 10:21:54 -0500 Subject: [PATCH 01/15] feat: use context-based tracing --- packages/opentelemetry-api/src/api/context.ts | 6 +- .../src/trace/SpanOptions.ts | 10 +- .../opentelemetry-api/src/trace/tracer.ts | 21 +- .../src/NodeTracerProvider.ts | 9 +- .../test/NodeTracerProvider.test.ts | 72 ++--- packages/opentelemetry-plugin-dns/src/dns.ts | 1 - .../src/documentLoad.ts | 90 +++--- .../opentelemetry-plugin-express/package.json | 1 + .../src/express.ts | 15 +- .../test/express.test.ts | 154 +++++---- .../opentelemetry-plugin-grpc/package.json | 2 + .../opentelemetry-plugin-grpc/src/grpc.ts | 102 +++--- .../test/grpc.test.ts | 26 +- .../opentelemetry-plugin-http/package.json | 1 + .../opentelemetry-plugin-http/src/http.ts | 301 ++++++++---------- .../test/functionals/http-enable.test.ts | 36 ++- .../test/functionals/http-package.test.ts | 30 +- .../test/functionals/utils.test.ts | 7 +- .../test/integrations/http-enable.test.ts | 2 - .../test/utils/DummyPropagation.ts | 8 +- .../opentelemetry-plugin-https/package.json | 2 + .../test/functionals/https-disable.test.ts | 3 - .../test/functionals/https-enable.test.ts | 34 +- .../test/functionals/https-package.test.ts | 2 - .../test/integrations/https-enable.test.ts | 3 - .../test/utils/DummyPropagation.ts | 8 +- .../opentelemetry-plugin-ioredis/src/utils.ts | 5 - .../src/mongodb.ts | 2 - .../opentelemetry-plugin-mysql/src/mysql.ts | 1 - .../src/pg-pool.ts | 1 - .../opentelemetry-plugin-pg/src/utils.ts | 1 - .../opentelemetry-plugin-redis/src/utils.ts | 1 - .../src/userInteraction.ts | 1 - .../test/userInteraction.test.ts | 2 - .../src/xhr.ts | 46 ++- .../test/xhr.test.ts | 27 +- .../src/shim.ts | 80 ++--- .../test/Shim.test.ts | 8 +- packages/opentelemetry-tracing/src/Tracer.ts | 80 ++--- packages/opentelemetry-tracing/src/types.ts | 24 +- .../test/BasicTracerRegistry.test.ts | 132 ++------ .../test/export/InMemorySpanExporter.test.ts | 13 +- .../src/WebTracerProvider.ts | 8 +- .../test/WebTracerProvider.test.ts | 43 +-- 44 files changed, 640 insertions(+), 781 deletions(-) diff --git a/packages/opentelemetry-api/src/api/context.ts b/packages/opentelemetry-api/src/api/context.ts index 6d4b89c4ea..e25b5bd5a8 100644 --- a/packages/opentelemetry-api/src/api/context.ts +++ b/packages/opentelemetry-api/src/api/context.ts @@ -57,12 +57,12 @@ export class ContextAPI { /** * Execute a function with an active context * + * @param context context to be active during function execution * @param fn function to execute in a context - * @param context context to be active during function execution. Defaults to the currently active context */ public with ReturnType>( - fn: T, - context: Context = this.active() + context: Context, + fn: T ): ReturnType { return this._scopeManager.with(context, fn); } diff --git a/packages/opentelemetry-api/src/trace/SpanOptions.ts b/packages/opentelemetry-api/src/trace/SpanOptions.ts index a546876a5f..6a9a8d8ae7 100644 --- a/packages/opentelemetry-api/src/trace/SpanOptions.ts +++ b/packages/opentelemetry-api/src/trace/SpanOptions.ts @@ -14,11 +14,9 @@ * limitations under the License. */ -import { Span } from './span'; import { Attributes } from './attributes'; -import { SpanKind } from './span_kind'; -import { SpanContext } from './span_context'; import { Link } from './link'; +import { SpanKind } from './span_kind'; /** * Options needed for span creation @@ -42,12 +40,6 @@ export interface SpanOptions { /** A spans links */ links?: Link[]; - /** - * A parent `SpanContext` (or `Span`, for convenience) that the newly-started - * span will be the child of. - */ - parent?: Span | SpanContext | null; - /** A manually specified start time for the created `Span` object. */ startTime?: number; } diff --git a/packages/opentelemetry-api/src/trace/tracer.ts b/packages/opentelemetry-api/src/trace/tracer.ts index 6d17cbc5a1..2bce251071 100644 --- a/packages/opentelemetry-api/src/trace/tracer.ts +++ b/packages/opentelemetry-api/src/trace/tracer.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { HttpTextFormat } from '../context/propagation/HttpTextFormat'; +import { Context } from '@opentelemetry/scope-base'; import { BinaryFormat } from '../context/propagation/BinaryFormat'; import { Span } from './span'; import { SpanOptions } from './SpanOptions'; @@ -40,9 +40,10 @@ export interface Tracer { * Starts a new {@link Span}. * @param name The name of the span * @param [options] SpanOptions used for span creation + * @param [context] Context to use to extract parent * @returns Span The newly created span */ - startSpan(name: string, options?: SpanOptions): Span; + startSpan(name: string, options?: SpanOptions, context?: Context): Span; /** * Executes the function given by fn within the context provided by Span @@ -61,9 +62,9 @@ export interface Tracer { * Bind a span as the target's scope or propagate the current one. * * @param target Any object to which a scope need to be set - * @param [span] Optionally specify the span which you want to assign + * @param [context] Optionally specify the context which you want to bind */ - bind(target: T, span?: Span): T; + bind(target: T, context?: Span): T; /** * Returns the {@link BinaryFormat} interface which can serialize/deserialize @@ -77,16 +78,4 @@ export interface Tracer { * @returns the {@link BinaryFormat} for this implementation. */ getBinaryFormat(): BinaryFormat; - - /** - * Returns the {@link HttpTextFormat} interface which can inject/extract - * Spans. - * - * If no tracer implementation is provided, this defaults to the W3C Trace - * Context HTTP text format {@link HttpTextFormat}. For more details see - * W3C Trace Context. - * - * @returns the {@link HttpTextFormat} for this implementation. - */ - getHttpTextFormat(): HttpTextFormat; } diff --git a/packages/opentelemetry-node/src/NodeTracerProvider.ts b/packages/opentelemetry-node/src/NodeTracerProvider.ts index b326a978d7..541ed38240 100644 --- a/packages/opentelemetry-node/src/NodeTracerProvider.ts +++ b/packages/opentelemetry-node/src/NodeTracerProvider.ts @@ -15,9 +15,8 @@ */ import { BasicTracerProvider } from '@opentelemetry/tracing'; -import { AsyncHooksScopeManager } from '@opentelemetry/scope-async-hooks'; +import { DEFAULT_INSTRUMENTATION_PLUGINS, NodeTracerConfig } from './config'; import { PluginLoader } from './instrumentation/PluginLoader'; -import { NodeTracerConfig, DEFAULT_INSTRUMENTATION_PLUGINS } from './config'; /** * This class represents a node tracer with `async_hooks` module. @@ -29,11 +28,7 @@ export class NodeTracerProvider extends BasicTracerProvider { * Constructs a new Tracer instance. */ constructor(config: NodeTracerConfig = {}) { - if (config.scopeManager === undefined) { - config.scopeManager = new AsyncHooksScopeManager(); - config.scopeManager.enable(); - } - super(Object.assign({ scopeManager: config.scopeManager }, config)); + super(Object.assign({}, config)); this._pluginLoader = new PluginLoader(this, this.logger); this._pluginLoader.load(config.plugins || DEFAULT_INSTRUMENTATION_PLUGINS); diff --git a/packages/opentelemetry-node/test/NodeTracerProvider.test.ts b/packages/opentelemetry-node/test/NodeTracerProvider.test.ts index 38ba91ff92..0fc598d50e 100644 --- a/packages/opentelemetry-node/test/NodeTracerProvider.test.ts +++ b/packages/opentelemetry-node/test/NodeTracerProvider.test.ts @@ -14,19 +14,20 @@ * limitations under the License. */ -import * as assert from 'assert'; +import { context, TraceFlags } from '@opentelemetry/api'; import { ALWAYS_SAMPLER, - BinaryTraceContext, - HttpTraceContext, NEVER_SAMPLER, NoopLogger, NoRecordingSpan, + setActiveSpan, } from '@opentelemetry/core'; -import { NodeTracerProvider } from '../src/NodeTracerProvider'; -import { TraceFlags } from '@opentelemetry/api'; +import { AsyncHooksScopeManager } from '@opentelemetry/scope-async-hooks'; import { Span } from '@opentelemetry/tracing'; +import * as assert from 'assert'; import * as path from 'path'; +import { NodeTracerProvider } from '../src/NodeTracerProvider'; +import { ScopeManager } from '../../opentelemetry-scope-base/build/src'; const sleep = (time: number) => new Promise(resolve => { @@ -41,14 +42,21 @@ const INSTALLED_PLUGINS_PATH = path.join( describe('NodeTracerProvider', () => { let provider: NodeTracerProvider; + let scopeManager: ScopeManager; before(() => { module.paths.push(INSTALLED_PLUGINS_PATH); }); + beforeEach(() => { + scopeManager = new AsyncHooksScopeManager(); + context.initGlobalContextManager(scopeManager.enable()); + }); + afterEach(() => { // clear require cache Object.keys(require.cache).forEach(key => delete require.cache[key]); provider.stop(); + scopeManager.disable(); }); describe('constructor', () => { @@ -57,20 +65,6 @@ describe('NodeTracerProvider', () => { assert.ok(provider instanceof NodeTracerProvider); }); - it('should construct an instance with binary format', () => { - provider = new NodeTracerProvider({ - binaryFormat: new BinaryTraceContext(), - }); - assert.ok(provider instanceof NodeTracerProvider); - }); - - it('should construct an instance with http text format', () => { - provider = new NodeTracerProvider({ - httpTextFormat: new HttpTraceContext(), - }); - assert.ok(provider instanceof NodeTracerProvider); - }); - it('should construct an instance with logger', () => { provider = new NodeTracerProvider({ logger: new NoopLogger(), @@ -180,7 +174,7 @@ describe('NodeTracerProvider', () => { it('should run scope with AsyncHooksScopeManager scope manager', done => { provider = new NodeTracerProvider({}); const span = provider.getTracer('default').startSpan('my-span'); - provider.getTracer('default').withSpan(span, () => { + context.with(setActiveSpan(context.active(), span), () => { assert.deepStrictEqual( provider.getTracer('default').getCurrentSpan(), span @@ -196,16 +190,15 @@ describe('NodeTracerProvider', () => { it('should run scope with AsyncHooksScopeManager scope manager with multiple spans', done => { provider = new NodeTracerProvider({}); const span = provider.getTracer('default').startSpan('my-span'); - provider.getTracer('default').withSpan(span, () => { + context.with(setActiveSpan(context.active(), span), () => { assert.deepStrictEqual( provider.getTracer('default').getCurrentSpan(), span ); - const span1 = provider - .getTracer('default') - .startSpan('my-span1', { parent: span }); - provider.getTracer('default').withSpan(span1, () => { + const span1 = provider.getTracer('default').startSpan('my-span1'); + + context.with(setActiveSpan(context.active(), span1), () => { assert.deepStrictEqual( provider.getTracer('default').getCurrentSpan(), span1 @@ -225,10 +218,10 @@ describe('NodeTracerProvider', () => { ); }); - it('should find correct scope with promises', done => { - provider = new NodeTracerProvider({}); + it('should find correct scope with promises', async () => { + provider = new NodeTracerProvider(); const span = provider.getTracer('default').startSpan('my-span'); - provider.getTracer('default').withSpan(span, async () => { + await context.with(setActiveSpan(context.active(), span), async () => { for (let i = 0; i < 3; i++) { await sleep(5).then(() => { assert.deepStrictEqual( @@ -237,7 +230,6 @@ describe('NodeTracerProvider', () => { ); }); } - return done(); }); assert.deepStrictEqual( provider.getTracer('default').getCurrentSpan(), @@ -257,28 +249,8 @@ describe('NodeTracerProvider', () => { ); return done(); }; - const patchedFn = provider.getTracer('default').bind(fn, span); + const patchedFn = context.bind(fn, setActiveSpan(context.active(), span)); return patchedFn(); }); }); - - describe('.getBinaryFormat()', () => { - it('should get default binary formatter', () => { - provider = new NodeTracerProvider({}); - assert.ok( - provider.getTracer('default').getBinaryFormat() instanceof - BinaryTraceContext - ); - }); - }); - - describe('.getHttpTextFormat()', () => { - it('should get default HTTP text formatter', () => { - provider = new NodeTracerProvider({}); - assert.ok( - provider.getTracer('default').getHttpTextFormat() instanceof - HttpTraceContext - ); - }); - }); }); diff --git a/packages/opentelemetry-plugin-dns/src/dns.ts b/packages/opentelemetry-plugin-dns/src/dns.ts index d305079dc7..b52a685b00 100644 --- a/packages/opentelemetry-plugin-dns/src/dns.ts +++ b/packages/opentelemetry-plugin-dns/src/dns.ts @@ -117,7 +117,6 @@ export class DnsPlugin extends BasePlugin { plugin._logger.debug('wrap lookup callback function and starts span'); const name = utils.getOperationName('lookup'); const span = plugin._startDnsSpan(name, { - parent: plugin._tracer.getCurrentSpan(), attributes: { [AttributeNames.PEER_HOSTNAME]: hostname, }, diff --git a/packages/opentelemetry-plugin-document-load/src/documentLoad.ts b/packages/opentelemetry-plugin-document-load/src/documentLoad.ts index cb661df6e5..f60b6aee3f 100644 --- a/packages/opentelemetry-plugin-document-load/src/documentLoad.ts +++ b/packages/opentelemetry-plugin-document-load/src/documentLoad.ts @@ -14,13 +14,19 @@ * limitations under the License. */ +import { + context, + PluginConfig, + propagation, + Span, + SpanOptions, +} from '@opentelemetry/api'; import { BasePlugin, otperformance, - parseTraceParent, + setActiveSpan, TRACE_PARENT_HEADER, } from '@opentelemetry/core'; -import { PluginConfig, Span, SpanOptions } from '@opentelemetry/api'; import { addSpanNetworkEvent, hasKey, @@ -71,9 +77,7 @@ export class DocumentLoad extends BasePlugin { ) as PerformanceResourceTiming[]; if (resources) { resources.forEach(resource => { - this._initResourceSpan(resource, { - parent: rootSpan, - }); + this._initResourceSpan(resource); }); } } @@ -102,40 +106,54 @@ export class DocumentLoad extends BasePlugin { ); const entries = this._getEntries(); + context.with( + propagation.extract({ + traceparent: (metaElement && metaElement.content) || '', + }), + () => { + const rootSpan = this._startSpan( + AttributeNames.DOCUMENT_LOAD, + PTN.FETCH_START, + entries + ); + if (!rootSpan) { + return; + } + context.with(setActiveSpan(context.active(), rootSpan), () => { + const fetchSpan = this._startSpan( + AttributeNames.DOCUMENT_FETCH, + PTN.FETCH_START, + entries + ); + if (fetchSpan) { + context.with(setActiveSpan(context.active(), fetchSpan), () => { + this._addSpanNetworkEvents(fetchSpan, entries); + this._endSpan(fetchSpan, PTN.RESPONSE_END, entries); + }); + } + }); - const rootSpan = this._startSpan( - AttributeNames.DOCUMENT_LOAD, - PTN.FETCH_START, - entries, - { parent: parseTraceParent((metaElement && metaElement.content) || '') } - ); - if (!rootSpan) { - return; - } - const fetchSpan = this._startSpan( - AttributeNames.DOCUMENT_FETCH, - PTN.FETCH_START, - entries, - { - parent: rootSpan, - } - ); - if (fetchSpan) { - this._addSpanNetworkEvents(fetchSpan, entries); - this._endSpan(fetchSpan, PTN.RESPONSE_END, entries); - } - - this._addResourcesSpans(rootSpan); + this._addResourcesSpans(rootSpan); - addSpanNetworkEvent(rootSpan, PTN.UNLOAD_EVENT_START, entries); - addSpanNetworkEvent(rootSpan, PTN.UNLOAD_EVENT_END, entries); - addSpanNetworkEvent(rootSpan, PTN.DOM_INTERACTIVE, entries); - addSpanNetworkEvent(rootSpan, PTN.DOM_CONTENT_LOADED_EVENT_START, entries); - addSpanNetworkEvent(rootSpan, PTN.DOM_CONTENT_LOADED_EVENT_END, entries); - addSpanNetworkEvent(rootSpan, PTN.DOM_COMPLETE, entries); - addSpanNetworkEvent(rootSpan, PTN.LOAD_EVENT_START, entries); + addSpanNetworkEvent(rootSpan, PTN.UNLOAD_EVENT_START, entries); + addSpanNetworkEvent(rootSpan, PTN.UNLOAD_EVENT_END, entries); + addSpanNetworkEvent(rootSpan, PTN.DOM_INTERACTIVE, entries); + addSpanNetworkEvent( + rootSpan, + PTN.DOM_CONTENT_LOADED_EVENT_START, + entries + ); + addSpanNetworkEvent( + rootSpan, + PTN.DOM_CONTENT_LOADED_EVENT_END, + entries + ); + addSpanNetworkEvent(rootSpan, PTN.DOM_COMPLETE, entries); + addSpanNetworkEvent(rootSpan, PTN.LOAD_EVENT_START, entries); - this._endSpan(rootSpan, PTN.LOAD_EVENT_END, entries); + this._endSpan(rootSpan, PTN.LOAD_EVENT_END, entries); + } + ); } /** diff --git a/packages/opentelemetry-plugin-express/package.json b/packages/opentelemetry-plugin-express/package.json index c0ed0ae612..400ff9ef1f 100644 --- a/packages/opentelemetry-plugin-express/package.json +++ b/packages/opentelemetry-plugin-express/package.json @@ -42,6 +42,7 @@ }, "devDependencies": { "@opentelemetry/node": "^0.4.0", + "@opentelemetry/scope-async-hooks": "^0.4.0", "@opentelemetry/tracing": "^0.4.0", "@types/express": "^4.17.2", "@types/mocha": "^5.2.7", diff --git a/packages/opentelemetry-plugin-express/src/express.ts b/packages/opentelemetry-plugin-express/src/express.ts index 6186b03696..c9ff72f979 100644 --- a/packages/opentelemetry-plugin-express/src/express.ts +++ b/packages/opentelemetry-plugin-express/src/express.ts @@ -14,27 +14,27 @@ * limitations under the License. */ -import { BasePlugin } from '@opentelemetry/core'; import { Attributes } from '@opentelemetry/api'; +import { BasePlugin } from '@opentelemetry/core'; import * as express from 'express'; import * as core from 'express-serve-static-core'; import * as shimmer from 'shimmer'; import { + AttributeNames, ExpressLayer, + ExpressLayerType, + ExpressPluginConfig, ExpressRouter, - AttributeNames, - PatchedRequest, Parameters, + PatchedRequest, PathParams, _LAYERS_STORE_PROPERTY, - ExpressPluginConfig, - ExpressLayerType, } from './types'; import { getLayerMetadata, - storeLayerPath, - patchEnd, isLayerIgnored, + patchEnd, + storeLayerPath, } from './utils'; import { VERSION } from './version'; @@ -188,7 +188,6 @@ export class ExpressPlugin extends BasePlugin { return original.apply(this, arguments); } const span = plugin._tracer.startSpan(metadata.name, { - parent: plugin._tracer.getCurrentSpan(), attributes: Object.assign(attributes, metadata.attributes), }); // verify we have a callback diff --git a/packages/opentelemetry-plugin-express/test/express.test.ts b/packages/opentelemetry-plugin-express/test/express.test.ts index 231663f1b9..4200fcec63 100644 --- a/packages/opentelemetry-plugin-express/test/express.test.ts +++ b/packages/opentelemetry-plugin-express/test/express.test.ts @@ -20,7 +20,8 @@ import * as express from 'express'; import * as http from 'http'; import { AddressInfo } from 'net'; import { plugin } from '../src'; -import { NoopLogger } from '@opentelemetry/core'; +import { NoopLogger, setActiveSpan } from '@opentelemetry/core'; +import { context } from '@opentelemetry/api'; import { InMemorySpanExporter, SimpleSpanProcessor, @@ -30,6 +31,7 @@ import { ExpressPluginConfig, ExpressLayerType, } from '../src/types'; +import { AsyncHooksScopeManager } from '@opentelemetry/scope-async-hooks'; const httpRequest = { get: (options: http.ClientRequestArgs | string) => { @@ -57,13 +59,20 @@ describe('Express Plugin', () => { const spanProcessor = new SimpleSpanProcessor(memoryExporter); provider.addSpanProcessor(spanProcessor); const tracer = provider.getTracer('default'); + let scopeManager: AsyncHooksScopeManager; before(() => { plugin.enable(express, provider, logger); }); + beforeEach(() => { + scopeManager = new AsyncHooksScopeManager(); + context.initGlobalContextManager(scopeManager.enable()); + }); + afterEach(() => { memoryExporter.reset(); + scopeManager.disable(); }); describe('Instrumenting normal get operations', () => { @@ -86,48 +95,51 @@ describe('Express Plugin', () => { server.listen(0, () => { const port = (server.address() as AddressInfo).port; assert.strictEqual(memoryExporter.getFinishedSpans().length, 0); - tracer.withSpan(rootSpan, async () => { - await httpRequest.get(`http://localhost:${port}/toto/tata`); - rootSpan.end(); - assert( - memoryExporter - .getFinishedSpans() - .find(span => span.name.includes('customMiddleware')) !== - undefined - ); - assert( - memoryExporter + scopeManager.with( + setActiveSpan(context.active(), rootSpan), + async () => { + await httpRequest.get(`http://localhost:${port}/toto/tata`); + rootSpan.end(); + assert( + memoryExporter + .getFinishedSpans() + .find(span => span.name.includes('customMiddleware')) !== + undefined + ); + assert( + memoryExporter + .getFinishedSpans() + .find(span => span.name.includes('query')) !== undefined + ); + assert( + memoryExporter + .getFinishedSpans() + .find(span => span.name.includes('jsonParser')) !== undefined + ); + const requestHandlerSpan = memoryExporter .getFinishedSpans() - .find(span => span.name.includes('query')) !== undefined - ); - assert( - memoryExporter + .find(span => span.name.includes('request handler')); + assert(requestHandlerSpan !== undefined); + assert( + requestHandlerSpan?.attributes[AttributeNames.COMPONENT] === + 'express' + ); + assert( + requestHandlerSpan?.attributes[AttributeNames.HTTP_ROUTE] === + '/toto/:id' + ); + assert( + requestHandlerSpan?.attributes[AttributeNames.EXPRESS_TYPE] === + 'request_handler' + ); + let exportedRootSpan = memoryExporter .getFinishedSpans() - .find(span => span.name.includes('jsonParser')) !== undefined - ); - const requestHandlerSpan = memoryExporter - .getFinishedSpans() - .find(span => span.name.includes('request handler')); - assert(requestHandlerSpan !== undefined); - assert( - requestHandlerSpan?.attributes[AttributeNames.COMPONENT] === - 'express' - ); - assert( - requestHandlerSpan?.attributes[AttributeNames.HTTP_ROUTE] === - '/toto/:id' - ); - assert( - requestHandlerSpan?.attributes[AttributeNames.EXPRESS_TYPE] === - 'request_handler' - ); - let exportedRootSpan = memoryExporter - .getFinishedSpans() - .find(span => span.name === 'rootSpan'); - assert(exportedRootSpan !== undefined); - server.close(); - return done(); - }); + .find(span => span.name === 'rootSpan'); + assert(exportedRootSpan !== undefined); + server.close(); + return done(); + } + ); }); }); }); @@ -152,26 +164,29 @@ describe('Express Plugin', () => { server.listen(0, () => { const port = (server.address() as AddressInfo).port; assert.strictEqual(memoryExporter.getFinishedSpans().length, 0); - tracer.withSpan(rootSpan, async () => { - await httpRequest.get(`http://localhost:${port}/toto/tata`); - rootSpan.end(); - assert.deepEqual( - memoryExporter + scopeManager.with( + setActiveSpan(context.active(), rootSpan), + async () => { + await httpRequest.get(`http://localhost:${port}/toto/tata`); + rootSpan.end(); + assert.deepEqual( + memoryExporter + .getFinishedSpans() + .filter( + span => + span.attributes[AttributeNames.EXPRESS_TYPE] === + ExpressLayerType.MIDDLEWARE + ).length, + 0 + ); + let exportedRootSpan = memoryExporter .getFinishedSpans() - .filter( - span => - span.attributes[AttributeNames.EXPRESS_TYPE] === - ExpressLayerType.MIDDLEWARE - ).length, - 0 - ); - let exportedRootSpan = memoryExporter - .getFinishedSpans() - .find(span => span.name === 'rootSpan'); - assert(exportedRootSpan !== undefined); - server.close(); - return done(); - }); + .find(span => span.name === 'rootSpan'); + assert(exportedRootSpan !== undefined); + server.close(); + return done(); + } + ); }); }); }); @@ -192,14 +207,17 @@ describe('Express Plugin', () => { server.listen(0, () => { const port = (server.address() as AddressInfo).port; assert.strictEqual(memoryExporter.getFinishedSpans().length, 0); - tracer.withSpan(rootSpan, async () => { - await httpRequest.get(`http://localhost:${port}/toto/tata`); - rootSpan.end(); - assert.deepEqual(memoryExporter.getFinishedSpans().length, 1); - assert(memoryExporter.getFinishedSpans()[0] !== undefined); - server.close(); - return done(); - }); + scopeManager.with( + setActiveSpan(context.active(), rootSpan), + async () => { + await httpRequest.get(`http://localhost:${port}/toto/tata`); + rootSpan.end(); + assert.deepEqual(memoryExporter.getFinishedSpans().length, 1); + assert(memoryExporter.getFinishedSpans()[0] !== undefined); + server.close(); + return done(); + } + ); }); }); }); diff --git a/packages/opentelemetry-plugin-grpc/package.json b/packages/opentelemetry-plugin-grpc/package.json index cb03707dbe..2cd07f8d1f 100644 --- a/packages/opentelemetry-plugin-grpc/package.json +++ b/packages/opentelemetry-plugin-grpc/package.json @@ -42,6 +42,8 @@ }, "devDependencies": { "@opentelemetry/node": "^0.4.0", + "@opentelemetry/scope-async-hooks": "^0.4.0", + "@opentelemetry/scope-base": "^0.4.0", "@opentelemetry/tracing": "^0.4.0", "@types/mocha": "^5.2.7", "@types/node": "^12.6.9", diff --git a/packages/opentelemetry-plugin-grpc/src/grpc.ts b/packages/opentelemetry-plugin-grpc/src/grpc.ts index 9123a2ce2f..3e08a5a168 100644 --- a/packages/opentelemetry-plugin-grpc/src/grpc.ts +++ b/packages/opentelemetry-plugin-grpc/src/grpc.ts @@ -14,20 +14,16 @@ * limitations under the License. */ -import { - BasePlugin, - getExtractedSpanContext, - setExtractedSpanContext, -} from '@opentelemetry/core'; import { CanonicalCode, + context, + propagation, Span, - SpanContext, SpanKind, SpanOptions, Status, - Context, } from '@opentelemetry/api'; +import { BasePlugin } from '@opentelemetry/core'; import * as events from 'events'; import * as grpcTypes from 'grpc'; import * as path from 'path'; @@ -127,22 +123,9 @@ export class GrpcPlugin extends BasePlugin { } } - private _getSpanContext( - metadata: grpcTypes.Metadata - ): SpanContext | undefined { - return getExtractedSpanContext( - this._tracer.getHttpTextFormat().extract(Context.TODO, metadata.getMap()) - ); - } - - private _setSpanContext( - metadata: grpcTypes.Metadata, - spanContext: SpanContext - ): void { + private _setSpanContext(metadata: grpcTypes.Metadata): void { const carrier = {}; - this._tracer - .getHttpTextFormat() - .inject(setExtractedSpanContext(Context.TODO, spanContext), carrier); + propagation.inject(carrier); for (const [k, v] of Object.entries(carrier)) { metadata.set(k, v as string); } @@ -180,7 +163,6 @@ export class GrpcPlugin extends BasePlugin { const spanName = `grpc.${name.replace('/', '')}`; const spanOptions: SpanOptions = { kind: SpanKind.SERVER, - parent: plugin._getSpanContext(call.metadata), }; plugin._logger.debug( @@ -188,37 +170,39 @@ export class GrpcPlugin extends BasePlugin { JSON.stringify(spanOptions) ); - const span = plugin._tracer - .startSpan(spanName, spanOptions) - .setAttributes({ - [AttributeNames.GRPC_KIND]: spanOptions.kind, - [AttributeNames.COMPONENT]: GrpcPlugin.component, + context.with(propagation.extract(call.metadata.getMap()), () => { + const span = plugin._tracer + .startSpan(spanName, spanOptions) + .setAttributes({ + [AttributeNames.GRPC_KIND]: spanOptions.kind, + [AttributeNames.COMPONENT]: GrpcPlugin.component, + }); + + plugin._tracer.withSpan(span, () => { + switch (type) { + case 'unary': + case 'client_stream': + return plugin._clientStreamAndUnaryHandler( + plugin, + span, + call, + callback, + originalFunc, + self + ); + case 'server_stream': + case 'bidi': + return plugin._serverStreamAndBidiHandler( + plugin, + span, + call, + originalFunc, + self + ); + default: + break; + } }); - - plugin._tracer.withSpan(span, () => { - switch (type) { - case 'unary': - case 'client_stream': - return plugin._clientStreamAndUnaryHandler( - plugin, - span, - call, - callback, - originalFunc, - self - ); - case 'server_stream': - case 'bidi': - return plugin._serverStreamAndBidiHandler( - plugin, - span, - call, - originalFunc, - self - ); - default: - break; - } }); }; } @@ -371,15 +355,11 @@ export class GrpcPlugin extends BasePlugin { const span = plugin._tracer .startSpan(name, { kind: SpanKind.CLIENT, - parent: plugin._tracer.getCurrentSpan(), }) .setAttribute(AttributeNames.COMPONENT, GrpcPlugin.component); - return plugin._makeGrpcClientRemoteCall( - original, - args, - this, - plugin - )(span); + return plugin._tracer.withSpan(span, () => + plugin._makeGrpcClientRemoteCall(original, args, this, plugin)(span) + ); }; }; } @@ -457,7 +437,7 @@ export class GrpcPlugin extends BasePlugin { [AttributeNames.GRPC_KIND]: SpanKind.CLIENT, }); - this._setSpanContext(metadata, span.context()); + this._setSpanContext(metadata); const call = original.apply(self, args); // if server stream or bidi diff --git a/packages/opentelemetry-plugin-grpc/test/grpc.test.ts b/packages/opentelemetry-plugin-grpc/test/grpc.test.ts index 0cd3b67669..25bc3f6a01 100644 --- a/packages/opentelemetry-plugin-grpc/test/grpc.test.ts +++ b/packages/opentelemetry-plugin-grpc/test/grpc.test.ts @@ -14,9 +14,16 @@ * limitations under the License. */ -import { NoopTracerProvider, SpanKind } from '@opentelemetry/api'; -import { NoopLogger } from '@opentelemetry/core'; +import { + context, + NoopTracerProvider, + SpanKind, + propagation, +} from '@opentelemetry/api'; +import { NoopLogger, HttpTraceContext } from '@opentelemetry/core'; import { NodeTracerProvider } from '@opentelemetry/node'; +import { AsyncHooksScopeManager } from '@opentelemetry/scope-async-hooks'; +import { ScopeManager } from '@opentelemetry/scope-base'; import { InMemorySpanExporter, SimpleSpanProcessor, @@ -293,6 +300,21 @@ function createClient(grpc: GrpcModule, proto: any) { } describe('GrpcPlugin', () => { + let scopeManger: ScopeManager; + + before(() => { + propagation.initGlobalPropagator(new HttpTraceContext()); + }); + + beforeEach(() => { + scopeManger = new AsyncHooksScopeManager().enable(); + context.initGlobalContextManager(scopeManger); + }); + + afterEach(() => { + scopeManger.disable(); + }); + it('should return a plugin', () => { assert.ok(plugin instanceof GrpcPlugin); }); diff --git a/packages/opentelemetry-plugin-http/package.json b/packages/opentelemetry-plugin-http/package.json index 9c550c69f0..384f16d0ce 100644 --- a/packages/opentelemetry-plugin-http/package.json +++ b/packages/opentelemetry-plugin-http/package.json @@ -42,6 +42,7 @@ }, "devDependencies": { "@opentelemetry/node": "^0.4.0", + "@opentelemetry/scope-async-hooks": "^0.4.0", "@opentelemetry/scope-base": "^0.4.0", "@opentelemetry/tracing": "^0.4.0", "@types/got": "^9.6.7", diff --git a/packages/opentelemetry-plugin-http/src/http.ts b/packages/opentelemetry-plugin-http/src/http.ts index 1107311305..faa5ab63c3 100644 --- a/packages/opentelemetry-plugin-http/src/http.ts +++ b/packages/opentelemetry-plugin-http/src/http.ts @@ -16,18 +16,14 @@ import { CanonicalCode, - Context, + context, + propagation, Span, SpanKind, SpanOptions, Status, } from '@opentelemetry/api'; -import { - BasePlugin, - getExtractedSpanContext, - isValid, - setActiveSpan, -} from '@opentelemetry/core'; +import { BasePlugin } from '@opentelemetry/core'; import { ClientRequest, IncomingMessage, @@ -175,87 +171,83 @@ export class HttpPlugin extends BasePlugin { } /** - * Injects span's context to header for distributed tracing and finishes the - * span when the response is finished. + * Attach event listeners to a client request to end span and add span attributes. + * * @param request The original request object. * @param options The arguments to the original function. * @param span representing the current operation */ - private _getMakeRequestTraceFunction( + private _traceClientRequest( request: ClientRequest, options: ParsedRequestOptions, span: Span - ): Func { - return (): ClientRequest => { - this._logger.debug('makeRequestTrace by injecting context into header'); - - const hostname = - options.hostname || - options.host?.replace(/^(.*)(\:[0-9]{1,5})/, '$1') || - 'localhost'; - const attributes = utils.getOutgoingRequestAttributes(options, { - component: this.component, - hostname, - }); - span.setAttributes(attributes); - - request.on( - 'response', - (response: IncomingMessage & { aborted?: boolean }) => { - const attributes = utils.getOutgoingRequestAttributesOnResponse( - response, - { hostname } - ); - span.setAttributes(attributes); - - this._tracer.bind(response); - this._logger.debug('outgoingRequest on response()'); - response.on('end', () => { - this._logger.debug('outgoingRequest on end()'); - let status: Status; - - if (response.aborted && !response.complete) { - status = { code: CanonicalCode.ABORTED }; - } else { - status = utils.parseResponseStatus(response.statusCode!); - } + ): ClientRequest { + const hostname = + options.hostname || + options.host?.replace(/^(.*)(\:[0-9]{1,5})/, '$1') || + 'localhost'; + const attributes = utils.getOutgoingRequestAttributes(options, { + component: this.component, + hostname, + }); + span.setAttributes(attributes); + + request.on( + 'response', + (response: IncomingMessage & { aborted?: boolean }) => { + const attributes = utils.getOutgoingRequestAttributesOnResponse( + response, + { hostname } + ); + span.setAttributes(attributes); + + this._tracer.bind(response); + this._logger.debug('outgoingRequest on response()'); + response.on('end', () => { + this._logger.debug('outgoingRequest on end()'); + let status: Status; + + if (response.aborted && !response.complete) { + status = { code: CanonicalCode.ABORTED }; + } else { + status = utils.parseResponseStatus(response.statusCode!); + } - span.setStatus(status); + span.setStatus(status); - if (this._config.applyCustomAttributesOnSpan) { - this._safeExecute( - span, - () => - this._config.applyCustomAttributesOnSpan!( - span, - request, - response - ), - false - ); - } + if (this._config.applyCustomAttributesOnSpan) { + this._safeExecute( + span, + () => + this._config.applyCustomAttributesOnSpan!( + span, + request, + response + ), + false + ); + } - this._closeHttpSpan(span); - }); - response.on('error', (error: Err) => { - utils.setSpanWithError(span, error, response); - this._closeHttpSpan(span); - }); - } - ); - request.on('close', () => { - if (!request.aborted) { this._closeHttpSpan(span); - } - }); - request.on('error', (error: Err) => { - utils.setSpanWithError(span, error, request); + }); + response.on('error', (error: Err) => { + utils.setSpanWithError(span, error, response); + this._closeHttpSpan(span); + }); + } + ); + request.on('close', () => { + if (!request.aborted) { this._closeHttpSpan(span); - }); - - this._logger.debug('makeRequestTrace return request'); - return request; - }; + } + }); + request.on('error', (error: Err) => { + utils.setSpanWithError(span, error, request); + this._closeHttpSpan(span); + }); + + this._logger.debug('makeRequestTrace return request'); + return request; } private _incomingRequestFunction( @@ -292,7 +284,6 @@ export class HttpPlugin extends BasePlugin { return original.apply(this, [event, ...args]); } - const propagation = plugin._tracer.getHttpTextFormat(); const headers = request.headers; const spanOptions: SpanOptions = { @@ -303,69 +294,65 @@ export class HttpPlugin extends BasePlugin { }), }; - // Using context directly like this is temporary. In a future PR, context - // will be managed by the scope manager (which may be renamed to context manager?) - const spanContext = getExtractedSpanContext( - propagation.extract(Context.TODO, headers) - ); - if (spanContext && isValid(spanContext)) { - spanOptions.parent = spanContext; - } - - const span = plugin._startHttpSpan(`${method} ${pathname}`, spanOptions); + return context.with(propagation.extract(headers), () => { + const span = plugin._startHttpSpan( + `${method} ${pathname}`, + spanOptions + ); - return plugin._tracer.withSpan(span, () => { - plugin._tracer.bind(request); - plugin._tracer.bind(response); - - // Wraps end (inspired by: - // https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/blob/master/src/plugins/plugin-connect.ts#L75) - const originalEnd = response.end; - response.end = function( - this: ServerResponse, - ...args: ResponseEndArgs - ) { - response.end = originalEnd; - // Cannot pass args of type ResponseEndArgs, - // tslint complains "Expected 1-2 arguments, but got 1 or more.", it does not make sense to me - const returned = plugin._safeExecute( - span, - // tslint:disable-next-line:no-any - () => response.end.apply(this, arguments as any), - true - ); + return plugin._tracer.withSpan(span, () => { + context.bind(request); + context.bind(response); + + // Wraps end (inspired by: + // https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/blob/master/src/plugins/plugin-connect.ts#L75) + const originalEnd = response.end; + response.end = function( + this: ServerResponse, + ...args: ResponseEndArgs + ) { + response.end = originalEnd; + // Cannot pass args of type ResponseEndArgs, + // tslint complains "Expected 1-2 arguments, but got 1 or more.", it does not make sense to me + const returned = plugin._safeExecute( + span, + // tslint:disable-next-line:no-any + () => response.end.apply(this, arguments as any), + true + ); - const attributes = utils.getIncomingRequestAttributesOnResponse( - request, - response - ); + const attributes = utils.getIncomingRequestAttributesOnResponse( + request, + response + ); - span - .setAttributes(attributes) - .setStatus(utils.parseResponseStatus(response.statusCode)); + span + .setAttributes(attributes) + .setStatus(utils.parseResponseStatus(response.statusCode)); - if (plugin._config.applyCustomAttributesOnSpan) { - plugin._safeExecute( - span, - () => - plugin._config.applyCustomAttributesOnSpan!( - span, - request, - response - ), - false - ); - } + if (plugin._config.applyCustomAttributesOnSpan) { + plugin._safeExecute( + span, + () => + plugin._config.applyCustomAttributesOnSpan!( + span, + request, + response + ), + false + ); + } - plugin._closeHttpSpan(span); - return returned; - }; + plugin._closeHttpSpan(span); + return returned; + }; - return plugin._safeExecute( - span, - () => original.apply(this, [event, ...args]), - true - ); + return plugin._safeExecute( + span, + () => original.apply(this, [event, ...args]), + true + ); + }); }); }; } @@ -393,10 +380,10 @@ export class HttpPlugin extends BasePlugin { extraOptions ); - options = optionsParsed; + const requestOptions = optionsParsed; if ( - utils.isOpenTelemetryRequest(options) || + utils.isOpenTelemetryRequest(requestOptions) || utils.isIgnored( origin + pathname, plugin._config.ignoreOutgoingUrls, @@ -404,48 +391,30 @@ export class HttpPlugin extends BasePlugin { plugin._logger.error('caught ignoreOutgoingUrls error: ', e) ) ) { - return original.apply(this, [options, ...args]); + return original.apply(this, [requestOptions, ...args]); } - const currentSpan = plugin._tracer.getCurrentSpan(); const operationName = `${method} ${pathname}`; const spanOptions: SpanOptions = { kind: SpanKind.CLIENT, - parent: currentSpan ? currentSpan : undefined, }; const span = plugin._startHttpSpan(operationName, spanOptions); - if (!options.headers) options.headers = {}; - plugin._tracer - .getHttpTextFormat() - // Using context directly like this is temporary. In a future PR, context - // will be managed by the scope manager (which may be renamed to context manager?) - .inject(setActiveSpan(Context.TODO, span), options.headers); - - const request: ClientRequest = plugin._safeExecute( - span, - () => original.apply(this, [options, ...args]), - true - ); - - plugin._logger.debug('%s plugin outgoingRequest', plugin.moduleName); - plugin._tracer.bind(request); + return plugin._tracer.withSpan(span, () => { + if (!requestOptions.headers) requestOptions.headers = {}; + propagation.inject(requestOptions.headers); - // Checks if this outgoing request is part of an operation by checking - // if there is a current span, if so, we create a child span. In - // case there is no active span, this means that the outgoing request is - // the first operation, therefore we create a span and call withSpan method. - if (!currentSpan) { - plugin._logger.debug('outgoingRequest starting a span without context'); - return plugin._tracer.withSpan( + const request: ClientRequest = plugin._safeExecute( span, - plugin._getMakeRequestTraceFunction(request, options, span) + () => original.apply(this, [requestOptions, ...args]), + true ); - } else { - plugin._logger.debug('outgoingRequest starting a child span'); - return plugin._getMakeRequestTraceFunction(request, options, span)(); - } + + plugin._logger.debug('%s plugin outgoingRequest', plugin.moduleName); + plugin._tracer.bind(request); + return plugin._traceClientRequest(request, requestOptions, span); + }); }; } diff --git a/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts b/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts index 32042d66d1..9564763595 100644 --- a/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts +++ b/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts @@ -14,24 +14,32 @@ * limitations under the License. */ +import { + CanonicalCode, + Span as ISpan, + SpanKind, + propagation, + context, +} from '@opentelemetry/api'; +import { NoopLogger } from '@opentelemetry/core'; +import { NodeTracerProvider } from '@opentelemetry/node'; import { InMemorySpanExporter, SimpleSpanProcessor, } from '@opentelemetry/tracing'; -import { NoopLogger } from '@opentelemetry/core'; -import { NodeTracerProvider } from '@opentelemetry/node'; -import { CanonicalCode, Span as ISpan, SpanKind } from '@opentelemetry/api'; import * as assert from 'assert'; import * as http from 'http'; -import * as path from 'path'; import * as nock from 'nock'; +import * as path from 'path'; +import { AttributeNames } from '../../src/enums/AttributeNames'; import { HttpPlugin, plugin } from '../../src/http'; +import { Http, HttpPluginConfig } from '../../src/types'; +import { OT_REQUEST_HEADER } from '../../src/utils'; import { assertSpan } from '../utils/assertSpan'; import { DummyPropagation } from '../utils/DummyPropagation'; import { httpRequest } from '../utils/httpRequest'; -import { OT_REQUEST_HEADER } from '../../src/utils'; -import { HttpPluginConfig, Http } from '../../src/types'; -import { AttributeNames } from '../../src/enums/AttributeNames'; +import { ScopeManager } from '@opentelemetry/scope-base'; +import { AsyncHooksScopeManager } from '@opentelemetry/scope-async-hooks'; const applyCustomAttributesOnSpanErrorMessage = 'bad applyCustomAttributesOnSpan function'; @@ -43,13 +51,12 @@ const hostname = 'localhost'; const pathname = '/test'; const serverName = 'my.server.name'; const memoryExporter = new InMemorySpanExporter(); -const httpTextFormat = new DummyPropagation(); const logger = new NoopLogger(); const provider = new NodeTracerProvider({ logger, - httpTextFormat, }); provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); +propagation.initGlobalPropagator(new DummyPropagation()); function doNock( hostname: string, @@ -70,6 +77,17 @@ export const customAttributeFunction = (span: ISpan): void => { }; describe('HttpPlugin', () => { + let scopeManger: ScopeManager; + + beforeEach(() => { + scopeManger = new AsyncHooksScopeManager().enable(); + context.initGlobalContextManager(scopeManger); + }); + + afterEach(() => { + scopeManger.disable(); + }); + it('should return a plugin', () => { assert.ok(plugin instanceof HttpPlugin); }); diff --git a/packages/opentelemetry-plugin-http/test/functionals/http-package.test.ts b/packages/opentelemetry-plugin-http/test/functionals/http-package.test.ts index 4f00515562..c6880fda54 100644 --- a/packages/opentelemetry-plugin-http/test/functionals/http-package.test.ts +++ b/packages/opentelemetry-plugin-http/test/functionals/http-package.test.ts @@ -14,27 +14,26 @@ * limitations under the License. */ -import { NoopLogger } from '@opentelemetry/core'; import { SpanKind } from '@opentelemetry/api'; -import * as assert from 'assert'; -import * as http from 'http'; -import * as nock from 'nock'; -import { plugin } from '../../src/http'; -import { assertSpan } from '../utils/assertSpan'; -import { DummyPropagation } from '../utils/DummyPropagation'; -import * as url from 'url'; -import axios, { AxiosResponse } from 'axios'; -import * as superagent from 'superagent'; -import * as got from 'got'; -import * as request from 'request-promise-native'; -import * as path from 'path'; +import { NoopLogger } from '@opentelemetry/core'; import { NodeTracerProvider } from '@opentelemetry/node'; import { InMemorySpanExporter, SimpleSpanProcessor, } from '@opentelemetry/tracing'; - +import * as assert from 'assert'; +import axios, { AxiosResponse } from 'axios'; +import * as got from 'got'; +import * as http from 'http'; +import * as nock from 'nock'; +import * as path from 'path'; +import * as request from 'request-promise-native'; +import * as superagent from 'superagent'; +import * as url from 'url'; +import { plugin } from '../../src/http'; import { HttpPluginConfig } from '../../src/types'; +import { assertSpan } from '../utils/assertSpan'; +import { DummyPropagation } from '../utils/DummyPropagation'; import { customAttributeFunction } from './http-enable.test'; const memoryExporter = new InMemorySpanExporter(); @@ -42,12 +41,9 @@ const protocol = 'http'; describe('Packages', () => { describe('get', () => { - const httpTextFormat = new DummyPropagation(); const logger = new NoopLogger(); - const provider = new NodeTracerProvider({ logger, - httpTextFormat, }); provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); beforeEach(() => { diff --git a/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts b/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts index 8bfb673fcf..f13c79fceb 100644 --- a/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts +++ b/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts @@ -14,10 +14,9 @@ * limitations under the License. */ +import { CanonicalCode, SpanKind } from '@opentelemetry/api'; import { NoopLogger } from '@opentelemetry/core'; -import { NoopScopeManager } from '@opentelemetry/scope-base'; import { BasicTracerProvider, Span } from '@opentelemetry/tracing'; -import { CanonicalCode, SpanKind } from '@opentelemetry/api'; import * as assert from 'assert'; import * as http from 'http'; import * as sinon from 'sinon'; @@ -255,9 +254,7 @@ describe('Utility', () => { const errorMessage = 'test error'; for (const obj of [undefined, { statusCode: 400 }]) { const span = new Span( - new BasicTracerProvider({ - scopeManager: new NoopScopeManager(), - }).getTracer('default'), + new BasicTracerProvider({}).getTracer('default'), 'test', { spanId: '', traceId: '' }, SpanKind.INTERNAL diff --git a/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts b/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts index 6f95768ef3..0a529c684e 100644 --- a/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts +++ b/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts @@ -58,11 +58,9 @@ describe('HttpPlugin Integration tests', () => { }); }); - const httpTextFormat = new DummyPropagation(); const logger = new NoopLogger(); const provider = new NodeTracerProvider({ logger, - httpTextFormat, }); provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); beforeEach(() => { diff --git a/packages/opentelemetry-plugin-http/test/utils/DummyPropagation.ts b/packages/opentelemetry-plugin-http/test/utils/DummyPropagation.ts index 5c65bd16d7..69f809b453 100644 --- a/packages/opentelemetry-plugin-http/test/utils/DummyPropagation.ts +++ b/packages/opentelemetry-plugin-http/test/utils/DummyPropagation.ts @@ -24,10 +24,14 @@ export class DummyPropagation implements HttpTextFormat { static TRACE_CONTEXT_KEY = 'x-dummy-trace-id'; static SPAN_CONTEXT_KEY = 'x-dummy-span-id'; extract(context: Context, carrier: http.OutgoingHttpHeaders) { - return setExtractedSpanContext(context, { + const extractedSpanContext = { traceId: carrier[DummyPropagation.TRACE_CONTEXT_KEY] as string, spanId: DummyPropagation.SPAN_CONTEXT_KEY, - }); + }; + if (extractedSpanContext.traceId && extractedSpanContext.spanId) { + return setExtractedSpanContext(context, extractedSpanContext); + } + return context; } inject(context: Context, headers: { [custom: string]: string }): void { const spanContext = getParentSpanContext(context); diff --git a/packages/opentelemetry-plugin-https/package.json b/packages/opentelemetry-plugin-https/package.json index e71888eb20..12ea4c4398 100644 --- a/packages/opentelemetry-plugin-https/package.json +++ b/packages/opentelemetry-plugin-https/package.json @@ -42,6 +42,8 @@ }, "devDependencies": { "@opentelemetry/node": "^0.4.0", + "@opentelemetry/scope-async-hooks": "^0.4.0", + "@opentelemetry/scope-base": "^0.4.0", "@opentelemetry/tracing": "^0.4.0", "@types/got": "^9.6.7", "@types/mocha": "^5.2.7", diff --git a/packages/opentelemetry-plugin-https/test/functionals/https-disable.test.ts b/packages/opentelemetry-plugin-https/test/functionals/https-disable.test.ts index 4f7e92339d..572ea6ea1a 100644 --- a/packages/opentelemetry-plugin-https/test/functionals/https-disable.test.ts +++ b/packages/opentelemetry-plugin-https/test/functionals/https-disable.test.ts @@ -23,7 +23,6 @@ import { AddressInfo } from 'net'; import * as nock from 'nock'; import * as sinon from 'sinon'; import { plugin } from '../../src/https'; -import { DummyPropagation } from '../utils/DummyPropagation'; import { httpsRequest } from '../utils/httpsRequest'; import { NodeTracerProvider } from '@opentelemetry/node'; import * as types from '@opentelemetry/api'; @@ -33,11 +32,9 @@ describe('HttpsPlugin', () => { let serverPort = 0; describe('disable()', () => { - const httpTextFormat = new DummyPropagation(); const logger = new NoopLogger(); const provider = new NodeTracerProvider({ logger, - httpTextFormat, }); // const tracer = provider.getTracer('test-https') let tracer: types.Tracer; diff --git a/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts b/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts index 5cd755a0e4..e393cc4d79 100644 --- a/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts +++ b/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts @@ -15,24 +15,32 @@ */ import { - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/tracing'; + CanonicalCode, + context, + propagation, + Span as ISpan, + SpanKind, +} from '@opentelemetry/api'; import { NoopLogger } from '@opentelemetry/core'; import { NodeTracerProvider } from '@opentelemetry/node'; import { + AttributeNames, Http, HttpPluginConfig, OT_REQUEST_HEADER, - AttributeNames, } from '@opentelemetry/plugin-http'; -import { CanonicalCode, Span as ISpan, SpanKind } from '@opentelemetry/api'; +import { AsyncHooksScopeManager } from '@opentelemetry/scope-async-hooks'; +import { ScopeManager } from '@opentelemetry/scope-base'; +import { + InMemorySpanExporter, + SimpleSpanProcessor, +} from '@opentelemetry/tracing'; import * as assert from 'assert'; import * as fs from 'fs'; import * as http from 'http'; import * as https from 'https'; -import * as path from 'path'; import * as nock from 'nock'; +import * as path from 'path'; import { HttpsPlugin, plugin } from '../../src/https'; import { assertSpan } from '../utils/assertSpan'; import { DummyPropagation } from '../utils/DummyPropagation'; @@ -48,14 +56,13 @@ const hostname = 'localhost'; const serverName = 'my.server.name'; const pathname = '/test'; const memoryExporter = new InMemorySpanExporter(); -const httpTextFormat = new DummyPropagation(); const logger = new NoopLogger(); const provider = new NodeTracerProvider({ logger, - httpTextFormat, }); const tracer = provider.getTracer('test-https'); provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); +propagation.initGlobalPropagator(new DummyPropagation()); function doNock( hostname: string, @@ -76,6 +83,17 @@ export const customAttributeFunction = (span: ISpan): void => { }; describe('HttpsPlugin', () => { + let scopeManger: ScopeManager; + + beforeEach(() => { + scopeManger = new AsyncHooksScopeManager().enable(); + context.initGlobalContextManager(scopeManger); + }); + + afterEach(() => { + scopeManger.disable(); + }); + it('should return a plugin', () => { assert.ok(plugin instanceof HttpsPlugin); }); diff --git a/packages/opentelemetry-plugin-https/test/functionals/https-package.test.ts b/packages/opentelemetry-plugin-https/test/functionals/https-package.test.ts index bf4d0e67c0..106d00a32e 100644 --- a/packages/opentelemetry-plugin-https/test/functionals/https-package.test.ts +++ b/packages/opentelemetry-plugin-https/test/functionals/https-package.test.ts @@ -44,12 +44,10 @@ export const customAttributeFunction = (span: Span): void => { describe('Packages', () => { describe('get', () => { - const httpTextFormat = new DummyPropagation(); const logger = new NoopLogger(); const provider = new NodeTracerProvider({ logger, - httpTextFormat, }); provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); beforeEach(() => { diff --git a/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts b/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts index 47bb93c03e..fa8be0e518 100644 --- a/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts +++ b/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts @@ -62,12 +62,9 @@ describe('HttpsPlugin Integration tests', () => { done(); }); }); - - const httpTextFormat = new DummyPropagation(); const logger = new NoopLogger(); const provider = new NodeTracerProvider({ logger, - httpTextFormat, }); provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); beforeEach(() => { diff --git a/packages/opentelemetry-plugin-https/test/utils/DummyPropagation.ts b/packages/opentelemetry-plugin-https/test/utils/DummyPropagation.ts index 5c65bd16d7..69f809b453 100644 --- a/packages/opentelemetry-plugin-https/test/utils/DummyPropagation.ts +++ b/packages/opentelemetry-plugin-https/test/utils/DummyPropagation.ts @@ -24,10 +24,14 @@ export class DummyPropagation implements HttpTextFormat { static TRACE_CONTEXT_KEY = 'x-dummy-trace-id'; static SPAN_CONTEXT_KEY = 'x-dummy-span-id'; extract(context: Context, carrier: http.OutgoingHttpHeaders) { - return setExtractedSpanContext(context, { + const extractedSpanContext = { traceId: carrier[DummyPropagation.TRACE_CONTEXT_KEY] as string, spanId: DummyPropagation.SPAN_CONTEXT_KEY, - }); + }; + if (extractedSpanContext.traceId && extractedSpanContext.spanId) { + return setExtractedSpanContext(context, extractedSpanContext); + } + return context; } inject(context: Context, headers: { [custom: string]: string }): void { const spanContext = getParentSpanContext(context); diff --git a/packages/opentelemetry-plugin-ioredis/src/utils.ts b/packages/opentelemetry-plugin-ioredis/src/utils.ts index 44f9f573ed..f27d9672ba 100644 --- a/packages/opentelemetry-plugin-ioredis/src/utils.ts +++ b/packages/opentelemetry-plugin-ioredis/src/utils.ts @@ -34,10 +34,8 @@ const endSpan = (span: Span, err: NodeJS.ErrnoException | null | undefined) => { export const traceConnection = (tracer: Tracer, original: Function) => { return function(this: ioredisTypes.Redis & IORedisPluginClientTypes) { - const parentSpan = tracer.getCurrentSpan(); const span = tracer.startSpan('connect', { kind: SpanKind.CLIENT, - parent: parentSpan, attributes: { [AttributeNames.COMPONENT]: IORedisPlugin.COMPONENT, [AttributeNames.DB_TYPE]: IORedisPlugin.DB_TYPE, @@ -67,12 +65,9 @@ export const traceSendCommand = (tracer: Tracer, original: Function) => { this: ioredisTypes.Redis & IORedisPluginClientTypes, cmd?: IORedisCommand ) { - const parentSpan = tracer.getCurrentSpan(); - if (arguments.length >= 1 && typeof cmd === 'object') { const span = tracer.startSpan(cmd.name, { kind: SpanKind.CLIENT, - parent: parentSpan, attributes: { [AttributeNames.COMPONENT]: IORedisPlugin.COMPONENT, [AttributeNames.DB_TYPE]: IORedisPlugin.DB_TYPE, diff --git a/packages/opentelemetry-plugin-mongodb/src/mongodb.ts b/packages/opentelemetry-plugin-mongodb/src/mongodb.ts index 93eed568df..01da629090 100644 --- a/packages/opentelemetry-plugin-mongodb/src/mongodb.ts +++ b/packages/opentelemetry-plugin-mongodb/src/mongodb.ts @@ -120,7 +120,6 @@ export class MongoDBPlugin extends BasePlugin { ? operationName : commandType; const span = plugin._tracer.startSpan(`mongodb.${type}`, { - parent: currentSpan, kind: SpanKind.CLIENT, }); plugin._populateAttributes( @@ -215,7 +214,6 @@ export class MongoDBPlugin extends BasePlugin { return original.apply(this, args); } const span = plugin._tracer.startSpan(`mongodb.query`, { - parent: currentSpan, kind: SpanKind.CLIENT, }); plugin._populateAttributes(span, this.ns, this.cmd, this.topology); diff --git a/packages/opentelemetry-plugin-mysql/src/mysql.ts b/packages/opentelemetry-plugin-mysql/src/mysql.ts index f5106d1032..35985fdfd1 100644 --- a/packages/opentelemetry-plugin-mysql/src/mysql.ts +++ b/packages/opentelemetry-plugin-mysql/src/mysql.ts @@ -209,7 +209,6 @@ export class MysqlPlugin extends BasePlugin { const spanName = getSpanName(query); const span = thisPlugin._tracer.startSpan(spanName, { - parent: thisPlugin._tracer.getCurrentSpan() || undefined, kind: SpanKind.CLIENT, attributes: { ...MysqlPlugin.COMMON_ATTRIBUTES, diff --git a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/src/pg-pool.ts b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/src/pg-pool.ts index c6ef88ab5a..2c624b5423 100644 --- a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/src/pg-pool.ts +++ b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/src/pg-pool.ts @@ -67,7 +67,6 @@ export class PostgresPoolPlugin extends BasePlugin { `${PostgresPoolPlugin.COMPONENT}.connect`, { kind: SpanKind.CLIENT, - parent: plugin._tracer.getCurrentSpan() || undefined, attributes: { [AttributeNames.COMPONENT]: PostgresPoolPlugin.COMPONENT, // required [AttributeNames.DB_TYPE]: PostgresPoolPlugin.DB_TYPE, // required diff --git a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/src/utils.ts b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/src/utils.ts index 373426e79a..9fa9c168dc 100644 --- a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/src/utils.ts +++ b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/src/utils.ts @@ -52,7 +52,6 @@ function pgStartSpan( const jdbcString = getJDBCString(client.connectionParameters); return tracer.startSpan(name, { kind: SpanKind.CLIENT, - parent: tracer.getCurrentSpan(), attributes: { [AttributeNames.COMPONENT]: PostgresPlugin.COMPONENT, // required [AttributeNames.DB_INSTANCE]: client.connectionParameters.database, // required diff --git a/packages/opentelemetry-plugin-redis/src/utils.ts b/packages/opentelemetry-plugin-redis/src/utils.ts index 8cf65426b3..4a0d37b69c 100644 --- a/packages/opentelemetry-plugin-redis/src/utils.ts +++ b/packages/opentelemetry-plugin-redis/src/utils.ts @@ -77,7 +77,6 @@ export const getTracedInternalSendCommand = ( if (arguments.length === 1 && typeof cmd === 'object') { const span = tracer.startSpan(`${RedisPlugin.COMPONENT}-${cmd.command}`, { kind: SpanKind.CLIENT, - parent: tracer.getCurrentSpan(), attributes: { [AttributeNames.COMPONENT]: RedisPlugin.COMPONENT, [AttributeNames.DB_STATEMENT]: cmd.command, diff --git a/packages/opentelemetry-plugin-user-interaction/src/userInteraction.ts b/packages/opentelemetry-plugin-user-interaction/src/userInteraction.ts index 3ce4028273..5e6037ad33 100644 --- a/packages/opentelemetry-plugin-user-interaction/src/userInteraction.ts +++ b/packages/opentelemetry-plugin-user-interaction/src/userInteraction.ts @@ -96,7 +96,6 @@ export class UserInteractionPlugin extends BasePlugin { [AttributeNames.HTTP_URL]: window.location.href, [AttributeNames.HTTP_USER_AGENT]: navigator.userAgent, }, - parent: this._tracer.getCurrentSpan(), }); this._spansData.set(span, { diff --git a/packages/opentelemetry-plugin-user-interaction/test/userInteraction.test.ts b/packages/opentelemetry-plugin-user-interaction/test/userInteraction.test.ts index 85507feff0..cc870e1a59 100644 --- a/packages/opentelemetry-plugin-user-interaction/test/userInteraction.test.ts +++ b/packages/opentelemetry-plugin-user-interaction/test/userInteraction.test.ts @@ -25,7 +25,6 @@ import * as sinon from 'sinon'; import { isWrapped, LogLevel } from '@opentelemetry/core'; import * as tracing from '@opentelemetry/tracing'; import { WebTracerProvider } from '@opentelemetry/web'; -import { ZoneScopeManager } from '@opentelemetry/scope-zone-peer-dep'; import { XMLHttpRequestPlugin } from '@opentelemetry/plugin-xml-http-request'; import { UserInteractionPlugin } from '../src'; import { WindowWithZone } from '../src/types'; @@ -68,7 +67,6 @@ describe('UserInteractionPlugin', () => { userInteractionPlugin = new UserInteractionPlugin(); webTracerProvider = new WebTracerProvider({ logLevel: LogLevel.ERROR, - scopeManager: new ZoneScopeManager(), plugins: [userInteractionPlugin, new XMLHttpRequestPlugin()], }); dummySpanExporter = new DummySpanExporter(); diff --git a/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts b/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts index cef6eff144..5ab4c02bbd 100644 --- a/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts +++ b/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts @@ -14,15 +14,15 @@ * limitations under the License. */ -import * as types from '@opentelemetry/api'; +import * as api from '@opentelemetry/api'; import { BasePlugin, hrTime, isUrlIgnored, isWrapped, otperformance, - setActiveSpan, urlMatches, + setActiveSpan, } from '@opentelemetry/core'; import { addSpanNetworkEvent, @@ -50,7 +50,7 @@ const OBSERVER_WAIT_TIME_MS = 300; /** * XMLHttpRequest config */ -export interface XMLHttpRequestPluginConfig extends types.PluginConfig { +export interface XMLHttpRequestPluginConfig extends api.PluginConfig { // the number of timing resources is limited, after the limit // (chrome 250, safari 150) the information is not collected anymore // the only way to prevent that is to regularly clean the resources @@ -83,16 +83,12 @@ export class XMLHttpRequestPlugin extends BasePlugin { * @param span * @private */ - private _addHeaders(xhr: XMLHttpRequest, span: types.Span, spanUrl: string) { + private _addHeaders(xhr: XMLHttpRequest, spanUrl: string) { if (!this._shouldPropagateTraceHeaders(spanUrl)) { return; } const headers: { [key: string]: unknown } = {}; - this._tracer - .getHttpTextFormat() - // Using context direclty like this is temporary. In a future PR, context - // will be managed by the scope manager (which may be renamed to context manager?) - .inject(setActiveSpan(types.Context.TODO, span), headers); + api.propagation.inject(headers); Object.keys(headers).forEach(key => { xhr.setRequestHeader(key, String(headers[key])); @@ -134,15 +130,16 @@ export class XMLHttpRequestPlugin extends BasePlugin { * @private */ private _addChildSpan( - span: types.Span, + span: api.Span, corsPreFlightRequest: PerformanceResourceTiming ): void { - const childSpan = this._tracer.startSpan('CORS Preflight', { - startTime: corsPreFlightRequest[PTN.FETCH_START], - parent: span, - }) as types.Span; - this._addSpanNetworkEvents(childSpan, corsPreFlightRequest); - childSpan.end(corsPreFlightRequest[PTN.RESPONSE_END]); + api.context.with(setActiveSpan(api.context.active(), span), () => { + const childSpan = this._tracer.startSpan('CORS Preflight', { + startTime: corsPreFlightRequest[PTN.FETCH_START], + }); + this._addSpanNetworkEvents(childSpan, corsPreFlightRequest); + childSpan.end(corsPreFlightRequest[PTN.RESPONSE_END]); + }); } /** @@ -152,7 +149,7 @@ export class XMLHttpRequestPlugin extends BasePlugin { * @param spanUrl * @private */ - _addFinalSpanAttributes(span: types.Span, xhrMem: XhrMem, spanUrl?: string) { + _addFinalSpanAttributes(span: api.Span, xhrMem: XhrMem, spanUrl?: string) { if (typeof spanUrl === 'string') { const parsedUrl = parseUrl(spanUrl); @@ -177,7 +174,7 @@ export class XMLHttpRequestPlugin extends BasePlugin { * @private */ private _addSpanNetworkEvents( - span: types.Span, + span: api.Span, resource: PerformanceResourceTiming ) { addSpanNetworkEvent(span, PTN.FETCH_START, resource); @@ -244,10 +241,10 @@ export class XMLHttpRequestPlugin extends BasePlugin { */ private _findResourceAndAddNetworkEvents( xhrMem: XhrMem, - span: types.Span, + span: api.Span, spanUrl?: string, - startTime?: types.HrTime, - endTime?: types.HrTime + startTime?: api.HrTime, + endTime?: api.HrTime ): void { if (!spanUrl || !startTime || !endTime || !xhrMem.createdResources) { return; @@ -314,14 +311,13 @@ export class XMLHttpRequestPlugin extends BasePlugin { xhr: XMLHttpRequest, url: string, method: string - ): types.Span | undefined { + ): api.Span | undefined { if (isUrlIgnored(url, this._config.ignoreUrls)) { this._logger.debug('ignoring span as url matches ignored url'); return; } const currentSpan = this._tracer.startSpan(url, { - parent: this._tracer.getCurrentSpan(), attributes: { [AttributeNames.COMPONENT]: this.component, [AttributeNames.HTTP_METHOD]: method, @@ -387,7 +383,7 @@ export class XMLHttpRequestPlugin extends BasePlugin { function endSpanTimeout( eventName: string, xhrMem: XhrMem, - endTime: types.HrTime + endTime: api.HrTime ) { const callbackToRemoveEvents = xhrMem.callbackToRemoveEvents; @@ -487,7 +483,7 @@ export class XMLHttpRequestPlugin extends BasePlugin { xhrMem.createdResources.observer.disconnect(); } }; - plugin._addHeaders(this, currentSpan, spanUrl); + plugin._addHeaders(this, spanUrl); plugin._addResourceObserver(this, spanUrl); } return original.apply(this, args); diff --git a/packages/opentelemetry-plugin-xml-http-request/test/xhr.test.ts b/packages/opentelemetry-plugin-xml-http-request/test/xhr.test.ts index 97a28908c0..c10f7933e3 100644 --- a/packages/opentelemetry-plugin-xml-http-request/test/xhr.test.ts +++ b/packages/opentelemetry-plugin-xml-http-request/test/xhr.test.ts @@ -13,24 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import * as assert from 'assert'; -import * as sinon from 'sinon'; - +import * as types from '@opentelemetry/api'; import { - B3Format, LogLevel, otperformance as performance, + setActiveSpan, X_B3_SAMPLED, X_B3_SPAN_ID, X_B3_TRACE_ID, } from '@opentelemetry/core'; import { ZoneScopeManager } from '@opentelemetry/scope-zone'; import * as tracing from '@opentelemetry/tracing'; -import * as types from '@opentelemetry/api'; import { PerformanceTimingNames as PTN, WebTracerProvider, } from '@opentelemetry/web'; +import * as assert from 'assert'; +import * as sinon from 'sinon'; +import { ScopeManager } from '../../opentelemetry-scope-base/build/src'; import { AttributeNames } from '../src/enums/AttributeNames'; import { EventNames } from '../src/enums/EventNames'; import { XMLHttpRequestPlugin } from '../src/xhr'; @@ -98,6 +98,16 @@ describe('xhr', () => { let requests: any[] = []; let prepareData: any; let clearData: any; + let scopeManager: ScopeManager; + + beforeEach(() => { + scopeManager = new ZoneScopeManager().enable(); + types.context.initGlobalContextManager(scopeManager); + }); + + afterEach(() => { + scopeManager.disable(); + }); describe('when request is successful', () => { let webTracerWithZone: types.Tracer; @@ -142,8 +152,6 @@ describe('xhr', () => { webTracerProviderWithZone = new WebTracerProvider({ logLevel: LogLevel.ERROR, - httpTextFormat: new B3Format(), - scopeManager: new ZoneScopeManager(), plugins: [ new XMLHttpRequestPlugin({ propagateTraceHeaderCorsUrls: propagateTraceHeaderCorsUrls, @@ -159,7 +167,7 @@ describe('xhr', () => { rootSpan = webTracerWithZone.startSpan('root'); - webTracerWithZone.withSpan(rootSpan, () => { + scopeManager.with(setActiveSpan(types.context.active(), rootSpan), () => { getData(fileUrl, () => { fakeNow = 100; }).then(() => { @@ -442,7 +450,6 @@ describe('xhr', () => { webTracerWithZoneProvider = new WebTracerProvider({ logLevel: LogLevel.ERROR, - scopeManager: new ZoneScopeManager(), plugins: [new XMLHttpRequestPlugin()], }); dummySpanExporter = new DummySpanExporter(); @@ -454,7 +461,7 @@ describe('xhr', () => { rootSpan = webTracerWithZone.startSpan('root'); - webTracerWithZone.withSpan(rootSpan, () => { + scopeManager.with(setActiveSpan(types.context.active(), rootSpan), () => { getData(url, () => { fakeNow = 100; }).then(() => { diff --git a/packages/opentelemetry-shim-opentracing/src/shim.ts b/packages/opentelemetry-shim-opentracing/src/shim.ts index 07fb689ccf..a919393486 100644 --- a/packages/opentelemetry-shim-opentracing/src/shim.ts +++ b/packages/opentelemetry-shim-opentracing/src/shim.ts @@ -14,18 +14,17 @@ * limitations under the License. */ -import * as types from '@opentelemetry/api'; +import * as api from '@opentelemetry/api'; import { getExtractedSpanContext, NoopLogger, setExtractedSpanContext, + setActiveSpan, } from '@opentelemetry/core'; import * as opentracing from 'opentracing'; -function translateReferences( - references: opentracing.Reference[] -): types.Link[] { - const links: types.Link[] = []; +function translateReferences(references: opentracing.Reference[]): api.Link[] { + const links: api.Link[] = []; for (const reference of references) { const context = reference.referencedContext(); if (context instanceof SpanContextShim) { @@ -40,8 +39,8 @@ function translateReferences( function translateSpanOptions( options: opentracing.SpanOptions -): types.SpanOptions { - const opts: types.SpanOptions = { +): api.SpanOptions { + const opts: api.SpanOptions = { startTime: options.startTime, }; @@ -49,14 +48,21 @@ function translateSpanOptions( opts.links = translateReferences(options.references); } + return opts; +} + +function getContextWithParent(options: opentracing.SpanOptions) { if (options.childOf) { if (options.childOf instanceof SpanShim) { - opts.parent = (options.childOf as SpanShim).getSpan(); + return setActiveSpan(api.context.active(), options.childOf.getSpan()); } else if (options.childOf instanceof SpanContextShim) { - opts.parent = (options.childOf as SpanContextShim).getSpanContext(); + return setExtractedSpanContext( + api.context.active(), + options.childOf.getSpanContext() + ); } } - return opts; + return api.context.active(); } /** @@ -64,9 +70,9 @@ function translateSpanOptions( * OpenTracing span context API. */ export class SpanContextShim extends opentracing.SpanContext { - private readonly _spanContext: types.SpanContext; + private readonly _spanContext: api.SpanContext; - constructor(spanContext: types.SpanContext) { + constructor(spanContext: api.SpanContext) { super(); this._spanContext = spanContext; } @@ -74,7 +80,7 @@ export class SpanContextShim extends opentracing.SpanContext { /** * Returns the underlying {@link types.SpanContext} */ - getSpanContext(): types.SpanContext { + getSpanContext(): api.SpanContext { return this._spanContext; } @@ -98,10 +104,10 @@ export class SpanContextShim extends opentracing.SpanContext { * OpenTracing tracer API. */ export class TracerShim extends opentracing.Tracer { - private readonly _tracer: types.Tracer; - private readonly _logger: types.Logger; + private readonly _tracer: api.Tracer; + private readonly _logger: api.Logger; - constructor(tracer: types.Tracer, logger?: types.Logger) { + constructor(tracer: api.Tracer, logger?: api.Logger) { super(); this._tracer = tracer; @@ -112,7 +118,11 @@ export class TracerShim extends opentracing.Tracer { name: string, options: opentracing.SpanOptions = {} ): opentracing.Span { - const span = this._tracer.startSpan(name, translateSpanOptions(options)); + const span = this._tracer.startSpan( + name, + translateSpanOptions(options), + getContextWithParent(options) + ); if (options.tags) { span.setAttributes(options.tags); @@ -124,23 +134,21 @@ export class TracerShim extends opentracing.Tracer { _inject( spanContext: opentracing.SpanContext, format: string, - carrier: types.Carrier + carrier: api.Carrier ): void { - const opentelemSpanContext: types.SpanContext = (spanContext as SpanContextShim).getSpanContext(); + const opentelemSpanContext: api.SpanContext = (spanContext as SpanContextShim).getSpanContext(); if (!carrier || typeof carrier !== 'object') return; switch (format) { // tslint:disable-next-line:no-switch-case-fall-through case opentracing.FORMAT_HTTP_HEADERS: case opentracing.FORMAT_TEXT_MAP: - this._tracer - .getHttpTextFormat() - .inject( - setExtractedSpanContext( - types.Context.ROOT_CONTEXT, - opentelemSpanContext - ), - carrier - ); + api.propagation.inject( + carrier, + setExtractedSpanContext( + api.Context.ROOT_CONTEXT, + opentelemSpanContext + ) + ); return; case opentracing.FORMAT_BINARY: this._logger.warn( @@ -154,16 +162,14 @@ export class TracerShim extends opentracing.Tracer { _extract( format: string, - carrier: types.Carrier + carrier: api.Carrier ): opentracing.SpanContext | null { switch (format) { // tslint:disable-next-line:no-switch-case-fall-through case opentracing.FORMAT_HTTP_HEADERS: case opentracing.FORMAT_TEXT_MAP: const context = getExtractedSpanContext( - this._tracer - .getHttpTextFormat() - .extract(types.Context.ROOT_CONTEXT, carrier) + api.propagation.extract(carrier) ); if (!context) { return null; @@ -189,11 +195,11 @@ export class TracerShim extends opentracing.Tracer { export class SpanShim extends opentracing.Span { // _span is the original OpenTelemetry span that we are wrapping with // an opentracing interface. - private readonly _span: types.Span; + private readonly _span: api.Span; private readonly _contextShim: SpanContextShim; private readonly _tracerShim: TracerShim; - constructor(tracerShim: TracerShim, span: types.Span) { + constructor(tracerShim: TracerShim, span: api.Span) { super(); this._span = span; this._contextShim = new SpanContextShim(span.context()); @@ -242,7 +248,7 @@ export class SpanShim extends opentracing.Span { * @param payload an arbitrary object to be attached to the event. */ logEvent(eventName: string, payload?: unknown): void { - let attrs: types.Attributes = {}; + let attrs: api.Attributes = {}; if (payload) { attrs = { payload }; } @@ -279,7 +285,7 @@ export class SpanShim extends opentracing.Span { key === opentracing.Tags.ERROR && (value === true || value === 'true') ) { - this._span.setStatus({ code: types.CanonicalCode.UNKNOWN }); + this._span.setStatus({ code: api.CanonicalCode.UNKNOWN }); return this; } @@ -301,7 +307,7 @@ export class SpanShim extends opentracing.Span { * Returns the underlying {@link types.Span} that the shim * is wrapping. */ - getSpan(): types.Span { + getSpan(): api.Span { return this._span; } } diff --git a/packages/opentelemetry-shim-opentracing/test/Shim.test.ts b/packages/opentelemetry-shim-opentracing/test/Shim.test.ts index fbecde28f4..ab7237c80e 100644 --- a/packages/opentelemetry-shim-opentracing/test/Shim.test.ts +++ b/packages/opentelemetry-shim-opentracing/test/Shim.test.ts @@ -18,7 +18,12 @@ import * as assert from 'assert'; import * as opentracing from 'opentracing'; import { BasicTracerProvider, Span } from '@opentelemetry/tracing'; import { TracerShim, SpanShim, SpanContextShim } from '../src/shim'; -import { INVALID_SPAN_CONTEXT, timeInputToHrTime } from '@opentelemetry/core'; +import { + INVALID_SPAN_CONTEXT, + timeInputToHrTime, + HttpTraceContext, +} from '@opentelemetry/core'; +import { propagation } from '@opentelemetry/api'; import { performance } from 'perf_hooks'; describe('OpenTracing Shim', () => { @@ -27,6 +32,7 @@ describe('OpenTracing Shim', () => { provider.getTracer('default') ); opentracing.initGlobalTracer(shimTracer); + propagation.initGlobalPropagator(new HttpTraceContext()); describe('TracerShim', () => { let span: opentracing.Span; diff --git a/packages/opentelemetry-tracing/src/Tracer.ts b/packages/opentelemetry-tracing/src/Tracer.ts index 6bff5f84d7..6497286e25 100644 --- a/packages/opentelemetry-tracing/src/Tracer.ts +++ b/packages/opentelemetry-tracing/src/Tracer.ts @@ -14,17 +14,17 @@ * limitations under the License. */ -import * as types from '@opentelemetry/api'; +import * as api from '@opentelemetry/api'; import { ConsoleLogger, - getActiveSpan, + getParentSpanContext, isValid, NoRecordingSpan, randomSpanId, randomTraceId, + getActiveSpan, setActiveSpan, } from '@opentelemetry/core'; -import { ScopeManager } from '@opentelemetry/scope-base'; import { BasicTracerProvider } from './BasicTracerProvider'; import { DEFAULT_CONFIG } from './config'; import { Span } from './Span'; @@ -34,14 +34,12 @@ import { mergeConfig } from './utility'; /** * This class represents a basic tracer. */ -export class Tracer implements types.Tracer { - private readonly _defaultAttributes: types.Attributes; - private readonly _binaryFormat: types.BinaryFormat; - private readonly _httpTextFormat: types.HttpTextFormat; - private readonly _sampler: types.Sampler; - private readonly _scopeManager: ScopeManager; +export class Tracer implements api.Tracer { + private readonly _defaultAttributes: api.Attributes; + private readonly _binaryFormat: api.BinaryFormat; + private readonly _sampler: api.Sampler; private readonly _traceParams: TraceParams; - readonly logger: types.Logger; + readonly logger: api.Logger; /** * Constructs a new Tracer instance. @@ -53,9 +51,7 @@ export class Tracer implements types.Tracer { const localConfig = mergeConfig(config); this._binaryFormat = localConfig.binaryFormat; this._defaultAttributes = localConfig.defaultAttributes; - this._httpTextFormat = localConfig.httpTextFormat; this._sampler = localConfig.sampler; - this._scopeManager = localConfig.scopeManager; this._traceParams = localConfig.traceParams; this.logger = config.logger || new ConsoleLogger(config.logLevel); } @@ -64,8 +60,12 @@ export class Tracer implements types.Tracer { * Starts a new Span or returns the default NoopSpan based on the sampling * decision. */ - startSpan(name: string, options: types.SpanOptions = {}): types.Span { - const parentContext = this._getParentSpanContext(options.parent); + startSpan( + name: string, + options: api.SpanOptions = {}, + context = api.context.active() + ): api.Span { + const parentContext = getParentSpanContext(context); // make sampling decision const samplingDecision = this._sampler.shouldSample(parentContext); const spanId = randomSpanId(); @@ -80,8 +80,8 @@ export class Tracer implements types.Tracer { traceState = parentContext.traceState; } const traceFlags = samplingDecision - ? types.TraceFlags.SAMPLED - : types.TraceFlags.UNSAMPLED; + ? api.TraceFlags.SAMPLED + : api.TraceFlags.UNSAMPLED; const spanContext = { traceId, spanId, traceFlags, traceState }; const recordEvents = options.isRecording || false; if (!recordEvents && !samplingDecision) { @@ -93,7 +93,7 @@ export class Tracer implements types.Tracer { this, name, spanContext, - options.kind || types.SpanKind.INTERNAL, + options.kind || api.SpanKind.INTERNAL, parentContext ? parentContext.spanId : undefined, options.links || [], options.startTime @@ -110,51 +110,39 @@ export class Tracer implements types.Tracer { * * If there is no Span associated with the current context, undefined is returned. */ - getCurrentSpan(): types.Span | undefined { + getCurrentSpan(): api.Span | undefined { + const ctx = api.context.active(); // Get the current Span from the context or null if none found. - return getActiveSpan(this._scopeManager.active()); + return getActiveSpan(ctx); } /** * Enters the scope of code where the given Span is in the current context. */ withSpan ReturnType>( - span: types.Span, + span: api.Span, fn: T ): ReturnType { // Set given span to context. - return this._scopeManager.with( - setActiveSpan(this._scopeManager.active(), span), - fn - ); + return api.context.with(setActiveSpan(api.context.active(), span), fn); } /** * Bind a span (or the current one) to the target's scope */ - bind(target: T, span?: types.Span): T { - return this._scopeManager.bind( + bind(target: T, span?: api.Span): T { + return api.context.bind( target, - span - ? setActiveSpan(this._scopeManager.active(), span) - : this._scopeManager.active() + span ? setActiveSpan(api.context.active(), span) : api.context.active() ); } /** * Returns the binary format interface which can serialize/deserialize Spans. */ - getBinaryFormat(): types.BinaryFormat { + getBinaryFormat(): api.BinaryFormat { return this._binaryFormat; } - - /** - * Returns the HTTP text format interface which can inject/extract Spans. - */ - getHttpTextFormat(): types.HttpTextFormat { - return this._httpTextFormat; - } - /** Returns the active {@link TraceParams}. */ getActiveTraceParams(): TraceParams { return this._traceParams; @@ -163,20 +151,4 @@ export class Tracer implements types.Tracer { getActiveSpanProcessor() { return this._tracerProvider.getActiveSpanProcessor(); } - - private _getParentSpanContext( - parent?: types.Span | types.SpanContext | null - ): types.SpanContext | undefined { - if (!parent) return undefined; - - // parent is a SpanContext - if ((parent as types.SpanContext).traceId) { - return parent as types.SpanContext; - } - - if (typeof (parent as types.Span).context === 'function') { - return (parent as Span).context(); - } - return undefined; - } } diff --git a/packages/opentelemetry-tracing/src/types.ts b/packages/opentelemetry-tracing/src/types.ts index e8ef51c0ae..3fdd9240f8 100644 --- a/packages/opentelemetry-tracing/src/types.ts +++ b/packages/opentelemetry-tracing/src/types.ts @@ -14,36 +14,19 @@ * limitations under the License. */ -import { ScopeManager } from '@opentelemetry/scope-base'; -import { - Attributes, - BinaryFormat, - HttpTextFormat, - Logger, - Sampler, -} from '@opentelemetry/api'; +import { Attributes, Logger, Sampler } from '@opentelemetry/api'; import { LogLevel } from '@opentelemetry/core'; /** * TracerConfig provides an interface for configuring a Basic Tracer. */ export interface TracerConfig { - /** - * Binary formatter which can serialize/deserialize Spans. - */ - binaryFormat?: BinaryFormat; - /** * Attributed that will be applied on every span created by Tracer. * Useful to add infrastructure and environment information to your spans. */ defaultAttributes?: Attributes; - /** - * HTTP text formatter which can inject/extract Spans. - */ - httpTextFormat?: HttpTextFormat; - /** * User provided logger. */ @@ -57,11 +40,6 @@ export interface TracerConfig { */ sampler?: Sampler; - /** - * Scope manager keeps context across in-process operations. - */ - scopeManager?: ScopeManager; - /** Trace Parameters */ traceParams?: TraceParams; } diff --git a/packages/opentelemetry-tracing/test/BasicTracerRegistry.test.ts b/packages/opentelemetry-tracing/test/BasicTracerRegistry.test.ts index f829de1ff7..f72837817f 100644 --- a/packages/opentelemetry-tracing/test/BasicTracerRegistry.test.ts +++ b/packages/opentelemetry-tracing/test/BasicTracerRegistry.test.ts @@ -14,18 +14,16 @@ * limitations under the License. */ -import { Context, TraceFlags } from '@opentelemetry/api'; +import { Context, SpanContext, TraceFlags } from '@opentelemetry/api'; import { ALWAYS_SAMPLER, - BinaryTraceContext, - HttpTraceContext, NEVER_SAMPLER, NoopLogger, NoRecordingSpan, setActiveSpan, + setExtractedSpanContext, TraceState, } from '@opentelemetry/core'; -import { NoopScopeManager, ScopeManager } from '@opentelemetry/scope-base'; import * as assert from 'assert'; import { BasicTracerProvider, Span } from '../src'; @@ -36,41 +34,22 @@ describe('BasicTracerProvider', () => { assert.ok(provider instanceof BasicTracerProvider); }); - it('should construct an instance with binary format', () => { - const provider = new BasicTracerProvider({ - binaryFormat: new BinaryTraceContext(), - }); - assert.ok(provider instanceof BasicTracerProvider); - }); - - it('should construct an instance with http text format', () => { - const provider = new BasicTracerProvider({ - httpTextFormat: new HttpTraceContext(), - scopeManager: new NoopScopeManager(), - }); - assert.ok(provider instanceof BasicTracerProvider); - }); - it('should construct an instance with logger', () => { const provider = new BasicTracerProvider({ logger: new NoopLogger(), - scopeManager: new NoopScopeManager(), }); assert.ok(provider instanceof BasicTracerProvider); }); it('should construct an instance with sampler', () => { const provider = new BasicTracerProvider({ - scopeManager: new NoopScopeManager(), sampler: ALWAYS_SAMPLER, }); assert.ok(provider instanceof BasicTracerProvider); }); it('should construct an instance with default trace params', () => { - const tracer = new BasicTracerProvider({ - scopeManager: new NoopScopeManager(), - }).getTracer('default'); + const tracer = new BasicTracerProvider({}).getTracer('default'); assert.deepStrictEqual(tracer.getActiveTraceParams(), { numberOfAttributesPerSpan: 32, numberOfEventsPerSpan: 128, @@ -80,7 +59,6 @@ describe('BasicTracerProvider', () => { it('should construct an instance with customized numberOfAttributesPerSpan trace params', () => { const tracer = new BasicTracerProvider({ - scopeManager: new NoopScopeManager(), traceParams: { numberOfAttributesPerSpan: 100, }, @@ -94,7 +72,6 @@ describe('BasicTracerProvider', () => { it('should construct an instance with customized numberOfEventsPerSpan trace params', () => { const tracer = new BasicTracerProvider({ - scopeManager: new NoopScopeManager(), traceParams: { numberOfEventsPerSpan: 300, }, @@ -108,7 +85,6 @@ describe('BasicTracerProvider', () => { it('should construct an instance with customized numberOfLinksPerSpan trace params', () => { const tracer = new BasicTracerProvider({ - scopeManager: new NoopScopeManager(), traceParams: { numberOfLinksPerSpan: 10, }, @@ -184,13 +160,16 @@ describe('BasicTracerProvider', () => { it('should start a span with name and parent spancontext', () => { const tracer = new BasicTracerProvider().getTracer('default'); const state = new TraceState('a=1,b=2'); - const span = tracer.startSpan('my-span', { - parent: { + + const span = tracer.startSpan( + 'my-span', + {}, + setExtractedSpanContext(Context.ROOT_CONTEXT, { traceId: 'd4cda95b652f4a1592b449d5929fda1b', spanId: '6e0c63257de34c92', traceState: state, - }, - }); + }) + ); assert.ok(span instanceof Span); const context = span.context(); assert.strictEqual(context.traceId, 'd4cda95b652f4a1592b449d5929fda1b'); @@ -202,9 +181,11 @@ describe('BasicTracerProvider', () => { it('should start a span with name and parent span', () => { const tracer = new BasicTracerProvider().getTracer('default'); const span = tracer.startSpan('my-span'); - const childSpan = tracer.startSpan('child-span', { - parent: span, - }); + const childSpan = tracer.startSpan( + 'child-span', + {}, + setActiveSpan(Context.ROOT_CONTEXT, span) + ); const context = childSpan.context(); assert.strictEqual(context.traceId, span.context().traceId); assert.strictEqual(context.traceFlags, TraceFlags.SAMPLED); @@ -214,17 +195,28 @@ describe('BasicTracerProvider', () => { it('should start a span with name and with invalid parent span', () => { const tracer = new BasicTracerProvider().getTracer('default'); - const span = tracer.startSpan('my-span', { - parent: ('invalid-parent' as unknown) as undefined, - }) as Span; - assert.deepStrictEqual(span.parentSpanId, undefined); + const span = tracer.startSpan( + 'my-span', + {}, + setExtractedSpanContext( + Context.ROOT_CONTEXT, + ('invalid-parent' as unknown) as SpanContext + ) + ); + assert.ok(span instanceof Span); + assert.deepStrictEqual((span as Span).parentSpanId, undefined); }); it('should start a span with name and with invalid spancontext', () => { const tracer = new BasicTracerProvider().getTracer('default'); - const span = tracer.startSpan('my-span', { - parent: { traceId: '0', spanId: '0' }, - }); + const span = tracer.startSpan( + 'my-span', + {}, + setExtractedSpanContext(Context.ROOT_CONTEXT, { + traceId: '0', + spanId: '0', + }) + ); assert.ok(span instanceof Span); const context = span.context(); assert.ok(context.traceId.match(/[a-f0-9]{32}/)); @@ -283,7 +275,6 @@ describe('BasicTracerProvider', () => { it('should create real span when sampled and recording events true', () => { const tracer = new BasicTracerProvider({ sampler: ALWAYS_SAMPLER, - scopeManager: new NoopScopeManager(), }).getTracer('default'); const span = tracer.startSpan('my-span', { isRecording: true }); assert.ok(span instanceof Span); @@ -296,7 +287,6 @@ describe('BasicTracerProvider', () => { foo: 'bar', }; const tracer = new BasicTracerProvider({ - scopeManager: new NoopScopeManager(), defaultAttributes, }).getTracer('default'); @@ -305,60 +295,4 @@ describe('BasicTracerProvider', () => { assert.deepStrictEqual(span.attributes, defaultAttributes); }); }); - - describe('.getCurrentSpan()', () => { - it('should return null with NoopScopeManager', () => { - const tracer = new BasicTracerProvider().getTracer('default'); - const currentSpan = tracer.getCurrentSpan(); - assert.deepStrictEqual(currentSpan, undefined); - }); - - it('should return current span when it exists', () => { - const tracer = new BasicTracerProvider({ - scopeManager: { - active: () => - setActiveSpan(Context.ROOT_CONTEXT, ('foo' as any) as Span), - } as ScopeManager, - }).getTracer('default'); - assert.deepStrictEqual(tracer.getCurrentSpan(), 'foo'); - }); - }); - - describe('.withSpan()', () => { - it('should run scope with NoopScopeManager scope manager', done => { - const tracer = new BasicTracerProvider().getTracer('default'); - const span = tracer.startSpan('my-span'); - tracer.withSpan(span, () => { - assert.deepStrictEqual(tracer.getCurrentSpan(), undefined); - return done(); - }); - }); - }); - - describe('.bind()', () => { - it('should bind scope with NoopScopeManager scope manager', done => { - const tracer = new BasicTracerProvider().getTracer('default'); - const span = tracer.startSpan('my-span'); - const fn = () => { - assert.deepStrictEqual(tracer.getCurrentSpan(), undefined); - return done(); - }; - const patchedFn = tracer.bind(fn, span); - return patchedFn(); - }); - }); - - describe('.getBinaryFormat()', () => { - it('should get default binary formatter', () => { - const tracer = new BasicTracerProvider().getTracer('default'); - assert.ok(tracer.getBinaryFormat() instanceof BinaryTraceContext); - }); - }); - - describe('.getHttpTextFormat()', () => { - it('should get default HTTP text formatter', () => { - const tracer = new BasicTracerProvider().getTracer('default'); - assert.ok(tracer.getHttpTextFormat() instanceof HttpTraceContext); - }); - }); }); diff --git a/packages/opentelemetry-tracing/test/export/InMemorySpanExporter.test.ts b/packages/opentelemetry-tracing/test/export/InMemorySpanExporter.test.ts index e0e3766c8d..cfd215ff88 100644 --- a/packages/opentelemetry-tracing/test/export/InMemorySpanExporter.test.ts +++ b/packages/opentelemetry-tracing/test/export/InMemorySpanExporter.test.ts @@ -21,6 +21,8 @@ import { BasicTracerProvider, } from '../../src'; import { ExportResult } from '@opentelemetry/base'; +import { context } from '@opentelemetry/api'; +import { setActiveSpan } from '@opentelemetry/core'; describe('InMemorySpanExporter', () => { const memoryExporter = new InMemorySpanExporter(); @@ -36,10 +38,10 @@ describe('InMemorySpanExporter', () => { const root = provider.getTracer('default').startSpan('root'); const child = provider .getTracer('default') - .startSpan('child', { parent: root }); + .startSpan('child', {}, setActiveSpan(context.active(), root)); const grandChild = provider .getTracer('default') - .startSpan('grand-child', { parent: child }); + .startSpan('grand-child', {}, setActiveSpan(context.active(), child)); assert.strictEqual(memoryExporter.getFinishedSpans().length, 0); grandChild.end(); @@ -59,11 +61,12 @@ describe('InMemorySpanExporter', () => { assert.strictEqual(span2.parentSpanId, span3.spanContext.spanId); }); - it('should shutdown the exorter', () => { + it('should shutdown the exporter', () => { const root = provider.getTracer('default').startSpan('root'); + provider .getTracer('default') - .startSpan('child', { parent: root }) + .startSpan('child', {}, setActiveSpan(context.active(), root)) .end(); root.end(); assert.strictEqual(memoryExporter.getFinishedSpans().length, 2); @@ -73,7 +76,7 @@ describe('InMemorySpanExporter', () => { // after shutdown no new spans are accepted provider .getTracer('default') - .startSpan('child1', { parent: root }) + .startSpan('child1', {}, setActiveSpan(context.active(), root)) .end(); assert.strictEqual(memoryExporter.getFinishedSpans().length, 0); }); diff --git a/packages/opentelemetry-web/src/WebTracerProvider.ts b/packages/opentelemetry-web/src/WebTracerProvider.ts index 831fbc475f..92d2bba29d 100644 --- a/packages/opentelemetry-web/src/WebTracerProvider.ts +++ b/packages/opentelemetry-web/src/WebTracerProvider.ts @@ -16,7 +16,6 @@ import { BasePlugin } from '@opentelemetry/core'; import { BasicTracerProvider, TracerConfig } from '@opentelemetry/tracing'; -import { StackScopeManager } from './StackScopeManager'; /** * WebTracerConfig provides an interface for configuring a Web Tracer. @@ -37,15 +36,10 @@ export class WebTracerProvider extends BasicTracerProvider { * @param config Web Tracer config */ constructor(config: WebTracerConfig = {}) { - if (typeof config.scopeManager === 'undefined') { - config.scopeManager = new StackScopeManager(); - } if (typeof config.plugins === 'undefined') { config.plugins = []; } - super(Object.assign({}, { scopeManager: config.scopeManager }, config)); - - config.scopeManager.enable(); + super(Object.assign({}, config)); for (const plugin of config.plugins) { plugin.enable([], this, this.logger); diff --git a/packages/opentelemetry-web/test/WebTracerProvider.test.ts b/packages/opentelemetry-web/test/WebTracerProvider.test.ts index 266cd119ae..2898ef784f 100644 --- a/packages/opentelemetry-web/test/WebTracerProvider.test.ts +++ b/packages/opentelemetry-web/test/WebTracerProvider.test.ts @@ -14,13 +14,14 @@ * limitations under the License. */ -import { BasePlugin } from '@opentelemetry/core'; +import { context } from '@opentelemetry/api'; +import { BasePlugin, setActiveSpan } from '@opentelemetry/core'; +import { ScopeManager } from '@opentelemetry/scope-base'; import { ZoneScopeManager } from '@opentelemetry/scope-zone'; -import { Tracer, TracerConfig } from '@opentelemetry/tracing'; +import { Tracer } from '@opentelemetry/tracing'; import * as assert from 'assert'; import * as sinon from 'sinon'; import { WebTracerConfig } from '../src'; -import { StackScopeManager } from '../src/StackScopeManager'; import { WebTracerProvider } from '../src/WebTracerProvider'; class DummyPlugin extends BasePlugin { @@ -36,11 +37,16 @@ class DummyPlugin extends BasePlugin { describe('WebTracerProvider', () => { describe('constructor', () => { let defaultOptions: WebTracerConfig; + let scopeManager: ScopeManager; beforeEach(() => { - defaultOptions = { - scopeManager: new StackScopeManager(), - }; + defaultOptions = {}; + scopeManager = new ZoneScopeManager().enable(); + context.initGlobalContextManager(scopeManager); + }); + + afterEach(() => { + scopeManager.disable(); }); it('should construct an instance with required only options', () => { @@ -50,17 +56,6 @@ describe('WebTracerProvider', () => { assert.ok(tracer instanceof Tracer); }); - it('should enable the scope manager', () => { - let options: TracerConfig; - const scopeManager = new StackScopeManager(); - options = { scopeManager }; - - const spy = sinon.spy(scopeManager, 'enable'); - new WebTracerProvider(options); - - assert.ok(spy.calledOnce === true); - }); - it('should enable all plugins', () => { let options: WebTracerConfig; const dummyPlugin1 = new DummyPlugin(); @@ -68,11 +63,9 @@ describe('WebTracerProvider', () => { const spyEnable1 = sinon.spy(dummyPlugin1, 'enable'); const spyEnable2 = sinon.spy(dummyPlugin2, 'enable'); - const scopeManager = new StackScopeManager(); - const plugins = [dummyPlugin1, dummyPlugin2]; - options = { plugins, scopeManager }; + options = { plugins }; new WebTracerProvider(options); assert.ok(spyEnable1.calledOnce === true); @@ -87,13 +80,11 @@ describe('WebTracerProvider', () => { describe('when scopeManager is "ZoneScopeManager"', () => { it('should correctly return the scopes for 2 parallel actions', () => { - const webTracerWithZone = new WebTracerProvider({ - scopeManager: new ZoneScopeManager(), - }).getTracer('default'); + const webTracerWithZone = new WebTracerProvider().getTracer('default'); const rootSpan = webTracerWithZone.startSpan('rootSpan'); - webTracerWithZone.withSpan(rootSpan, () => { + context.with(setActiveSpan(context.active(), rootSpan), () => { assert.ok( webTracerWithZone.getCurrentSpan() === rootSpan, 'Current span is rootSpan' @@ -105,7 +96,7 @@ describe('WebTracerProvider', () => { 'concurrentSpan2' ); - webTracerWithZone.withSpan(concurrentSpan1, () => { + context.with(setActiveSpan(context.active(), concurrentSpan1), () => { setTimeout(() => { assert.ok( webTracerWithZone.getCurrentSpan() === concurrentSpan1, @@ -114,7 +105,7 @@ describe('WebTracerProvider', () => { }, 10); }); - webTracerWithZone.withSpan(concurrentSpan2, () => { + context.with(setActiveSpan(context.active(), concurrentSpan2), () => { setTimeout(() => { assert.ok( webTracerWithZone.getCurrentSpan() === concurrentSpan2, From acdae622138c1ae3227936521c82f40d1fb4730b Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Wed, 26 Feb 2020 11:01:23 -0500 Subject: [PATCH 02/15] chore: use withSpan where possible --- .../opentelemetry-node/test/NodeTracerProvider.test.ts | 8 ++++---- .../src/documentLoad.ts | 5 ++--- packages/opentelemetry-plugin-xml-http-request/src/xhr.ts | 3 +-- packages/opentelemetry-web/test/WebTracerProvider.test.ts | 6 +++--- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/opentelemetry-node/test/NodeTracerProvider.test.ts b/packages/opentelemetry-node/test/NodeTracerProvider.test.ts index 0fc598d50e..e5675c9ee0 100644 --- a/packages/opentelemetry-node/test/NodeTracerProvider.test.ts +++ b/packages/opentelemetry-node/test/NodeTracerProvider.test.ts @@ -174,7 +174,7 @@ describe('NodeTracerProvider', () => { it('should run scope with AsyncHooksScopeManager scope manager', done => { provider = new NodeTracerProvider({}); const span = provider.getTracer('default').startSpan('my-span'); - context.with(setActiveSpan(context.active(), span), () => { + provider.getTracer('default').withSpan(span, () => { assert.deepStrictEqual( provider.getTracer('default').getCurrentSpan(), span @@ -190,7 +190,7 @@ describe('NodeTracerProvider', () => { it('should run scope with AsyncHooksScopeManager scope manager with multiple spans', done => { provider = new NodeTracerProvider({}); const span = provider.getTracer('default').startSpan('my-span'); - context.with(setActiveSpan(context.active(), span), () => { + provider.getTracer('default').withSpan(span, () => { assert.deepStrictEqual( provider.getTracer('default').getCurrentSpan(), span @@ -198,7 +198,7 @@ describe('NodeTracerProvider', () => { const span1 = provider.getTracer('default').startSpan('my-span1'); - context.with(setActiveSpan(context.active(), span1), () => { + provider.getTracer('default').withSpan(span1, () => { assert.deepStrictEqual( provider.getTracer('default').getCurrentSpan(), span1 @@ -221,7 +221,7 @@ describe('NodeTracerProvider', () => { it('should find correct scope with promises', async () => { provider = new NodeTracerProvider(); const span = provider.getTracer('default').startSpan('my-span'); - await context.with(setActiveSpan(context.active(), span), async () => { + await provider.getTracer('default').withSpan(span, async () => { for (let i = 0; i < 3; i++) { await sleep(5).then(() => { assert.deepStrictEqual( diff --git a/packages/opentelemetry-plugin-document-load/src/documentLoad.ts b/packages/opentelemetry-plugin-document-load/src/documentLoad.ts index f60b6aee3f..69b2366114 100644 --- a/packages/opentelemetry-plugin-document-load/src/documentLoad.ts +++ b/packages/opentelemetry-plugin-document-load/src/documentLoad.ts @@ -24,7 +24,6 @@ import { import { BasePlugin, otperformance, - setActiveSpan, TRACE_PARENT_HEADER, } from '@opentelemetry/core'; import { @@ -119,14 +118,14 @@ export class DocumentLoad extends BasePlugin { if (!rootSpan) { return; } - context.with(setActiveSpan(context.active(), rootSpan), () => { + this._tracer.withSpan(rootSpan, () => { const fetchSpan = this._startSpan( AttributeNames.DOCUMENT_FETCH, PTN.FETCH_START, entries ); if (fetchSpan) { - context.with(setActiveSpan(context.active(), fetchSpan), () => { + this._tracer.withSpan(fetchSpan, () => { this._addSpanNetworkEvents(fetchSpan, entries); this._endSpan(fetchSpan, PTN.RESPONSE_END, entries); }); diff --git a/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts b/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts index 5ab4c02bbd..3886f257e0 100644 --- a/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts +++ b/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts @@ -22,7 +22,6 @@ import { isWrapped, otperformance, urlMatches, - setActiveSpan, } from '@opentelemetry/core'; import { addSpanNetworkEvent, @@ -133,7 +132,7 @@ export class XMLHttpRequestPlugin extends BasePlugin { span: api.Span, corsPreFlightRequest: PerformanceResourceTiming ): void { - api.context.with(setActiveSpan(api.context.active(), span), () => { + this._tracer.withSpan(span, () => { const childSpan = this._tracer.startSpan('CORS Preflight', { startTime: corsPreFlightRequest[PTN.FETCH_START], }); diff --git a/packages/opentelemetry-web/test/WebTracerProvider.test.ts b/packages/opentelemetry-web/test/WebTracerProvider.test.ts index 2898ef784f..461daa182f 100644 --- a/packages/opentelemetry-web/test/WebTracerProvider.test.ts +++ b/packages/opentelemetry-web/test/WebTracerProvider.test.ts @@ -84,7 +84,7 @@ describe('WebTracerProvider', () => { const rootSpan = webTracerWithZone.startSpan('rootSpan'); - context.with(setActiveSpan(context.active(), rootSpan), () => { + webTracerWithZone.withSpan(rootSpan, () => { assert.ok( webTracerWithZone.getCurrentSpan() === rootSpan, 'Current span is rootSpan' @@ -96,7 +96,7 @@ describe('WebTracerProvider', () => { 'concurrentSpan2' ); - context.with(setActiveSpan(context.active(), concurrentSpan1), () => { + webTracerWithZone.withSpan(concurrentSpan1, () => { setTimeout(() => { assert.ok( webTracerWithZone.getCurrentSpan() === concurrentSpan1, @@ -105,7 +105,7 @@ describe('WebTracerProvider', () => { }, 10); }); - context.with(setActiveSpan(context.active(), concurrentSpan2), () => { + webTracerWithZone.withSpan(concurrentSpan2, () => { setTimeout(() => { assert.ok( webTracerWithZone.getCurrentSpan() === concurrentSpan2, From a46ed3dfbc462ad991ffe1e4cce9774247b2313f Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Wed, 26 Feb 2020 11:17:55 -0500 Subject: [PATCH 03/15] chore: simplify and fix tests --- .../src/express.ts | 14 +++++------ .../opentelemetry-plugin-http/src/http.ts | 24 +++++++++---------- .../test/WebTracerProvider.test.ts | 2 +- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/opentelemetry-plugin-express/src/express.ts b/packages/opentelemetry-plugin-express/src/express.ts index c9ff72f979..7800cf67dc 100644 --- a/packages/opentelemetry-plugin-express/src/express.ts +++ b/packages/opentelemetry-plugin-express/src/express.ts @@ -14,27 +14,27 @@ * limitations under the License. */ -import { Attributes } from '@opentelemetry/api'; import { BasePlugin } from '@opentelemetry/core'; +import { Attributes } from '@opentelemetry/api'; import * as express from 'express'; import * as core from 'express-serve-static-core'; import * as shimmer from 'shimmer'; import { - AttributeNames, ExpressLayer, - ExpressLayerType, - ExpressPluginConfig, ExpressRouter, - Parameters, + AttributeNames, PatchedRequest, + Parameters, PathParams, _LAYERS_STORE_PROPERTY, + ExpressPluginConfig, + ExpressLayerType, } from './types'; import { getLayerMetadata, - isLayerIgnored, - patchEnd, storeLayerPath, + patchEnd, + isLayerIgnored, } from './utils'; import { VERSION } from './version'; diff --git a/packages/opentelemetry-plugin-http/src/http.ts b/packages/opentelemetry-plugin-http/src/http.ts index faa5ab63c3..d1d0c66324 100644 --- a/packages/opentelemetry-plugin-http/src/http.ts +++ b/packages/opentelemetry-plugin-http/src/http.ts @@ -375,15 +375,15 @@ export class HttpPlugin extends BasePlugin { (typeof options === 'string' || options instanceof url.URL) ? (args.shift() as RequestOptions) : undefined; - const { origin, pathname, method, optionsParsed } = utils.getRequestInfo( - options, - extraOptions - ); - - const requestOptions = optionsParsed; + const { + origin, + pathname, + method, + optionsParsed, + } = utils.getRequestInfo(options, extraOptions); if ( - utils.isOpenTelemetryRequest(requestOptions) || + utils.isOpenTelemetryRequest(optionsParsed) || utils.isIgnored( origin + pathname, plugin._config.ignoreOutgoingUrls, @@ -391,7 +391,7 @@ export class HttpPlugin extends BasePlugin { plugin._logger.error('caught ignoreOutgoingUrls error: ', e) ) ) { - return original.apply(this, [requestOptions, ...args]); + return original.apply(this, [optionsParsed, ...args]); } const operationName = `${method} ${pathname}`; @@ -402,18 +402,18 @@ export class HttpPlugin extends BasePlugin { const span = plugin._startHttpSpan(operationName, spanOptions); return plugin._tracer.withSpan(span, () => { - if (!requestOptions.headers) requestOptions.headers = {}; - propagation.inject(requestOptions.headers); + if (!optionsParsed.headers) optionsParsed.headers = {}; + propagation.inject(optionsParsed.headers); const request: ClientRequest = plugin._safeExecute( span, - () => original.apply(this, [requestOptions, ...args]), + () => original.apply(this, [optionsParsed, ...args]), true ); plugin._logger.debug('%s plugin outgoingRequest', plugin.moduleName); plugin._tracer.bind(request); - return plugin._traceClientRequest(request, requestOptions, span); + return plugin._traceClientRequest(request, optionsParsed, span); }); }; } diff --git a/packages/opentelemetry-web/test/WebTracerProvider.test.ts b/packages/opentelemetry-web/test/WebTracerProvider.test.ts index 461daa182f..63e1f1cb03 100644 --- a/packages/opentelemetry-web/test/WebTracerProvider.test.ts +++ b/packages/opentelemetry-web/test/WebTracerProvider.test.ts @@ -15,7 +15,7 @@ */ import { context } from '@opentelemetry/api'; -import { BasePlugin, setActiveSpan } from '@opentelemetry/core'; +import { BasePlugin } from '@opentelemetry/core'; import { ScopeManager } from '@opentelemetry/scope-base'; import { ZoneScopeManager } from '@opentelemetry/scope-zone'; import { Tracer } from '@opentelemetry/tracing'; From 792ea3fe1376df9497684d61ed1bc5aad63f8b82 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Wed, 26 Feb 2020 14:10:46 -0500 Subject: [PATCH 04/15] chore: lint --- packages/opentelemetry-plugin-http/src/http.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/opentelemetry-plugin-http/src/http.ts b/packages/opentelemetry-plugin-http/src/http.ts index d1d0c66324..826143e8ae 100644 --- a/packages/opentelemetry-plugin-http/src/http.ts +++ b/packages/opentelemetry-plugin-http/src/http.ts @@ -375,12 +375,10 @@ export class HttpPlugin extends BasePlugin { (typeof options === 'string' || options instanceof url.URL) ? (args.shift() as RequestOptions) : undefined; - const { - origin, - pathname, - method, - optionsParsed, - } = utils.getRequestInfo(options, extraOptions); + const { origin, pathname, method, optionsParsed } = utils.getRequestInfo( + options, + extraOptions + ); if ( utils.isOpenTelemetryRequest(optionsParsed) || From fa9428bf02ba3a48ee40ea5946485b53c26a73b5 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Wed, 26 Feb 2020 14:35:34 -0500 Subject: [PATCH 05/15] chore: propagate context in tests --- .../opentelemetry-plugin-ioredis/package.json | 1 + .../test/ioredis.test.ts | 23 ++++++++++---- .../opentelemetry-plugin-mongodb/package.json | 1 + .../test/mongodb.test.ts | 19 +++++++----- .../opentelemetry-plugin-mysql/package.json | 1 + .../test/mysql.test.ts | 11 +++++-- .../opentelemetry-plugin-pg-pool/package.json | 1 + .../test/pg-pool.test.ts | 30 +++++++++++-------- .../opentelemetry-plugin-pg/package.json | 1 + .../opentelemetry-plugin-pg/test/pg.test.ts | 28 ++++++++++------- .../opentelemetry-plugin-redis/package.json | 1 + .../test/redis.test.ts | 23 ++++++++++---- 12 files changed, 95 insertions(+), 45 deletions(-) diff --git a/packages/opentelemetry-plugin-ioredis/package.json b/packages/opentelemetry-plugin-ioredis/package.json index 320d8cd829..7e1b5d31cf 100644 --- a/packages/opentelemetry-plugin-ioredis/package.json +++ b/packages/opentelemetry-plugin-ioredis/package.json @@ -45,6 +45,7 @@ }, "devDependencies": { "@opentelemetry/node": "^0.4.0", + "@opentelemetry/scope-async-hooks": "^0.4.0", "@opentelemetry/test-utils": "^0.4.0", "@opentelemetry/tracing": "^0.4.0", "@types/ioredis": "^4.14.3", diff --git a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts index 5dca23af19..0b5f6b1930 100644 --- a/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts +++ b/packages/opentelemetry-plugin-ioredis/test/ioredis.test.ts @@ -14,17 +14,18 @@ * limitations under the License. */ -import * as assert from 'assert'; +import { CanonicalCode, context, SpanKind, Status } from '@opentelemetry/api'; +import { NoopLogger } from '@opentelemetry/core'; +import { NodeTracerProvider } from '@opentelemetry/node'; +import { AsyncHooksScopeManager } from '@opentelemetry/scope-async-hooks'; +import * as testUtils from '@opentelemetry/test-utils'; import { InMemorySpanExporter, SimpleSpanProcessor, } from '@opentelemetry/tracing'; -import { NodeTracerProvider } from '@opentelemetry/node'; -import { plugin, IORedisPlugin } from '../src'; +import * as assert from 'assert'; import * as ioredisTypes from 'ioredis'; -import { NoopLogger } from '@opentelemetry/core'; -import * as testUtils from '@opentelemetry/test-utils'; -import { SpanKind, Status, CanonicalCode } from '@opentelemetry/api'; +import { IORedisPlugin, plugin } from '../src'; import { AttributeNames } from '../src/enums'; const memoryExporter = new InMemorySpanExporter(); @@ -54,6 +55,16 @@ describe('ioredis', () => { const shouldTestLocal = process.env.RUN_REDIS_TESTS_LOCAL; const shouldTest = process.env.RUN_REDIS_TESTS || shouldTestLocal; + let scopeManager: AsyncHooksScopeManager; + beforeEach(() => { + scopeManager = new AsyncHooksScopeManager().enable(); + context.initGlobalContextManager(scopeManager); + }); + + afterEach(() => { + scopeManager.disable(); + }); + before(function() { // needs to be "function" to have MochaContext "this" scope if (!shouldTest) { diff --git a/packages/opentelemetry-plugin-mongodb/package.json b/packages/opentelemetry-plugin-mongodb/package.json index a88514d2bb..800f9824d5 100644 --- a/packages/opentelemetry-plugin-mongodb/package.json +++ b/packages/opentelemetry-plugin-mongodb/package.json @@ -42,6 +42,7 @@ }, "devDependencies": { "@opentelemetry/node": "^0.4.0", + "@opentelemetry/scope-async-hooks": "^0.4.0", "@opentelemetry/tracing": "^0.4.0", "@types/mocha": "^5.2.7", "@types/mongodb": "^3.2.3", diff --git a/packages/opentelemetry-plugin-mongodb/test/mongodb.test.ts b/packages/opentelemetry-plugin-mongodb/test/mongodb.test.ts index 8ad83efe58..effe65cf20 100644 --- a/packages/opentelemetry-plugin-mongodb/test/mongodb.test.ts +++ b/packages/opentelemetry-plugin-mongodb/test/mongodb.test.ts @@ -14,18 +14,19 @@ * limitations under the License. */ -import { NodeTracerProvider } from '@opentelemetry/node'; -import * as assert from 'assert'; -import * as mongodb from 'mongodb'; -import { plugin } from '../src'; -import { SpanKind, CanonicalCode } from '@opentelemetry/api'; +import { CanonicalCode, context, SpanKind } from '@opentelemetry/api'; import { NoopLogger } from '@opentelemetry/core'; -import { AttributeNames } from '../src/types'; +import { NodeTracerProvider } from '@opentelemetry/node'; +import { AsyncHooksScopeManager } from '@opentelemetry/scope-async-hooks'; import { InMemorySpanExporter, - SimpleSpanProcessor, ReadableSpan, + SimpleSpanProcessor, } from '@opentelemetry/tracing'; +import * as assert from 'assert'; +import * as mongodb from 'mongodb'; +import { plugin } from '../src'; +import { AttributeNames } from '../src/types'; interface MongoDBAccess { client: mongodb.MongoClient; @@ -102,6 +103,7 @@ describe('MongoDBPlugin', () => { const DB_NAME = process.env.MONGODB_DB || 'opentelemetry-tests'; const COLLECTION_NAME = 'test'; + let scopeManager: AsyncHooksScopeManager; let client: mongodb.MongoClient; let collection: mongodb.Collection; const logger = new NoopLogger(); @@ -140,10 +142,13 @@ describe('MongoDBPlugin', () => { collection.insertMany(insertData, (err, result) => { done(); }); + scopeManager = new AsyncHooksScopeManager().enable(); + context.initGlobalContextManager(scopeManager); }); afterEach(done => { collection.deleteOne({}, done); + scopeManager.disable(); }); after(() => { diff --git a/packages/opentelemetry-plugin-mysql/package.json b/packages/opentelemetry-plugin-mysql/package.json index 63d75df0b5..577b84b1d6 100644 --- a/packages/opentelemetry-plugin-mysql/package.json +++ b/packages/opentelemetry-plugin-mysql/package.json @@ -42,6 +42,7 @@ }, "devDependencies": { "@opentelemetry/node": "^0.4.0", + "@opentelemetry/scope-async-hooks": "^0.4.0", "@opentelemetry/test-utils": "^0.4.0", "@opentelemetry/tracing": "^0.4.0", "@types/mocha": "^5.2.7", diff --git a/packages/opentelemetry-plugin-mysql/test/mysql.test.ts b/packages/opentelemetry-plugin-mysql/test/mysql.test.ts index 42683793f5..617bee06af 100644 --- a/packages/opentelemetry-plugin-mysql/test/mysql.test.ts +++ b/packages/opentelemetry-plugin-mysql/test/mysql.test.ts @@ -14,19 +14,20 @@ * limitations under the License. */ +import { CanonicalCode, context } from '@opentelemetry/api'; import { NoopLogger } from '@opentelemetry/core'; import { NodeTracerProvider } from '@opentelemetry/node'; +import { AsyncHooksScopeManager } from '@opentelemetry/scope-async-hooks'; +import * as testUtils from '@opentelemetry/test-utils'; import { InMemorySpanExporter, - SimpleSpanProcessor, ReadableSpan, + SimpleSpanProcessor, } from '@opentelemetry/tracing'; import * as assert from 'assert'; import * as mysql from 'mysql'; import { MysqlPlugin, plugin } from '../src'; -import * as testUtils from '@opentelemetry/test-utils'; import { AttributeNames } from '../src/enums'; -import { CanonicalCode } from '@opentelemetry/api'; const port = parseInt(process.env.MYSQL_PORT || '33306', 10); const database = process.env.MYSQL_DATABASE || 'test_db'; @@ -35,6 +36,7 @@ const user = process.env.MYSQL_USER || 'otel'; const password = process.env.MYSQL_PASSWORD || 'secret'; describe('mysql@2.x', () => { + let scopeManager: AsyncHooksScopeManager; let connection: mysql.Connection; let pool: mysql.Pool; let poolCluster: mysql.PoolCluster; @@ -71,6 +73,8 @@ describe('mysql@2.x', () => { }); beforeEach(function() { + scopeManager = new AsyncHooksScopeManager().enable(); + context.initGlobalContextManager(scopeManager); plugin.enable(mysql, provider, logger); connection = mysql.createConnection({ port, @@ -97,6 +101,7 @@ describe('mysql@2.x', () => { }); afterEach(done => { + scopeManager.disable(); memoryExporter.reset(); plugin.disable(); connection.end(() => { diff --git a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/package.json b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/package.json index ca17610db5..d03e4ceeb9 100644 --- a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/package.json +++ b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/package.json @@ -47,6 +47,7 @@ }, "devDependencies": { "@opentelemetry/plugin-pg": "^0.4.0", + "@opentelemetry/scope-async-hooks": "^0.4.0", "@opentelemetry/test-utils": "^0.4.0", "@types/mocha": "^5.2.7", "@types/node": "^12.6.9", diff --git a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/test/pg-pool.test.ts b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/test/pg-pool.test.ts index 7da3cfd58f..fe6ed7951a 100644 --- a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/test/pg-pool.test.ts +++ b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/test/pg-pool.test.ts @@ -14,27 +14,29 @@ * limitations under the License. */ -import { NoopLogger } from '@opentelemetry/core'; -import { NodeTracerProvider } from '@opentelemetry/node'; -import { - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/tracing'; import { - SpanKind, Attributes, - TimedEvent, - Span, CanonicalCode, + context, + Span, + SpanKind, Status, + TimedEvent, } from '@opentelemetry/api'; +import { NoopLogger } from '@opentelemetry/core'; +import { NodeTracerProvider } from '@opentelemetry/node'; import { plugin as pgPlugin, PostgresPlugin } from '@opentelemetry/plugin-pg'; -import { plugin, PostgresPoolPlugin } from '../src'; -import { AttributeNames } from '../src/enums'; +import { AsyncHooksScopeManager } from '@opentelemetry/scope-async-hooks'; +import * as testUtils from '@opentelemetry/test-utils'; +import { + InMemorySpanExporter, + SimpleSpanProcessor, +} from '@opentelemetry/tracing'; import * as assert from 'assert'; import * as pg from 'pg'; import * as pgPool from 'pg-pool'; -import * as testUtils from '@opentelemetry/test-utils'; +import { plugin, PostgresPoolPlugin } from '../src'; +import { AttributeNames } from '../src/enums'; const memoryExporter = new InMemorySpanExporter(); @@ -92,6 +94,7 @@ const runCallbackTest = ( describe('pg-pool@2.x', () => { let pool: pgPool; + let scopeManager: AsyncHooksScopeManager; const provider = new NodeTracerProvider(); const logger = new NoopLogger(); const testPostgres = process.env.RUN_POSTGRES_TESTS; // For CI: assumes local postgres db is already available @@ -125,12 +128,15 @@ describe('pg-pool@2.x', () => { beforeEach(function() { plugin.enable(pgPool, provider, logger); pgPlugin.enable(pg, provider, logger); + scopeManager = new AsyncHooksScopeManager().enable(); + context.initGlobalContextManager(scopeManager); }); afterEach(() => { memoryExporter.reset(); plugin.disable(); pgPlugin.disable(); + scopeManager.disable(); }); it('should return a plugin', () => { diff --git a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/package.json b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/package.json index 56ad863f40..fa3b18a722 100644 --- a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/package.json +++ b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/package.json @@ -46,6 +46,7 @@ }, "devDependencies": { "@opentelemetry/node": "^0.4.0", + "@opentelemetry/scope-async-hooks": "^0.4.0", "@opentelemetry/test-utils": "^0.4.0", "@opentelemetry/tracing": "^0.4.0", "@types/mocha": "^5.2.7", diff --git a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/test/pg.test.ts b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/test/pg.test.ts index d4ff201de2..d3392c7ac9 100644 --- a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/test/pg.test.ts +++ b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/test/pg.test.ts @@ -14,25 +14,27 @@ * limitations under the License. */ +import { + Attributes, + CanonicalCode, + context, + Span, + SpanKind, + Status, + TimedEvent, +} from '@opentelemetry/api'; import { NoopLogger } from '@opentelemetry/core'; import { NodeTracerProvider } from '@opentelemetry/node'; +import { AsyncHooksScopeManager } from '@opentelemetry/scope-async-hooks'; +import * as testUtils from '@opentelemetry/test-utils'; import { InMemorySpanExporter, SimpleSpanProcessor, } from '@opentelemetry/tracing'; -import { - SpanKind, - Attributes, - TimedEvent, - Span, - CanonicalCode, - Status, -} from '@opentelemetry/api'; -import { plugin, PostgresPlugin } from '../src'; -import { AttributeNames } from '../src/enums'; import * as assert from 'assert'; import * as pg from 'pg'; -import * as testUtils from '@opentelemetry/test-utils'; +import { plugin, PostgresPlugin } from '../src'; +import { AttributeNames } from '../src/enums'; const memoryExporter = new InMemorySpanExporter(); @@ -81,6 +83,7 @@ const runCallbackTest = ( describe('pg@7.x', () => { let client: pg.Client; + let scopeManager: AsyncHooksScopeManager; const provider = new NodeTracerProvider(); const tracer = provider.getTracer('external'); const logger = new NoopLogger(); @@ -117,11 +120,14 @@ describe('pg@7.x', () => { beforeEach(function() { plugin.enable(pg, provider, logger); + scopeManager = new AsyncHooksScopeManager().enable(); + context.initGlobalContextManager(scopeManager); }); afterEach(() => { memoryExporter.reset(); plugin.disable(); + scopeManager.disable(); }); it('should return a plugin', () => { diff --git a/packages/opentelemetry-plugin-redis/package.json b/packages/opentelemetry-plugin-redis/package.json index 0b64a6a6fb..14cfe0cae4 100644 --- a/packages/opentelemetry-plugin-redis/package.json +++ b/packages/opentelemetry-plugin-redis/package.json @@ -44,6 +44,7 @@ }, "devDependencies": { "@opentelemetry/node": "^0.4.0", + "@opentelemetry/scope-async-hooks": "^0.4.0", "@opentelemetry/test-utils": "^0.4.0", "@opentelemetry/tracing": "^0.4.0", "@types/mocha": "^5.2.7", diff --git a/packages/opentelemetry-plugin-redis/test/redis.test.ts b/packages/opentelemetry-plugin-redis/test/redis.test.ts index e991c733a0..a5cbe96346 100644 --- a/packages/opentelemetry-plugin-redis/test/redis.test.ts +++ b/packages/opentelemetry-plugin-redis/test/redis.test.ts @@ -14,17 +14,18 @@ * limitations under the License. */ -import * as assert from 'assert'; +import { CanonicalCode, context, SpanKind, Status } from '@opentelemetry/api'; +import { NoopLogger } from '@opentelemetry/core'; +import { NodeTracerProvider } from '@opentelemetry/node'; +import { AsyncHooksScopeManager } from '@opentelemetry/scope-async-hooks'; +import * as testUtils from '@opentelemetry/test-utils'; import { InMemorySpanExporter, SimpleSpanProcessor, } from '@opentelemetry/tracing'; -import { NodeTracerProvider } from '@opentelemetry/node'; -import { plugin, RedisPlugin } from '../src'; +import * as assert from 'assert'; import * as redisTypes from 'redis'; -import { NoopLogger } from '@opentelemetry/core'; -import * as testUtils from '@opentelemetry/test-utils'; -import { SpanKind, Status, CanonicalCode } from '@opentelemetry/api'; +import { plugin, RedisPlugin } from '../src'; import { AttributeNames } from '../src/enums'; const memoryExporter = new InMemorySpanExporter(); @@ -54,6 +55,16 @@ describe('redis@2.x', () => { const shouldTestLocal = process.env.RUN_REDIS_TESTS_LOCAL; const shouldTest = process.env.RUN_REDIS_TESTS || shouldTestLocal; + let scopeManager: AsyncHooksScopeManager; + beforeEach(() => { + scopeManager = new AsyncHooksScopeManager().enable(); + context.initGlobalContextManager(scopeManager); + }); + + afterEach(() => { + scopeManager.disable(); + }); + before(function() { // needs to be "function" to have MochaContext "this" scope if (!shouldTest) { From f008593848bc75862d0fe5ace2a034f4e656158a Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Wed, 26 Feb 2020 16:29:07 -0500 Subject: [PATCH 06/15] chore: fix pg tests --- .../opentelemetry-plugin-pg/test/pg.test.ts | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/test/pg.test.ts b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/test/pg.test.ts index d3392c7ac9..2d4c9dc589 100644 --- a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/test/pg.test.ts +++ b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/test/pg.test.ts @@ -365,15 +365,18 @@ describe('pg@7.x', () => { it('should handle the same callback being given to multiple client.query()s', done => { let events = 0; + const parent = tracer.startSpan('parent'); - const queryHandler = (err: Error, res: pg.QueryResult) => { + const queryHandler = (err?: Error, res?: pg.QueryResult) => { const span = tracer.getCurrentSpan(); - assert.ok(span); - assert.strictEqual((span as any)['_ended'], false); + assert.deepStrictEqual(span!.context(), parent.context()); if (err) { throw err; } events += 1; + if (events === 7) { + done(); + } }; const config = { @@ -381,16 +384,17 @@ describe('pg@7.x', () => { callback: queryHandler, }; - client.query(config.text, config.callback); // 1 - client.query(config); // 2 - client.query(config.text, queryHandler); // 3 - client.query(config.text, queryHandler); // 4 - client.query(config.text); // Not using queryHandler - client.query(config); // 5 - client.query(config); // 6 - client.query(config.text, (err, res) => { - assert.strictEqual(events, 6); - done(); + tracer.withSpan(parent, () => { + client.query(config.text, config.callback); // 1 + client.query(config); // 2 + client.query(config.text, queryHandler); // 3 + client.query(config.text, queryHandler); // 4 + client + .query(config.text) + .then(result => queryHandler(undefined, result)) + .catch(err => queryHandler(err)); // 5 + client.query(config); // 6 + client.query(config); // 7 }); }); From d2a93a7c46c86a7b13e3ab4d1c04d392e997ce23 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Wed, 26 Feb 2020 18:27:08 -0500 Subject: [PATCH 07/15] chore: fix browser tests --- .../src/documentLoad.ts | 74 +++++++++---------- .../test/documentLoad.test.ts | 27 ++++++- .../test/userInteraction.nozone.test.ts | 13 +++- .../test/userInteraction.test.ts | 15 ++-- .../src/xhr.ts | 38 +++++----- .../test/xhr.test.ts | 19 ++--- 6 files changed, 104 insertions(+), 82 deletions(-) diff --git a/packages/opentelemetry-plugin-document-load/src/documentLoad.ts b/packages/opentelemetry-plugin-document-load/src/documentLoad.ts index 69b2366114..666d11c061 100644 --- a/packages/opentelemetry-plugin-document-load/src/documentLoad.ts +++ b/packages/opentelemetry-plugin-document-load/src/documentLoad.ts @@ -105,54 +105,46 @@ export class DocumentLoad extends BasePlugin { ); const entries = this._getEntries(); - context.with( - propagation.extract({ - traceparent: (metaElement && metaElement.content) || '', - }), - () => { - const rootSpan = this._startSpan( - AttributeNames.DOCUMENT_LOAD, + const traceparent = (metaElement && metaElement.content) || ''; + context.with(propagation.extract({ traceparent }), () => { + const rootSpan = this._startSpan( + AttributeNames.DOCUMENT_LOAD, + PTN.FETCH_START, + entries + ); + if (!rootSpan) { + return; + } + this._tracer.withSpan(rootSpan, () => { + const fetchSpan = this._startSpan( + AttributeNames.DOCUMENT_FETCH, PTN.FETCH_START, entries ); - if (!rootSpan) { - return; + if (fetchSpan) { + this._tracer.withSpan(fetchSpan, () => { + this._addSpanNetworkEvents(fetchSpan, entries); + this._endSpan(fetchSpan, PTN.RESPONSE_END, entries); + }); } - this._tracer.withSpan(rootSpan, () => { - const fetchSpan = this._startSpan( - AttributeNames.DOCUMENT_FETCH, - PTN.FETCH_START, - entries - ); - if (fetchSpan) { - this._tracer.withSpan(fetchSpan, () => { - this._addSpanNetworkEvents(fetchSpan, entries); - this._endSpan(fetchSpan, PTN.RESPONSE_END, entries); - }); - } - }); + }); - this._addResourcesSpans(rootSpan); + this._addResourcesSpans(rootSpan); - addSpanNetworkEvent(rootSpan, PTN.UNLOAD_EVENT_START, entries); - addSpanNetworkEvent(rootSpan, PTN.UNLOAD_EVENT_END, entries); - addSpanNetworkEvent(rootSpan, PTN.DOM_INTERACTIVE, entries); - addSpanNetworkEvent( - rootSpan, - PTN.DOM_CONTENT_LOADED_EVENT_START, - entries - ); - addSpanNetworkEvent( - rootSpan, - PTN.DOM_CONTENT_LOADED_EVENT_END, - entries - ); - addSpanNetworkEvent(rootSpan, PTN.DOM_COMPLETE, entries); - addSpanNetworkEvent(rootSpan, PTN.LOAD_EVENT_START, entries); + addSpanNetworkEvent(rootSpan, PTN.UNLOAD_EVENT_START, entries); + addSpanNetworkEvent(rootSpan, PTN.UNLOAD_EVENT_END, entries); + addSpanNetworkEvent(rootSpan, PTN.DOM_INTERACTIVE, entries); + addSpanNetworkEvent( + rootSpan, + PTN.DOM_CONTENT_LOADED_EVENT_START, + entries + ); + addSpanNetworkEvent(rootSpan, PTN.DOM_CONTENT_LOADED_EVENT_END, entries); + addSpanNetworkEvent(rootSpan, PTN.DOM_COMPLETE, entries); + addSpanNetworkEvent(rootSpan, PTN.LOAD_EVENT_START, entries); - this._endSpan(rootSpan, PTN.LOAD_EVENT_END, entries); - } - ); + this._endSpan(rootSpan, PTN.LOAD_EVENT_END, entries); + }); } /** diff --git a/packages/opentelemetry-plugin-document-load/test/documentLoad.test.ts b/packages/opentelemetry-plugin-document-load/test/documentLoad.test.ts index 541044e05a..4b58f7ab01 100644 --- a/packages/opentelemetry-plugin-document-load/test/documentLoad.test.ts +++ b/packages/opentelemetry-plugin-document-load/test/documentLoad.test.ts @@ -18,19 +18,32 @@ * Can't use Sinon Fake Time here as then cannot stub the performance getEntriesByType with desired metrics */ -import { ConsoleLogger, TRACE_PARENT_HEADER } from '@opentelemetry/core'; +import { + context, + Logger, + PluginConfig, + propagation, + TimedEvent, +} from '@opentelemetry/api'; +import { + ConsoleLogger, + HttpTraceContext, + TRACE_PARENT_HEADER, +} from '@opentelemetry/core'; import { BasicTracerProvider, ReadableSpan, SimpleSpanProcessor, SpanExporter, } from '@opentelemetry/tracing'; -import { Logger, PluginConfig, TimedEvent } from '@opentelemetry/api'; +import { + PerformanceTimingNames as PTN, + StackScopeManager, +} from '@opentelemetry/web'; import * as assert from 'assert'; import * as sinon from 'sinon'; import { ExportResult } from '../../opentelemetry-base/build/src'; import { DocumentLoad } from '../src'; -import { PerformanceTimingNames as PTN } from '@opentelemetry/web'; export class DummyExporter implements SpanExporter { export( @@ -196,8 +209,11 @@ describe('DocumentLoad Plugin', () => { let config: PluginConfig; let spanProcessor: SimpleSpanProcessor; let dummyExporter: DummyExporter; + let scopeManager: StackScopeManager; beforeEach(() => { + scopeManager = new StackScopeManager().enable(); + context.initGlobalContextManager(scopeManager); Object.defineProperty(window.document, 'readyState', { writable: true, value: 'complete', @@ -213,12 +229,17 @@ describe('DocumentLoad Plugin', () => { }); afterEach(() => { + scopeManager.disable(); Object.defineProperty(window.document, 'readyState', { writable: true, value: 'complete', }); }); + before(() => { + propagation.initGlobalPropagator(new HttpTraceContext()); + }); + describe('constructor', () => { it('should construct an instance', () => { plugin = new DocumentLoad(); diff --git a/packages/opentelemetry-plugin-user-interaction/test/userInteraction.nozone.test.ts b/packages/opentelemetry-plugin-user-interaction/test/userInteraction.nozone.test.ts index c3091d3cfc..fb2133a4a9 100644 --- a/packages/opentelemetry-plugin-user-interaction/test/userInteraction.nozone.test.ts +++ b/packages/opentelemetry-plugin-user-interaction/test/userInteraction.nozone.test.ts @@ -18,14 +18,15 @@ // code outside zone.js. This needs to be done before all const originalSetTimeout = window.setTimeout; -import * as assert from 'assert'; -import * as sinon from 'sinon'; +import { context } from '@opentelemetry/api'; import { isWrapped, LogLevel } from '@opentelemetry/core'; +import { XMLHttpRequestPlugin } from '@opentelemetry/plugin-xml-http-request'; +import { ZoneScopeManager } from '@opentelemetry/scope-zone-peer-dep'; import * as tracing from '@opentelemetry/tracing'; import { WebTracerProvider } from '@opentelemetry/web'; -import { XMLHttpRequestPlugin } from '@opentelemetry/plugin-xml-http-request'; +import * as assert from 'assert'; +import * as sinon from 'sinon'; import { UserInteractionPlugin } from '../src'; - import { assertClickSpan, DummySpanExporter, @@ -38,6 +39,7 @@ const FILE_URL = describe('UserInteractionPlugin', () => { describe('when zone.js is NOT available', () => { + let scopeManager: ZoneScopeManager; let userInteractionPlugin: UserInteractionPlugin; let sandbox: sinon.SinonSandbox; let webTracerProvider: WebTracerProvider; @@ -45,6 +47,8 @@ describe('UserInteractionPlugin', () => { let exportSpy: sinon.SinonSpy; let requests: sinon.SinonFakeXMLHttpRequest[] = []; beforeEach(() => { + scopeManager = new ZoneScopeManager().enable(); + context.initGlobalContextManager(scopeManager); sandbox = sinon.createSandbox(); const fakeXhr = sandbox.useFakeXMLHttpRequest(); fakeXhr.onCreate = function(xhr: sinon.SinonFakeXMLHttpRequest) { @@ -85,6 +89,7 @@ describe('UserInteractionPlugin', () => { requests = []; sandbox.restore(); exportSpy.restore(); + scopeManager.disable(); }); it('should handle task without async operation', () => { diff --git a/packages/opentelemetry-plugin-user-interaction/test/userInteraction.test.ts b/packages/opentelemetry-plugin-user-interaction/test/userInteraction.test.ts index cc870e1a59..c38f6fb2cd 100644 --- a/packages/opentelemetry-plugin-user-interaction/test/userInteraction.test.ts +++ b/packages/opentelemetry-plugin-user-interaction/test/userInteraction.test.ts @@ -18,14 +18,15 @@ // code outside zone.js. This needs to be done before all const originalSetTimeout = window.setTimeout; -import 'zone.js'; - -import * as assert from 'assert'; -import * as sinon from 'sinon'; +import { context } from '@opentelemetry/api'; import { isWrapped, LogLevel } from '@opentelemetry/core'; +import { XMLHttpRequestPlugin } from '@opentelemetry/plugin-xml-http-request'; +import { ZoneScopeManager } from '@opentelemetry/scope-zone-peer-dep'; import * as tracing from '@opentelemetry/tracing'; import { WebTracerProvider } from '@opentelemetry/web'; -import { XMLHttpRequestPlugin } from '@opentelemetry/plugin-xml-http-request'; +import * as assert from 'assert'; +import * as sinon from 'sinon'; +import 'zone.js'; import { UserInteractionPlugin } from '../src'; import { WindowWithZone } from '../src/types'; import { @@ -41,6 +42,7 @@ const FILE_URL = describe('UserInteractionPlugin', () => { describe('when zone.js is available', () => { + let scopeManager: ZoneScopeManager; let userInteractionPlugin: UserInteractionPlugin; let sandbox: sinon.SinonSandbox; let webTracerProvider: WebTracerProvider; @@ -48,6 +50,8 @@ describe('UserInteractionPlugin', () => { let exportSpy: sinon.SinonSpy; let requests: sinon.SinonFakeXMLHttpRequest[] = []; beforeEach(() => { + scopeManager = new ZoneScopeManager().enable(); + context.initGlobalContextManager(scopeManager); sandbox = sinon.createSandbox(); history.pushState({ test: 'testing' }, '', `${location.pathname}`); const fakeXhr = sandbox.useFakeXMLHttpRequest(); @@ -83,6 +87,7 @@ describe('UserInteractionPlugin', () => { requests = []; sandbox.restore(); exportSpy.restore(); + scopeManager.disable(); }); it('should handle task without async operation', () => { diff --git a/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts b/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts index 3886f257e0..cfdf6840ae 100644 --- a/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts +++ b/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts @@ -22,6 +22,7 @@ import { isWrapped, otperformance, urlMatches, + getParentSpanContext, } from '@opentelemetry/core'; import { addSpanNetworkEvent, @@ -88,6 +89,7 @@ export class XMLHttpRequestPlugin extends BasePlugin { } const headers: { [key: string]: unknown } = {}; api.propagation.inject(headers); + console.log(getParentSpanContext(api.context.active())); Object.keys(headers).forEach(key => { xhr.setRequestHeader(key, String(headers[key])); @@ -467,23 +469,25 @@ export class XMLHttpRequestPlugin extends BasePlugin { const spanUrl = xhrMem.spanUrl; if (currentSpan && spanUrl) { - plugin._tasksCount++; - xhrMem.sendStartTime = hrTime(); - currentSpan.addEvent(EventNames.METHOD_SEND); - - this.addEventListener('abort', onAbort); - this.addEventListener('error', onError); - this.addEventListener('load', onLoad); - this.addEventListener('timeout', onTimeout); - - xhrMem.callbackToRemoveEvents = () => { - unregister(this); - if (xhrMem.createdResources) { - xhrMem.createdResources.observer.disconnect(); - } - }; - plugin._addHeaders(this, spanUrl); - plugin._addResourceObserver(this, spanUrl); + plugin._tracer.withSpan(currentSpan, () => { + plugin._tasksCount++; + xhrMem.sendStartTime = hrTime(); + currentSpan.addEvent(EventNames.METHOD_SEND); + + this.addEventListener('abort', onAbort); + this.addEventListener('error', onError); + this.addEventListener('load', onLoad); + this.addEventListener('timeout', onTimeout); + + xhrMem.callbackToRemoveEvents = () => { + unregister(this); + if (xhrMem.createdResources) { + xhrMem.createdResources.observer.disconnect(); + } + }; + plugin._addHeaders(this, spanUrl); + plugin._addResourceObserver(this, spanUrl); + }); } return original.apply(this, args); }; diff --git a/packages/opentelemetry-plugin-xml-http-request/test/xhr.test.ts b/packages/opentelemetry-plugin-xml-http-request/test/xhr.test.ts index c10f7933e3..f2f936d0f3 100644 --- a/packages/opentelemetry-plugin-xml-http-request/test/xhr.test.ts +++ b/packages/opentelemetry-plugin-xml-http-request/test/xhr.test.ts @@ -15,6 +15,7 @@ */ import * as types from '@opentelemetry/api'; import { + B3Format, LogLevel, otperformance as performance, setActiveSpan, @@ -30,7 +31,6 @@ import { } from '@opentelemetry/web'; import * as assert from 'assert'; import * as sinon from 'sinon'; -import { ScopeManager } from '../../opentelemetry-scope-base/build/src'; import { AttributeNames } from '../src/enums/AttributeNames'; import { EventNames } from '../src/enums/EventNames'; import { XMLHttpRequestPlugin } from '../src/xhr'; @@ -98,7 +98,7 @@ describe('xhr', () => { let requests: any[] = []; let prepareData: any; let clearData: any; - let scopeManager: ScopeManager; + let scopeManager: ZoneScopeManager; beforeEach(() => { scopeManager = new ZoneScopeManager().enable(); @@ -109,6 +109,10 @@ describe('xhr', () => { scopeManager.disable(); }); + before(() => { + types.propagation.initGlobalPropagator(new B3Format()); + }); + describe('when request is successful', () => { let webTracerWithZone: types.Tracer; let webTracerProviderWithZone: WebTracerProvider; @@ -166,8 +170,7 @@ describe('xhr', () => { ); rootSpan = webTracerWithZone.startSpan('root'); - - scopeManager.with(setActiveSpan(types.context.active(), rootSpan), () => { + webTracerWithZone.withSpan(rootSpan, () => { getData(fileUrl, () => { fakeNow = 100; }).then(() => { @@ -194,14 +197,6 @@ describe('xhr', () => { clearData(); }); - it('current span should be root span', () => { - assert.strictEqual( - webTracerWithZone.getCurrentSpan(), - rootSpan, - 'root span is wrong' - ); - }); - it('should create a span with correct root span', () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; assert.strictEqual( From f8b435b40893914c0ee23bcba913861a7df1908f Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Wed, 26 Feb 2020 18:31:55 -0500 Subject: [PATCH 08/15] chore: undo unnecessary context calls in tests --- .../test/express.test.ts | 159 +++++++++--------- .../test/xhr.test.ts | 3 +- 2 files changed, 76 insertions(+), 86 deletions(-) diff --git a/packages/opentelemetry-plugin-express/test/express.test.ts b/packages/opentelemetry-plugin-express/test/express.test.ts index 4200fcec63..f1766f6d65 100644 --- a/packages/opentelemetry-plugin-express/test/express.test.ts +++ b/packages/opentelemetry-plugin-express/test/express.test.ts @@ -14,24 +14,24 @@ * limitations under the License. */ +import { context } from '@opentelemetry/api'; +import { NoopLogger } from '@opentelemetry/core'; import { NodeTracerProvider } from '@opentelemetry/node'; +import { AsyncHooksScopeManager } from '@opentelemetry/scope-async-hooks'; +import { + InMemorySpanExporter, + SimpleSpanProcessor, +} from '@opentelemetry/tracing'; import * as assert from 'assert'; import * as express from 'express'; import * as http from 'http'; import { AddressInfo } from 'net'; import { plugin } from '../src'; -import { NoopLogger, setActiveSpan } from '@opentelemetry/core'; -import { context } from '@opentelemetry/api'; -import { - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/tracing'; import { AttributeNames, - ExpressPluginConfig, ExpressLayerType, + ExpressPluginConfig, } from '../src/types'; -import { AsyncHooksScopeManager } from '@opentelemetry/scope-async-hooks'; const httpRequest = { get: (options: http.ClientRequestArgs | string) => { @@ -95,51 +95,48 @@ describe('Express Plugin', () => { server.listen(0, () => { const port = (server.address() as AddressInfo).port; assert.strictEqual(memoryExporter.getFinishedSpans().length, 0); - scopeManager.with( - setActiveSpan(context.active(), rootSpan), - async () => { - await httpRequest.get(`http://localhost:${port}/toto/tata`); - rootSpan.end(); - assert( - memoryExporter - .getFinishedSpans() - .find(span => span.name.includes('customMiddleware')) !== - undefined - ); - assert( - memoryExporter - .getFinishedSpans() - .find(span => span.name.includes('query')) !== undefined - ); - assert( - memoryExporter - .getFinishedSpans() - .find(span => span.name.includes('jsonParser')) !== undefined - ); - const requestHandlerSpan = memoryExporter + tracer.withSpan(rootSpan, async () => { + await httpRequest.get(`http://localhost:${port}/toto/tata`); + rootSpan.end(); + assert( + memoryExporter .getFinishedSpans() - .find(span => span.name.includes('request handler')); - assert(requestHandlerSpan !== undefined); - assert( - requestHandlerSpan?.attributes[AttributeNames.COMPONENT] === - 'express' - ); - assert( - requestHandlerSpan?.attributes[AttributeNames.HTTP_ROUTE] === - '/toto/:id' - ); - assert( - requestHandlerSpan?.attributes[AttributeNames.EXPRESS_TYPE] === - 'request_handler' - ); - let exportedRootSpan = memoryExporter + .find(span => span.name.includes('customMiddleware')) !== + undefined + ); + assert( + memoryExporter .getFinishedSpans() - .find(span => span.name === 'rootSpan'); - assert(exportedRootSpan !== undefined); - server.close(); - return done(); - } - ); + .find(span => span.name.includes('query')) !== undefined + ); + assert( + memoryExporter + .getFinishedSpans() + .find(span => span.name.includes('jsonParser')) !== undefined + ); + const requestHandlerSpan = memoryExporter + .getFinishedSpans() + .find(span => span.name.includes('request handler')); + assert(requestHandlerSpan !== undefined); + assert( + requestHandlerSpan?.attributes[AttributeNames.COMPONENT] === + 'express' + ); + assert( + requestHandlerSpan?.attributes[AttributeNames.HTTP_ROUTE] === + '/toto/:id' + ); + assert( + requestHandlerSpan?.attributes[AttributeNames.EXPRESS_TYPE] === + 'request_handler' + ); + let exportedRootSpan = memoryExporter + .getFinishedSpans() + .find(span => span.name === 'rootSpan'); + assert(exportedRootSpan !== undefined); + server.close(); + return done(); + }); }); }); }); @@ -164,29 +161,26 @@ describe('Express Plugin', () => { server.listen(0, () => { const port = (server.address() as AddressInfo).port; assert.strictEqual(memoryExporter.getFinishedSpans().length, 0); - scopeManager.with( - setActiveSpan(context.active(), rootSpan), - async () => { - await httpRequest.get(`http://localhost:${port}/toto/tata`); - rootSpan.end(); - assert.deepEqual( - memoryExporter - .getFinishedSpans() - .filter( - span => - span.attributes[AttributeNames.EXPRESS_TYPE] === - ExpressLayerType.MIDDLEWARE - ).length, - 0 - ); - let exportedRootSpan = memoryExporter + tracer.withSpan(rootSpan, async () => { + await httpRequest.get(`http://localhost:${port}/toto/tata`); + rootSpan.end(); + assert.deepEqual( + memoryExporter .getFinishedSpans() - .find(span => span.name === 'rootSpan'); - assert(exportedRootSpan !== undefined); - server.close(); - return done(); - } - ); + .filter( + span => + span.attributes[AttributeNames.EXPRESS_TYPE] === + ExpressLayerType.MIDDLEWARE + ).length, + 0 + ); + let exportedRootSpan = memoryExporter + .getFinishedSpans() + .find(span => span.name === 'rootSpan'); + assert(exportedRootSpan !== undefined); + server.close(); + return done(); + }); }); }); }); @@ -207,17 +201,14 @@ describe('Express Plugin', () => { server.listen(0, () => { const port = (server.address() as AddressInfo).port; assert.strictEqual(memoryExporter.getFinishedSpans().length, 0); - scopeManager.with( - setActiveSpan(context.active(), rootSpan), - async () => { - await httpRequest.get(`http://localhost:${port}/toto/tata`); - rootSpan.end(); - assert.deepEqual(memoryExporter.getFinishedSpans().length, 1); - assert(memoryExporter.getFinishedSpans()[0] !== undefined); - server.close(); - return done(); - } - ); + tracer.withSpan(rootSpan, async () => { + await httpRequest.get(`http://localhost:${port}/toto/tata`); + rootSpan.end(); + assert.deepEqual(memoryExporter.getFinishedSpans().length, 1); + assert(memoryExporter.getFinishedSpans()[0] !== undefined); + server.close(); + return done(); + }); }); }); }); diff --git a/packages/opentelemetry-plugin-xml-http-request/test/xhr.test.ts b/packages/opentelemetry-plugin-xml-http-request/test/xhr.test.ts index f2f936d0f3..9ee2a96d74 100644 --- a/packages/opentelemetry-plugin-xml-http-request/test/xhr.test.ts +++ b/packages/opentelemetry-plugin-xml-http-request/test/xhr.test.ts @@ -18,7 +18,6 @@ import { B3Format, LogLevel, otperformance as performance, - setActiveSpan, X_B3_SAMPLED, X_B3_SPAN_ID, X_B3_TRACE_ID, @@ -456,7 +455,7 @@ describe('xhr', () => { rootSpan = webTracerWithZone.startSpan('root'); - scopeManager.with(setActiveSpan(types.context.active(), rootSpan), () => { + webTracerWithZone.withSpan(rootSpan, () => { getData(url, () => { fakeNow = 100; }).then(() => { From 0b3e4bae8ca16b89493a2bf5d779fb9a2cde9676 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Wed, 26 Feb 2020 18:34:39 -0500 Subject: [PATCH 09/15] chore: remove console log --- packages/opentelemetry-plugin-xml-http-request/src/xhr.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts b/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts index cfdf6840ae..c88ffde76a 100644 --- a/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts +++ b/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts @@ -89,8 +89,6 @@ export class XMLHttpRequestPlugin extends BasePlugin { } const headers: { [key: string]: unknown } = {}; api.propagation.inject(headers); - console.log(getParentSpanContext(api.context.active())); - Object.keys(headers).forEach(key => { xhr.setRequestHeader(key, String(headers[key])); }); From 20d42cda03fc80dffd44703b1c2a0a29ed2381b4 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Wed, 26 Feb 2020 18:47:40 -0500 Subject: [PATCH 10/15] chore: review comments and lint --- packages/opentelemetry-plugin-http/src/http.ts | 2 +- .../opentelemetry-plugin-http/test/functionals/utils.test.ts | 2 +- packages/opentelemetry-plugin-xml-http-request/src/xhr.ts | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/opentelemetry-plugin-http/src/http.ts b/packages/opentelemetry-plugin-http/src/http.ts index 826143e8ae..53bbebe5bb 100644 --- a/packages/opentelemetry-plugin-http/src/http.ts +++ b/packages/opentelemetry-plugin-http/src/http.ts @@ -246,7 +246,7 @@ export class HttpPlugin extends BasePlugin { this._closeHttpSpan(span); }); - this._logger.debug('makeRequestTrace return request'); + this._logger.debug('_traceClientRequest return request'); return request; } diff --git a/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts b/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts index f13c79fceb..2e84582598 100644 --- a/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts +++ b/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts @@ -254,7 +254,7 @@ describe('Utility', () => { const errorMessage = 'test error'; for (const obj of [undefined, { statusCode: 400 }]) { const span = new Span( - new BasicTracerProvider({}).getTracer('default'), + new BasicTracerProvider().getTracer('default'), 'test', { spanId: '', traceId: '' }, SpanKind.INTERNAL diff --git a/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts b/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts index c88ffde76a..4f31b06958 100644 --- a/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts +++ b/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts @@ -22,7 +22,6 @@ import { isWrapped, otperformance, urlMatches, - getParentSpanContext, } from '@opentelemetry/core'; import { addSpanNetworkEvent, From dfe5520b5d456cadbeb83cd984210057a8fd5f4e Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Thu, 27 Feb 2020 10:18:23 -0500 Subject: [PATCH 11/15] chore: fix build --- packages/opentelemetry-api/src/trace/tracer.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/opentelemetry-api/src/trace/tracer.ts b/packages/opentelemetry-api/src/trace/tracer.ts index 95bd6a5a14..b869036e3c 100644 --- a/packages/opentelemetry-api/src/trace/tracer.ts +++ b/packages/opentelemetry-api/src/trace/tracer.ts @@ -15,8 +15,6 @@ */ import { Context } from '@opentelemetry/scope-base'; -import { BinaryFormat } from '../context/propagation/BinaryFormat'; -import { HttpTextFormat } from '../context/propagation/HttpTextFormat'; import { Span } from './span'; import { SpanOptions } from './SpanOptions'; From 16eb912f28b2013d326be26c36adf9aeea29ddc0 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Fri, 28 Feb 2020 14:16:19 -0500 Subject: [PATCH 12/15] chore: review comments --- packages/opentelemetry-node/src/NodeTracerProvider.ts | 2 +- packages/opentelemetry-plugin-grpc/src/grpc.ts | 2 +- packages/opentelemetry-plugin-mongodb/src/mongodb.ts | 5 +---- packages/opentelemetry-web/src/WebTracerProvider.ts | 2 +- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/opentelemetry-node/src/NodeTracerProvider.ts b/packages/opentelemetry-node/src/NodeTracerProvider.ts index 541ed38240..acdf887dc3 100644 --- a/packages/opentelemetry-node/src/NodeTracerProvider.ts +++ b/packages/opentelemetry-node/src/NodeTracerProvider.ts @@ -28,7 +28,7 @@ export class NodeTracerProvider extends BasicTracerProvider { * Constructs a new Tracer instance. */ constructor(config: NodeTracerConfig = {}) { - super(Object.assign({}, config)); + super(config); this._pluginLoader = new PluginLoader(this, this.logger); this._pluginLoader.load(config.plugins || DEFAULT_INSTRUMENTATION_PLUGINS); diff --git a/packages/opentelemetry-plugin-grpc/src/grpc.ts b/packages/opentelemetry-plugin-grpc/src/grpc.ts index 3e08a5a168..582763412f 100644 --- a/packages/opentelemetry-plugin-grpc/src/grpc.ts +++ b/packages/opentelemetry-plugin-grpc/src/grpc.ts @@ -125,7 +125,7 @@ export class GrpcPlugin extends BasePlugin { private _setSpanContext(metadata: grpcTypes.Metadata): void { const carrier = {}; - propagation.inject(carrier); + propagation.inject(metadata, undefined, (c, k, v) => c.set()); for (const [k, v] of Object.entries(carrier)) { metadata.set(k, v as string); } diff --git a/packages/opentelemetry-plugin-mongodb/src/mongodb.ts b/packages/opentelemetry-plugin-mongodb/src/mongodb.ts index 01da629090..dc4c61e645 100644 --- a/packages/opentelemetry-plugin-mongodb/src/mongodb.ts +++ b/packages/opentelemetry-plugin-mongodb/src/mongodb.ts @@ -103,11 +103,9 @@ export class MongoDBPlugin extends BasePlugin { options: {} | Function, callback: Function ): mongodb.Server { - const currentSpan = plugin._tracer.getCurrentSpan(); const resultHandler = typeof options === 'function' ? options : callback; if ( - !currentSpan || typeof resultHandler !== 'function' || typeof commands !== 'object' ) { @@ -208,9 +206,8 @@ export class MongoDBPlugin extends BasePlugin { }, ...args: unknown[] ): mongodb.Cursor { - const currentSpan = plugin._tracer.getCurrentSpan(); const resultHandler = args[0]; - if (!currentSpan || typeof resultHandler !== 'function') { + if (typeof resultHandler !== 'function') { return original.apply(this, args); } const span = plugin._tracer.startSpan(`mongodb.query`, { diff --git a/packages/opentelemetry-web/src/WebTracerProvider.ts b/packages/opentelemetry-web/src/WebTracerProvider.ts index 92d2bba29d..05f066cd53 100644 --- a/packages/opentelemetry-web/src/WebTracerProvider.ts +++ b/packages/opentelemetry-web/src/WebTracerProvider.ts @@ -39,7 +39,7 @@ export class WebTracerProvider extends BasicTracerProvider { if (typeof config.plugins === 'undefined') { config.plugins = []; } - super(Object.assign({}, config)); + super(config); for (const plugin of config.plugins) { plugin.enable([], this, this.logger); From aef5926be4bed0e4fa7a051e0fd7da674e300700 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Fri, 28 Feb 2020 14:42:46 -0500 Subject: [PATCH 13/15] chore: revert bad change from previous commit --- packages/opentelemetry-plugin-grpc/src/grpc.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-plugin-grpc/src/grpc.ts b/packages/opentelemetry-plugin-grpc/src/grpc.ts index 582763412f..3e08a5a168 100644 --- a/packages/opentelemetry-plugin-grpc/src/grpc.ts +++ b/packages/opentelemetry-plugin-grpc/src/grpc.ts @@ -125,7 +125,7 @@ export class GrpcPlugin extends BasePlugin { private _setSpanContext(metadata: grpcTypes.Metadata): void { const carrier = {}; - propagation.inject(metadata, undefined, (c, k, v) => c.set()); + propagation.inject(carrier); for (const [k, v] of Object.entries(carrier)) { metadata.set(k, v as string); } From 9386997a9685a55994a64483f572793b6b53a680 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Fri, 28 Feb 2020 14:56:55 -0500 Subject: [PATCH 14/15] chore: allow spanoptions to override parent from context --- .../src/trace/SpanOptions.ts | 13 +++++++ packages/opentelemetry-tracing/src/Tracer.ts | 14 ++++++-- .../test/BasicTracerRegistry.test.ts | 36 +++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/packages/opentelemetry-api/src/trace/SpanOptions.ts b/packages/opentelemetry-api/src/trace/SpanOptions.ts index 6a9a8d8ae7..56d2d38428 100644 --- a/packages/opentelemetry-api/src/trace/SpanOptions.ts +++ b/packages/opentelemetry-api/src/trace/SpanOptions.ts @@ -17,6 +17,8 @@ import { Attributes } from './attributes'; import { Link } from './link'; import { SpanKind } from './span_kind'; +import { Span } from './span'; +import { SpanContext } from './span_context'; /** * Options needed for span creation @@ -40,6 +42,17 @@ export interface SpanOptions { /** A spans links */ links?: Link[]; + /** + * This option is NOT RECOMMENDED for normal use and should ONLY be used + * if your application manages context manually without the global context + * manager, or you are trying to override the parent extracted from context. + * + * A parent `SpanContext` (or `Span`, for convenience) that the newly-started + * span will be the child of. This overrides the parent span extracted from + * the currently active context. + */ + parent?: Span | SpanContext | null; + /** A manually specified start time for the created `Span` object. */ startTime?: number; } diff --git a/packages/opentelemetry-tracing/src/Tracer.ts b/packages/opentelemetry-tracing/src/Tracer.ts index 65ef7b4b15..088c4f16cb 100644 --- a/packages/opentelemetry-tracing/src/Tracer.ts +++ b/packages/opentelemetry-tracing/src/Tracer.ts @@ -17,12 +17,12 @@ import * as api from '@opentelemetry/api'; import { ConsoleLogger, + getActiveSpan, getParentSpanContext, isValid, NoRecordingSpan, randomSpanId, randomTraceId, - getActiveSpan, setActiveSpan, } from '@opentelemetry/core'; import { BasicTracerProvider } from './BasicTracerProvider'; @@ -63,7 +63,9 @@ export class Tracer implements api.Tracer { options: api.SpanOptions = {}, context = api.context.active() ): api.Span { - const parentContext = getParentSpanContext(context); + const parentContext = options.parent + ? getContext(options.parent) + : getParentSpanContext(context); // make sampling decision const samplingDecision = this._sampler.shouldSample(parentContext); const spanId = randomSpanId(); @@ -144,3 +146,11 @@ export class Tracer implements api.Tracer { return this._tracerProvider.getActiveSpanProcessor(); } } + +function getContext(span: api.Span | api.SpanContext) { + return isSpan(span) ? span.context() : span; +} + +function isSpan(span: api.Span | api.SpanContext): span is api.Span { + return typeof (span as api.Span).context === 'function'; +} diff --git a/packages/opentelemetry-tracing/test/BasicTracerRegistry.test.ts b/packages/opentelemetry-tracing/test/BasicTracerRegistry.test.ts index c8e3229002..1ce6c12c63 100644 --- a/packages/opentelemetry-tracing/test/BasicTracerRegistry.test.ts +++ b/packages/opentelemetry-tracing/test/BasicTracerRegistry.test.ts @@ -198,6 +198,42 @@ describe('BasicTracerProvider', () => { childSpan.end(); }); + it('should override context parent with option parent', () => { + const tracer = new BasicTracerProvider().getTracer('default'); + const span = tracer.startSpan('my-span'); + const overrideParent = tracer.startSpan('my-parent-override-span'); + const childSpan = tracer.startSpan( + 'child-span', + { + parent: overrideParent, + }, + setActiveSpan(Context.ROOT_CONTEXT, span) + ); + const context = childSpan.context(); + assert.strictEqual(context.traceId, overrideParent.context().traceId); + assert.strictEqual(context.traceFlags, TraceFlags.SAMPLED); + span.end(); + childSpan.end(); + }); + + it('should override context parent with option parent context', () => { + const tracer = new BasicTracerProvider().getTracer('default'); + const span = tracer.startSpan('my-span'); + const overrideParent = tracer.startSpan('my-parent-override-span'); + const childSpan = tracer.startSpan( + 'child-span', + { + parent: overrideParent.context(), + }, + setActiveSpan(Context.ROOT_CONTEXT, span) + ); + const context = childSpan.context(); + assert.strictEqual(context.traceId, overrideParent.context().traceId); + assert.strictEqual(context.traceFlags, TraceFlags.SAMPLED); + span.end(); + childSpan.end(); + }); + it('should start a span with name and with invalid parent span', () => { const tracer = new BasicTracerProvider().getTracer('default'); const span = tracer.startSpan( From 5aefeada9d82ce20b94139420379070cc8138976 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Fri, 28 Feb 2020 15:07:00 -0500 Subject: [PATCH 15/15] chore: revert bad mongo change This reverts bad change from 16eb912f28b2013d326be26c36adf9aeea29ddc0. --- packages/opentelemetry-plugin-mongodb/src/mongodb.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/opentelemetry-plugin-mongodb/src/mongodb.ts b/packages/opentelemetry-plugin-mongodb/src/mongodb.ts index dc4c61e645..01da629090 100644 --- a/packages/opentelemetry-plugin-mongodb/src/mongodb.ts +++ b/packages/opentelemetry-plugin-mongodb/src/mongodb.ts @@ -103,9 +103,11 @@ export class MongoDBPlugin extends BasePlugin { options: {} | Function, callback: Function ): mongodb.Server { + const currentSpan = plugin._tracer.getCurrentSpan(); const resultHandler = typeof options === 'function' ? options : callback; if ( + !currentSpan || typeof resultHandler !== 'function' || typeof commands !== 'object' ) { @@ -206,8 +208,9 @@ export class MongoDBPlugin extends BasePlugin { }, ...args: unknown[] ): mongodb.Cursor { + const currentSpan = plugin._tracer.getCurrentSpan(); const resultHandler = args[0]; - if (typeof resultHandler !== 'function') { + if (!currentSpan || typeof resultHandler !== 'function') { return original.apply(this, args); } const span = plugin._tracer.startSpan(`mongodb.query`, {