Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: extend the Tracer and ScopeManager with async functionality #753

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions packages/opentelemetry-api/src/trace/NoopTracer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
* limitations under the License.
*/

import { BinaryFormat, HttpTextFormat, Span, SpanOptions, Tracer } from '..';
import { NOOP_BINARY_FORMAT } from '../context/propagation/NoopBinaryFormat';
import { NOOP_HTTP_TEXT_FORMAT } from '../context/propagation/NoopHttpTextFormat';
import { NOOP_SPAN } from './NoopSpan';
import { BinaryFormat, HttpTextFormat, Span, SpanOptions, Tracer } from "..";
import { NOOP_BINARY_FORMAT } from "../context/propagation/NoopBinaryFormat";
import { NOOP_HTTP_TEXT_FORMAT } from "../context/propagation/NoopHttpTextFormat";
import { NOOP_SPAN } from "./NoopSpan";

/**
* No-op implementations of {@link Tracer}.
Expand All @@ -39,6 +39,13 @@ export class NoopTracer implements Tracer {
return fn();
}

async withSpanAsync<
T extends (...args: unknown[]) => Promise<T2>,
T2 extends unknown
>(span: Span, fn: T): Promise<T2> {
return await fn();
}

bind<T>(target: T, span?: Span): T {
return target;
}
Expand Down
25 changes: 21 additions & 4 deletions packages/opentelemetry-api/src/trace/tracer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
* limitations under the License.
*/

import { HttpTextFormat } from '../context/propagation/HttpTextFormat';
import { BinaryFormat } from '../context/propagation/BinaryFormat';
import { Span } from './span';
import { SpanOptions } from './SpanOptions';
import { HttpTextFormat } from "../context/propagation/HttpTextFormat";
import { BinaryFormat } from "../context/propagation/BinaryFormat";
import { Span } from "./span";
import { SpanOptions } from "./SpanOptions";

/**
* Tracer provides an interface for creating {@link Span}s and propagating
Expand Down Expand Up @@ -57,6 +57,23 @@ export interface Tracer {
fn: T
): ReturnType<T>;

/**
* Asynchronously executes the function given by fn within the context
* provided by Span
*
* @param span The span that provides the context
* @param fn The function to be executed inside the provided context
* @example
* tracer.withSpan(span, function() { ... });
*/
withSpanAsync<
T extends (...args: unknown[]) => Promise<T2>,
T2 extends unknown
>(
span: Span,
fn: T
): Promise<T2>;

/**
* Bind a span as the target's scope or propagate the current one.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,47 +14,63 @@
* limitations under the License.
*/

import * as assert from 'assert';
import { NoopTracer, NOOP_SPAN, SpanKind } from '../../src';
import * as assert from "assert";
import { NoopTracer, NOOP_SPAN, SpanKind } from "../../src";

