From bbfaae791bce2cb3016b42789ff520c73cdcb58e Mon Sep 17 00:00:00 2001 From: davidwitten Date: Thu, 18 Jun 2020 20:18:36 +0000 Subject: [PATCH] Added another test --- .../src/platform/browser/index.ts | 1 - .../platform/node/CollectorMetricExporter.ts | 3 +- .../src/transform.ts | 47 +++-- .../src/types.ts | 9 +- .../common/CollectorMetricExporter.test.ts | 173 ++++++++++++++++++ 5 files changed, 201 insertions(+), 32 deletions(-) create mode 100644 packages/opentelemetry-exporter-collector/test/common/CollectorMetricExporter.test.ts diff --git a/packages/opentelemetry-exporter-collector/src/platform/browser/index.ts b/packages/opentelemetry-exporter-collector/src/platform/browser/index.ts index cc8467f9a12..971b8166de3 100644 --- a/packages/opentelemetry-exporter-collector/src/platform/browser/index.ts +++ b/packages/opentelemetry-exporter-collector/src/platform/browser/index.ts @@ -15,5 +15,4 @@ */ export * from './CollectorExporter'; - export * from './CollectorMetricExporter'; diff --git a/packages/opentelemetry-exporter-collector/src/platform/node/CollectorMetricExporter.ts b/packages/opentelemetry-exporter-collector/src/platform/node/CollectorMetricExporter.ts index 5722571d878..a690e1121ce 100644 --- a/packages/opentelemetry-exporter-collector/src/platform/node/CollectorMetricExporter.ts +++ b/packages/opentelemetry-exporter-collector/src/platform/node/CollectorMetricExporter.ts @@ -31,7 +31,6 @@ const DEFAULT_COLLECTOR_URL = 'localhost:55678'; */ export class CollectorMetricExporter extends CollectorMetricExporterBase { grpcMetricsQueue: GRPCMetricQueueItem[] = []; - metricServiceClient?: MetricsServiceClient = undefined; credentials: grpc.ChannelCredentials; isShutDown: boolean = false; @@ -103,7 +102,7 @@ export class CollectorMetricExporter extends CollectorMetricExporterBase { if (err) { this.logger.error( 'exportTraceServiceRequest', - {} // exportMetricServiceRequest + exportMetricServiceRequest ); onError(err); } else { diff --git a/packages/opentelemetry-exporter-collector/src/transform.ts b/packages/opentelemetry-exporter-collector/src/transform.ts index c50b695d8ba..10844210c95 100644 --- a/packages/opentelemetry-exporter-collector/src/transform.ts +++ b/packages/opentelemetry-exporter-collector/src/transform.ts @@ -248,16 +248,6 @@ export function toCollectorExportTraceServiceRequest< }; } -export function toStringKeyValue(labels: { - [key: string]: string; -}): opentelemetryProto.common.v1.StringKeyValue[] { - const collectorLabels: opentelemetryProto.common.v1.StringKeyValue[] = []; - for (const [key, value] of Object.entries(labels)) { - collectorLabels.push({ key: key, value: value }); - } - return []; -} - export function getCollectorPoints(metric: MetricRecord) { const metricKind = metric.descriptor.metricKind; const valueType = metric.descriptor.valueType; @@ -268,7 +258,8 @@ export function toCollectorMetricDescriptor( metric: MetricRecord ): opentelemetryProto.metrics.v1.MetricDescriptor { let type: opentelemetryProto.metrics.v1.MetricDescriptor_Type; - const temporality = opentelemetryProto.metrics.v1.MetricDescriptor_Temporality.CUMULATIVE; + const temporality = + opentelemetryProto.metrics.v1.MetricDescriptor_Temporality.CUMULATIVE; if (metric.descriptor.valueType === apiValueType.INT) { if (metric.descriptor.monotonic) { @@ -287,7 +278,6 @@ export function toCollectorMetricDescriptor( } else { type = opentelemetryProto.metrics.v1.MetricDescriptor_Type.INVALID_TYPE; } - return { name: metric.descriptor.name, @@ -303,21 +293,30 @@ export function toCollectorMetric( metric: MetricRecord, startTime: number ): opentelemetryProto.metrics.v1.Metric { + let int64DataPoints: opentelemetryProto.metrics.v1.Int64DataPoint[] = []; + let doubleDataPoints: opentelemetryProto.metrics.v1.DoubleDataPoint[] = []; + + const points = { + labels: toCollectorLabels(metric.labels), + value: metric.aggregator.toPoint().value as number, + startTimeUnixNano: startTime, + timeUnixNano: core.hrTimeToNanoseconds( + metric.aggregator.toPoint().timestamp + ), + }; + + if (metric.descriptor.valueType == apiValueType.INT) { + int64DataPoints = [points]; + } else if (metric.descriptor.valueType === apiValueType.DOUBLE) { + doubleDataPoints = [points]; + } + return { metricDescriptor: toCollectorMetricDescriptor(metric), - doubleDataPoints: [], - histogramDataPoints: [], + doubleDataPoints, + int64DataPoints, summaryDataPoints: [], - int64DataPoints: [ - { - labels: toCollectorLabels(metric.labels), - value: metric.aggregator.toPoint().value as number, - startTimeUnixNano: startTime, - timeUnixNano: core.hrTimeToNanoseconds( - metric.aggregator.toPoint().timestamp - ), - }, - ], + histogramDataPoints: [], }; } diff --git a/packages/opentelemetry-exporter-collector/src/types.ts b/packages/opentelemetry-exporter-collector/src/types.ts index f346770df42..34cf8eac202 100644 --- a/packages/opentelemetry-exporter-collector/src/types.ts +++ b/packages/opentelemetry-exporter-collector/src/types.ts @@ -92,13 +92,12 @@ export namespace opentelemetryProto { value: number; count: number; sum: number; - buckets: opentelemetryProto.metrics.v1.HistogramDataPoint_Bucket; + buckets: opentelemetryProto.metrics.v1.HistogramDataPointBucket; explicitBounds: number[]; } - export interface HistogramDataPoint_Bucket { + export interface HistogramDataPointBucket { count: number; - exemplar: number; // CHANGE LATER } export interface SummaryDataPoint { @@ -108,10 +107,10 @@ export namespace opentelemetryProto { value: number; count: number; sum: number; - percentileValues: opentelemetryProto.metrics.v1.SummaryDataPoint_ValueAtPercentile[]; + percentileValues: opentelemetryProto.metrics.v1.SummaryDataPointValueAtPercentile[]; } - export interface SummaryDataPoint_ValueAtPercentile { + export interface SummaryDataPointValueAtPercentile { percentile: number; value: number; } diff --git a/packages/opentelemetry-exporter-collector/test/common/CollectorMetricExporter.test.ts b/packages/opentelemetry-exporter-collector/test/common/CollectorMetricExporter.test.ts new file mode 100644 index 00000000000..e773f66b1aa --- /dev/null +++ b/packages/opentelemetry-exporter-collector/test/common/CollectorMetricExporter.test.ts @@ -0,0 +1,173 @@ +/*! + * Copyright 2020, 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, NoopLogger } from '@opentelemetry/core'; +import * as assert from 'assert'; +import * as sinon from 'sinon'; +import { CollectorMetricExporterBase } from '../../src/CollectorMetricExporterBase'; +import { ExporterOptions } from '../../src/types'; +import { MetricRecord, MeterProvider } from '@opentelemetry/metrics'; +import { Labels } from '@opentelemetry/api'; + +class CollectorMetricExporter extends CollectorMetricExporterBase { + onInit() {} + onShutdown() {} + sendMetrics() {} + getDefaultUrl(url: string) { return url || '';} +} + +describe('CollectorMetricExporter - common', () => { + let collectorExporter: CollectorMetricExporter; + let collectorExporterConfig: ExporterOptions; + let records: MetricRecord[]; + describe('constructor', () => { + let onInitSpy: any; + + beforeEach(() => { + onInitSpy = sinon.stub(CollectorMetricExporter.prototype, 'onInit'); + collectorExporterConfig = { + hostName: 'foo', + logger: new NoopLogger(), + serviceName: 'bar', + attributes: {}, + url: 'http://foo.bar.com', + }; + collectorExporter = new CollectorMetricExporter(collectorExporterConfig); + const meter = new MeterProvider().getMeter('test-meter'); + const labels: Labels = { ['keyb']: 'value2', ['keya']: 'value1' }; + const counter = meter.createCounter('name', { + labelKeys: ['keya', 'keyb'], + }); + counter.bind(labels).add(10); + meter.collect(); + records = meter.getBatcher().checkPointSet(); + }); + + afterEach(() => { + onInitSpy.restore(); + }); + + it('should create an instance', () => { + assert.ok(typeof collectorExporter !== 'undefined'); + }); + + it('should call onInit', () => { + assert.strictEqual(onInitSpy.callCount, 1); + }); + + describe('when config contains certain params', () => { + it('should set hostName', () => { + assert.strictEqual(collectorExporter.hostName, 'foo'); + }); + + it('should set serviceName', () => { + assert.strictEqual(collectorExporter.serviceName, 'bar'); + }); + + it('should set url', () => { + assert.strictEqual(collectorExporter.url, 'http://foo.bar.com'); + }); + + it('should set logger', () => { + assert.ok(collectorExporter.logger === collectorExporterConfig.logger); + }); + }); + + describe('when config is missing certain params', () => { + beforeEach(() => { + collectorExporter = new CollectorMetricExporter(); + }); + + it('should set default serviceName', () => { + assert.strictEqual(collectorExporter.serviceName, 'collector-metric-exporter'); + }); + + it('should set default logger', () => { + assert.ok(collectorExporter.logger instanceof NoopLogger); + }); + }); + }); + + describe('export', () => { + let spySend: any; + beforeEach(() => { + spySend = sinon.stub(CollectorMetricExporter.prototype, 'sendMetrics'); + collectorExporter = new CollectorMetricExporter(collectorExporterConfig); + }); + afterEach(() => { + spySend.restore(); + }); + + it('should export spans as collectorTypes.Spans', done => { + const metrics: MetricRecord[] = []; + metrics.push(Object.assign({}, records[0])); + + collectorExporter.export(metrics, () => {}); + setTimeout(() => { + const metric1 = spySend.args[0][0][0] as MetricRecord; + assert.deepStrictEqual(metrics[0], metric1); + done(); + }); + assert.strictEqual(spySend.callCount, 1); + }); + + describe('when exporter is shutdown', () => { + it('should not export anything but return callback with code "FailedNotRetryable"', () => { + const metrics: MetricRecord[] = []; + metrics.push(Object.assign({}, records[0])); + collectorExporter.shutdown(); + spySend.resetHistory(); + + const callbackSpy = sinon.spy(); + collectorExporter.export(metrics, callbackSpy); + const returnCode = callbackSpy.args[0][0]; + + assert.strictEqual( + returnCode, + ExportResult.FAILED_NOT_RETRYABLE, + 'return value is wrong' + ); + assert.strictEqual(spySend.callCount, 0, 'should not call send'); + }); + }); + }); + + describe('shutdown', () => { + let onShutdownSpy: any; + beforeEach(() => { + onShutdownSpy = sinon.stub(CollectorMetricExporter.prototype, 'onShutdown'); + collectorExporterConfig = { + hostName: 'foo', + logger: new NoopLogger(), + serviceName: 'bar', + attributes: {}, + url: 'http://foo.bar.com', + }; + collectorExporter = new CollectorMetricExporter(collectorExporterConfig); + }); + afterEach(() => { + onShutdownSpy.restore(); + }); + + it('should call onShutdown', done => { + collectorExporter.shutdown(); + setTimeout(() => { + assert.equal(onShutdownSpy.callCount, 1); + done(); + }); + }); + }); +});