feat(sdk-trace-base): add dropped attributes and events count on span (#3576)

* feat(sdk-trace-base): add dropped attributes and events count on span

* feat(sdk-trace-base): sync with main

* feat(sdk-trace-base): [revert] sync with main

* feat(sdk-trace-base): fix tests, make both counts truthy

* feat(sdk-trace-base): Update dropped counts type to number

* feat(sdk-trace-base): Use exposed counts on ReadableSpan in jaeger and zipkin exporters

* feat(sdk-trace-base): WIP - revert tsconfig, add new attribute limits

* feat(sdk-trace-base): Update attribute truncate method

* Fix bad conflict resolution

* Remove unused import

* chore: lint

* feat(sdk-trace-base): Remove dropped event attributes logic, add Jaeger field

* feat(sdk-trace-base): Undo formatting in changelog

* fix(changelog): Update experimental changelog

* Update packages/opentelemetry-sdk-trace-base/src/Span.ts

Co-authored-by: Gerhard Stöbich <deb2001-github@yahoo.de>

* Update packages/opentelemetry-sdk-trace-base/src/Span.ts

Co-authored-by: Gerhard Stöbich <deb2001-github@yahoo.de>

* fix(sdk-trace-base): Resolve review comments

* fix(sdk-trace-base): Fix failing tests

* fix(sdk-trace-base): make droppedAttributesCount on event optional

* fix(changelog): Update changelog files

---------

Co-authored-by: Daniel Dyla <dyladan@users.noreply.github.com>
Co-authored-by: Gerhard Stöbich <deb2001-github@yahoo.de>
This commit is contained in:
Mohit Karekar 2023-03-08 04:47:04 +01:00 committed by GitHub
parent 95bf6fc7cc
commit 48ecf229eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 295 additions and 62 deletions

View File

@ -20,6 +20,7 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/
* feat: support TraceState in SamplingResult [#3530](https://github.com/open-telemetry/opentelemetry-js/pull/3530) @raphael-theriault-swi
* feat(sdk-trace-base): add diagnostic logging when spans are dropped [#3610](https://github.com/open-telemetry/opentelemetry-js/pull/3610) @neoeinstein
* feat: add unit to view instrument selection criteria [#3647](https://github.com/open-telemetry/opentelemetry-js/pull/3647) @jlabatut
* feat(tracing): expose dropped counts for attributes, events and links on span [#3576](https://github.com/open-telemetry/opentelemetry-js/pull/3576) @mohitk05
### :bug: (Bug Fix)

View File

@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file.
### :rocket: (Enhancement)
* feat(api): add `getActiveBaggage` API [#3385](https://github.com/open-telemetry/opentelemetry-js/pull/3385)
* feat(api): add optional `droppedAttributesCount` property in the `Link` interface [#3576](https://github.com/open-telemetry/opentelemetry-js/pull/3576) @mohitk05
### :bug: (Bug Fix)

View File

@ -37,4 +37,6 @@ export interface Link {
context: SpanContext;
/** A set of {@link SpanAttributes} on the link. */
attributes?: SpanAttributes;
/** Count of attributes of the link that were dropped due to collection limits */
droppedAttributesCount?: number;
}

View File

@ -16,6 +16,7 @@ All notable changes to experimental packages in this project will be documented
* feat(sdk-node): install diag logger with OTEL_LOG_LEVEL [#3627](https://github.com/open-telemetry/opentelemetry-js/pull/3627) @legendecas
* feat(otlp-exporter-base): add retries [#3207](https://github.com/open-telemetry/opentelemetry-js/pull/3207) @svetlanabrennan
* feat(sdk-node): override IdGenerator when using NodeSDK [#3645](https://github.com/open-telemetry/opentelemetry-js/pull/3645) @haddasbronfman
* feat(otlp-transformer): expose dropped attributes, events and links counts on the transformed otlp span [#3576](https://github.com/open-telemetry/opentelemetry-js/pull/3576) @mohitk05
### :bug: (Bug Fix)

View File

@ -61,22 +61,34 @@ export const mockedReadableSpan: ReadableSpan = {
},
],
events: [
{ name: 'fetchStart', time: [1574120165, 429803070] },
{
name: 'fetchStart',
time: [1574120165, 429803070],
},
{
name: 'domainLookupStart',
time: [1574120165, 429803070],
},
{ name: 'domainLookupEnd', time: [1574120165, 429803070] },
{
name: 'domainLookupEnd',
time: [1574120165, 429803070],
},
{
name: 'connectStart',
time: [1574120165, 429803070],
},
{ name: 'connectEnd', time: [1574120165, 429803070] },
{
name: 'connectEnd',
time: [1574120165, 429803070],
},
{
name: 'requestStart',
time: [1574120165, 435513070],
},
{ name: 'responseStart', time: [1574120165, 436923070] },
{
name: 'responseStart',
time: [1574120165, 436923070],
},
{
name: 'responseEnd',
time: [1574120165, 438688070],
@ -91,6 +103,9 @@ export const mockedReadableSpan: ReadableSpan = {
})
),
instrumentationLibrary: { name: 'default', version: '0.0.1' },
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
};
export function ensureExportedEventsAreCorrect(events: IEvent[]) {

View File

@ -72,22 +72,34 @@ export const mockedReadableSpan: ReadableSpan = {
},
],
events: [
{ name: 'fetchStart', time: [1574120165, 429803070] },
{
name: 'fetchStart',
time: [1574120165, 429803070],
},
{
name: 'domainLookupStart',
time: [1574120165, 429803070],
},
{ name: 'domainLookupEnd', time: [1574120165, 429803070] },
{
name: 'domainLookupEnd',
time: [1574120165, 429803070],
},
{
name: 'connectStart',
time: [1574120165, 429803070],
},
{ name: 'connectEnd', time: [1574120165, 429803070] },
{
name: 'connectEnd',
time: [1574120165, 429803070],
},
{
name: 'requestStart',
time: [1574120165, 435513070],
},
{ name: 'responseStart', time: [1574120165, 436923070] },
{
name: 'responseStart',
time: [1574120165, 436923070],
},
{
name: 'responseEnd',
time: [1574120165, 438688070],
@ -102,6 +114,9 @@ export const mockedReadableSpan: ReadableSpan = {
})
),
instrumentationLibrary: { name: 'default', version: '0.0.1' },
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
};
export const mockedResources: Resource[] = [
@ -142,6 +157,9 @@ export const basicTrace: ReadableSpan[] = [
duration: [0, 8885000],
resource: mockedResources[0],
instrumentationLibrary: mockedInstrumentationLibraries[0],
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
},
{
name: 'span2',
@ -164,6 +182,9 @@ export const basicTrace: ReadableSpan[] = [
duration: [0, 8775000],
resource: mockedResources[0],
instrumentationLibrary: mockedInstrumentationLibraries[0],
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
},
{
name: 'span3',
@ -186,6 +207,9 @@ export const basicTrace: ReadableSpan[] = [
duration: [0, 8775000],
resource: mockedResources[0],
instrumentationLibrary: mockedInstrumentationLibraries[0],
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
},
];

View File

@ -59,22 +59,34 @@ export const mockedReadableSpan: ReadableSpan = {
},
],
events: [
{ name: 'fetchStart', time: [1574120165, 429803070] },
{
name: 'fetchStart',
time: [1574120165, 429803070],
},
{
name: 'domainLookupStart',
time: [1574120165, 429803070],
},
{ name: 'domainLookupEnd', time: [1574120165, 429803070] },
{
name: 'domainLookupEnd',
time: [1574120165, 429803070],
},
{
name: 'connectStart',
time: [1574120165, 429803070],
},
{ name: 'connectEnd', time: [1574120165, 429803070] },
{
name: 'connectEnd',
time: [1574120165, 429803070],
},
{
name: 'requestStart',
time: [1574120165, 435513070],
},
{ name: 'responseStart', time: [1574120165, 436923070] },
{
name: 'responseStart',
time: [1574120165, 436923070],
},
{
name: 'responseEnd',
time: [1574120165, 438688070],
@ -87,6 +99,9 @@ export const mockedReadableSpan: ReadableSpan = {
cost: 112.12,
}),
instrumentationLibrary: { name: 'default', version: '0.0.1' },
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
};
export function ensureProtoEventsAreCorrect(events: IEvent[]) {

View File

@ -61,22 +61,34 @@ export const mockedReadableSpan: ReadableSpan = {
},
],
events: [
{ name: 'fetchStart', time: [1574120165, 429803070] },
{
name: 'fetchStart',
time: [1574120165, 429803070],
},
{
name: 'domainLookupStart',
time: [1574120165, 429803070],
},
{ name: 'domainLookupEnd', time: [1574120165, 429803070] },
{
name: 'domainLookupEnd',
time: [1574120165, 429803070],
},
{
name: 'connectStart',
time: [1574120165, 429803070],
},
{ name: 'connectEnd', time: [1574120165, 429803070] },
{
name: 'connectEnd',
time: [1574120165, 429803070],
},
{
name: 'requestStart',
time: [1574120165, 435513070],
},
{ name: 'responseStart', time: [1574120165, 436923070] },
{
name: 'responseStart',
time: [1574120165, 436923070],
},
{
name: 'responseEnd',
time: [1574120165, 438688070],
@ -91,6 +103,9 @@ export const mockedReadableSpan: ReadableSpan = {
})
),
instrumentationLibrary: { name: 'default', version: '0.0.1' },
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
};
export function ensureExportedEventsAreCorrect(events: IEvent[]) {

View File

@ -39,16 +39,16 @@ export function sdkSpanToOtlpSpan(span: ReadableSpan, useHex?: boolean): ISpan {
startTimeUnixNano: hrTimeToNanoseconds(span.startTime),
endTimeUnixNano: hrTimeToNanoseconds(span.endTime),
attributes: toAttributes(span.attributes),
droppedAttributesCount: 0,
droppedAttributesCount: span.droppedAttributesCount,
events: span.events.map(toOtlpSpanEvent),
droppedEventsCount: 0,
droppedEventsCount: span.droppedEventsCount,
status: {
// API and proto enums share the same values
code: status.code as unknown as EStatusCode,
message: status.message,
},
links: span.links.map(link => toOtlpLink(link, useHex)),
droppedLinksCount: 0,
droppedLinksCount: span.droppedLinksCount,
};
}
@ -62,7 +62,7 @@ export function toOtlpLink(link: Link, useHex?: boolean): ILink {
? link.context.traceId
: core.hexToBase64(link.context.traceId),
traceState: link.context.traceState?.serialize(),
droppedAttributesCount: 0,
droppedAttributesCount: link.droppedAttributesCount || 0,
};
}
@ -73,6 +73,6 @@ export function toOtlpSpanEvent(timedEvent: TimedEvent): IEvent {
: [],
name: timedEvent.name,
timeUnixNano: hrTimeToNanoseconds(timedEvent.time),
droppedAttributesCount: 0,
droppedAttributesCount: timedEvent.droppedAttributesCount || 0,
};
}

View File

@ -178,6 +178,9 @@ describe('Trace', () => {
status: {
code: SpanStatusCode.OK,
},
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
};
});

View File

@ -47,6 +47,8 @@ const ENVIRONMENT_NUMBERS_KEYS = [
'OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT',
'OTEL_SPAN_EVENT_COUNT_LIMIT',
'OTEL_SPAN_LINK_COUNT_LIMIT',
'OTEL_SPAN_ATTRIBUTE_PER_EVENT_COUNT_LIMIT',
'OTEL_SPAN_ATTRIBUTE_PER_LINK_COUNT_LIMIT',
'OTEL_EXPORTER_OTLP_TIMEOUT',
'OTEL_EXPORTER_OTLP_TRACES_TIMEOUT',
'OTEL_EXPORTER_OTLP_METRICS_TIMEOUT',
@ -131,6 +133,9 @@ export const DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT = Infinity;
export const DEFAULT_ATTRIBUTE_COUNT_LIMIT = 128;
export const DEFAULT_SPAN_ATTRIBUTE_PER_EVENT_COUNT_LIMIT = 128;
export const DEFAULT_SPAN_ATTRIBUTE_PER_LINK_COUNT_LIMIT = 128;
/**
* Default environment variables
*/
@ -172,6 +177,10 @@ export const DEFAULT_ENVIRONMENT: Required<ENVIRONMENT> = {
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: DEFAULT_ATTRIBUTE_COUNT_LIMIT,
OTEL_SPAN_EVENT_COUNT_LIMIT: 128,
OTEL_SPAN_LINK_COUNT_LIMIT: 128,
OTEL_SPAN_ATTRIBUTE_PER_EVENT_COUNT_LIMIT:
DEFAULT_SPAN_ATTRIBUTE_PER_EVENT_COUNT_LIMIT,
OTEL_SPAN_ATTRIBUTE_PER_LINK_COUNT_LIMIT:
DEFAULT_SPAN_ATTRIBUTE_PER_LINK_COUNT_LIMIT,
OTEL_TRACES_EXPORTER: '',
OTEL_TRACES_SAMPLER: TracesSamplerValues.ParentBasedAlwaysOn,
OTEL_TRACES_SAMPLER_ARG: '',

View File

@ -86,6 +86,30 @@ export function spanToThrift(span: ReadableSpan): ThriftSpan {
});
}
/* Add droppedAttributesCount as a tag */
if (span.droppedAttributesCount) {
tags.push({
key: 'otel.dropped_attributes_count',
value: toTagValue(span.droppedAttributesCount),
});
}
/* Add droppedEventsCount as a tag */
if (span.droppedEventsCount) {
tags.push({
key: 'otel.dropped_events_count',
value: toTagValue(span.droppedEventsCount),
});
}
/* Add droppedLinksCount as a tag */
if (span.droppedLinksCount) {
tags.push({
key: 'otel.dropped_links_count',
value: toTagValue(span.droppedLinksCount),
});
}
const spanTags: ThriftTag[] = ThriftUtils.getThriftTags(tags);
const logs = span.events.map((event): Log => {
@ -96,6 +120,12 @@ export function spanToThrift(span: ReadableSpan): ThriftSpan {
fields.push({ key: attr, value: toTagValue(attrs[attr]) })
);
}
if (event.droppedAttributesCount) {
fields.push({
key: 'otel.event.dropped_attributes_count',
value: event.droppedAttributesCount,
});
}
return { timestamp: hrTimeToMilliseconds(event.time), fields };
});
const spanLogs: ThriftLog[] = ThriftUtils.getThriftLogs(logs);

View File

@ -53,6 +53,9 @@ describe('JaegerExporter', () => {
name: 'default',
version: '0.0.1',
},
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
};
describe('constructor', () => {
afterEach(() => {

View File

@ -82,6 +82,9 @@ describe('transform', () => {
name: 'default',
version: '0.0.1',
},
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
};
const thriftSpan = spanToThrift(readableSpan);
@ -179,6 +182,9 @@ describe('transform', () => {
name: 'default',
version: '0.0.1',
},
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
};
const thriftSpan = spanToThrift(readableSpan);
@ -246,6 +252,9 @@ describe('transform', () => {
name: 'default',
version: '0.0.1',
},
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
};
const thriftSpan = spanToThrift(readableSpan);
@ -291,6 +300,9 @@ describe('transform', () => {
name: 'default',
version: '0.0.1',
},
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
};
const thriftSpan = spanToThrift(readableSpan);
@ -353,6 +365,9 @@ describe('transform', () => {
name: 'default',
version: '0.0.1',
},
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
};
let thriftSpan = spanToThrift(readableSpan);
assert.strictEqual(

View File

@ -18,7 +18,6 @@ import * as api from '@opentelemetry/api';
import { ReadableSpan, TimedEvent } from '@opentelemetry/sdk-trace-base';
import { hrTimeToMicroseconds } from '@opentelemetry/core';
import * as zipkinTypes from './types';
import { IResource } from '@opentelemetry/resources';
const ZIPKIN_SPAN_KIND_MAPPING = {
[api.SpanKind.CLIENT]: zipkinTypes.SpanKind.CLIENT,
@ -51,13 +50,7 @@ export function toZipkinSpan(
timestamp: hrTimeToMicroseconds(span.startTime),
duration: hrTimeToMicroseconds(span.duration),
localEndpoint: { serviceName },
tags: _toZipkinTags(
span.attributes,
span.status,
statusCodeTagName,
statusErrorTagName,
span.resource
),
tags: _toZipkinTags(span, statusCodeTagName, statusErrorTagName),
annotations: span.events.length
? _toZipkinAnnotations(span.events)
: undefined,
@ -66,13 +59,18 @@ export function toZipkinSpan(
return zipkinSpan;
}
/** Converts OpenTelemetry SpanAttributes and SpanStatus to Zipkin Tags format. */
/** Converts OpenTelemetry Span properties to Zipkin Tags format. */
export function _toZipkinTags(
attributes: api.SpanAttributes,
status: api.SpanStatus,
{
attributes,
resource,
status,
droppedAttributesCount,
droppedEventsCount,
droppedLinksCount,
}: ReadableSpan,
statusCodeTagName: string,
statusErrorTagName: string,
resource: IResource
statusErrorTagName: string
): zipkinTypes.Tags {
const tags: { [key: string]: string } = {};
for (const key of Object.keys(attributes)) {
@ -84,6 +82,20 @@ export function _toZipkinTags(
if (status.code === api.SpanStatusCode.ERROR && status.message) {
tags[statusErrorTagName] = status.message;
}
/* Add droppedAttributesCount as a tag */
if (droppedAttributesCount) {
tags['otel.dropped_attributes_count'] = String(droppedAttributesCount);
}
/* Add droppedEventsCount as a tag */
if (droppedEventsCount) {
tags['otel.dropped_events_count'] = String(droppedEventsCount);
}
/* Add droppedLinksCount as a tag */
if (droppedLinksCount) {
tags['otel.dropped_links_count'] = String(droppedLinksCount);
}
Object.keys(resource.attributes).forEach(
name => (tags[name] = String(resource.attributes[name]))

View File

@ -36,6 +36,9 @@ const tracer = new BasicTracerProvider({
resource: Resource.default().merge(
new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'zipkin-test',
cost: '112.12',
service: 'ui',
version: '1',
})
),
}).getTracer('default');
@ -50,13 +53,6 @@ const spanContext: api.SpanContext = {
traceFlags: api.TraceFlags.SAMPLED,
};
const DUMMY_RESOURCE = new Resource({
service: 'ui',
version: 1,
cost: 112.12,
[SemanticResourceAttributes.SERVICE_NAME]: 'zipkin-test',
});
describe('transform', () => {
describe('toZipkinSpan', () => {
it('should convert an OpenTelemetry span to a Zipkin span', () => {
@ -102,6 +98,9 @@ describe('transform', () => {
key1: 'value1',
key2: 'value2',
[SemanticResourceAttributes.SERVICE_NAME]: 'zipkin-test',
cost: '112.12',
service: 'ui',
version: '1',
'telemetry.sdk.language': language,
'telemetry.sdk.name': 'opentelemetry',
'telemetry.sdk.version': VERSION,
@ -140,6 +139,9 @@ describe('transform', () => {
parentId: undefined,
tags: {
[SemanticResourceAttributes.SERVICE_NAME]: 'zipkin-test',
cost: '112.12',
service: 'ui',
version: '1',
'telemetry.sdk.language': language,
'telemetry.sdk.name': 'opentelemetry',
'telemetry.sdk.version': VERSION,
@ -188,6 +190,9 @@ describe('transform', () => {
parentId: undefined,
tags: {
[SemanticResourceAttributes.SERVICE_NAME]: 'zipkin-test',
cost: '112.12',
service: 'ui',
version: '1',
'telemetry.sdk.language': language,
'telemetry.sdk.name': 'opentelemetry',
'telemetry.sdk.version': VERSION,
@ -214,20 +219,21 @@ describe('transform', () => {
key2: 'value2',
});
const tags: zipkinTypes.Tags = _toZipkinTags(
span.attributes,
span.status,
span,
defaultStatusCodeTagName,
defaultStatusErrorTagName,
DUMMY_RESOURCE
defaultStatusErrorTagName
);
assert.deepStrictEqual(tags, {
key1: 'value1',
key2: 'value2',
[SemanticResourceAttributes.SERVICE_NAME]: 'zipkin-test',
'telemetry.sdk.language': language,
'telemetry.sdk.name': 'opentelemetry',
'telemetry.sdk.version': VERSION,
cost: '112.12',
service: 'ui',
version: '1',
[SemanticResourceAttributes.SERVICE_NAME]: 'zipkin-test',
});
});
it('should map OpenTelemetry SpanStatus.code to a Zipkin tag', () => {
@ -248,15 +254,9 @@ describe('transform', () => {
key2: 'value2',
});
const tags: zipkinTypes.Tags = _toZipkinTags(
span.attributes,
span.status,
span,
defaultStatusCodeTagName,
defaultStatusErrorTagName,
Resource.empty().merge(
new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'zipkin-test',
})
)
defaultStatusErrorTagName
);
assert.deepStrictEqual(tags, {
@ -264,6 +264,12 @@ describe('transform', () => {
key2: 'value2',
[defaultStatusCodeTagName]: 'ERROR',
[SemanticResourceAttributes.SERVICE_NAME]: 'zipkin-test',
'telemetry.sdk.language': language,
'telemetry.sdk.name': 'opentelemetry',
'telemetry.sdk.version': VERSION,
cost: '112.12',
service: 'ui',
version: '1',
});
});
it('should map OpenTelemetry SpanStatus.message to a Zipkin tag', () => {
@ -285,15 +291,9 @@ describe('transform', () => {
key2: 'value2',
});
const tags: zipkinTypes.Tags = _toZipkinTags(
span.attributes,
span.status,
span,
defaultStatusCodeTagName,
defaultStatusErrorTagName,
Resource.empty().merge(
new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'zipkin-test',
})
)
defaultStatusErrorTagName
);
assert.deepStrictEqual(tags, {
@ -302,6 +302,12 @@ describe('transform', () => {
[defaultStatusCodeTagName]: 'ERROR',
[defaultStatusErrorTagName]: status.message,
[SemanticResourceAttributes.SERVICE_NAME]: 'zipkin-test',
'telemetry.sdk.language': language,
'telemetry.sdk.name': 'opentelemetry',
'telemetry.sdk.version': VERSION,
cost: '112.12',
service: 'ui',
version: '1',
});
});
});

View File

@ -45,6 +45,9 @@ export const mockedReadableSpan: ReadableSpan = {
cost: 112.12,
}),
instrumentationLibrary: { name: 'default', version: '0.0.1' },
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
};
export function ensureHeadersContain(

View File

@ -56,6 +56,9 @@ function getReadableSpan() {
events: [],
resource: Resource.empty(),
instrumentationLibrary: { name: 'default', version: '0.0.1' },
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
};
return readableSpan;
}
@ -164,6 +167,9 @@ describe('Zipkin Exporter - node', () => {
],
resource: Resource.empty(),
instrumentationLibrary: { name: 'default', version: '0.0.1' },
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
};
const span2: ReadableSpan = {
name: 'my-span',
@ -187,6 +193,9 @@ describe('Zipkin Exporter - node', () => {
events: [],
resource: Resource.empty(),
instrumentationLibrary: { name: 'default', version: '0.0.1' },
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
};
const exporter = new ZipkinExporter({
@ -379,6 +388,9 @@ describe('Zipkin Exporter - node', () => {
[SemanticResourceAttributes.SERVICE_NAME]: resource_service_name,
}),
instrumentationLibrary: { name: 'default', version: '0.0.1' },
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
};
const span2: ReadableSpan = {
name: 'my-span',
@ -402,6 +414,9 @@ describe('Zipkin Exporter - node', () => {
[SemanticResourceAttributes.SERVICE_NAME]: resource_service_name_prime,
}),
instrumentationLibrary: { name: 'default', version: '0.0.1' },
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
};
const exporter = new ZipkinExporter({});
@ -467,6 +482,9 @@ describe('Zipkin Exporter - node', () => {
],
resource: Resource.empty(),
instrumentationLibrary: { name: 'default', version: '0.0.1' },
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
};
const span2: ReadableSpan = {
name: 'my-span',
@ -490,6 +508,9 @@ describe('Zipkin Exporter - node', () => {
events: [],
resource: Resource.empty(),
instrumentationLibrary: { name: 'default', version: '0.0.1' },
droppedAttributesCount: 0,
droppedEventsCount: 0,
droppedLinksCount: 0,
};
const exporter = new ZipkinExporter({});

View File

@ -66,6 +66,11 @@ export class Span implements APISpan, ReadableSpan {
readonly startTime: HrTime;
readonly resource: IResource;
readonly instrumentationLibrary: InstrumentationLibrary;
private _droppedAttributesCount = 0;
private _droppedEventsCount: number = 0;
private _droppedLinksCount: number = 0;
name: string;
status: SpanStatus = {
code: SpanStatusCode.UNSET,
@ -141,6 +146,7 @@ export class Span implements APISpan, ReadableSpan {
this._spanLimits.attributeCountLimit! &&
!Object.prototype.hasOwnProperty.call(this.attributes, key)
) {
this._droppedAttributesCount++;
return this;
}
this.attributes[key] = this._truncateToSize(value);
@ -169,11 +175,13 @@ export class Span implements APISpan, ReadableSpan {
if (this._isSpanEnded()) return this;
if (this._spanLimits.eventCountLimit === 0) {
diag.warn('No events allowed.');
this._droppedEventsCount++;
return this;
}
if (this.events.length >= this._spanLimits.eventCountLimit!) {
diag.warn('Dropping extra events.');
this.events.shift();
this._droppedEventsCount++;
}
if (isTimeInput(attributesOrStartTime)) {
@ -184,10 +192,12 @@ export class Span implements APISpan, ReadableSpan {
}
const attributes = sanitizeAttributes(attributesOrStartTime);
this.events.push({
name,
attributes,
time: this._getTime(timeStamp),
droppedAttributesCount: 0,
});
return this;
}
@ -298,6 +308,18 @@ export class Span implements APISpan, ReadableSpan {
return this._ended;
}
get droppedAttributesCount(): number {
return this._droppedAttributesCount;
}
get droppedEventsCount(): number {
return this._droppedEventsCount;
}
get droppedLinksCount(): number {
return this._droppedLinksCount;
}
private _isSpanEnded(): boolean {
if (this._ended) {
diag.warn(

View File

@ -26,4 +26,6 @@ export interface TimedEvent {
name: string;
/** The attributes of the event. */
attributes?: SpanAttributes;
/** Count of attributes of the event that were dropped due to collection limits */
droppedAttributesCount?: number;
}

View File

@ -49,6 +49,10 @@ export function loadDefaultConfig() {
attributeCountLimit: getEnv().OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT,
linkCountLimit: getEnv().OTEL_SPAN_LINK_COUNT_LIMIT,
eventCountLimit: getEnv().OTEL_SPAN_EVENT_COUNT_LIMIT,
attributePerEventCountLimit:
getEnv().OTEL_SPAN_ATTRIBUTE_PER_EVENT_COUNT_LIMIT,
attributePerLinkCountLimit:
getEnv().OTEL_SPAN_ATTRIBUTE_PER_LINK_COUNT_LIMIT,
},
};
}

View File

@ -41,4 +41,7 @@ export interface ReadableSpan {
readonly ended: boolean;
readonly resource: IResource;
readonly instrumentationLibrary: InstrumentationLibrary;
readonly droppedAttributesCount: number;
readonly droppedEventsCount: number;
readonly droppedLinksCount: number;
}

View File

@ -81,6 +81,10 @@ export interface SpanLimits {
linkCountLimit?: number;
/** eventCountLimit is number of message events per span */
eventCountLimit?: number;
/** attributePerEventCountLimit is the maximum number of attributes allowed per span event */
attributePerEventCountLimit?: number;
/** attributePerLinkCountLimit is the maximum number of attributes allowed per span link */
attributePerLinkCountLimit?: number;
}
/** Interface configuration for a buffer. */

View File

@ -179,6 +179,8 @@ describe('BasicTracerProvider', () => {
attributeCountLimit: 128,
eventCountLimit: 128,
linkCountLimit: 128,
attributePerEventCountLimit: 128,
attributePerLinkCountLimit: 128,
});
});
});

View File

@ -312,6 +312,10 @@ describe('Span', () => {
assert.strictEqual(span.attributes['foo99'], 'bar99');
assert.strictEqual(span.attributes['foo149'], undefined);
});
it('should store the count of dropped attributes in droppedAttributesCount', () => {
assert.strictEqual(span.droppedAttributesCount, 50);
});
});
describe('when "attributeValueLengthLimit" option defined', () => {
@ -791,6 +795,22 @@ describe('Span', () => {
assert.strictEqual(span.events[span.events.length - 1].name, 'sent149');
});
it('should store the count of dropped events in droppedEventsCount', () => {
const span = new Span(
tracer,
ROOT_CONTEXT,
name,
spanContext,
SpanKind.CLIENT
);
for (let i = 0; i < 150; i++) {
span.addEvent('sent' + i);
}
span.end();
assert.strictEqual(span.droppedEventsCount, 50);
});
it('should add no event', () => {
const tracer = new BasicTracerProvider({
spanLimits: {