From 65427576217f6a0a16de74c454bd2c9f19405bed Mon Sep 17 00:00:00 2001 From: Marc Pichler Date: Wed, 18 Sep 2024 17:30:32 +0200 Subject: [PATCH 1/2] feat(exporters): collapse base class into one --- .../src/OTLPLogExporter.ts | 25 +-- .../src/platform/browser/OTLPLogExporter.ts | 20 +- .../src/platform/node/OTLPLogExporter.ts | 31 ++- .../test/browser/OTLPLogExporter.test.ts | 82 -------- .../test/node/OTLPLogExporter.test.ts | 46 ---- .../src/platform/browser/OTLPLogExporter.ts | 20 +- .../src/platform/node/OTLPLogExporter.ts | 32 ++- .../src/OTLPTraceExporter.ts | 22 +- .../src/platform/browser/OTLPTraceExporter.ts | 22 +- .../src/platform/node/OTLPTraceExporter.ts | 32 +-- .../browser/CollectorTraceExporter.test.ts | 80 +------ .../test/node/CollectorTraceExporter.test.ts | 45 ++-- .../src/platform/browser/OTLPTraceExporter.ts | 20 +- .../src/platform/node/OTLPTraceExporter.ts | 32 ++- .../test/node/OTLPTraceExporter.test.ts | 13 -- .../src/OTLPMetricExporter.ts | 36 ++-- .../src/OTLPMetricExporterBase.ts | 22 +- .../platform/browser/OTLPMetricExporter.ts | 39 ++-- .../src/platform/node/OTLPMetricExporter.ts | 48 ++--- .../browser/CollectorMetricExporter.test.ts | 5 +- .../test/browser/index-webpack.ts | 3 - .../common/CollectorMetricExporter.test.ts | 173 ---------------- .../src/OTLPMetricExporter.ts | 46 ++-- .../test/OTLPMetricExporter.test.ts | 15 +- .../packages/otlp-exporter-base/README.md | 6 +- .../packages/otlp-exporter-base/package.json | 39 +++- .../src/OTLPExporterBase.ts | 104 +--------- .../convert-legacy-browser-http-options.ts | 45 ++++ .../convert-legacy-node-http-options.ts | 79 +++++++ .../create-legacy-browser-delegate.ts | 51 +++++ .../legacy-base-configuration.ts} | 13 +- .../legacy-node-configuration.ts} | 8 +- .../configuration/otlp-http-configuration.ts | 10 + .../src/export-promise-queue.ts | 64 ++++++ .../index.ts => index-browser-http.ts} | 11 +- .../node/index.ts => index-node-http.ts} | 5 +- .../packages/otlp-exporter-base/src/index.ts | 23 +- .../src/otlp-browser-http-export-delegate.ts | 51 +++++ .../src/otlp-export-delegate.ts | 136 ++++++++++++ .../src/otlp-http-export-delegate.ts | 40 ++++ .../src/otlp-network-export-delegate.ts | 39 ++++ .../browser/OTLPExporterBrowserBase.ts | 128 ------------ .../src/platform/node/OTLPExporterNodeBase.ts | 122 ----------- .../node/convert-legacy-agent-options.ts | 46 ---- .../http-exporter-transport.ts | 4 +- .../http-transport-types.ts | 2 +- .../http-transport-utils.ts | 6 +- .../send-beacon-transport.ts | 4 +- .../browser => transport}/xhr-transport.ts | 6 +- .../packages/otlp-exporter-base/src/types.ts | 12 -- .../browser/send-beacon-transport.test.ts | 2 +- .../test/browser/xhr-transport.test.ts | 2 +- .../test/common/CollectorExporter.test.ts | 184 ---------------- .../otlp-http-configuration.test.ts | 1 + .../shared-env-configuration.test.ts | 6 +- .../test/node/http-exporter-transport.test.ts | 2 +- .../otlp-exporter-base/test/node/util.test.ts | 36 ---- .../src/OTLPGRPCExporterNodeBase.ts | 133 ------------ .../convert-legacy-otlp-grpc-options.ts | 54 +++++ .../otlp-grpc-env-configuration.ts | 2 +- .../src/grpc-exporter-transport.ts | 6 + .../otlp-grpc-exporter-base/src/index.ts | 3 +- .../src/otlp-grpc-export-delegate.ts | 43 ++++ .../test/OTLPGRPCExporterNodeBase.test.ts | 196 ------------------ 64 files changed, 933 insertions(+), 1700 deletions(-) delete mode 100644 experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/CollectorMetricExporter.test.ts create mode 100644 experimental/packages/otlp-exporter-base/src/configuration/convert-legacy-browser-http-options.ts create mode 100644 experimental/packages/otlp-exporter-base/src/configuration/convert-legacy-node-http-options.ts create mode 100644 experimental/packages/otlp-exporter-base/src/configuration/create-legacy-browser-delegate.ts rename experimental/packages/otlp-exporter-base/src/{platform/browser/index.ts => configuration/legacy-base-configuration.ts} (66%) rename experimental/packages/otlp-exporter-base/src/{platform/node/types.ts => configuration/legacy-node-configuration.ts} (89%) create mode 100644 experimental/packages/otlp-exporter-base/src/export-promise-queue.ts rename experimental/packages/otlp-exporter-base/src/{platform/index.ts => index-browser-http.ts} (65%) rename experimental/packages/otlp-exporter-base/src/{platform/node/index.ts => index-node-http.ts} (69%) create mode 100644 experimental/packages/otlp-exporter-base/src/otlp-browser-http-export-delegate.ts create mode 100644 experimental/packages/otlp-exporter-base/src/otlp-export-delegate.ts create mode 100644 experimental/packages/otlp-exporter-base/src/otlp-http-export-delegate.ts create mode 100644 experimental/packages/otlp-exporter-base/src/otlp-network-export-delegate.ts delete mode 100644 experimental/packages/otlp-exporter-base/src/platform/browser/OTLPExporterBrowserBase.ts delete mode 100644 experimental/packages/otlp-exporter-base/src/platform/node/OTLPExporterNodeBase.ts delete mode 100644 experimental/packages/otlp-exporter-base/src/platform/node/convert-legacy-agent-options.ts rename experimental/packages/otlp-exporter-base/src/{platform/node => transport}/http-exporter-transport.ts (95%) rename experimental/packages/otlp-exporter-base/src/{platform/node => transport}/http-transport-types.ts (94%) rename experimental/packages/otlp-exporter-base/src/{platform/node => transport}/http-transport-utils.ts (96%) rename experimental/packages/otlp-exporter-base/src/{platform/browser => transport}/send-beacon-transport.ts (93%) rename experimental/packages/otlp-exporter-base/src/{platform/browser => transport}/xhr-transport.ts (94%) delete mode 100644 experimental/packages/otlp-exporter-base/test/common/CollectorExporter.test.ts delete mode 100644 experimental/packages/otlp-exporter-base/test/node/util.test.ts delete mode 100644 experimental/packages/otlp-grpc-exporter-base/src/OTLPGRPCExporterNodeBase.ts create mode 100644 experimental/packages/otlp-grpc-exporter-base/src/configuration/convert-legacy-otlp-grpc-options.ts create mode 100644 experimental/packages/otlp-grpc-exporter-base/src/otlp-grpc-export-delegate.ts delete mode 100644 experimental/packages/otlp-grpc-exporter-base/test/OTLPGRPCExporterNodeBase.test.ts diff --git a/experimental/packages/exporter-logs-otlp-grpc/src/OTLPLogExporter.ts b/experimental/packages/exporter-logs-otlp-grpc/src/OTLPLogExporter.ts index 31f1daf95e..e7e883e1ea 100644 --- a/experimental/packages/exporter-logs-otlp-grpc/src/OTLPLogExporter.ts +++ b/experimental/packages/exporter-logs-otlp-grpc/src/OTLPLogExporter.ts @@ -16,31 +16,28 @@ import { LogRecordExporter, ReadableLogRecord } from '@opentelemetry/sdk-logs'; import { + convertLegacyOtlpGrpcOptions, + createOtlpGrpcExportDelegate, OTLPGRPCExporterConfigNode, - OTLPGRPCExporterNodeBase, } from '@opentelemetry/otlp-grpc-exporter-base'; -import { - IExportLogsServiceResponse, - ProtobufLogsSerializer, -} from '@opentelemetry/otlp-transformer'; +import { ProtobufLogsSerializer } from '@opentelemetry/otlp-transformer'; +import { OTLPExporterBase } from '@opentelemetry/otlp-exporter-base'; /** * OTLP Logs Exporter for Node */ export class OTLPLogExporter - extends OTLPGRPCExporterNodeBase< - ReadableLogRecord, - IExportLogsServiceResponse - > + extends OTLPExporterBase implements LogRecordExporter { constructor(config: OTLPGRPCExporterConfigNode = {}) { super( - config, - ProtobufLogsSerializer, - 'LogsExportService', - '/opentelemetry.proto.collector.logs.v1.LogsService/Export', - 'LOGS' + createOtlpGrpcExportDelegate( + convertLegacyOtlpGrpcOptions(config, 'LOGS'), + ProtobufLogsSerializer, + 'LogsExportService', + '/opentelemetry.proto.collector.logs.v1.LogsService/Export' + ) ); } } diff --git a/experimental/packages/exporter-logs-otlp-http/src/platform/browser/OTLPLogExporter.ts b/experimental/packages/exporter-logs-otlp-http/src/platform/browser/OTLPLogExporter.ts index 3f4f5c60ff..01a0d1f26a 100644 --- a/experimental/packages/exporter-logs-otlp-http/src/platform/browser/OTLPLogExporter.ts +++ b/experimental/packages/exporter-logs-otlp-http/src/platform/browser/OTLPLogExporter.ts @@ -19,27 +19,25 @@ import type { LogRecordExporter, } from '@opentelemetry/sdk-logs'; import type { OTLPExporterConfigBase } from '@opentelemetry/otlp-exporter-base'; -import type { IExportLogsServiceResponse } from '@opentelemetry/otlp-transformer'; -import { OTLPExporterBrowserBase } from '@opentelemetry/otlp-exporter-base'; +import { OTLPExporterBase } from '@opentelemetry/otlp-exporter-base'; import { JsonLogsSerializer } from '@opentelemetry/otlp-transformer'; +import { createLegacyOtlpBrowserExportDelegate } from '@opentelemetry/otlp-exporter-base/browser-http'; /** * Collector Logs Exporter for Web */ export class OTLPLogExporter - extends OTLPExporterBrowserBase + extends OTLPExporterBase implements LogRecordExporter { constructor(config: OTLPExporterConfigBase = {}) { super( - { - ...config, - }, - JsonLogsSerializer, - { - 'Content-Type': 'application/json', - }, - 'v1/logs' + createLegacyOtlpBrowserExportDelegate( + config, + JsonLogsSerializer, + 'v1/logs', + { 'Content-Type': 'application/json' } + ) ); } } diff --git a/experimental/packages/exporter-logs-otlp-http/src/platform/node/OTLPLogExporter.ts b/experimental/packages/exporter-logs-otlp-http/src/platform/node/OTLPLogExporter.ts index 93efa8fed9..28bb25319d 100644 --- a/experimental/packages/exporter-logs-otlp-http/src/platform/node/OTLPLogExporter.ts +++ b/experimental/packages/exporter-logs-otlp-http/src/platform/node/OTLPLogExporter.ts @@ -19,35 +19,30 @@ import type { LogRecordExporter, } from '@opentelemetry/sdk-logs'; import type { OTLPExporterNodeConfigBase } from '@opentelemetry/otlp-exporter-base'; -import type { IExportLogsServiceResponse } from '@opentelemetry/otlp-transformer'; -import { OTLPExporterNodeBase } from '@opentelemetry/otlp-exporter-base'; +import { OTLPExporterBase } from '@opentelemetry/otlp-exporter-base'; import { JsonLogsSerializer } from '@opentelemetry/otlp-transformer'; - import { VERSION } from '../../version'; - -const USER_AGENT = { - 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, -}; +import { + convertLegacyHttpOptions, + createOtlpHttpExportDelegate, +} from '@opentelemetry/otlp-exporter-base/node-http'; /** * Collector Logs Exporter for Node */ export class OTLPLogExporter - extends OTLPExporterNodeBase + extends OTLPExporterBase implements LogRecordExporter { constructor(config: OTLPExporterNodeConfigBase = {}) { super( - { - ...config, - }, - JsonLogsSerializer, - { - ...USER_AGENT, - 'Content-Type': 'application/json', - }, - 'LOGS', - 'v1/logs' + createOtlpHttpExportDelegate( + convertLegacyHttpOptions(config, 'LOGS', 'v1/logs', { + 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, + 'Content-Type': 'application/json', + }), + JsonLogsSerializer + ) ); } } diff --git a/experimental/packages/exporter-logs-otlp-http/test/browser/OTLPLogExporter.test.ts b/experimental/packages/exporter-logs-otlp-http/test/browser/OTLPLogExporter.test.ts index 69f93c6ea9..b02e56f069 100644 --- a/experimental/packages/exporter-logs-otlp-http/test/browser/OTLPLogExporter.test.ts +++ b/experimental/packages/exporter-logs-otlp-http/test/browser/OTLPLogExporter.test.ts @@ -17,15 +17,8 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; import { OTLPLogExporter } from '../../src/platform/browser'; -import { OTLPExporterConfigBase } from '@opentelemetry/otlp-exporter-base'; -import { ReadableLogRecord } from '@opentelemetry/sdk-logs'; -import { mockedReadableLogRecord } from '../logHelper'; -import { ExportResultCode } from '@opentelemetry/core'; describe('OTLPLogExporter', () => { - let collectorExporter: OTLPLogExporter; - let collectorExporterConfig: OTLPExporterConfigBase; - afterEach(() => { sinon.restore(); }); @@ -36,79 +29,4 @@ describe('OTLPLogExporter', () => { assert.ok(exporter instanceof OTLPLogExporter); }); }); - - describe('export - common', () => { - let spySend: any; - beforeEach(() => { - spySend = sinon.stub(OTLPLogExporter.prototype, 'send'); - collectorExporter = new OTLPLogExporter(collectorExporterConfig); - }); - - it('should export spans as otlpTypes.Spans', done => { - const logs: ReadableLogRecord[] = []; - logs.push(Object.assign({}, mockedReadableLogRecord)); - - collectorExporter.export(logs, () => {}); - setTimeout(() => { - const log = spySend.args[0][0][0] as ReadableLogRecord; - assert.deepStrictEqual(logs[0], log); - done(); - }); - assert.strictEqual(spySend.callCount, 1); - }); - - describe('when exporter is shutdown', () => { - it( - 'should not export anything but return callback with code' + - ' "FailedNotRetryable"', - async () => { - const spans: ReadableLogRecord[] = []; - spans.push(Object.assign({}, mockedReadableLogRecord)); - await collectorExporter.shutdown(); - spySend.resetHistory(); - - const callbackSpy = sinon.spy(); - collectorExporter.export(spans, callbackSpy); - const returnCode = callbackSpy.args[0][0]; - - assert.strictEqual( - returnCode.code, - ExportResultCode.FAILED, - 'return value is wrong' - ); - assert.strictEqual(spySend.callCount, 0, 'should not call send'); - } - ); - }); - describe('when an error occurs', () => { - it('should return failed export result', done => { - const spans: ReadableLogRecord[] = []; - spans.push(Object.assign({}, mockedReadableLogRecord)); - spySend.throws({ - code: 100, - details: 'Test error', - metadata: {}, - message: 'Non-retryable', - stack: 'Stack', - }); - const callbackSpy = sinon.spy(); - collectorExporter.export(spans, callbackSpy); - setTimeout(() => { - const returnCode = callbackSpy.args[0][0]; - assert.strictEqual( - returnCode.code, - ExportResultCode.FAILED, - 'return value is wrong' - ); - assert.strictEqual( - returnCode.error.message, - 'Non-retryable', - 'return error message is wrong' - ); - assert.strictEqual(spySend.callCount, 1, 'should call send'); - done(); - }); - }); - }); - }); }); diff --git a/experimental/packages/exporter-logs-otlp-http/test/node/OTLPLogExporter.test.ts b/experimental/packages/exporter-logs-otlp-http/test/node/OTLPLogExporter.test.ts index 726be702fa..dd31888e14 100644 --- a/experimental/packages/exporter-logs-otlp-http/test/node/OTLPLogExporter.test.ts +++ b/experimental/packages/exporter-logs-otlp-http/test/node/OTLPLogExporter.test.ts @@ -30,7 +30,6 @@ import { import { PassThrough, Stream } from 'stream'; import { IExportLogsServiceRequest } from '@opentelemetry/otlp-transformer'; import { ExportResultCode } from '@opentelemetry/core'; -import { VERSION } from '../../src/version'; let fakeRequest: PassThrough; @@ -57,7 +56,6 @@ class MockedResponse extends Stream { } describe('OTLPLogExporter', () => { - let envSource: Record; let collectorExporter: OTLPLogExporter; let collectorExporterConfig: OTLPExporterNodeConfigBase; let logs: ReadableLogRecord[]; @@ -70,55 +68,11 @@ describe('OTLPLogExporter', () => { sinon.restore(); }); - if (global.process?.versions?.node === undefined) { - envSource = globalThis as unknown as Record; - } else { - envSource = process.env as Record; - } - describe('constructor', () => { it('should create an instance', () => { const exporter = new OTLPLogExporter(); assert.ok(exporter instanceof OTLPLogExporter); }); - - it('should include user-agent header by default', () => { - const exporter = new OTLPLogExporter(); - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers'][ - 'User-Agent' - ], - `OTel-OTLP-Exporter-JavaScript/${VERSION}` - ); - }); - - it('should use headers defined via env', () => { - envSource.OTEL_EXPORTER_OTLP_LOGS_HEADERS = 'foo=bar'; - const exporter = new OTLPLogExporter(); - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers']['foo'], - 'bar' - ); - delete envSource.OTEL_EXPORTER_OTLP_LOGS_HEADERS; - }); - - it('should override headers defined via env with headers defined in constructor', () => { - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar,bar=foo'; - const exporter = new OTLPLogExporter({ - headers: { - foo: 'constructor', - }, - }); - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers']['foo'], - 'constructor' - ); - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers']['bar'], - 'foo' - ); - envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; - }); }); describe('export', () => { diff --git a/experimental/packages/exporter-logs-otlp-proto/src/platform/browser/OTLPLogExporter.ts b/experimental/packages/exporter-logs-otlp-proto/src/platform/browser/OTLPLogExporter.ts index 470a40f5fb..91293ce473 100644 --- a/experimental/packages/exporter-logs-otlp-proto/src/platform/browser/OTLPLogExporter.ts +++ b/experimental/packages/exporter-logs-otlp-proto/src/platform/browser/OTLPLogExporter.ts @@ -16,28 +16,28 @@ import { OTLPExporterConfigBase, - OTLPExporterBrowserBase, + OTLPExporterBase, } from '@opentelemetry/otlp-exporter-base'; -import { - IExportLogsServiceResponse, - ProtobufLogsSerializer, -} from '@opentelemetry/otlp-transformer'; +import { ProtobufLogsSerializer } from '@opentelemetry/otlp-transformer'; import { ReadableLogRecord, LogRecordExporter } from '@opentelemetry/sdk-logs'; +import { createLegacyOtlpBrowserExportDelegate } from '@opentelemetry/otlp-exporter-base/browser-http'; /** * Collector Trace Exporter for Web */ export class OTLPLogExporter - extends OTLPExporterBrowserBase + extends OTLPExporterBase implements LogRecordExporter { constructor(config: OTLPExporterConfigBase = {}) { super( - config, - ProtobufLogsSerializer, - { 'Content-Type': 'application/x-protobuf' }, - 'v1/logs' + createLegacyOtlpBrowserExportDelegate( + config, + ProtobufLogsSerializer, + 'v1/logs', + { 'Content-Type': 'application/x-protobuf' } + ) ); } } diff --git a/experimental/packages/exporter-logs-otlp-proto/src/platform/node/OTLPLogExporter.ts b/experimental/packages/exporter-logs-otlp-proto/src/platform/node/OTLPLogExporter.ts index 828a11cbc6..399cb307e8 100644 --- a/experimental/packages/exporter-logs-otlp-proto/src/platform/node/OTLPLogExporter.ts +++ b/experimental/packages/exporter-logs-otlp-proto/src/platform/node/OTLPLogExporter.ts @@ -15,38 +15,34 @@ */ import { + OTLPExporterBase, OTLPExporterConfigBase, - OTLPExporterNodeBase, } from '@opentelemetry/otlp-exporter-base'; +import { ProtobufLogsSerializer } from '@opentelemetry/otlp-transformer'; import { - IExportLogsServiceResponse, - ProtobufLogsSerializer, -} from '@opentelemetry/otlp-transformer'; + convertLegacyHttpOptions, + createOtlpHttpExportDelegate, +} from '@opentelemetry/otlp-exporter-base/node-http'; import { ReadableLogRecord, LogRecordExporter } from '@opentelemetry/sdk-logs'; import { VERSION } from '../../version'; -const USER_AGENT = { - 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, -}; - /** - * Collector Trace Exporter for Node + * OTLP Log Protobuf Exporter for Node.js */ export class OTLPLogExporter - extends OTLPExporterNodeBase + extends OTLPExporterBase implements LogRecordExporter { constructor(config: OTLPExporterConfigBase = {}) { super( - config, - ProtobufLogsSerializer, - { - ...USER_AGENT, - 'Content-Type': 'application/x-protobuf', - }, - 'LOGS', - 'v1/logs' + createOtlpHttpExportDelegate( + convertLegacyHttpOptions(config, 'LOGS', 'v1/logs', { + 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, + 'Content-Type': 'application/x-protobuf', + }), + ProtobufLogsSerializer + ) ); } } diff --git a/experimental/packages/exporter-trace-otlp-grpc/src/OTLPTraceExporter.ts b/experimental/packages/exporter-trace-otlp-grpc/src/OTLPTraceExporter.ts index e785475a1d..daca6f8987 100644 --- a/experimental/packages/exporter-trace-otlp-grpc/src/OTLPTraceExporter.ts +++ b/experimental/packages/exporter-trace-otlp-grpc/src/OTLPTraceExporter.ts @@ -16,28 +16,28 @@ import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'; import { + convertLegacyOtlpGrpcOptions, + createOtlpGrpcExportDelegate, OTLPGRPCExporterConfigNode, - OTLPGRPCExporterNodeBase, } from '@opentelemetry/otlp-grpc-exporter-base'; -import { - IExportTraceServiceResponse, - ProtobufTraceSerializer, -} from '@opentelemetry/otlp-transformer'; +import { ProtobufTraceSerializer } from '@opentelemetry/otlp-transformer'; +import { OTLPExporterBase } from '@opentelemetry/otlp-exporter-base'; /** * OTLP Trace Exporter for Node */ export class OTLPTraceExporter - extends OTLPGRPCExporterNodeBase + extends OTLPExporterBase implements SpanExporter { constructor(config: OTLPGRPCExporterConfigNode = {}) { super( - config, - ProtobufTraceSerializer, - 'TraceExportService', - '/opentelemetry.proto.collector.trace.v1.TraceService/Export', - 'TRACES' + createOtlpGrpcExportDelegate( + convertLegacyOtlpGrpcOptions(config, 'TRACES'), + ProtobufTraceSerializer, + 'TraceExportService', + '/opentelemetry.proto.collector.trace.v1.TraceService/Export' + ) ); } } 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 2e03ef845e..47dff844a0 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 @@ -17,28 +17,26 @@ import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'; import { OTLPExporterConfigBase, - OTLPExporterBrowserBase, + OTLPExporterBase, } from '@opentelemetry/otlp-exporter-base'; -import { - IExportTraceServiceResponse, - JsonTraceSerializer, -} from '@opentelemetry/otlp-transformer'; - -const DEFAULT_COLLECTOR_RESOURCE_PATH = 'v1/traces'; +import { JsonTraceSerializer } from '@opentelemetry/otlp-transformer'; +import { createLegacyOtlpBrowserExportDelegate } from '@opentelemetry/otlp-exporter-base/browser-http'; /** * Collector Trace Exporter for Web */ export class OTLPTraceExporter - extends OTLPExporterBrowserBase + extends OTLPExporterBase implements SpanExporter { constructor(config: OTLPExporterConfigBase = {}) { super( - config, - JsonTraceSerializer, - { 'Content-Type': 'application/json' }, - DEFAULT_COLLECTOR_RESOURCE_PATH + createLegacyOtlpBrowserExportDelegate( + config, + JsonTraceSerializer, + 'v1/traces', + { 'Content-Type': 'application/json' } + ) ); } } 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 8d41df8f0d..9d8b4900a5 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,33 +15,33 @@ */ import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'; -import { OTLPExporterNodeBase } from '@opentelemetry/otlp-exporter-base'; -import { OTLPExporterNodeConfigBase } from '@opentelemetry/otlp-exporter-base'; -import { IExportTraceServiceResponse } from '@opentelemetry/otlp-transformer'; +import { + OTLPExporterNodeConfigBase, + OTLPExporterBase, +} from '@opentelemetry/otlp-exporter-base'; import { VERSION } from '../../version'; import { JsonTraceSerializer } from '@opentelemetry/otlp-transformer'; - -const USER_AGENT = { - 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, -}; +import { + convertLegacyHttpOptions, + createOtlpHttpExportDelegate, +} from '@opentelemetry/otlp-exporter-base/node-http'; /** * Collector Trace Exporter for Node */ export class OTLPTraceExporter - extends OTLPExporterNodeBase + extends OTLPExporterBase implements SpanExporter { constructor(config: OTLPExporterNodeConfigBase = {}) { super( - config, - JsonTraceSerializer, - { - ...USER_AGENT, - 'Content-Type': 'application/json', - }, - 'TRACES', - 'v1/traces' + createOtlpHttpExportDelegate( + convertLegacyHttpOptions(config, 'TRACES', 'v1/traces', { + 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, + 'Content-Type': 'application/json', + }), + JsonTraceSerializer + ) ); } } 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 9049caf60d..00e160dab4 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 @@ -133,9 +133,8 @@ describe('OTLPTraceExporter - web', () => { queueMicrotask(() => { try { - const response: any = spyLoggerDebug.args[2][0]; - assert.strictEqual(response, 'SendBeacon success'); - assert.strictEqual(spyLoggerError.args.length, 0); + sinon.assert.calledWith(spyLoggerDebug, 'SendBeacon success'); + sinon.assert.notCalled(spyLoggerError); done(); } catch (e) { @@ -283,81 +282,6 @@ describe('OTLPTraceExporter - web', () => { }); }); - describe('export - common', () => { - let spySend: any; - beforeEach(() => { - spySend = sinon.stub(OTLPTraceExporter.prototype, 'send'); - collectorTraceExporter = new OTLPTraceExporter(collectorExporterConfig); - }); - - it('should export spans as otlpTypes.Spans', done => { - const spans: ReadableSpan[] = []; - spans.push(Object.assign({}, mockedReadableSpan)); - - collectorTraceExporter.export(spans, () => {}); - setTimeout(() => { - const span1 = spySend.args[0][0][0] as ReadableSpan; - assert.deepStrictEqual(spans[0], span1); - done(); - }); - assert.strictEqual(spySend.callCount, 1); - }); - - describe('when exporter is shutdown', () => { - it( - 'should not export anything but return callback with code' + - ' "FailedNotRetryable"', - async () => { - const spans: ReadableSpan[] = []; - spans.push(Object.assign({}, mockedReadableSpan)); - await collectorTraceExporter.shutdown(); - spySend.resetHistory(); - - const callbackSpy = sinon.spy(); - collectorTraceExporter.export(spans, callbackSpy); - const returnCode = callbackSpy.args[0][0]; - - assert.strictEqual( - returnCode.code, - ExportResultCode.FAILED, - 'return value is wrong' - ); - assert.strictEqual(spySend.callCount, 0, 'should not call send'); - } - ); - }); - describe('when an error occurs', () => { - it('should return failed export result', done => { - const spans: ReadableSpan[] = []; - spans.push(Object.assign({}, mockedReadableSpan)); - spySend.throws({ - code: 100, - details: 'Test error', - metadata: {}, - message: 'Non-retryable', - stack: 'Stack', - }); - const callbackSpy = sinon.spy(); - collectorTraceExporter.export(spans, callbackSpy); - setTimeout(() => { - const returnCode = callbackSpy.args[0][0]; - assert.strictEqual( - returnCode.code, - ExportResultCode.FAILED, - 'return value is wrong' - ); - assert.strictEqual( - returnCode.error.message, - 'Non-retryable', - 'return error message is wrong' - ); - assert.strictEqual(spySend.callCount, 1, 'should call send'); - done(); - }); - }); - }); - }); - describe('export with custom headers', () => { let server: any; const customHeaders = { 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 b36be29534..38e1bf5c2b 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 @@ -14,40 +14,42 @@ * limitations under the License. */ -import { diag, DiagLogger } from '@opentelemetry/api'; -import * as core from '@opentelemetry/core'; +import * as assert from 'assert'; +import * as http from 'http'; +import * as sinon from 'sinon'; +import * as zlib from 'zlib'; +import { PassThrough, Stream } from 'stream'; + +import { diag } from '@opentelemetry/api'; import { CompressionAlgorithm, OTLPExporterError, OTLPExporterNodeConfigBase, } from '@opentelemetry/otlp-exporter-base'; import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; -import * as assert from 'assert'; -import * as http from 'http'; -import * as sinon from 'sinon'; -import { PassThrough, Stream } from 'stream'; -import * as zlib from 'zlib'; +import * as core from '@opentelemetry/core'; +import { IExportTraceServiceRequest } from '@opentelemetry/otlp-transformer'; + +import { MockedResponse } from './nodeHelpers'; import { OTLPTraceExporter } from '../../src/platform/node'; import { ensureExportTraceServiceRequestIsSet, ensureSpanIsCorrect, mockedReadableSpan, } from '../traceHelper'; -import { MockedResponse } from './nodeHelpers'; -import { IExportTraceServiceRequest } from '@opentelemetry/otlp-transformer'; let fakeRequest: PassThrough; const address = 'localhost:1501'; -describe('OTLPTraceExporter - node with json over http', () => { +describe('OTLPTraceExporter - node with json over http', function () { let collectorExporter: OTLPTraceExporter; let collectorExporterConfig: OTLPExporterNodeConfigBase; let stubRequest: sinon.SinonStub; let spySetHeader: sinon.SinonSpy; let spans: ReadableSpan[]; - afterEach(() => { + afterEach(function () { fakeRequest = new Stream.PassThrough(); Object.defineProperty(fakeRequest, 'setTimeout', { value: function (_timeout: number) {}, @@ -55,27 +57,20 @@ describe('OTLPTraceExporter - node with json over http', () => { sinon.restore(); }); - describe('instance', () => { + describe('instance', function () { it('should warn about metadata when using json', () => { const metadata = 'foo'; - // Need to stub/spy on the underlying logger as the "diag" instance is global - const warnStub = sinon.stub(); - const nop = () => {}; - const diagLogger: DiagLogger = { - debug: nop, - error: nop, - info: nop, - verbose: nop, - warn: warnStub, - }; - diag.setLogger(diagLogger); + const warnLoggerSpy = sinon.stub(diag, 'warn'); collectorExporter = new OTLPTraceExporter({ metadata, url: address, } as any); - const args = warnStub.args[0]; - assert.strictEqual(args[0], 'Metadata cannot be set when using http'); + sinon.assert.calledOnce(warnLoggerSpy); + sinon.assert.calledOnceWithExactly( + warnLoggerSpy, + 'Metadata cannot be set when using http' + ); }); }); diff --git a/experimental/packages/exporter-trace-otlp-proto/src/platform/browser/OTLPTraceExporter.ts b/experimental/packages/exporter-trace-otlp-proto/src/platform/browser/OTLPTraceExporter.ts index 85d208b741..7292f6a639 100644 --- a/experimental/packages/exporter-trace-otlp-proto/src/platform/browser/OTLPTraceExporter.ts +++ b/experimental/packages/exporter-trace-otlp-proto/src/platform/browser/OTLPTraceExporter.ts @@ -17,12 +17,10 @@ import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'; import { OTLPExporterConfigBase, - OTLPExporterBrowserBase, + OTLPExporterBase, } from '@opentelemetry/otlp-exporter-base'; -import { - IExportTraceServiceResponse, - ProtobufTraceSerializer, -} from '@opentelemetry/otlp-transformer'; +import { ProtobufTraceSerializer } from '@opentelemetry/otlp-transformer'; +import { createLegacyOtlpBrowserExportDelegate } from '@opentelemetry/otlp-exporter-base/browser-http'; const DEFAULT_COLLECTOR_RESOURCE_PATH = 'v1/traces'; @@ -30,15 +28,17 @@ const DEFAULT_COLLECTOR_RESOURCE_PATH = 'v1/traces'; * Collector Trace Exporter for Web */ export class OTLPTraceExporter - extends OTLPExporterBrowserBase + extends OTLPExporterBase implements SpanExporter { constructor(config: OTLPExporterConfigBase = {}) { super( - config, - ProtobufTraceSerializer, - { 'Content-Type': 'application/x-protobuf' }, - DEFAULT_COLLECTOR_RESOURCE_PATH + createLegacyOtlpBrowserExportDelegate( + config, + ProtobufTraceSerializer, + DEFAULT_COLLECTOR_RESOURCE_PATH, + { 'Content-Type': 'application/x-protobuf' } + ) ); } } diff --git a/experimental/packages/exporter-trace-otlp-proto/src/platform/node/OTLPTraceExporter.ts b/experimental/packages/exporter-trace-otlp-proto/src/platform/node/OTLPTraceExporter.ts index 1e1b35a230..f546fcb643 100644 --- a/experimental/packages/exporter-trace-otlp-proto/src/platform/node/OTLPTraceExporter.ts +++ b/experimental/packages/exporter-trace-otlp-proto/src/platform/node/OTLPTraceExporter.ts @@ -17,35 +17,31 @@ import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'; import { OTLPExporterNodeConfigBase, - OTLPExporterNodeBase, + OTLPExporterBase, } from '@opentelemetry/otlp-exporter-base'; -import { - IExportTraceServiceResponse, - ProtobufTraceSerializer, -} from '@opentelemetry/otlp-transformer'; +import { ProtobufTraceSerializer } from '@opentelemetry/otlp-transformer'; import { VERSION } from '../../version'; - -const USER_AGENT = { - 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, -}; +import { + createOtlpHttpExportDelegate, + convertLegacyHttpOptions, +} from '@opentelemetry/otlp-exporter-base/node-http'; /** * Collector Trace Exporter for Node with protobuf */ export class OTLPTraceExporter - extends OTLPExporterNodeBase + extends OTLPExporterBase implements SpanExporter { constructor(config: OTLPExporterNodeConfigBase = {}) { super( - config, - ProtobufTraceSerializer, - { - ...USER_AGENT, - 'Content-Type': 'application/x-protobuf', - }, - 'TRACES', - 'v1/traces' + createOtlpHttpExportDelegate( + convertLegacyHttpOptions(config, 'TRACES', 'v1/traces', { + 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, + 'Content-Type': 'application/x-protobuf', + }), + ProtobufTraceSerializer + ) ); } } diff --git a/experimental/packages/exporter-trace-otlp-proto/test/node/OTLPTraceExporter.test.ts b/experimental/packages/exporter-trace-otlp-proto/test/node/OTLPTraceExporter.test.ts index b034778e33..355dacac21 100644 --- a/experimental/packages/exporter-trace-otlp-proto/test/node/OTLPTraceExporter.test.ts +++ b/experimental/packages/exporter-trace-otlp-proto/test/node/OTLPTraceExporter.test.ts @@ -36,7 +36,6 @@ import { } from '@opentelemetry/otlp-exporter-base'; import { IExportTraceServiceRequest } from '@opentelemetry/otlp-transformer'; import { Root } from 'protobufjs'; -import { VERSION } from '../../src/version'; import * as path from 'path'; const dir = path.resolve(__dirname, '../../../otlp-transformer/protos'); @@ -69,18 +68,6 @@ describe('OTLPTraceExporter - node with proto over http', () => { sinon.restore(); }); - describe('default behavior for headers', () => { - const exporter = new OTLPTraceExporter(); - it('should include user agent in header', () => { - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers'][ - 'User-Agent' - ], - `OTel-OTLP-Exporter-JavaScript/${VERSION}` - ); - }); - }); - describe('export', () => { beforeEach(() => { collectorExporterConfig = { 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 2602911aa1..074c5e5c63 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/src/OTLPMetricExporter.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/src/OTLPMetricExporter.ts @@ -18,36 +18,26 @@ import { OTLPMetricExporterBase, OTLPMetricExporterOptions, } from '@opentelemetry/exporter-metrics-otlp-http'; -import { ResourceMetrics } from '@opentelemetry/sdk-metrics'; import { + convertLegacyOtlpGrpcOptions, + createOtlpGrpcExportDelegate, OTLPGRPCExporterConfigNode, - OTLPGRPCExporterNodeBase, } from '@opentelemetry/otlp-grpc-exporter-base'; -import { - IExportMetricsServiceResponse, - ProtobufMetricsSerializer, -} from '@opentelemetry/otlp-transformer'; - -class OTLPMetricExporterProxy extends OTLPGRPCExporterNodeBase< - ResourceMetrics, - IExportMetricsServiceResponse -> { - constructor(config?: OTLPGRPCExporterConfigNode & OTLPMetricExporterOptions) { - super( - config, - ProtobufMetricsSerializer, - 'MetricsExportService', - '/opentelemetry.proto.collector.metrics.v1.MetricsService/Export', - 'METRICS' - ); - } -} +import { ProtobufMetricsSerializer } from '@opentelemetry/otlp-transformer'; /** * OTLP-gRPC metric exporter */ -export class OTLPMetricExporter extends OTLPMetricExporterBase { +export class OTLPMetricExporter extends OTLPMetricExporterBase { constructor(config?: OTLPGRPCExporterConfigNode & OTLPMetricExporterOptions) { - super(new OTLPMetricExporterProxy(config), config); + super( + createOtlpGrpcExportDelegate( + convertLegacyOtlpGrpcOptions(config ?? {}, 'METRICS'), + ProtobufMetricsSerializer, + 'MetricsExportService', + '/opentelemetry.proto.collector.metrics.v1.MetricsService/Export' + ), + config + ); } } 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 76b7f74dfd..52e3be2c8b 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/OTLPMetricExporterBase.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/OTLPMetricExporterBase.ts @@ -28,7 +28,7 @@ import { AggregationTemporalityPreference, OTLPMetricExporterOptions, } from './OTLPMetricExporterOptions'; -import { OTLPExporterBase } from '@opentelemetry/otlp-exporter-base'; +import { IOLTPExportDelegate } from '@opentelemetry/otlp-exporter-base'; import { diag } from '@opentelemetry/api'; export const CumulativeTemporalitySelector: AggregationTemporalitySelector = @@ -117,16 +117,16 @@ function chooseAggregationSelector( } } -export class OTLPMetricExporterBase< - T extends OTLPExporterBase, -> implements PushMetricExporter -{ - public _otlpExporter: T; +export class OTLPMetricExporterBase implements PushMetricExporter { + public _delegate: IOLTPExportDelegate; private _aggregationTemporalitySelector: AggregationTemporalitySelector; private _aggregationSelector: AggregationSelector; - constructor(exporter: T, config?: OTLPMetricExporterOptions) { - this._otlpExporter = exporter; + constructor( + delegate: IOLTPExportDelegate, + config?: OTLPMetricExporterOptions + ) { + this._delegate = delegate; this._aggregationSelector = chooseAggregationSelector(config); this._aggregationTemporalitySelector = chooseTemporalitySelector( config?.temporalityPreference @@ -137,15 +137,15 @@ export class OTLPMetricExporterBase< metrics: ResourceMetrics, resultCallback: (result: ExportResult) => void ): void { - this._otlpExporter.export([metrics], resultCallback); + this._delegate.export([metrics], resultCallback); } async shutdown(): Promise { - await this._otlpExporter.shutdown(); + await this._delegate.shutdown(); } forceFlush(): Promise { - return Promise.resolve(); + return this._delegate.forceFlush(); } selectAggregation(instrumentType: InstrumentType): Aggregation { 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 828cd61b0c..01a39a2479 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 @@ -14,39 +14,24 @@ * limitations under the License. */ -import { ResourceMetrics } from '@opentelemetry/sdk-metrics'; import { OTLPMetricExporterOptions } from '../../OTLPMetricExporterOptions'; import { OTLPMetricExporterBase } from '../../OTLPMetricExporterBase'; -import { - OTLPExporterBrowserBase, - OTLPExporterConfigBase, -} from '@opentelemetry/otlp-exporter-base'; -import { - IExportMetricsServiceResponse, - JsonMetricsSerializer, -} from '@opentelemetry/otlp-transformer'; - -const DEFAULT_COLLECTOR_RESOURCE_PATH = 'v1/metrics'; - -class OTLPExporterBrowserProxy extends OTLPExporterBrowserBase< - ResourceMetrics, - IExportMetricsServiceResponse -> { - constructor(config?: OTLPMetricExporterOptions & OTLPExporterConfigBase) { - super( - config, - JsonMetricsSerializer, - { 'Content-Type': 'application/json' }, - DEFAULT_COLLECTOR_RESOURCE_PATH - ); - } -} +import { OTLPExporterConfigBase } from '@opentelemetry/otlp-exporter-base'; +import { JsonMetricsSerializer } from '@opentelemetry/otlp-transformer'; +import { createLegacyOtlpBrowserExportDelegate } from '@opentelemetry/otlp-exporter-base/browser-http'; /** * Collector Metric Exporter for Web */ -export class OTLPMetricExporter extends OTLPMetricExporterBase { +export class OTLPMetricExporter extends OTLPMetricExporterBase { constructor(config?: OTLPExporterConfigBase & OTLPMetricExporterOptions) { - super(new OTLPExporterBrowserProxy(config), config); + super( + createLegacyOtlpBrowserExportDelegate( + config ?? {}, + JsonMetricsSerializer, + 'v1/metrics', + { 'Content-Type': 'application/json' } + ) + ); } } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/node/OTLPMetricExporter.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/node/OTLPMetricExporter.ts index 368858190a..ae7ea320da 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/node/OTLPMetricExporter.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/node/OTLPMetricExporter.ts @@ -14,46 +14,34 @@ * limitations under the License. */ -import { ResourceMetrics } from '@opentelemetry/sdk-metrics'; import { OTLPMetricExporterOptions } from '../../OTLPMetricExporterOptions'; import { OTLPMetricExporterBase } from '../../OTLPMetricExporterBase'; -import { - OTLPExporterNodeBase, - OTLPExporterNodeConfigBase, -} from '@opentelemetry/otlp-exporter-base'; -import { - IExportMetricsServiceResponse, - JsonMetricsSerializer, -} from '@opentelemetry/otlp-transformer'; +import { OTLPExporterNodeConfigBase } from '@opentelemetry/otlp-exporter-base'; +import { JsonMetricsSerializer } from '@opentelemetry/otlp-transformer'; import { VERSION } from '../../version'; +import { + convertLegacyHttpOptions, + createOtlpHttpExportDelegate, +} from '@opentelemetry/otlp-exporter-base/node-http'; const USER_AGENT = { 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, }; -class OTLPExporterNodeProxy extends OTLPExporterNodeBase< - ResourceMetrics, - IExportMetricsServiceResponse -> { - constructor(config?: OTLPExporterNodeConfigBase & OTLPMetricExporterOptions) { - super( - config, - JsonMetricsSerializer, - { - ...USER_AGENT, - 'Content-Type': 'application/json', - }, - 'METRICS', - 'v1/metrics' - ); - } -} - /** - * Collector Metric Exporter for Node + * OTLP Metric Exporter for Node.js */ -export class OTLPMetricExporter extends OTLPMetricExporterBase { +export class OTLPMetricExporter extends OTLPMetricExporterBase { constructor(config?: OTLPExporterNodeConfigBase & OTLPMetricExporterOptions) { - super(new OTLPExporterNodeProxy(config), config); + super( + createOtlpHttpExportDelegate( + convertLegacyHttpOptions(config ?? {}, 'METRICS', 'v1/metrics', { + ...USER_AGENT, + 'Content-Type': 'application/json', + }), + JsonMetricsSerializer + ), + config + ); } } 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 5650fc8510..aa7270a6ff 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 @@ -192,9 +192,8 @@ describe('OTLPMetricExporter - web', () => { collectorExporter.export(metrics, () => {}); queueMicrotask(() => { - const response: any = debugStub.args[2][0]; - assert.strictEqual(response, 'SendBeacon success'); - assert.strictEqual(errorStub.args.length, 0); + sinon.assert.calledWith(debugStub, 'SendBeacon success'); + sinon.assert.notCalled(errorStub); done(); }); diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/browser/index-webpack.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/browser/index-webpack.ts index 99100a0f6e..ae7d4b5a9d 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/browser/index-webpack.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-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/opentelemetry-exporter-metrics-otlp-http/test/common/CollectorMetricExporter.test.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/CollectorMetricExporter.test.ts deleted file mode 100644 index e6e646f06a..0000000000 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/CollectorMetricExporter.test.ts +++ /dev/null @@ -1,173 +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 { ExportResultCode } from '@opentelemetry/core'; -import { ResourceMetrics } from '@opentelemetry/sdk-metrics'; -import * as assert from 'assert'; -import * as sinon from 'sinon'; -import { - collect, - mockCounter, - mockObservableGauge, - setUp, - shutdown, -} from '../metricsHelper'; -import { - OTLPExporterBase, - OTLPExporterConfigBase, -} from '@opentelemetry/otlp-exporter-base'; - -type CollectorExporterConfig = OTLPExporterConfigBase; - -class OTLPMetricExporter extends OTLPExporterBase< - CollectorExporterConfig, - ResourceMetrics -> { - onShutdown() {} - - send() {} - - getDefaultUrl(config: CollectorExporterConfig) { - return config.url || ''; - } -} - -describe('OTLPMetricExporter - common', () => { - let collectorExporter: OTLPMetricExporter; - let collectorExporterConfig: CollectorExporterConfig; - let metrics: ResourceMetrics; - - beforeEach(() => { - setUp(); - }); - - afterEach(async () => { - await shutdown(); - sinon.restore(); - }); - - describe('constructor', () => { - beforeEach(async () => { - collectorExporterConfig = { - url: 'http://foo.bar.com', - }; - collectorExporter = new OTLPMetricExporter(collectorExporterConfig); - const counter = mockCounter(); - mockObservableGauge(observableResult => { - observableResult.observe(3, {}); - observableResult.observe(6, {}); - }, 'double-observable-gauge3'); - counter.add(1); - - const { resourceMetrics, errors } = await collect(); - assert.strictEqual(errors.length, 0); - metrics = resourceMetrics; - }); - - it('should create an instance', () => { - assert.ok(typeof collectorExporter !== 'undefined'); - }); - - describe('when config is missing certain params', () => { - beforeEach(() => { - collectorExporter = new OTLPMetricExporter(); - }); - }); - }); - - describe('export', () => { - let spySend: any; - beforeEach(() => { - spySend = sinon.stub(OTLPMetricExporter.prototype, 'send'); - collectorExporter = new OTLPMetricExporter(collectorExporterConfig); - }); - - it('should export metrics as otlpTypes.Metrics', done => { - collectorExporter.export([metrics], () => {}); - setTimeout(() => { - const metric1 = spySend.args[0][0][0] as ResourceMetrics; - assert.deepStrictEqual(metrics, metric1); - done(); - }); - assert.strictEqual(spySend.callCount, 1); - }); - - describe('when exporter is shutdown', () => { - it( - 'should not export anything but return callback with code' + - ' "FailedNotRetryable"', - async () => { - await collectorExporter.shutdown(); - spySend.resetHistory(); - - const callbackSpy = sinon.spy(); - collectorExporter.export([metrics], callbackSpy); - const returnCode = callbackSpy.args[0][0]; - assert.strictEqual( - returnCode.code, - ExportResultCode.FAILED, - 'return value is wrong' - ); - assert.strictEqual(spySend.callCount, 0, 'should not call send'); - } - ); - }); - describe('when an error occurs', () => { - it('should return failed export result', done => { - spySend.throws({ - code: 600, - details: 'Test error', - metadata: {}, - message: 'Non-Retryable', - stack: 'Stack', - }); - const callbackSpy = sinon.spy(); - collectorExporter.export([metrics], callbackSpy); - setTimeout(() => { - const returnCode = callbackSpy.args[0][0]; - assert.strictEqual( - returnCode.code, - ExportResultCode.FAILED, - 'return value is wrong' - ); - assert.strictEqual( - returnCode.error.message, - 'Non-Retryable', - 'return error message is wrong' - ); - assert.strictEqual(spySend.callCount, 1, 'should call send'); - done(); - }); - }); - }); - }); - - describe('shutdown', () => { - let onShutdownSpy: any; - beforeEach(() => { - onShutdownSpy = sinon.stub(OTLPMetricExporter.prototype, 'onShutdown'); - collectorExporterConfig = { - url: 'http://foo.bar.com', - }; - collectorExporter = new OTLPMetricExporter(collectorExporterConfig); - }); - - it('should call onShutdown', async () => { - await collectorExporter.shutdown(); - assert.strictEqual(onShutdownSpy.callCount, 1); - }); - }); -}); 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 9f594055a5..f2509dceda 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/src/OTLPMetricExporter.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/src/OTLPMetricExporter.ts @@ -15,42 +15,26 @@ */ import { OTLPMetricExporterOptions } from '@opentelemetry/exporter-metrics-otlp-http'; -import { ResourceMetrics } from '@opentelemetry/sdk-metrics'; import { OTLPMetricExporterBase } from '@opentelemetry/exporter-metrics-otlp-http'; -import { - OTLPExporterNodeConfigBase, - OTLPExporterNodeBase, -} from '@opentelemetry/otlp-exporter-base'; -import { - IExportMetricsServiceResponse, - ProtobufMetricsSerializer, -} from '@opentelemetry/otlp-transformer'; +import { OTLPExporterNodeConfigBase } from '@opentelemetry/otlp-exporter-base'; +import { ProtobufMetricsSerializer } from '@opentelemetry/otlp-transformer'; import { VERSION } from './version'; +import { + convertLegacyHttpOptions, + createOtlpHttpExportDelegate, +} from '@opentelemetry/otlp-exporter-base/node-http'; -const USER_AGENT = { - 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, -}; - -class OTLPMetricExporterNodeProxy extends OTLPExporterNodeBase< - ResourceMetrics, - IExportMetricsServiceResponse -> { +export class OTLPMetricExporter extends OTLPMetricExporterBase { constructor(config?: OTLPExporterNodeConfigBase & OTLPMetricExporterOptions) { super( - config, - ProtobufMetricsSerializer, - { - ...USER_AGENT, - 'Content-Type': 'application/x-protobuf', - }, - 'METRICS', - 'v1/metrics' + createOtlpHttpExportDelegate( + convertLegacyHttpOptions(config ?? {}, 'METRICS', 'v1/metrics', { + 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, + 'Content-Type': 'application/x-protobuf', + }), + ProtobufMetricsSerializer + ), + config ); } } - -export class OTLPMetricExporter extends OTLPMetricExporterBase { - constructor(config?: OTLPExporterNodeConfigBase & OTLPMetricExporterOptions) { - super(new OTLPMetricExporterNodeProxy(config), config); - } -} diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/OTLPMetricExporter.test.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/OTLPMetricExporter.test.ts index 42408adab9..d8dcdc72b6 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/OTLPMetricExporter.test.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/OTLPMetricExporter.test.ts @@ -41,7 +41,6 @@ import { } from '@opentelemetry/exporter-metrics-otlp-http'; import { Stream, PassThrough } from 'stream'; import { OTLPExporterNodeConfigBase } from '@opentelemetry/otlp-exporter-base'; -import { VERSION } from '../src/version'; import { Root } from 'protobufjs'; import * as path from 'path'; @@ -76,18 +75,6 @@ describe('OTLPMetricExporter - node with proto over http', () => { sinon.restore(); }); - describe('default behavior for headers', () => { - const exporter = new OTLPMetricExporter(); - it('should include user agent in header', () => { - assert.strictEqual( - exporter._otlpExporter['_transport']['_transport']['_parameters'][ - 'headers' - ]['User-Agent'], - `OTel-OTLP-Exporter-JavaScript/${VERSION}` - ); - }); - }); - describe('export', () => { beforeEach(async () => { collectorExporterConfig = { @@ -276,7 +263,7 @@ describe('OTLPMetricExporter - node with proto over http', () => { collectorExporter.export(metrics, result => { assert.strictEqual(result.code, ExportResultCode.SUCCESS); - assert.strictEqual(spyLoggerError.args.length, 0); + sinon.assert.notCalled(spyLoggerError); done(); }); }); diff --git a/experimental/packages/otlp-exporter-base/README.md b/experimental/packages/otlp-exporter-base/README.md index 2ec8baf1e8..3f11c957c2 100644 --- a/experimental/packages/otlp-exporter-base/README.md +++ b/experimental/packages/otlp-exporter-base/README.md @@ -7,7 +7,7 @@ **Note: This is an experimental package under active development. New releases may include breaking changes.** -This module provides a base exporter for web and node to be used with [opentelemetry-collector][opentelemetry-collector-url]. +This module provides base components for OTLP Exporters, both Web and Node.js. ## Installation @@ -19,10 +19,6 @@ npm install --save @opentelemetry/otlp-exporter-base For GRPC please check [npm-url-grpc] -## PROTOBUF - -For PROTOBUF please check [npm-url-proto] - ## Useful links - For more information on OpenTelemetry, visit: diff --git a/experimental/packages/otlp-exporter-base/package.json b/experimental/packages/otlp-exporter-base/package.json index 9f1f1d0db7..b444e7a499 100644 --- a/experimental/packages/otlp-exporter-base/package.json +++ b/experimental/packages/otlp-exporter-base/package.json @@ -6,13 +6,40 @@ "module": "build/esm/index.js", "esnext": "build/esnext/index.js", "types": "build/src/index.d.ts", - "repository": "open-telemetry/opentelemetry-js", - "browser": { - "./src/platform/index.ts": "./src/platform/browser/index.ts", - "./build/esm/platform/index.js": "./build/esm/platform/browser/index.js", - "./build/esnext/platform/index.js": "./build/esnext/platform/browser/index.js", - "./build/src/platform/index.js": "./build/src/platform/browser/index.js" + "exports": { + ".": { + "module": "./build/esm/index.js", + "esnext": "./build/esnext/index.js", + "types": "./build/src/index.d.ts", + "default": "./build/src/index.js" + }, + "./node-http": { + "module": "./build/esm/index-node-http.js", + "esnext": "./build/esnext/index-node-http.js", + "types": "./build/src/index-node-http.d.ts", + "default": "./build/src/index-node-http.js" + }, + "./browser-http": { + "module": "./build/esm/index-browser-http.js", + "esnext": "./build/esnext/index-browser-http.js", + "types": "./build/src/index-browser-http.d.ts", + "default": "./build/src/index-browser-http.js" + } + }, + "typesVersions": { + "*": { + "*": [ + "./build/src/index.d.ts" + ], + "node-http": [ + "./build/src/index-node-http.d.ts" + ], + "browser-http": [ + "./build/src/index-browser-http.d.ts" + ] + } }, + "repository": "open-telemetry/opentelemetry-js", "scripts": { "prepublishOnly": "npm run compile", "compile": "tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json", diff --git a/experimental/packages/otlp-exporter-base/src/OTLPExporterBase.ts b/experimental/packages/otlp-exporter-base/src/OTLPExporterBase.ts index 986447add2..94b3d8bf2a 100644 --- a/experimental/packages/otlp-exporter-base/src/OTLPExporterBase.ts +++ b/experimental/packages/otlp-exporter-base/src/OTLPExporterBase.ts @@ -14,41 +14,14 @@ * limitations under the License. */ -import { diag } from '@opentelemetry/api'; -import { - ExportResult, - ExportResultCode, - BindOnceFuture, -} from '@opentelemetry/core'; -import { - OTLPExporterError, - OTLPExporterConfigBase, - ExportServiceError, -} from './types'; +import { ExportResult } from '@opentelemetry/core'; +import { IOLTPExportDelegate } from './otlp-export-delegate'; /** - * Collector Exporter abstract base class + * @deprecated */ -export abstract class OTLPExporterBase< - T extends OTLPExporterConfigBase, - ExportItem, -> { - protected _concurrencyLimit: number; - protected _sendingPromises: Promise[] = []; - protected _shutdownOnce: BindOnceFuture; - - /** - * @param config - */ - constructor(config: T = {} as T) { - this.shutdown = this.shutdown.bind(this); - this._shutdownOnce = new BindOnceFuture(this._shutdown, this); - - this._concurrencyLimit = - typeof config.concurrencyLimit === 'number' - ? config.concurrencyLimit - : 30; - } +export class OTLPExporterBase { + constructor(private _delegate: IOLTPExportDelegate) {} /** * Export items. @@ -56,74 +29,17 @@ export abstract class OTLPExporterBase< * @param resultCallback */ export( - items: ExportItem[], + items: Internal, resultCallback: (result: ExportResult) => void ): void { - if (this._shutdownOnce.isCalled) { - resultCallback({ - code: ExportResultCode.FAILED, - error: new Error('Exporter has been shutdown'), - }); - return; - } - - if (this._sendingPromises.length >= this._concurrencyLimit) { - resultCallback({ - code: ExportResultCode.FAILED, - error: new Error('Concurrent export limit reached'), - }); - return; - } - - this._export(items) - .then(() => { - resultCallback({ code: ExportResultCode.SUCCESS }); - }) - .catch((error: ExportServiceError) => { - resultCallback({ code: ExportResultCode.FAILED, error }); - }); - } - - private _export(items: ExportItem[]): Promise { - return new Promise((resolve, reject) => { - try { - diag.debug('items to be sent', items); - this.send(items, resolve, reject); - } catch (e) { - reject(e); - } - }); - } - - /** - * Shutdown the exporter. - */ - shutdown(): Promise { - return this._shutdownOnce.call(); + this._delegate.export(items, resultCallback); } - /** - * Exports any pending spans in the exporter - */ forceFlush(): Promise { - return Promise.all(this._sendingPromises).then(() => { - /** ignore resolved values */ - }); + return this._delegate.forceFlush(); } - /** - * Called by _shutdownOnce with BindOnceFuture - */ - private _shutdown(): Promise { - diag.debug('shutdown started'); - this.onShutdown(); - return this.forceFlush(); + shutdown(): Promise { + return this._delegate.shutdown(); } - - abstract onShutdown(): void; - abstract send( - items: ExportItem[], - onSuccess: () => void, - onError: (error: OTLPExporterError) => void - ): void; } diff --git a/experimental/packages/otlp-exporter-base/src/configuration/convert-legacy-browser-http-options.ts b/experimental/packages/otlp-exporter-base/src/configuration/convert-legacy-browser-http-options.ts new file mode 100644 index 0000000000..955945e6f1 --- /dev/null +++ b/experimental/packages/otlp-exporter-base/src/configuration/convert-legacy-browser-http-options.ts @@ -0,0 +1,45 @@ +/* + * 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 { + getHttpConfigurationDefaults, + mergeOtlpHttpConfigurationWithDefaults, + OtlpHttpConfiguration, +} from './otlp-http-configuration'; +import { OTLPExporterNodeConfigBase } from './legacy-node-configuration'; + +/** + * @deprecated this will be removed in 2.0 + * + * @param config + * @param signalResourcePath + * @param requiredHeaders + */ +export function convertLegacyBrowserHttpOptions( + config: OTLPExporterNodeConfigBase, + signalResourcePath: string, + requiredHeaders: Record +): OtlpHttpConfiguration { + return mergeOtlpHttpConfigurationWithDefaults( + { + url: config.url, + timeoutMillis: config.timeoutMillis, + headers: config.headers, + concurrencyLimit: config.concurrencyLimit, + }, + {}, // no fallback for browser case + getHttpConfigurationDefaults(requiredHeaders, signalResourcePath) + ); +} diff --git a/experimental/packages/otlp-exporter-base/src/configuration/convert-legacy-node-http-options.ts b/experimental/packages/otlp-exporter-base/src/configuration/convert-legacy-node-http-options.ts new file mode 100644 index 0000000000..6380a7d3f9 --- /dev/null +++ b/experimental/packages/otlp-exporter-base/src/configuration/convert-legacy-node-http-options.ts @@ -0,0 +1,79 @@ +/* + * 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 { OTLPExporterNodeConfigBase } from './legacy-node-configuration'; +import { + getHttpConfigurationDefaults, + mergeOtlpHttpConfigurationWithDefaults, + OtlpHttpConfiguration, +} from './otlp-http-configuration'; +import { getHttpConfigurationFromEnvironment } from './otlp-http-env-configuration'; +import type * as http from 'http'; +import type * as https from 'https'; +import { diag } from '@opentelemetry/api'; + +function convertLegacyAgentOptions( + config: OTLPExporterNodeConfigBase +): http.AgentOptions | https.AgentOptions | undefined { + // populate keepAlive for use with new settings + if (config?.keepAlive != null) { + if (config.httpAgentOptions != null) { + if (config.httpAgentOptions.keepAlive == null) { + // specific setting is not set, populate with non-specific setting. + config.httpAgentOptions.keepAlive = config.keepAlive; + } + // do nothing, use specific setting otherwise + } else { + // populate specific option if AgentOptions does not exist. + config.httpAgentOptions = { + keepAlive: config.keepAlive, + }; + } + } + + return config.httpAgentOptions; +} + +/** + * @deprecated this will be removed in 2.0 + * @param config + * @param signalIdentifier + * @param signalResourcePath + * @param requiredHeaders + */ +export function convertLegacyHttpOptions( + config: OTLPExporterNodeConfigBase, + signalIdentifier: string, + signalResourcePath: string, + requiredHeaders: Record +): OtlpHttpConfiguration { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if ((config as any).metadata) { + diag.warn('Metadata cannot be set when using http'); + } + + return mergeOtlpHttpConfigurationWithDefaults( + { + url: config.url, + headers: config.headers, + concurrencyLimit: config.concurrencyLimit, + timeoutMillis: config.timeoutMillis, + compression: config.compression, + agentOptions: convertLegacyAgentOptions(config), + }, + getHttpConfigurationFromEnvironment(signalIdentifier, signalResourcePath), + getHttpConfigurationDefaults(requiredHeaders, signalResourcePath) + ); +} diff --git a/experimental/packages/otlp-exporter-base/src/configuration/create-legacy-browser-delegate.ts b/experimental/packages/otlp-exporter-base/src/configuration/create-legacy-browser-delegate.ts new file mode 100644 index 0000000000..0e0e224329 --- /dev/null +++ b/experimental/packages/otlp-exporter-base/src/configuration/create-legacy-browser-delegate.ts @@ -0,0 +1,51 @@ +/* + * 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 { ISerializer } from '@opentelemetry/otlp-transformer'; +import { + createOtlpSendBeaconExportDelegate, + createOtlpXhrExportDelegate, +} from '../otlp-browser-http-export-delegate'; +import { convertLegacyBrowserHttpOptions } from './convert-legacy-browser-http-options'; +import { IOLTPExportDelegate } from '../otlp-export-delegate'; +import { OTLPExporterConfigBase } from './legacy-base-configuration'; + +/** + * @deprecated + * @param config + * @param serializer + * @param signalResourcePath + * @param requiredHeaders + */ +export function createLegacyOtlpBrowserExportDelegate( + config: OTLPExporterConfigBase, + serializer: ISerializer, + signalResourcePath: string, + requiredHeaders: Record +): IOLTPExportDelegate { + const useXhr = !!config.headers || typeof navigator.sendBeacon !== 'function'; + + const options = convertLegacyBrowserHttpOptions( + config, + signalResourcePath, + requiredHeaders + ); + + if (useXhr) { + return createOtlpXhrExportDelegate(options, serializer); + } else { + return createOtlpSendBeaconExportDelegate(options, serializer); + } +} diff --git a/experimental/packages/otlp-exporter-base/src/platform/browser/index.ts b/experimental/packages/otlp-exporter-base/src/configuration/legacy-base-configuration.ts similarity index 66% rename from experimental/packages/otlp-exporter-base/src/platform/browser/index.ts rename to experimental/packages/otlp-exporter-base/src/configuration/legacy-base-configuration.ts index 6c0c4bd425..a7058cb796 100644 --- a/experimental/packages/otlp-exporter-base/src/platform/browser/index.ts +++ b/experimental/packages/otlp-exporter-base/src/configuration/legacy-base-configuration.ts @@ -13,5 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -export { OTLPExporterBrowserBase } from './OTLPExporterBrowserBase'; +/** + * @deprecated will be removed in 2.0 + */ +export interface OTLPExporterConfigBase { + headers?: Record; + url?: string; + concurrencyLimit?: number; + /** Maximum time the OTLP exporter will wait for each batch export. + * The default value is 10000ms. */ + timeoutMillis?: number; +} diff --git a/experimental/packages/otlp-exporter-base/src/platform/node/types.ts b/experimental/packages/otlp-exporter-base/src/configuration/legacy-node-configuration.ts similarity index 89% rename from experimental/packages/otlp-exporter-base/src/platform/node/types.ts rename to experimental/packages/otlp-exporter-base/src/configuration/legacy-node-configuration.ts index b1e355de2d..c2e60c11c1 100644 --- a/experimental/packages/otlp-exporter-base/src/platform/node/types.ts +++ b/experimental/packages/otlp-exporter-base/src/configuration/legacy-node-configuration.ts @@ -13,12 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import type * as http from 'http'; import type * as https from 'https'; -import { OTLPExporterConfigBase } from '../../types'; +import { OTLPExporterConfigBase } from './legacy-base-configuration'; /** + * @deprecated + * * Collector Exporter node base config */ export interface OTLPExporterNodeConfigBase extends OTLPExporterConfigBase { @@ -27,6 +30,9 @@ export interface OTLPExporterNodeConfigBase extends OTLPExporterConfigBase { httpAgentOptions?: http.AgentOptions | https.AgentOptions; } +/** + * @deprecated + */ export enum CompressionAlgorithm { NONE = 'none', GZIP = 'gzip', diff --git a/experimental/packages/otlp-exporter-base/src/configuration/otlp-http-configuration.ts b/experimental/packages/otlp-exporter-base/src/configuration/otlp-http-configuration.ts index a303bd5c73..0b78a7777b 100644 --- a/experimental/packages/otlp-exporter-base/src/configuration/otlp-http-configuration.ts +++ b/experimental/packages/otlp-exporter-base/src/configuration/otlp-http-configuration.ts @@ -21,9 +21,14 @@ import { } from './shared-configuration'; import { validateAndNormalizeHeaders } from '../util'; +// NOTE: do not change these imports to be actual imports, otherwise they WILL break `@opentelemetry/instrumentation-http` +import type * as http from 'http'; +import type * as https from 'https'; + export interface OtlpHttpConfiguration extends OtlpSharedConfiguration { url: string; headers: Record; + agentOptions: http.AgentOptions | https.AgentOptions; } function mergeHeaders( @@ -89,6 +94,10 @@ export function mergeOtlpHttpConfigurationWithDefaults( validateUserProvidedUrl(userProvidedConfiguration.url) ?? fallbackConfiguration.url ?? defaultConfiguration.url, + agentOptions: + userProvidedConfiguration.agentOptions ?? + fallbackConfiguration.agentOptions ?? + defaultConfiguration.agentOptions, }; } @@ -100,5 +109,6 @@ export function getHttpConfigurationDefaults( ...getSharedConfigurationDefaults(), headers: requiredHeaders, url: 'http://localhost:4318/' + signalResourcePath, + agentOptions: { keepAlive: true }, }; } diff --git a/experimental/packages/otlp-exporter-base/src/export-promise-queue.ts b/experimental/packages/otlp-exporter-base/src/export-promise-queue.ts new file mode 100644 index 0000000000..e66f566978 --- /dev/null +++ b/experimental/packages/otlp-exporter-base/src/export-promise-queue.ts @@ -0,0 +1,64 @@ +/* + * 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. + */ + +export interface IExportPromiseHandler { + pushPromise(promise: Promise): void; + hasReachedLimit(): boolean; + awaitAll(): Promise; +} + +/** + * Promise queue for keeping track of export promises. Finished promises will be auto-dequeued. + * Allows for awaiting all promises in the queue. + * + * TODO: this still has some confusing behavior on enqueue. + */ +class BoundedQueueExportPromiseHandler implements IExportPromiseHandler { + private readonly _concurrencyLimit: number; + private _sendingPromises: Promise[] = []; + + /** + * @param concurrencyLimit maximum promises allowed in a queue at the same time. + */ + constructor(concurrencyLimit: number) { + this._concurrencyLimit = concurrencyLimit; + } + + public pushPromise(promise: Promise): void { + this._sendingPromises.push(promise); + const popPromise = () => { + const index = this._sendingPromises.indexOf(promise); + this._sendingPromises.splice(index, 1); + }; + promise.then(popPromise, popPromise); + } + + public hasReachedLimit(): boolean { + return this._sendingPromises.length >= this._concurrencyLimit; + } + + public awaitAll(): Promise { + return Promise.all(this._sendingPromises).then(() => { + /** ignore resolved values */ + }); + } +} + +export function createBoundedQueueExportPromiseHandler(options: { + concurrencyLimit: number; +}): IExportPromiseHandler { + return new BoundedQueueExportPromiseHandler(options.concurrencyLimit); +} diff --git a/experimental/packages/otlp-exporter-base/src/platform/index.ts b/experimental/packages/otlp-exporter-base/src/index-browser-http.ts similarity index 65% rename from experimental/packages/otlp-exporter-base/src/platform/index.ts rename to experimental/packages/otlp-exporter-base/src/index-browser-http.ts index 14deab5550..bf10a2be97 100644 --- a/experimental/packages/otlp-exporter-base/src/platform/index.ts +++ b/experimental/packages/otlp-exporter-base/src/index-browser-http.ts @@ -15,8 +15,9 @@ */ export { - OTLPExporterNodeBase, - OTLPExporterNodeConfigBase, - CompressionAlgorithm, -} from './node'; -export { OTLPExporterBrowserBase } from './browser'; + createOtlpXhrExportDelegate, + createOtlpSendBeaconExportDelegate, +} from './otlp-browser-http-export-delegate'; + +export { convertLegacyBrowserHttpOptions } from './configuration/convert-legacy-browser-http-options'; +export { createLegacyOtlpBrowserExportDelegate } from './configuration/create-legacy-browser-delegate'; diff --git a/experimental/packages/otlp-exporter-base/src/platform/node/index.ts b/experimental/packages/otlp-exporter-base/src/index-node-http.ts similarity index 69% rename from experimental/packages/otlp-exporter-base/src/platform/node/index.ts rename to experimental/packages/otlp-exporter-base/src/index-node-http.ts index fcfca512a8..29c9def42f 100644 --- a/experimental/packages/otlp-exporter-base/src/platform/node/index.ts +++ b/experimental/packages/otlp-exporter-base/src/index-node-http.ts @@ -14,5 +14,6 @@ * limitations under the License. */ -export { OTLPExporterNodeBase } from './OTLPExporterNodeBase'; -export { OTLPExporterNodeConfigBase, CompressionAlgorithm } from './types'; +export { createOtlpHttpExportDelegate } from './otlp-http-export-delegate'; +export { getSharedConfigurationFromEnvironment } from './configuration/shared-env-configuration'; +export { convertLegacyHttpOptions } from './configuration/convert-legacy-node-http-options'; diff --git a/experimental/packages/otlp-exporter-base/src/index.ts b/experimental/packages/otlp-exporter-base/src/index.ts index 5edc2d680a..818151f7ac 100644 --- a/experimental/packages/otlp-exporter-base/src/index.ts +++ b/experimental/packages/otlp-exporter-base/src/index.ts @@ -14,17 +14,8 @@ * limitations under the License. */ -/* eslint no-restricted-syntax: ["warn", "ExportAllDeclaration"] -- - * TODO: Replace export * with named exports before next major version - */ -export * from './platform'; export { OTLPExporterBase } from './OTLPExporterBase'; -export { - OTLPExporterError, - OTLPExporterConfigBase, - ExportServiceError, -} from './types'; -export { validateAndNormalizeHeaders } from './util'; +export { OTLPExporterError } from './types'; export { ExportResponse, @@ -41,4 +32,14 @@ export { getSharedConfigurationDefaults, } from './configuration/shared-configuration'; -export { getSharedConfigurationFromEnvironment } from './configuration/shared-env-configuration'; +export { + OTLPExporterNodeConfigBase, + CompressionAlgorithm, +} from './configuration/legacy-node-configuration'; +export { OTLPExporterConfigBase } from './configuration/legacy-base-configuration'; +export { + createOtlpExportDelegate, + IOLTPExportDelegate, +} from './otlp-export-delegate'; +export { createBoundedQueueExportPromiseHandler } from './export-promise-queue'; +export { createOtlpNetworkExportDelegate } from './otlp-network-export-delegate'; diff --git a/experimental/packages/otlp-exporter-base/src/otlp-browser-http-export-delegate.ts b/experimental/packages/otlp-exporter-base/src/otlp-browser-http-export-delegate.ts new file mode 100644 index 0000000000..5b8c46d7d9 --- /dev/null +++ b/experimental/packages/otlp-exporter-base/src/otlp-browser-http-export-delegate.ts @@ -0,0 +1,51 @@ +/* + * 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 { OtlpHttpConfiguration } from './configuration/otlp-http-configuration'; +import { ISerializer } from '@opentelemetry/otlp-transformer'; +import { IOLTPExportDelegate } from './otlp-export-delegate'; +import { createRetryingTransport } from './retrying-transport'; +import { createXhrTransport } from './transport/xhr-transport'; +import { createSendBeaconTransport } from './transport/send-beacon-transport'; +import { createOtlpNetworkExportDelegate } from './otlp-network-export-delegate'; + +export function createOtlpXhrExportDelegate( + options: OtlpHttpConfiguration, + serializer: ISerializer +): IOLTPExportDelegate { + return createOtlpNetworkExportDelegate( + options, + serializer, + createRetryingTransport({ + transport: createXhrTransport(options), + }) + ); +} + +export function createOtlpSendBeaconExportDelegate( + options: OtlpHttpConfiguration, + serializer: ISerializer +): IOLTPExportDelegate { + return createOtlpNetworkExportDelegate( + options, + serializer, + createRetryingTransport({ + transport: createSendBeaconTransport({ + url: options.url, + blobType: options.headers['Content-Type'], + }), + }) + ); +} diff --git a/experimental/packages/otlp-exporter-base/src/otlp-export-delegate.ts b/experimental/packages/otlp-exporter-base/src/otlp-export-delegate.ts new file mode 100644 index 0000000000..583874e131 --- /dev/null +++ b/experimental/packages/otlp-exporter-base/src/otlp-export-delegate.ts @@ -0,0 +1,136 @@ +/* + * 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 { ExportResult, ExportResultCode } from '@opentelemetry/core'; +import { IExporterTransport } from './exporter-transport'; +import { IExportPromiseHandler } from './export-promise-queue'; +import { ISerializer } from '@opentelemetry/otlp-transformer'; +import { OTLPExporterError } from './types'; + +/** + * Internally shared export logic for OTLP. + */ +export interface IOLTPExportDelegate { + export( + internalRepresentation: Internal, + resultCallback: (result: ExportResult) => void + ): void; + forceFlush(): Promise; + shutdown(): Promise; +} + +class OTLPExportDelegate + implements IOLTPExportDelegate +{ + constructor( + private _transport: IExporterTransport, + private _serializer: ISerializer, + private _promiseQueue: IExportPromiseHandler, + private _timeout: number + ) {} + + export( + internalRepresentation: Internal, + resultCallback: (result: ExportResult) => void + ): void { + // don't do any work if too many exports are in progress. + if (this._promiseQueue.hasReachedLimit()) { + resultCallback({ + code: ExportResultCode.FAILED, + error: new Error('Concurrent export limit reached'), + }); + return; + } + + const serializedRequest = this._serializer.serializeRequest( + internalRepresentation + ); + + if (serializedRequest == null) { + resultCallback({ + code: ExportResultCode.FAILED, + error: new Error('Nothing to send'), + }); + return; + } + + this._promiseQueue.pushPromise( + this._transport.send(serializedRequest, this._timeout).then( + response => { + if (response.status === 'success') { + // No matter the response, we can consider the export still successful. + resultCallback({ + code: ExportResultCode.SUCCESS, + }); + return; + } else if (response.status === 'failure' && response.error) { + resultCallback({ + code: ExportResultCode.FAILED, + error: response.error, + }); + return; + } else if (response.status === 'retryable') { + resultCallback({ + code: ExportResultCode.FAILED, + error: new OTLPExporterError( + 'Export failed with retryable status' + ), + }); + } else { + resultCallback({ + code: ExportResultCode.FAILED, + error: new OTLPExporterError('Export failed with unknown error'), + }); + } + }, + reason => + resultCallback({ + code: ExportResultCode.FAILED, + error: reason, + }) + ) + ); + } + + forceFlush(): Promise { + return this._promiseQueue.awaitAll(); + } + + async shutdown(): Promise { + await this.forceFlush(); + this._transport.shutdown(); + } +} + +/** + * Creates a generic delegate for OTLP exports which only contains parts of the OTLP export that are shared across all + * signals. + */ +export function createOtlpExportDelegate( + components: { + transport: IExporterTransport; + serializer: ISerializer; + promiseHandler: IExportPromiseHandler; + }, + settings: { timeout: number } +): IOLTPExportDelegate { + return new OTLPExportDelegate( + components.transport, + components.serializer, + components.promiseHandler, + settings.timeout + ); +} diff --git a/experimental/packages/otlp-exporter-base/src/otlp-http-export-delegate.ts b/experimental/packages/otlp-exporter-base/src/otlp-http-export-delegate.ts new file mode 100644 index 0000000000..6da5de3f08 --- /dev/null +++ b/experimental/packages/otlp-exporter-base/src/otlp-http-export-delegate.ts @@ -0,0 +1,40 @@ +/* + * 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 { + createOtlpExportDelegate, + IOLTPExportDelegate, +} from './otlp-export-delegate'; +import { OtlpHttpConfiguration } from './configuration/otlp-http-configuration'; +import { ISerializer } from '@opentelemetry/otlp-transformer'; +import { createHttpExporterTransport } from './transport/http-exporter-transport'; +import { createBoundedQueueExportPromiseHandler } from './export-promise-queue'; +import { createRetryingTransport } from './retrying-transport'; + +export function createOtlpHttpExportDelegate( + options: OtlpHttpConfiguration, + serializer: ISerializer +): IOLTPExportDelegate { + return createOtlpExportDelegate( + { + transport: createRetryingTransport({ + transport: createHttpExporterTransport(options), + }), + serializer: serializer, + promiseHandler: createBoundedQueueExportPromiseHandler(options), + }, + { timeout: options.timeoutMillis } + ); +} diff --git a/experimental/packages/otlp-exporter-base/src/otlp-network-export-delegate.ts b/experimental/packages/otlp-exporter-base/src/otlp-network-export-delegate.ts new file mode 100644 index 0000000000..c263404b0e --- /dev/null +++ b/experimental/packages/otlp-exporter-base/src/otlp-network-export-delegate.ts @@ -0,0 +1,39 @@ +/* + * 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 { createBoundedQueueExportPromiseHandler } from './export-promise-queue'; +import { OtlpSharedConfiguration } from './configuration/shared-configuration'; +import { ISerializer } from '@opentelemetry/otlp-transformer'; +import { IExporterTransport } from './exporter-transport'; +import { + createOtlpExportDelegate, + IOLTPExportDelegate, +} from './otlp-export-delegate'; + +export function createOtlpNetworkExportDelegate( + options: OtlpSharedConfiguration, + serializer: ISerializer, + transport: IExporterTransport +): IOLTPExportDelegate { + return createOtlpExportDelegate( + { + transport: transport, + serializer, + promiseHandler: createBoundedQueueExportPromiseHandler(options), + }, + { timeout: options.timeoutMillis } + ); +} diff --git a/experimental/packages/otlp-exporter-base/src/platform/browser/OTLPExporterBrowserBase.ts b/experimental/packages/otlp-exporter-base/src/platform/browser/OTLPExporterBrowserBase.ts deleted file mode 100644 index da5401698c..0000000000 --- a/experimental/packages/otlp-exporter-base/src/platform/browser/OTLPExporterBrowserBase.ts +++ /dev/null @@ -1,128 +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 { OTLPExporterBase } from '../../OTLPExporterBase'; -import { OTLPExporterConfigBase, OTLPExporterError } from '../../types'; -import { diag } from '@opentelemetry/api'; -import { ISerializer } from '@opentelemetry/otlp-transformer'; -import { IExporterTransport } from '../../exporter-transport'; -import { createXhrTransport } from './xhr-transport'; -import { createSendBeaconTransport } from './send-beacon-transport'; -import { createRetryingTransport } from '../../retrying-transport'; -import { - getHttpConfigurationDefaults, - mergeOtlpHttpConfigurationWithDefaults, -} from '../../configuration/otlp-http-configuration'; - -/** - * Collector Metric Exporter abstract base class - */ -export abstract class OTLPExporterBrowserBase< - ExportItem, - ServiceResponse, -> extends OTLPExporterBase { - private _serializer: ISerializer; - private _transport: IExporterTransport; - private _timeoutMillis: number; - - /** - * @param config - * @param serializer - * @param requiredHeaders - * @param signalResourcePath - */ - constructor( - config: OTLPExporterConfigBase = {}, - serializer: ISerializer, - requiredHeaders: Record, - signalResourcePath: string - ) { - super(config); - this._serializer = serializer; - const useXhr = - !!config.headers || typeof navigator.sendBeacon !== 'function'; - - const actualConfig = mergeOtlpHttpConfigurationWithDefaults( - { - url: config.url, - timeoutMillis: config.timeoutMillis, - headers: config.headers, - concurrencyLimit: config.concurrencyLimit, - }, - {}, // no fallback for browser case - getHttpConfigurationDefaults(requiredHeaders, signalResourcePath) - ); - - this._timeoutMillis = actualConfig.timeoutMillis; - this._concurrencyLimit = actualConfig.concurrencyLimit; - - if (useXhr) { - this._transport = createRetryingTransport({ - transport: createXhrTransport({ - headers: actualConfig.headers, - url: actualConfig.url, - }), - }); - } else { - // sendBeacon has no way to signal retry, so we do not wrap it in a RetryingTransport - this._transport = createSendBeaconTransport({ - url: actualConfig.url, - blobType: actualConfig.headers['Content-Type'], - }); - } - } - - onShutdown(): void {} - - send( - objects: ExportItem[], - onSuccess: () => void, - onError: (error: OTLPExporterError) => void - ): void { - if (this._shutdownOnce.isCalled) { - diag.debug('Shutdown already started. Cannot send objects'); - return; - } - - const data = this._serializer.serializeRequest(objects); - - if (data == null) { - onError(new Error('Could not serialize message')); - return; - } - - const promise = this._transport - .send(data, this._timeoutMillis) - .then(response => { - if (response.status === 'success') { - onSuccess(); - } else if (response.status === 'failure' && response.error) { - onError(response.error); - } else if (response.status === 'retryable') { - onError(new OTLPExporterError('Export failed with retryable status')); - } else { - onError(new OTLPExporterError('Export failed with unknown error')); - } - }, onError); - - this._sendingPromises.push(promise); - const popPromise = () => { - const index = this._sendingPromises.indexOf(promise); - this._sendingPromises.splice(index, 1); - }; - promise.then(popPromise, popPromise); - } -} diff --git a/experimental/packages/otlp-exporter-base/src/platform/node/OTLPExporterNodeBase.ts b/experimental/packages/otlp-exporter-base/src/platform/node/OTLPExporterNodeBase.ts deleted file mode 100644 index f5d858016c..0000000000 --- a/experimental/packages/otlp-exporter-base/src/platform/node/OTLPExporterNodeBase.ts +++ /dev/null @@ -1,122 +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 { OTLPExporterBase } from '../../OTLPExporterBase'; -import { OTLPExporterNodeConfigBase } from './types'; -import { diag } from '@opentelemetry/api'; -import { ISerializer } from '@opentelemetry/otlp-transformer'; -import { IExporterTransport } from '../../exporter-transport'; -import { createHttpExporterTransport } from './http-exporter-transport'; -import { OTLPExporterError } from '../../types'; -import { createRetryingTransport } from '../../retrying-transport'; -import { convertLegacyAgentOptions } from './convert-legacy-agent-options'; -import { - getHttpConfigurationDefaults, - mergeOtlpHttpConfigurationWithDefaults, -} from '../../configuration/otlp-http-configuration'; -import { getHttpConfigurationFromEnvironment } from '../../configuration/otlp-http-env-configuration'; - -/** - * Collector Metric Exporter abstract base class - */ -export abstract class OTLPExporterNodeBase< - ExportItem, - ServiceResponse, -> extends OTLPExporterBase { - private _serializer: ISerializer; - private _transport: IExporterTransport; - private _timeoutMillis: number; - - constructor( - config: OTLPExporterNodeConfigBase = {}, - serializer: ISerializer, - requiredHeaders: Record, - signalIdentifier: string, - signalResourcePath: string - ) { - super(config); - const actualConfig = mergeOtlpHttpConfigurationWithDefaults( - { - url: config.url, - headers: config.headers, - concurrencyLimit: config.concurrencyLimit, - timeoutMillis: config.timeoutMillis, - compression: config.compression, - }, - getHttpConfigurationFromEnvironment(signalIdentifier, signalResourcePath), - getHttpConfigurationDefaults(requiredHeaders, signalResourcePath) - ); - - this._timeoutMillis = actualConfig.timeoutMillis; - this._concurrencyLimit = actualConfig.concurrencyLimit; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - if ((config as any).metadata) { - diag.warn('Metadata cannot be set when using http'); - } - this._serializer = serializer; - - this._transport = createRetryingTransport({ - transport: createHttpExporterTransport({ - agentOptions: convertLegacyAgentOptions(config), - compression: actualConfig.compression, - headers: actualConfig.headers, - url: actualConfig.url, - }), - }); - } - - send( - objects: ExportItem[], - onSuccess: () => void, - onError: (error: OTLPExporterError) => void - ): void { - if (this._shutdownOnce.isCalled) { - diag.debug('Shutdown already started. Cannot send objects'); - return; - } - - const data = this._serializer.serializeRequest(objects); - - if (data == null) { - onError(new Error('Could not serialize message')); - return; - } - - const promise = this._transport - .send(data, this._timeoutMillis) - .then(response => { - if (response.status === 'success') { - onSuccess(); - } else if (response.status === 'failure' && response.error) { - onError(response.error); - } else if (response.status === 'retryable') { - onError(new OTLPExporterError('Export failed with retryable status')); - } else { - onError(new OTLPExporterError('Export failed with unknown error')); - } - }, onError); - - this._sendingPromises.push(promise); - const popPromise = () => { - const index = this._sendingPromises.indexOf(promise); - this._sendingPromises.splice(index, 1); - }; - promise.then(popPromise, popPromise); - } - - onShutdown(): void {} -} diff --git a/experimental/packages/otlp-exporter-base/src/platform/node/convert-legacy-agent-options.ts b/experimental/packages/otlp-exporter-base/src/platform/node/convert-legacy-agent-options.ts deleted file mode 100644 index a73c5b7ead..0000000000 --- a/experimental/packages/otlp-exporter-base/src/platform/node/convert-legacy-agent-options.ts +++ /dev/null @@ -1,46 +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 { OTLPExporterNodeConfigBase } from './types'; -import type * as http from 'http'; -import type * as https from 'https'; - -/** - * Replicates old config behavior where there's two ways to set keepAlive. - * This function sets keepAlive in AgentOptions if it is defined. In the future, we will remove - * this duplicate to only allow setting keepAlive in AgentOptions. - * @param config - */ -export function convertLegacyAgentOptions( - config: OTLPExporterNodeConfigBase -): http.AgentOptions | https.AgentOptions { - // populate keepAlive for use with new settings - if (config?.keepAlive != null) { - if (config.httpAgentOptions != null) { - if (config.httpAgentOptions.keepAlive == null) { - // specific setting is not set, populate with non-specific setting. - config.httpAgentOptions.keepAlive = config.keepAlive; - } - // do nothing, use specific setting otherwise - } else { - // populate specific option if AgentOptions does not exist. - config.httpAgentOptions = { - keepAlive: config.keepAlive, - }; - } - } - - return config.httpAgentOptions ?? { keepAlive: true }; -} diff --git a/experimental/packages/otlp-exporter-base/src/platform/node/http-exporter-transport.ts b/experimental/packages/otlp-exporter-base/src/transport/http-exporter-transport.ts similarity index 95% rename from experimental/packages/otlp-exporter-base/src/platform/node/http-exporter-transport.ts rename to experimental/packages/otlp-exporter-base/src/transport/http-exporter-transport.ts index 1b638f7c41..bdeb2db00e 100644 --- a/experimental/packages/otlp-exporter-base/src/platform/node/http-exporter-transport.ts +++ b/experimental/packages/otlp-exporter-base/src/transport/http-exporter-transport.ts @@ -23,8 +23,8 @@ import type { // as they'd be imported before the http/https modules can be wrapped. import type * as https from 'https'; import type * as http from 'http'; -import { ExportResponse } from '../../export-response'; -import { IExporterTransport } from '../../exporter-transport'; +import { ExportResponse } from '../export-response'; +import { IExporterTransport } from '../exporter-transport'; class HttpExporterTransport implements IExporterTransport { private _send: sendWithHttp | null = null; diff --git a/experimental/packages/otlp-exporter-base/src/platform/node/http-transport-types.ts b/experimental/packages/otlp-exporter-base/src/transport/http-transport-types.ts similarity index 94% rename from experimental/packages/otlp-exporter-base/src/platform/node/http-transport-types.ts rename to experimental/packages/otlp-exporter-base/src/transport/http-transport-types.ts index 1a041aedf2..07ee5b9daf 100644 --- a/experimental/packages/otlp-exporter-base/src/platform/node/http-transport-types.ts +++ b/experimental/packages/otlp-exporter-base/src/transport/http-transport-types.ts @@ -16,7 +16,7 @@ import type * as http from 'http'; import type * as https from 'https'; -import { ExportResponse } from '../../export-response'; +import { ExportResponse } from '../export-response'; export type sendWithHttp = ( params: HttpRequestParameters, diff --git a/experimental/packages/otlp-exporter-base/src/platform/node/http-transport-utils.ts b/experimental/packages/otlp-exporter-base/src/transport/http-transport-utils.ts similarity index 96% rename from experimental/packages/otlp-exporter-base/src/platform/node/http-transport-utils.ts rename to experimental/packages/otlp-exporter-base/src/transport/http-transport-utils.ts index 8fa7529fdc..06c2ceef59 100644 --- a/experimental/packages/otlp-exporter-base/src/platform/node/http-transport-utils.ts +++ b/experimental/packages/otlp-exporter-base/src/transport/http-transport-utils.ts @@ -18,12 +18,12 @@ import * as https from 'https'; import * as zlib from 'zlib'; import { Readable } from 'stream'; import { HttpRequestParameters } from './http-transport-types'; -import { ExportResponse } from '../../export-response'; +import { ExportResponse } from '../export-response'; import { isExportRetryable, parseRetryAfterToMills, -} from '../../is-export-retryable'; -import { OTLPExporterError } from '../../types'; +} from '../is-export-retryable'; +import { OTLPExporterError } from '../types'; /** * Sends data using http diff --git a/experimental/packages/otlp-exporter-base/src/platform/browser/send-beacon-transport.ts b/experimental/packages/otlp-exporter-base/src/transport/send-beacon-transport.ts similarity index 93% rename from experimental/packages/otlp-exporter-base/src/platform/browser/send-beacon-transport.ts rename to experimental/packages/otlp-exporter-base/src/transport/send-beacon-transport.ts index 79f9bd9ff8..448265c27b 100644 --- a/experimental/packages/otlp-exporter-base/src/platform/browser/send-beacon-transport.ts +++ b/experimental/packages/otlp-exporter-base/src/transport/send-beacon-transport.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { IExporterTransport } from '../../exporter-transport'; -import { ExportResponse } from '../../export-response'; +import { IExporterTransport } from '../exporter-transport'; +import { ExportResponse } from '../export-response'; import { diag } from '@opentelemetry/api'; export interface SendBeaconParameters { diff --git a/experimental/packages/otlp-exporter-base/src/platform/browser/xhr-transport.ts b/experimental/packages/otlp-exporter-base/src/transport/xhr-transport.ts similarity index 94% rename from experimental/packages/otlp-exporter-base/src/platform/browser/xhr-transport.ts rename to experimental/packages/otlp-exporter-base/src/transport/xhr-transport.ts index 6517a994b6..36d497549a 100644 --- a/experimental/packages/otlp-exporter-base/src/platform/browser/xhr-transport.ts +++ b/experimental/packages/otlp-exporter-base/src/transport/xhr-transport.ts @@ -14,13 +14,13 @@ * limitations under the License. */ -import { IExporterTransport } from '../../exporter-transport'; -import { ExportResponse } from '../../export-response'; +import { IExporterTransport } from '../exporter-transport'; +import { ExportResponse } from '../export-response'; import { diag } from '@opentelemetry/api'; import { isExportRetryable, parseRetryAfterToMills, -} from '../../is-export-retryable'; +} from '../is-export-retryable'; export interface XhrRequestParameters { url: string; diff --git a/experimental/packages/otlp-exporter-base/src/types.ts b/experimental/packages/otlp-exporter-base/src/types.ts index 7964f0048c..8d1dae72b2 100644 --- a/experimental/packages/otlp-exporter-base/src/types.ts +++ b/experimental/packages/otlp-exporter-base/src/types.ts @@ -40,15 +40,3 @@ export interface ExportServiceError { message: string; stack: string; } - -/** - * Collector Exporter base config - */ -export interface OTLPExporterConfigBase { - headers?: Record; - url?: string; - concurrencyLimit?: number; - /** Maximum time the OTLP exporter will wait for each batch export. - * The default value is 10000ms. */ - timeoutMillis?: number; -} diff --git a/experimental/packages/otlp-exporter-base/test/browser/send-beacon-transport.test.ts b/experimental/packages/otlp-exporter-base/test/browser/send-beacon-transport.test.ts index e108df7f00..86a44d0435 100644 --- a/experimental/packages/otlp-exporter-base/test/browser/send-beacon-transport.test.ts +++ b/experimental/packages/otlp-exporter-base/test/browser/send-beacon-transport.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import * as sinon from 'sinon'; -import { createSendBeaconTransport } from '../../src/platform/browser/send-beacon-transport'; +import { createSendBeaconTransport } from '../../src/transport/send-beacon-transport'; import * as assert from 'assert'; describe('SendBeaconTransport', function () { diff --git a/experimental/packages/otlp-exporter-base/test/browser/xhr-transport.test.ts b/experimental/packages/otlp-exporter-base/test/browser/xhr-transport.test.ts index f1efbe130d..af14aa41f2 100644 --- a/experimental/packages/otlp-exporter-base/test/browser/xhr-transport.test.ts +++ b/experimental/packages/otlp-exporter-base/test/browser/xhr-transport.test.ts @@ -16,7 +16,7 @@ import * as sinon from 'sinon'; import * as assert from 'assert'; -import { createXhrTransport } from '../../src/platform/browser/xhr-transport'; +import { createXhrTransport } from '../../src/transport/xhr-transport'; import { ExportResponseRetryable, ExportResponseFailure, diff --git a/experimental/packages/otlp-exporter-base/test/common/CollectorExporter.test.ts b/experimental/packages/otlp-exporter-base/test/common/CollectorExporter.test.ts deleted file mode 100644 index 89874f353d..0000000000 --- a/experimental/packages/otlp-exporter-base/test/common/CollectorExporter.test.ts +++ /dev/null @@ -1,184 +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 { ExportResultCode } from '@opentelemetry/core'; -import * as assert from 'assert'; -import * as sinon from 'sinon'; -import { OTLPExporterBase } from '../../src/OTLPExporterBase'; -import { OTLPExporterConfigBase } from '../../src/types'; -import { ComplexTestObject, mockedComplexTestObject } from '../testHelper'; -import * as otlpTypes from '../../src/types'; - -type CollectorExporterConfig = OTLPExporterConfigBase; -class OTLPTraceExporter extends OTLPExporterBase< - CollectorExporterConfig, - ComplexTestObject -> { - onShutdown() {} - send( - items: any[], - onSuccess: () => void, - onError: (error: otlpTypes.OTLPExporterError) => void - ) { - const promise = Promise.resolve(null); - this._sendingPromises.push( - promise.then(() => - this._sendingPromises.splice(this._sendingPromises.indexOf(promise), 1) - ) - ); - } - getDefaultUrl(config: CollectorExporterConfig): string { - return config.url || ''; - } -} - -describe('OTLPTraceExporter - common', () => { - let collectorExporter: OTLPTraceExporter; - let collectorExporterConfig: CollectorExporterConfig; - - afterEach(() => { - sinon.restore(); - }); - - describe('constructor', () => { - beforeEach(() => { - collectorExporterConfig = { - url: 'http://foo.bar.com', - }; - collectorExporter = new OTLPTraceExporter(collectorExporterConfig); - }); - - it('should create an instance', () => { - assert.ok(typeof collectorExporter !== 'undefined'); - }); - }); - - describe('export', () => { - let spySend: any; - beforeEach(() => { - spySend = sinon.stub(OTLPTraceExporter.prototype, 'send'); - collectorExporter = new OTLPTraceExporter(collectorExporterConfig); - }); - - it('should export spans as otlpTypes.Spans', done => { - const spans: ComplexTestObject[] = []; - spans.push(Object.assign({}, mockedComplexTestObject)); - - collectorExporter.export(spans, () => {}); - setTimeout(() => { - const span1 = spySend.args[0][0][0] as ComplexTestObject; - assert.deepStrictEqual(spans[0], span1); - done(); - }); - assert.strictEqual(spySend.callCount, 1); - }); - - describe('when exporter is shutdown', () => { - it( - 'should not export anything but return callback with code' + - ' "FailedNotRetryable"', - async () => { - const spans: ComplexTestObject[] = []; - spans.push(Object.assign({}, mockedComplexTestObject)); - await collectorExporter.shutdown(); - spySend.resetHistory(); - - const callbackSpy = sinon.spy(); - collectorExporter.export(spans, callbackSpy); - const returnCode = callbackSpy.args[0][0]; - - assert.strictEqual( - returnCode.code, - ExportResultCode.FAILED, - 'return value is wrong' - ); - assert.strictEqual(spySend.callCount, 0, 'should not call send'); - } - ); - }); - describe('when an error occurs', () => { - it('should return failed export result', done => { - const spans: ComplexTestObject[] = []; - spans.push(Object.assign({}, mockedComplexTestObject)); - spySend.throws({ - code: 100, - details: 'Test error', - metadata: {}, - message: 'Non-retryable', - stack: 'Stack', - }); - const callbackSpy = sinon.spy(); - collectorExporter.export(spans, callbackSpy); - setTimeout(() => { - const returnCode = callbackSpy.args[0][0]; - assert.strictEqual( - returnCode.code, - ExportResultCode.FAILED, - 'return value is wrong' - ); - assert.strictEqual( - returnCode.error.message, - 'Non-retryable', - 'return error message is wrong' - ); - assert.strictEqual(spySend.callCount, 1, 'should call send'); - done(); - }); - }); - }); - }); - describe('export - concurrency limit', () => { - it('should error if too many concurrent exports are queued', done => { - const collectorExporterWithConcurrencyLimit = new OTLPTraceExporter({ - ...collectorExporterConfig, - concurrencyLimit: 3, - }); - const spans: ComplexTestObject[] = [{ ...mockedComplexTestObject }]; - const callbackSpy = sinon.spy(); - for (let i = 0; i < 7; i++) { - collectorExporterWithConcurrencyLimit.export(spans, callbackSpy); - } - - setTimeout(() => { - // Expect 4 failures - assert.strictEqual(callbackSpy.args.length, 4); - callbackSpy.args.forEach(([result]) => { - assert.strictEqual(result.code, ExportResultCode.FAILED); - assert.strictEqual( - result.error!.message, - 'Concurrent export limit reached' - ); - }); - done(); - }); - }); - }); - describe('shutdown', () => { - let onShutdownSpy: any; - beforeEach(() => { - onShutdownSpy = sinon.stub(OTLPTraceExporter.prototype, 'onShutdown'); - collectorExporterConfig = { - url: 'http://foo.bar.com', - }; - collectorExporter = new OTLPTraceExporter(collectorExporterConfig); - }); - - it('should call onShutdown', async () => { - await collectorExporter.shutdown(); - assert.strictEqual(onShutdownSpy.callCount, 1); - }); - }); -}); diff --git a/experimental/packages/otlp-exporter-base/test/common/configuration/otlp-http-configuration.test.ts b/experimental/packages/otlp-exporter-base/test/common/configuration/otlp-http-configuration.test.ts index 03d040b14f..a5197deee5 100644 --- a/experimental/packages/otlp-exporter-base/test/common/configuration/otlp-http-configuration.test.ts +++ b/experimental/packages/otlp-exporter-base/test/common/configuration/otlp-http-configuration.test.ts @@ -28,6 +28,7 @@ describe('mergeOtlpHttpConfigurationWithDefaults', function () { compression: 'none', concurrencyLimit: 2, headers: { 'User-Agent': 'default-user-agent' }, + agentOptions: { keepAlive: true }, }; describe('headers', function () { diff --git a/experimental/packages/otlp-exporter-base/test/node/configuration/shared-env-configuration.test.ts b/experimental/packages/otlp-exporter-base/test/node/configuration/shared-env-configuration.test.ts index 51d55d4980..28f38c4e35 100644 --- a/experimental/packages/otlp-exporter-base/test/node/configuration/shared-env-configuration.test.ts +++ b/experimental/packages/otlp-exporter-base/test/node/configuration/shared-env-configuration.test.ts @@ -18,10 +18,8 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; import { diag } from '@opentelemetry/api'; import * as process from 'process'; -import { - getSharedConfigurationFromEnvironment, - OtlpSharedConfiguration, -} from '../../../src'; +import { OtlpSharedConfiguration } from '../../../src'; +import { getSharedConfigurationFromEnvironment } from '../../../src/index-node-http'; export function testSharedConfigurationFromEnvironment( sut: (signalIdentifier: string) => Partial diff --git a/experimental/packages/otlp-exporter-base/test/node/http-exporter-transport.test.ts b/experimental/packages/otlp-exporter-base/test/node/http-exporter-transport.test.ts index f94a58760c..6d9c935025 100644 --- a/experimental/packages/otlp-exporter-base/test/node/http-exporter-transport.test.ts +++ b/experimental/packages/otlp-exporter-base/test/node/http-exporter-transport.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { createHttpExporterTransport } from '../../src/platform/node/http-exporter-transport'; +import { createHttpExporterTransport } from '../../src/transport/http-exporter-transport'; import * as http from 'http'; import * as assert from 'assert'; import sinon = require('sinon'); diff --git a/experimental/packages/otlp-exporter-base/test/node/util.test.ts b/experimental/packages/otlp-exporter-base/test/node/util.test.ts deleted file mode 100644 index 93b70db197..0000000000 --- a/experimental/packages/otlp-exporter-base/test/node/util.test.ts +++ /dev/null @@ -1,36 +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 { OTLPExporterNodeBase } from '../../src/platform/node/OTLPExporterNodeBase'; -import { ISerializer } from '@opentelemetry/otlp-transformer'; - -class Exporter extends OTLPExporterNodeBase {} - -const noopSerializer: ISerializer = { - serializeRequest(request: object): Uint8Array | undefined { - return new Uint8Array(); - }, - deserializeResponse(data: Uint8Array): object { - return {}; - }, -}; - -describe('force flush', () => { - it('forceFlush should flush spans and return', async () => { - const exporter = new Exporter({}, noopSerializer, {}, 'TEST', 'v1/test'); - await exporter.forceFlush(); - }); -}); diff --git a/experimental/packages/otlp-grpc-exporter-base/src/OTLPGRPCExporterNodeBase.ts b/experimental/packages/otlp-grpc-exporter-base/src/OTLPGRPCExporterNodeBase.ts deleted file mode 100644 index e2ba61779b..0000000000 --- a/experimental/packages/otlp-grpc-exporter-base/src/OTLPGRPCExporterNodeBase.ts +++ /dev/null @@ -1,133 +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 { diag } from '@opentelemetry/api'; -import { OTLPGRPCExporterConfigNode } from './types'; -import { - OTLPExporterBase, - OTLPExporterError, -} from '@opentelemetry/otlp-exporter-base'; -import { - createEmptyMetadata, - GrpcExporterTransport, -} from './grpc-exporter-transport'; -import { ISerializer } from '@opentelemetry/otlp-transformer'; -import { IExporterTransport } from '@opentelemetry/otlp-exporter-base'; -import { - getOtlpGrpcDefaultConfiguration, - mergeOtlpGrpcConfigurationWithDefaults, -} from './configuration/otlp-grpc-configuration'; -import { getOtlpGrpcConfigurationFromEnv } from './configuration/otlp-grpc-env-configuration'; - -/** - * OTLP Exporter abstract base class - */ -export abstract class OTLPGRPCExporterNodeBase< - ExportItem, - ServiceResponse, -> extends OTLPExporterBase { - private _transport: IExporterTransport; - private _serializer: ISerializer; - private _timeoutMillis: number; - - constructor( - config: OTLPGRPCExporterConfigNode = {}, - serializer: ISerializer, - grpcName: string, - grpcPath: string, - signalIdentifier: string - ) { - super(config); - // keep credentials locally in case user updates the reference on the config object - const userProvidedCredentials = config.credentials; - const actualConfig = mergeOtlpGrpcConfigurationWithDefaults( - { - url: config.url, - metadata: () => { - // metadata resolution strategy is merge, so we can return empty here, and it will not override the rest of the settings. - return config.metadata ?? createEmptyMetadata(); - }, - compression: config.compression, - timeoutMillis: config.timeoutMillis, - concurrencyLimit: config.concurrencyLimit, - credentials: - userProvidedCredentials != null - ? () => userProvidedCredentials - : undefined, - }, - getOtlpGrpcConfigurationFromEnv(signalIdentifier), - getOtlpGrpcDefaultConfiguration() - ); - this._serializer = serializer; - this._timeoutMillis = actualConfig.timeoutMillis; - this._concurrencyLimit = actualConfig.concurrencyLimit; - if (config.headers) { - diag.warn('Headers cannot be set when using grpc'); - } - - this._transport = new GrpcExporterTransport({ - address: actualConfig.url, - compression: actualConfig.compression, - credentials: actualConfig.credentials, - grpcName: grpcName, - grpcPath: grpcPath, - metadata: actualConfig.metadata, - }); - } - - override onShutdown() { - this._transport.shutdown(); - } - - send( - objects: ExportItem[], - onSuccess: () => void, - onError: (error: OTLPExporterError) => void - ): void { - if (this._shutdownOnce.isCalled) { - diag.debug('Shutdown already started. Cannot send objects'); - return; - } - - const data = this._serializer.serializeRequest(objects); - - if (data == null) { - onError(new Error('Could not serialize message')); - return; - } - - const promise = this._transport - .send(data, this._timeoutMillis) - .then(response => { - if (response.status === 'success') { - onSuccess(); - } else if (response.status === 'failure' && response.error) { - onError(response.error); - } else if (response.status === 'retryable') { - onError(new OTLPExporterError('Export failed with retryable status')); - } else { - onError(new OTLPExporterError('Export failed with unknown error')); - } - }, onError); - - this._sendingPromises.push(promise); - const popPromise = () => { - const index = this._sendingPromises.indexOf(promise); - this._sendingPromises.splice(index, 1); - }; - promise.then(popPromise, popPromise); - } -} diff --git a/experimental/packages/otlp-grpc-exporter-base/src/configuration/convert-legacy-otlp-grpc-options.ts b/experimental/packages/otlp-grpc-exporter-base/src/configuration/convert-legacy-otlp-grpc-options.ts new file mode 100644 index 0000000000..2967e213d5 --- /dev/null +++ b/experimental/packages/otlp-grpc-exporter-base/src/configuration/convert-legacy-otlp-grpc-options.ts @@ -0,0 +1,54 @@ +/* + * 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 { OTLPGRPCExporterConfigNode } from '../types'; +import { diag } from '@opentelemetry/api'; +import { + getOtlpGrpcDefaultConfiguration, + mergeOtlpGrpcConfigurationWithDefaults, + OtlpGrpcConfiguration, +} from './otlp-grpc-configuration'; +import { createEmptyMetadata } from '../grpc-exporter-transport'; +import { getOtlpGrpcConfigurationFromEnv } from './otlp-grpc-env-configuration'; + +export function convertLegacyOtlpGrpcOptions( + config: OTLPGRPCExporterConfigNode, + signalIdentifier: string +): OtlpGrpcConfiguration { + if (config.headers) { + diag.warn('Headers cannot be set when using grpc'); + } + + // keep credentials locally in case user updates the reference on the config object + const userProvidedCredentials = config.credentials; + return mergeOtlpGrpcConfigurationWithDefaults( + { + url: config.url, + metadata: () => { + // metadata resolution strategy is merge, so we can return empty here, and it will not override the rest of the settings. + return config.metadata ?? createEmptyMetadata(); + }, + compression: config.compression, + timeoutMillis: config.timeoutMillis, + concurrencyLimit: config.concurrencyLimit, + credentials: + userProvidedCredentials != null + ? () => userProvidedCredentials + : undefined, + }, + getOtlpGrpcConfigurationFromEnv(signalIdentifier), + getOtlpGrpcDefaultConfiguration() + ); +} diff --git a/experimental/packages/otlp-grpc-exporter-base/src/configuration/otlp-grpc-env-configuration.ts b/experimental/packages/otlp-grpc-exporter-base/src/configuration/otlp-grpc-env-configuration.ts index 6ad8926604..e0c10adcd4 100644 --- a/experimental/packages/otlp-grpc-exporter-base/src/configuration/otlp-grpc-env-configuration.ts +++ b/experimental/packages/otlp-grpc-exporter-base/src/configuration/otlp-grpc-env-configuration.ts @@ -21,7 +21,7 @@ import { createInsecureCredentials, createSslCredentials, } from '../grpc-exporter-transport'; -import { getSharedConfigurationFromEnvironment } from '@opentelemetry/otlp-exporter-base'; +import { getSharedConfigurationFromEnvironment } from '@opentelemetry/otlp-exporter-base/node-http'; import * as fs from 'fs'; import * as path from 'path'; import { diag } from '@opentelemetry/api'; diff --git a/experimental/packages/otlp-grpc-exporter-base/src/grpc-exporter-transport.ts b/experimental/packages/otlp-grpc-exporter-base/src/grpc-exporter-transport.ts index d3f3b193e6..40044d9448 100644 --- a/experimental/packages/otlp-grpc-exporter-base/src/grpc-exporter-transport.ts +++ b/experimental/packages/otlp-grpc-exporter-base/src/grpc-exporter-transport.ts @@ -177,3 +177,9 @@ export class GrpcExporterTransport implements IExporterTransport { }); } } + +export function createOtlpGrpcExporterTransport( + options: GrpcExporterTransportParameters +): IExporterTransport { + return new GrpcExporterTransport(options); +} diff --git a/experimental/packages/otlp-grpc-exporter-base/src/index.ts b/experimental/packages/otlp-grpc-exporter-base/src/index.ts index 3a445ed91b..ef58b2a034 100644 --- a/experimental/packages/otlp-grpc-exporter-base/src/index.ts +++ b/experimental/packages/otlp-grpc-exporter-base/src/index.ts @@ -14,5 +14,6 @@ * limitations under the License. */ -export { OTLPGRPCExporterNodeBase } from './OTLPGRPCExporterNodeBase'; +export { convertLegacyOtlpGrpcOptions } from './configuration/convert-legacy-otlp-grpc-options'; +export { createOtlpGrpcExportDelegate } from './otlp-grpc-export-delegate'; export { OTLPGRPCExporterConfigNode } from './types'; diff --git a/experimental/packages/otlp-grpc-exporter-base/src/otlp-grpc-export-delegate.ts b/experimental/packages/otlp-grpc-exporter-base/src/otlp-grpc-export-delegate.ts new file mode 100644 index 0000000000..9873ce413e --- /dev/null +++ b/experimental/packages/otlp-grpc-exporter-base/src/otlp-grpc-export-delegate.ts @@ -0,0 +1,43 @@ +/* + * 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 { + createOtlpNetworkExportDelegate, + IOLTPExportDelegate, +} from '@opentelemetry/otlp-exporter-base'; +import { ISerializer } from '@opentelemetry/otlp-transformer'; +import { OtlpGrpcConfiguration } from './configuration/otlp-grpc-configuration'; +import { createOtlpGrpcExporterTransport } from './grpc-exporter-transport'; + +export function createOtlpGrpcExportDelegate( + options: OtlpGrpcConfiguration, + serializer: ISerializer, + grpcName: string, + grpcPath: string +): IOLTPExportDelegate { + return createOtlpNetworkExportDelegate( + options, + serializer, + createOtlpGrpcExporterTransport({ + address: options.url, + compression: options.compression, + credentials: options.credentials, + metadata: options.metadata, + grpcName, + grpcPath, + }) + ); +} diff --git a/experimental/packages/otlp-grpc-exporter-base/test/OTLPGRPCExporterNodeBase.test.ts b/experimental/packages/otlp-grpc-exporter-base/test/OTLPGRPCExporterNodeBase.test.ts deleted file mode 100644 index 037fc53895..0000000000 --- a/experimental/packages/otlp-grpc-exporter-base/test/OTLPGRPCExporterNodeBase.test.ts +++ /dev/null @@ -1,196 +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 { ReadableSpan } from '@opentelemetry/sdk-trace-base'; -import * as assert from 'assert'; -import { OTLPGRPCExporterNodeBase } from '../src/OTLPGRPCExporterNodeBase'; -import { mockedReadableSpan } from './traceHelper'; -import { - ExportResponse, - ExportResponseSuccess, - IExporterTransport, -} from '@opentelemetry/otlp-exporter-base'; -import { ISerializer } from '@opentelemetry/otlp-transformer'; -import sinon = require('sinon'); - -class MockCollectorExporter extends OTLPGRPCExporterNodeBase< - ReadableSpan, - any -> {} - -const successfulResponse: ExportResponseSuccess = { - status: 'success', -}; - -describe('OTLPGRPCExporterNodeBase', () => { - let exporter: MockCollectorExporter; - const concurrencyLimit = 5; - - beforeEach(done => { - const transportStubs = { - // make transport succeed - send: sinon.stub().resolves(successfulResponse), - shutdown: sinon.stub(), - }; - const mockTransport = transportStubs; - - const serializerStubs = { - serializeRequest: sinon.stub().resolves(Buffer.from([1, 2, 3])), - deserializeResponse: sinon - .stub() - .resolves({ responseKey: 'responseValue' }), - }; - - const serializer = >serializerStubs; - - exporter = new MockCollectorExporter( - { concurrencyLimit }, - serializer, - 'grpcName', - 'grpcPath', - 'SIGNAL' - ); - - exporter['_transport'] = mockTransport; - done(); - }); - - afterEach(function () { - sinon.restore(); - }); - - describe('export', () => { - it('should export requests concurrently', async () => { - const sendResolveFunctions: ((response: ExportResponse) => void)[] = []; - const transportStubs = { - send: sinon.stub().returns( - new Promise(resolve => { - sendResolveFunctions.push(resolve); - }) - ), - shutdown: sinon.stub(), - }; - exporter['_transport'] = transportStubs; - - const spans = [Object.assign({}, mockedReadableSpan)]; - const numToExport = concurrencyLimit; - - for (let i = 0; i < numToExport; ++i) { - exporter.export(spans, () => {}); - } - - assert.strictEqual(exporter['_sendingPromises'].length, numToExport); - const promisesAllDone = Promise.all(exporter['_sendingPromises']); - // Mock that all requests finish sending - sendResolveFunctions.forEach(resolve => resolve(successfulResponse)); - - // All finished promises should be popped off - await promisesAllDone; - assert.strictEqual(exporter['_sendingPromises'].length, 0); - }); - - it('should drop new export requests when already sending at concurrencyLimit', async () => { - const sendResolveFunctions: ((response: ExportResponse) => void)[] = []; - const transportStubs = { - send: sinon.stub().returns( - new Promise(resolve => { - sendResolveFunctions.push(resolve); - }) - ), - shutdown: sinon.stub(), - }; - exporter['_transport'] = transportStubs; - - const spans = [Object.assign({}, mockedReadableSpan)]; - const numToExport = concurrencyLimit + 5; - - for (let i = 0; i < numToExport; ++i) { - exporter.export(spans, () => {}); - } - - assert.strictEqual(exporter['_sendingPromises'].length, concurrencyLimit); - const promisesAllDone = Promise.all(exporter['_sendingPromises']); - // Mock that all requests finish sending - sendResolveFunctions.forEach(resolve => resolve(successfulResponse)); - - // All finished promises should be popped off - await promisesAllDone; - assert.strictEqual(exporter['_sendingPromises'].length, 0); - }); - - it('should pop export request promises even if they failed', async () => { - const sendRejectFunctions: ((error: Error) => void)[] = []; - const transportStubs = { - send: sinon.stub().returns( - new Promise((_, reject) => { - sendRejectFunctions.push(reject); - }) - ), - shutdown: sinon.stub(), - }; - exporter['_transport'] = transportStubs; - - const spans = [Object.assign({}, mockedReadableSpan)]; - - exporter.export(spans, () => {}); - assert.strictEqual(exporter['_sendingPromises'].length, 1); - const promisesAllDone = Promise.all(exporter['_sendingPromises']); - // Mock that all requests fail sending - sendRejectFunctions.forEach(reject => reject(new Error('export failed'))); - - // All finished promises should be popped off - await promisesAllDone; - assert.strictEqual(exporter['_sendingPromises'].length, 0); - }); - - it('should pop export request promises even if resolve throws error', async () => { - const transportStubs = { - send: sinon.stub().returns( - new Promise(_ => { - throw new Error('this failed'); - }) - ), - shutdown: sinon.stub(), - }; - exporter['_transport'] = transportStubs; - - const spans = [Object.assign({}, mockedReadableSpan)]; - exporter.export(spans, () => {}); - - assert.strictEqual(exporter['_sendingPromises'].length, 1); - - const promisesAllDone = Promise.all(exporter['_sendingPromises']) - // catch expected unhandled exception - .catch(() => {}); - - // All finished promises should be popped off - await promisesAllDone; - assert.strictEqual(exporter['_sendingPromises'].length, 0); - }); - }); - - describe('shutdown', function () { - it('calls shutdown on transport', function () { - const transportStubs = { - send: sinon.stub(), - shutdown: sinon.stub(), - }; - exporter['_transport'] = transportStubs; - exporter.shutdown(); - sinon.assert.calledOnce(transportStubs.shutdown); - }); - }); -}); From b11928bc6bc088dd78dfd0cfa25b8fb782fcd8d8 Mon Sep 17 00:00:00 2001 From: Marc Pichler Date: Fri, 20 Sep 2024 13:20:37 +0200 Subject: [PATCH 2/2] depreacte --- .../src/configuration/convert-legacy-otlp-grpc-options.ts | 5 +++++ experimental/packages/otlp-grpc-exporter-base/src/types.ts | 2 ++ 2 files changed, 7 insertions(+) diff --git a/experimental/packages/otlp-grpc-exporter-base/src/configuration/convert-legacy-otlp-grpc-options.ts b/experimental/packages/otlp-grpc-exporter-base/src/configuration/convert-legacy-otlp-grpc-options.ts index 2967e213d5..2144f647fc 100644 --- a/experimental/packages/otlp-grpc-exporter-base/src/configuration/convert-legacy-otlp-grpc-options.ts +++ b/experimental/packages/otlp-grpc-exporter-base/src/configuration/convert-legacy-otlp-grpc-options.ts @@ -23,6 +23,11 @@ import { import { createEmptyMetadata } from '../grpc-exporter-transport'; import { getOtlpGrpcConfigurationFromEnv } from './otlp-grpc-env-configuration'; +/** + * @deprecated + * @param config + * @param signalIdentifier + */ export function convertLegacyOtlpGrpcOptions( config: OTLPGRPCExporterConfigNode, signalIdentifier: string diff --git a/experimental/packages/otlp-grpc-exporter-base/src/types.ts b/experimental/packages/otlp-grpc-exporter-base/src/types.ts index 61f5613de3..778c848546 100644 --- a/experimental/packages/otlp-grpc-exporter-base/src/types.ts +++ b/experimental/packages/otlp-grpc-exporter-base/src/types.ts @@ -22,6 +22,8 @@ import { } from '@opentelemetry/otlp-exporter-base'; /** + * @deprecated + * * OTLP Exporter Config for Node */ export interface OTLPGRPCExporterConfigNode extends OTLPExporterConfigBase {