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

Add no-op implementations of Tracer API #98

Merged
merged 4 commits into from
Jul 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { SpanContext, BinaryFormat } from '@opentelemetry/types';

/**
* No-op implementations of {@link BinaryFormat}.
*/
class NoopBinaryFormat implements BinaryFormat {
private readonly _buff = new ArrayBuffer(0);
// By default does nothing
toBytes(spanContext: SpanContext): ArrayBuffer {
return this._buff;
}

// By default does nothing
fromBytes(buf: ArrayBuffer): SpanContext | null {
return null;
}
}

export const NOOP_BINARY_FORMAT = new NoopBinaryFormat();
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { HttpTextFormat, SpanContext } from '@opentelemetry/types';

/**
* No-op implementations of {@link HttpTextFormat}.
*/
class NoopHttpTextFormat implements HttpTextFormat {
// By default does nothing
inject(spanContext: SpanContext, format: string, carrier: unknown): void {}
// By default does nothing
extract(format: string, carrier: unknown): SpanContext | null {
return null;
}
}

export const NOOP_HTTP_TEXT_FORMAT = new NoopHttpTextFormat();
1 change: 1 addition & 0 deletions packages/opentelemetry-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@
export * from './context/propagation/HttpTraceContext';
export * from './resources/Resource';
export * from './trace/NoopSpan';
export * from './trace/NoopTracer';
64 changes: 64 additions & 0 deletions packages/opentelemetry-core/src/trace/NoopTracer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {
Tracer,
SpanOptions,
Span,
HttpTextFormat,
BinaryFormat,
} from '@opentelemetry/types';
import { NOOP_HTTP_TEXT_FORMAT } from '../context/propagation/NoopHttpTextFormat';
import { NOOP_BINARY_FORMAT } from '../context/propagation/NoopBinaryFormat';
import { NoopSpan } from './NoopSpan';
import { INVALID_SPAN_CONTEXT } from './spancontext-utils';

export const NOOP_SPAN = new NoopSpan(INVALID_SPAN_CONTEXT);

/**
* No-op implementations of {@link Tracer}.
*/
export class NoopTracer implements Tracer {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that NOOP_SPAN is useful to return for non-sampled cases, but what would be the use case for NoopTracer?

If it's intended to just get some code started, what would you think about calling this TracerBase or similar? Would this be the class that eventually takes a scope storage in the constructor?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main reason for NoopTracer, at least for us, is when users disable the tracer then the implementation is switched to the noop to avoid unnecessary overhead. The noop tracer then uses only noop classes for the entire API surface.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, makes sense!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 on @rochdev comment (#98 (comment))

And In OpenTracing there is a concept of Global tracer (which user can set using initGlobalTracer() function). AFAIK Global tracer (global.Tracer()) should return minimal Tracer - NoopTracer if the app chooses to NOT use the SDK like basic-tracer or auto-tracer or vendor specific.

getCurrentSpan(): Span {
return NOOP_SPAN;
}

// startSpan starts a noop span.
startSpan(name: string, options?: SpanOptions): Span {
return NOOP_SPAN;
}

// @todo: dependency on https://github.com/open-telemetry/opentelemetry-js/pull/100, Use new return type.
withSpan<T extends (...args: unknown[]) => unknown>(
span: Span,
fn: T
): ReturnType<T> {
throw new Error('Method not implemented.');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tracer should never throw.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, added @todo for now (dependency on #100).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should discuss it on our next SIG the similar conversation here:
#99 (comment)

}

// By default does nothing
recordSpanData(span: Span): void {}

// By default does nothing
getBinaryFormat(): BinaryFormat {
return NOOP_BINARY_FORMAT;
}

// By default does nothing
getHttpTextFormat(): HttpTextFormat {
return NOOP_HTTP_TEXT_FORMAT;
}
}
35 changes: 35 additions & 0 deletions packages/opentelemetry-core/src/trace/spancontext-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { SpanContext } from '@opentelemetry/types';

/**
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export const INVALID_SPANID = '0';
export const INVALID_TRACEID = '0';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these be fully padded values? To my knowledge, there aren't many ways a single '0' can sneak into a span context.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the idea was if we are not able to extract the SpanContext correctly, then assign INVALID_SPAN_CONTEXT with INVALID_TRACEID(0) and INVALID_SPANID(0).

Just noticed, in the HTTP plugin we are using _emptySpanContext, ideally we should have used INVALID_SPAN_CONTEXT there.

/cc @dyladan @obecny

export const INVALID_SPAN_CONTEXT: SpanContext = {
traceId: INVALID_TRACEID,
spanId: INVALID_SPANID,
};

/**
* Returns true if this {@link SpanContext} is valid.
* @return true if this {@link SpanContext} is valid.
*/
export function isValid(spanContext: SpanContext): boolean {
return (
spanContext.traceId !== INVALID_TRACEID &&
spanContext.spanId !== INVALID_SPANID
);
}
56 changes: 56 additions & 0 deletions packages/opentelemetry-core/test/trace/NoopTracer.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import * as assert from 'assert';
import { NoopTracer, NOOP_SPAN } from '../../src/trace/NoopTracer';
import { SpanKind } from '@opentelemetry/types';

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

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

tracer.recordSpanData(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);

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

assert.throws(() => {
tracer.withSpan(NOOP_SPAN, () => {});
});
});
});
1 change: 1 addition & 0 deletions packages/opentelemetry-types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ export * from './trace/SpanOptions';
export * from './trace/span_context';
export * from './trace/span_kind';
export * from './trace/status';
export * from './trace/tracer';
export * from './trace/trace_options';
export * from './trace/trace_state';