opentelemetry-js/packages/opentelemetry-tracing/test/BasicTracerProvider.test.ts

349 lines
12 KiB
TypeScript

/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
context,
SpanContext,
TraceFlags,
ContextManager,
ROOT_CONTEXT,
setSpan,
setSpanContext,
} from '@opentelemetry/api';
import {
AlwaysOnSampler,
AlwaysOffSampler,
NoopLogger,
NoRecordingSpan,
TraceState,
} from '@opentelemetry/core';
import { Resource } from '@opentelemetry/resources';
import * as assert from 'assert';
import * as sinon from 'sinon';
import { BasicTracerProvider, Span } from '../src';
describe('BasicTracerProvider', () => {
let sandbox: sinon.SinonSandbox;
let removeEvent: Function | undefined;
beforeEach(() => {
context.disable();
sandbox = sinon.createSandbox();
});
afterEach(() => {
sandbox.restore();
if (removeEvent) {
removeEvent();
removeEvent = undefined;
}
});
describe('constructor', () => {
it('should construct an instance without any options', () => {
const provider = new BasicTracerProvider();
assert.ok(provider instanceof BasicTracerProvider);
});
it('should construct an instance with logger', () => {
const provider = new BasicTracerProvider({
logger: new NoopLogger(),
});
assert.ok(provider instanceof BasicTracerProvider);
});
it('should construct an instance with sampler', () => {
const provider = new BasicTracerProvider({
sampler: new AlwaysOnSampler(),
});
assert.ok(provider instanceof BasicTracerProvider);
});
it('should construct an instance with default trace params', () => {
const tracer = new BasicTracerProvider({}).getTracer('default');
assert.deepStrictEqual(tracer.getActiveTraceParams(), {
numberOfAttributesPerSpan: 1000,
numberOfEventsPerSpan: 1000,
numberOfLinksPerSpan: 1000,
});
});
it('should construct an instance with customized numberOfAttributesPerSpan trace params', () => {
const tracer = new BasicTracerProvider({
traceParams: {
numberOfAttributesPerSpan: 100,
},
}).getTracer('default');
assert.deepStrictEqual(tracer.getActiveTraceParams(), {
numberOfAttributesPerSpan: 100,
numberOfEventsPerSpan: 1000,
numberOfLinksPerSpan: 1000,
});
});
it('should construct an instance with customized numberOfEventsPerSpan trace params', () => {
const tracer = new BasicTracerProvider({
traceParams: {
numberOfEventsPerSpan: 300,
},
}).getTracer('default');
assert.deepStrictEqual(tracer.getActiveTraceParams(), {
numberOfAttributesPerSpan: 1000,
numberOfEventsPerSpan: 300,
numberOfLinksPerSpan: 1000,
});
});
it('should construct an instance with customized numberOfLinksPerSpan trace params', () => {
const tracer = new BasicTracerProvider({
traceParams: {
numberOfLinksPerSpan: 10,
},
}).getTracer('default');
assert.deepStrictEqual(tracer.getActiveTraceParams(), {
numberOfAttributesPerSpan: 1000,
numberOfEventsPerSpan: 1000,
numberOfLinksPerSpan: 10,
});
});
it('should construct an instance of BasicTracerProvider', () => {
const tracer = new BasicTracerProvider();
assert.ok(tracer instanceof BasicTracerProvider);
});
});
describe('.startSpan()', () => {
it('should start a span with name only', () => {
const tracer = new BasicTracerProvider().getTracer('default');
const span = tracer.startSpan('my-span');
assert.ok(span);
assert.ok(span instanceof Span);
});
it('should propagate resources', () => {
const tracerProvider = new BasicTracerProvider();
const tracer = tracerProvider.getTracer('default');
const span = tracer.startSpan('my-span') as Span;
assert.strictEqual(tracer.resource, tracerProvider.resource);
assert.strictEqual(span.resource, tracerProvider.resource);
});
it('should start a span with name and options', () => {
const tracer = new BasicTracerProvider().getTracer('default');
const span = tracer.startSpan('my-span', {});
assert.ok(span);
assert.ok(span instanceof Span);
const context = span.context();
assert.ok(context.traceId.match(/[a-f0-9]{32}/));
assert.ok(context.spanId.match(/[a-f0-9]{16}/));
assert.strictEqual(context.traceFlags, TraceFlags.SAMPLED);
assert.deepStrictEqual(context.traceState, undefined);
span.end();
});
it('should start a span with given attributes', () => {
const tracer = new BasicTracerProvider().getTracer('default');
const span = tracer.startSpan('my-span', {
attributes: { foo: 'foo', bar: 'bar' },
}) as Span;
assert.deepStrictEqual(span.attributes, { bar: 'bar', foo: 'foo' });
span.end();
});
it('should start a span with spanoptions->attributes', () => {
const tracer = new BasicTracerProvider().getTracer('default');
const span = tracer.startSpan('my-span', {
attributes: { foo: 'foo', bar: 'bar' },
}) as Span;
assert.deepStrictEqual(span.attributes, { foo: 'foo', bar: 'bar' });
span.end();
});
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',
{},
setSpanContext(ROOT_CONTEXT, {
traceId: 'd4cda95b652f4a1592b449d5929fda1b',
spanId: '6e0c63257de34c92',
traceFlags: TraceFlags.SAMPLED,
traceState: state,
})
);
assert.ok(span instanceof Span);
const context = span.context();
assert.strictEqual(context.traceId, 'd4cda95b652f4a1592b449d5929fda1b');
assert.strictEqual(context.traceFlags, TraceFlags.SAMPLED);
assert.deepStrictEqual(context.traceState, state);
span.end();
});
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',
{},
setSpan(ROOT_CONTEXT, span)
);
const context = childSpan.context();
assert.strictEqual(context.traceId, span.context().traceId);
assert.strictEqual(context.traceFlags, TraceFlags.SAMPLED);
span.end();
childSpan.end();
});
it('should create a root span when root is true', () => {
const tracer = new BasicTracerProvider().getTracer('default');
const span = tracer.startSpan('my-span');
const overrideParent = tracer.startSpan('my-parent-override-span');
const rootSpan = tracer.startSpan(
'root-span',
{ root: true },
setSpan(ROOT_CONTEXT, span)
);
const context = rootSpan.context();
assert.notStrictEqual(context.traceId, overrideParent.context().traceId);
span.end();
rootSpan.end();
});
it('should start a span with name and with invalid parent span', () => {
const tracer = new BasicTracerProvider().getTracer('default');
const span = tracer.startSpan(
'my-span',
{},
setSpanContext(
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',
{},
setSpanContext(ROOT_CONTEXT, {
traceId: '0',
spanId: '0',
traceFlags: TraceFlags.SAMPLED,
})
);
assert.ok(span instanceof Span);
const context = span.context();
assert.ok(context.traceId.match(/[a-f0-9]{32}/));
assert.ok(context.spanId.match(/[a-f0-9]{16}/));
assert.strictEqual(context.traceFlags, TraceFlags.SAMPLED);
assert.deepStrictEqual(context.traceState, undefined);
});
it('should return a no recording span when never sampling', () => {
const tracer = new BasicTracerProvider({
sampler: new AlwaysOffSampler(),
logger: new NoopLogger(),
}).getTracer('default');
const span = tracer.startSpan('my-span');
assert.ok(span instanceof NoRecordingSpan);
const context = span.context();
assert.ok(context.traceId.match(/[a-f0-9]{32}/));
assert.ok(context.spanId.match(/[a-f0-9]{16}/));
assert.strictEqual(context.traceFlags, TraceFlags.NONE);
assert.deepStrictEqual(context.traceState, undefined);
span.end();
});
it('should create real span when sampled', () => {
const tracer = new BasicTracerProvider({
sampler: new AlwaysOnSampler(),
}).getTracer('default');
const span = tracer.startSpan('my-span');
assert.ok(span instanceof Span);
assert.strictEqual(span.context().traceFlags, TraceFlags.SAMPLED);
assert.strictEqual(span.isRecording(), true);
});
it('should assign a resource', () => {
const tracer = new BasicTracerProvider().getTracer('default');
const span = tracer.startSpan('my-span') as Span;
assert.ok(span);
assert.ok(span.resource instanceof Resource);
});
});
describe('.getCurrentSpan()', () => {
it('should return current span when it exists', () => {
context.setGlobalContextManager({
active: () => setSpan(ROOT_CONTEXT, ('foo' as any) as Span),
disable: () => {},
} as ContextManager);
const tracer = new BasicTracerProvider().getTracer('default');
assert.deepStrictEqual(tracer.getCurrentSpan(), 'foo');
});
});
describe('.withSpan()', () => {
it('should run context with NoopContextManager context 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 context with NoopContextManager context 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('.resource', () => {
it('should return a Resource', () => {
const tracerProvider = new BasicTracerProvider();
assert.ok(tracerProvider.resource instanceof Resource);
});
});
describe('.shutdown()', () => {
it('should trigger shutdown when manually invoked', () => {
const tracerProvider = new BasicTracerProvider();
const shutdownStub = sandbox.stub(
tracerProvider.getActiveSpanProcessor(),
'shutdown'
);
tracerProvider.shutdown();
sinon.assert.calledOnce(shutdownStub);
});
});
});