opentelemetry-js/packages/opentelemetry-node/test/NodeTracerProvider.test.ts

247 lines
8.3 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,
TraceFlags,
propagation, trace,
} from '@opentelemetry/api';
import { AlwaysOnSampler, AlwaysOffSampler } from '@opentelemetry/core';
import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks';
import { Span } from '@opentelemetry/tracing';
import { Resource } from '@opentelemetry/resources';
import { ResourceAttributes } from '@opentelemetry/semantic-conventions';
import * as assert from 'assert';
import * as path from 'path';
import { ContextManager, ROOT_CONTEXT } from '@opentelemetry/api';
import { NodeTracerProvider } from '../src/NodeTracerProvider';
const sleep = (time: number) =>
new Promise(resolve => {
return setTimeout(resolve, time);
});
const INSTALLED_PLUGINS_PATH = path.join(
__dirname,
'instrumentation',
'node_modules'
);
describe('NodeTracerProvider', () => {
let provider: NodeTracerProvider;
let contextManager: ContextManager;
before(() => {
module.paths.push(INSTALLED_PLUGINS_PATH);
});
beforeEach(() => {
contextManager = new AsyncHooksContextManager();
context.setGlobalContextManager(contextManager.enable());
});
afterEach(() => {
// clear require cache
Object.keys(require.cache).forEach(key => delete require.cache[key]);
contextManager.disable();
context.disable();
});
describe('constructor', () => {
it('should construct an instance with required only options', () => {
provider = new NodeTracerProvider();
assert.ok(provider instanceof NodeTracerProvider);
});
it('should construct an instance with logger', () => {
provider = new NodeTracerProvider();
assert.ok(provider instanceof NodeTracerProvider);
});
it('should construct an instance with sampler', () => {
provider = new NodeTracerProvider({
sampler: new AlwaysOnSampler(),
});
assert.ok(provider instanceof NodeTracerProvider);
});
});
describe('.startSpan()', () => {
it('should start a span with name only', () => {
provider = new NodeTracerProvider();
const span = provider.getTracer('default').startSpan('my-span');
assert.ok(span);
});
it('should start a span with name and options', () => {
provider = new NodeTracerProvider();
const span = provider.getTracer('default').startSpan('my-span', {});
assert.ok(span);
});
it('should return a default span with no sampling (AlwaysOffSampler)', () => {
provider = new NodeTracerProvider({
sampler: new AlwaysOffSampler(),
});
const span = provider.getTracer('default').startSpan('my-span');
assert.strictEqual(span.spanContext().traceFlags, TraceFlags.NONE);
assert.strictEqual(span.isRecording(), false);
});
it('should start a recording span with always sampling (AlwaysOnSampler)', () => {
provider = new NodeTracerProvider({
sampler: new AlwaysOnSampler(),
});
const span = provider.getTracer('default').startSpan('my-span');
assert.ok(span instanceof Span);
assert.strictEqual(span.spanContext().traceFlags, TraceFlags.SAMPLED);
assert.strictEqual(span.isRecording(), true);
});
it('should sample with AlwaysOnSampler if parent was not sampled', () => {
provider = new NodeTracerProvider({
sampler: new AlwaysOnSampler(),
});
const sampledParent = provider.getTracer('default').startSpan(
'not-sampled-span',
{},
trace.setSpanContext(ROOT_CONTEXT, {
traceId: 'd4cda95b652f4a1592b449d5929fda1b',
spanId: '6e0c63257de34c92',
traceFlags: TraceFlags.NONE,
})
);
assert.ok(sampledParent instanceof Span);
assert.strictEqual(
sampledParent.spanContext().traceFlags,
TraceFlags.SAMPLED
);
assert.strictEqual(sampledParent.isRecording(), true);
const span = provider
.getTracer('default')
.startSpan('child-span', {}, trace.setSpan(ROOT_CONTEXT, sampledParent));
assert.ok(span instanceof Span);
assert.strictEqual(span.spanContext().traceFlags, TraceFlags.SAMPLED);
assert.strictEqual(span.isRecording(), true);
});
it('should assign resource to span', () => {
provider = new NodeTracerProvider();
const span = provider.getTracer('default').startSpan('my-span') as Span;
assert.ok(span);
assert.ok(span.resource instanceof Resource);
assert.equal(
span.resource.attributes[ResourceAttributes.TELEMETRY_SDK_LANGUAGE],
'nodejs'
);
});
});
describe('.withSpan()', () => {
it('should run context with AsyncHooksContextManager context manager', done => {
provider = new NodeTracerProvider({});
const span = provider.getTracer('default').startSpan('my-span');
context.with(trace.setSpan(context.active(), span), () => {
assert.deepStrictEqual(trace.getSpan(context.active()), span);
return done();
});
assert.deepStrictEqual(trace.getSpan(context.active()), undefined);
});
it('should run context with AsyncHooksContextManager context manager with multiple spans', done => {
provider = new NodeTracerProvider({});
const span = provider.getTracer('default').startSpan('my-span');
context.with(trace.setSpan(context.active(), span), () => {
assert.deepStrictEqual(trace.getSpan(context.active()), span);
const span1 = provider.getTracer('default').startSpan('my-span1');
context.with(trace.setSpan(context.active(), span1), () => {
assert.deepStrictEqual(trace.getSpan(context.active()), span1);
assert.deepStrictEqual(
span1.spanContext().traceId,
span.spanContext().traceId
);
return done();
});
});
// when span ended.
// @todo: below check is not running.
assert.deepStrictEqual(trace.getSpan(context.active()), undefined);
});
it('should find correct context with promises', async () => {
provider = new NodeTracerProvider();
const span = provider.getTracer('default').startSpan('my-span');
await context.with(trace.setSpan(context.active(), span), async () => {
for (let i = 0; i < 3; i++) {
await sleep(5).then(() => {
assert.deepStrictEqual(trace.getSpan(context.active()), span);
});
}
});
assert.deepStrictEqual(trace.getSpan(context.active()), undefined);
});
});
describe('.bind()', () => {
it('should bind context with AsyncHooksContextManager context manager', done => {
const provider = new NodeTracerProvider({});
const span = provider.getTracer('default').startSpan('my-span');
const fn = () => {
assert.deepStrictEqual(trace.getSpan(context.active()), span);
return done();
};
const patchedFn = context.bind(fn, trace.setSpan(context.active(), span));
return patchedFn();
});
});
describe('.register()', () => {
let originalPropagators: string | number | undefined | string[];
beforeEach(() => {
originalPropagators = process.env.OTEL_PROPAGATORS;
});
afterEach(() => {
// otherwise we may assign 'undefined' (a string)
if (originalPropagators !== undefined) {
(process.env as any).OTEL_PROPAGATORS = originalPropagators;
} else {
delete (process.env as any).OTEL_PROPAGATORS;
}
});
it('should allow propagators as per the specification', () => {
(process.env as any).OTEL_PROPAGATORS = 'b3,b3multi,jaeger';
const provider = new NodeTracerProvider();
provider.register();
assert.deepStrictEqual(propagation.fields(), [
'b3',
'x-b3-traceid',
'x-b3-spanid',
'x-b3-flags',
'x-b3-sampled',
'x-b3-parentspanid',
'uber-trace-id',
]);
});
});
});