diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a0b731305..cb3b53e4d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/api/CHANGELOG.md b/api/CHANGELOG.md index 31697a308c..646b83c1de 100644 --- a/api/CHANGELOG.md +++ b/api/CHANGELOG.md @@ -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) diff --git a/api/src/trace/link.ts b/api/src/trace/link.ts index 1d81d38d69..2b7ed19dd7 100644 --- a/api/src/trace/link.ts +++ b/api/src/trace/link.ts @@ -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; } diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 7d5a24082a..a9d94b05c8 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -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) diff --git a/experimental/packages/exporter-trace-otlp-grpc/test/traceHelper.ts b/experimental/packages/exporter-trace-otlp-grpc/test/traceHelper.ts index e95868f305..6ce80bf2c2 100644 --- a/experimental/packages/exporter-trace-otlp-grpc/test/traceHelper.ts +++ b/experimental/packages/exporter-trace-otlp-grpc/test/traceHelper.ts @@ -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[]) { diff --git a/experimental/packages/exporter-trace-otlp-http/test/traceHelper.ts b/experimental/packages/exporter-trace-otlp-http/test/traceHelper.ts index feab35b55e..06e256d253 100644 --- a/experimental/packages/exporter-trace-otlp-http/test/traceHelper.ts +++ b/experimental/packages/exporter-trace-otlp-http/test/traceHelper.ts @@ -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, }, ]; diff --git a/experimental/packages/exporter-trace-otlp-proto/test/traceHelper.ts b/experimental/packages/exporter-trace-otlp-proto/test/traceHelper.ts index 67ea6593bc..5e2b71c573 100644 --- a/experimental/packages/exporter-trace-otlp-proto/test/traceHelper.ts +++ b/experimental/packages/exporter-trace-otlp-proto/test/traceHelper.ts @@ -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[]) { diff --git a/experimental/packages/otlp-grpc-exporter-base/test/traceHelper.ts b/experimental/packages/otlp-grpc-exporter-base/test/traceHelper.ts index 8aef302d05..13103b405b 100644 --- a/experimental/packages/otlp-grpc-exporter-base/test/traceHelper.ts +++ b/experimental/packages/otlp-grpc-exporter-base/test/traceHelper.ts @@ -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[]) { diff --git a/experimental/packages/otlp-transformer/src/trace/internal.ts b/experimental/packages/otlp-transformer/src/trace/internal.ts index e4d787349e..6811a8182f 100644 --- a/experimental/packages/otlp-transformer/src/trace/internal.ts +++ b/experimental/packages/otlp-transformer/src/trace/internal.ts @@ -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, }; } diff --git a/experimental/packages/otlp-transformer/test/trace.test.ts b/experimental/packages/otlp-transformer/test/trace.test.ts index 7308139004..da3e1d0c7d 100644 --- a/experimental/packages/otlp-transformer/test/trace.test.ts +++ b/experimental/packages/otlp-transformer/test/trace.test.ts @@ -178,6 +178,9 @@ describe('Trace', () => { status: { code: SpanStatusCode.OK, }, + droppedAttributesCount: 0, + droppedEventsCount: 0, + droppedLinksCount: 0, }; }); diff --git a/packages/opentelemetry-core/src/utils/environment.ts b/packages/opentelemetry-core/src/utils/environment.ts index 5d529d08a9..57983cdeef 100644 --- a/packages/opentelemetry-core/src/utils/environment.ts +++ b/packages/opentelemetry-core/src/utils/environment.ts @@ -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 = { 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: '', diff --git a/packages/opentelemetry-exporter-jaeger/src/transform.ts b/packages/opentelemetry-exporter-jaeger/src/transform.ts index 61321e78aa..b91e0c2929 100644 --- a/packages/opentelemetry-exporter-jaeger/src/transform.ts +++ b/packages/opentelemetry-exporter-jaeger/src/transform.ts @@ -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); diff --git a/packages/opentelemetry-exporter-jaeger/test/jaeger.test.ts b/packages/opentelemetry-exporter-jaeger/test/jaeger.test.ts index 7a6130322b..bcfdbf051f 100644 --- a/packages/opentelemetry-exporter-jaeger/test/jaeger.test.ts +++ b/packages/opentelemetry-exporter-jaeger/test/jaeger.test.ts @@ -53,6 +53,9 @@ describe('JaegerExporter', () => { name: 'default', version: '0.0.1', }, + droppedAttributesCount: 0, + droppedEventsCount: 0, + droppedLinksCount: 0, }; describe('constructor', () => { afterEach(() => { diff --git a/packages/opentelemetry-exporter-jaeger/test/transform.test.ts b/packages/opentelemetry-exporter-jaeger/test/transform.test.ts index 5b9de96aa5..35a11c7ffd 100644 --- a/packages/opentelemetry-exporter-jaeger/test/transform.test.ts +++ b/packages/opentelemetry-exporter-jaeger/test/transform.test.ts @@ -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( diff --git a/packages/opentelemetry-exporter-zipkin/src/transform.ts b/packages/opentelemetry-exporter-zipkin/src/transform.ts index c3c1887616..94983057f6 100644 --- a/packages/opentelemetry-exporter-zipkin/src/transform.ts +++ b/packages/opentelemetry-exporter-zipkin/src/transform.ts @@ -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])) diff --git a/packages/opentelemetry-exporter-zipkin/test/common/transform.test.ts b/packages/opentelemetry-exporter-zipkin/test/common/transform.test.ts index 3c7b5a5ddb..65a06922af 100644 --- a/packages/opentelemetry-exporter-zipkin/test/common/transform.test.ts +++ b/packages/opentelemetry-exporter-zipkin/test/common/transform.test.ts @@ -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', }); }); }); diff --git a/packages/opentelemetry-exporter-zipkin/test/helper.ts b/packages/opentelemetry-exporter-zipkin/test/helper.ts index 3cfd694e2b..bd4d300a1e 100644 --- a/packages/opentelemetry-exporter-zipkin/test/helper.ts +++ b/packages/opentelemetry-exporter-zipkin/test/helper.ts @@ -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( diff --git a/packages/opentelemetry-exporter-zipkin/test/node/zipkin.test.ts b/packages/opentelemetry-exporter-zipkin/test/node/zipkin.test.ts index d1aa4e8888..eeab75348e 100644 --- a/packages/opentelemetry-exporter-zipkin/test/node/zipkin.test.ts +++ b/packages/opentelemetry-exporter-zipkin/test/node/zipkin.test.ts @@ -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({}); diff --git a/packages/opentelemetry-sdk-trace-base/src/Span.ts b/packages/opentelemetry-sdk-trace-base/src/Span.ts index fb2b717d48..0f742855d1 100644 --- a/packages/opentelemetry-sdk-trace-base/src/Span.ts +++ b/packages/opentelemetry-sdk-trace-base/src/Span.ts @@ -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( diff --git a/packages/opentelemetry-sdk-trace-base/src/TimedEvent.ts b/packages/opentelemetry-sdk-trace-base/src/TimedEvent.ts index 93cb9b47f3..1f835ba710 100644 --- a/packages/opentelemetry-sdk-trace-base/src/TimedEvent.ts +++ b/packages/opentelemetry-sdk-trace-base/src/TimedEvent.ts @@ -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; } diff --git a/packages/opentelemetry-sdk-trace-base/src/config.ts b/packages/opentelemetry-sdk-trace-base/src/config.ts index 18753ea70e..3448b793da 100644 --- a/packages/opentelemetry-sdk-trace-base/src/config.ts +++ b/packages/opentelemetry-sdk-trace-base/src/config.ts @@ -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, }, }; } diff --git a/packages/opentelemetry-sdk-trace-base/src/export/ReadableSpan.ts b/packages/opentelemetry-sdk-trace-base/src/export/ReadableSpan.ts index aa19891099..20ffea4c56 100644 --- a/packages/opentelemetry-sdk-trace-base/src/export/ReadableSpan.ts +++ b/packages/opentelemetry-sdk-trace-base/src/export/ReadableSpan.ts @@ -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; } diff --git a/packages/opentelemetry-sdk-trace-base/src/types.ts b/packages/opentelemetry-sdk-trace-base/src/types.ts index 6854f0315a..99eeedbe81 100644 --- a/packages/opentelemetry-sdk-trace-base/src/types.ts +++ b/packages/opentelemetry-sdk-trace-base/src/types.ts @@ -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. */ diff --git a/packages/opentelemetry-sdk-trace-base/test/common/BasicTracerProvider.test.ts b/packages/opentelemetry-sdk-trace-base/test/common/BasicTracerProvider.test.ts index d7a900cf9f..7a0483ffa9 100644 --- a/packages/opentelemetry-sdk-trace-base/test/common/BasicTracerProvider.test.ts +++ b/packages/opentelemetry-sdk-trace-base/test/common/BasicTracerProvider.test.ts @@ -179,6 +179,8 @@ describe('BasicTracerProvider', () => { attributeCountLimit: 128, eventCountLimit: 128, linkCountLimit: 128, + attributePerEventCountLimit: 128, + attributePerLinkCountLimit: 128, }); }); }); diff --git a/packages/opentelemetry-sdk-trace-base/test/common/Span.test.ts b/packages/opentelemetry-sdk-trace-base/test/common/Span.test.ts index dac8728138..11a94ffc7c 100644 --- a/packages/opentelemetry-sdk-trace-base/test/common/Span.test.ts +++ b/packages/opentelemetry-sdk-trace-base/test/common/Span.test.ts @@ -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: {