diff --git a/README.md b/README.md index 3a9a825440..9bfcce580d 100644 --- a/README.md +++ b/README.md @@ -276,6 +276,13 @@ These instrumentations are hosted at - -This example shows how to use [@opentelemetry/exporter-otlp-http](https://github.com/open-telemetry/opentelemetry-js/tree/v0.26.0/experimental/packages/opentelemetry-exporter-otlp-http) to instrument a simple Node.js application. - -This example will export spans data simultaneously using [Exporter Collector](https://github.com/open-telemetry/opentelemetry-js/tree/v0.26.0/experimental/packages/opentelemetry-exporter-otlp-http) and grpc. It will use [proto format](https://github.com/open-telemetry/opentelemetry-proto). +This example shows how to use +[@opentelemetry/exporter-trace-otlp-http](https://github.com/open-telemetry/opentelemetry-js/tree/v0.28.0/experimental/packages/exporter-trace-otlp-http) +and [@opentelemetry/exporter-metrics-otlp-http](https://github.com/open-telemetry/opentelemetry-js/tree/v0.28.0/experimental/packages/opentelemetry-exporter-metrics-otlp-http) +to instrument a simple Node.js application. ## Installation diff --git a/examples/otlp-exporter-node/docker/docker-compose.yaml b/examples/otlp-exporter-node/docker/docker-compose.yaml index d0b6d62e7c..6eaed872a6 100644 --- a/examples/otlp-exporter-node/docker/docker-compose.yaml +++ b/examples/otlp-exporter-node/docker/docker-compose.yaml @@ -2,7 +2,7 @@ version: "3" services: # Collector collector: - image: otel/opentelemetry-collector-contrib:0.42.0 + image: otel/opentelemetry-collector-contrib:0.50.0 # image: otel/opentelemetry-collector-contrib:latest command: ["--config=/conf/collector-config.yaml"] volumes: diff --git a/examples/otlp-exporter-node/metrics.js b/examples/otlp-exporter-node/metrics.js index 114fd0028a..049d8229ec 100644 --- a/examples/otlp-exporter-node/metrics.js +++ b/examples/otlp-exporter-node/metrics.js @@ -2,26 +2,29 @@ const { DiagConsoleLogger, DiagLogLevel, diag } = require('@opentelemetry/api'); const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-http'); -// const { OTLPMetricExporter } = require('@opentelemetry/exporter-otlp-grpc'); -// const { OTLPMetricExporter } = require('@opentelemetry/exporter-otlp-proto'); -const { MeterProvider } = require('@opentelemetry/sdk-metrics-base'); +// const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-grpc'); +// const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-proto'); +const { MeterProvider, PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics-base'); const { Resource } = require('@opentelemetry/resources'); const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions'); // Optional and only needed to see the internal diagnostic logging (during development) diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG); -const metricExporter = new OTLPMetricExporter({ - url: 'http://localhost:4318/v1/metrics', -}); +const metricExporter = new OTLPMetricExporter({}); -const meter = new MeterProvider({ - exporter: metricExporter, - interval: 1000, +const meterProvider = new MeterProvider({ resource: new Resource({ [SemanticResourceAttributes.SERVICE_NAME]: 'basic-metric-service', }), -}).getMeter('example-exporter-collector'); +}); + +meterProvider.addMetricReader(new PeriodicExportingMetricReader({ + exporter: metricExporter, + exportIntervalMillis: 1000, +})); + +const meter = meterProvider.getMeter('example-exporter-collector'); const requestCounter = meter.createCounter('requests', { description: 'Example of a Counter', @@ -35,10 +38,10 @@ const histogram = meter.createHistogram('test_histogram', { description: 'Example of a Histogram', }); -const labels = { pid: process.pid, environment: 'staging' }; +const attributes = { pid: process.pid, environment: 'staging' }; setInterval(() => { - requestCounter.add(1, labels); - upDownCounter.add(Math.random() > 0.5 ? 1 : -1, labels); - histogram.record(Math.random(), labels); + requestCounter.add(1, attributes); + upDownCounter.add(Math.random() > 0.5 ? 1 : -1, attributes); + histogram.record(Math.random(), attributes); }, 1000); diff --git a/examples/otlp-exporter-node/package.json b/examples/otlp-exporter-node/package.json index 4a3c3aacf4..a43a114437 100644 --- a/examples/otlp-exporter-node/package.json +++ b/examples/otlp-exporter-node/package.json @@ -29,17 +29,18 @@ }, "dependencies": { "@opentelemetry/api": "^1.0.2", - "@opentelemetry/core": "1.0.1", - "@opentelemetry/exporter-trace-otlp-http": "0.27.0", - "@opentelemetry/exporter-metrics-otlp-http": "0.27.0", - "@opentelemetry/exporter-trace-otlp-grpc": "0.27.0", - "@opentelemetry/exporter-metrics-otlp-grpc": "0.27.0", - "@opentelemetry/exporter-trace-otlp-proto": "0.27.0", - "@opentelemetry/exporter-metrics-otlp-proto": "0.27.0", - "@opentelemetry/resources": "1.0.1", - "@opentelemetry/semantic-conventions": "1.0.1", - "@opentelemetry/sdk-metrics-base": "0.27.0", - "@opentelemetry/sdk-trace-base": "1.0.1" + "@opentelemetry/api-metrics": "0.28.0", + "@opentelemetry/core": "1.1.1", + "@opentelemetry/exporter-trace-otlp-http": "0.28.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.28.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.28.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "0.28.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.28.0", + "@opentelemetry/exporter-metrics-otlp-proto": "0.28.0", + "@opentelemetry/resources": "1.1.1", + "@opentelemetry/semantic-conventions": "1.1.1", + "@opentelemetry/sdk-metrics-base": "0.28.0", + "@opentelemetry/sdk-trace-base": "1.1.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js#readme" } diff --git a/examples/otlp-exporter-node/tracing.js b/examples/otlp-exporter-node/tracing.js index 661c419348..d2a6414442 100644 --- a/examples/otlp-exporter-node/tracing.js +++ b/examples/otlp-exporter-node/tracing.js @@ -1,17 +1,20 @@ 'use strict'; -const opentelemetry = require('@opentelemetry/api'); const { BasicTracerProvider, ConsoleSpanExporter, SimpleSpanProcessor } = require('@opentelemetry/sdk-trace-base'); -const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http'); const { Resource } = require('@opentelemetry/resources'); const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions'); -// const { OTLPTraceExporter } = require('@opentelemetry/exporter-otlp-grpc'); -// const { OTLPTraceExporter } = require('@opentelemetry/exporter-otlp-proto'); +const { + diag, + trace, + context, + DiagConsoleLogger, + DiagLogLevel, +} = require('@opentelemetry/api'); +const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http'); +// const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc'); +// const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-proto'); -// opentelemetry.diag.setLogger( -// new opentelemetry.DiagConsoleLogger(), -// opentelemetry.DiagLogLevel.DEBUG, -// ); +diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG); const exporter = new OTLPTraceExporter({ // headers: { @@ -28,7 +31,7 @@ provider.addSpanProcessor(new SimpleSpanProcessor(exporter)); provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); provider.register(); -const tracer = opentelemetry.trace.getTracer('example-otlp-exporter-node'); +const tracer = trace.getTracer('example-otlp-exporter-node'); // Create a span. A span must be closed. const parentSpan = tracer.startSpan('main'); @@ -47,7 +50,7 @@ setTimeout(() => { function doWork(parent) { // Start another span. In this example, the main method already started a // span, so that'll be the parent span, and this will be a child span. - const ctx = opentelemetry.trace.setSpan(opentelemetry.context.active(), parent); + const ctx = trace.setSpan(context.active(), parent); const span = tracer.startSpan('doWork', undefined, ctx); // simulate some random work. diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 4eed8e2217..1f5c9f30f0 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -8,6 +8,8 @@ All notable changes to experimental packages in this project will be documented ### :rocket: (Enhancement) +* feat(exporters): update proto version and use otlp-transformer #2929 @pichlermarc + ### :bug: (Bug Fix) ### :books: (Refine Doc) diff --git a/experimental/packages/exporter-trace-otlp-grpc/README.md b/experimental/packages/exporter-trace-otlp-grpc/README.md index 1ce3b37ada..fa58300b9b 100644 --- a/experimental/packages/exporter-trace-otlp-grpc/README.md +++ b/experimental/packages/exporter-trace-otlp-grpc/README.md @@ -3,7 +3,8 @@ [![NPM Published Version][npm-img]][npm-url] [![Apache License][license-image]][license-image] -This module provides exporter for web and node to be used with [opentelemetry-collector][opentelemetry-collector-url] - last tested with version **0.25.0**. +This module provides exporter for web and node to be used with [opentelemetry-collector][opentelemetry-collector-url]. +Compatible with [opentelemetry-collector][opentelemetry-collector-url] versions `>=0.16 <=0.50`. ## Installation @@ -135,9 +136,8 @@ OTEL_EXPORTER_OTLP_TRACES_COMPRESSION=gzip ## Running opentelemetry-collector locally to see the traces -1. Go to examples/otlp-exporter-node -2. run `npm run docker:start` -3. Open page at `http://localhost:9411/zipkin/` to observe the traces +1. Go to `examples/otlp-exporter-node` +2. Follow the instructions there to inspect traces. ## Useful links diff --git a/experimental/packages/exporter-trace-otlp-grpc/package.json b/experimental/packages/exporter-trace-otlp-grpc/package.json index c63e084cff..fcc3bd3eff 100644 --- a/experimental/packages/exporter-trace-otlp-grpc/package.json +++ b/experimental/packages/exporter-trace-otlp-grpc/package.json @@ -70,7 +70,7 @@ "@opentelemetry/core": "1.2.0", "@grpc/grpc-js": "^1.5.9", "@grpc/proto-loader": "^0.6.9", - "@opentelemetry/exporter-trace-otlp-http": "0.28.0", + "@opentelemetry/otlp-transformer": "0.28.0", "@opentelemetry/otlp-grpc-exporter-base": "0.28.0", "@opentelemetry/resources": "1.2.0", "@opentelemetry/sdk-trace-base": "1.2.0" diff --git a/experimental/packages/exporter-trace-otlp-grpc/src/OTLPTraceExporter.ts b/experimental/packages/exporter-trace-otlp-grpc/src/OTLPTraceExporter.ts index d08e679e99..5ea0829dc5 100644 --- a/experimental/packages/exporter-trace-otlp-grpc/src/OTLPTraceExporter.ts +++ b/experimental/packages/exporter-trace-otlp-grpc/src/OTLPTraceExporter.ts @@ -15,10 +15,6 @@ */ import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'; -import { - otlpTypes, - toOTLPExportTraceServiceRequest, -} from '@opentelemetry/exporter-trace-otlp-http'; import { baggageUtils, getEnv } from '@opentelemetry/core'; import { Metadata } from '@grpc/grpc-js'; import { @@ -27,6 +23,7 @@ import { ServiceClientType, validateAndNormalizeUrl } from '@opentelemetry/otlp-grpc-exporter-base'; +import { createExportTraceServiceRequest, IExportTraceServiceRequest } from '@opentelemetry/otlp-transformer'; const DEFAULT_COLLECTOR_URL = 'localhost:4317'; @@ -35,7 +32,7 @@ const DEFAULT_COLLECTOR_URL = 'localhost:4317'; */ export class OTLPTraceExporter extends OTLPGRPCExporterNodeBase + IExportTraceServiceRequest> implements SpanExporter { constructor(config: OTLPGRPCExporterConfigNode = {}) { @@ -47,10 +44,8 @@ export class OTLPTraceExporter } } - convert( - spans: ReadableSpan[] - ): otlpTypes.opentelemetryProto.collector.trace.v1.ExportTraceServiceRequest { - return toOTLPExportTraceServiceRequest(spans, this); + convert(spans: ReadableSpan[]): IExportTraceServiceRequest { + return createExportTraceServiceRequest(spans); } getDefaultUrl(config: OTLPGRPCExporterConfigNode) { diff --git a/experimental/packages/exporter-trace-otlp-grpc/test/OTLPTraceExporter.test.ts b/experimental/packages/exporter-trace-otlp-grpc/test/OTLPTraceExporter.test.ts index fc4383f183..5eb2fc585d 100644 --- a/experimental/packages/exporter-trace-otlp-grpc/test/OTLPTraceExporter.test.ts +++ b/experimental/packages/exporter-trace-otlp-grpc/test/OTLPTraceExporter.test.ts @@ -15,7 +15,6 @@ */ import * as protoLoader from '@grpc/proto-loader'; -import { otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; import { diag } from '@opentelemetry/api'; import { BasicTracerProvider, @@ -37,6 +36,7 @@ import { } from './traceHelper'; import { CompressionAlgorithm } from '@opentelemetry/otlp-exporter-base'; import { GrpcCompressionAlgorithm } from '@opentelemetry/otlp-grpc-exporter-base'; +import { IExportTraceServiceRequest, IResourceSpans } from '@opentelemetry/otlp-transformer'; const traceServiceProtoPath = 'opentelemetry/proto/collector/trace/v1/trace_service.proto'; @@ -59,7 +59,7 @@ const testCollectorExporter = (params: TestParams) => let collectorExporter: OTLPTraceExporter; let server: grpc.Server; let exportedData: - | otlpTypes.opentelemetryProto.trace.v1.ResourceSpans + | IResourceSpans | undefined; let reqMetadata: grpc.Metadata | undefined; @@ -83,15 +83,13 @@ const testCollectorExporter = (params: TestParams) => .service, { Export: (data: { - request: otlpTypes.opentelemetryProto.collector.trace.v1.ExportTraceServiceRequest; + request: IExportTraceServiceRequest; metadata: grpc.Metadata; }) => { - try { + if (data.request.resourceSpans != null) { exportedData = data.request.resourceSpans[0]; - reqMetadata = data.metadata; - } catch (e) { - exportedData = undefined; } + reqMetadata = data.metadata; }, } ); @@ -178,24 +176,26 @@ const testCollectorExporter = (params: TestParams) => typeof exportedData !== 'undefined', 'resource' + " doesn't exist" ); - let spans; - let resource; - if (exportedData) { - spans = exportedData.instrumentationLibrarySpans[0].spans; - resource = exportedData.resource; - ensureExportedSpanIsCorrect(spans[0]); - assert.ok( - typeof resource !== 'undefined', - "resource doesn't exist" - ); - if (resource) { - ensureResourceIsCorrect(resource); - } - } - if (params.metadata && reqMetadata) { - ensureMetadataIsCorrect(reqMetadata, params.metadata); - } + const spans = exportedData.scopeSpans[0].spans; + const resource = exportedData.resource; + + assert.ok( + typeof spans !== 'undefined', + 'spans do not exist' + ); + + ensureExportedSpanIsCorrect(spans[0]); + + assert.ok( + typeof resource !== 'undefined', + "resource doesn't exist" + ); + + ensureResourceIsCorrect(resource); + + ensureMetadataIsCorrect(reqMetadata, params?.metadata); + done(); }, 200); }); @@ -228,24 +228,23 @@ const testCollectorExporter = (params: TestParams) => typeof exportedData !== 'undefined', 'resource' + " doesn't exist" ); - let spans; - let resource; - if (exportedData) { - spans = exportedData.instrumentationLibrarySpans[0].spans; - resource = exportedData.resource; - ensureExportedSpanIsCorrect(spans[0]); + const spans = exportedData.scopeSpans[0].spans; + const resource = exportedData.resource; + + assert.ok( + typeof spans !== 'undefined', + 'spans do not exist' + ); + ensureExportedSpanIsCorrect(spans[0]); + + assert.ok( + typeof resource !== 'undefined', + "resource doesn't exist" + ); + ensureResourceIsCorrect(resource); + + ensureMetadataIsCorrect(reqMetadata, params.metadata); - assert.ok( - typeof resource !== 'undefined', - "resource doesn't exist" - ); - if (resource) { - ensureResourceIsCorrect(resource); - } - } - if (params.metadata && reqMetadata) { - ensureMetadataIsCorrect(reqMetadata, params.metadata); - } done(); }, 500); }); @@ -261,7 +260,7 @@ const testCollectorExporter = (params: TestParams) => ) : undefined; - envSource.OTEL_EXPORTER_OTLP_COMPRESSION='gzip'; + envSource.OTEL_EXPORTER_OTLP_COMPRESSION = 'gzip'; collectorExporter = new OTLPTraceExporter({ url: 'grpcs://' + address, credentials, @@ -337,5 +336,3 @@ describe('when configuring via environment', () => { testCollectorExporter({ useTLS: true }); testCollectorExporter({ useTLS: false }); testCollectorExporter({ metadata }); - - diff --git a/experimental/packages/exporter-trace-otlp-grpc/test/traceHelper.ts b/experimental/packages/exporter-trace-otlp-grpc/test/traceHelper.ts index 83578d3c12..5b0d0444a1 100644 --- a/experimental/packages/exporter-trace-otlp-grpc/test/traceHelper.ts +++ b/experimental/packages/exporter-trace-otlp-grpc/test/traceHelper.ts @@ -15,12 +15,12 @@ */ import { SpanStatusCode, TraceFlags } from '@opentelemetry/api'; -import { otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; import { Resource } from '@opentelemetry/resources'; import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; import * as assert from 'assert'; import * as grpc from '@grpc/grpc-js'; import { VERSION } from '@opentelemetry/core'; +import { IEvent, IKeyValue, ILink, IResource, ISpan } from '@opentelemetry/otlp-transformer'; const traceIdArr = [ 31, @@ -101,7 +101,7 @@ export const mockedReadableSpan: ReadableSpan = { }; export function ensureExportedEventsAreCorrect( - events: otlpTypes.opentelemetryProto.trace.v1.Span.Event[] + events: IEvent[] ) { assert.deepStrictEqual( events, @@ -160,7 +160,7 @@ export function ensureExportedEventsAreCorrect( } export function ensureExportedAttributesAreCorrect( - attributes: otlpTypes.opentelemetryProto.common.v1.KeyValue[] + attributes: IKeyValue[] ) { assert.deepStrictEqual( attributes, @@ -178,7 +178,7 @@ export function ensureExportedAttributesAreCorrect( } export function ensureExportedLinksAreCorrect( - attributes: otlpTypes.opentelemetryProto.trace.v1.Span.Link[] + attributes: ILink[] ) { assert.deepStrictEqual( attributes, @@ -204,7 +204,7 @@ export function ensureExportedLinksAreCorrect( } export function ensureExportedSpanIsCorrect( - span: otlpTypes.opentelemetryProto.trace.v1.Span + span: ISpan ) { if (span.attributes) { ensureExportedAttributesAreCorrect(span.attributes); @@ -254,7 +254,6 @@ export function ensureExportedSpanIsCorrect( span.status, { code: 'STATUS_CODE_OK', - deprecatedCode: 'DEPRECATED_STATUS_CODE_OK', message: '', }, 'status is wrong' @@ -262,7 +261,7 @@ export function ensureExportedSpanIsCorrect( } export function ensureResourceIsCorrect( - resource: otlpTypes.opentelemetryProto.resource.v1.Resource + resource: IResource ) { assert.deepStrictEqual(resource, { attributes: [ @@ -321,11 +320,11 @@ export function ensureResourceIsCorrect( } export function ensureMetadataIsCorrect( - actual: grpc.Metadata, - expected: grpc.Metadata + actual?: grpc.Metadata, + expected?: grpc.Metadata ) { //ignore user agent - expected.remove('user-agent'); - actual.remove('user-agent'); - assert.deepStrictEqual(actual.getMap(), expected.getMap()); + expected?.remove('user-agent'); + actual?.remove('user-agent'); + assert.deepStrictEqual(actual?.getMap(), expected?.getMap() ?? {}); } diff --git a/experimental/packages/exporter-trace-otlp-grpc/tsconfig.json b/experimental/packages/exporter-trace-otlp-grpc/tsconfig.json index 4a71bbfef6..94d47b6e19 100644 --- a/experimental/packages/exporter-trace-otlp-grpc/tsconfig.json +++ b/experimental/packages/exporter-trace-otlp-grpc/tsconfig.json @@ -18,14 +18,14 @@ { "path": "../../../packages/opentelemetry-sdk-trace-base" }, - { - "path": "../exporter-trace-otlp-http" - }, { "path": "../otlp-exporter-base" }, { "path": "../otlp-grpc-exporter-base" + }, + { + "path": "../otlp-transformer" } ] } diff --git a/experimental/packages/exporter-trace-otlp-http/README.md b/experimental/packages/exporter-trace-otlp-http/README.md index f03ca83ff5..8621e73291 100644 --- a/experimental/packages/exporter-trace-otlp-http/README.md +++ b/experimental/packages/exporter-trace-otlp-http/README.md @@ -3,7 +3,8 @@ [![NPM Published Version][npm-img]][npm-url] [![Apache License][license-image]][license-image] -This module provides exporter for web and node to be used with [opentelemetry-collector][opentelemetry-collector-url] - last tested with version **0.25.0**. +This module provides exporter for web and node to be used with [opentelemetry-collector][opentelemetry-collector-url] +Compatible with [opentelemetry-collector][opentelemetry-collector-url] versions `>=0.48 <=0.50`. ## Installation @@ -86,19 +87,19 @@ For PROTOBUF please check [npm-url-proto] ## Configuration options as environment variables -Instead of providing options to `OTLPMetricExporter` and `OTLPTraceExporter` explicitly, environment variables may be provided instead. +Instead of providing options to `OTLPTraceExporter` explicitly, environment variables may be provided instead. ```sh -OTEL_EXPORTER_OTLP_ENDPOINT=https://localhost:4317 +OTEL_EXPORTER_OTLP_ENDPOINT=https://localhost:4318 # this will automatically append the version and signal path -# e.g. https://localhost:4317/v1/traces for `OTLPTraceExporter` and https://localhost:4317/v1/metrics for `OTLPMetricExporter` +# e.g. https://localhost:4318/v1/traces for `OTLPTraceExporter` and https://localhost:4318/v1/metrics for `OTLPMetricExporter` ``` If the trace and metric exporter endpoints have different providers, the env var for per-signal endpoints are available to use ```sh -OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://trace-service:4317/v1/traces -OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=https://metric-service:4317/v1/metrics +OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://trace-service:4318/v1/traces +OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=https://metric-service:4318/v1/metrics # version and signal needs to be explicit ``` @@ -108,9 +109,8 @@ For more details, see [OpenTelemetry Specification on Protocol Exporter][opentel ## Running opentelemetry-collector locally to see the traces -1. Go to examples/otlp-exporter-node -2. run `npm run docker:start` -3. Open page at `http://localhost:9411/zipkin/` to observe the traces +1. Go to `examples/otlp-exporter-node` +2. Follow the instructions there to inspect traces. ## Useful links diff --git a/experimental/packages/exporter-trace-otlp-http/package.json b/experimental/packages/exporter-trace-otlp-http/package.json index f7528a5349..f973ae4b43 100644 --- a/experimental/packages/exporter-trace-otlp-http/package.json +++ b/experimental/packages/exporter-trace-otlp-http/package.json @@ -97,6 +97,7 @@ "@opentelemetry/core": "1.2.0", "@opentelemetry/resources": "1.2.0", "@opentelemetry/sdk-trace-base": "1.2.0", - "@opentelemetry/otlp-exporter-base": "0.28.0" + "@opentelemetry/otlp-exporter-base": "0.28.0", + "@opentelemetry/otlp-transformer": "0.28.0" } } diff --git a/experimental/packages/exporter-trace-otlp-http/src/index.ts b/experimental/packages/exporter-trace-otlp-http/src/index.ts index d22bb9029d..52ec5f71f5 100644 --- a/experimental/packages/exporter-trace-otlp-http/src/index.ts +++ b/experimental/packages/exporter-trace-otlp-http/src/index.ts @@ -15,5 +15,3 @@ */ export * from './platform'; -export * as otlpTypes from './types'; -export { toCollectorResource, toOTLPExportTraceServiceRequest } from './transform'; diff --git a/experimental/packages/exporter-trace-otlp-http/src/platform/browser/OTLPTraceExporter.ts b/experimental/packages/exporter-trace-otlp-http/src/platform/browser/OTLPTraceExporter.ts index 1f78f66ef6..21c78e8368 100644 --- a/experimental/packages/exporter-trace-otlp-http/src/platform/browser/OTLPTraceExporter.ts +++ b/experimental/packages/exporter-trace-otlp-http/src/platform/browser/OTLPTraceExporter.ts @@ -16,10 +16,9 @@ import { appendResourcePathToUrlIfNotPresent, OTLPExporterBrowserBase } from '@opentelemetry/otlp-exporter-base'; import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'; -import { toOTLPExportTraceServiceRequest } from '../../transform'; -import * as otlpTypes from '../../types'; import { getEnv, baggageUtils } from '@opentelemetry/core'; import { OTLPExporterConfigBase } from '@opentelemetry/otlp-exporter-base'; +import { createExportTraceServiceRequest, IExportTraceServiceRequest } from '@opentelemetry/otlp-transformer'; const DEFAULT_COLLECTOR_RESOURCE_PATH = '/v1/traces'; const DEFAULT_COLLECTOR_URL=`http://localhost:4318${DEFAULT_COLLECTOR_RESOURCE_PATH}`; @@ -30,7 +29,7 @@ const DEFAULT_COLLECTOR_URL=`http://localhost:4318${DEFAULT_COLLECTOR_RESOURCE_P export class OTLPTraceExporter extends OTLPExporterBrowserBase< ReadableSpan, - otlpTypes.opentelemetryProto.collector.trace.v1.ExportTraceServiceRequest + IExportTraceServiceRequest > implements SpanExporter { constructor(config: OTLPExporterConfigBase = {}) { @@ -42,10 +41,8 @@ export class OTLPTraceExporter ) ); } - convert( - spans: ReadableSpan[] - ): otlpTypes.opentelemetryProto.collector.trace.v1.ExportTraceServiceRequest { - return toOTLPExportTraceServiceRequest(spans, this, true); + convert(spans: ReadableSpan[]): IExportTraceServiceRequest { + return createExportTraceServiceRequest(spans, true); } getDefaultUrl(config: OTLPExporterConfigBase): string { diff --git a/experimental/packages/exporter-trace-otlp-http/src/platform/node/OTLPTraceExporter.ts b/experimental/packages/exporter-trace-otlp-http/src/platform/node/OTLPTraceExporter.ts index d4a6e21a93..517148717d 100644 --- a/experimental/packages/exporter-trace-otlp-http/src/platform/node/OTLPTraceExporter.ts +++ b/experimental/packages/exporter-trace-otlp-http/src/platform/node/OTLPTraceExporter.ts @@ -15,14 +15,13 @@ */ import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'; -import * as otlpTypes from '../../types'; -import { toOTLPExportTraceServiceRequest } from '../../transform'; import { getEnv, baggageUtils } from '@opentelemetry/core'; import { OTLPExporterNodeBase } from '@opentelemetry/otlp-exporter-base'; import { OTLPExporterNodeConfigBase, appendResourcePathToUrlIfNotPresent } from '@opentelemetry/otlp-exporter-base'; +import { createExportTraceServiceRequest, IExportTraceServiceRequest } from '@opentelemetry/otlp-transformer'; const DEFAULT_COLLECTOR_RESOURCE_PATH = '/v1/traces'; const DEFAULT_COLLECTOR_URL = `http://localhost:4318${DEFAULT_COLLECTOR_RESOURCE_PATH}`; @@ -32,7 +31,7 @@ const DEFAULT_COLLECTOR_URL = `http://localhost:4318${DEFAULT_COLLECTOR_RESOURCE */ export class OTLPTraceExporter extends OTLPExporterNodeBase + IExportTraceServiceRequest> implements SpanExporter { constructor(config: OTLPExporterNodeConfigBase = {}) { super(config); @@ -44,10 +43,8 @@ export class OTLPTraceExporter ); } - convert( - spans: ReadableSpan[] - ): otlpTypes.opentelemetryProto.collector.trace.v1.ExportTraceServiceRequest { - return toOTLPExportTraceServiceRequest(spans, this, true); + convert(spans: ReadableSpan[]): IExportTraceServiceRequest { + return createExportTraceServiceRequest(spans, true); } getDefaultUrl(config: OTLPExporterNodeConfigBase): string { diff --git a/experimental/packages/exporter-trace-otlp-http/src/transform.ts b/experimental/packages/exporter-trace-otlp-http/src/transform.ts deleted file mode 100644 index 5224c527fa..0000000000 --- a/experimental/packages/exporter-trace-otlp-http/src/transform.ts +++ /dev/null @@ -1,367 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - SpanAttributes, - Link, - SpanKind, - SpanStatus, - TraceState, -} from '@opentelemetry/api'; -import * as core from '@opentelemetry/core'; -import { Resource } from '@opentelemetry/resources'; -import { ReadableSpan, TimedEvent } from '@opentelemetry/sdk-trace-base'; -import { - OTLP_SPAN_KIND_MAPPING, - opentelemetryProto, -} from './types'; -import { OTLPExporterBase, OTLPExporterConfigBase } from '@opentelemetry/otlp-exporter-base'; - -const MAX_INTEGER_VALUE = 2147483647; -const MIN_INTEGER_VALUE = -2147483648; - -/** - * Converts attributes to KeyValue array - * @param attributes - */ -export function toCollectorAttributes( - attributes: SpanAttributes -): opentelemetryProto.common.v1.KeyValue[] { - return Object.keys(attributes).map(key => { - return toCollectorAttributeKeyValue(key, attributes[key]); - }); -} - -/** - * Converts array of unknown value to ArrayValue - * @param values - */ -export function toCollectorArrayValue( - values: unknown[] -): opentelemetryProto.common.v1.ArrayValue { - return { - values: values.map(value => toCollectorAnyValue(value)), - }; -} - -/** - * Converts attributes to KeyValueList - * @param attributes - */ -export function toCollectorKeyValueList( - attributes: SpanAttributes -): opentelemetryProto.common.v1.KeyValueList { - return { - values: toCollectorAttributes(attributes), - }; -} - -/** - * Converts key and unknown value to KeyValue - * @param value event value - */ -export function toCollectorAttributeKeyValue( - key: string, - value: unknown -): opentelemetryProto.common.v1.KeyValue { - const anyValue = toCollectorAnyValue(value); - return { - key, - value: anyValue, - }; -} - -/** - * Converts unknown value to AnyValue - * @param value - */ -export function toCollectorAnyValue( - value: unknown -): opentelemetryProto.common.v1.AnyValue { - const anyValue: opentelemetryProto.common.v1.AnyValue = {}; - if (typeof value === 'string') { - anyValue.stringValue = value; - } else if (typeof value === 'boolean') { - anyValue.boolValue = value; - } else if ( - typeof value === 'number' && - value <= MAX_INTEGER_VALUE && - value >= MIN_INTEGER_VALUE && - Number.isInteger(value) - ) { - anyValue.intValue = value; - } else if (typeof value === 'number') { - anyValue.doubleValue = value; - } else if (Array.isArray(value)) { - anyValue.arrayValue = toCollectorArrayValue(value); - } else if (value) { - anyValue.kvlistValue = toCollectorKeyValueList(value as SpanAttributes); - } - return anyValue; -} - -/** - * - * Converts events - * @param events array of events - */ -export function toCollectorEvents( - timedEvents: TimedEvent[] -): opentelemetryProto.trace.v1.Span.Event[] { - return timedEvents.map(timedEvent => { - const timeUnixNano = core.hrTimeToNanoseconds(timedEvent.time); - const name = timedEvent.name; - const attributes = toCollectorAttributes(timedEvent.attributes || {}); - const droppedAttributesCount = 0; - - const protoEvent: opentelemetryProto.trace.v1.Span.Event = { - timeUnixNano, - name, - attributes, - droppedAttributesCount, - }; - - return protoEvent; - }); -} - -/** - * Converts links - * @param span - * @param useHex - if ids should be kept as hex without converting to base64 - */ -function toCollectorLinks( - span: ReadableSpan, - useHex?: boolean -): opentelemetryProto.trace.v1.Span.Link[] { - return span.links.map((link: Link) => { - const protoLink: opentelemetryProto.trace.v1.Span.Link = { - traceId: useHex - ? link.context.traceId - : core.hexToBase64(link.context.traceId), - spanId: useHex - ? link.context.spanId - : core.hexToBase64(link.context.spanId), - attributes: toCollectorAttributes(link.attributes || {}), - droppedAttributesCount: 0, - }; - return protoLink; - }); -} - -/** - * Converts span - * @param span - * @param useHex - if ids should be kept as hex without converting to base64 - */ -export function toCollectorSpan( - span: ReadableSpan, - useHex?: boolean -): opentelemetryProto.trace.v1.Span { - return { - traceId: useHex - ? span.spanContext().traceId - : core.hexToBase64(span.spanContext().traceId), - spanId: useHex - ? span.spanContext().spanId - : core.hexToBase64(span.spanContext().spanId), - parentSpanId: span.parentSpanId - ? useHex - ? span.parentSpanId - : core.hexToBase64(span.parentSpanId) - : undefined, - traceState: toCollectorTraceState(span.spanContext().traceState), - name: span.name, - kind: toCollectorKind(span.kind), - startTimeUnixNano: core.hrTimeToNanoseconds(span.startTime), - endTimeUnixNano: core.hrTimeToNanoseconds(span.endTime), - attributes: toCollectorAttributes(span.attributes), - droppedAttributesCount: 0, - events: toCollectorEvents(span.events), - droppedEventsCount: 0, - status: toCollectorStatus(span.status), - links: toCollectorLinks(span, useHex), - droppedLinksCount: 0, - }; -} - -/** - * Converts status - * @param status - */ -export function toCollectorStatus( - status: SpanStatus -): opentelemetryProto.trace.v1.SpanStatus { - const spanStatus: opentelemetryProto.trace.v1.SpanStatus = { - code: status.code, - }; - if (typeof status.message !== 'undefined') { - spanStatus.message = status.message; - } - return spanStatus; -} - -/** - * Converts resource - * @param resource - * @param additionalAttributes - */ -export function toCollectorResource( - resource?: Resource, - additionalAttributes: { [key: string]: unknown } = {} -): opentelemetryProto.resource.v1.Resource { - const attr = Object.assign( - {}, - additionalAttributes, - resource ? resource.attributes : {} - ); - const resourceProto: opentelemetryProto.resource.v1.Resource = { - attributes: toCollectorAttributes(attr), - droppedAttributesCount: 0, - }; - - return resourceProto; -} - -/** - * Converts span kind - * @param kind - */ -export function toCollectorKind( - kind: SpanKind -): opentelemetryProto.trace.v1.Span.SpanKind { - const collectorKind = OTLP_SPAN_KIND_MAPPING[kind]; - return typeof collectorKind === 'number' - ? collectorKind - : opentelemetryProto.trace.v1.Span.SpanKind.SPAN_KIND_UNSPECIFIED; -} - -/** - * Converts traceState - * @param traceState - */ -export function toCollectorTraceState( - traceState?: TraceState -): opentelemetryProto.trace.v1.Span.TraceState | undefined { - if (!traceState) return undefined; - return traceState.serialize(); -} - -/** - * Prepares trace service request to be sent to collector - * @param spans spans - * @param collectorExporterBase - * @param useHex - if ids should be kept as hex without converting to base64 - */ -export function toOTLPExportTraceServiceRequest< - T extends OTLPExporterConfigBase ->( - spans: ReadableSpan[], - collectorTraceExporterBase: OTLPExporterBase< - T, - ReadableSpan, - opentelemetryProto.collector.trace.v1.ExportTraceServiceRequest - >, - useHex?: boolean -): opentelemetryProto.collector.trace.v1.ExportTraceServiceRequest { - const groupedSpans: Map< - Resource, - Map - > = groupSpansByResourceAndLibrary(spans); - - const additionalAttributes = Object.assign( - {}, - collectorTraceExporterBase.attributes - ); - - return { - resourceSpans: toCollectorResourceSpans( - groupedSpans, - additionalAttributes, - useHex - ), - }; -} - -/** - * 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>()); -} - -/** - * Convert to InstrumentationLibrarySpans - * @param instrumentationLibrary - * @param spans - * @param useHex - if ids should be kept as hex without converting to base64 - */ -function toCollectorInstrumentationLibrarySpans( - instrumentationLibrary: core.InstrumentationLibrary, - spans: ReadableSpan[], - useHex?: boolean -): opentelemetryProto.trace.v1.InstrumentationLibrarySpans { - return { - spans: spans.map(span => toCollectorSpan(span, useHex)), - instrumentationLibrary, - }; -} - -/** - * Returns a list of resource spans which will be exported to the collector - * @param groupedSpans - * @param baseAttributes - * @param useHex - if ids should be kept as hex without converting to base64 - */ -function toCollectorResourceSpans( - groupedSpans: Map>, - baseAttributes: SpanAttributes, - useHex?: boolean -): opentelemetryProto.trace.v1.ResourceSpans[] { - return Array.from(groupedSpans, ([resource, libSpans]) => { - return { - resource: toCollectorResource(resource, baseAttributes), - instrumentationLibrarySpans: Array.from( - libSpans, - ([instrumentationLibrary, spans]) => - toCollectorInstrumentationLibrarySpans( - instrumentationLibrary, - spans, - useHex - ) - ), - }; - }); -} diff --git a/experimental/packages/exporter-trace-otlp-http/src/types.ts b/experimental/packages/exporter-trace-otlp-http/src/types.ts deleted file mode 100644 index 02be0ed2e4..0000000000 --- a/experimental/packages/exporter-trace-otlp-http/src/types.ts +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { SpanKind, SpanStatusCode } from '@opentelemetry/api'; - -/* eslint-disable @typescript-eslint/no-namespace */ -/* eslint-disable @typescript-eslint/no-unused-vars */ - -export namespace opentelemetryProto { - export namespace collector { - export namespace trace.v1 { - export interface TraceService { - service: opentelemetryProto.collector.trace.v1.TraceService; - } - - export interface ExportTraceServiceRequest { - resourceSpans: opentelemetryProto.trace.v1.ResourceSpans[]; - } - } - export namespace metrics.v1 { - export interface ExportMetricsServiceRequest { - resourceMetrics: opentelemetryProto.metrics.v1.ResourceMetrics[]; - } - } - } - - export namespace resource.v1 { - export interface Resource { - attributes: opentelemetryProto.common.v1.KeyValue[]; - droppedAttributesCount: number; - } - } - - export namespace metrics.v1 { - export interface Metric { - name: string; - description: string; - unit: string; - // data: - intGauge?: opentelemetryProto.metrics.v1.Gauge; - doubleGauge?: opentelemetryProto.metrics.v1.Gauge; - intSum?: opentelemetryProto.metrics.v1.Sum; - doubleSum?: opentelemetryProto.metrics.v1.Sum; - intHistogram?: opentelemetryProto.metrics.v1.Histogram; - doubleHistogram?: opentelemetryProto.metrics.v1.Histogram; - } - - export interface Gauge { - dataPoints: opentelemetryProto.metrics.v1.DataPoint[]; - } - - export interface Sum { - dataPoints: opentelemetryProto.metrics.v1.DataPoint[]; - aggregationTemporality: opentelemetryProto.metrics.v1.AggregationTemporality; - isMonotonic: boolean; - } - - export interface Histogram { - dataPoints: opentelemetryProto.metrics.v1.HistogramDataPoint[]; - aggregationTemporality: opentelemetryProto.metrics.v1.AggregationTemporality; - } - - export interface DataPoint { - labels: opentelemetryProto.common.v1.StringKeyValue[]; - startTimeUnixNano: number; - timeUnixNano: number; - value: number; - exemplars?: opentelemetryProto.metrics.v1.Exemplar[]; - } - - export interface Exemplar { - filteredLabels: opentelemetryProto.common.v1.StringKeyValue[]; - timeUnixNano: number; - value: number; - spanId: Uint8Array; - traceId: Uint8Array; - } - - export interface HistogramDataPoint { - labels: opentelemetryProto.common.v1.StringKeyValue[]; - startTimeUnixNano: number; - timeUnixNano: number; - count: number; - sum: number; - bucketCounts?: number[]; - explicitBounds?: number[]; - exemplars?: opentelemetryProto.metrics.v1.Exemplar[][]; - } - - export interface InstrumentationLibraryMetrics { - instrumentationLibrary?: opentelemetryProto.common.v1.InstrumentationLibrary; - metrics: opentelemetryProto.metrics.v1.Metric[]; - } - - export interface ResourceMetrics { - resource?: opentelemetryProto.resource.v1.Resource; - instrumentationLibraryMetrics: opentelemetryProto.metrics.v1.InstrumentationLibraryMetrics[]; - } - - export enum AggregationTemporality { - // UNSPECIFIED is the default AggregationTemporality, it MUST not be used. - AGGREGATION_TEMPORALITY_UNSPECIFIED = 0, - - // DELTA is an AggregationTemporality for a metric aggregator which reports - // changes since last report time. Successive metrics contain aggregation of - // values from continuous and non-overlapping intervals. - // - // The values for a DELTA metric are based only on the time interval - // associated with one measurement cycle. There is no dependency on - // previous measurements like is the case for CUMULATIVE metrics. - // - // For example, consider a system measuring the number of requests that - // it receives and reports the sum of these requests every second as a - // DELTA metric: - // - // 1. The system starts receiving at time=t_0. - // 2. A request is received, the system measures 1 request. - // 3. A request is received, the system measures 1 request. - // 4. A request is received, the system measures 1 request. - // 5. The 1 second collection cycle ends. A metric is exported for the - // number of requests received over the interval of time t_0 to - // t_0+1 with a value of 3. - // 6. A request is received, the system measures 1 request. - // 7. A request is received, the system measures 1 request. - // 8. The 1 second collection cycle ends. A metric is exported for the - // number of requests received over the interval of time t_0+1 to - // t_0+2 with a value of 2. - AGGREGATION_TEMPORALITY_DELTA = 1, - - // CUMULATIVE is an AggregationTemporality for a metric aggregator which - // reports changes since a fixed start time. This means that current values - // of a CUMULATIVE metric depend on all previous measurements since the - // start time. Because of this, the sender is required to retain this state - // in some form. If this state is lost or invalidated, the CUMULATIVE metric - // values MUST be reset and a new fixed start time following the last - // reported measurement time sent MUST be used. - // - // For example, consider a system measuring the number of requests that - // it receives and reports the sum of these requests every second as a - // CUMULATIVE metric: - // - // 1. The system starts receiving at time=t_0. - // 2. A request is received, the system measures 1 request. - // 3. A request is received, the system measures 1 request. - // 4. A request is received, the system measures 1 request. - // 5. The 1 second collection cycle ends. A metric is exported for the - // number of requests received over the interval of time t_0 to - // t_0+1 with a value of 3. - // 6. A request is received, the system measures 1 request. - // 7. A request is received, the system measures 1 request. - // 8. The 1 second collection cycle ends. A metric is exported for the - // number of requests received over the interval of time t_0 to - // t_0+2 with a value of 5. - // 9. The system experiences a fault and loses state. - // 10. The system recovers and resumes receiving at time=t_1. - // 11. A request is received, the system measures 1 request. - // 12. The 1 second collection cycle ends. A metric is exported for the - // number of requests received over the interval of time t_1 to - // t_0+1 with a value of 1. - // - // Note: Even though, when reporting changes since last report time, using - // CUMULATIVE is valid, it is not recommended. This may cause problems for - // systems that do not use start_time to determine when the aggregation - // value was reset (e.g. Prometheus). - AGGREGATION_TEMPORALITY_CUMULATIVE = 2, - } - } - - export namespace trace.v1 { - export namespace ConstantSampler { - export enum ConstantDecision { - ALWAYS_OFF = 0, - ALWAYS_ON = 1, - ALWAYS_PARENT = 2, - } - } - export namespace Span { - export interface Event { - timeUnixNano: number; - name: string; - attributes?: opentelemetryProto.common.v1.KeyValue[]; - droppedAttributesCount: number; - } - - export interface Link { - traceId: string; - spanId: string; - traceState?: opentelemetryProto.trace.v1.Span.TraceState; - attributes?: opentelemetryProto.common.v1.KeyValue[]; - droppedAttributesCount: number; - } - - // eslint-disable-next-line @typescript-eslint/no-shadow - export enum SpanKind { - SPAN_KIND_UNSPECIFIED, - SPAN_KIND_INTERNAL, - SPAN_KIND_SERVER, - SPAN_KIND_CLIENT, - SPAN_KIND_PRODUCER, - SPAN_KIND_CONSUMER, - } - - export type TraceState = string | undefined; - } - - export interface ConstantSampler { - decision?: opentelemetryProto.trace.v1.ConstantSampler.ConstantDecision; - } - - export interface InstrumentationLibrarySpans { - instrumentationLibrary?: opentelemetryProto.common.v1.InstrumentationLibrary; - spans: opentelemetryProto.trace.v1.Span[]; - } - - export interface ProbabilitySampler { - samplingProbability?: number | null; - } - - export interface RateLimitingSampler { - qps?: number | null; - } - - export interface ResourceSpans { - resource?: opentelemetryProto.resource.v1.Resource; - instrumentationLibrarySpans: opentelemetryProto.trace.v1.InstrumentationLibrarySpans[]; - } - - export interface Span { - traceId: string; - spanId: string; - traceState: opentelemetryProto.trace.v1.Span.TraceState; - parentSpanId?: string; - name?: string; - kind?: opentelemetryProto.trace.v1.Span.SpanKind; - startTimeUnixNano?: number; - endTimeUnixNano?: number; - attributes?: opentelemetryProto.common.v1.KeyValue[]; - droppedAttributesCount: number; - events?: opentelemetryProto.trace.v1.Span.Event[]; - droppedEventsCount: number; - links?: opentelemetryProto.trace.v1.Span.Link[]; - droppedLinksCount: number; - status?: SpanStatus; - } - - export interface SpanStatus { - /** The status code of this message. */ - code: SpanStatusCode; - /** A developer-facing error message. */ - message?: string; - } - - export interface TraceConfig { - constantSampler?: ConstantSampler | null; - probabilitySampler?: ProbabilitySampler | null; - rateLimitingSampler?: RateLimitingSampler | null; - } - } - export namespace common.v1 { - export interface KeyValue { - key: string; - value: AnyValue; - } - - export type ArrayValue = { - values: AnyValue[]; - }; - - export interface KeyValueList { - values: KeyValue[]; - } - - export type AnyValue = { - stringValue?: string; - boolValue?: boolean; - intValue?: number; - doubleValue?: number; - arrayValue?: ArrayValue; - kvlistValue?: KeyValueList; - }; - - export interface InstrumentationLibrary { - name: string; - version?: string; - } - - export interface StringKeyValue { - key: string; - value: string; - } - - export enum ValueType { - STRING, - INT, - DOUBLE, - BOOL, - } - } -} - -/** - * Mapping between api SpanKind and proto SpanKind - */ -export const OTLP_SPAN_KIND_MAPPING = { - [SpanKind.INTERNAL]: - opentelemetryProto.trace.v1.Span.SpanKind.SPAN_KIND_INTERNAL, - [SpanKind.SERVER]: opentelemetryProto.trace.v1.Span.SpanKind.SPAN_KIND_SERVER, - [SpanKind.CLIENT]: opentelemetryProto.trace.v1.Span.SpanKind.SPAN_KIND_CLIENT, - [SpanKind.PRODUCER]: - opentelemetryProto.trace.v1.Span.SpanKind.SPAN_KIND_PRODUCER, - [SpanKind.CONSUMER]: - opentelemetryProto.trace.v1.Span.SpanKind.SPAN_KIND_CONSUMER, -}; diff --git a/experimental/packages/exporter-trace-otlp-http/test/browser/CollectorTraceExporter.test.ts b/experimental/packages/exporter-trace-otlp-http/test/browser/CollectorTraceExporter.test.ts index 1f57a5e959..b6835b0151 100644 --- a/experimental/packages/exporter-trace-otlp-http/test/browser/CollectorTraceExporter.test.ts +++ b/experimental/packages/exporter-trace-otlp-http/test/browser/CollectorTraceExporter.test.ts @@ -20,7 +20,6 @@ import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; import * as assert from 'assert'; import * as sinon from 'sinon'; import { OTLPTraceExporter } from '../../src/platform/browser/index'; -import * as otlpTypes from '../../src/types'; import { ensureSpanIsCorrect, @@ -30,6 +29,7 @@ import { mockedReadableSpan, } from '../traceHelper'; import { OTLPExporterConfigBase } from '@opentelemetry/otlp-exporter-base'; +import { IExportTraceServiceRequest } from '@opentelemetry/otlp-transformer'; describe('OTLPTraceExporter - web', () => { let collectorTraceExporter: OTLPTraceExporter; @@ -103,35 +103,34 @@ describe('OTLPTraceExporter - web', () => { }); setTimeout(async () => { - const args = stubBeacon.args[0]; - const url = args[0]; - const blob: Blob = args[1]; - const body = await blob.text(); - const json = JSON.parse( - body - ) as otlpTypes.opentelemetryProto.collector.trace.v1.ExportTraceServiceRequest; - const span1 = - json.resourceSpans[0].instrumentationLibrarySpans[0].spans[0]; - - assert.ok(typeof span1 !== 'undefined', "span doesn't exist"); - if (span1) { + try { + const args = stubBeacon.args[0]; + const url = args[0]; + const blob: Blob = args[1]; + const body = await blob.text(); + const json = JSON.parse( + body + ) as IExportTraceServiceRequest; + const span1 = + json.resourceSpans?.[0].scopeSpans?.[0].spans?.[0]; + + assert.ok(typeof span1 !== 'undefined', "span doesn't exist"); ensureSpanIsCorrect(span1); - } - const resource = json.resourceSpans[0].resource; - assert.ok(typeof resource !== 'undefined', "resource doesn't exist"); - if (resource) { + const resource = json.resourceSpans?.[0].resource; + assert.ok(typeof resource !== 'undefined', "resource doesn't exist"); ensureWebResourceIsCorrect(resource); - } - assert.strictEqual(url, 'http://foo.bar.com'); - assert.strictEqual(stubBeacon.callCount, 1); + assert.strictEqual(url, 'http://foo.bar.com'); + assert.strictEqual(stubBeacon.callCount, 1); - assert.strictEqual(stubOpen.callCount, 0); + assert.strictEqual(stubOpen.callCount, 0); - ensureExportTraceServiceRequestIsSet(json); - - done(); + ensureExportTraceServiceRequestIsSet(json); + done(); + } catch (err) { + done(err); + } }); }); @@ -200,20 +199,16 @@ describe('OTLPTraceExporter - web', () => { const body = request.requestBody; const json = JSON.parse( body - ) as otlpTypes.opentelemetryProto.collector.trace.v1.ExportTraceServiceRequest; + ) as IExportTraceServiceRequest; const span1 = - json.resourceSpans[0].instrumentationLibrarySpans[0].spans[0]; + json.resourceSpans?.[0].scopeSpans?.[0].spans?.[0]; assert.ok(typeof span1 !== 'undefined', "span doesn't exist"); - if (span1) { - ensureSpanIsCorrect(span1); - } + ensureSpanIsCorrect(span1); - const resource = json.resourceSpans[0].resource; + const resource = json.resourceSpans?.[0].resource; assert.ok(typeof resource !== 'undefined', "resource doesn't exist"); - if (resource) { - ensureWebResourceIsCorrect(resource); - } + ensureWebResourceIsCorrect(resource); assert.strictEqual(stubBeacon.callCount, 0); diff --git a/experimental/packages/exporter-trace-otlp-http/test/browser/index-webpack.ts b/experimental/packages/exporter-trace-otlp-http/test/browser/index-webpack.ts index 99100a0f6e..ae7d4b5a9d 100644 --- a/experimental/packages/exporter-trace-otlp-http/test/browser/index-webpack.ts +++ b/experimental/packages/exporter-trace-otlp-http/test/browser/index-webpack.ts @@ -16,8 +16,5 @@ const testsContext = require.context('../browser', true, /test$/); testsContext.keys().forEach(testsContext); -const testsContextCommon = require.context('../common', true, /test$/); -testsContextCommon.keys().forEach(testsContextCommon); - const srcContext = require.context('.', true, /src$/); srcContext.keys().forEach(srcContext); diff --git a/experimental/packages/exporter-trace-otlp-http/test/common/transform.test.ts b/experimental/packages/exporter-trace-otlp-http/test/common/transform.test.ts deleted file mode 100644 index 94932201b7..0000000000 --- a/experimental/packages/exporter-trace-otlp-http/test/common/transform.test.ts +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { SpanAttributes, SpanStatusCode } from '@opentelemetry/api'; -import { TimedEvent } from '@opentelemetry/sdk-trace-base'; -import * as assert from 'assert'; -import * as transform from '../../src/transform'; -import { - ensureSpanIsCorrect, - mockedReadableSpan, - mockedResources, - mockedInstrumentationLibraries, - multiResourceTrace, - multiInstrumentationLibraryTrace, -} from '../traceHelper'; -import { Resource } from '@opentelemetry/resources'; - -describe('transform', () => { - describe('toCollectorAttributes', () => { - it('should convert attribute string', () => { - const attributes: SpanAttributes = { - foo: 'bar', - }; - assert.deepStrictEqual(transform.toCollectorAttributes(attributes), [ - { key: 'foo', value: { stringValue: 'bar' } }, - ]); - }); - - it('should convert attribute integer to integer', () => { - const attributes: SpanAttributes = { - foo: 13, - }; - assert.deepStrictEqual(transform.toCollectorAttributes(attributes), [ - { key: 'foo', value: { intValue: 13 } }, - ]); - }); - - it('should convert attribute integer to double', () => { - const attributes: SpanAttributes = { - foo: 2247483647, - }; - assert.deepStrictEqual(transform.toCollectorAttributes(attributes), [ - { key: 'foo', value: { doubleValue: 2247483647 } }, - ]); - }); - - it('should convert attribute boolean', () => { - const attributes: SpanAttributes = { - foo: true, - }; - assert.deepStrictEqual(transform.toCollectorAttributes(attributes), [ - { key: 'foo', value: { boolValue: true } }, - ]); - }); - - it('should convert attribute double', () => { - const attributes: SpanAttributes = { - foo: 1.34, - }; - assert.deepStrictEqual(transform.toCollectorAttributes(attributes), [ - { key: 'foo', value: { doubleValue: 1.34 } }, - ]); - }); - }); - - describe('toCollectorEvents', () => { - it('should convert events to otc events', () => { - const events: TimedEvent[] = [ - { name: 'foo', time: [123, 123], attributes: { a: 'b' } }, - { - name: 'foo2', - time: [321, 321], - attributes: { c: 'd' }, - }, - ]; - assert.deepStrictEqual(transform.toCollectorEvents(events), [ - { - timeUnixNano: 123000000123, - name: 'foo', - attributes: [{ key: 'a', value: { stringValue: 'b' } }], - droppedAttributesCount: 0, - }, - { - timeUnixNano: 321000000321, - name: 'foo2', - attributes: [{ key: 'c', value: { stringValue: 'd' } }], - droppedAttributesCount: 0, - }, - ]); - }); - }); - - describe('toCollectorAnyValue', () => { - it('should use correct type on array', () => { - assert.deepStrictEqual(transform.toCollectorAnyValue(['string', true, 1]), { - arrayValue: { - values: - [ - { stringValue: 'string' }, - { boolValue: true }, - { intValue: 1 } - ] - } - }); - }); - - it('should use correct type on kvlist', () => { - assert.deepStrictEqual(transform.toCollectorAnyValue({ string: 'string', boolean: true, integer: 1 }), { - kvlistValue: { - values: - [ - { key: 'string', value: { stringValue: 'string' } }, - { key: 'boolean', value: { boolValue: true } }, - { key: 'integer', value: { intValue: 1 } } - ] - } - }); - }); - }); - - describe('toCollectorStatus', () => { - it('should set message if status is not undefined', () => { - const result = transform.toCollectorStatus({ - code: SpanStatusCode.OK, - message: 'message' - }); - assert.deepStrictEqual(result.message, 'message'); - }); - }); - - describe('toCollectorSpan', () => { - it('should convert span using hex', () => { - ensureSpanIsCorrect(transform.toCollectorSpan(mockedReadableSpan, true)); - }); - it('should convert span using base64', () => { - ensureSpanIsCorrect(transform.toCollectorSpan(mockedReadableSpan), false); - }); - }); - - describe('toCollectorResource', () => { - it('should convert resource', () => { - const resource = transform.toCollectorResource( - new Resource({ - service: 'ui', - version: 1.0, - success: true, - }) - ); - assert.deepStrictEqual(resource, { - attributes: [ - { - key: 'service', - value: { stringValue: 'ui' }, - }, - { - key: 'version', - value: { intValue: 1 }, - }, - { key: 'success', value: { boolValue: true } }, - ], - droppedAttributesCount: 0, - }); - }); - }); - 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/experimental/packages/exporter-trace-otlp-http/test/node/CollectorTraceExporter.test.ts b/experimental/packages/exporter-trace-otlp-http/test/node/CollectorTraceExporter.test.ts index 6ddd63a0fc..bfe9d4ccb4 100644 --- a/experimental/packages/exporter-trace-otlp-http/test/node/CollectorTraceExporter.test.ts +++ b/experimental/packages/exporter-trace-otlp-http/test/node/CollectorTraceExporter.test.ts @@ -30,13 +30,13 @@ import * as zlib from 'zlib'; import { OTLPTraceExporter } from '../../src/platform/node'; -import * as otlpTypes from '../../src/types'; import { ensureExportTraceServiceRequestIsSet, ensureSpanIsCorrect, mockedReadableSpan } from '../traceHelper'; import { MockedResponse } from './nodeHelpers'; +import { IExportTraceServiceRequest } from '@opentelemetry/otlp-transformer'; let fakeRequest: PassThrough; @@ -227,15 +227,10 @@ describe('OTLPTraceExporter - node with json over http', () => { fakeRequest.on('end', () => { const responseBody = buff.toString(); - const json = JSON.parse( - responseBody - ) as otlpTypes.opentelemetryProto.collector.trace.v1.ExportTraceServiceRequest; - const span1 = - json.resourceSpans[0].instrumentationLibrarySpans[0].spans[0]; + const json = JSON.parse(responseBody) as IExportTraceServiceRequest; + const span1 = json.resourceSpans?.[0].scopeSpans?.[0].spans?.[0]; assert.ok(typeof span1 !== 'undefined', "span doesn't exist"); - if (span1) { - ensureSpanIsCorrect(span1); - } + ensureSpanIsCorrect(span1); ensureExportTraceServiceRequestIsSet(json); @@ -323,15 +318,10 @@ describe('OTLPTraceExporter - node with json over http', () => { fakeRequest.on('end', () => { const responseBody = zlib.gunzipSync(buff).toString(); - const json = JSON.parse( - responseBody - ) as otlpTypes.opentelemetryProto.collector.trace.v1.ExportTraceServiceRequest; - const span1 = - json.resourceSpans[0].instrumentationLibrarySpans[0].spans[0]; + const json = JSON.parse(responseBody) as IExportTraceServiceRequest; + const span1 = json.resourceSpans?.[0].scopeSpans?.[0].spans?.[0]; assert.ok(typeof span1 !== 'undefined', "span doesn't exist"); - if (span1) { - ensureSpanIsCorrect(span1); - } + ensureSpanIsCorrect(span1); ensureExportTraceServiceRequestIsSet(json); assert.ok(spySetHeader.calledWith('Content-Encoding', 'gzip')); diff --git a/experimental/packages/exporter-trace-otlp-http/test/traceHelper.ts b/experimental/packages/exporter-trace-otlp-http/test/traceHelper.ts index 1ad9e891a7..e5415fcf6a 100644 --- a/experimental/packages/exporter-trace-otlp-http/test/traceHelper.ts +++ b/experimental/packages/exporter-trace-otlp-http/test/traceHelper.ts @@ -19,8 +19,15 @@ import { hexToBase64, InstrumentationLibrary, VERSION } from '@opentelemetry/cor import { Resource } from '@opentelemetry/resources'; import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; import * as assert from 'assert'; -import * as otlpTypes from '../src/types'; -import { opentelemetryProto } from '../src/types'; +import { + ESpanKind, + IEvent, + IExportTraceServiceRequest, + IKeyValue, + ILink, + IResource, + ISpan +} from '@opentelemetry/otlp-transformer'; if (typeof Buffer === 'undefined') { (window as any).Buffer = { @@ -208,7 +215,7 @@ export const multiInstrumentationLibraryTrace: ReadableSpan[] = [ ]; export function ensureEventsAreCorrect( - events: opentelemetryProto.trace.v1.Span.Event[] + events: IEvent[] ) { assert.deepStrictEqual( events, @@ -267,7 +274,7 @@ export function ensureEventsAreCorrect( } export function ensureAttributesAreCorrect( - attributes: opentelemetryProto.common.v1.KeyValue[] + attributes: IKeyValue[] ) { assert.deepStrictEqual( attributes, @@ -284,7 +291,7 @@ export function ensureAttributesAreCorrect( } export function ensureLinksAreCorrect( - attributes: opentelemetryProto.trace.v1.Span.Link[], + attributes: ILink[], useHex?: boolean ) { assert.deepStrictEqual( @@ -309,7 +316,7 @@ export function ensureLinksAreCorrect( } export function ensureSpanIsCorrect( - span: otlpTypes.opentelemetryProto.trace.v1.Span, + span: ISpan, useHex = true ) { if (span.attributes) { @@ -339,7 +346,7 @@ export function ensureSpanIsCorrect( assert.strictEqual(span.name, 'documentFetch', 'name is wrong'); assert.strictEqual( span.kind, - opentelemetryProto.trace.v1.Span.SpanKind.SPAN_KIND_INTERNAL, + ESpanKind.SPAN_KIND_INTERNAL, 'kind is wrong' ); assert.strictEqual( @@ -367,7 +374,7 @@ export function ensureSpanIsCorrect( } export function ensureWebResourceIsCorrect( - resource: otlpTypes.opentelemetryProto.resource.v1.Resource + resource: IResource ) { assert.strictEqual(resource.attributes.length, 7); assert.strictEqual(resource.attributes[0].key, 'service.name'); @@ -388,36 +395,22 @@ export function ensureWebResourceIsCorrect( } export function ensureExportTraceServiceRequestIsSet( - json: otlpTypes.opentelemetryProto.collector.trace.v1.ExportTraceServiceRequest + json: IExportTraceServiceRequest ) { const resourceSpans = json.resourceSpans; - assert.strictEqual( - resourceSpans && resourceSpans.length, - 1, - 'resourceSpans is missing' - ); + assert.strictEqual(resourceSpans?.length, 1, 'resourceSpans is missing'); - const resource = resourceSpans[0].resource; - assert.strictEqual(!!resource, true, 'resource is missing'); + const resource = resourceSpans?.[0].resource; + assert.ok(resource, 'resource is missing'); - const instrumentationLibrarySpans = - resourceSpans[0].instrumentationLibrarySpans; - assert.strictEqual( - instrumentationLibrarySpans && instrumentationLibrarySpans.length, - 1, - 'instrumentationLibrarySpans is missing' - ); + const scopeSpans = resourceSpans?.[0].scopeSpans; + assert.strictEqual(scopeSpans?.length, 1, 'scopeSpans is missing'); - const instrumentationLibrary = - instrumentationLibrarySpans[0].instrumentationLibrary; - assert.strictEqual( - !!instrumentationLibrary, - true, - 'instrumentationLibrary is missing' - ); + const scope = scopeSpans?.[0].scope; + assert.ok(scope, 'scope is missing'); - const spans = instrumentationLibrarySpans[0].spans; - assert.strictEqual(spans && spans.length, 1, 'spans are missing'); + const spans = scopeSpans?.[0].spans; + assert.strictEqual(spans?.length, 1, 'spans are missing'); } export function ensureHeadersContain( diff --git a/experimental/packages/exporter-trace-otlp-http/tsconfig.json b/experimental/packages/exporter-trace-otlp-http/tsconfig.json index 82df98e67b..087c804079 100644 --- a/experimental/packages/exporter-trace-otlp-http/tsconfig.json +++ b/experimental/packages/exporter-trace-otlp-http/tsconfig.json @@ -20,6 +20,9 @@ }, { "path": "../otlp-exporter-base" + }, + { + "path": "../otlp-transformer" } ] } diff --git a/experimental/packages/exporter-trace-otlp-proto/README.md b/experimental/packages/exporter-trace-otlp-proto/README.md index 19e91f0f46..5e5d2ed6e8 100644 --- a/experimental/packages/exporter-trace-otlp-proto/README.md +++ b/experimental/packages/exporter-trace-otlp-proto/README.md @@ -3,7 +3,8 @@ [![NPM Published Version][npm-img]][npm-url] [![Apache License][license-image]][license-image] -This module provides exporter for node to be used with [opentelemetry-collector][opentelemetry-collector-url] - last tested with version **0.25.0**. +This module provides exporter for node to be used with [opentelemetry-collector][opentelemetry-collector-url]. +Compatible with [opentelemetry-collector][opentelemetry-collector-url] versions `>=0.32 <=0.50`. ## Installation diff --git a/experimental/packages/exporter-trace-otlp-proto/package.json b/experimental/packages/exporter-trace-otlp-proto/package.json index 7367f0efd5..c0a652e869 100644 --- a/experimental/packages/exporter-trace-otlp-proto/package.json +++ b/experimental/packages/exporter-trace-otlp-proto/package.json @@ -68,11 +68,11 @@ "dependencies": { "@opentelemetry/core": "1.1.1", "@grpc/proto-loader": "^0.6.9", - "@opentelemetry/exporter-trace-otlp-http": "0.28.0", "@opentelemetry/resources": "1.2.0", "@opentelemetry/sdk-trace-base": "1.2.0", "@opentelemetry/otlp-exporter-base": "0.28.0", "@opentelemetry/otlp-proto-exporter-base": "0.28.0", - "protobufjs": "^6.9.0" + "protobufjs": "^6.9.0", + "@opentelemetry/otlp-transformer": "0.28.0" } } diff --git a/experimental/packages/exporter-trace-otlp-proto/src/OTLPTraceExporter.ts b/experimental/packages/exporter-trace-otlp-proto/src/OTLPTraceExporter.ts index e2e98de304..9fcc7f5a3f 100644 --- a/experimental/packages/exporter-trace-otlp-proto/src/OTLPTraceExporter.ts +++ b/experimental/packages/exporter-trace-otlp-proto/src/OTLPTraceExporter.ts @@ -15,13 +15,10 @@ */ import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'; -import { - otlpTypes, - toOTLPExportTraceServiceRequest -} from '@opentelemetry/exporter-trace-otlp-http'; import { getEnv, baggageUtils } from '@opentelemetry/core'; import { appendResourcePathToUrlIfNotPresent, OTLPExporterNodeConfigBase } from '@opentelemetry/otlp-exporter-base'; import { OTLPProtoExporterNodeBase, ServiceClientType } from '@opentelemetry/otlp-proto-exporter-base'; +import { createExportTraceServiceRequest, IExportTraceServiceRequest } from '@opentelemetry/otlp-transformer'; const DEFAULT_COLLECTOR_RESOURCE_PATH = '/v1/traces'; const DEFAULT_COLLECTOR_URL=`http://localhost:4318${DEFAULT_COLLECTOR_RESOURCE_PATH}`; @@ -32,7 +29,7 @@ const DEFAULT_COLLECTOR_URL=`http://localhost:4318${DEFAULT_COLLECTOR_RESOURCE_P export class OTLPTraceExporter extends OTLPProtoExporterNodeBase< ReadableSpan, - otlpTypes.opentelemetryProto.collector.trace.v1.ExportTraceServiceRequest + IExportTraceServiceRequest > implements SpanExporter { constructor(config: OTLPExporterNodeConfigBase = {}) { @@ -45,10 +42,8 @@ export class OTLPTraceExporter ); } - convert( - spans: ReadableSpan[] - ): otlpTypes.opentelemetryProto.collector.trace.v1.ExportTraceServiceRequest { - return toOTLPExportTraceServiceRequest(spans, this); + convert(spans: ReadableSpan[]): IExportTraceServiceRequest { + return createExportTraceServiceRequest(spans); } getDefaultUrl(config: OTLPExporterNodeConfigBase) { diff --git a/experimental/packages/exporter-trace-otlp-proto/test/OTLPTraceExporter.test.ts b/experimental/packages/exporter-trace-otlp-proto/test/OTLPTraceExporter.test.ts index 9554fea1d8..56f6aba69c 100644 --- a/experimental/packages/exporter-trace-otlp-proto/test/OTLPTraceExporter.test.ts +++ b/experimental/packages/exporter-trace-otlp-proto/test/OTLPTraceExporter.test.ts @@ -16,9 +16,6 @@ import { diag } from '@opentelemetry/api'; import { ExportResultCode } from '@opentelemetry/core'; -import { - otlpTypes -} from '@opentelemetry/exporter-trace-otlp-http'; import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; import * as assert from 'assert'; import * as http from 'http'; @@ -34,6 +31,7 @@ import { } from './traceHelper'; import { CompressionAlgorithm, OTLPExporterNodeConfigBase } from '@opentelemetry/otlp-exporter-base'; import { getExportRequestProto } from '@opentelemetry/otlp-proto-exporter-base'; +import { IExportTraceServiceRequest } from '@opentelemetry/otlp-transformer'; const fakeRequest = { end: function () { }, @@ -155,13 +153,10 @@ describe('OTLPTraceExporter - node with proto over http', () => { fakeRequest.on('end', () => { const ExportTraceServiceRequestProto = getExportRequestProto(); const data = ExportTraceServiceRequestProto?.decode(buff); - const json = data?.toJSON() as otlpTypes.opentelemetryProto.collector.trace.v1.ExportTraceServiceRequest; - const span1 = - json.resourceSpans[0].instrumentationLibrarySpans[0].spans[0]; + const json = data?.toJSON() as IExportTraceServiceRequest; + const span1 = json.resourceSpans?.[0].scopeSpans?.[0].spans?.[0]; assert.ok(typeof span1 !== 'undefined', "span doesn't exist"); - if (span1) { - ensureProtoSpanIsCorrect(span1); - } + ensureProtoSpanIsCorrect(span1); ensureExportTraceServiceRequestIsSet(json); @@ -242,13 +237,10 @@ describe('OTLPTraceExporter - node with proto over http', () => { const unzippedBuff = zlib.gunzipSync(buff); const ExportTraceServiceRequestProto = getExportRequestProto(); const data = ExportTraceServiceRequestProto?.decode(unzippedBuff); - const json = data?.toJSON() as otlpTypes.opentelemetryProto.collector.trace.v1.ExportTraceServiceRequest; - const span1 = - json.resourceSpans[0].instrumentationLibrarySpans[0].spans[0]; + const json = data?.toJSON() as IExportTraceServiceRequest; + const span1 = json.resourceSpans?.[0].scopeSpans?.[0].spans?.[0]; assert.ok(typeof span1 !== 'undefined', "span doesn't exist"); - if (span1) { - ensureProtoSpanIsCorrect(span1); - } + ensureProtoSpanIsCorrect(span1); ensureExportTraceServiceRequestIsSet(json); assert.ok(spySetHeader.calledWith('Content-Encoding', 'gzip')); diff --git a/experimental/packages/exporter-trace-otlp-proto/test/traceHelper.ts b/experimental/packages/exporter-trace-otlp-proto/test/traceHelper.ts index 86ff5dc32c..10a0765458 100644 --- a/experimental/packages/exporter-trace-otlp-proto/test/traceHelper.ts +++ b/experimental/packages/exporter-trace-otlp-proto/test/traceHelper.ts @@ -16,11 +16,11 @@ import { SpanStatusCode, TraceFlags } from '@opentelemetry/api'; import { hexToBase64 } from '@opentelemetry/core'; -import { otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; import { Resource } from '@opentelemetry/resources'; import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; import * as assert from 'assert'; import { Stream } from 'stream'; +import { IEvent, IExportTraceServiceRequest, IKeyValue, ILink, ISpan } from '@opentelemetry/otlp-transformer'; const traceIdHex = '1f1008dc8e270e85c40a0d7c3939b278'; const spanIdHex = '5e107261f64fa53e'; @@ -84,7 +84,7 @@ export const mockedReadableSpan: ReadableSpan = { }; export function ensureProtoEventsAreCorrect( - events: otlpTypes.opentelemetryProto.trace.v1.Span.Event[] + events: IEvent[] ) { assert.deepStrictEqual( events, @@ -135,7 +135,7 @@ export function ensureProtoEventsAreCorrect( } export function ensureProtoAttributesAreCorrect( - attributes: otlpTypes.opentelemetryProto.common.v1.KeyValue[] + attributes: IKeyValue[] ) { assert.deepStrictEqual( attributes, @@ -152,7 +152,7 @@ export function ensureProtoAttributesAreCorrect( } export function ensureProtoLinksAreCorrect( - attributes: otlpTypes.opentelemetryProto.trace.v1.Span.Link[] + attributes: ILink[] ) { assert.deepStrictEqual( attributes, @@ -176,7 +176,7 @@ export function ensureProtoLinksAreCorrect( } export function ensureProtoSpanIsCorrect( - span: otlpTypes.opentelemetryProto.trace.v1.Span + span: ISpan ) { if (span.attributes) { ensureProtoAttributesAreCorrect(span.attributes); @@ -229,36 +229,26 @@ export function ensureProtoSpanIsCorrect( } export function ensureExportTraceServiceRequestIsSet( - json: otlpTypes.opentelemetryProto.collector.trace.v1.ExportTraceServiceRequest + json: IExportTraceServiceRequest ) { const resourceSpans = json.resourceSpans; - assert.strictEqual( - resourceSpans && resourceSpans.length, - 1, - 'resourceSpans is missing' - ); + assert.strictEqual(resourceSpans?.length, 1, 'resourceSpans is missing'); - const resource = resourceSpans[0].resource; - assert.strictEqual(!!resource, true, 'resource is missing'); + const resource = resourceSpans?.[0].resource; + assert.ok(resource, 'resource is missing'); - const instrumentationLibrarySpans = - resourceSpans[0].instrumentationLibrarySpans; + const scopeSpans = resourceSpans?.[0].scopeSpans; assert.strictEqual( - instrumentationLibrarySpans && instrumentationLibrarySpans.length, + scopeSpans?.length, 1, - 'instrumentationLibrarySpans is missing' + 'scopeSpans is missing' ); - const instrumentationLibrary = - instrumentationLibrarySpans[0].instrumentationLibrary; - assert.strictEqual( - !!instrumentationLibrary, - true, - 'instrumentationLibrary is missing' - ); + const scope = scopeSpans?.[0].scope; + assert.ok(scope, 'scope is missing'); - const spans = instrumentationLibrarySpans[0].spans; - assert.strictEqual(spans && spans.length, 1, 'spans are missing'); + const spans = scopeSpans?.[0].spans; + assert.strictEqual(spans?.length, 1, 'spans are missing'); } export class MockedResponse extends Stream { diff --git a/experimental/packages/exporter-trace-otlp-proto/tsconfig.json b/experimental/packages/exporter-trace-otlp-proto/tsconfig.json index 69212c4878..3324a8a529 100644 --- a/experimental/packages/exporter-trace-otlp-proto/tsconfig.json +++ b/experimental/packages/exporter-trace-otlp-proto/tsconfig.json @@ -18,14 +18,14 @@ { "path": "../../../packages/opentelemetry-sdk-trace-base" }, - { - "path": "../exporter-trace-otlp-http" - }, { "path": "../otlp-exporter-base" }, { "path": "../otlp-proto-exporter-base" + }, + { + "path": "../otlp-transformer" } ] } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/README.md b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/README.md index 99f13136be..8d514d0e82 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/README.md +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/README.md @@ -3,7 +3,8 @@ [![NPM Published Version][npm-img]][npm-url] [![Apache License][license-image]][license-image] -This module provides exporter for web and node to be used with [opentelemetry-collector][opentelemetry-collector-url] - last tested with version **0.25.0**. +This module provides exporter for web and node to be used with [opentelemetry-collector][opentelemetry-collector-url]. +Compatible with [opentelemetry-collector][opentelemetry-collector-url] versions `>=0.16 <=0.50`. ## Installation @@ -19,37 +20,38 @@ To see sample code and documentation for the traces exporter, as well as instruc ## Metrics in Node - GRPC -The OTLPTraceExporter in Node expects the URL to only be the hostname. It will not work with `/v1/metrics`. All options that work with trace also work with metrics. +The OTLPMetricsExporter in Node expects the URL to only be the hostname. It will not work with `/v1/metrics`. All options that work with trace also work with metrics. ```js -const { MeterProvider } = require('@opentelemetry/sdk-metrics-base'); +const { MeterProvider, PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics-base'); const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-grpc'); const collectorOptions = { // url is optional and can be omitted - default is grpc://localhost:4317 url: 'grpc://:', }; + const exporter = new OTLPMetricExporter(collectorOptions); +const meterProvider = new MeterProvider({}); + +meterProvider.addMetricReader(new PeriodicExportingMetricReader({ + exporter: metricExporter, + exportIntervalMillis: 1000, +})); -// Register the exporter -const provider = new MeterProvider({ - exporter, - interval: 60000, -}) ['SIGINT', 'SIGTERM'].forEach(signal => { - process.on(signal, () => provider.shutdown().catch(console.error)); + process.on(signal, () => meterProvider.shutdown().catch(console.error)); }); // Now, start recording data -const meter = provider.getMeter('example-meter'); +const meter = meterProvider.getMeter('example-meter'); const counter = meter.createCounter('metric_name'); counter.add(10, { 'key': 'value' }); ``` ## Running opentelemetry-collector locally to see the metrics -1. Go to examples/otlp-exporter-node -2. run `npm run docker:start` -3. Open page at `http://localhost:9411/zipkin/` to observe the metrics +1. Go to `examples/otlp-exporter-node` +2. Follow the instructions there to observe the metrics. ## Useful links diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/package.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/package.json index 0d05d93ec0..0b143e0cf1 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/package.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/package.json @@ -72,7 +72,7 @@ "@opentelemetry/core": "1.2.0", "@opentelemetry/exporter-metrics-otlp-http": "0.28.0", "@opentelemetry/otlp-grpc-exporter-base": "0.28.0", - "@opentelemetry/exporter-trace-otlp-http": "0.28.0", + "@opentelemetry/otlp-transformer": "0.28.0", "@opentelemetry/resources": "1.2.0", "@opentelemetry/sdk-metrics-base": "0.28.0" } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/src/OTLPMetricExporter.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/src/OTLPMetricExporter.ts index af5960eace..3d7b178352 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/src/OTLPMetricExporter.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/src/OTLPMetricExporter.ts @@ -14,12 +14,11 @@ * limitations under the License. */ -import { otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; import { defaultExporterTemporality, defaultOptions, - OTLPMetricExporterBase, OTLPMetricExporterOptions, - toOTLPExportMetricServiceRequest + OTLPMetricExporterBase, + OTLPMetricExporterOptions } from '@opentelemetry/exporter-metrics-otlp-http'; import { AggregationTemporality, ResourceMetrics } from '@opentelemetry/sdk-metrics-base'; import { @@ -30,18 +29,19 @@ import { } from '@opentelemetry/otlp-grpc-exporter-base'; import { baggageUtils, getEnv } from '@opentelemetry/core'; import { Metadata } from '@grpc/grpc-js'; +import { createExportMetricsServiceRequest, IExportMetricsServiceRequest } from '@opentelemetry/otlp-transformer'; const DEFAULT_COLLECTOR_URL = 'localhost:4317'; class OTLPMetricExporterProxy extends OTLPGRPCExporterNodeBase { + IExportMetricsServiceRequest> { protected readonly _aggregationTemporality: AggregationTemporality; constructor(config: OTLPGRPCExporterConfigNode & OTLPMetricExporterOptions= defaultOptions) { super(config); - this.metadata ||= new Metadata(); const headers = baggageUtils.parseKeyPairsIntoRecord(getEnv().OTEL_EXPORTER_OTLP_METRICS_HEADERS); + this.metadata ||= new Metadata(); for (const [k, v] of Object.entries(headers)) { this.metadata.set(k, v); } @@ -66,12 +66,8 @@ class OTLPMetricExporterProxy extends OTLPGRPCExporterNodeBase let collectorExporter: OTLPMetricExporter; let server: grpc.Server; let exportedData: - | otlpTypes.opentelemetryProto.metrics.v1.ResourceMetrics[] + | IResourceMetrics[] | undefined; let metrics: ResourceMetrics; let reqMetadata: grpc.Metadata | undefined; @@ -82,7 +82,7 @@ const testOTLPMetricExporter = (params: TestParams) => .MetricsService.service, { Export: (data: { - request: otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest; + request: IExportMetricsServiceRequest; metadata: grpc.Metadata; }) => { try { @@ -207,45 +207,43 @@ const testOTLPMetricExporter = (params: TestParams) => setTimeout(() => { assert.ok( typeof exportedData !== 'undefined', - 'resource' + " doesn't exist" + 'resource does not exist' ); - let resource; - if (exportedData) { - resource = exportedData[0].resource; - const counter = - exportedData[0].instrumentationLibraryMetrics[0].metrics[0]; - const observableGauge = - exportedData[0].instrumentationLibraryMetrics[0].metrics[1]; - const histogram = - exportedData[0].instrumentationLibraryMetrics[0].metrics[2]; - ensureExportedCounterIsCorrect( - counter, - counter.intSum?.dataPoints[0].timeUnixNano, - counter.intSum?.dataPoints[0].startTimeUnixNano - ); - ensureExportedObservableGaugeIsCorrect( - observableGauge, - observableGauge.doubleGauge?.dataPoints[0].timeUnixNano, - observableGauge.doubleGauge?.dataPoints[0].startTimeUnixNano - ); - ensureExportedHistogramIsCorrect( - histogram, - histogram.intHistogram?.dataPoints[0].timeUnixNano, - histogram.intHistogram?.dataPoints[0].startTimeUnixNano, - [0, 100], - ['0', '2', '0'] - ); - assert.ok( - typeof resource !== 'undefined', - "resource doesn't exist" - ); - if (resource) { - ensureResourceIsCorrect(resource); - } - } - if (params.metadata && reqMetadata) { - ensureMetadataIsCorrect(reqMetadata, params.metadata); - } + + assert.ok(exportedData, 'exportedData does not exist'); + + const resource = exportedData[0].resource; + const counter = + exportedData[0].scopeMetrics[0].metrics[0]; + const observableGauge = + exportedData[0].scopeMetrics[0].metrics[1]; + const histogram = + exportedData[0].scopeMetrics[0].metrics[2]; + ensureExportedCounterIsCorrect( + counter, + counter.sum?.dataPoints[0].timeUnixNano, + counter.sum?.dataPoints[0].startTimeUnixNano + ); + ensureExportedObservableGaugeIsCorrect( + observableGauge, + observableGauge.gauge?.dataPoints[0].timeUnixNano, + observableGauge.gauge?.dataPoints[0].startTimeUnixNano + ); + ensureExportedHistogramIsCorrect( + histogram, + histogram.histogram?.dataPoints[0].timeUnixNano, + histogram.histogram?.dataPoints[0].startTimeUnixNano, + [0, 100], + ['0', '2', '0'] + ); + assert.ok( + typeof resource !== 'undefined', + "resource doesn't exist" + ); + ensureResourceIsCorrect(resource); + + ensureMetadataIsCorrect(reqMetadata, params.metadata); + done(); }, 500); }); @@ -319,11 +317,6 @@ describe('when configuring via environment', () => { }); }); -describe('', () => { - testOTLPMetricExporter({ useTLS: true }); - testOTLPMetricExporter({ useTLS: false }); - testOTLPMetricExporter({ metadata }); -}); - - - +testOTLPMetricExporter({ useTLS: true }); +testOTLPMetricExporter({ useTLS: false }); +testOTLPMetricExporter({ metadata }); diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/metricsHelper.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/metricsHelper.ts index fcb0b4db50..7f33ceb9ef 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/metricsHelper.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/metricsHelper.ts @@ -15,12 +15,12 @@ */ import { Counter, Histogram, ObservableResult, ValueType } from '@opentelemetry/api-metrics'; -import { otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; import { Resource } from '@opentelemetry/resources'; import * as assert from 'assert'; import * as grpc from '@grpc/grpc-js'; import { VERSION } from '@opentelemetry/core'; import { ExplicitBucketHistogramAggregation, MeterProvider, MetricReader } from '@opentelemetry/sdk-metrics-base'; +import { IKeyValue, IMetric, IResource } from '@opentelemetry/otlp-transformer'; export class TestMetricReader extends MetricReader { protected onForceFlush(): Promise { @@ -95,7 +95,7 @@ export function mockHistogram(): Histogram { } export function ensureExportedAttributesAreCorrect( - attributes: otlpTypes.opentelemetryProto.common.v1.KeyValue[] + attributes: IKeyValue[] ) { assert.deepStrictEqual( attributes, @@ -113,7 +113,7 @@ export function ensureExportedAttributesAreCorrect( } export function ensureExportedCounterIsCorrect( - metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, + metric: IMetric, time?: number, startTime?: number ) { @@ -121,13 +121,15 @@ export function ensureExportedCounterIsCorrect( name: 'int-counter', description: 'sample counter description', unit: '1', - data: 'intSum', - intSum: { + data: 'sum', + sum: { dataPoints: [ { - labels: [], + attributes: [], exemplars: [], - value: '1', + value: 'asInt', + asInt: '1', + flags: 0, startTimeUnixNano: String(startTime), timeUnixNano: String(time), }, @@ -139,7 +141,7 @@ export function ensureExportedCounterIsCorrect( } export function ensureExportedObservableGaugeIsCorrect( - metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, + metric: IMetric, time?: number, startTime?: number ) { @@ -147,13 +149,15 @@ export function ensureExportedObservableGaugeIsCorrect( name: 'double-observable-gauge', description: 'sample observable gauge description', unit: '1', - data: 'doubleGauge', - doubleGauge: { + data: 'gauge', + gauge: { dataPoints: [ { - labels: [], + attributes: [], exemplars: [], - value: 6, + value: 'asDouble', + asDouble: 6, + flags: 0, startTimeUnixNano: String(startTime), timeUnixNano: String(time), }, @@ -163,7 +167,7 @@ export function ensureExportedObservableGaugeIsCorrect( } export function ensureExportedHistogramIsCorrect( - metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, + metric: IMetric, time?: number, startTime?: number, explicitBounds: number[] = [Infinity], @@ -173,13 +177,15 @@ export function ensureExportedHistogramIsCorrect( name: 'int-histogram', description: 'sample histogram description', unit: '1', - data: 'intHistogram', - intHistogram: { + data: 'histogram', + histogram: { dataPoints: [ { - labels: [], + attributes: [], exemplars: [], - sum: '21', + flags: 0, + _sum: 'sum', + sum: 21, count: '2', startTimeUnixNano: String(startTime), timeUnixNano: String(time), @@ -193,7 +199,7 @@ export function ensureExportedHistogramIsCorrect( } export function ensureResourceIsCorrect( - resource: otlpTypes.opentelemetryProto.resource.v1.Resource + resource: IResource ) { assert.deepStrictEqual(resource, { attributes: [ @@ -252,11 +258,11 @@ export function ensureResourceIsCorrect( } export function ensureMetadataIsCorrect( - actual: grpc.Metadata, - expected: grpc.Metadata + actual?: grpc.Metadata, + expected?: grpc.Metadata ) { //ignore user agent - expected.remove('user-agent'); - actual.remove('user-agent'); - assert.deepStrictEqual(actual.getMap(), expected.getMap()); + expected?.remove('user-agent'); + actual?.remove('user-agent'); + assert.deepStrictEqual(actual?.getMap(), expected?.getMap() ?? {}); } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/tsconfig.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/tsconfig.json index 6380e3f172..77f463edd1 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/tsconfig.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/tsconfig.json @@ -15,9 +15,6 @@ { "path": "../../../packages/opentelemetry-resources" }, - { - "path": "../exporter-trace-otlp-http" - }, { "path": "../opentelemetry-api-metrics" }, @@ -29,6 +26,9 @@ }, { "path": "../otlp-grpc-exporter-base" + }, + { + "path": "../otlp-transformer" } ] } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/README.md b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/README.md index fc5ebe33b8..5035110219 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/README.md +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/README.md @@ -3,7 +3,8 @@ [![NPM Published Version][npm-img]][npm-url] [![Apache License][license-image]][license-image] -This module provides exporter for web and node to be used with [opentelemetry-collector][opentelemetry-collector-url] - last tested with version **0.25.0**. +This module provides exporter for web and node to be used with [opentelemetry-collector][opentelemetry-collector-url]. +Compatible with [opentelemetry-collector][opentelemetry-collector-url] versions `>=0.48 <=0.50`. ## Installation @@ -22,7 +23,7 @@ To see sample code and documentation for the traces exporter, visit the [Collect The OTLPMetricExporter in Web expects the endpoint to end in `/v1/metrics`. ```js -import { MeterProvider } from '@opentelemetry/sdk-metrics-base'; +import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics-base'; import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'; const collectorOptions = { url: '', // url is optional and can be omitted - default is http://localhost:4318/v1/metrics @@ -30,37 +31,38 @@ const collectorOptions = { concurrencyLimit: 1, // an optional limit on pending requests }; const exporter = new OTLPMetricExporter(collectorOptions); +const meterProvider = new MeterProvider({}); -// Register the exporter -const meter = new MeterProvider({ - exporter, - interval: 60000, -}).getMeter('example-meter'); +meterProvider.addMetricReader(new PeriodicExportingMetricReader({ + exporter: metricExporter, + exportIntervalMillis: 1000, +})); // Now, start recording data +const meter = meterProvider.getMeter('example-meter'); const counter = meter.createCounter('metric_name'); counter.add(10, { 'key': 'value' }); - ``` ## Metrics in Node ```js -const { MeterProvider } = require('@opentelemetry/sdk-metrics-base'); +const { MeterProvider, PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics-base'); const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-http'); const collectorOptions = { url: '', // url is optional and can be omitted - default is http://localhost:4318/v1/metrics concurrencyLimit: 1, // an optional limit on pending requests }; const exporter = new OTLPMetricExporter(collectorOptions); +const meterProvider = new MeterProvider({}); -// Register the exporter -const meter = new MeterProvider({ - exporter, - interval: 60000, -}).getMeter('example-meter'); +meterProvider.addMetricReader(new PeriodicExportingMetricReader({ + exporter: metricExporter, + exportIntervalMillis: 1000, +})); // Now, start recording data +const meter = meterProvider.getMeter('example-meter'); const counter = meter.createCounter('metric_name'); counter.add(10, { 'key': 'value' }); @@ -79,16 +81,16 @@ For exporting metrics with PROTOBUF please check [exporter-metrics-otlp-proto][n Instead of providing options to `OTLPMetricExporter` and `OTLPTraceExporter` explicitly, environment variables may be provided instead. ```sh -OTEL_EXPORTER_OTLP_ENDPOINT=https://localhost:4317 +OTEL_EXPORTER_OTLP_ENDPOINT=https://localhost:4318 # this will automatically append the version and signal path -# e.g. https://localhost:4317/v1/traces for `OTLPTraceExporter` and https://localhost:4317/v1/metrics for `OTLPMetricExporter` +# e.g. https://localhost:4318/v1/traces for `OTLPTraceExporter` and https://localhost:4318/v1/metrics for `OTLPMetricExporter` ``` If the trace and metric exporter endpoints have different providers, the env var for per-signal endpoints are available to use ```sh -OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://trace-service:4317/v1/traces -OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=https://metric-service:4317/v1/metrics +OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://trace-service:4318/v1/traces +OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=https://metric-service:4318/v1/metrics # version and signal needs to be explicit ``` @@ -98,9 +100,8 @@ For more details, see [OpenTelemetry Specification on Protocol Exporter][opentel ## Running opentelemetry-collector locally to see the metrics -1. Go to examples/otlp-exporter-node -2. run `npm run docker:start` -3. Open page at `http://localhost:9411/zipkin/` to observe the metrics +1. Go to `examples/otlp-exporter-node` +2. Follow the instructions there to observe the metrics. ## Useful links diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/package.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/package.json index d896cc02ec..fa25fe0707 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/package.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/package.json @@ -96,7 +96,7 @@ "dependencies": { "@opentelemetry/api-metrics": "0.28.0", "@opentelemetry/core": "1.2.0", - "@opentelemetry/exporter-trace-otlp-http": "0.28.0", + "@opentelemetry/otlp-transformer": "0.28.0", "@opentelemetry/otlp-exporter-base": "0.28.0", "@opentelemetry/resources": "1.2.0", "@opentelemetry/sdk-metrics-base": "0.28.0" diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/OTLPMetricExporterBase.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/OTLPMetricExporterBase.ts index 75a0617bec..5d06cd60bd 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/OTLPMetricExporterBase.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/OTLPMetricExporterBase.ts @@ -16,13 +16,13 @@ import { ExportResult } from '@opentelemetry/core'; import { AggregationTemporality, PushMetricExporter, ResourceMetrics } from '@opentelemetry/sdk-metrics-base'; -import { otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; import { defaultOptions, OTLPMetricExporterOptions } from './OTLPMetricExporterOptions'; import { OTLPExporterBase } from '@opentelemetry/otlp-exporter-base'; +import { IExportMetricsServiceRequest } from '@opentelemetry/otlp-transformer'; export class OTLPMetricExporterBase> + IExportMetricsServiceRequest>> implements PushMetricExporter { public _otlpExporter: T; protected _preferredAggregationTemporality: AggregationTemporality; @@ -48,5 +48,4 @@ implements PushMetricExporter { getPreferredAggregationTemporality(): AggregationTemporality { return this._preferredAggregationTemporality; } - } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/index.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/index.ts index c1deb8d977..ee1b1574a6 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/index.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/index.ts @@ -17,4 +17,3 @@ export * from './platform'; export * from './OTLPMetricExporterOptions'; export * from './OTLPMetricExporterBase'; -export { toOTLPExportMetricServiceRequest } from './transformMetrics'; diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/browser/OTLPMetricExporter.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/browser/OTLPMetricExporter.ts index 6daa90742e..0189b6d9b3 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/browser/OTLPMetricExporter.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/browser/OTLPMetricExporter.ts @@ -15,10 +15,6 @@ */ import { AggregationTemporality, ResourceMetrics } from '@opentelemetry/sdk-metrics-base'; -import { - otlpTypes -} from '@opentelemetry/exporter-trace-otlp-http'; -import { toOTLPExportMetricServiceRequest } from '../../transformMetrics'; import { baggageUtils, getEnv } from '@opentelemetry/core'; import { defaultExporterTemporality, defaultOptions, OTLPMetricExporterOptions } from '../../OTLPMetricExporterOptions'; import { OTLPMetricExporterBase } from '../../OTLPMetricExporterBase'; @@ -27,12 +23,13 @@ import { OTLPExporterBrowserBase, OTLPExporterConfigBase } from '@opentelemetry/otlp-exporter-base'; +import { createExportMetricsServiceRequest, IExportMetricsServiceRequest } from '@opentelemetry/otlp-transformer'; const DEFAULT_COLLECTOR_RESOURCE_PATH = '/v1/metrics'; const DEFAULT_COLLECTOR_URL = `http://localhost:4318${DEFAULT_COLLECTOR_RESOURCE_PATH}`; class OTLPExporterBrowserProxy extends OTLPExporterBrowserBase { + IExportMetricsServiceRequest> { protected readonly _aggregationTemporality: AggregationTemporality; constructor(config: OTLPMetricExporterOptions & OTLPExporterConfigBase = defaultOptions) { @@ -56,13 +53,10 @@ class OTLPExporterBrowserProxy extends OTLPExporterBrowserBase { + IExportMetricsServiceRequest> { protected readonly _aggregationTemporality: AggregationTemporality; constructor(config: OTLPExporterNodeConfigBase & OTLPMetricExporterOptions = defaultOptions) { @@ -46,13 +43,10 @@ class OTLPExporterNodeProxy extends OTLPExporterNodeBase { - return { key, value: String(value) }; - }); -} - -/** - * Convert {@link AggregationTemporality} to a collector-compatible format. - * @param aggregationTemporality - */ -export function toAggregationTemporality( - aggregationTemporality: AggregationTemporality -): otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality { - if (aggregationTemporality === AggregationTemporality.CUMULATIVE) { - return otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality - .AGGREGATION_TEMPORALITY_CUMULATIVE; - } - if (aggregationTemporality === AggregationTemporality.DELTA) { - return otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality - .AGGREGATION_TEMPORALITY_DELTA; - } - - return otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality - .AGGREGATION_TEMPORALITY_UNSPECIFIED; -} - -/** - * Convert {@link MetricData} of {@link DataPointType.SINGULAR} to a collector-compatible format. - * @param metricData - */ -export function toSingularDataPoints( - metricData: MetricData -): otlpTypes.opentelemetryProto.metrics.v1.DataPoint[] { - return Array.from(metricData.dataPoints.map(dataPoint => { - return { - labels: toCollectorAttributes(dataPoint.attributes), - value: dataPoint.value as number, - startTimeUnixNano: core.hrTimeToNanoseconds( - dataPoint.startTime - ), - timeUnixNano: core.hrTimeToNanoseconds( - dataPoint.endTime - ), - }; - })); -} - -/** - * Convert {@link MetricData} of {@link DataPointType.HISTOGRAM} to a collector-compatible format. - * @param metricData - */ -export function toHistogramDataPoints( - metricData: MetricData -): otlpTypes.opentelemetryProto.metrics.v1.HistogramDataPoint[] { - return Array.from(metricData.dataPoints.map(dataPoints => { - const histogram = dataPoints.value as Histogram; - return { - labels: toCollectorAttributes(dataPoints.attributes), - sum: histogram.sum, - count: histogram.count, - startTimeUnixNano: core.hrTimeToNanoseconds( - dataPoints.startTime - ), - timeUnixNano: core.hrTimeToNanoseconds( - dataPoints.endTime - ), - bucketCounts: histogram.buckets.counts, - explicitBounds: histogram.buckets.boundaries, - }; - })); -} - -/** - * Converts {@link MetricData} to a collector-compatible format. - * @param metricData - * @param aggregationTemporality - */ -export function toCollectorMetric( - metricData: MetricData, - aggregationTemporality: AggregationTemporality -): otlpTypes.opentelemetryProto.metrics.v1.Metric { - const metricCollector: otlpTypes.opentelemetryProto.metrics.v1.Metric = { - name: metricData.descriptor.name, - description: metricData.descriptor.description, - unit: metricData.descriptor.unit, - }; - - if (metricData.dataPointType === DataPointType.SINGULAR) { - const result = { - dataPoints: toSingularDataPoints(metricData), - isMonotonic: - metricData.descriptor.type === InstrumentType.COUNTER || - metricData.descriptor.type === InstrumentType.OBSERVABLE_COUNTER, - aggregationTemporality: toAggregationTemporality(aggregationTemporality), - }; - - if ( - metricData.descriptor.type === InstrumentType.COUNTER || - metricData.descriptor.type === InstrumentType.OBSERVABLE_COUNTER || - metricData.descriptor.type === InstrumentType.UP_DOWN_COUNTER || - metricData.descriptor.type === InstrumentType.OBSERVABLE_UP_DOWN_COUNTER - ) { - if (metricData.descriptor.valueType === ValueType.INT) { - metricCollector.intSum = result; - } else { - metricCollector.doubleSum = result; - } - } else{ - // Instrument is a gauge. - if (metricData.descriptor.valueType === ValueType.INT) { - metricCollector.intGauge = result; - } else { - metricCollector.doubleGauge = result; - } - } - } else if (metricData.dataPointType === DataPointType.HISTOGRAM) { - const result = { - dataPoints: toHistogramDataPoints(metricData), - aggregationTemporality: toAggregationTemporality(aggregationTemporality) - }; - if (metricData.descriptor.valueType === ValueType.INT) { - metricCollector.intHistogram = result; - } else { - metricCollector.doubleHistogram = result; - } - } - - // TODO: Add support for exponential histograms when they're ready. - - return metricCollector; -} - -/** - * Prepares metric service request to be sent to collector - * @param metrics metrics - * @param aggregationTemporality - * @param collectorExporterBase - */ -export function toOTLPExportMetricServiceRequest( - metrics: ResourceMetrics, - aggregationTemporality: AggregationTemporality, - collectorExporterBase: OTLPExporterBase -): otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest { - const additionalAttributes = Object.assign( - {}, - collectorExporterBase.attributes - ); - return { - resourceMetrics: toCollectorResourceMetrics( - metrics, - additionalAttributes, - aggregationTemporality - ), - }; -} - -/** - * Convert to InstrumentationLibraryMetrics - * @param instrumentationLibrary - * @param metrics - * @param aggregationTemporality - */ -function toCollectorInstrumentationLibraryMetrics( - instrumentationLibrary: core.InstrumentationLibrary, - metrics: MetricData[], - aggregationTemporality: AggregationTemporality -): otlpTypes.opentelemetryProto.metrics.v1.InstrumentationLibraryMetrics { - return { - metrics: metrics.map(metric => toCollectorMetric(metric, aggregationTemporality)), - instrumentationLibrary, - }; -} - -/** - * Returns a list of resource metrics which will be exported to the collector - * @param resourceMetrics - * @param baseAttributes - * @param aggregationTemporality - */ -function toCollectorResourceMetrics( - resourceMetrics: ResourceMetrics, - baseAttributes: SpanAttributes, - aggregationTemporality: AggregationTemporality -): otlpTypes.opentelemetryProto.metrics.v1.ResourceMetrics[] { - return [{ - resource: toCollectorResource(resourceMetrics.resource, baseAttributes), - instrumentationLibraryMetrics: Array.from(resourceMetrics.instrumentationLibraryMetrics.map( - instrumentationLibraryMetrics => toCollectorInstrumentationLibraryMetrics( - instrumentationLibraryMetrics.instrumentationLibrary, - instrumentationLibraryMetrics.metrics, - aggregationTemporality - ))) - }]; -} diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/browser/CollectorMetricExporter.test.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/browser/CollectorMetricExporter.test.ts index fb76700f59..21cec954f9 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/browser/CollectorMetricExporter.test.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/browser/CollectorMetricExporter.test.ts @@ -21,7 +21,6 @@ import { AggregationTemporality, ResourceMetrics, } from '@opentelemetry/sdk-met import * as assert from 'assert'; import * as sinon from 'sinon'; import { OTLPMetricExporter } from '../../src/platform/browser'; -import { otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; import { collect, ensureCounterIsCorrect, @@ -37,6 +36,7 @@ import { } from '../metricsHelper'; import { OTLPMetricExporterOptions } from '../../src'; import { OTLPExporterConfigBase } from '@opentelemetry/otlp-exporter-base'; +import { IExportMetricsServiceRequest } from '@opentelemetry/otlp-transformer'; describe('OTLPMetricExporter - web', () => { let collectorExporter: OTLPMetricExporter; @@ -106,62 +106,51 @@ describe('OTLPMetricExporter - web', () => { const url = args[0]; const blob: Blob = args[1]; const body = await blob.text(); - const json = JSON.parse( - body - ) as otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest; - const metric1 = - json.resourceMetrics[0].instrumentationLibraryMetrics[0].metrics[0]; - const metric2 = - json.resourceMetrics[0].instrumentationLibraryMetrics[0].metrics[1]; - const metric3 = - json.resourceMetrics[0].instrumentationLibraryMetrics[0].metrics[2]; + const json = JSON.parse(body) as IExportMetricsServiceRequest; + const metric1 = json.resourceMetrics[0].scopeMetrics[0].metrics[0]; + const metric2 = json.resourceMetrics[0].scopeMetrics[0].metrics[1]; + const metric3 = json.resourceMetrics[0].scopeMetrics[0].metrics[2]; assert.ok(typeof metric1 !== 'undefined', "metric doesn't exist"); - if (metric1) { - ensureCounterIsCorrect( - metric1, - hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[0].dataPoints[0].endTime), - hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[0].dataPoints[0].startTime) - ); - } + + ensureCounterIsCorrect( + metric1, + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[0].dataPoints[0].endTime), + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[0].dataPoints[0].startTime) + ); + assert.ok( typeof metric2 !== 'undefined', "second metric doesn't exist" ); - if (metric2) { - ensureObservableGaugeIsCorrect( - metric2, - hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[1].dataPoints[0].endTime), - hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[1].dataPoints[0].startTime), - 6, - 'double-observable-gauge2' - ); - } + ensureObservableGaugeIsCorrect( + metric2, + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[1].dataPoints[0].endTime), + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[1].dataPoints[0].startTime), + 6, + 'double-observable-gauge2' + ); assert.ok( typeof metric3 !== 'undefined', "third metric doesn't exist" ); - if (metric3) { - ensureHistogramIsCorrect( - metric3, - hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[2].dataPoints[0].endTime), - hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[2].dataPoints[0].startTime), - [0, 100], - [0, 2, 0] - ); - } + ensureHistogramIsCorrect( + metric3, + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[2].dataPoints[0].endTime), + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[2].dataPoints[0].startTime), + [0, 100], + [0, 2, 0] + ); const resource = json.resourceMetrics[0].resource; assert.ok(typeof resource !== 'undefined', "resource doesn't exist"); - if (resource) { - ensureWebResourceIsCorrect(resource); - } + ensureWebResourceIsCorrect(resource); assert.strictEqual(url, 'http://foo.bar.com'); - assert.strictEqual(stubBeacon.callCount, 1); + assert.strictEqual(stubBeacon.callCount, 1); assert.strictEqual(stubOpen.callCount, 0); ensureExportMetricsServiceRequestIsSet(json); @@ -224,56 +213,45 @@ describe('OTLPMetricExporter - web', () => { assert.strictEqual(request.url, 'http://foo.bar.com'); const body = request.requestBody; - const json = JSON.parse( - body - ) as otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest; - const metric1 = - json.resourceMetrics[0].instrumentationLibraryMetrics[0].metrics[0]; - const metric2 = - json.resourceMetrics[0].instrumentationLibraryMetrics[0].metrics[1]; - const metric3 = - json.resourceMetrics[0].instrumentationLibraryMetrics[0].metrics[2]; + const json = JSON.parse(body) as IExportMetricsServiceRequest; + const metric1 = json.resourceMetrics[0].scopeMetrics[0].metrics[0]; + const metric2 = json.resourceMetrics[0].scopeMetrics[0].metrics[1]; + const metric3 = json.resourceMetrics[0].scopeMetrics[0].metrics[2]; + assert.ok(typeof metric1 !== 'undefined', "metric doesn't exist"); - if (metric1) { - ensureCounterIsCorrect( - metric1, - hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[0].dataPoints[0].endTime), - hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[0].dataPoints[0].startTime) - ); - } + ensureCounterIsCorrect( + metric1, + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[0].dataPoints[0].endTime), + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[0].dataPoints[0].startTime) + ); + assert.ok( typeof metric2 !== 'undefined', "second metric doesn't exist" ); - if (metric2) { - ensureObservableGaugeIsCorrect( - metric2, - hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[1].dataPoints[0].endTime), - hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[1].dataPoints[0].startTime), - 6, - 'double-observable-gauge2' - ); - } + ensureObservableGaugeIsCorrect( + metric2, + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[1].dataPoints[0].endTime), + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[1].dataPoints[0].startTime), + 6, + 'double-observable-gauge2' + ); assert.ok( typeof metric3 !== 'undefined', "third metric doesn't exist" ); - if (metric3) { - ensureHistogramIsCorrect( - metric3, - hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[2].dataPoints[0].endTime), - hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[2].dataPoints[0].startTime), - [0, 100], - [0, 2, 0] - ); - } + ensureHistogramIsCorrect( + metric3, + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[2].dataPoints[0].endTime), + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[2].dataPoints[0].startTime), + [0, 100], + [0, 2, 0] + ); const resource = json.resourceMetrics[0].resource; assert.ok(typeof resource !== 'undefined', "resource doesn't exist"); - if (resource) { - ensureWebResourceIsCorrect(resource); - } + ensureWebResourceIsCorrect(resource); assert.strictEqual(stubBeacon.callCount, 0); ensureExportMetricsServiceRequestIsSet(json); diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/CollectorMetricExporter.test.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/CollectorMetricExporter.test.ts index 9c2b4e4c89..baddaa76bc 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/CollectorMetricExporter.test.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/CollectorMetricExporter.test.ts @@ -20,15 +20,15 @@ import { } from '@opentelemetry/sdk-metrics-base'; import * as assert from 'assert'; import * as sinon from 'sinon'; -import { otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; import { collect, mockCounter, mockObservableGauge, setUp, shutdown } from '../metricsHelper'; import { OTLPExporterBase, OTLPExporterConfigBase } from '@opentelemetry/otlp-exporter-base'; +import { IExportMetricsServiceRequest } from '@opentelemetry/otlp-transformer'; type CollectorExporterConfig = OTLPExporterConfigBase; class OTLPMetricExporter extends OTLPExporterBase< CollectorExporterConfig, ResourceMetrics, - otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest + IExportMetricsServiceRequest > { onInit() {} onShutdown() {} @@ -36,9 +36,7 @@ class OTLPMetricExporter extends OTLPExporterBase< getDefaultUrl(config: CollectorExporterConfig) { return config.url || ''; } - convert( - metrics: ResourceMetrics[] - ): otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest { + convert(metrics: ResourceMetrics[]): IExportMetricsServiceRequest { return { resourceMetrics: [] }; } } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/transformMetrics.test.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/transformMetrics.test.ts deleted file mode 100644 index 6232752304..0000000000 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/transformMetrics.test.ts +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { hrTimeToNanoseconds, hrTime } from '@opentelemetry/core'; -import { AggregationTemporality, DataPointType, InstrumentType } from '@opentelemetry/sdk-metrics-base'; -import * as assert from 'assert'; -import * as transform from '../../src/transformMetrics'; -import { - collect, - ensureCounterIsCorrect, - ensureDoubleCounterIsCorrect, - ensureHistogramIsCorrect, - ensureObservableCounterIsCorrect, - ensureObservableGaugeIsCorrect, - ensureObservableUpDownCounterIsCorrect, - mockCounter, - mockDoubleCounter, - mockHistogram, - mockObservableCounter, - mockObservableGauge, - mockObservableUpDownCounter, - setUp, -} from '../metricsHelper'; - -describe('transformMetrics', () => { - describe('toCollectorMetric', async () => { - - function getValue(count: number) { - if (count % 2 === 0) { - return 3; - } - return -1; - } - - beforeEach(() => { - setUp(); - }); - - it('should convert counter', async () => { - const counter = mockCounter(); - counter.add(1); - const metrics = (await collect()); - - const metric = metrics.instrumentationLibraryMetrics[0].metrics[0]; - ensureCounterIsCorrect( - transform.toCollectorMetric(metric, AggregationTemporality.CUMULATIVE), - hrTimeToNanoseconds(metric.dataPoints[0].endTime), - hrTimeToNanoseconds(metric.dataPoints[0].startTime) - ); - } - ); - - it('should convert double counter', async () => { - const doubleCounter = mockDoubleCounter(); - doubleCounter.add(8); - const metrics = (await collect()); - - const metric = metrics.instrumentationLibraryMetrics[0].metrics[0]; - ensureDoubleCounterIsCorrect( - transform.toCollectorMetric(metric, AggregationTemporality.CUMULATIVE), - hrTimeToNanoseconds(metric.dataPoints[0].endTime), - hrTimeToNanoseconds(metric.dataPoints[0].startTime), - ); - } - ); - - it('should convert observable gauge', async () => { - let count = 0; - mockObservableGauge(observableResult => { - count++; - observableResult.observe(getValue(count), {}); - }); - - // collect three times. - await collect(); - await collect(); - const metrics = (await collect()); - - const metric = metrics.instrumentationLibraryMetrics[0].metrics[0]; - ensureObservableGaugeIsCorrect( - transform.toCollectorMetric(metric, AggregationTemporality.CUMULATIVE), - hrTimeToNanoseconds(metric.dataPoints[0].endTime), - hrTimeToNanoseconds(metric.dataPoints[0].startTime), - -1, - ); - } - ); - - - it('should convert observable counter', async () => { - mockObservableCounter(observableResult => { - observableResult.observe(1, {}); - }); - - // collect three times. - await collect(); - await collect(); - const metrics = (await collect()); - // TODO: Collect seems to not deliver the last observation -> why? - - const metric = metrics.instrumentationLibraryMetrics[0].metrics[0]; - ensureObservableCounterIsCorrect( - transform.toCollectorMetric(metric, AggregationTemporality.CUMULATIVE), - hrTimeToNanoseconds(metric.dataPoints[0].endTime), - hrTimeToNanoseconds(metric.dataPoints[0].startTime), - 2, - ); - } - ); - - it('should convert observable up-down counter', async () => { - mockObservableUpDownCounter(observableResult => { - observableResult.observe(1, {}); - }); - - // collect three times. - await collect(); - await collect(); - const metrics = (await collect()); - // TODO: Collect seems to not deliver the last observation -> why? - - const metric = metrics.instrumentationLibraryMetrics[0].metrics[0]; - ensureObservableUpDownCounterIsCorrect( - transform.toCollectorMetric(metric, AggregationTemporality.CUMULATIVE), - hrTimeToNanoseconds(metric.dataPoints[0].endTime), - hrTimeToNanoseconds(metric.dataPoints[0].startTime), - 2, - ); - } - ); - - it('should convert observable histogram', async () => { - const histogram = mockHistogram(); - histogram.record(7); - histogram.record(14); - - const metrics = (await collect()); - - const metric = metrics.instrumentationLibraryMetrics[0].metrics[0]; - ensureHistogramIsCorrect( - transform.toCollectorMetric(metric, AggregationTemporality.CUMULATIVE), - hrTimeToNanoseconds(metric.dataPoints[0].endTime), - hrTimeToNanoseconds(metric.dataPoints[0].startTime), - [0, 100], - [0, 2, 0] - ); - } - ); - - it('should convert metric attributes value to string', () => { - const metric = transform.toCollectorMetric( - { - descriptor: { - name: 'name', - description: 'description', - unit: 'unit', - type: InstrumentType.COUNTER, - valueType: 0, - }, - dataPoints: [ - { - value: 1, - attributes: { foo: (1 as unknown) as string }, - startTime: hrTime(), - endTime: hrTime(), - } - ], - dataPointType: DataPointType.SINGULAR, - }, - AggregationTemporality.CUMULATIVE - ); - const collectorMetric = metric.intSum?.dataPoints[0]; - assert.strictEqual(collectorMetric?.labels[0].value, '1'); - }); - }); -}); diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/metricsHelper.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/metricsHelper.ts index a38db6ca83..457b525f19 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/metricsHelper.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/metricsHelper.ts @@ -14,12 +14,26 @@ * limitations under the License. */ -import { Counter, Histogram, ObservableResult, ValueType, } from '@opentelemetry/api-metrics'; -import { InstrumentationLibrary, VERSION } from '@opentelemetry/core'; -import { ExplicitBucketHistogramAggregation, MeterProvider, MetricReader } from '@opentelemetry/sdk-metrics-base'; +import { + Counter, + ObservableResult, + Histogram, + ValueType, +} from '@opentelemetry/api-metrics'; import { Resource } from '@opentelemetry/resources'; import * as assert from 'assert'; -import { otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; +import { InstrumentationLibrary, VERSION } from '@opentelemetry/core'; +import { + ExplicitBucketHistogramAggregation, + MeterProvider, + MetricReader +} from '@opentelemetry/sdk-metrics-base'; +import { + IExportMetricsServiceRequest, + IKeyValue, + IMetric, + IResource +} from '@opentelemetry/otlp-transformer'; if (typeof Buffer === 'undefined') { (window as any).Buffer = { @@ -164,7 +178,7 @@ export const mockedInstrumentationLibraries: InstrumentationLibrary[] = [ ]; export function ensureAttributesAreCorrect( - attributes: otlpTypes.opentelemetryProto.common.v1.KeyValue[] + attributes: IKeyValue[] ) { assert.deepStrictEqual( attributes, @@ -181,7 +195,7 @@ export function ensureAttributesAreCorrect( } export function ensureWebResourceIsCorrect( - resource: otlpTypes.opentelemetryProto.resource.v1.Resource + resource: IResource ) { assert.strictEqual(resource.attributes.length, 7); assert.strictEqual(resource.attributes[0].key, 'service.name'); @@ -202,33 +216,31 @@ export function ensureWebResourceIsCorrect( } export function ensureCounterIsCorrect( - metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, - endTime: number, - startTime: number + metric: IMetric, + time?: number, + startTime?: number ) { assert.deepStrictEqual(metric, { name: 'int-counter', description: 'sample counter description', unit: '1', - intSum: { + sum: { dataPoints: [ { - labels: [], - value: 1, + attributes: [], + asInt: 1, startTimeUnixNano: startTime, - timeUnixNano: endTime, + timeUnixNano: time, }, ], isMonotonic: true, - aggregationTemporality: - otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality - .AGGREGATION_TEMPORALITY_CUMULATIVE, + aggregationTemporality: 2, }, }); } export function ensureDoubleCounterIsCorrect( - metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, + metric: IMetric, time: number, endTime: number ) { @@ -246,15 +258,13 @@ export function ensureDoubleCounterIsCorrect( }, ], isMonotonic: true, - aggregationTemporality: - otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality - .AGGREGATION_TEMPORALITY_CUMULATIVE, + aggregationTemporality: 2, }, }); } export function ensureObservableGaugeIsCorrect( - metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, + metric: IMetric, time: number, startTime: number, value: number, @@ -264,25 +274,21 @@ export function ensureObservableGaugeIsCorrect( name, description: 'sample observable gauge description', unit: '1', - doubleGauge: { + gauge: { dataPoints: [ { - labels: [], - value, + attributes: [], + asDouble: value, startTimeUnixNano: startTime, timeUnixNano: time, }, - ], - aggregationTemporality: - otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality - .AGGREGATION_TEMPORALITY_CUMULATIVE, - isMonotonic: false, + ] }, }); } export function ensureObservableCounterIsCorrect( - metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, + metric: IMetric, time: number, startTime: number, value: number, @@ -296,21 +302,19 @@ export function ensureObservableCounterIsCorrect( isMonotonic: true, dataPoints: [ { - labels: [], + attributes: [], value, startTimeUnixNano: startTime, timeUnixNano: time, }, ], - aggregationTemporality: - otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality - .AGGREGATION_TEMPORALITY_CUMULATIVE, + aggregationTemporality: 2 }, }); } export function ensureObservableUpDownCounterIsCorrect( - metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, + metric: IMetric, time: number, startTime: number, value: number, @@ -330,15 +334,13 @@ export function ensureObservableUpDownCounterIsCorrect( timeUnixNano: time, }, ], - aggregationTemporality: - otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality - .AGGREGATION_TEMPORALITY_CUMULATIVE, + aggregationTemporality: 2 }, }); } export function ensureHistogramIsCorrect( - metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, + metric: IMetric, time: number, startTime: number, explicitBounds: (number | null)[] = [Infinity], @@ -348,10 +350,10 @@ export function ensureHistogramIsCorrect( name: 'int-histogram', description: 'sample histogram description', unit: '1', - intHistogram: { + histogram: { dataPoints: [ { - labels: [], + attributes: [], sum: 21, count: 2, startTimeUnixNano: startTime, @@ -360,15 +362,13 @@ export function ensureHistogramIsCorrect( explicitBounds, }, ], - aggregationTemporality: - otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality - .AGGREGATION_TEMPORALITY_CUMULATIVE, + aggregationTemporality: 2 }, }); } export function ensureExportMetricsServiceRequestIsSet( - json: otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest + json: IExportMetricsServiceRequest ) { const resourceMetrics = json.resourceMetrics; assert.strictEqual( @@ -378,25 +378,15 @@ export function ensureExportMetricsServiceRequestIsSet( ); const resource = resourceMetrics[0].resource; - assert.strictEqual(!!resource, true, 'resource is missing'); + assert.ok(resource, 'resource is missing'); - const instrumentationLibraryMetrics = - resourceMetrics[0].instrumentationLibraryMetrics; - assert.strictEqual( - instrumentationLibraryMetrics && instrumentationLibraryMetrics.length, - 1, - 'instrumentationLibraryMetrics is missing' - ); + const scopeMetrics = resourceMetrics[0].scopeMetrics; + assert.strictEqual(scopeMetrics?.length, 1, 'scopeMetrics is missing'); - const instrumentationLibrary = - instrumentationLibraryMetrics[0].instrumentationLibrary; - assert.strictEqual( - !!instrumentationLibrary, - true, - 'instrumentationLibrary is missing' - ); + const scope = scopeMetrics[0].scope; + assert.ok(scope, 'scope is missing'); - const metrics = resourceMetrics[0].instrumentationLibraryMetrics[0].metrics; + const metrics = resourceMetrics[0].scopeMetrics[0].metrics; assert.strictEqual(metrics.length, 3, 'Metrics are missing'); } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/node/CollectorMetricExporter.test.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/node/CollectorMetricExporter.test.ts index d5eb13986d..93c9ba70e1 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/node/CollectorMetricExporter.test.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/node/CollectorMetricExporter.test.ts @@ -27,7 +27,6 @@ import { import { OTLPMetricExporter } from '../../src/platform/node'; -import { otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; import { ensureCounterIsCorrect, ensureExportMetricsServiceRequestIsSet, @@ -41,6 +40,7 @@ import { import { MockedResponse } from './nodeHelpers'; import { AggregationTemporality, ResourceMetrics } from '@opentelemetry/sdk-metrics-base'; import { OTLPExporterError, OTLPExporterNodeConfigBase } from '@opentelemetry/otlp-exporter-base'; +import { IExportMetricsServiceRequest } from '@opentelemetry/otlp-transformer'; const fakeRequest = { end: function () { @@ -230,15 +230,10 @@ describe('OTLPMetricExporter - node with json over http', () => { setTimeout(() => { const writeArgs = stubWrite.args[0]; - const json = JSON.parse( - writeArgs[0] - ) as otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest; - const metric1 = - json.resourceMetrics[0].instrumentationLibraryMetrics[0].metrics[0]; - const metric2 = - json.resourceMetrics[0].instrumentationLibraryMetrics[0].metrics[1]; - const metric3 = - json.resourceMetrics[0].instrumentationLibraryMetrics[0].metrics[2]; + const json = JSON.parse(writeArgs[0]) as IExportMetricsServiceRequest; + const metric1 = json.resourceMetrics[0].scopeMetrics[0].metrics[0]; + const metric2 = json.resourceMetrics[0].scopeMetrics[0].metrics[1]; + const metric3 = json.resourceMetrics[0].scopeMetrics[0].metrics[2]; assert.ok(typeof metric1 !== 'undefined', "counter doesn't exist"); ensureCounterIsCorrect( diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/tsconfig.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/tsconfig.json index db701b3310..69019ed409 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/tsconfig.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/tsconfig.json @@ -15,9 +15,6 @@ { "path": "../../../packages/opentelemetry-resources" }, - { - "path": "../exporter-trace-otlp-http" - }, { "path": "../opentelemetry-api-metrics" }, @@ -26,6 +23,9 @@ }, { "path": "../otlp-exporter-base" + }, + { + "path": "../otlp-transformer" } ] } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/README.md b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/README.md index cbf16261e7..c0e3dde68f 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/README.md +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/README.md @@ -3,7 +3,8 @@ [![NPM Published Version][npm-img]][npm-url] [![Apache License][license-image]][license-image] -This module provides exporter for node to be used with [opentelemetry-collector][opentelemetry-collector-url] - last tested with version **0.25.0**. +This module provides exporter for node to be used with [opentelemetry-collector][opentelemetry-collector-url]. +Compatible with [opentelemetry-collector][opentelemetry-collector-url] versions `>=0.32 <=0.50`. ## Installation @@ -20,20 +21,21 @@ To see sample code and documentation for the traces exporter, visit the [Collect ## Metrics in Node - PROTO over http ```js -const { MeterProvider } = require('@opentelemetry/sdk-metrics-base'); +const { MeterProvider, PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics-base'); const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-proto'); const collectorOptions = { url: '', // url is optional and can be omitted - default is http://localhost:4318/v1/metrics }; const exporter = new OTLPMetricExporter(collectorOptions); +const meterProvider = new MeterProvider({}); -// Register the exporter -const meter = new MeterProvider({ - exporter, - interval: 60000, -}).getMeter('example-meter'); +meterProvider.addMetricReader(new PeriodicExportingMetricReader({ + exporter: metricExporter, + exportIntervalMillis: 1000, +})); // Now, start recording data +const meter = meterProvider.getMeter('example-meter'); const counter = meter.createCounter('metric_name'); counter.add(10, { 'key': 'value' }); @@ -41,9 +43,8 @@ counter.add(10, { 'key': 'value' }); ## Running opentelemetry-collector locally to see the metrics -1. Go to examples/otlp-exporter-node -2. run `npm run docker:start` -3. Open page at `http://localhost:9411/zipkin/` to observe the metrics +1. Go to `examples/otlp-exporter-node` +2. Follow the instructions there to observe the metrics. ## Useful links diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json index a69ed4d616..dafcc23c94 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json @@ -72,7 +72,7 @@ "@opentelemetry/exporter-metrics-otlp-http": "0.28.0", "@opentelemetry/otlp-proto-exporter-base": "0.28.0", "@opentelemetry/otlp-exporter-base": "0.28.0", - "@opentelemetry/exporter-trace-otlp-http": "0.28.0", + "@opentelemetry/otlp-transformer": "0.28.0", "@opentelemetry/resources": "1.2.0", "@opentelemetry/sdk-metrics-base": "0.28.0", "protobufjs": "^6.9.0" diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/src/OTLPMetricExporter.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/src/OTLPMetricExporter.ts index 105f0c2cf3..eee22e33d9 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/src/OTLPMetricExporter.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/src/OTLPMetricExporter.ts @@ -13,28 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import { - otlpTypes -} from '@opentelemetry/exporter-trace-otlp-http'; import { defaultExporterTemporality, defaultOptions, - OTLPMetricExporterOptions, - toOTLPExportMetricServiceRequest + OTLPMetricExporterOptions } from '@opentelemetry/exporter-metrics-otlp-http'; import { ServiceClientType, OTLPProtoExporterNodeBase } from '@opentelemetry/otlp-proto-exporter-base'; import { getEnv, baggageUtils} from '@opentelemetry/core'; import { AggregationTemporality, ResourceMetrics} from '@opentelemetry/sdk-metrics-base'; import { OTLPMetricExporterBase } from '@opentelemetry/exporter-metrics-otlp-http'; import { appendResourcePathToUrlIfNotPresent, OTLPExporterNodeConfigBase } from '@opentelemetry/otlp-exporter-base'; +import { createExportMetricsServiceRequest, IExportMetricsServiceRequest } from '@opentelemetry/otlp-transformer'; const DEFAULT_COLLECTOR_RESOURCE_PATH = '/v1/metrics'; const DEFAULT_COLLECTOR_URL = `http://localhost:4318${DEFAULT_COLLECTOR_RESOURCE_PATH}`; class OTLPMetricExporterNodeProxy extends OTLPProtoExporterNodeBase { + IExportMetricsServiceRequest> { protected readonly _aggregationTemporality: AggregationTemporality; constructor(config: OTLPExporterNodeConfigBase & OTLPMetricExporterOptions = defaultOptions) { @@ -48,13 +44,10 @@ class OTLPMetricExporterNodeProxy extends OTLPProtoExporterNodeBase { }); it('should successfully send metrics', done => { - collectorExporter.export(metrics, () => { - }); - sinon.stub(http, 'request').returns({ write: () => {}, on: () => {}, end: (...writeArgs: any[]) => { const ExportTraceServiceRequestProto = getExportRequestProto(); const data = ExportTraceServiceRequestProto?.decode(writeArgs[0]); - const json = data?.toJSON() as otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest; + const json = data?.toJSON() as IExportMetricsServiceRequest; - const metric1 = - json.resourceMetrics[0].instrumentationLibraryMetrics[0].metrics[0]; - const metric2 = - json.resourceMetrics[0].instrumentationLibraryMetrics[0].metrics[1]; - const metric3 = - json.resourceMetrics[0].instrumentationLibraryMetrics[0].metrics[2]; + const metric1 = json.resourceMetrics[0].scopeMetrics[0].metrics[0]; + const metric2 = json.resourceMetrics[0].scopeMetrics[0].metrics[1]; + const metric3 = json.resourceMetrics[0].scopeMetrics[0].metrics[2]; assert.ok(typeof metric1 !== 'undefined', "counter doesn't exist"); ensureExportedCounterIsCorrect( metric1, - metric1.intSum?.dataPoints[0].timeUnixNano, - metric1.intSum?.dataPoints[0].startTimeUnixNano + metric1.sum?.dataPoints[0].timeUnixNano, + metric1.sum?.dataPoints[0].startTimeUnixNano ); assert.ok(typeof metric2 !== 'undefined', "observable gauge doesn't exist"); ensureExportedObservableGaugeIsCorrect( metric2, - metric2.doubleGauge?.dataPoints[0].timeUnixNano, - metric2.doubleGauge?.dataPoints[0].startTimeUnixNano + metric2.gauge?.dataPoints[0].timeUnixNano, + metric2.gauge?.dataPoints[0].startTimeUnixNano ); assert.ok( typeof metric3 !== 'undefined', @@ -209,17 +201,20 @@ describe('OTLPMetricExporter - node with proto over http', () => { ); ensureExportedHistogramIsCorrect( metric3, - metric3.intHistogram?.dataPoints[0].timeUnixNano, - metric3.intHistogram?.dataPoints[0].startTimeUnixNano, + metric3.histogram?.dataPoints[0].timeUnixNano, + metric3.histogram?.dataPoints[0].startTimeUnixNano, [0, 100], ['0', '2', '0'] ); ensureExportMetricsServiceRequestIsSet(json); - done(); }, } as any); + + collectorExporter.export(metrics, result => { + done(result.error); + }); }); it('should log the successful message', done => { diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/metricsHelper.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/metricsHelper.ts index 8daa508726..1006f34804 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/metricsHelper.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/metricsHelper.ts @@ -20,15 +20,15 @@ import { Histogram, ValueType, } from '@opentelemetry/api-metrics'; -import { otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; import { Resource } from '@opentelemetry/resources'; import * as assert from 'assert'; -import { Stream } from 'stream'; import { ExplicitBucketHistogramAggregation, MeterProvider, MetricReader } from '@opentelemetry/sdk-metrics-base'; +import { IExportMetricsServiceRequest, IKeyValue, IMetric } from '@opentelemetry/otlp-transformer'; +import { Stream } from 'stream'; export class TestMetricReader extends MetricReader { protected onForceFlush(): Promise { @@ -103,7 +103,7 @@ export function mockHistogram(): Histogram { } export function ensureProtoAttributesAreCorrect( - attributes: otlpTypes.opentelemetryProto.common.v1.KeyValue[] + attributes: IKeyValue[] ) { assert.deepStrictEqual( attributes, @@ -120,7 +120,7 @@ export function ensureProtoAttributesAreCorrect( } export function ensureExportedCounterIsCorrect( - metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, + metric: IMetric, time?: number, startTime?: number ) { @@ -128,10 +128,10 @@ export function ensureExportedCounterIsCorrect( name: 'int-counter', description: 'sample counter description', unit: '1', - intSum: { + sum: { dataPoints: [ { - value: '1', + asInt: '1', startTimeUnixNano: String(startTime), timeUnixNano: String(time), }, @@ -143,7 +143,7 @@ export function ensureExportedCounterIsCorrect( } export function ensureExportedObservableGaugeIsCorrect( - metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, + metric: IMetric, time?: number, startTime?: number ) { @@ -151,10 +151,10 @@ export function ensureExportedObservableGaugeIsCorrect( name: 'double-observable-gauge', description: 'sample observable gauge description', unit: '1', - doubleGauge: { + gauge: { dataPoints: [ { - value: 6, + asDouble: 6, startTimeUnixNano: String(startTime), timeUnixNano: String(time), }, @@ -164,7 +164,7 @@ export function ensureExportedObservableGaugeIsCorrect( } export function ensureExportedHistogramIsCorrect( - metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, + metric: IMetric, time?: number, startTime?: number, explicitBounds: number[] = [Infinity], @@ -174,10 +174,10 @@ export function ensureExportedHistogramIsCorrect( name: 'int-histogram', description: 'sample histogram description', unit: '1', - intHistogram: { + histogram: { dataPoints: [ { - sum: '21', + sum: 21, count: '2', startTimeUnixNano: String(startTime), timeUnixNano: String(time), @@ -191,7 +191,7 @@ export function ensureExportedHistogramIsCorrect( } export function ensureExportMetricsServiceRequestIsSet( - json: otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest + json: IExportMetricsServiceRequest ) { const resourceMetrics = json.resourceMetrics; assert.strictEqual( @@ -201,25 +201,15 @@ export function ensureExportMetricsServiceRequestIsSet( ); const resource = resourceMetrics[0].resource; - assert.strictEqual(!!resource, true, 'resource is missing'); + assert.ok(resource, 'resource is missing'); - const instrumentationLibraryMetrics = - resourceMetrics[0].instrumentationLibraryMetrics; - assert.strictEqual( - instrumentationLibraryMetrics && instrumentationLibraryMetrics.length, - 1, - 'instrumentationLibraryMetrics is missing' - ); + const scopeMetrics = resourceMetrics[0].scopeMetrics; + assert.strictEqual(scopeMetrics?.length, 1, 'scopeMetrics is missing'); - const instrumentationLibrary = - instrumentationLibraryMetrics[0].instrumentationLibrary; - assert.strictEqual( - !!instrumentationLibrary, - true, - 'instrumentationLibrary is missing' - ); + const scope = scopeMetrics[0].scope; + assert.ok(scope, 'scope is missing'); - const metrics = resourceMetrics[0].instrumentationLibraryMetrics[0].metrics; + const metrics = resourceMetrics[0].scopeMetrics[0].metrics; assert.strictEqual(metrics.length, 3, 'Metrics are missing'); } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/tsconfig.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/tsconfig.json index d81d9c0a67..1023f08037 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/tsconfig.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/tsconfig.json @@ -15,9 +15,6 @@ { "path": "../../../packages/opentelemetry-resources" }, - { - "path": "../exporter-trace-otlp-http" - }, { "path": "../opentelemetry-api-metrics" }, @@ -32,6 +29,9 @@ }, { "path": "../otlp-proto-exporter-base" + }, + { + "path": "../otlp-transformer" } ] } diff --git a/experimental/packages/otlp-grpc-exporter-base/package.json b/experimental/packages/otlp-grpc-exporter-base/package.json index e3c34d8c02..26486d265c 100644 --- a/experimental/packages/otlp-grpc-exporter-base/package.json +++ b/experimental/packages/otlp-grpc-exporter-base/package.json @@ -64,8 +64,8 @@ "ts-mocha": "9.0.2", "typescript": "4.4.4", "@opentelemetry/sdk-trace-base": "1.2.0", - "@opentelemetry/resources": "1.2.0", - "@opentelemetry/exporter-trace-otlp-http": "0.28.0" + "@opentelemetry/otlp-transformer": "0.28.0", + "@opentelemetry/resources": "1.2.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" diff --git a/experimental/packages/otlp-grpc-exporter-base/protos b/experimental/packages/otlp-grpc-exporter-base/protos index 59c488bfb8..9657f2f707 160000 --- a/experimental/packages/otlp-grpc-exporter-base/protos +++ b/experimental/packages/otlp-grpc-exporter-base/protos @@ -1 +1 @@ -Subproject commit 59c488bfb8fb6d0458ad6425758b70259ff4a2bd +Subproject commit 9657f2f70739433d27412fe99d81479676814e26 diff --git a/experimental/packages/otlp-grpc-exporter-base/submodule.md b/experimental/packages/otlp-grpc-exporter-base/submodule.md index 68f912dcd9..fd5d1a7a47 100644 --- a/experimental/packages/otlp-grpc-exporter-base/submodule.md +++ b/experimental/packages/otlp-grpc-exporter-base/submodule.md @@ -40,7 +40,7 @@ the latest sha when this guide was written is `59c488bfb8fb6d0458ad6425758b70259 8. Now thing which is very important. You have to commit this to apply these changes ```shell script - git commit -am "chore: updating submodule for opentelemetry-proto" + git commit -am "chore: updating submodule for otlp-grpc-exporter-base" ``` 9. If you look now at git log you will notice that the folder `protos` has been changed and it will show what was the previous sha and what is current one. diff --git a/experimental/packages/otlp-grpc-exporter-base/test/traceHelper.ts b/experimental/packages/otlp-grpc-exporter-base/test/traceHelper.ts index 83578d3c12..e8b0204d61 100644 --- a/experimental/packages/otlp-grpc-exporter-base/test/traceHelper.ts +++ b/experimental/packages/otlp-grpc-exporter-base/test/traceHelper.ts @@ -15,12 +15,12 @@ */ import { SpanStatusCode, TraceFlags } from '@opentelemetry/api'; -import { otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; import { Resource } from '@opentelemetry/resources'; import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; import * as assert from 'assert'; import * as grpc from '@grpc/grpc-js'; import { VERSION } from '@opentelemetry/core'; +import { IEvent, IKeyValue, ILink, IResource, ISpan } from '@opentelemetry/otlp-transformer'; const traceIdArr = [ 31, @@ -100,9 +100,7 @@ export const mockedReadableSpan: ReadableSpan = { instrumentationLibrary: { name: 'default', version: '0.0.1' }, }; -export function ensureExportedEventsAreCorrect( - events: otlpTypes.opentelemetryProto.trace.v1.Span.Event[] -) { +export function ensureExportedEventsAreCorrect(events: IEvent[]) { assert.deepStrictEqual( events, [ @@ -159,9 +157,7 @@ export function ensureExportedEventsAreCorrect( ); } -export function ensureExportedAttributesAreCorrect( - attributes: otlpTypes.opentelemetryProto.common.v1.KeyValue[] -) { +export function ensureExportedAttributesAreCorrect(attributes: IKeyValue[]) { assert.deepStrictEqual( attributes, [ @@ -177,9 +173,7 @@ export function ensureExportedAttributesAreCorrect( ); } -export function ensureExportedLinksAreCorrect( - attributes: otlpTypes.opentelemetryProto.trace.v1.Span.Link[] -) { +export function ensureExportedLinksAreCorrect(attributes: ILink[]) { assert.deepStrictEqual( attributes, [ @@ -203,9 +197,7 @@ export function ensureExportedLinksAreCorrect( ); } -export function ensureExportedSpanIsCorrect( - span: otlpTypes.opentelemetryProto.trace.v1.Span -) { +export function ensureExportedSpanIsCorrect(span: ISpan) { if (span.attributes) { ensureExportedAttributesAreCorrect(span.attributes); } @@ -261,9 +253,7 @@ export function ensureExportedSpanIsCorrect( ); } -export function ensureResourceIsCorrect( - resource: otlpTypes.opentelemetryProto.resource.v1.Resource -) { +export function ensureResourceIsCorrect(resource: IResource) { assert.deepStrictEqual(resource, { attributes: [ { diff --git a/experimental/packages/otlp-grpc-exporter-base/tsconfig.json b/experimental/packages/otlp-grpc-exporter-base/tsconfig.json index c8e54e3690..087c804079 100644 --- a/experimental/packages/otlp-grpc-exporter-base/tsconfig.json +++ b/experimental/packages/otlp-grpc-exporter-base/tsconfig.json @@ -19,10 +19,10 @@ "path": "../../../packages/opentelemetry-sdk-trace-base" }, { - "path": "../exporter-trace-otlp-http" + "path": "../otlp-exporter-base" }, { - "path": "../otlp-exporter-base" + "path": "../otlp-transformer" } ] } diff --git a/experimental/packages/otlp-proto-exporter-base/protos b/experimental/packages/otlp-proto-exporter-base/protos index 59c488bfb8..9657f2f707 160000 --- a/experimental/packages/otlp-proto-exporter-base/protos +++ b/experimental/packages/otlp-proto-exporter-base/protos @@ -1 +1 @@ -Subproject commit 59c488bfb8fb6d0458ad6425758b70259ff4a2bd +Subproject commit 9657f2f70739433d27412fe99d81479676814e26 diff --git a/experimental/packages/otlp-proto-exporter-base/submodule.md b/experimental/packages/otlp-proto-exporter-base/submodule.md index c2838b1ba5..2b3be0c7b7 100644 --- a/experimental/packages/otlp-proto-exporter-base/submodule.md +++ b/experimental/packages/otlp-proto-exporter-base/submodule.md @@ -40,7 +40,7 @@ Knowing this if you want to change the submodule to point to a different version 8. Now thing which is very important. You have to commit this to apply these changes ```shell script - git commit -am "chore: updating submodule for opentelemetry-proto" + git commit -am "chore: updating submodule for otlp-proto-exporter-base" ``` 9. If you look now at git log you will notice that the folder `protos` has been changed and it will show what was the previous sha and what is current one. diff --git a/experimental/packages/otlp-transformer/package.json b/experimental/packages/otlp-transformer/package.json index 9ec6ec2d84..540c5af84f 100644 --- a/experimental/packages/otlp-transformer/package.json +++ b/experimental/packages/otlp-transformer/package.json @@ -1,14 +1,15 @@ { "name": "@opentelemetry/otlp-transformer", - "private": true, + "private": false, "publishConfig": { - "access": "restricted" + "access": "public" }, "version": "0.28.0", "description": "Transform OpenTelemetry SDK data into OTLP", "module": "build/esm/index.js", "esnext": "build/esnext/index.js", "types": "build/src/index.d.ts", + "main": "build/src/index.js", "repository": "open-telemetry/opentelemetry-js", "scripts": { "compile": "tsc --build tsconfig.all.json", diff --git a/experimental/packages/otlp-transformer/src/common/types.ts b/experimental/packages/otlp-transformer/src/common/types.ts index c706015b84..7ca7ecd1a3 100644 --- a/experimental/packages/otlp-transformer/src/common/types.ts +++ b/experimental/packages/otlp-transformer/src/common/types.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -/** Properties of an InstrumentationLibrary. */ -export interface IInstrumentationLibrary { - /** InstrumentationLibrary name */ +/** Properties of an InstrumentationScope. */ +export interface IInstrumentationScope { + /** InstrumentationScope name */ name: string - /** InstrumentationLibrary version */ + /** InstrumentationScope version */ version?: string; } diff --git a/experimental/packages/otlp-transformer/src/index.ts b/experimental/packages/otlp-transformer/src/index.ts index b7052c8f54..51408c10d8 100644 --- a/experimental/packages/otlp-transformer/src/index.ts +++ b/experimental/packages/otlp-transformer/src/index.ts @@ -15,9 +15,9 @@ */ export * from './common/types'; -// export * from './metrics/types'; +export * from './metrics/types'; export * from './resource/types'; export * from './trace/types'; export { createExportTraceServiceRequest } from './trace'; -// export { createExportMetricsServiceRequest } from './metrics'; +export { createExportMetricsServiceRequest } from './metrics'; diff --git a/experimental/packages/otlp-transformer/src/metrics/index.ts b/experimental/packages/otlp-transformer/src/metrics/index.ts index 9022048c29..494d4b6c4a 100644 --- a/experimental/packages/otlp-transformer/src/metrics/index.ts +++ b/experimental/packages/otlp-transformer/src/metrics/index.ts @@ -14,30 +14,13 @@ * limitations under the License. */ import type { ResourceMetrics } from '@opentelemetry/sdk-metrics-base'; -import { toAttributes } from '../common/internal'; -import { toMetric } from './internal'; import type { IExportMetricsServiceRequest } from './types'; import { AggregationTemporality } from '@opentelemetry/sdk-metrics-base'; +import { toResourceMetrics } from './internal'; -export function createExportMetricsServiceRequest(resourceMetrics: ResourceMetrics, - aggregationTemporality: AggregationTemporality): IExportMetricsServiceRequest | null { +export function createExportMetricsServiceRequest(resourceMetrics: ResourceMetrics[], + aggregationTemporality: AggregationTemporality): IExportMetricsServiceRequest { return { - resourceMetrics: [{ - resource: { - attributes: toAttributes(resourceMetrics.resource.attributes), - droppedAttributesCount: 0 - }, - schemaUrl: undefined, // TODO: Schema Url does not exist yet in the SDK. - instrumentationLibraryMetrics: Array.from(resourceMetrics.instrumentationLibraryMetrics.map(metrics => { - return { - instrumentationLibrary: { - name: metrics.instrumentationLibrary.name, - version: metrics.instrumentationLibrary.version, - }, - metrics: metrics.metrics.map(metricData => toMetric(metricData, aggregationTemporality)), - schemaUrl: metrics.instrumentationLibrary.schemaUrl - }; - })) - }] + resourceMetrics: resourceMetrics.map(metrics => toResourceMetrics(metrics, aggregationTemporality)) }; } diff --git a/experimental/packages/otlp-transformer/src/metrics/internal.ts b/experimental/packages/otlp-transformer/src/metrics/internal.ts index 1dab2f4417..48e592a1a5 100644 --- a/experimental/packages/otlp-transformer/src/metrics/internal.ts +++ b/experimental/packages/otlp-transformer/src/metrics/internal.ts @@ -20,12 +20,47 @@ import { DataPoint, DataPointType, Histogram, + InstrumentationLibraryMetrics, InstrumentType, - MetricData + MetricData, + ResourceMetrics } from '@opentelemetry/sdk-metrics-base'; import { toAttributes } from '../common/internal'; -import { EAggregationTemporality, IHistogramDataPoint, IMetric, INumberDataPoint } from './types'; +import { + EAggregationTemporality, + IHistogramDataPoint, + IMetric, + INumberDataPoint, + IResourceMetrics, + IScopeMetrics +} from './types'; + +export function toResourceMetrics(resourceMetrics: ResourceMetrics, + aggregationTemporality: AggregationTemporality): IResourceMetrics { + return { + resource: { + attributes: toAttributes(resourceMetrics.resource.attributes), + droppedAttributesCount: 0 + }, + schemaUrl: undefined, // TODO: Schema Url does not exist yet in the SDK. + scopeMetrics: toScopeMetrics(resourceMetrics.instrumentationLibraryMetrics, aggregationTemporality) + }; +} +export function toScopeMetrics(instrumentationLibraryMetrics: InstrumentationLibraryMetrics[], + aggregationTemporality: AggregationTemporality): IScopeMetrics[]{ + return Array.from(instrumentationLibraryMetrics.map(metrics => { + const scopeMetrics : IScopeMetrics = { + scope: { + name: metrics.instrumentationLibrary.name, + version: metrics.instrumentationLibrary.version, + }, + metrics: metrics.metrics.map(metricData => toMetric(metricData, aggregationTemporality)), + schemaUrl: metrics.instrumentationLibrary.schemaUrl + }; + return scopeMetrics; + })); +} export function toMetric(metricData: MetricData, metricTemporality: AggregationTemporality): IMetric { const out: IMetric = { diff --git a/experimental/packages/otlp-transformer/src/metrics/types.ts b/experimental/packages/otlp-transformer/src/metrics/types.ts index b5900c31e0..0e98ff2a25 100644 --- a/experimental/packages/otlp-transformer/src/metrics/types.ts +++ b/experimental/packages/otlp-transformer/src/metrics/types.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { IInstrumentationLibrary, IKeyValue } from '../common/types'; +import { IInstrumentationScope, IKeyValue } from '../common/types'; import { IResource } from '../resource/types'; /** Properties of an ExportMetricsServiceRequest. */ @@ -23,30 +23,29 @@ export interface IExportMetricsServiceRequest { resourceMetrics: IResourceMetrics[] } - /** Properties of a ResourceMetrics. */ export interface IResourceMetrics { + /** ResourceMetrics resource */ resource?: IResource; - /** ResourceMetrics instrumentationLibraryMetrics */ - instrumentationLibraryMetrics: IInstrumentationLibraryMetrics[] + /** ResourceMetrics scopeMetrics */ + scopeMetrics: IScopeMetrics[] /** ResourceMetrics schemaUrl */ schemaUrl?: string; } +/** Properties of an IScopeMetrics. */ +export interface IScopeMetrics { -/** Properties of an InstrumentationLibraryMetrics. */ -export interface IInstrumentationLibraryMetrics { - - /** InstrumentationLibraryMetrics instrumentationLibrary */ - instrumentationLibrary?: IInstrumentationLibrary; + /** ScopeMetrics scope */ + scope?: IInstrumentationScope; - /** InstrumentationLibraryMetrics metrics */ + /** ScopeMetrics metrics */ metrics: IMetric[]; - /** InstrumentationLibraryMetrics schemaUrl */ + /** ScopeMetrics schemaUrl */ schemaUrl?: string; } @@ -100,7 +99,7 @@ export interface ISum { /** Properties of a Histogram. */ export interface IHistogram { /** Histogram dataPoints */ - dataPoints?: IHistogramDataPoint[] + dataPoints: IHistogramDataPoint[] /** Histogram aggregationTemporality */ aggregationTemporality?: EAggregationTemporality diff --git a/experimental/packages/otlp-transformer/src/trace/index.ts b/experimental/packages/otlp-transformer/src/trace/index.ts index 1329fe7f03..0601f017a9 100644 --- a/experimental/packages/otlp-transformer/src/trace/index.ts +++ b/experimental/packages/otlp-transformer/src/trace/index.ts @@ -13,46 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { InstrumentationLibrary } from '@opentelemetry/core'; import type { Resource } from '@opentelemetry/resources'; import type { ReadableSpan } from '@opentelemetry/sdk-trace-base'; import { toAttributes } from '../common/internal'; import { sdkSpanToOtlpSpan } from './internal'; -import { IExportTraceServiceRequest } from './types'; +import { IExportTraceServiceRequest, IResourceSpans, IScopeSpans } from './types'; -export function createExportTraceServiceRequest(spans: ReadableSpan[]): IExportTraceServiceRequest | null { +export function createExportTraceServiceRequest(spans: ReadableSpan[], useHex?: boolean): IExportTraceServiceRequest { return { - resourceSpans: spanRecordsToResourceSpans(spans).map(({ resource, resourceSpans, resourceSchemaUrl }) => ({ - resource: { - attributes: toAttributes(resource.attributes), - droppedAttributesCount: 0, - }, - instrumentationLibrarySpans: resourceSpans.map(({ instrumentationLibrary, instrumentationLibrarySpans, librarySchemaUrl }) => ({ - instrumentationLibrary, - spans: instrumentationLibrarySpans.map(sdkSpanToOtlpSpan), - schemaUrl: librarySchemaUrl, - })), - schemaUrl: resourceSchemaUrl, - })) + resourceSpans: spanRecordsToResourceSpans(spans, useHex) }; } -type ResourceSpans = { - resource: Resource, - resourceSpans: InstrumentationLibrarySpans[], - resourceSchemaUrl?: string, -}; - -type InstrumentationLibrarySpans = { - instrumentationLibrary: InstrumentationLibrary, - instrumentationLibrarySpans: ReadableSpan[], - librarySchemaUrl?: string, -}; - -function spanRecordsToResourceSpans(spans: ReadableSpan[]): ResourceSpans[] { +function createResourceMap(readableSpans: ReadableSpan[]) { const resourceMap: Map> = new Map(); - - for (const record of spans) { + for (const record of readableSpans) { let ilmMap = resourceMap.get(record.resource); if (!ilmMap) { @@ -72,25 +47,45 @@ function spanRecordsToResourceSpans(spans: ReadableSpan[]): ResourceSpans[] { records.push(record); } - const out: ResourceSpans[] = []; + return resourceMap; +} + +function spanRecordsToResourceSpans(readableSpans: ReadableSpan[], useHex?: boolean): IResourceSpans[] { + const resourceMap = createResourceMap(readableSpans); + const out: IResourceSpans[] = []; const entryIterator = resourceMap.entries(); let entry = entryIterator.next(); while (!entry.done) { const [resource, ilmMap] = entry.value; - const resourceSpans: InstrumentationLibrarySpans[] = []; + const scopeResourceSpans: IScopeSpans[] = []; const ilmIterator = ilmMap.values(); let ilmEntry = ilmIterator.next(); while (!ilmEntry.done) { - const instrumentationLibrarySpans = ilmEntry.value; - if (instrumentationLibrarySpans.length > 0) { - const { name, version, schemaUrl } = instrumentationLibrarySpans[0].instrumentationLibrary; - resourceSpans.push({ instrumentationLibrary: { name, version }, instrumentationLibrarySpans, librarySchemaUrl: schemaUrl }); + const scopeSpans = ilmEntry.value; + if (scopeSpans.length > 0) { + const { name, version, schemaUrl } = scopeSpans[0].instrumentationLibrary; + const spans = scopeSpans.map(readableSpan => sdkSpanToOtlpSpan(readableSpan, useHex)); + + scopeResourceSpans.push({ + scope: { name, version }, + spans: spans, + schemaUrl: schemaUrl + }); } ilmEntry = ilmIterator.next(); } // TODO SDK types don't provide resource schema URL at this time - out.push({ resource, resourceSpans }); + const transformedSpans: IResourceSpans = { + resource: { + attributes: toAttributes(resource.attributes), + droppedAttributesCount: 0, + }, + scopeSpans: scopeResourceSpans, + schemaUrl: undefined + }; + + out.push(transformedSpans); entry = entryIterator.next(); } diff --git a/experimental/packages/otlp-transformer/src/trace/internal.ts b/experimental/packages/otlp-transformer/src/trace/internal.ts index 02fe74af96..87a8182c3f 100644 --- a/experimental/packages/otlp-transformer/src/trace/internal.ts +++ b/experimental/packages/otlp-transformer/src/trace/internal.ts @@ -18,16 +18,19 @@ import { hrTimeToNanoseconds } from '@opentelemetry/core'; import type { ReadableSpan, TimedEvent } from '@opentelemetry/sdk-trace-base'; import { toAttributes } from '../common/internal'; import { EStatusCode, IEvent, ILink, ISpan } from './types'; +import * as core from '@opentelemetry/core'; export function sdkSpanToOtlpSpan( span: ReadableSpan, + useHex?: boolean ): ISpan { const ctx = span.spanContext(); const status = span.status; + const parentSpanId = useHex? span.parentSpanId : span.parentSpanId != null? core.hexToBase64(span.parentSpanId): undefined; return { - traceId: ctx.traceId, - spanId: ctx.spanId, - parentSpanId: span.parentSpanId, + traceId: useHex? ctx.traceId : core.hexToBase64(ctx.traceId), + spanId: useHex? ctx.spanId : core.hexToBase64(ctx.spanId), + parentSpanId: parentSpanId, name: span.name, // Span kind is offset by 1 because the API does not define a value for unset kind: span.kind == null ? 0 : span.kind + 1, @@ -42,16 +45,16 @@ export function sdkSpanToOtlpSpan( code: status.code as unknown as EStatusCode, message: status.message, }, - links: span.links.map(toOtlpLink), + links: span.links.map(link => toOtlpLink(link, useHex)), droppedLinksCount: 0, }; } -export function toOtlpLink(link: Link): ILink { +export function toOtlpLink(link: Link, useHex?: boolean): ILink { return { attributes: link.attributes ? toAttributes(link.attributes) : [], - spanId: link.context.spanId, - traceId: link.context.traceId, + spanId: useHex? link.context.spanId : core.hexToBase64(link.context.spanId), + traceId: useHex? link.context.traceId : core.hexToBase64(link.context.traceId), droppedAttributesCount: 0, }; } diff --git a/experimental/packages/otlp-transformer/src/trace/types.ts b/experimental/packages/otlp-transformer/src/trace/types.ts index e14437a55e..a12260c7e4 100644 --- a/experimental/packages/otlp-transformer/src/trace/types.ts +++ b/experimental/packages/otlp-transformer/src/trace/types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { IInstrumentationLibrary, IKeyValue } from '../common/types'; +import { IInstrumentationScope, IKeyValue } from '../common/types'; import { IResource } from '../resource/types'; /** Properties of an ExportTraceServiceRequest. */ @@ -30,27 +30,26 @@ export interface IResourceSpans { /** ResourceSpans resource */ resource?: IResource; - /** ResourceSpans instrumentationLibrarySpans */ - instrumentationLibrarySpans: IInstrumentationLibrarySpans[]; + /** ResourceSpans scopeSpans */ + scopeSpans: IScopeSpans[]; /** ResourceSpans schemaUrl */ schemaUrl?: string; } -/** Properties of an InstrumentationLibrarySpans. */ -export interface IInstrumentationLibrarySpans { +/** Properties of an ScopeSpans. */ +export interface IScopeSpans { - /** InstrumentationLibrarySpans instrumentationLibrary */ - instrumentationLibrary?: IInstrumentationLibrary; + /** IScopeSpans scope */ + scope?: IInstrumentationScope; - /** InstrumentationLibrarySpans spans */ + /** IScopeSpans spans */ spans?: ISpan[] - /** InstrumentationLibrarySpans schemaUrl */ + /** IScopeSpans schemaUrl */ schemaUrl?: (string | null); } - /** Properties of a Span. */ export interface ISpan { /** Span traceId */ diff --git a/experimental/packages/otlp-transformer/test/metrics.test.ts b/experimental/packages/otlp-transformer/test/metrics.test.ts index 37ecdaf560..c1fa7586ed 100644 --- a/experimental/packages/otlp-transformer/test/metrics.test.ts +++ b/experimental/packages/otlp-transformer/test/metrics.test.ts @@ -144,7 +144,7 @@ describe('Metrics', () => { } it('serializes a sum metric record', () => { - const metrics = createResourceMetrics([createCounterData(10)]); + const metrics = [createResourceMetrics([createCounterData(10)])]; const exportRequest = createExportMetricsServiceRequest( metrics, AggregationTemporality.DELTA @@ -166,9 +166,9 @@ describe('Metrics', () => { droppedAttributesCount: 0, }, schemaUrl: undefined, - instrumentationLibraryMetrics: [ + scopeMetrics: [ { - instrumentationLibrary: { + scope: { name: 'mylib', version: '0.1.0', }, @@ -208,7 +208,7 @@ describe('Metrics', () => { it('serializes an observable sum metric record', () => { const exportRequest = createExportMetricsServiceRequest( - createResourceMetrics([createObservableCounterData(10)]), + [createResourceMetrics([createObservableCounterData(10)])], AggregationTemporality.DELTA ); assert.ok(exportRequest); @@ -228,9 +228,9 @@ describe('Metrics', () => { droppedAttributesCount: 0, }, schemaUrl: undefined, - instrumentationLibraryMetrics: [ + scopeMetrics: [ { - instrumentationLibrary: { + scope: { name: 'mylib', version: '0.1.0', }, @@ -270,7 +270,7 @@ describe('Metrics', () => { it('serializes a gauge metric record', () => { const exportRequest = createExportMetricsServiceRequest( - createResourceMetrics([createObservableGaugeData(10.5)]), + [createResourceMetrics([createObservableGaugeData(10.5)])], AggregationTemporality.DELTA ); assert.ok(exportRequest); @@ -290,9 +290,9 @@ describe('Metrics', () => { droppedAttributesCount: 0, }, schemaUrl: undefined, - instrumentationLibraryMetrics: [ + scopeMetrics: [ { - instrumentationLibrary: { + scope: { name: 'mylib', version: '0.1.0', }, @@ -330,7 +330,7 @@ describe('Metrics', () => { it('serializes a histogram metric record', () => { const exportRequest = createExportMetricsServiceRequest( - createResourceMetrics([createHistogramMetrics(2, 9, [5], [1,1])]), + [createResourceMetrics([createHistogramMetrics(2, 9, [5], [1,1])])], AggregationTemporality.CUMULATIVE ); assert.ok(exportRequest); @@ -350,9 +350,9 @@ describe('Metrics', () => { droppedAttributesCount: 0, }, schemaUrl: undefined, - instrumentationLibraryMetrics: [ + scopeMetrics: [ { - instrumentationLibrary: { + scope: { name: 'mylib', version: '0.1.0', }, diff --git a/experimental/packages/otlp-transformer/test/trace.test.ts b/experimental/packages/otlp-transformer/test/trace.test.ts index 3bd7be6b4c..b47dcaf1e9 100644 --- a/experimental/packages/otlp-transformer/test/trace.test.ts +++ b/experimental/packages/otlp-transformer/test/trace.test.ts @@ -14,17 +14,103 @@ * limitations under the License. */ import { SpanKind, SpanStatusCode } from '@opentelemetry/api'; -import { TraceState } from '@opentelemetry/core'; +import { TraceState, hexToBase64 } from '@opentelemetry/core'; import { Resource } from '@opentelemetry/resources'; import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; import * as assert from 'assert'; import { createExportTraceServiceRequest, ESpanKind, EStatusCode } from '../src'; +function createExpectedSpanJson(useHex: boolean){ + const traceId = useHex? '00000000000000000000000000000001' : hexToBase64('00000000000000000000000000000001'); + const spanId = useHex? '0000000000000002' : hexToBase64('0000000000000002'); + const parentSpanId = useHex? '0000000000000001' : hexToBase64('0000000000000001'); + const linkSpanId = useHex? '0000000000000003' : hexToBase64('0000000000000003'); + const linkTraceId = useHex? '00000000000000000000000000000002' : hexToBase64('00000000000000000000000000000002'); + + return { + resourceSpans: [ + { + resource: { + attributes: [ + { + key: 'resource-attribute', + value: { stringValue: 'resource attribute value' }, + }, + ], + droppedAttributesCount: 0, + }, + schemaUrl: undefined, + scopeSpans: [ + { + scope: { name: 'myLib', version: '0.1.0' }, + spans: [ + { + traceId: traceId, + spanId: spanId, + parentSpanId: parentSpanId, + name: 'span-name', + kind: ESpanKind.SPAN_KIND_CLIENT, + links: [ + { + droppedAttributesCount: 0, + spanId: linkSpanId, + traceId: linkTraceId, + attributes: [ + { + key: 'link-attribute', + value: { + stringValue: 'string value' + } + } + ] + } + ], + // eslint-disable-next-line @typescript-eslint/no-loss-of-precision + startTimeUnixNano: 1640715557342725388, + // eslint-disable-next-line @typescript-eslint/no-loss-of-precision + endTimeUnixNano: 1640715558642725388, + events: [ + { + droppedAttributesCount: 0, + attributes: [ + { + key: 'event-attribute', + value: { + stringValue: 'some string value' + } + } + ], + name: 'some event', + timeUnixNano: 1640715558542725400 + } + ], + attributes: [ + { + key: 'string-attribute', + value: { stringValue: 'some attribute value' }, + }, + ], + droppedAttributesCount: 0, + droppedEventsCount: 0, + droppedLinksCount: 0, + status: { + code: EStatusCode.STATUS_CODE_OK, + message: undefined, + }, + }, + ], + schemaUrl: 'http://url.to.schema', + }, + ], + }, + ], + }; +} + describe('Trace', () => { describe('createExportTraceServiceRequest', () => { let resource: Resource; let span: ReadableSpan; - let expectedSpanJson: any; beforeEach(() => { resource = new Resource({ @@ -79,160 +165,87 @@ describe('Trace', () => { code: SpanStatusCode.OK, }, }; - - expectedSpanJson = { - resourceSpans: [ - { - resource: { - attributes: [ - { - key: 'resource-attribute', - value: { stringValue: 'resource attribute value' }, - }, - ], - droppedAttributesCount: 0, - }, - schemaUrl: undefined, - instrumentationLibrarySpans: [ - { - instrumentationLibrary: { name: 'myLib', version: '0.1.0' }, - spans: [ - { - traceId: '00000000000000000000000000000001', - spanId: '0000000000000002', - parentSpanId: '0000000000000001', - name: 'span-name', - kind: ESpanKind.SPAN_KIND_CLIENT, - links: [ - { - droppedAttributesCount: 0, - spanId: '0000000000000003', - traceId: '00000000000000000000000000000002', - attributes: [ - { - key: 'link-attribute', - value: { - stringValue: 'string value' - } - } - ] - } - ], - // eslint-disable-next-line @typescript-eslint/no-loss-of-precision - startTimeUnixNano: 1640715557342725388, - // eslint-disable-next-line @typescript-eslint/no-loss-of-precision - endTimeUnixNano: 1640715558642725388, - events: [ - { - droppedAttributesCount: 0, - attributes: [ - { - key: 'event-attribute', - value: { - stringValue: 'some string value' - } - } - ], - name: 'some event', - timeUnixNano: 1640715558542725400 - } - ], - attributes: [ - { - key: 'string-attribute', - value: { stringValue: 'some attribute value' }, - }, - ], - droppedAttributesCount: 0, - droppedEventsCount: 0, - droppedLinksCount: 0, - status: { - code: EStatusCode.STATUS_CODE_OK, - message: undefined, - }, - }, - ], - schemaUrl: 'http://url.to.schema', - }, - ], - }, - ], - }; }); it('returns null on an empty list', () => { - assert.deepStrictEqual(createExportTraceServiceRequest([]), { resourceSpans: [] }); + assert.deepStrictEqual(createExportTraceServiceRequest([], true), { resourceSpans: [] }); }); - it('serializes a span', () => { - const exportRequest = createExportTraceServiceRequest([span]); + it('serializes a span with useHex = true', () => { + const exportRequest = createExportTraceServiceRequest([span], true); assert.ok(exportRequest); - assert.deepStrictEqual(exportRequest, expectedSpanJson); + assert.deepStrictEqual(exportRequest, createExpectedSpanJson(true)); }); - it('serializes a span', () => { - const exportRequest = createExportTraceServiceRequest([span]); + it('serializes a span with useHex = false', () => { + const exportRequest = createExportTraceServiceRequest([span], false); assert.ok(exportRequest); - assert.deepStrictEqual(exportRequest, expectedSpanJson); + assert.deepStrictEqual(exportRequest, createExpectedSpanJson(false)); }); - it('serializes a span without a parent', () => { + it('serializes a span without a parent with useHex = true', () => { (span as any).parentSpanId = undefined; - const exportRequest = createExportTraceServiceRequest([span]); + const exportRequest = createExportTraceServiceRequest([span], true); assert.ok(exportRequest); - assert.strictEqual(exportRequest.resourceSpans?.[0].instrumentationLibrarySpans[0].spans?.[0].parentSpanId, undefined); + assert.strictEqual(exportRequest.resourceSpans?.[0].scopeSpans[0].spans?.[0].parentSpanId, undefined); + }); + it('serializes a span without a parent with useHex = false', () => { + (span as any).parentSpanId = undefined; + const exportRequest = createExportTraceServiceRequest([span], false); + assert.ok(exportRequest); + assert.strictEqual(exportRequest.resourceSpans?.[0].scopeSpans[0].spans?.[0].parentSpanId, undefined); }); describe('status code', () => { it('error', () => { span.status.code = SpanStatusCode.ERROR; span.status.message = 'error message'; - const exportRequest = createExportTraceServiceRequest([span]); + const exportRequest = createExportTraceServiceRequest([span], true); assert.ok(exportRequest); - const spanStatus = exportRequest.resourceSpans?.[0].instrumentationLibrarySpans[0].spans?.[0].status; + const spanStatus = exportRequest.resourceSpans?.[0].scopeSpans[0].spans?.[0].status; assert.strictEqual(spanStatus?.code, EStatusCode.STATUS_CODE_ERROR); assert.strictEqual(spanStatus?.message, 'error message'); }); it('unset', () => { span.status.code = SpanStatusCode.UNSET; - const exportRequest = createExportTraceServiceRequest([span]); + const exportRequest = createExportTraceServiceRequest([span], true); assert.ok(exportRequest); - assert.strictEqual(exportRequest.resourceSpans?.[0].instrumentationLibrarySpans[0].spans?.[0].status.code, EStatusCode.STATUS_CODE_UNSET); + assert.strictEqual(exportRequest.resourceSpans?.[0].scopeSpans[0].spans?.[0].status.code, EStatusCode.STATUS_CODE_UNSET); }); }); describe('span kind', () => { it('consumer', () => { (span as any).kind = SpanKind.CONSUMER; - const exportRequest = createExportTraceServiceRequest([span]); + const exportRequest = createExportTraceServiceRequest([span], true); assert.ok(exportRequest); - assert.strictEqual(exportRequest.resourceSpans?.[0].instrumentationLibrarySpans[0].spans?.[0].kind, ESpanKind.SPAN_KIND_CONSUMER); + assert.strictEqual(exportRequest.resourceSpans?.[0].scopeSpans[0].spans?.[0].kind, ESpanKind.SPAN_KIND_CONSUMER); }); it('internal', () => { (span as any).kind = SpanKind.INTERNAL; - const exportRequest = createExportTraceServiceRequest([span]); + const exportRequest = createExportTraceServiceRequest([span], true); assert.ok(exportRequest); - assert.strictEqual(exportRequest.resourceSpans?.[0].instrumentationLibrarySpans[0].spans?.[0].kind, ESpanKind.SPAN_KIND_INTERNAL); + assert.strictEqual(exportRequest.resourceSpans?.[0].scopeSpans[0].spans?.[0].kind, ESpanKind.SPAN_KIND_INTERNAL); }); it('producer', () => { (span as any).kind = SpanKind.PRODUCER; - const exportRequest = createExportTraceServiceRequest([span]); + const exportRequest = createExportTraceServiceRequest([span], true); assert.ok(exportRequest); - assert.strictEqual(exportRequest.resourceSpans?.[0].instrumentationLibrarySpans[0].spans?.[0].kind, ESpanKind.SPAN_KIND_PRODUCER); + assert.strictEqual(exportRequest.resourceSpans?.[0].scopeSpans[0].spans?.[0].kind, ESpanKind.SPAN_KIND_PRODUCER); }); it('server', () => { (span as any).kind = SpanKind.SERVER; - const exportRequest = createExportTraceServiceRequest([span]); + const exportRequest = createExportTraceServiceRequest([span], true); assert.ok(exportRequest); - assert.strictEqual(exportRequest.resourceSpans?.[0].instrumentationLibrarySpans[0].spans?.[0].kind, ESpanKind.SPAN_KIND_SERVER); + assert.strictEqual(exportRequest.resourceSpans?.[0].scopeSpans[0].spans?.[0].kind, ESpanKind.SPAN_KIND_SERVER); }); it('unspecified', () => { (span as any).kind = undefined; - const exportRequest = createExportTraceServiceRequest([span]); + const exportRequest = createExportTraceServiceRequest([span], true); assert.ok(exportRequest); - assert.strictEqual(exportRequest.resourceSpans?.[0].instrumentationLibrarySpans[0].spans?.[0].kind, ESpanKind.SPAN_KIND_UNSPECIFIED); + assert.strictEqual(exportRequest.resourceSpans?.[0].scopeSpans[0].spans?.[0].kind, ESpanKind.SPAN_KIND_UNSPECIFIED); }); }); }); diff --git a/lerna.json b/lerna.json index 7c9b4dc384..98304af897 100644 --- a/lerna.json +++ b/lerna.json @@ -6,6 +6,7 @@ "experimental/packages/*", "experimental/backwards-compatability/*", "integration-tests/*", - "selenium-tests" + "selenium-tests", + "examples/otlp-exporter-node" ] }