353 lines
10 KiB
TypeScript
353 lines
10 KiB
TypeScript
/*!
|
|
* 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 {
|
|
SpanKind,
|
|
CanonicalCode,
|
|
TraceFlags,
|
|
SpanContext,
|
|
} from '@opentelemetry/types';
|
|
import { BasicTracerRegistry, Span } from '../src';
|
|
import {
|
|
hrTime,
|
|
hrTimeToNanoseconds,
|
|
hrTimeToMilliseconds,
|
|
NoopLogger,
|
|
hrTimeDuration,
|
|
} from '@opentelemetry/core';
|
|
|
|
const performanceTimeOrigin = hrTime();
|
|
|
|
describe('Span', () => {
|
|
const tracer = new BasicTracerRegistry({
|
|
logger: new NoopLogger(),
|
|
}).getTracer('default');
|
|
const name = 'span1';
|
|
const spanContext: SpanContext = {
|
|
traceId: 'd4cda95b652f4a1592b449d5929fda1b',
|
|
spanId: '6e0c63257de34c92',
|
|
traceFlags: TraceFlags.SAMPLED,
|
|
};
|
|
|
|
it('should create a Span instance', () => {
|
|
const span = new Span(tracer, name, spanContext, SpanKind.SERVER);
|
|
assert.ok(span instanceof Span);
|
|
span.end();
|
|
});
|
|
|
|
it('should have valid startTime', () => {
|
|
const span = new Span(tracer, name, spanContext, SpanKind.SERVER);
|
|
assert.ok(
|
|
hrTimeToMilliseconds(span.startTime) >
|
|
hrTimeToMilliseconds(performanceTimeOrigin)
|
|
);
|
|
});
|
|
|
|
it('should have valid endTime', () => {
|
|
const span = new Span(tracer, name, spanContext, SpanKind.SERVER);
|
|
span.end();
|
|
assert.ok(
|
|
hrTimeToNanoseconds(span.endTime) >= hrTimeToNanoseconds(span.startTime),
|
|
'end time must be bigger or equal start time'
|
|
);
|
|
|
|
assert.ok(
|
|
hrTimeToMilliseconds(span.endTime) >
|
|
hrTimeToMilliseconds(performanceTimeOrigin),
|
|
'end time must be bigger than time origin'
|
|
);
|
|
});
|
|
|
|
it('should have a duration', () => {
|
|
const span = new Span(tracer, name, spanContext, SpanKind.SERVER);
|
|
span.end();
|
|
assert.ok(hrTimeToNanoseconds(span.duration) >= 0);
|
|
});
|
|
|
|
it('should have valid event.time', () => {
|
|
const span = new Span(tracer, name, spanContext, SpanKind.SERVER);
|
|
span.addEvent('my-event');
|
|
assert.ok(
|
|
hrTimeToMilliseconds(span.events[0].time) >
|
|
hrTimeToMilliseconds(performanceTimeOrigin)
|
|
);
|
|
});
|
|
|
|
it('should have an entered time for event', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
name,
|
|
spanContext,
|
|
SpanKind.SERVER,
|
|
undefined,
|
|
[],
|
|
0
|
|
);
|
|
const timeMS = 123;
|
|
const spanStartTime = hrTimeToMilliseconds(span.startTime);
|
|
const eventTime = spanStartTime + timeMS;
|
|
|
|
span.addEvent('my-event', undefined, eventTime);
|
|
|
|
const diff = hrTimeDuration(span.startTime, span.events[0].time);
|
|
assert.strictEqual(hrTimeToMilliseconds(diff), 123);
|
|
});
|
|
|
|
describe('when 2nd param is "TimeInput" type', () => {
|
|
it('should have an entered time for event - ', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
name,
|
|
spanContext,
|
|
SpanKind.SERVER,
|
|
undefined,
|
|
[],
|
|
0
|
|
);
|
|
const timeMS = 123;
|
|
const spanStartTime = hrTimeToMilliseconds(span.startTime);
|
|
const eventTime = spanStartTime + timeMS;
|
|
|
|
span.addEvent('my-event', eventTime);
|
|
|
|
const diff = hrTimeDuration(span.startTime, span.events[0].time);
|
|
assert.strictEqual(hrTimeToMilliseconds(diff), 123);
|
|
});
|
|
});
|
|
|
|
it('should get the span context of span', () => {
|
|
const span = new Span(tracer, name, spanContext, SpanKind.CLIENT);
|
|
const context = span.context();
|
|
assert.strictEqual(context.traceId, spanContext.traceId);
|
|
assert.strictEqual(context.traceFlags, TraceFlags.SAMPLED);
|
|
assert.strictEqual(context.traceState, undefined);
|
|
assert.ok(context.spanId.match(/[a-f0-9]{16}/));
|
|
assert.ok(span.isRecording());
|
|
span.end();
|
|
});
|
|
|
|
it('should return true when isRecording:true', () => {
|
|
const span = new Span(tracer, name, spanContext, SpanKind.CLIENT);
|
|
assert.ok(span.isRecording());
|
|
span.end();
|
|
});
|
|
|
|
it('should set an attribute', () => {
|
|
const span = new Span(tracer, name, spanContext, SpanKind.CLIENT);
|
|
|
|
['String', 'Number', 'Boolean'].forEach(attType => {
|
|
span.setAttribute('testKey' + attType, 'testValue' + attType);
|
|
});
|
|
span.setAttribute('object', { foo: 'bar' });
|
|
span.end();
|
|
});
|
|
|
|
it('should set an event', () => {
|
|
const span = new Span(tracer, name, spanContext, SpanKind.CLIENT);
|
|
span.addEvent('sent');
|
|
span.addEvent('rev', { attr1: 'value', attr2: 123, attr3: true });
|
|
span.end();
|
|
});
|
|
|
|
it('should set a link', () => {
|
|
const spanContext: SpanContext = {
|
|
traceId: 'a3cda95b652f4a1592b449d5929fda1b',
|
|
spanId: '5e0c63257de34c92',
|
|
traceFlags: TraceFlags.SAMPLED,
|
|
};
|
|
const attributes = { attr1: 'value', attr2: 123, attr3: true };
|
|
const span = new Span(tracer, name, spanContext, SpanKind.CLIENT, '12345', [
|
|
{ spanContext },
|
|
{ spanContext, attributes },
|
|
]);
|
|
span.end();
|
|
});
|
|
|
|
it('should drop extra links, attributes and events', () => {
|
|
const span = new Span(tracer, name, spanContext, SpanKind.CLIENT);
|
|
for (let i = 0; i < 150; i++) {
|
|
span.setAttribute('foo' + i, 'bar' + i);
|
|
span.addEvent('sent' + i);
|
|
}
|
|
span.end();
|
|
|
|
assert.strictEqual(span.events.length, 128);
|
|
assert.strictEqual(Object.keys(span.attributes).length, 32);
|
|
assert.strictEqual(span.events[span.events.length - 1].name, 'sent149');
|
|
assert.strictEqual(span.attributes['foo0'], undefined);
|
|
assert.strictEqual(span.attributes['foo149'], 'bar149');
|
|
});
|
|
|
|
it('should set an error status', () => {
|
|
const span = new Span(tracer, name, spanContext, SpanKind.CLIENT);
|
|
span.setStatus({
|
|
code: CanonicalCode.PERMISSION_DENIED,
|
|
message: 'This is an error',
|
|
});
|
|
span.end();
|
|
});
|
|
|
|
it('should return ReadableSpan', () => {
|
|
const parentId = '5c1c63257de34c67';
|
|
const span = new Span(
|
|
tracer,
|
|
'my-span',
|
|
spanContext,
|
|
SpanKind.INTERNAL,
|
|
parentId
|
|
);
|
|
|
|
const readableSpan = span.toReadableSpan();
|
|
assert.strictEqual(readableSpan.name, 'my-span');
|
|
assert.strictEqual(readableSpan.kind, SpanKind.INTERNAL);
|
|
assert.strictEqual(readableSpan.parentSpanId, parentId);
|
|
assert.strictEqual(readableSpan.spanContext.traceId, spanContext.traceId);
|
|
assert.deepStrictEqual(readableSpan.status, {
|
|
code: CanonicalCode.OK,
|
|
});
|
|
assert.deepStrictEqual(readableSpan.attributes, {});
|
|
assert.deepStrictEqual(readableSpan.links, []);
|
|
assert.deepStrictEqual(readableSpan.events, []);
|
|
});
|
|
|
|
it('should return ReadableSpan with attributes', () => {
|
|
const span = new Span(tracer, 'my-span', spanContext, SpanKind.CLIENT);
|
|
span.setAttribute('attr1', 'value1');
|
|
let readableSpan = span.toReadableSpan();
|
|
assert.deepStrictEqual(readableSpan.attributes, { attr1: 'value1' });
|
|
|
|
span.setAttributes({ attr2: 123, attr1: false });
|
|
readableSpan = span.toReadableSpan();
|
|
assert.deepStrictEqual(readableSpan.attributes, {
|
|
attr1: false,
|
|
attr2: 123,
|
|
});
|
|
|
|
span.end();
|
|
// shouldn't add new attribute
|
|
span.setAttribute('attr3', 'value3');
|
|
readableSpan = span.toReadableSpan();
|
|
assert.deepStrictEqual(readableSpan.attributes, {
|
|
attr1: false,
|
|
attr2: 123,
|
|
});
|
|
});
|
|
|
|
it('should return ReadableSpan with links', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
'my-span',
|
|
spanContext,
|
|
SpanKind.CLIENT,
|
|
undefined,
|
|
[
|
|
{ spanContext },
|
|
{
|
|
spanContext,
|
|
attributes: { attr1: 'value', attr2: 123, attr3: true },
|
|
},
|
|
]
|
|
);
|
|
const readableSpan = span.toReadableSpan();
|
|
assert.strictEqual(readableSpan.links.length, 2);
|
|
assert.deepStrictEqual(readableSpan.links, [
|
|
{
|
|
spanContext,
|
|
},
|
|
{
|
|
attributes: { attr1: 'value', attr2: 123, attr3: true },
|
|
spanContext,
|
|
},
|
|
]);
|
|
|
|
span.end();
|
|
});
|
|
|
|
it('should return ReadableSpan with events', () => {
|
|
const span = new Span(tracer, 'my-span', spanContext, SpanKind.CLIENT);
|
|
span.addEvent('sent');
|
|
let readableSpan = span.toReadableSpan();
|
|
assert.strictEqual(readableSpan.events.length, 1);
|
|
const [event] = readableSpan.events;
|
|
assert.deepStrictEqual(event.name, 'sent');
|
|
assert.ok(!event.attributes);
|
|
assert.ok(event.time[0] > 0);
|
|
|
|
span.addEvent('rev', { attr1: 'value', attr2: 123, attr3: true });
|
|
readableSpan = span.toReadableSpan();
|
|
assert.strictEqual(readableSpan.events.length, 2);
|
|
const [event1, event2] = readableSpan.events;
|
|
assert.deepStrictEqual(event1.name, 'sent');
|
|
assert.ok(!event1.attributes);
|
|
assert.ok(event1.time[0] > 0);
|
|
assert.deepStrictEqual(event2.name, 'rev');
|
|
assert.deepStrictEqual(event2.attributes, {
|
|
attr1: 'value',
|
|
attr2: 123,
|
|
attr3: true,
|
|
});
|
|
assert.ok(event2.time[0] > 0);
|
|
|
|
span.end();
|
|
// shouldn't add new event
|
|
span.addEvent('sent');
|
|
assert.strictEqual(readableSpan.events.length, 2);
|
|
readableSpan = span.toReadableSpan();
|
|
assert.strictEqual(readableSpan.events.length, 2);
|
|
});
|
|
|
|
it('should return ReadableSpan with new status', () => {
|
|
const span = new Span(tracer, name, spanContext, SpanKind.CLIENT);
|
|
span.setStatus({
|
|
code: CanonicalCode.PERMISSION_DENIED,
|
|
message: 'This is an error',
|
|
});
|
|
const readableSpan = span.toReadableSpan();
|
|
assert.strictEqual(
|
|
readableSpan.status.code,
|
|
CanonicalCode.PERMISSION_DENIED
|
|
);
|
|
assert.strictEqual(readableSpan.status.message, 'This is an error');
|
|
span.end();
|
|
|
|
// shouldn't update status
|
|
span.setStatus({
|
|
code: CanonicalCode.OK,
|
|
message: 'OK',
|
|
});
|
|
assert.strictEqual(span.status.code, CanonicalCode.PERMISSION_DENIED);
|
|
});
|
|
|
|
it('should only end a span once', () => {
|
|
const span = new Span(tracer, name, spanContext, SpanKind.SERVER);
|
|
const endTime = Date.now();
|
|
span.end(endTime);
|
|
span.end(endTime + 10);
|
|
assert.deepStrictEqual(span.endTime[0], Math.trunc(endTime / 1000));
|
|
});
|
|
|
|
it('should update name', () => {
|
|
const span = new Span(tracer, name, spanContext, SpanKind.SERVER);
|
|
span.updateName('foo-span');
|
|
span.end();
|
|
|
|
// shouldn't update name
|
|
span.updateName('bar-span');
|
|
assert.strictEqual(span.name, 'foo-span');
|
|
});
|
|
});
|