744 lines
19 KiB
TypeScript
744 lines
19 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 {
|
|
SpanStatusCode,
|
|
Exception,
|
|
ROOT_CONTEXT,
|
|
SpanContext,
|
|
SpanKind,
|
|
TraceFlags,
|
|
} from '@opentelemetry/api';
|
|
import {
|
|
hrTime,
|
|
hrTimeDuration,
|
|
hrTimeToMilliseconds,
|
|
hrTimeToNanoseconds,
|
|
} from '@opentelemetry/core';
|
|
import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
|
|
import * as assert from 'assert';
|
|
import { BasicTracerProvider, Span, SpanProcessor } from '../src';
|
|
|
|
const performanceTimeOrigin = hrTime();
|
|
|
|
describe('Span', () => {
|
|
const tracer = new BasicTracerProvider({
|
|
spanLimits: {
|
|
attributeCountLimit: 100,
|
|
eventCountLimit: 100,
|
|
},
|
|
}).getTracer('default');
|
|
const name = 'span1';
|
|
const spanContext: SpanContext = {
|
|
traceId: 'd4cda95b652f4a1592b449d5929fda1b',
|
|
spanId: '6e0c63257de34c92',
|
|
traceFlags: TraceFlags.SAMPLED,
|
|
};
|
|
const linkContext: SpanContext = {
|
|
traceId: 'e4cda95b652f4a1592b449d5929fda1b',
|
|
spanId: '7e0c63257de34c92',
|
|
traceFlags: TraceFlags.SAMPLED
|
|
};
|
|
|
|
it('should create a Span instance', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
name,
|
|
spanContext,
|
|
SpanKind.SERVER
|
|
);
|
|
assert.ok(span instanceof Span);
|
|
span.end();
|
|
});
|
|
|
|
it('should have valid startTime', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
name,
|
|
spanContext,
|
|
SpanKind.SERVER
|
|
);
|
|
assert.ok(
|
|
hrTimeToMilliseconds(span.startTime) >
|
|
hrTimeToMilliseconds(performanceTimeOrigin)
|
|
);
|
|
});
|
|
|
|
it('should have valid endTime', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
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,
|
|
ROOT_CONTEXT,
|
|
name,
|
|
spanContext,
|
|
SpanKind.SERVER
|
|
);
|
|
span.end();
|
|
assert.ok(hrTimeToNanoseconds(span.duration) >= 0);
|
|
});
|
|
|
|
it('should have valid event.time', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
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,
|
|
ROOT_CONTEXT,
|
|
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,
|
|
ROOT_CONTEXT,
|
|
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,
|
|
ROOT_CONTEXT,
|
|
name,
|
|
spanContext,
|
|
SpanKind.CLIENT
|
|
);
|
|
const context = span.spanContext();
|
|
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();
|
|
});
|
|
|
|
describe('isRecording', () => {
|
|
it('should return true when span is not ended', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
name,
|
|
spanContext,
|
|
SpanKind.CLIENT
|
|
);
|
|
assert.ok(span.isRecording());
|
|
span.end();
|
|
});
|
|
it('should return false when span is ended', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
name,
|
|
spanContext,
|
|
SpanKind.CLIENT
|
|
);
|
|
span.end();
|
|
assert.ok(span.isRecording() === false);
|
|
});
|
|
});
|
|
|
|
it('should set an attribute', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
name,
|
|
spanContext,
|
|
SpanKind.CLIENT
|
|
);
|
|
|
|
span.setAttribute('string', 'string');
|
|
span.setAttribute('number', 0);
|
|
span.setAttribute('bool', true);
|
|
span.setAttribute('array<string>', ['str1', 'str2']);
|
|
span.setAttribute('array<number>', [1, 2]);
|
|
span.setAttribute('array<bool>', [true, false]);
|
|
|
|
//@ts-expect-error invalid attribute type object
|
|
span.setAttribute('object', { foo: 'bar' });
|
|
//@ts-expect-error invalid attribute inhomogenous array
|
|
span.setAttribute('non-homogeneous-array', [0, '']);
|
|
|
|
assert.deepStrictEqual(span.attributes, {
|
|
string: 'string',
|
|
number: 0,
|
|
bool: true,
|
|
'array<string>': ['str1', 'str2'],
|
|
'array<number>': [1, 2],
|
|
'array<bool>': [true, false],
|
|
});
|
|
});
|
|
|
|
it('should overwrite attributes', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
name,
|
|
spanContext,
|
|
SpanKind.CLIENT
|
|
);
|
|
|
|
span.setAttribute('overwrite', 'initial value');
|
|
span.setAttribute('overwrite', 'overwritten value');
|
|
|
|
assert.deepStrictEqual(span.attributes, {
|
|
overwrite: 'overwritten value',
|
|
});
|
|
});
|
|
|
|
it('should set attributes', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
name,
|
|
spanContext,
|
|
SpanKind.CLIENT
|
|
);
|
|
|
|
span.setAttributes({
|
|
string: 'string',
|
|
number: 0,
|
|
bool: true,
|
|
'array<string>': ['str1', 'str2'],
|
|
'array<number>': [1, 2],
|
|
'array<bool>': [true, false],
|
|
//@ts-expect-error invalid attribute type object
|
|
object: { foo: 'bar' },
|
|
//@ts-expect-error invalid attribute inhomogenous array
|
|
'non-homogeneous-array': [0, ''],
|
|
});
|
|
|
|
assert.deepStrictEqual(span.attributes, {
|
|
string: 'string',
|
|
number: 0,
|
|
bool: true,
|
|
'array<string>': ['str1', 'str2'],
|
|
'array<number>': [1, 2],
|
|
'array<bool>': [true, false],
|
|
});
|
|
});
|
|
|
|
it('should set an event', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
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 linkContext: SpanContext = {
|
|
traceId: 'b3cda95b652f4a1592b449d5929fda1b',
|
|
spanId: '6e0c63257de34c92',
|
|
traceFlags: TraceFlags.SAMPLED
|
|
};
|
|
const attributes = { attr1: 'value', attr2: 123, attr3: true };
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
name,
|
|
spanContext,
|
|
SpanKind.CLIENT,
|
|
'12345',
|
|
[{ context: linkContext }, { context: linkContext, attributes }]
|
|
);
|
|
span.end();
|
|
});
|
|
|
|
it('should drop extra attributes and events', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
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, 100);
|
|
assert.strictEqual(Object.keys(span.attributes).length, 100);
|
|
assert.strictEqual(span.events[span.events.length - 1].name, 'sent149');
|
|
assert.strictEqual(span.attributes['foo0'], 'bar0');
|
|
assert.strictEqual(span.attributes['foo99'], 'bar99');
|
|
assert.strictEqual(span.attributes['sent100'], undefined);
|
|
});
|
|
|
|
it('should set an error status', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
name,
|
|
spanContext,
|
|
SpanKind.CLIENT
|
|
);
|
|
span.setStatus({
|
|
code: SpanStatusCode.ERROR,
|
|
message: 'This is an error',
|
|
});
|
|
span.end();
|
|
});
|
|
|
|
it('should return ReadableSpan', () => {
|
|
const parentId = '5c1c63257de34c67';
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
'my-span',
|
|
spanContext,
|
|
SpanKind.INTERNAL,
|
|
parentId
|
|
);
|
|
|
|
assert.strictEqual(span.name, 'my-span');
|
|
assert.strictEqual(span.kind, SpanKind.INTERNAL);
|
|
assert.strictEqual(span.parentSpanId, parentId);
|
|
assert.strictEqual(span.spanContext().traceId, spanContext.traceId);
|
|
assert.deepStrictEqual(span.status, {
|
|
code: SpanStatusCode.UNSET,
|
|
});
|
|
assert.deepStrictEqual(span.attributes, {});
|
|
assert.deepStrictEqual(span.links, []);
|
|
assert.deepStrictEqual(span.events, []);
|
|
|
|
assert.ok(span.instrumentationLibrary);
|
|
const { name, version } = span.instrumentationLibrary;
|
|
assert.strictEqual(name, 'default');
|
|
assert.strictEqual(version, undefined);
|
|
});
|
|
|
|
it('should return ReadableSpan with attributes', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
'my-span',
|
|
spanContext,
|
|
SpanKind.CLIENT
|
|
);
|
|
span.setAttribute('attr1', 'value1');
|
|
assert.deepStrictEqual(span.attributes, { attr1: 'value1' });
|
|
|
|
span.setAttributes({ attr2: 123, attr1: false });
|
|
assert.deepStrictEqual(span.attributes, {
|
|
attr1: false,
|
|
attr2: 123,
|
|
});
|
|
|
|
span.end();
|
|
// shouldn't add new attribute
|
|
span.setAttribute('attr3', 'value3');
|
|
assert.deepStrictEqual(span.attributes, {
|
|
attr1: false,
|
|
attr2: 123,
|
|
});
|
|
});
|
|
|
|
it('should return ReadableSpan with links', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
'my-span',
|
|
spanContext,
|
|
SpanKind.CLIENT,
|
|
undefined,
|
|
[
|
|
{ context: linkContext },
|
|
{
|
|
context: linkContext,
|
|
attributes: { attr1: 'value', attr2: 123, attr3: true },
|
|
},
|
|
]
|
|
);
|
|
assert.strictEqual(span.links.length, 2);
|
|
assert.deepStrictEqual(span.links, [
|
|
{
|
|
context: linkContext,
|
|
},
|
|
{
|
|
attributes: { attr1: 'value', attr2: 123, attr3: true },
|
|
context: linkContext,
|
|
},
|
|
]);
|
|
|
|
span.end();
|
|
});
|
|
|
|
it('should return ReadableSpan with events', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
'my-span',
|
|
spanContext,
|
|
SpanKind.CLIENT
|
|
);
|
|
span.addEvent('sent');
|
|
assert.strictEqual(span.events.length, 1);
|
|
const [event] = span.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 });
|
|
assert.strictEqual(span.events.length, 2);
|
|
const [event1, event2] = span.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(span.events.length, 2);
|
|
});
|
|
|
|
it('should return ReadableSpan with new status', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
name,
|
|
spanContext,
|
|
SpanKind.CLIENT
|
|
);
|
|
span.setStatus({
|
|
code: SpanStatusCode.ERROR,
|
|
message: 'This is an error',
|
|
});
|
|
assert.strictEqual(span.status.code, SpanStatusCode.ERROR);
|
|
assert.strictEqual(span.status.message, 'This is an error');
|
|
span.end();
|
|
|
|
// shouldn't update status
|
|
span.setStatus({
|
|
code: SpanStatusCode.OK,
|
|
message: 'OK',
|
|
});
|
|
assert.strictEqual(span.status.code, SpanStatusCode.ERROR);
|
|
});
|
|
|
|
it('should only end a span once', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
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,
|
|
ROOT_CONTEXT,
|
|
name,
|
|
spanContext,
|
|
SpanKind.SERVER
|
|
);
|
|
span.updateName('foo-span');
|
|
span.end();
|
|
|
|
// shouldn't update name
|
|
span.updateName('bar-span');
|
|
assert.strictEqual(span.name, 'foo-span');
|
|
});
|
|
|
|
it('should have ended', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
name,
|
|
spanContext,
|
|
SpanKind.SERVER
|
|
);
|
|
assert.strictEqual(span.ended, false);
|
|
span.end();
|
|
assert.strictEqual(span.ended, true);
|
|
});
|
|
|
|
describe('span processor', () => {
|
|
it('should call onStart synchronously when span is started', () => {
|
|
let started = false;
|
|
const processor: SpanProcessor = {
|
|
onStart: () => {
|
|
started = true;
|
|
},
|
|
forceFlush: () => Promise.resolve(),
|
|
onEnd() {},
|
|
shutdown: () => Promise.resolve(),
|
|
};
|
|
|
|
const provider = new BasicTracerProvider();
|
|
|
|
provider.addSpanProcessor(processor);
|
|
|
|
provider.getTracer('default').startSpan('test');
|
|
assert.ok(started);
|
|
});
|
|
|
|
it('should call onEnd synchronously when span is ended', () => {
|
|
let ended = false;
|
|
const processor: SpanProcessor = {
|
|
onStart: () => {},
|
|
forceFlush: () => Promise.resolve(),
|
|
onEnd() {
|
|
ended = true;
|
|
},
|
|
shutdown: () => Promise.resolve(),
|
|
};
|
|
|
|
const provider = new BasicTracerProvider();
|
|
|
|
provider.addSpanProcessor(processor);
|
|
|
|
provider.getTracer('default').startSpan('test').end();
|
|
assert.ok(ended);
|
|
});
|
|
|
|
it('should call onStart with a writeable span', () => {
|
|
const processor: SpanProcessor = {
|
|
onStart: span => {
|
|
span.setAttribute('attr', true);
|
|
},
|
|
forceFlush: () => Promise.resolve(),
|
|
onEnd() {},
|
|
shutdown: () => Promise.resolve(),
|
|
};
|
|
|
|
const provider = new BasicTracerProvider();
|
|
|
|
provider.addSpanProcessor(processor);
|
|
|
|
const s = provider.getTracer('default').startSpan('test') as Span;
|
|
assert.ok(s.attributes.attr);
|
|
});
|
|
});
|
|
|
|
describe('recordException', () => {
|
|
const invalidExceptions: any[] = [
|
|
1,
|
|
null,
|
|
undefined,
|
|
{ foo: 'bar' },
|
|
{ stack: 'bar' },
|
|
['a', 'b', 'c'],
|
|
];
|
|
|
|
invalidExceptions.forEach(key => {
|
|
describe(`when exception is (${JSON.stringify(key)})`, () => {
|
|
it('should NOT record an exception', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
name,
|
|
spanContext,
|
|
SpanKind.CLIENT
|
|
);
|
|
assert.strictEqual(span.events.length, 0);
|
|
span.recordException(key);
|
|
assert.strictEqual(span.events.length, 0);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when exception type is "string"', () => {
|
|
let error: Exception;
|
|
beforeEach(() => {
|
|
error = 'boom';
|
|
});
|
|
it('should record an exception', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
name,
|
|
spanContext,
|
|
SpanKind.CLIENT
|
|
);
|
|
assert.strictEqual(span.events.length, 0);
|
|
span.recordException(error);
|
|
|
|
const event = span.events[0];
|
|
assert.strictEqual(event.name, 'exception');
|
|
assert.deepStrictEqual(event.attributes, {
|
|
'exception.message': 'boom',
|
|
});
|
|
assert.ok(event.time[0] > 0);
|
|
});
|
|
});
|
|
|
|
const errorsObj = [
|
|
{
|
|
description: 'code',
|
|
obj: { code: 'Error', message: 'boom', stack: 'bar' },
|
|
},
|
|
{
|
|
description: 'name',
|
|
obj: { name: 'Error', message: 'boom', stack: 'bar' },
|
|
},
|
|
];
|
|
errorsObj.forEach(errorObj => {
|
|
describe(`when exception type is an object with ${errorObj.description}`, () => {
|
|
const error: Exception = errorObj.obj;
|
|
it('should record an exception', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
name,
|
|
spanContext,
|
|
SpanKind.CLIENT
|
|
);
|
|
assert.strictEqual(span.events.length, 0);
|
|
span.recordException(error);
|
|
|
|
const event = span.events[0];
|
|
assert.ok(event.time[0] > 0);
|
|
assert.strictEqual(event.name, 'exception');
|
|
|
|
assert.ok(event.attributes);
|
|
|
|
const type = event.attributes[SemanticAttributes.EXCEPTION_TYPE];
|
|
const message =
|
|
event.attributes[SemanticAttributes.EXCEPTION_MESSAGE];
|
|
const stacktrace = String(
|
|
event.attributes[SemanticAttributes.EXCEPTION_STACKTRACE]
|
|
);
|
|
assert.strictEqual(type, 'Error');
|
|
assert.strictEqual(message, 'boom');
|
|
assert.strictEqual(stacktrace, 'bar');
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when time is provided', () => {
|
|
it('should record an exception with provided time', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
name,
|
|
spanContext,
|
|
SpanKind.CLIENT
|
|
);
|
|
assert.strictEqual(span.events.length, 0);
|
|
span.recordException('boom', [0, 123]);
|
|
const event = span.events[0];
|
|
assert.deepStrictEqual(event.time, [0, 123]);
|
|
});
|
|
});
|
|
|
|
describe('when exception code is numeric', () => {
|
|
it('should record an exception with string value', () => {
|
|
const span = new Span(
|
|
tracer,
|
|
ROOT_CONTEXT,
|
|
name,
|
|
spanContext,
|
|
SpanKind.CLIENT
|
|
);
|
|
assert.strictEqual(span.events.length, 0);
|
|
span.recordException({ code: 12 });
|
|
const event = span.events[0];
|
|
assert.deepStrictEqual(event.attributes, {
|
|
[SemanticAttributes.EXCEPTION_TYPE]: '12',
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|