From b2c9f4086c3664ed02def6aa4b475bbfb145bc07 Mon Sep 17 00:00:00 2001 From: Mohit Karekar Date: Sat, 28 Jan 2023 00:14:07 +0100 Subject: [PATCH] feat(sdk-trace-base): add dropped attributes and events count on span --- api/src/index.ts | 6 ++- api/src/trace/attributes.ts | 6 +++ .../tsconfig.esm.json | 15 +++++++ .../tsconfig.esnext.json | 15 +++++++ .../tsconfig.esm.json | 3 ++ .../tsconfig.esnext.json | 9 ++-- .../opentelemetry-sdk-trace-base/src/Span.ts | 41 ++++++++++++++---- .../src/TimedEvent.ts | 6 +++ .../src/export/ReadableSpan.ts | 5 ++- .../test/common/Span.test.ts | 43 +++++++++++++++++++ tsconfig.esm.json | 6 +++ tsconfig.esnext.json | 6 +++ 12 files changed, 146 insertions(+), 15 deletions(-) diff --git a/api/src/index.ts b/api/src/index.ts index 2326aba9dc..52e727fb5c 100644 --- a/api/src/index.ts +++ b/api/src/index.ts @@ -71,7 +71,11 @@ export { export type { PropagationAPI } from './api/propagation'; // Trace APIs -export { SpanAttributes, SpanAttributeValue } from './trace/attributes'; +export { + SpanAttributes, + SpanAttributeValue, + SpanDroppedAttributes, +} from './trace/attributes'; export { Link } from './trace/link'; export { ProxyTracer, TracerDelegator } from './trace/ProxyTracer'; export { ProxyTracerProvider } from './trace/ProxyTracerProvider'; diff --git a/api/src/trace/attributes.ts b/api/src/trace/attributes.ts index 01feff0b0a..ce29b07a3c 100644 --- a/api/src/trace/attributes.ts +++ b/api/src/trace/attributes.ts @@ -25,3 +25,9 @@ export type SpanAttributes = Attributes; * @deprecated please use {@link AttributeValue} */ export type SpanAttributeValue = AttributeValue; + +/** + * Dropped attributes count is a map of key-value pairs + * of attributes that were dropped due to collection limits + */ +export type SpanDroppedAttributes = Attributes | undefined; diff --git a/experimental/packages/exporter-trace-otlp-proto/tsconfig.esm.json b/experimental/packages/exporter-trace-otlp-proto/tsconfig.esm.json index ae47403334..22887f7b0a 100644 --- a/experimental/packages/exporter-trace-otlp-proto/tsconfig.esm.json +++ b/experimental/packages/exporter-trace-otlp-proto/tsconfig.esm.json @@ -14,6 +14,21 @@ }, { "path": "../../../packages/opentelemetry-core" + }, + { + "path": "../../../packages/opentelemetry-resources" + }, + { + "path": "../../../packages/opentelemetry-sdk-trace-base" + }, + { + "path": "../otlp-exporter-base" + }, + { + "path": "../otlp-proto-exporter-base" + }, + { + "path": "../otlp-transformer" } ] } diff --git a/experimental/packages/exporter-trace-otlp-proto/tsconfig.esnext.json b/experimental/packages/exporter-trace-otlp-proto/tsconfig.esnext.json index 5f6cf572d5..199ea3b325 100644 --- a/experimental/packages/exporter-trace-otlp-proto/tsconfig.esnext.json +++ b/experimental/packages/exporter-trace-otlp-proto/tsconfig.esnext.json @@ -14,6 +14,21 @@ }, { "path": "../../../packages/opentelemetry-core" + }, + { + "path": "../../../packages/opentelemetry-resources" + }, + { + "path": "../../../packages/opentelemetry-sdk-trace-base" + }, + { + "path": "../otlp-exporter-base" + }, + { + "path": "../otlp-proto-exporter-base" + }, + { + "path": "../otlp-transformer" } ] } diff --git a/experimental/packages/otlp-proto-exporter-base/tsconfig.esm.json b/experimental/packages/otlp-proto-exporter-base/tsconfig.esm.json index ce8637cb82..d5c055a849 100644 --- a/experimental/packages/otlp-proto-exporter-base/tsconfig.esm.json +++ b/experimental/packages/otlp-proto-exporter-base/tsconfig.esm.json @@ -16,6 +16,9 @@ }, { "path": "../../../packages/opentelemetry-core" + }, + { + "path": "../otlp-exporter-base" } ] } diff --git a/experimental/packages/otlp-proto-exporter-base/tsconfig.esnext.json b/experimental/packages/otlp-proto-exporter-base/tsconfig.esnext.json index 4a1cb2c30b..ae0686aa84 100644 --- a/experimental/packages/otlp-proto-exporter-base/tsconfig.esnext.json +++ b/experimental/packages/otlp-proto-exporter-base/tsconfig.esnext.json @@ -1,14 +1,12 @@ { "extends": "../../../tsconfig.base.esnext.json", "compilerOptions": { - "allowJs": true, - "outDir": "build/esnext", "rootDir": "src", + "outDir": "build/esnext", "tsBuildInfoFile": "build/esnext/tsconfig.esnext.tsbuildinfo" }, "include": [ - "src/**/*.ts", - "src/generated/*.js", + "src/**/*.ts" ], "references": [ { @@ -16,6 +14,9 @@ }, { "path": "../../../packages/opentelemetry-core" + }, + { + "path": "../otlp-exporter-base" } ] } diff --git a/packages/opentelemetry-sdk-trace-base/src/Span.ts b/packages/opentelemetry-sdk-trace-base/src/Span.ts index 7677e6f5ce..64767039a6 100644 --- a/packages/opentelemetry-sdk-trace-base/src/Span.ts +++ b/packages/opentelemetry-sdk-trace-base/src/Span.ts @@ -24,6 +24,7 @@ import { SpanAttributes, SpanAttributeValue, SpanContext, + SpanDroppedAttributes, SpanKind, SpanStatus, SpanStatusCode, @@ -47,7 +48,7 @@ import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import { ExceptionEventName } from './enums'; import { ReadableSpan } from './export/ReadableSpan'; import { SpanProcessor } from './SpanProcessor'; -import { TimedEvent } from './TimedEvent'; +import { SpanDroppedEvents, TimedEvent } from './TimedEvent'; import { Tracer } from './Tracer'; import { SpanLimits } from './types'; @@ -66,6 +67,10 @@ export class Span implements APISpan, ReadableSpan { readonly startTime: HrTime; readonly resource: Resource; readonly instrumentationLibrary: InstrumentationLibrary; + + droppedAttributesCount: SpanDroppedAttributes; + droppedEventsCount: SpanDroppedEvents; + name: string; status: SpanStatus = { code: SpanStatusCode.UNSET, @@ -141,6 +146,10 @@ export class Span implements APISpan, ReadableSpan { this._spanLimits.attributeCountLimit! && !Object.prototype.hasOwnProperty.call(this.attributes, key) ) { + if (!this.droppedAttributesCount) { + this.droppedAttributesCount = {}; + } + this.droppedAttributesCount[key] = this._truncateToSize(value); return this; } this.attributes[key] = this._truncateToSize(value); @@ -167,14 +176,6 @@ export class Span implements APISpan, ReadableSpan { timeStamp?: TimeInput ): this { if (this._isSpanEnded()) return this; - if (this._spanLimits.eventCountLimit === 0) { - diag.warn('No events allowed.'); - return this; - } - if (this.events.length >= this._spanLimits.eventCountLimit!) { - diag.warn('Dropping extra events.'); - this.events.shift(); - } if (isTimeInput(attributesOrStartTime)) { if (!isTimeInput(timeStamp)) { @@ -184,6 +185,28 @@ export class Span implements APISpan, ReadableSpan { } const attributes = sanitizeAttributes(attributesOrStartTime); + + if (this._spanLimits.eventCountLimit === 0) { + diag.warn('No events allowed.'); + if (!this.droppedEventsCount) { + this.droppedEventsCount = {}; + } + this.droppedEventsCount[name] = { + name, + attributes, + time: this._getTime(timeStamp), + }; + return this; + } + if (this.events.length >= this._spanLimits.eventCountLimit!) { + diag.warn('Dropping extra events.'); + const dropped = this.events.shift(); + if (!this.droppedEventsCount) { + this.droppedEventsCount = {}; + } + this.droppedEventsCount[dropped!.name] = dropped!; + } + this.events.push({ name, attributes, diff --git a/packages/opentelemetry-sdk-trace-base/src/TimedEvent.ts b/packages/opentelemetry-sdk-trace-base/src/TimedEvent.ts index 93cb9b47f3..3a29e5aaf9 100644 --- a/packages/opentelemetry-sdk-trace-base/src/TimedEvent.ts +++ b/packages/opentelemetry-sdk-trace-base/src/TimedEvent.ts @@ -27,3 +27,9 @@ export interface TimedEvent { /** The attributes of the event. */ attributes?: SpanAttributes; } + +export type SpanDroppedEvents = + | { + [name: string]: TimedEvent; + } + | undefined; diff --git a/packages/opentelemetry-sdk-trace-base/src/export/ReadableSpan.ts b/packages/opentelemetry-sdk-trace-base/src/export/ReadableSpan.ts index 8552134a56..cc7395ea87 100644 --- a/packages/opentelemetry-sdk-trace-base/src/export/ReadableSpan.ts +++ b/packages/opentelemetry-sdk-trace-base/src/export/ReadableSpan.ts @@ -21,10 +21,11 @@ import { HrTime, Link, SpanContext, + SpanDroppedAttributes, } from '@opentelemetry/api'; import { Resource } from '@opentelemetry/resources'; import { InstrumentationLibrary } from '@opentelemetry/core'; -import { TimedEvent } from '../TimedEvent'; +import { TimedEvent, SpanDroppedEvents } from '../TimedEvent'; export interface ReadableSpan { readonly name: string; @@ -41,4 +42,6 @@ export interface ReadableSpan { readonly ended: boolean; readonly resource: Resource; readonly instrumentationLibrary: InstrumentationLibrary; + readonly droppedAttributesCount: SpanDroppedAttributes; + readonly droppedEventsCount: SpanDroppedEvents; } 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..7bcd108eeb 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,17 @@ describe('Span', () => { assert.strictEqual(span.attributes['foo99'], 'bar99'); assert.strictEqual(span.attributes['foo149'], undefined); }); + + it('should store all dropped attributes in droppedAttributesCount', () => { + assert.ok(span.droppedAttributesCount); + assert.strictEqual( + Object.keys(span.droppedAttributesCount).length, + 50 + ); + assert.strictEqual(span.droppedAttributesCount['foo100'], 'bar100'); + assert.strictEqual(span.droppedAttributesCount['foo149'], 'bar149'); + assert.strictEqual(span.droppedAttributesCount['foo99'], undefined); + }); }); describe('when "attributeValueLengthLimit" option defined', () => { @@ -791,6 +802,38 @@ describe('Span', () => { assert.strictEqual(span.events[span.events.length - 1].name, 'sent149'); }); + it('should store dropped events in droppedEventsCount', () => { + const span = new Span( + tracer, + ROOT_CONTEXT, + name, + spanContext, + SpanKind.CLIENT + ); + const testAttributes = { + attribute_1: 'forty-two', + }; + for (let i = 0; i < 150; i++) { + if (i === 42) { + span.addEvent('sent42', testAttributes); + } else { + span.addEvent('sent' + i); + } + } + span.end(); + + assert.ok(span.droppedEventsCount); + assert.strictEqual(Object.keys(span.droppedEventsCount).length, 50); + assert.strictEqual(span.droppedEventsCount['sent0'].name, 'sent0'); + assert.strictEqual(span.droppedEventsCount['sent42'].name, 'sent42'); + assert.ok(span.droppedEventsCount['sent42'].attributes); + assert.strictEqual( + span.droppedEventsCount['sent42'].attributes['attribute_1'], + 'forty-two' + ); + assert.strictEqual(span.droppedEventsCount['sent50'], undefined); + }); + it('should add no event', () => { const tracer = new BasicTracerProvider({ spanLimits: { diff --git a/tsconfig.esm.json b/tsconfig.esm.json index 3c3cb876dd..afd8742243 100644 --- a/tsconfig.esm.json +++ b/tsconfig.esm.json @@ -11,6 +11,9 @@ { "path": "experimental/packages/exporter-trace-otlp-http/tsconfig.esm.json" }, + { + "path": "experimental/packages/exporter-trace-otlp-proto/tsconfig.esm.json" + }, { "path": "experimental/packages/opentelemetry-browser-detector/tsconfig.esm.json" }, @@ -29,6 +32,9 @@ { "path": "experimental/packages/otlp-exporter-base/tsconfig.esm.json" }, + { + "path": "experimental/packages/otlp-proto-exporter-base/tsconfig.esm.json" + }, { "path": "experimental/packages/otlp-transformer/tsconfig.esm.json" }, diff --git a/tsconfig.esnext.json b/tsconfig.esnext.json index 63c1e27aa4..c826225f26 100644 --- a/tsconfig.esnext.json +++ b/tsconfig.esnext.json @@ -11,6 +11,9 @@ { "path": "experimental/packages/exporter-trace-otlp-http/tsconfig.esnext.json" }, + { + "path": "experimental/packages/exporter-trace-otlp-proto/tsconfig.esnext.json" + }, { "path": "experimental/packages/opentelemetry-browser-detector/tsconfig.esnext.json" }, @@ -29,6 +32,9 @@ { "path": "experimental/packages/otlp-exporter-base/tsconfig.esnext.json" }, + { + "path": "experimental/packages/otlp-proto-exporter-base/tsconfig.esnext.json" + }, { "path": "experimental/packages/otlp-transformer/tsconfig.esnext.json" },