Skip to content

Commit

Permalink
feat: use context-based tracing (#816)
Browse files Browse the repository at this point in the history
* feat: use context-based tracing

* chore: use withSpan where possible

* chore: propagate context in tests

* chore: allow spanoptions to override parent from context
  • Loading branch information
dyladan authored Mar 2, 2020
1 parent 3818515 commit 3c60157
Show file tree
Hide file tree
Showing 58 changed files with 774 additions and 716 deletions.
6 changes: 3 additions & 3 deletions packages/opentelemetry-api/src/api/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<T extends (...args: unknown[]) => ReturnType<T>>(
fn: T,
context: Context = this.active()
context: Context,
fn: T
): ReturnType<T> {
return this._scopeManager.with(context, fn);
}
Expand Down
11 changes: 8 additions & 3 deletions packages/opentelemetry-api/src/trace/SpanOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
* limitations under the License.
*/

import { Span } from './span';
import { Attributes } from './attributes';
import { Link } from './link';
import { SpanKind } from './span_kind';
import { Span } from './span';
import { SpanContext } from './span_context';
import { Link } from './link';

/**
* Options needed for span creation
Expand All @@ -43,8 +43,13 @@ export interface SpanOptions {
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.
* span will be the child of. This overrides the parent span extracted from
* the currently active context.
*/
parent?: Span | SpanContext | null;

Expand Down
21 changes: 5 additions & 16 deletions packages/opentelemetry-api/src/trace/tracer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { HttpTextFormat } from '../context/propagation/HttpTextFormat';
import { Context } from '@opentelemetry/scope-base';
import { Span } from './span';
import { SpanOptions } from './SpanOptions';

Expand All @@ -39,9 +39,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
Expand All @@ -60,19 +61,7 @@ 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<T>(target: T, span?: Span): T;

/**
* 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
* <a href="https://w3c.github.io/trace-context/">W3C Trace Context</a>.
*
* @returns the {@link HttpTextFormat} for this implementation.
*/
getHttpTextFormat(): HttpTextFormat;
bind<T>(target: T, context?: Span): T;
}
9 changes: 2 additions & 7 deletions packages/opentelemetry-node/src/NodeTracerProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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(config);

this._pluginLoader = new PluginLoader(this, this.logger);
this._pluginLoader.load(config.plugins || DEFAULT_INSTRUMENTATION_PLUGINS);
Expand Down
48 changes: 19 additions & 29 deletions packages/opentelemetry-node/test/NodeTracerProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,20 @@
* limitations under the License.
*/

import * as assert from 'assert';
import { context, TraceFlags } from '@opentelemetry/api';
import {
ALWAYS_SAMPLER,
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 { ScopeManager } from '../../opentelemetry-scope-base/build/src';
import { NodeTracerProvider } from '../src/NodeTracerProvider';

const sleep = (time: number) =>
new Promise(resolve => {
Expand All @@ -40,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', () => {
Expand All @@ -56,13 +65,6 @@ describe('NodeTracerProvider', () => {
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(),
Expand Down Expand Up @@ -194,9 +196,8 @@ describe('NodeTracerProvider', () => {
span
);

const span1 = provider
.getTracer('default')
.startSpan('my-span1', { parent: span });
const span1 = provider.getTracer('default').startSpan('my-span1');

provider.getTracer('default').withSpan(span1, () => {
assert.deepStrictEqual(
provider.getTracer('default').getCurrentSpan(),
Expand All @@ -217,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 provider.getTracer('default').withSpan(span, async () => {
for (let i = 0; i < 3; i++) {
await sleep(5).then(() => {
assert.deepStrictEqual(
Expand All @@ -229,7 +230,6 @@ describe('NodeTracerProvider', () => {
);
});
}
return done();
});
assert.deepStrictEqual(
provider.getTracer('default').getCurrentSpan(),
Expand All @@ -249,18 +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('.getHttpTextFormat()', () => {
it('should get default HTTP text formatter', () => {
provider = new NodeTracerProvider({});
assert.ok(
provider.getTracer('default').getHttpTextFormat() instanceof
HttpTraceContext
);
});
});
});
1 change: 0 additions & 1 deletion packages/opentelemetry-plugin-dns/src/dns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ export class DnsPlugin extends BasePlugin<Dns> {
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,
},
Expand Down
79 changes: 44 additions & 35 deletions packages/opentelemetry-plugin-document-load/src/documentLoad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,18 @@
* limitations under the License.
*/

import {
context,
PluginConfig,
propagation,
Span,
SpanOptions,
} from '@opentelemetry/api';
import {
BasePlugin,
otperformance,
parseTraceParent,
TRACE_PARENT_HEADER,
} from '@opentelemetry/core';
import { PluginConfig, Span, SpanOptions } from '@opentelemetry/api';
import {
addSpanNetworkEvent,
hasKey,
Expand Down Expand Up @@ -71,9 +76,7 @@ export class DocumentLoad extends BasePlugin<unknown> {
) as PerformanceResourceTiming[];
if (resources) {
resources.forEach(resource => {
this._initResourceSpan(resource, {
parent: rootSpan,
});
this._initResourceSpan(resource);
});
}
}
Expand Down Expand Up @@ -102,40 +105,46 @@ export class DocumentLoad extends BasePlugin<unknown> {
);

const entries = this._getEntries();

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,
const traceparent = (metaElement && metaElement.content) || '';
context.with(propagation.extract({ traceparent }), () => {
const rootSpan = this._startSpan(
AttributeNames.DOCUMENT_LOAD,
PTN.FETCH_START,
entries
);
if (!rootSpan) {
return;
}
);
if (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);
});
}

/**
Expand Down
Loading

0 comments on commit 3c60157

Please sign in to comment.