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 9181b082cf9..e0d4660f220 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,10 +19,13 @@ import type { LogRecordExporter, } from '@opentelemetry/sdk-logs'; import type { OTLPExporterNodeConfigBase } from '@opentelemetry/otlp-exporter-base'; -import type { IExportLogsServiceRequest } from '@opentelemetry/otlp-transformer'; +import type { + IExportLogsServiceRequest, + IExportLogsServiceResponse, +} from '@opentelemetry/otlp-transformer'; import { getEnv, baggageUtils } from '@opentelemetry/core'; import { OTLPExporterNodeBase } from '@opentelemetry/otlp-exporter-base'; -import { createExportLogsServiceRequest } from '@opentelemetry/otlp-transformer'; +import { JsonLogsSerializer } from '@opentelemetry/otlp-transformer'; import { getDefaultUrl } from '../config'; import { VERSION } from '../../version'; @@ -35,15 +38,23 @@ const USER_AGENT = { * Collector Logs Exporter for Node */ export class OTLPLogExporter - extends OTLPExporterNodeBase + extends OTLPExporterNodeBase< + ReadableLogRecord, + IExportLogsServiceRequest, + IExportLogsServiceResponse + > implements LogRecordExporter { constructor(config: OTLPExporterNodeConfigBase = {}) { // load OTEL_EXPORTER_OTLP_LOGS_TIMEOUT env - super({ - timeoutMillis: getEnv().OTEL_EXPORTER_OTLP_LOGS_TIMEOUT, - ...config, - }); + super( + { + timeoutMillis: getEnv().OTEL_EXPORTER_OTLP_LOGS_TIMEOUT, + ...config, + }, + JsonLogsSerializer, + 'application/json' + ); this.headers = { ...this.headers, ...USER_AGENT, @@ -54,13 +65,6 @@ export class OTLPLogExporter }; } - convert(logRecords: ReadableLogRecord[]): IExportLogsServiceRequest { - return createExportLogsServiceRequest(logRecords, { - useHex: true, - useLongBits: false, - }); - } - getDefaultUrl(config: OTLPExporterNodeConfigBase): string { return getDefaultUrl(config); } 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 caec7352fff..f27ca229e08 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 @@ -19,14 +19,13 @@ import { OTLPExporterConfigBase, appendResourcePathToUrl, appendRootPathToUrlIfNeeded, + OTLPExporterNodeBase, } from '@opentelemetry/otlp-exporter-base'; +import { ServiceClientType } from '@opentelemetry/otlp-proto-exporter-base'; import { - OTLPProtoExporterNodeBase, - ServiceClientType, -} from '@opentelemetry/otlp-proto-exporter-base'; -import { - createExportLogsServiceRequest, IExportLogsServiceRequest, + IExportLogsServiceResponse, + ProtobufLogsSerializer, } from '@opentelemetry/otlp-transformer'; import { ReadableLogRecord, LogRecordExporter } from '@opentelemetry/sdk-logs'; @@ -43,14 +42,15 @@ const DEFAULT_COLLECTOR_URL = `http://localhost:4318/${DEFAULT_COLLECTOR_RESOURC * Collector Trace Exporter for Node */ export class OTLPLogExporter - extends OTLPProtoExporterNodeBase< + extends OTLPExporterNodeBase< ReadableLogRecord, - IExportLogsServiceRequest + IExportLogsServiceRequest, + IExportLogsServiceResponse > implements LogRecordExporter { constructor(config: OTLPExporterConfigBase = {}) { - super(config); + super(config, ProtobufLogsSerializer, 'application/x-protobuf'); this.headers = { ...this.headers, ...USER_AGENT, @@ -60,9 +60,6 @@ export class OTLPLogExporter ...config.headers, }; } - convert(logs: ReadableLogRecord[]): IExportLogsServiceRequest { - return createExportLogsServiceRequest(logs); - } getDefaultUrl(config: OTLPExporterConfigBase): string { return typeof config.url === 'string' diff --git a/experimental/packages/exporter-logs-otlp-proto/test/node/OTLPLogExporter.test.ts b/experimental/packages/exporter-logs-otlp-proto/test/node/OTLPLogExporter.test.ts index 9778c950464..8bf14362062 100644 --- a/experimental/packages/exporter-logs-otlp-proto/test/node/OTLPLogExporter.test.ts +++ b/experimental/packages/exporter-logs-otlp-proto/test/node/OTLPLogExporter.test.ts @@ -193,8 +193,6 @@ describe('OTLPLogExporter - node with proto over http', () => { }); it('should open the connection', done => { - collectorExporter.export(logs, () => {}); - sinon.stub(http, 'request').callsFake((options: any, cb: any) => { assert.strictEqual(options.hostname, 'foo.bar.com'); assert.strictEqual(options.method, 'POST'); @@ -206,11 +204,10 @@ describe('OTLPLogExporter - node with proto over http', () => { done(); return fakeRequest as any; }); + collectorExporter.export(logs, () => {}); }); it('should set custom headers', done => { - collectorExporter.export(logs, () => {}); - sinon.stub(http, 'request').callsFake((options: any, cb: any) => { assert.strictEqual(options.headers['foo'], 'bar'); @@ -220,11 +217,10 @@ describe('OTLPLogExporter - node with proto over http', () => { done(); return fakeRequest as any; }); + collectorExporter.export(logs, () => {}); }); it('should have keep alive and keepAliveMsecs option set', done => { - collectorExporter.export(logs, () => {}); - sinon.stub(http, 'request').callsFake((options: any, cb: any) => { assert.strictEqual(options.agent.keepAlive, true); assert.strictEqual(options.agent.options.keepAliveMsecs, 2000); @@ -235,6 +231,7 @@ describe('OTLPLogExporter - node with proto over http', () => { done(); return fakeRequest as any; }); + collectorExporter.export(logs, () => {}); }); it('should successfully send the logs', done => { @@ -271,28 +268,21 @@ describe('OTLPLogExporter - node with proto over http', () => { // Need to stub/spy on the underlying logger as the "diag" instance is global const spyLoggerError = sinon.stub(diag, 'error'); - collectorExporter.export(logs, result => { - assert.strictEqual(result.code, ExportResultCode.SUCCESS); - assert.strictEqual(spyLoggerError.args.length, 0); - done(); - }); - sinon.stub(http, 'request').callsFake((options: any, cb: any) => { const mockRes = new MockedResponse(200); cb(mockRes); mockRes.send('success'); return fakeRequest as any; }); - }); - it('should log the error message', done => { collectorExporter.export(logs, result => { - assert.strictEqual(result.code, ExportResultCode.FAILED); - // @ts-expect-error verify error code - assert.strictEqual(result.error.code, 400); + assert.strictEqual(result.code, ExportResultCode.SUCCESS); + assert.strictEqual(spyLoggerError.args.length, 0); done(); }); + }); + it('should log the error message', done => { sinon.stub(http, 'request').callsFake((options: any, cb: any) => { const mockResError = new MockedResponse(400); cb(mockResError); @@ -300,6 +290,13 @@ describe('OTLPLogExporter - node with proto over http', () => { return fakeRequest as any; }); + + collectorExporter.export(logs, result => { + assert.strictEqual(result.code, ExportResultCode.FAILED); + // @ts-expect-error verify error code + assert.strictEqual(result.error.code, 400); + done(); + }); }); }); describe('export - with compression', () => { 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 aeb3b94ca72..b829ee76fdd 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 @@ -23,10 +23,11 @@ import { appendRootPathToUrlIfNeeded, } from '@opentelemetry/otlp-exporter-base'; import { - createExportTraceServiceRequest, IExportTraceServiceRequest, + IExportTraceServiceResponse, } from '@opentelemetry/otlp-transformer'; import { VERSION } from '../../version'; +import { JsonTraceSerializer } from '@opentelemetry/otlp-transformer'; const DEFAULT_COLLECTOR_RESOURCE_PATH = 'v1/traces'; const DEFAULT_COLLECTOR_URL = `http://localhost:4318/${DEFAULT_COLLECTOR_RESOURCE_PATH}`; @@ -38,11 +39,15 @@ const USER_AGENT = { * Collector Trace Exporter for Node */ export class OTLPTraceExporter - extends OTLPExporterNodeBase + extends OTLPExporterNodeBase< + ReadableSpan, + IExportTraceServiceRequest, + IExportTraceServiceResponse + > implements SpanExporter { constructor(config: OTLPExporterNodeConfigBase = {}) { - super(config); + super(config, JsonTraceSerializer, 'application/json'); this.headers = { ...this.headers, ...USER_AGENT, @@ -53,13 +58,6 @@ export class OTLPTraceExporter }; } - convert(spans: ReadableSpan[]): IExportTraceServiceRequest { - return createExportTraceServiceRequest(spans, { - useHex: true, - useLongBits: false, - }); - } - getDefaultUrl(config: OTLPExporterNodeConfigBase): string { return typeof config.url === 'string' ? config.url 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 be115583cd6..8a0d222ae7f 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 @@ -20,14 +20,13 @@ import { OTLPExporterNodeConfigBase, appendResourcePathToUrl, appendRootPathToUrlIfNeeded, + OTLPExporterNodeBase, } from '@opentelemetry/otlp-exporter-base'; +import { ServiceClientType } from '@opentelemetry/otlp-proto-exporter-base'; import { - OTLPProtoExporterNodeBase, - ServiceClientType, -} from '@opentelemetry/otlp-proto-exporter-base'; -import { - createExportTraceServiceRequest, IExportTraceServiceRequest, + IExportTraceServiceResponse, + ProtobufTraceSerializer, } from '@opentelemetry/otlp-transformer'; import { VERSION } from '../../version'; @@ -41,11 +40,15 @@ const USER_AGENT = { * Collector Trace Exporter for Node with protobuf */ export class OTLPTraceExporter - extends OTLPProtoExporterNodeBase + extends OTLPExporterNodeBase< + ReadableSpan, + IExportTraceServiceRequest, + IExportTraceServiceResponse + > implements SpanExporter { constructor(config: OTLPExporterNodeConfigBase = {}) { - super(config); + super(config, ProtobufTraceSerializer, 'application/x-protobuf'); this.headers = { ...this.headers, ...USER_AGENT, @@ -56,10 +59,6 @@ export class OTLPTraceExporter }; } - convert(spans: ReadableSpan[]): IExportTraceServiceRequest { - return createExportTraceServiceRequest(spans); - } - getDefaultUrl(config: OTLPExporterNodeConfigBase) { return typeof config.url === 'string' ? config.url 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 b18c5a39deb..7dfbb101fe5 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 @@ -197,8 +197,6 @@ describe('OTLPTraceExporter - node with proto over http', () => { }); it('should open the connection', done => { - collectorExporter.export(spans, () => {}); - sinon.stub(http, 'request').callsFake((options: any, cb: any) => { assert.strictEqual(options.hostname, 'foo.bar.com'); assert.strictEqual(options.method, 'POST'); @@ -210,11 +208,11 @@ describe('OTLPTraceExporter - node with proto over http', () => { done(); return fakeRequest as any; }); - }); - it('should set custom headers', done => { collectorExporter.export(spans, () => {}); + }); + it('should set custom headers', done => { sinon.stub(http, 'request').callsFake((options: any, cb: any) => { assert.strictEqual(options.headers['foo'], 'bar'); @@ -224,11 +222,11 @@ describe('OTLPTraceExporter - node with proto over http', () => { done(); return fakeRequest as any; }); - }); - it('should have keep alive and keepAliveMsecs option set', done => { collectorExporter.export(spans, () => {}); + }); + it('should have keep alive and keepAliveMsecs option set', done => { sinon.stub(http, 'request').callsFake((options: any, cb: any) => { assert.strictEqual(options.agent.keepAlive, true); assert.strictEqual(options.agent.options.keepAliveMsecs, 2000); @@ -239,6 +237,8 @@ describe('OTLPTraceExporter - node with proto over http', () => { done(); return fakeRequest as any; }); + + collectorExporter.export(spans, () => {}); }); it('should successfully send the spans', done => { @@ -275,28 +275,21 @@ describe('OTLPTraceExporter - node with proto over http', () => { // Need to stub/spy on the underlying logger as the "diag" instance is global const spyLoggerError = sinon.stub(diag, 'error'); - collectorExporter.export(spans, result => { - assert.strictEqual(result.code, ExportResultCode.SUCCESS); - assert.strictEqual(spyLoggerError.args.length, 0); - done(); - }); - sinon.stub(http, 'request').callsFake((options: any, cb: any) => { const mockRes = new MockedResponse(200); cb(mockRes); mockRes.send('success'); return fakeRequest as any; }); - }); - it('should log the error message', done => { collectorExporter.export(spans, result => { - assert.strictEqual(result.code, ExportResultCode.FAILED); - // @ts-expect-error verify error code - assert.strictEqual(result.error.code, 400); + assert.strictEqual(result.code, ExportResultCode.SUCCESS); + assert.strictEqual(spyLoggerError.args.length, 0); done(); }); + }); + it('should log the error message', done => { sinon.stub(http, 'request').callsFake((options: any, cb: any) => { const mockResError = new MockedResponse(400); cb(mockResError); @@ -304,6 +297,13 @@ describe('OTLPTraceExporter - node with proto over http', () => { return fakeRequest as any; }); + + collectorExporter.export(spans, result => { + assert.strictEqual(result.code, ExportResultCode.FAILED); + // @ts-expect-error verify error code + assert.strictEqual(result.error.code, 400); + done(); + }); }); }); describe('export - with compression', () => { 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 cd648ce8753..f52fe8583e2 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 @@ -25,8 +25,9 @@ import { appendRootPathToUrlIfNeeded, } from '@opentelemetry/otlp-exporter-base'; import { - createExportMetricsServiceRequest, IExportMetricsServiceRequest, + IExportMetricsServiceResponse, + JsonMetricsSerializer, } from '@opentelemetry/otlp-transformer'; import { VERSION } from '../../version'; @@ -38,10 +39,11 @@ const USER_AGENT = { class OTLPExporterNodeProxy extends OTLPExporterNodeBase< ResourceMetrics, - IExportMetricsServiceRequest + IExportMetricsServiceRequest, + IExportMetricsServiceResponse > { constructor(config?: OTLPExporterNodeConfigBase & OTLPMetricExporterOptions) { - super(config); + super(config, JsonMetricsSerializer, 'application/json'); this.headers = { ...this.headers, ...USER_AGENT, @@ -52,10 +54,6 @@ class OTLPExporterNodeProxy extends OTLPExporterNodeBase< }; } - convert(metrics: ResourceMetrics[]): IExportMetricsServiceRequest { - return createExportMetricsServiceRequest(metrics, { useLongBits: false }); - } - getDefaultUrl(config: OTLPExporterNodeConfigBase): string { return typeof config.url === 'string' ? config.url 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 52c9991e30a..fceac9d95af 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/src/OTLPMetricExporter.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/src/OTLPMetricExporter.ts @@ -15,10 +15,7 @@ */ import { OTLPMetricExporterOptions } from '@opentelemetry/exporter-metrics-otlp-http'; -import { - ServiceClientType, - OTLPProtoExporterNodeBase, -} from '@opentelemetry/otlp-proto-exporter-base'; +import { ServiceClientType } from '@opentelemetry/otlp-proto-exporter-base'; import { getEnv, baggageUtils } from '@opentelemetry/core'; import { ResourceMetrics } from '@opentelemetry/sdk-metrics'; import { OTLPMetricExporterBase } from '@opentelemetry/exporter-metrics-otlp-http'; @@ -26,10 +23,12 @@ import { OTLPExporterNodeConfigBase, appendResourcePathToUrl, appendRootPathToUrlIfNeeded, + OTLPExporterNodeBase, } from '@opentelemetry/otlp-exporter-base'; import { - createExportMetricsServiceRequest, IExportMetricsServiceRequest, + IExportMetricsServiceResponse, + ProtobufMetricsSerializer, } from '@opentelemetry/otlp-transformer'; import { VERSION } from './version'; @@ -39,12 +38,13 @@ const USER_AGENT = { 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, }; -class OTLPMetricExporterNodeProxy extends OTLPProtoExporterNodeBase< +class OTLPMetricExporterNodeProxy extends OTLPExporterNodeBase< ResourceMetrics, - IExportMetricsServiceRequest + IExportMetricsServiceRequest, + IExportMetricsServiceResponse > { constructor(config?: OTLPExporterNodeConfigBase & OTLPMetricExporterOptions) { - super(config); + super(config, ProtobufMetricsSerializer, 'application/x-protobuf'); this.headers = { ...this.headers, ...USER_AGENT, @@ -55,10 +55,6 @@ class OTLPMetricExporterNodeProxy extends OTLPProtoExporterNodeBase< }; } - convert(metrics: ResourceMetrics[]): IExportMetricsServiceRequest { - return createExportMetricsServiceRequest(metrics); - } - getDefaultUrl(config: OTLPExporterNodeConfigBase) { return typeof config.url === 'string' ? config.url 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 51c175f8e6c..8398b2a3f77 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 @@ -228,8 +228,6 @@ describe('OTLPMetricExporter - node with proto over http', () => { }); it('should open the connection', done => { - collectorExporter.export(metrics, () => {}); - sinon.stub(http, 'request').callsFake((options: any, cb: any) => { assert.strictEqual(options.hostname, 'foo.bar.com'); assert.strictEqual(options.method, 'POST'); @@ -241,11 +239,11 @@ describe('OTLPMetricExporter - node with proto over http', () => { done(); return fakeRequest as any; }); - }); - it('should set custom headers', done => { collectorExporter.export(metrics, () => {}); + }); + it('should set custom headers', done => { sinon.stub(http, 'request').callsFake((options: any, cb: any) => { assert.strictEqual(options.headers['foo'], 'bar'); @@ -256,11 +254,11 @@ describe('OTLPMetricExporter - node with proto over http', () => { done(); return fakeRequest as any; }); - }); - it('should have keep alive and keepAliveMsecs option set', done => { collectorExporter.export(metrics, () => {}); + }); + it('should have keep alive and keepAliveMsecs option set', done => { sinon.stub(http, 'request').callsFake((options: any, cb: any) => { assert.strictEqual(options.agent.keepAlive, true); assert.strictEqual(options.agent.options.keepAliveMsecs, 2000); @@ -272,6 +270,8 @@ describe('OTLPMetricExporter - node with proto over http', () => { done(); return fakeRequest as any; }); + + collectorExporter.export(metrics, () => {}); }); it('should successfully send metrics', done => { @@ -353,28 +353,21 @@ describe('OTLPMetricExporter - node with proto over http', () => { // Need to stub/spy on the underlying logger as the "diag" instance is global const spyLoggerError = sinon.stub(diag, 'error'); - collectorExporter.export(metrics, result => { - assert.strictEqual(result.code, ExportResultCode.SUCCESS); - assert.strictEqual(spyLoggerError.args.length, 0); - done(); - }); - sinon.stub(http, 'request').callsFake((options: any, cb: any) => { const mockRes = new MockedResponse(200); cb(mockRes); mockRes.send('success'); return fakeRequest as any; }); - }); - it('should log the error message', done => { collectorExporter.export(metrics, result => { - assert.strictEqual(result.code, ExportResultCode.FAILED); - // @ts-expect-error verify error code - assert.strictEqual(result.error.code, 400); + assert.strictEqual(result.code, ExportResultCode.SUCCESS); + assert.strictEqual(spyLoggerError.args.length, 0); done(); }); + }); + it('should log the error message', done => { sinon.stub(http, 'request').callsFake((options: any, cb: any) => { const mockResError = new MockedResponse(400); cb(mockResError); @@ -382,6 +375,13 @@ describe('OTLPMetricExporter - node with proto over http', () => { return fakeRequest as any; }); + + collectorExporter.export(metrics, result => { + assert.strictEqual(result.code, ExportResultCode.FAILED); + // @ts-expect-error verify error code + assert.strictEqual(result.error.code, 400); + done(); + }); }); }); }); diff --git a/experimental/packages/otlp-exporter-base/src/platform/node/OTLPExporterNodeBase.ts b/experimental/packages/otlp-exporter-base/src/platform/node/OTLPExporterNodeBase.ts index 5a8b1dfdfa5..c4b13e8218d 100644 --- a/experimental/packages/otlp-exporter-base/src/platform/node/OTLPExporterNodeBase.ts +++ b/experimental/packages/otlp-exporter-base/src/platform/node/OTLPExporterNodeBase.ts @@ -24,6 +24,7 @@ import { parseHeaders } from '../../util'; import { createHttpAgent, sendWithHttp, configureCompression } from './util'; import { diag } from '@opentelemetry/api'; import { getEnv, baggageUtils } from '@opentelemetry/core'; +import { ISerializer } from '@opentelemetry/otlp-transformer'; /** * Collector Metric Exporter abstract base class @@ -31,6 +32,7 @@ import { getEnv, baggageUtils } from '@opentelemetry/core'; export abstract class OTLPExporterNodeBase< ExportItem, ServiceRequest, + ServiceResponse, > extends OTLPExporterBase< OTLPExporterNodeConfigBase, ExportItem, @@ -40,9 +42,16 @@ export abstract class OTLPExporterNodeBase< headers: Record; agent: http.Agent | https.Agent | undefined; compression: CompressionAlgorithm; + private _serializer: ISerializer; + private _contentType: string; - constructor(config: OTLPExporterNodeConfigBase = {}) { + constructor( + config: OTLPExporterNodeConfigBase = {}, + serializer: ISerializer, + contentType: string + ) { super(config); + this._contentType = contentType; // eslint-disable-next-line @typescript-eslint/no-explicit-any if ((config as any).metadata) { diag.warn('Metadata cannot be set when using http'); @@ -54,10 +63,16 @@ export abstract class OTLPExporterNodeBase< ); this.agent = createHttpAgent(config); this.compression = configureCompression(config.compression); + this._serializer = serializer; } onInit(_config: OTLPExporterNodeConfigBase): void {} + override convert(_objects: ExportItem[]): ServiceRequest { + // TODO(pichlermarc): needs to be removed from base in a follow-up + return {} as ServiceRequest; + } + send( objects: ExportItem[], onSuccess: () => void, @@ -67,13 +82,12 @@ export abstract class OTLPExporterNodeBase< diag.debug('Shutdown already started. Cannot send objects'); return; } - const serviceRequest = this.convert(objects); const promise = new Promise((resolve, reject) => { sendWithHttp( this, - JSON.stringify(serviceRequest), - 'application/json', + this._serializer.serializeRequest(objects) ?? new Uint8Array(), + this._contentType, resolve, reject ); diff --git a/experimental/packages/otlp-exporter-base/src/platform/node/util.ts b/experimental/packages/otlp-exporter-base/src/platform/node/util.ts index fd40981e857..0bc8587abc5 100644 --- a/experimental/packages/otlp-exporter-base/src/platform/node/util.ts +++ b/experimental/packages/otlp-exporter-base/src/platform/node/util.ts @@ -41,9 +41,9 @@ import { * @param onSuccess * @param onError */ -export function sendWithHttp( - collector: OTLPExporterNodeBase, - data: string | Buffer, +export function sendWithHttp( + collector: OTLPExporterNodeBase, + data: string | Uint8Array, contentType: string, onSuccess: () => void, onError: (error: OTLPExporterError) => void @@ -165,7 +165,7 @@ export function sendWithHttp( switch (collector.compression) { case CompressionAlgorithm.GZIP: { req.setHeader('Content-Encoding', 'gzip'); - const dataStream = readableFromBuffer(data); + const dataStream = readableFromUnit8Array(data); dataStream .on('error', onError) .pipe(zlib.createGzip()) @@ -182,7 +182,7 @@ export function sendWithHttp( sendWithRetry(); } -function readableFromBuffer(buff: string | Buffer): Readable { +function readableFromUnit8Array(buff: string | Uint8Array): Readable { const readable = new Readable(); readable.push(buff); readable.push(null); diff --git a/experimental/packages/otlp-exporter-base/test/node/util.test.ts b/experimental/packages/otlp-exporter-base/test/node/util.test.ts index b279e57b9a1..6886422f3de 100644 --- a/experimental/packages/otlp-exporter-base/test/node/util.test.ts +++ b/experimental/packages/otlp-exporter-base/test/node/util.test.ts @@ -28,6 +28,7 @@ import { OTLPExporterError } from '../../src/types'; import { PassThrough } from 'stream'; import * as http from 'http'; import * as zlib from 'zlib'; +import { ISerializer } from '@opentelemetry/otlp-transformer'; // Meant to simulate http.IncomingMessage, at least the parts that sendWithHttp cares about // but make it a PassThrough so we can inspect it for the test @@ -50,19 +51,24 @@ class HttpRequest extends PassThrough { // Barebones exporter for use by sendWithHttp type ExporterConfig = OTLPExporterNodeConfigBase; -class Exporter extends OTLPExporterNodeBase { +class Exporter extends OTLPExporterNodeBase { getDefaultUrl(config: ExporterConfig): string { return config.url || ''; } +} - convert(spans: object[]): object { +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({}); + const exporter = new Exporter({}, noopSerializer, ''); await exporter.forceFlush(); }); }); @@ -219,10 +225,14 @@ describe('sendWithHttp', () => { }); it('should send with no compression if configured to do so', () => { - exporter = new Exporter({ - url: 'http://foobar.com', - compression: CompressionAlgorithm.NONE, - }); + exporter = new Exporter( + { + url: 'http://foobar.com', + compression: CompressionAlgorithm.NONE, + }, + noopSerializer, + '' + ); const data = JSON.stringify(spanData); // Show that data is written to the request stream @@ -255,7 +265,7 @@ describe('sendWithHttp', () => { exporter = new Exporter({ url: 'http://foobar.com', compression: CompressionAlgorithm.GZIP, - }); + }, noopSerializer, ''); const data = JSON.stringify(spanData); const compressedData = zlib.gzipSync(Buffer.from(data)); @@ -290,7 +300,7 @@ describe('sendWithHttp', () => { exporter = new Exporter({ url: 'http://foobar.com', compression: CompressionAlgorithm.GZIP, - }); + }, noopSerializer, ''); const data = JSON.stringify(spanData); const compressedData = zlib.gzipSync(Buffer.from(data)); diff --git a/experimental/packages/otlp-proto-exporter-base/src/platform/index.ts b/experimental/packages/otlp-proto-exporter-base/src/platform/index.ts index fc344756ea5..f6ead82b842 100644 --- a/experimental/packages/otlp-proto-exporter-base/src/platform/index.ts +++ b/experimental/packages/otlp-proto-exporter-base/src/platform/index.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -export { OTLPProtoExporterNodeBase, send } from './node'; export { OTLPProtoExporterBrowserBase } from './browser'; export { ServiceClientType } from './types'; export { ExportRequestType, getExportRequestProto } from './util'; diff --git a/experimental/packages/otlp-proto-exporter-base/src/platform/node/OTLPProtoExporterNodeBase.ts b/experimental/packages/otlp-proto-exporter-base/src/platform/node/OTLPProtoExporterNodeBase.ts deleted file mode 100644 index 17804c47b49..00000000000 --- a/experimental/packages/otlp-proto-exporter-base/src/platform/node/OTLPProtoExporterNodeBase.ts +++ /dev/null @@ -1,88 +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 { ServiceClientType } from '../types'; -import { - OTLPExporterNodeBase as OTLPExporterBaseMain, - CompressionAlgorithm, - OTLPExporterError, - OTLPExporterNodeConfigBase, -} from '@opentelemetry/otlp-exporter-base'; - -type SendFn = ( - collector: OTLPProtoExporterNodeBase, - objects: ExportItem[], - compression: CompressionAlgorithm, - onSuccess: () => void, - onError: (error: OTLPExporterError) => void -) => void; - -/** - * Collector Exporter abstract base class - */ -export abstract class OTLPProtoExporterNodeBase< - ExportItem, - ServiceRequest, -> extends OTLPExporterBaseMain { - private _send!: SendFn; - - constructor(config: OTLPExporterNodeConfigBase = {}) { - super(config); - } - - private _sendPromise( - objects: ExportItem[], - onSuccess: () => void, - onError: (error: OTLPExporterError) => void - ): void { - const promise = new Promise((resolve, reject) => { - this._send(this, objects, this.compression, resolve, reject); - }).then(onSuccess, onError); - - this._sendingPromises.push(promise); - const popPromise = () => { - const index = this._sendingPromises.indexOf(promise); - this._sendingPromises.splice(index, 1); - }; - promise.then(popPromise, popPromise); - } - - override send( - objects: ExportItem[], - onSuccess: () => void, - onError: (error: OTLPExporterError) => void - ): void { - if (this._shutdownOnce.isCalled) { - diag.debug('Shutdown already started. Cannot send objects'); - return; - } - if (!this._send) { - // defer to next tick and lazy load to avoid loading protobufjs too early - // and making this impossible to be instrumented - setImmediate(() => { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const { send } = require('./util'); - this._send = send; - this._sendPromise(objects, onSuccess, onError); - }); - } else { - this._sendPromise(objects, onSuccess, onError); - } - } - - abstract getServiceClientType(): ServiceClientType; -} diff --git a/experimental/packages/otlp-proto-exporter-base/src/platform/node/index.ts b/experimental/packages/otlp-proto-exporter-base/src/platform/node/index.ts deleted file mode 100644 index 08016fec935..00000000000 --- a/experimental/packages/otlp-proto-exporter-base/src/platform/node/index.ts +++ /dev/null @@ -1,18 +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. - */ - -export { OTLPProtoExporterNodeBase } from './OTLPProtoExporterNodeBase'; -export { send } from './util'; diff --git a/experimental/packages/otlp-proto-exporter-base/src/platform/node/util.ts b/experimental/packages/otlp-proto-exporter-base/src/platform/node/util.ts deleted file mode 100644 index 36fe866d678..00000000000 --- a/experimental/packages/otlp-proto-exporter-base/src/platform/node/util.ts +++ /dev/null @@ -1,53 +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 { OTLPProtoExporterNodeBase } from './OTLPProtoExporterNodeBase'; -import { - CompressionAlgorithm, - OTLPExporterError, - sendWithHttp, -} from '@opentelemetry/otlp-exporter-base'; - -import { getExportRequestProto } from '../util'; - -export function send( - collector: OTLPProtoExporterNodeBase, - objects: ExportItem[], - compression: CompressionAlgorithm, - onSuccess: () => void, - onError: (error: OTLPExporterError) => void -): void { - const serviceRequest = collector.convert(objects); - - const exportRequestType = getExportRequestProto( - collector.getServiceClientType() - ); - const message = exportRequestType.create(serviceRequest); - if (message) { - const body = exportRequestType.encode(message).finish(); - if (body) { - sendWithHttp( - collector, - Buffer.from(body), - 'application/x-protobuf', - onSuccess, - onError - ); - } - } else { - onError(new OTLPExporterError('No proto')); - } -} diff --git a/experimental/packages/otlp-transformer/src/index.ts b/experimental/packages/otlp-transformer/src/index.ts index 6f7ca0009a9..66040a07c0b 100644 --- a/experimental/packages/otlp-transformer/src/index.ts +++ b/experimental/packages/otlp-transformer/src/index.ts @@ -31,4 +31,10 @@ export { ProtobufTraceSerializer, } from './protobuf/serializers'; +export { + JsonTraceSerializer, + JsonLogsSerializer, + JsonMetricsSerializer, +} from './json/serializers'; + export { ISerializer } from './common/i-serializer';