describe('NoopTracer', () => {
it('should not crash', () => {
const spanContext = { traceId: '', spanId: '' };
describe("NoopTracer", () => {
it("should not crash", () => {
const spanContext = { traceId: "", spanId: "" };
const tracer = new NoopTracer();

assert.deepStrictEqual(tracer.startSpan('span-name'), NOOP_SPAN);
assert.deepStrictEqual(tracer.startSpan("span-name"), NOOP_SPAN);
assert.deepStrictEqual(
tracer.startSpan('span-name1', { kind: SpanKind.CLIENT }),
tracer.startSpan("span-name1", { kind: SpanKind.CLIENT }),
NOOP_SPAN
);
assert.deepStrictEqual(
tracer.startSpan('span-name2', {
tracer.startSpan("span-name2", {
kind: SpanKind.CLIENT,
isRecording: true,
isRecording: true
}),
NOOP_SPAN
);

assert.deepStrictEqual(tracer.getCurrentSpan(), NOOP_SPAN);
const httpTextFormat = tracer.getHttpTextFormat();
assert.ok(httpTextFormat);
httpTextFormat.inject(spanContext, 'HttpTextFormat', {});
assert.deepStrictEqual(httpTextFormat.extract('HttpTextFormat', {}), null);
httpTextFormat.inject(spanContext, "HttpTextFormat", {});
assert.deepStrictEqual(httpTextFormat.extract("HttpTextFormat", {}), null);

const binaryFormat = tracer.getBinaryFormat();
assert.ok(binaryFormat);
assert.ok(binaryFormat.toBytes(spanContext), typeof ArrayBuffer);
assert.deepStrictEqual(binaryFormat.fromBytes(new ArrayBuffer(0)), null);
});

it('should not crash when .withSpan()', done => {
it("should not crash when .withSpan()", done => {
const tracer = new NoopTracer();
tracer.withSpan(NOOP_SPAN, () => {
return done();
});
});

it('should not crash when .bind()', done => {
it("should not crash when .withSpanAsync()", async done => {
const tracer = new NoopTracer();
await tracer.withSpanAsync(NOOP_SPAN, () => {
return done();
});
});

it("should return the expected value when .withSpanAsync()", async () => {
const tracer = new NoopTracer();
const result = await tracer.withSpanAsync(NOOP_SPAN, async () => {
return await Promise.resolve(5);
});

assert.equal(result, 5);
});

it("should not crash when .bind()", done => {
const tracer = new NoopTracer();
const fn = () => {
return done();
Expand Down
11 changes: 9 additions & 2 deletions packages/opentelemetry-scope-base/src/NoopScopeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
* limitations under the License.
*/

import * as types from './types';
import { ScopeManager } from "./types";

export class NoopScopeManager implements types.ScopeManager {
export class NoopScopeManager implements ScopeManager {
active(): unknown {
return undefined;
}
Expand All @@ -28,6 +28,13 @@ export class NoopScopeManager implements types.ScopeManager {
return fn();
}

async withAsync<
T extends (...args: unknown[]) => Promise<T2>,
T2 extends unknown
>(scope: unknown, fn: T): Promise<T2> {
return await fn();
}

bind<T>(target: T, scope?: unknown): T {
return target;
}
Expand Down
11 changes: 11 additions & 0 deletions packages/opentelemetry-scope-base/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ export interface ScopeManager {
fn: T
): ReturnType<T>;

/**
* Asynchronously run the fn callback with object set as the
* current active scope
* @param scope Any object to set as the current active scope
* @param fn A callback to be immediately run within a specific scope
*/
withAsync<T extends (...args: unknown[]) => Promise<T2>, T2 extends unknown>(
scope: unknown,
fn: T
): Promise<T2>;

/**
* Bind an object as the current scope (or a specific one)
* @param target Any object to which a scope need to be set
Expand Down
70 changes: 48 additions & 22 deletions packages/opentelemetry-scope-base/test/NoopScopeManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,48 +14,48 @@
* limitations under the License.
*/

import * as assert from 'assert';
import { NoopScopeManager } from '../src';
import * as assert from "assert";
import { NoopScopeManager } from "../src";

describe('NoopScopeManager', () => {
describe("NoopScopeManager", () => {
let scopeManager: NoopScopeManager;

describe('.enable()', () => {
it('should work', () => {
describe(".enable()", () => {
it("should work", () => {
assert.doesNotThrow(() => {
scopeManager = new NoopScopeManager();
assert(scopeManager.enable() === scopeManager, 'should return this');
assert(scopeManager.enable() === scopeManager, "should return this");
});
});
});

describe('.disable()', () => {
it('should work', () => {
describe(".disable()", () => {
it("should work", () => {
assert.doesNotThrow(() => {
assert(scopeManager.disable() === scopeManager, 'should return this');
assert(scopeManager.disable() === scopeManager, "should return this");
});
scopeManager.enable();
});
});

describe('.with()', () => {
it('should run the callback (null as target)', done => {
describe(".with()", () => {
it("should run the callback (null as target)", done => {
scopeManager.with(null, done);
});

it('should run the callback (object as target)', done => {
it("should run the callback (object as target)", done => {
const test = { a: 1 };
scopeManager.with(test, () => {
assert.strictEqual(
scopeManager.active(),
undefined,
'should not have scope'
"should not have scope"
);
return done();
});
});

it('should run the callback (when disabled)', done => {
it("should run the callback (when disabled)", done => {
scopeManager.disable();
scopeManager.with(null, () => {
scopeManager.enable();
Expand All @@ -64,33 +64,59 @@ describe('NoopScopeManager', () => {
});
});

describe('.active()', () => {
it('should always return null (when enabled)', () => {
describe(".withAsync()", () => {
it("should run the callback (null as target)", async done => {
await scopeManager.withAsync(null, async () => done());
});

it("should run the callback (object as target)", async done => {
const test = { a: 1 };
await scopeManager.withAsync(test, async () => {
assert.strictEqual(
scopeManager.active(),
undefined,
"should not have scope"
);
return done();
});
});

it("should run the callback (when disabled)", async done => {
scopeManager.disable();
await scopeManager.withAsync(null, async () => {
scopeManager.enable();
return done();
});
});
});

describe(".active()", () => {
it("should always return null (when enabled)", () => {
assert.strictEqual(
scopeManager.active(),
undefined,
'should not have scope'
"should not have scope"
);
});

it('should always return null (when disabled)', () => {
it("should always return null (when disabled)", () => {
scopeManager.disable();
assert.strictEqual(
scopeManager.active(),
undefined,
'should not have scope'
"should not have scope"
);
scopeManager.enable();
});
});

describe('.bind()', () => {
it('should return the same target (when enabled)', () => {
describe(".bind()", () => {
it("should return the same target (when enabled)", () => {
const test = { a: 1 };
assert.deepStrictEqual(scopeManager.bind(test), test);
});

it('should return the same target (when disabled)', () => {
it("should return the same target (when disabled)", () => {
scopeManager.disable();
const test = { a: 1 };
assert.deepStrictEqual(scopeManager.bind(test), test);
Expand Down
Loading