From 4627892c4a77a942cfd34aa804bc2cf8e2d038ef Mon Sep 17 00:00:00 2001 From: Mayur Kale Date: Thu, 12 Mar 2020 16:03:03 -0700 Subject: [PATCH] export resource to exporters (#843) * export resource to exporters * update Zipkin and Stackdriver exporter to use Resource * chore: remove redundant dependency * update Collector exporter to use Resource * rebase with #846 * minor * fix collector resource * fix build --- .../package.json | 2 +- .../src/CollectorExporter.ts | 8 ++- .../src/platform/browser/sendSpans.ts | 6 ++- .../src/platform/node/sendSpans.ts | 52 ++++++++++++------- .../src/transform.ts | 16 ++++++ .../test/common/transform.test.ts | 20 +++++++ .../test/helper.ts | 6 ++- .../src/transform.ts | 6 +++ .../test/transform.test.ts | 29 +++++++++-- .../package.json | 2 +- .../src/transform.ts | 24 ++++++--- .../test/transform.test.ts | 11 +++- .../package.json | 2 +- .../src/transform.ts | 14 +++-- .../test/transform.test.ts | 29 +++++++++-- 15 files changed, 183 insertions(+), 44 deletions(-) diff --git a/packages/opentelemetry-exporter-collector/package.json b/packages/opentelemetry-exporter-collector/package.json index 9ca0d23062..9e11325b52 100644 --- a/packages/opentelemetry-exporter-collector/package.json +++ b/packages/opentelemetry-exporter-collector/package.json @@ -50,7 +50,6 @@ }, "devDependencies": { "@babel/core": "^7.6.0", - "@opentelemetry/resources": "^0.4.0", "@types/mocha": "^5.2.5", "@types/node": "^12.6.8", "@types/sinon": "^7.0.13", @@ -83,6 +82,7 @@ "@opentelemetry/api": "^0.4.0", "@opentelemetry/base": "^0.4.0", "@opentelemetry/core": "^0.4.0", + "@opentelemetry/resources": "^0.4.0", "@opentelemetry/tracing": "^0.4.0" } } diff --git a/packages/opentelemetry-exporter-collector/src/CollectorExporter.ts b/packages/opentelemetry-exporter-collector/src/CollectorExporter.ts index 82aee23399..2bbc989cc8 100644 --- a/packages/opentelemetry-exporter-collector/src/CollectorExporter.ts +++ b/packages/opentelemetry-exporter-collector/src/CollectorExporter.ts @@ -19,8 +19,9 @@ import { NoopLogger } from '@opentelemetry/core'; import { ReadableSpan, SpanExporter } from '@opentelemetry/tracing'; import { Attributes, Logger } from '@opentelemetry/api'; import * as collectorTypes from './types'; -import { toCollectorSpan } from './transform'; +import { toCollectorSpan, toCollectorResource } from './transform'; import { onInit, onShutdown, sendSpans } from './platform/index'; +import { Resource } from '@opentelemetry/resources'; /** * Collector Exporter Config @@ -100,10 +101,13 @@ export class CollectorExporter implements SpanExporter { toCollectorSpan(span) ); this.logger.debug('spans to be sent', spansToBeSent); + const resource = toCollectorResource( + spansToBeSent.length > 0 ? spans[0].resource : Resource.empty() + ); // Send spans to [opentelemetry collector]{@link https://github.com/open-telemetry/opentelemetry-collector} // it will use the appropriate transport layer automatically depends on platform - sendSpans(spansToBeSent, resolve, reject, this); + sendSpans(spansToBeSent, resolve, reject, this, resource); } catch (e) { reject(e); } diff --git a/packages/opentelemetry-exporter-collector/src/platform/browser/sendSpans.ts b/packages/opentelemetry-exporter-collector/src/platform/browser/sendSpans.ts index ca8b1f9203..a41faa3bf6 100644 --- a/packages/opentelemetry-exporter-collector/src/platform/browser/sendSpans.ts +++ b/packages/opentelemetry-exporter-collector/src/platform/browser/sendSpans.ts @@ -43,12 +43,14 @@ export function onShutdown(shutdownF: EventListener) { * @param onSuccess * @param onError * @param collectorExporter + * @param resource */ export function sendSpans( spans: collectorTypes.Span[], onSuccess: () => void, onError: (status?: number) => void, - collectorExporter: CollectorExporter + collectorExporter: CollectorExporter, + resource: collectorTypes.Resource ) { const exportTraceServiceRequest: collectorTypes.ExportTraceServiceRequest = { node: { @@ -66,7 +68,7 @@ export function sendSpans( }, attributes: collectorExporter.attributes, }, - // resource: '', not implemented + resource, spans, }; diff --git a/packages/opentelemetry-exporter-collector/src/platform/node/sendSpans.ts b/packages/opentelemetry-exporter-collector/src/platform/node/sendSpans.ts index aaf23d2820..18faf59bce 100644 --- a/packages/opentelemetry-exporter-collector/src/platform/node/sendSpans.ts +++ b/packages/opentelemetry-exporter-collector/src/platform/node/sendSpans.ts @@ -47,32 +47,20 @@ export function onShutdown(shutdownF: Function) {} * @param onSuccess * @param onError * @param collectorExporter + * @param resource */ export function sendSpans( spans: collectorTypes.Span[], onSuccess: () => void, onError: (status?: number) => void, - collectorExporter: CollectorExporter + collectorExporter: CollectorExporter, + resource: collectorTypes.Resource ) { - const exportTraceServiceRequest: collectorTypes.ExportTraceServiceRequest = { - node: { - identifier: { - hostName: collectorExporter.hostName, - startTimestamp: core.hrTimeToTimeStamp(core.hrTime()), - }, - libraryInfo: { - language: collectorTypes.LibraryInfoLanguage.NODE_JS, - coreLibraryVersion: core.VERSION, - exporterVersion: VERSION, - }, - serviceInfo: { - name: collectorExporter.serviceName, - }, - attributes: collectorExporter.attributes, - }, - // resource: '', not implemented + const exportTraceServiceRequest = toCollectorTraceServiceRequest( spans, - }; + collectorExporter, + resource + ); const body = JSON.stringify(exportTraceServiceRequest); const parsedUrl = url.parse(collectorExporter.url); @@ -105,3 +93,29 @@ export function sendSpans( req.write(body); req.end(); } + +export function toCollectorTraceServiceRequest( + spans: collectorTypes.Span[], + collectorExporter: CollectorExporter, + resource: collectorTypes.Resource +): collectorTypes.ExportTraceServiceRequest { + return { + node: { + identifier: { + hostName: collectorExporter.hostName, + startTimestamp: core.hrTimeToTimeStamp(core.hrTime()), + }, + libraryInfo: { + language: collectorTypes.LibraryInfoLanguage.NODE_JS, + coreLibraryVersion: core.VERSION, + exporterVersion: VERSION, + }, + serviceInfo: { + name: collectorExporter.serviceName, + }, + attributes: collectorExporter.attributes, + }, + resource, + spans, + }; +} diff --git a/packages/opentelemetry-exporter-collector/src/transform.ts b/packages/opentelemetry-exporter-collector/src/transform.ts index a1299ec8b9..84fb97f3d9 100644 --- a/packages/opentelemetry-exporter-collector/src/transform.ts +++ b/packages/opentelemetry-exporter-collector/src/transform.ts @@ -18,6 +18,7 @@ import { hexToBase64, hrTimeToTimeStamp } from '@opentelemetry/core'; import { ReadableSpan } from '@opentelemetry/tracing'; import { Attributes, Link, TimedEvent, TraceState } from '@opentelemetry/api'; import * as collectorTypes from './types'; +import { Resource } from '@opentelemetry/resources'; const OT_MAX_STRING_LENGTH = 128; @@ -201,6 +202,21 @@ export function toCollectorSpan(span: ReadableSpan): collectorTypes.Span { }; } +/** + * converts span resource + * @param resource + */ +export function toCollectorResource( + resource: Resource +): collectorTypes.Resource { + const labels: { [key: string]: string } = {}; + Object.keys(resource.labels).forEach( + name => (labels[name] = String(resource.labels[name])) + ); + // @TODO: add type support + return { labels }; +} + /** * @param traceState */ diff --git a/packages/opentelemetry-exporter-collector/test/common/transform.test.ts b/packages/opentelemetry-exporter-collector/test/common/transform.test.ts index 4eb4213eac..544c7a136f 100644 --- a/packages/opentelemetry-exporter-collector/test/common/transform.test.ts +++ b/packages/opentelemetry-exporter-collector/test/common/transform.test.ts @@ -18,6 +18,7 @@ import { Attributes, TimedEvent } from '@opentelemetry/api'; import * as assert from 'assert'; import * as transform from '../../src/transform'; import { ensureSpanIsCorrect, mockedReadableSpan } from '../helper'; +import { Resource } from '@opentelemetry/resources'; describe('transform', () => { describe('toCollectorTruncatableString', () => { @@ -149,4 +150,23 @@ describe('transform', () => { ensureSpanIsCorrect(transform.toCollectorSpan(mockedReadableSpan)); }); }); + + describe('toCollectorResource', () => { + it('should convert resource', () => { + const resource = transform.toCollectorResource( + new Resource({ + service: 'ui', + version: 1.0, + success: true, + }) + ); + assert.deepStrictEqual(resource, { + labels: { + service: 'ui', + version: '1', + success: 'true', + }, + }); + }); + }); }); diff --git a/packages/opentelemetry-exporter-collector/test/helper.ts b/packages/opentelemetry-exporter-collector/test/helper.ts index 750f32599b..043865b86a 100644 --- a/packages/opentelemetry-exporter-collector/test/helper.ts +++ b/packages/opentelemetry-exporter-collector/test/helper.ts @@ -69,7 +69,11 @@ export const mockedReadableSpan: ReadableSpan = { }, ], duration: [0, 8885000], - resource: Resource.empty(), + resource: new Resource({ + service: 'ui', + version: 1, + cost: 112.12, + }), }; export function ensureSpanIsCorrect(span: collectorTypes.Span) { diff --git a/packages/opentelemetry-exporter-jaeger/src/transform.ts b/packages/opentelemetry-exporter-jaeger/src/transform.ts index 04279deb35..668576ce6e 100644 --- a/packages/opentelemetry-exporter-jaeger/src/transform.ts +++ b/packages/opentelemetry-exporter-jaeger/src/transform.ts @@ -64,6 +64,12 @@ export function spanToThrift(span: ReadableSpan): ThriftSpan { if (span.kind !== undefined) { tags.push({ key: 'span.kind', value: SpanKind[span.kind] }); } + Object.keys(span.resource.labels).forEach(name => + tags.push({ + key: name, + value: toTagValue(span.resource.labels[name]), + }) + ); const spanTags: ThriftTag[] = ThriftUtils.getThriftTags(tags); diff --git a/packages/opentelemetry-exporter-jaeger/test/transform.test.ts b/packages/opentelemetry-exporter-jaeger/test/transform.test.ts index bc58b031aa..c79fb6d1b6 100644 --- a/packages/opentelemetry-exporter-jaeger/test/transform.test.ts +++ b/packages/opentelemetry-exporter-jaeger/test/transform.test.ts @@ -70,7 +70,11 @@ describe('transform', () => { }, ], duration: [32, 800000000], - resource: Resource.empty(), + resource: new Resource({ + service: 'ui', + version: 1, + cost: 112.12, + }), }; const thriftSpan = spanToThrift(readableSpan); @@ -95,8 +99,18 @@ describe('transform', () => { thriftSpan.startTime, Utils.encodeInt64(hrTimeToMicroseconds(readableSpan.startTime)) ); - assert.strictEqual(thriftSpan.tags.length, 6); - const [tag1, tag2, tag3, tag4, tag5, tag6] = thriftSpan.tags; + assert.strictEqual(thriftSpan.tags.length, 9); + const [ + tag1, + tag2, + tag3, + tag4, + tag5, + tag6, + tag7, + tag8, + tag9, + ] = thriftSpan.tags; assert.strictEqual(tag1.key, 'testBool'); assert.strictEqual(tag1.vType, 'BOOL'); assert.strictEqual(tag1.vBool, true); @@ -115,6 +129,15 @@ describe('transform', () => { assert.strictEqual(tag6.key, 'span.kind'); assert.strictEqual(tag6.vType, 'STRING'); assert.strictEqual(tag6.vStr, 'INTERNAL'); + assert.strictEqual(tag7.key, 'service'); + assert.strictEqual(tag7.vType, 'STRING'); + assert.strictEqual(tag7.vStr, 'ui'); + assert.strictEqual(tag8.key, 'version'); + assert.strictEqual(tag8.vType, 'DOUBLE'); + assert.strictEqual(tag8.vDouble, 1); + assert.strictEqual(tag9.key, 'cost'); + assert.strictEqual(tag9.vType, 'DOUBLE'); + assert.strictEqual(tag9.vDouble, 112.12); assert.strictEqual(thriftSpan.references.length, 0); assert.strictEqual(thriftSpan.logs.length, 1); diff --git a/packages/opentelemetry-exporter-stackdriver-trace/package.json b/packages/opentelemetry-exporter-stackdriver-trace/package.json index f1de5af310..088bb8580c 100644 --- a/packages/opentelemetry-exporter-stackdriver-trace/package.json +++ b/packages/opentelemetry-exporter-stackdriver-trace/package.json @@ -41,7 +41,6 @@ "access": "public" }, "devDependencies": { - "@opentelemetry/resources": "^0.4.0", "@types/mocha": "^5.2.7", "@types/nock": "^11.1.0", "@types/node": "^12.6.9", @@ -63,6 +62,7 @@ "@opentelemetry/api": "^0.4.0", "@opentelemetry/base": "^0.4.0", "@opentelemetry/core": "^0.4.0", + "@opentelemetry/resources": "^0.4.0", "@opentelemetry/tracing": "^0.4.0", "google-auth-library": "^5.7.0", "googleapis": "^46.0.0" diff --git a/packages/opentelemetry-exporter-stackdriver-trace/src/transform.ts b/packages/opentelemetry-exporter-stackdriver-trace/src/transform.ts index d694f9e216..b99e34f607 100644 --- a/packages/opentelemetry-exporter-stackdriver-trace/src/transform.ts +++ b/packages/opentelemetry-exporter-stackdriver-trace/src/transform.ts @@ -30,6 +30,7 @@ import { TruncatableString, } from './types'; import { VERSION } from './version'; +import { Resource } from '@opentelemetry/resources'; const AGENT_LABEL_KEY = 'g.co/agent'; const AGENT_LABEL_VALUE = `opentelemetry-js [${CORE_VERSION}]; stackdriver-trace-exporter [${VERSION}]`; @@ -38,10 +39,14 @@ export function getReadableSpanTransformer( projectId: string ): (span: ReadableSpan) => Span { return span => { - const attributes = transformAttributes(span.attributes, { - project_id: projectId, - [AGENT_LABEL_KEY]: AGENT_LABEL_VALUE, - }); + const attributes = transformAttributes( + span.attributes, + { + project_id: projectId, + [AGENT_LABEL_KEY]: AGENT_LABEL_VALUE, + }, + span.resource + ); const out: Span = { attributes, @@ -85,9 +90,16 @@ function transformLink(link: ot.Link): Link { function transformAttributes( requestAttributes: ot.Attributes = {}, - serviceAttributes: ot.Attributes = {} + serviceAttributes: ot.Attributes = {}, + resource: Resource = Resource.empty() ): Attributes { - const attributes = Object.assign({}, requestAttributes, serviceAttributes); + const attributes = Object.assign( + {}, + requestAttributes, + serviceAttributes, + resource.labels + ); + const attributeMap = transformAttributeValues(attributes); return { attributeMap: attributeMap, diff --git a/packages/opentelemetry-exporter-stackdriver-trace/test/transform.test.ts b/packages/opentelemetry-exporter-stackdriver-trace/test/transform.test.ts index 32f7de8312..a1d257b89c 100644 --- a/packages/opentelemetry-exporter-stackdriver-trace/test/transform.test.ts +++ b/packages/opentelemetry-exporter-stackdriver-trace/test/transform.test.ts @@ -51,7 +51,11 @@ describe('transform', () => { name: 'my-span', spanContext, status: { code: types.CanonicalCode.OK }, - resource: Resource.empty(), + resource: new Resource({ + service: 'ui', + version: 1, + cost: 112.12, + }), }; }); @@ -67,6 +71,9 @@ describe('transform', () => { value: `opentelemetry-js [${CORE_VERSION}]; stackdriver-trace-exporter [${VERSION}]`, }, }, + cost: { intValue: '112' }, + service: { stringValue: { value: 'ui' } }, + version: { intValue: '1' }, }, droppedAttributesCount: 0, }, @@ -130,7 +137,7 @@ describe('transform', () => { assert.deepStrictEqual(result.attributes!.droppedAttributesCount, 1); assert.deepStrictEqual( Object.keys(result.attributes!.attributeMap!).length, - 2 + 5 ); }); diff --git a/packages/opentelemetry-exporter-zipkin/package.json b/packages/opentelemetry-exporter-zipkin/package.json index 44788807a6..3b5db3ba0b 100644 --- a/packages/opentelemetry-exporter-zipkin/package.json +++ b/packages/opentelemetry-exporter-zipkin/package.json @@ -39,7 +39,6 @@ "access": "public" }, "devDependencies": { - "@opentelemetry/resources": "^0.4.0", "@types/mocha": "^5.2.7", "@types/nock": "^10.0.3", "@types/node": "^12.6.9", @@ -59,6 +58,7 @@ "@opentelemetry/api": "^0.4.0", "@opentelemetry/base": "^0.4.0", "@opentelemetry/core": "^0.4.0", + "@opentelemetry/resources": "^0.4.0", "@opentelemetry/tracing": "^0.4.0" } } diff --git a/packages/opentelemetry-exporter-zipkin/src/transform.ts b/packages/opentelemetry-exporter-zipkin/src/transform.ts index cbf0908526..b52e152f7a 100644 --- a/packages/opentelemetry-exporter-zipkin/src/transform.ts +++ b/packages/opentelemetry-exporter-zipkin/src/transform.ts @@ -18,6 +18,7 @@ import * as types from '@opentelemetry/api'; import { ReadableSpan } from '@opentelemetry/tracing'; import { hrTimeToMicroseconds } from '@opentelemetry/core'; import * as zipkinTypes from './types'; +import { Resource } from '@opentelemetry/resources'; const ZIPKIN_SPAN_KIND_MAPPING = { [types.SpanKind.CLIENT]: zipkinTypes.SpanKind.CLIENT, @@ -54,7 +55,8 @@ export function toZipkinSpan( span.attributes, span.status, statusCodeTagName, - statusDescriptionTagName + statusDescriptionTagName, + span.resource ), annotations: span.events.length ? _toZipkinAnnotations(span.events) @@ -69,9 +71,10 @@ export function _toZipkinTags( attributes: types.Attributes, status: types.Status, statusCodeTagName: string, - statusDescriptionTagName: string + statusDescriptionTagName: string, + resource: Resource ): zipkinTypes.Tags { - const tags: { [key: string]: string } = {}; + const tags: { [key: string]: unknown } = {}; for (const key of Object.keys(attributes)) { tags[key] = String(attributes[key]); } @@ -79,6 +82,11 @@ export function _toZipkinTags( if (status.message) { tags[statusDescriptionTagName] = status.message; } + + Object.keys(resource.labels).forEach( + name => (tags[name] = resource.labels[name]) + ); + return tags; } diff --git a/packages/opentelemetry-exporter-zipkin/test/transform.test.ts b/packages/opentelemetry-exporter-zipkin/test/transform.test.ts index f32512003f..6f5854d2e6 100644 --- a/packages/opentelemetry-exporter-zipkin/test/transform.test.ts +++ b/packages/opentelemetry-exporter-zipkin/test/transform.test.ts @@ -21,6 +21,7 @@ import { NoopLogger, hrTimeToMicroseconds, hrTimeDuration, + VERSION, } from '@opentelemetry/core'; import { toZipkinSpan, @@ -30,6 +31,7 @@ import { statusDescriptionTagName, } from '../src/transform'; import * as zipkinTypes from '../src/types'; +import { Resource } from '@opentelemetry/resources'; const logger = new NoopLogger(); const tracer = new BasicTracerProvider({ @@ -42,6 +44,12 @@ const spanContext: types.SpanContext = { traceFlags: types.TraceFlags.SAMPLED, }; +const DUMMY_RESOUCE = new Resource({ + service: 'ui', + version: 1, + cost: 112.12, +}); + describe('transform', () => { describe('toZipkinSpan', () => { it('should convert an OpenTelemetry span to a Zipkin span', () => { @@ -86,6 +94,9 @@ describe('transform', () => { key1: 'value1', key2: 'value2', [statusCodeTagName]: 'OK', + 'telemetry.sdk.language': 'nodejs', + 'telemetry.sdk.name': 'opentelemetry', + 'telemetry.sdk.version': VERSION, }, timestamp: hrTimeToMicroseconds(span.startTime), traceId: span.spanContext.traceId, @@ -120,6 +131,9 @@ describe('transform', () => { parentId: undefined, tags: { [statusCodeTagName]: 'OK', + 'telemetry.sdk.language': 'nodejs', + 'telemetry.sdk.name': 'opentelemetry', + 'telemetry.sdk.version': VERSION, }, timestamp: hrTimeToMicroseconds(span.startTime), traceId: span.spanContext.traceId, @@ -159,6 +173,9 @@ describe('transform', () => { parentId: undefined, tags: { [statusCodeTagName]: 'OK', + 'telemetry.sdk.language': 'nodejs', + 'telemetry.sdk.name': 'opentelemetry', + 'telemetry.sdk.version': VERSION, }, timestamp: hrTimeToMicroseconds(span.startTime), traceId: span.spanContext.traceId, @@ -184,13 +201,17 @@ describe('transform', () => { span.attributes, span.status, statusCodeTagName, - statusDescriptionTagName + statusDescriptionTagName, + DUMMY_RESOUCE ); assert.deepStrictEqual(tags, { key1: 'value1', key2: 'value2', [statusCodeTagName]: 'OK', + cost: 112.12, + service: 'ui', + version: 1, }); }); it('should map OpenTelemetry Status.code to a Zipkin tag', () => { @@ -213,7 +234,8 @@ describe('transform', () => { span.attributes, span.status, statusCodeTagName, - statusDescriptionTagName + statusDescriptionTagName, + Resource.empty() ); assert.deepStrictEqual(tags, { @@ -243,7 +265,8 @@ describe('transform', () => { span.attributes, span.status, statusCodeTagName, - statusDescriptionTagName + statusDescriptionTagName, + Resource.empty() ); assert.deepStrictEqual(tags, {