diff --git a/packages/opentelemetry-core/src/common/types.ts b/packages/opentelemetry-core/src/common/types.ts index a5b2e553d0..4de46a327b 100644 --- a/packages/opentelemetry-core/src/common/types.ts +++ b/packages/opentelemetry-core/src/common/types.ts @@ -40,3 +40,13 @@ export interface ShimWrapped { __unwrap: Function; __original: Function; } + +/** + * An instrumentation library consists of the name and version used to + * obtain a tracer or meter from a provider. This metadata is made available + * on ReadableSpan and MetricRecord for use by the export pipeline. + */ +export interface InstrumentationLibrary { + readonly name: string; + readonly version: string; +} diff --git a/packages/opentelemetry-exporter-collector/src/transform.ts b/packages/opentelemetry-exporter-collector/src/transform.ts index 8fed306770..09af80dbf0 100644 --- a/packages/opentelemetry-exporter-collector/src/transform.ts +++ b/packages/opentelemetry-exporter-collector/src/transform.ts @@ -30,6 +30,7 @@ import { } from './CollectorExporterBase'; import { COLLECTOR_SPAN_KIND_MAPPING, opentelemetryProto } from './types'; import ValueType = opentelemetryProto.common.v1.ValueType; +import { InstrumentationLibrary } from '@opentelemetry/core'; /** * Converts attributes @@ -202,11 +203,10 @@ export function toCollectorExportTraceServiceRequest< collectorExporterBase: CollectorExporterBase, name = '' ): opentelemetryProto.collector.trace.v1.ExportTraceServiceRequest { - const spansToBeSent: opentelemetryProto.trace.v1.Span[] = spans.map(span => - toCollectorSpan(span) - ); - const resource: Resource = - spans.length > 0 ? spans[0].resource : Resource.empty(); + const groupedSpans: Map< + Resource, + Map + > = groupSpansByResourceAndLibrary(spans); const additionalAttributes = Object.assign( {}, @@ -215,23 +215,60 @@ export function toCollectorExportTraceServiceRequest< 'service.name': collectorExporterBase.serviceName, } ); - const protoResource: opentelemetryProto.resource.v1.Resource = toCollectorResource( - resource, - additionalAttributes - ); - const instrumentationLibrarySpans: opentelemetryProto.trace.v1.InstrumentationLibrarySpans = { - spans: spansToBeSent, - instrumentationLibrary: { - name: name || `${core.SDK_INFO.NAME} - ${core.SDK_INFO.LANGUAGE}`, - version: core.SDK_INFO.VERSION, - }, - }; - const resourceSpan: opentelemetryProto.trace.v1.ResourceSpans = { - resource: protoResource, - instrumentationLibrarySpans: [instrumentationLibrarySpans], + + return { + resourceSpans: toCollectorResourceSpans(groupedSpans, additionalAttributes), }; +} +/** + * Takes an array of spans and groups them by resource and instrumentation + * library + * @param spans spans + */ +export function groupSpansByResourceAndLibrary( + spans: ReadableSpan[] +): Map> { + return spans.reduce((spanMap, span) => { + //group by resource + let resourceSpans = spanMap.get(span.resource); + if (!resourceSpans) { + resourceSpans = new Map(); + spanMap.set(span.resource, resourceSpans); + } + //group by instrumentation library + let libSpans = resourceSpans.get(span.instrumentationLibrary); + if (!libSpans) { + libSpans = new Array(); + resourceSpans.set(span.instrumentationLibrary, libSpans); + } + libSpans.push(span); + return spanMap; + }, new Map>()); +} + +function toCollectorInstrumentationLibrarySpans( + instrumentationLibrary: InstrumentationLibrary, + spans: ReadableSpan[] +): opentelemetryProto.trace.v1.InstrumentationLibrarySpans { return { - resourceSpans: [resourceSpan], + spans: spans.map(toCollectorSpan), + instrumentationLibrary, }; } + +function toCollectorResourceSpans( + groupedSpans: Map>, + baseAttributes: Attributes +): opentelemetryProto.trace.v1.ResourceSpans[] { + return Array.from(groupedSpans, ([resource, libSpans]) => { + return { + resource: toCollectorResource(resource, baseAttributes), + instrumentationLibrarySpans: Array.from( + libSpans, + ([instrumentationLibrary, spans]) => + toCollectorInstrumentationLibrarySpans(instrumentationLibrary, spans) + ), + }; + }); +} diff --git a/packages/opentelemetry-exporter-collector/test/common/transform.test.ts b/packages/opentelemetry-exporter-collector/test/common/transform.test.ts index 665ef10391..01357b2ada 100644 --- a/packages/opentelemetry-exporter-collector/test/common/transform.test.ts +++ b/packages/opentelemetry-exporter-collector/test/common/transform.test.ts @@ -17,7 +17,14 @@ import { Attributes, TimedEvent } from '@opentelemetry/api'; import * as assert from 'assert'; import * as transform from '../../src/transform'; -import { ensureSpanIsCorrect, mockedReadableSpan } from '../helper'; +import { + ensureSpanIsCorrect, + mockedReadableSpan, + mockedResources, + mockedInstrumentationLibraries, + multiResourceTrace, + multiInstrumentationLibraryTrace, +} from '../helper'; import { Resource } from '@opentelemetry/resources'; describe('transform', () => { @@ -119,4 +126,45 @@ describe('transform', () => { }); }); }); + + describe('groupSpansByResourceAndLibrary', () => { + it('should group by resource', () => { + const [resource1, resource2] = mockedResources; + const [instrumentationLibrary] = mockedInstrumentationLibraries; + const [span1, span2, span3] = multiResourceTrace; + + const expected = new Map([ + [resource1, new Map([[instrumentationLibrary, [span1]]])], + [resource2, new Map([[instrumentationLibrary, [span2, span3]]])], + ]); + + const result = transform.groupSpansByResourceAndLibrary( + multiResourceTrace + ); + + assert.deepStrictEqual(result, expected); + }); + + it('should group by instrumentation library', () => { + const [resource] = mockedResources; + const [lib1, lib2] = mockedInstrumentationLibraries; + const [span1, span2, span3] = multiInstrumentationLibraryTrace; + + const expected = new Map([ + [ + resource, + new Map([ + [lib1, [span1, span2]], + [lib2, [span3]], + ]), + ], + ]); + + const result = transform.groupSpansByResourceAndLibrary( + multiInstrumentationLibraryTrace + ); + + assert.deepStrictEqual(result, expected); + }); + }); }); diff --git a/packages/opentelemetry-exporter-collector/test/helper.ts b/packages/opentelemetry-exporter-collector/test/helper.ts index 4737c66b96..056d95d2a7 100644 --- a/packages/opentelemetry-exporter-collector/test/helper.ts +++ b/packages/opentelemetry-exporter-collector/test/helper.ts @@ -20,6 +20,7 @@ import { Resource } from '@opentelemetry/resources'; import * as assert from 'assert'; import { opentelemetryProto } from '../src/types'; import * as collectorTypes from '../src/types'; +import { InstrumentationLibrary } from '@opentelemetry/core'; import * as grpc from 'grpc'; if (typeof Buffer === 'undefined') { @@ -106,8 +107,118 @@ export const mockedReadableSpan: ReadableSpan = { version: 1, cost: 112.12, }), + instrumentationLibrary: { name: 'default', version: '0.0.1' }, }; +export const mockedResources: Resource[] = [ + new Resource({ name: 'resource 1' }), + new Resource({ name: 'resource 2' }), +]; + +export const mockedInstrumentationLibraries: InstrumentationLibrary[] = [ + { + name: 'lib1', + version: '0.0.1', + }, + { + name: 'lib2', + version: '0.0.2', + }, +]; + +export const basicTrace: ReadableSpan[] = [ + { + name: 'span1', + kind: 0, + spanContext: { + traceId: '1f1008dc8e270e85c40a0d7c3939b278', + spanId: '5e107261f64fa53e', + traceFlags: TraceFlags.SAMPLED, + }, + parentSpanId: '78a8915098864388', + startTime: [1574120165, 429803070], + endTime: [1574120165, 438688070], + ended: true, + status: { code: 0 }, + attributes: {}, + links: [], + events: [], + duration: [0, 8885000], + resource: mockedResources[0], + instrumentationLibrary: mockedInstrumentationLibraries[0], + }, + { + name: 'span2', + kind: 0, + spanContext: { + traceId: '1f1008dc8e270e85c40a0d7c3939b278', + spanId: 'f64fa53e5e107261', + traceFlags: TraceFlags.SAMPLED, + }, + parentSpanId: '78a8915098864388', + startTime: [1575120165, 439803070], + endTime: [1575120165, 448688070], + ended: true, + status: { code: 0 }, + attributes: {}, + links: [], + events: [], + duration: [0, 8775000], + resource: mockedResources[0], + instrumentationLibrary: mockedInstrumentationLibraries[0], + }, + { + name: 'span3', + kind: 0, + spanContext: { + traceId: '1f1008dc8e270e85c40a0d7c3939b278', + spanId: '07261f64fa53e5e1', + traceFlags: TraceFlags.SAMPLED, + }, + parentSpanId: 'a891578098864388', + startTime: [1575120165, 439803070], + endTime: [1575120165, 448688070], + ended: true, + status: { code: 0 }, + attributes: {}, + links: [], + events: [], + duration: [0, 8775000], + resource: mockedResources[0], + instrumentationLibrary: mockedInstrumentationLibraries[0], + }, +]; + +export const multiResourceTrace: ReadableSpan[] = [ + { + ...basicTrace[0], + resource: mockedResources[0], + }, + { + ...basicTrace[1], + resource: mockedResources[1], + }, + { + ...basicTrace[2], + resource: mockedResources[1], + }, +]; + +export const multiInstrumentationLibraryTrace: ReadableSpan[] = [ + { + ...basicTrace[0], + instrumentationLibrary: mockedInstrumentationLibraries[0], + }, + { + ...basicTrace[1], + instrumentationLibrary: mockedInstrumentationLibraries[0], + }, + { + ...basicTrace[2], + instrumentationLibrary: mockedInstrumentationLibraries[1], + }, +]; + export function ensureExportedEventsAreCorrect( events: opentelemetryProto.trace.v1.Span.Event[] ) { diff --git a/packages/opentelemetry-exporter-jaeger/test/jaeger.test.ts b/packages/opentelemetry-exporter-jaeger/test/jaeger.test.ts index 4b5d6ff7fe..7e8baa4e88 100644 --- a/packages/opentelemetry-exporter-jaeger/test/jaeger.test.ts +++ b/packages/opentelemetry-exporter-jaeger/test/jaeger.test.ts @@ -145,6 +145,10 @@ describe('JaegerExporter', () => { events: [], duration: [32, 800000000], resource: Resource.empty(), + instrumentationLibrary: { + name: 'default', + version: '0.0.1', + }, }; exporter.export([readableSpan], (result: ExportResult) => { @@ -194,6 +198,10 @@ describe('JaegerExporter', () => { events: [], duration: [32, 800000000], resource: Resource.empty(), + instrumentationLibrary: { + name: 'default', + version: '0.0.1', + }, }; exporter.export([readableSpan], () => {}); }); diff --git a/packages/opentelemetry-exporter-jaeger/test/transform.test.ts b/packages/opentelemetry-exporter-jaeger/test/transform.test.ts index 173ac26010..332b540cdc 100644 --- a/packages/opentelemetry-exporter-jaeger/test/transform.test.ts +++ b/packages/opentelemetry-exporter-jaeger/test/transform.test.ts @@ -75,6 +75,10 @@ describe('transform', () => { version: 1, cost: 112.12, }), + instrumentationLibrary: { + name: 'default', + version: '0.0.1', + }, }; const thriftSpan = spanToThrift(readableSpan); @@ -169,6 +173,10 @@ describe('transform', () => { events: [], duration: [32, 800000000], resource: Resource.empty(), + instrumentationLibrary: { + name: 'default', + version: '0.0.1', + }, }; const thriftSpan = spanToThrift(readableSpan); @@ -234,6 +242,10 @@ describe('transform', () => { events: [], duration: [32, 800000000], resource: Resource.empty(), + instrumentationLibrary: { + name: 'default', + version: '0.0.1', + }, }; const thriftSpan = spanToThrift(readableSpan); @@ -273,6 +285,10 @@ describe('transform', () => { events: [], duration: [32, 800000000], resource: Resource.empty(), + instrumentationLibrary: { + name: 'default', + version: '0.0.1', + }, }; const thriftSpan = spanToThrift(readableSpan); diff --git a/packages/opentelemetry-exporter-zipkin/test/zipkin.test.ts b/packages/opentelemetry-exporter-zipkin/test/zipkin.test.ts index 484e0c02b7..7dd50b8381 100644 --- a/packages/opentelemetry-exporter-zipkin/test/zipkin.test.ts +++ b/packages/opentelemetry-exporter-zipkin/test/zipkin.test.ts @@ -54,6 +54,7 @@ function getReadableSpan() { links: [], events: [], resource: Resource.empty(), + instrumentationLibrary: { name: 'default', version: '0.0.1' }, }; return readableSpan; } @@ -176,6 +177,7 @@ describe('ZipkinExporter', () => { }, ], resource: Resource.empty(), + instrumentationLibrary: { name: 'default', version: '0.0.1' }, }; const span2: ReadableSpan = { name: 'my-span', @@ -196,6 +198,7 @@ describe('ZipkinExporter', () => { links: [], events: [], resource: Resource.empty(), + instrumentationLibrary: { name: 'default', version: '0.0.1' }, }; const exporter = new ZipkinExporter({ @@ -386,6 +389,7 @@ describe('ZipkinExporter', () => { }, ], resource: Resource.empty(), + instrumentationLibrary: { name: 'default', version: '0.0.1' }, }; const span2: ReadableSpan = { name: 'my-span', @@ -406,6 +410,7 @@ describe('ZipkinExporter', () => { links: [], events: [], resource: Resource.empty(), + instrumentationLibrary: { name: 'default', version: '0.0.1' }, }; const exporter = new ZipkinExporter({}); @@ -458,6 +463,7 @@ describe('ZipkinExporter', () => { resource: new Resource({ [SERVICE_RESOURCE.NAME]: resource_service_name, }), + instrumentationLibrary: { name: 'default', version: '0.0.1' }, }; const span2: ReadableSpan = { name: 'my-span', @@ -478,6 +484,7 @@ describe('ZipkinExporter', () => { links: [], events: [], resource: Resource.empty(), + instrumentationLibrary: { name: 'default', version: '0.0.1' }, }; const exporter = new ZipkinExporter({}); diff --git a/packages/opentelemetry-metrics/src/Meter.ts b/packages/opentelemetry-metrics/src/Meter.ts index a632befae6..22d4fda93f 100644 --- a/packages/opentelemetry-metrics/src/Meter.ts +++ b/packages/opentelemetry-metrics/src/Meter.ts @@ -15,7 +15,7 @@ */ import * as api from '@opentelemetry/api'; -import { ConsoleLogger } from '@opentelemetry/core'; +import { ConsoleLogger, InstrumentationLibrary } from '@opentelemetry/core'; import { Resource } from '@opentelemetry/resources'; import { BaseBoundInstrument } from './BoundInstrument'; import { UpDownCounterMetric } from './UpDownCounterMetric'; @@ -43,14 +43,19 @@ export class Meter implements api.Meter { private readonly _metrics = new Map>(); private readonly _batcher: Batcher; private readonly _resource: Resource; + private readonly _instrumentationLibrary: InstrumentationLibrary; /** * Constructs a new Meter instance. */ - constructor(config: MeterConfig = DEFAULT_CONFIG) { + constructor( + instrumentationLibrary: InstrumentationLibrary, + config: MeterConfig = DEFAULT_CONFIG + ) { this._logger = config.logger || new ConsoleLogger(config.logLevel); this._batcher = config.batcher ?? new UngroupedBatcher(); this._resource = config.resource || Resource.createTelemetrySDKResource(); + this._instrumentationLibrary = instrumentationLibrary; // start the push controller const exporter = config.exporter || new NoopExporter(); const interval = config.interval; @@ -83,7 +88,8 @@ export class Meter implements api.Meter { name, opt, this._batcher, - this._resource + this._resource, + this._instrumentationLibrary ); this._registerMetric(name, valueRecorder); return valueRecorder; @@ -108,7 +114,13 @@ export class Meter implements api.Meter { ...DEFAULT_METRIC_OPTIONS, ...options, }; - const counter = new CounterMetric(name, opt, this._batcher, this._resource); + const counter = new CounterMetric( + name, + opt, + this._batcher, + this._resource, + this._instrumentationLibrary + ); this._registerMetric(name, counter); return counter; } @@ -142,7 +154,8 @@ export class Meter implements api.Meter { name, opt, this._batcher, - this._resource + this._resource, + this._instrumentationLibrary ); this._registerMetric(name, upDownCounter); return upDownCounter; @@ -169,7 +182,8 @@ export class Meter implements api.Meter { name, opt, this._batcher, - this._resource + this._resource, + this._instrumentationLibrary ); this._registerMetric(name, observer); return observer; diff --git a/packages/opentelemetry-metrics/src/MeterProvider.ts b/packages/opentelemetry-metrics/src/MeterProvider.ts index ae8ef64539..b178593dfa 100644 --- a/packages/opentelemetry-metrics/src/MeterProvider.ts +++ b/packages/opentelemetry-metrics/src/MeterProvider.ts @@ -46,7 +46,10 @@ export class MeterProvider implements api.MeterProvider { getMeter(name: string, version = '*', config?: MeterConfig): Meter { const key = `${name}@${version}`; if (!this._meters.has(key)) { - this._meters.set(key, new Meter(config || this._config)); + this._meters.set( + key, + new Meter({ name, version }, config || this._config) + ); } return this._meters.get(key)!; diff --git a/packages/opentelemetry-metrics/src/Metric.ts b/packages/opentelemetry-metrics/src/Metric.ts index a446e4ddc4..7dcb3ab52c 100644 --- a/packages/opentelemetry-metrics/src/Metric.ts +++ b/packages/opentelemetry-metrics/src/Metric.ts @@ -27,6 +27,7 @@ import { MetricOptions } from './types'; import { MetricKind, MetricDescriptor, MetricRecord } from './export/types'; import { Batcher } from './export/Batcher'; import { hashLabels } from './Utils'; +import { InstrumentationLibrary } from '@opentelemetry/core'; /** This is a SDK implementation of {@link Metric} interface. */ export abstract class Metric @@ -41,7 +42,8 @@ export abstract class Metric private readonly _name: string, private readonly _options: MetricOptions, private readonly _kind: MetricKind, - readonly resource: Resource + readonly resource: Resource, + readonly instrumentationLibrary: InstrumentationLibrary ) { this._disabled = _options.disabled; this._valueType = _options.valueType; @@ -86,6 +88,7 @@ export abstract class Metric labels: instrument.getLabels(), aggregator: instrument.getAggregator(), resource: this.resource, + instrumentationLibrary: this.instrumentationLibrary, })); } @@ -108,9 +111,10 @@ export class CounterMetric extends Metric implements api.Counter { name: string, options: MetricOptions, private readonly _batcher: Batcher, - resource: Resource + resource: Resource, + instrumentationLibrary: InstrumentationLibrary ) { - super(name, options, MetricKind.COUNTER, resource); + super(name, options, MetricKind.COUNTER, resource, instrumentationLibrary); } protected _makeInstrument(labels: api.Labels): BoundCounter { return new BoundCounter( @@ -142,9 +146,16 @@ export class ValueRecorderMetric extends Metric name: string, options: MetricOptions, private readonly _batcher: Batcher, - resource: Resource + resource: Resource, + instrumentationLibrary: InstrumentationLibrary ) { - super(name, options, MetricKind.VALUE_RECORDER, resource); + super( + name, + options, + MetricKind.VALUE_RECORDER, + resource, + instrumentationLibrary + ); this._absolute = options.absolute !== undefined ? options.absolute : true; // Absolute default is true } @@ -173,9 +184,10 @@ export class ObserverMetric extends Metric name: string, options: MetricOptions, private readonly _batcher: Batcher, - resource: Resource + resource: Resource, + instrumentationLibrary: InstrumentationLibrary ) { - super(name, options, MetricKind.OBSERVER, resource); + super(name, options, MetricKind.OBSERVER, resource, instrumentationLibrary); } protected _makeInstrument(labels: api.Labels): BoundObserver { diff --git a/packages/opentelemetry-metrics/src/UpDownCounterMetric.ts b/packages/opentelemetry-metrics/src/UpDownCounterMetric.ts index 3618ec2f50..11a468aa6b 100644 --- a/packages/opentelemetry-metrics/src/UpDownCounterMetric.ts +++ b/packages/opentelemetry-metrics/src/UpDownCounterMetric.ts @@ -16,6 +16,7 @@ import * as api from '@opentelemetry/api'; import { Resource } from '@opentelemetry/resources'; +import { InstrumentationLibrary } from '@opentelemetry/core'; import { BoundUpDownCounter } from './BoundInstrument'; import { MetricOptions } from './types'; import { MetricKind } from './export/types'; @@ -29,9 +30,16 @@ export class UpDownCounterMetric extends Metric name: string, options: MetricOptions, private readonly _batcher: Batcher, - resource: Resource + resource: Resource, + instrumentationLibrary: InstrumentationLibrary ) { - super(name, options, MetricKind.UP_DOWN_COUNTER, resource); + super( + name, + options, + MetricKind.UP_DOWN_COUNTER, + resource, + instrumentationLibrary + ); } protected _makeInstrument(labels: api.Labels): BoundUpDownCounter { return new BoundUpDownCounter( diff --git a/packages/opentelemetry-metrics/src/export/types.ts b/packages/opentelemetry-metrics/src/export/types.ts index 94e6e57c0a..295b1ebaf1 100644 --- a/packages/opentelemetry-metrics/src/export/types.ts +++ b/packages/opentelemetry-metrics/src/export/types.ts @@ -15,7 +15,7 @@ */ import { ValueType, HrTime, Labels } from '@opentelemetry/api'; -import { ExportResult } from '@opentelemetry/core'; +import { ExportResult, InstrumentationLibrary } from '@opentelemetry/core'; import { Resource } from '@opentelemetry/resources'; /** The kind of metric. */ @@ -74,6 +74,7 @@ export interface MetricRecord { readonly labels: Labels; readonly aggregator: Aggregator; readonly resource: Resource; + readonly instrumentationLibrary: InstrumentationLibrary; } export interface MetricDescriptor { diff --git a/packages/opentelemetry-metrics/test/Meter.test.ts b/packages/opentelemetry-metrics/test/Meter.test.ts index 5efdbb3499..62e8860c0f 100644 --- a/packages/opentelemetry-metrics/test/Meter.test.ts +++ b/packages/opentelemetry-metrics/test/Meter.test.ts @@ -114,6 +114,18 @@ describe('Meter', () => { assert.ok(record.resource instanceof Resource); }); + it('should pipe through instrumentation library', () => { + const counter = meter.createCounter('name') as CounterMetric; + assert.ok(counter.instrumentationLibrary); + + counter.add(1, { foo: 'bar' }); + + const [record] = counter.getMetricRecord(); + const { name, version } = record.instrumentationLibrary; + assert.strictEqual(name, 'test-meter'); + assert.strictEqual(version, '*'); + }); + describe('.bind()', () => { it('should create a counter instrument', () => { const counter = meter.createCounter('name') as CounterMetric; @@ -501,6 +513,20 @@ describe('Meter', () => { assert.ok(record.resource instanceof Resource); }); + it('should pipe through instrumentation library', () => { + const valueRecorder = meter.createValueRecorder( + 'name' + ) as ValueRecorderMetric; + assert.ok(valueRecorder.instrumentationLibrary); + + valueRecorder.record(1, { foo: 'bar' }); + + const [record] = valueRecorder.getMetricRecord(); + const { name, version } = record.instrumentationLibrary; + assert.strictEqual(name, 'test-meter'); + assert.strictEqual(version, '*'); + }); + describe('names', () => { it('should return no op metric if name is an empty string', () => { const valueRecorder = meter.createValueRecorder(''); @@ -719,6 +745,20 @@ describe('Meter', () => { const [record] = observer.getMetricRecord(); assert.ok(record.resource instanceof Resource); }); + + it('should pipe through instrumentation library', () => { + const observer = meter.createObserver('name') as ObserverMetric; + assert.ok(observer.instrumentationLibrary); + + observer.setCallback(result => { + result.observe(() => 42, { foo: 'bar' }); + }); + + const [record] = observer.getMetricRecord(); + const { name, version } = record.instrumentationLibrary; + assert.strictEqual(name, 'test-meter'); + assert.strictEqual(version, '*'); + }); }); describe('#getMetrics', () => { diff --git a/packages/opentelemetry-tracing/src/BasicTracerProvider.ts b/packages/opentelemetry-tracing/src/BasicTracerProvider.ts index e1b4a54bb0..cd19857f43 100644 --- a/packages/opentelemetry-tracing/src/BasicTracerProvider.ts +++ b/packages/opentelemetry-tracing/src/BasicTracerProvider.ts @@ -52,7 +52,10 @@ export class BasicTracerProvider implements api.TracerProvider { getTracer(name: string, version = '*', config?: TracerConfig): Tracer { const key = `${name}@${version}`; if (!this._tracers.has(key)) { - this._tracers.set(key, new Tracer(config || this._config, this)); + this._tracers.set( + key, + new Tracer({ name, version }, config || this._config, this) + ); } return this._tracers.get(key)!; diff --git a/packages/opentelemetry-tracing/src/Span.ts b/packages/opentelemetry-tracing/src/Span.ts index d602d64dd6..6272729d2d 100644 --- a/packages/opentelemetry-tracing/src/Span.ts +++ b/packages/opentelemetry-tracing/src/Span.ts @@ -18,6 +18,7 @@ import * as api from '@opentelemetry/api'; import { hrTime, hrTimeDuration, + InstrumentationLibrary, isTimeInput, timeInputToHrTime, } from '@opentelemetry/core'; @@ -41,6 +42,7 @@ export class Span implements api.Span, ReadableSpan { readonly events: api.TimedEvent[] = []; readonly startTime: api.HrTime; readonly resource: Resource; + readonly instrumentationLibrary: InstrumentationLibrary; name: string; status: api.Status = { code: api.CanonicalCode.OK, @@ -69,6 +71,7 @@ export class Span implements api.Span, ReadableSpan { this.links = links; this.startTime = timeInputToHrTime(startTime); this.resource = parentTracer.resource; + this.instrumentationLibrary = parentTracer.instrumentationLibrary; this._logger = parentTracer.logger; this._traceParams = parentTracer.getActiveTraceParams(); this._spanProcessor = parentTracer.getActiveSpanProcessor(); diff --git a/packages/opentelemetry-tracing/src/Tracer.ts b/packages/opentelemetry-tracing/src/Tracer.ts index 9bc8004eab..694829dda0 100644 --- a/packages/opentelemetry-tracing/src/Tracer.ts +++ b/packages/opentelemetry-tracing/src/Tracer.ts @@ -19,6 +19,7 @@ import { ConsoleLogger, getActiveSpan, getParentSpanContext, + InstrumentationLibrary, isValid, NoRecordingSpan, randomSpanId, @@ -39,12 +40,14 @@ export class Tracer implements api.Tracer { private readonly _sampler: api.Sampler; private readonly _traceParams: TraceParams; readonly resource: Resource; + readonly instrumentationLibrary: InstrumentationLibrary; readonly logger: api.Logger; /** * Constructs a new Tracer instance. */ constructor( + instrumentationLibrary: InstrumentationLibrary, config: TracerConfig, private _tracerProvider: BasicTracerProvider ) { @@ -53,6 +56,7 @@ export class Tracer implements api.Tracer { this._sampler = localConfig.sampler; this._traceParams = localConfig.traceParams; this.resource = _tracerProvider.resource; + this.instrumentationLibrary = instrumentationLibrary; this.logger = config.logger || new ConsoleLogger(config.logLevel); } diff --git a/packages/opentelemetry-tracing/src/export/ReadableSpan.ts b/packages/opentelemetry-tracing/src/export/ReadableSpan.ts index 48436444be..5964e0bf3a 100644 --- a/packages/opentelemetry-tracing/src/export/ReadableSpan.ts +++ b/packages/opentelemetry-tracing/src/export/ReadableSpan.ts @@ -24,6 +24,7 @@ import { TimedEvent, } from '@opentelemetry/api'; import { Resource } from '@opentelemetry/resources'; +import { InstrumentationLibrary } from '@opentelemetry/core'; export interface ReadableSpan { readonly name: string; @@ -39,4 +40,5 @@ export interface ReadableSpan { readonly duration: HrTime; readonly ended: boolean; readonly resource: Resource; + readonly instrumentationLibrary: InstrumentationLibrary; } diff --git a/packages/opentelemetry-tracing/test/Span.test.ts b/packages/opentelemetry-tracing/test/Span.test.ts index 9c070c4189..6f1494518f 100644 --- a/packages/opentelemetry-tracing/test/Span.test.ts +++ b/packages/opentelemetry-tracing/test/Span.test.ts @@ -230,6 +230,11 @@ describe('Span', () => { assert.deepStrictEqual(span.attributes, {}); assert.deepStrictEqual(span.links, []); assert.deepStrictEqual(span.events, []); + + assert.ok(span.instrumentationLibrary); + const { name, version } = span.instrumentationLibrary; + assert.strictEqual(name, 'default'); + assert.strictEqual(version, '*'); }); it('should return ReadableSpan with attributes', () => { diff --git a/packages/opentelemetry-tracing/test/Tracer.test.ts b/packages/opentelemetry-tracing/test/Tracer.test.ts index 1c4922db84..3403115ea6 100644 --- a/packages/opentelemetry-tracing/test/Tracer.test.ts +++ b/packages/opentelemetry-tracing/test/Tracer.test.ts @@ -17,7 +17,12 @@ import * as assert from 'assert'; import { NoopSpan, Sampler, SamplingDecision } from '@opentelemetry/api'; import { BasicTracerProvider, Tracer, Span } from '../src'; -import { NoopLogger, ALWAYS_SAMPLER, NEVER_SAMPLER } from '@opentelemetry/core'; +import { + InstrumentationLibrary, + NoopLogger, + ALWAYS_SAMPLER, + NEVER_SAMPLER, +} from '@opentelemetry/core'; describe('Tracer', () => { const tracerProvider = new BasicTracerProvider({ @@ -36,28 +41,57 @@ describe('Tracer', () => { } it('should create a Tracer instance', () => { - const tracer = new Tracer({}, tracerProvider); + const tracer = new Tracer( + { name: 'default', version: '0.0.1' }, + {}, + tracerProvider + ); assert.ok(tracer instanceof Tracer); }); it('should respect NO_RECORD sampling result', () => { - const tracer = new Tracer({ sampler: NEVER_SAMPLER }, tracerProvider); + const tracer = new Tracer( + { name: 'default', version: '0.0.1' }, + { sampler: NEVER_SAMPLER }, + tracerProvider + ); const span = tracer.startSpan('span1'); assert.ok(span instanceof NoopSpan); span.end(); }); it('should respect RECORD_AND_SAMPLE sampling result', () => { - const tracer = new Tracer({ sampler: ALWAYS_SAMPLER }, tracerProvider); + const tracer = new Tracer( + { name: 'default', version: '0.0.1' }, + { sampler: ALWAYS_SAMPLER }, + tracerProvider + ); const span = tracer.startSpan('span2'); assert.ok(!(span instanceof NoopSpan)); span.end(); }); it('should start a span with attributes in sampling result', () => { - const tracer = new Tracer({ sampler: new TestSampler() }, tracerProvider); + const tracer = new Tracer( + { name: 'default', version: '0.0.1' }, + { sampler: new TestSampler() }, + tracerProvider + ); const span = tracer.startSpan('span3'); assert.strictEqual((span as Span).attributes.testAttribute, 'foobar'); span.end(); }); + + it('should have an instrumentationLibrary', () => { + const tracer = new Tracer( + { name: 'default', version: '0.0.1' }, + {}, + tracerProvider + ); + + const lib: InstrumentationLibrary = tracer.instrumentationLibrary; + + assert.strictEqual(lib.name, 'default'); + assert.strictEqual(lib.version, '0.0.1'); + }); });