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:
parent
95bf6fc7cc
commit
48ecf229eb
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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[]) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -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[]) {
|
||||
|
|
|
|||
|
|
@ -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[]) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -178,6 +178,9 @@ describe('Trace', () => {
|
|||
status: {
|
||||
code: SpanStatusCode.OK,
|
||||
},
|
||||
droppedAttributesCount: 0,
|
||||
droppedEventsCount: 0,
|
||||
droppedLinksCount: 0,
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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: '',
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@ describe('JaegerExporter', () => {
|
|||
name: 'default',
|
||||
version: '0.0.1',
|
||||
},
|
||||
droppedAttributesCount: 0,
|
||||
droppedEventsCount: 0,
|
||||
droppedLinksCount: 0,
|
||||
};
|
||||
describe('constructor', () => {
|
||||
afterEach(() => {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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]))
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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({});
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -179,6 +179,8 @@ describe('BasicTracerProvider', () => {
|
|||
attributeCountLimit: 128,
|
||||
eventCountLimit: 128,
|
||||
linkCountLimit: 128,
|
||||
attributePerEventCountLimit: 128,
|
||||
attributePerLinkCountLimit: 128,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
Loading…
Reference in New Issue