From 0dd2ea7470ebede21856f385b5f5a672d667b98d Mon Sep 17 00:00:00 2001 From: Mayur Kale Date: Wed, 16 Oct 2019 17:06:34 -0700 Subject: [PATCH 01/15] feat(metrics): add registerMetric and getMetric functionality --- packages/opentelemetry-metrics/src/Meter.ts | 34 +++++++++++- packages/opentelemetry-metrics/src/Metric.ts | 45 ++++++++++++++-- .../opentelemetry-metrics/src/export/types.ts | 4 +- packages/opentelemetry-metrics/src/index.ts | 1 + .../opentelemetry-metrics/test/Meter.test.ts | 53 ++++++++++++++++++- 5 files changed, 128 insertions(+), 9 deletions(-) diff --git a/packages/opentelemetry-metrics/src/Meter.ts b/packages/opentelemetry-metrics/src/Meter.ts index 9d889530af4..8a3237c92e1 100644 --- a/packages/opentelemetry-metrics/src/Meter.ts +++ b/packages/opentelemetry-metrics/src/Meter.ts @@ -28,12 +28,14 @@ import { DEFAULT_CONFIG, MeterConfig, } from './types'; +import { ReadableMetric } from './export/types'; /** * Meter is an implementation of the {@link Meter} interface. */ export class Meter implements types.Meter { private readonly _logger: types.Logger; + private readonly _metrics = new Map(); /** * Constructs a new Meter instance. @@ -85,7 +87,9 @@ export class Meter implements types.Meter { ...DEFAULT_METRIC_OPTIONS, ...options, }; - return new CounterMetric(name, opt); + const counter = new CounterMetric(name, opt); + this._registerMetric(name, counter); + return counter; } /** @@ -113,7 +117,33 @@ export class Meter implements types.Meter { ...DEFAULT_METRIC_OPTIONS, ...options, }; - return new GaugeMetric(name, opt); + const gauge = new GaugeMetric(name, opt); + this._registerMetric(name, gauge); + return gauge; + } + + /** + * Gets a collection of Metric`s to be exported. + * @returns The list of metrics. + */ + getMetrics(): ReadableMetric[] { + return Array.from(this._metrics.values()) + .map(metric => metric.getMetric()) + .filter(metric => !!metric) as ReadableMetric[]; + } + + /** + * Registers metric to register. + * @param name The name of the metric. + * @param metric The metric to register. + */ + private _registerMetric(name: string, metric: Metric): void { + if (this._metrics.has(name)) { + throw new Error( + `A metric with the name ${name} has already been registered.` + ); + } + this._metrics.set(name, metric); } /** diff --git a/packages/opentelemetry-metrics/src/Metric.ts b/packages/opentelemetry-metrics/src/Metric.ts index 1daecfd88cb..0b0de975b5d 100644 --- a/packages/opentelemetry-metrics/src/Metric.ts +++ b/packages/opentelemetry-metrics/src/Metric.ts @@ -18,18 +18,28 @@ import * as types from '@opentelemetry/types'; import { hashLabelValues } from './Utils'; import { CounterHandle, GaugeHandle } from './Handle'; import { MetricOptions } from './types'; +import { + ReadableMetric, + MetricDescriptor, + MetricDescriptorType, +} from './export/types'; /** This is a SDK implementation of {@link Metric} interface. */ export abstract class Metric implements types.Metric { protected readonly _monotonic: boolean; protected readonly _disabled: boolean; protected readonly _logger: types.Logger; + private readonly _metricDescriptor: MetricDescriptor; private readonly _handles: Map = new Map(); - constructor(name: string, options: MetricOptions) { - this._monotonic = options.monotonic; - this._disabled = options.disabled; - this._logger = options.logger; + constructor( + private readonly _name: string, + private readonly _options: MetricOptions + ) { + this._monotonic = _options.monotonic; + this._disabled = _options.disabled; + this._logger = _options.logger; + this._metricDescriptor = this._getMetricDescriptor(); } /** @@ -77,6 +87,33 @@ export abstract class Metric implements types.Metric { return; } + /** + * Provides a ReadableMetric with one or more TimeSeries. + * @returns The ReadableMetric, or null if TimeSeries is not present in + * Metric. + */ + getMetric(): ReadableMetric | null { + if (this._handles.size === 0) return null; + + //const timestamp = [20, 1000]; + return { + descriptor: this._metricDescriptor, + // timeseries: Array.from(this._handles, ([_, handle]) => + // handle.getTimeSeries(timestamp) + // ), + }; + } + + private _getMetricDescriptor(): MetricDescriptor { + return { + name: this._name, + description: this._options.description, + unit: this._options.unit, + labelKeys: this._options.labelKeys, + type: MetricDescriptorType.GAUGE_INT64, + }; + } + protected abstract _makeHandle(labelValues: string[]): T; } diff --git a/packages/opentelemetry-metrics/src/export/types.ts b/packages/opentelemetry-metrics/src/export/types.ts index 582d3b85a43..494010be51a 100644 --- a/packages/opentelemetry-metrics/src/export/types.ts +++ b/packages/opentelemetry-metrics/src/export/types.ts @@ -39,11 +39,11 @@ export interface ReadableMetric { * One or more timeseries for a single metric, where each timeseries has * one or more points. */ - readonly timeseries: TimeSeries[]; + readonly timeseries?: TimeSeries[]; // The resource for the metric. If unset, it may be set to a default value // provided for a sequence of messages in an RPC stream. - resource: Resource; + resource?: Resource; } /** Properties of a Metric type and its schema */ diff --git a/packages/opentelemetry-metrics/src/index.ts b/packages/opentelemetry-metrics/src/index.ts index 95ea05565fd..6c1d1c23b9c 100644 --- a/packages/opentelemetry-metrics/src/index.ts +++ b/packages/opentelemetry-metrics/src/index.ts @@ -17,3 +17,4 @@ export * from './Handle'; export * from './Meter'; export * from './Metric'; +export * from './export/types'; diff --git a/packages/opentelemetry-metrics/test/Meter.test.ts b/packages/opentelemetry-metrics/test/Meter.test.ts index e227cc51035..0feff98fb21 100644 --- a/packages/opentelemetry-metrics/test/Meter.test.ts +++ b/packages/opentelemetry-metrics/test/Meter.test.ts @@ -15,7 +15,7 @@ */ import * as assert from 'assert'; -import { Meter, Metric, CounterMetric, GaugeMetric } from '../src'; +import { Meter, Metric, CounterMetric, GaugeMetric, MetricDescriptorType } from '../src'; import * as types from '@opentelemetry/types'; import { NoopLogger, NoopMetric } from '@opentelemetry/core'; @@ -46,6 +46,13 @@ describe('Meter', () => { assert.ok(counter instanceof Metric); }); + it('should throw an error when create the same metric', () => { + meter.createCounter('test_metric'); + assert.throws(() => { + meter.createCounter('test_metric'); + }, /^Error: A metric with the name test_metric has already been registered.$/); + }); + describe('.getHandle()', () => { it('should create a counter handle', () => { const counter = meter.createCounter('name') as CounterMetric; @@ -308,4 +315,48 @@ describe('Meter', () => { }); }); }); + + describe('#getMetrics', () => { + it('should create a counter', () => { + const counter = meter.createCounter('counter', { + description: 'test', + labelKeys: ['key'], + }); + const handle = counter.getHandle(labelValues); + handle.add(10); + + assert.deepStrictEqual(meter.getMetrics(), [ + { + descriptor: { + name: 'counter', + description: 'test', + unit: '1', + type: MetricDescriptorType.GAUGE_INT64, + labelKeys: ['key'], + }, + }, + ]); + }); + + it('should create a gauge', () => { + const gauge = meter.createGauge('gauge', { + labelKeys: ['gauge-key'], + unit: 'ms', + }); + const handle = gauge.getHandle(labelValues); + handle.set(200); + + assert.deepStrictEqual(meter.getMetrics(), [ + { + descriptor: { + name: 'gauge', + description: '', + unit: 'ms', + type: MetricDescriptorType.GAUGE_INT64, + labelKeys: ['gauge-key'], + }, + }, + ]); + }); + }); }); From aeb84cd5d8d2b0e3596bf213f3ff9451001b6c19 Mon Sep 17 00:00:00 2001 From: Mayur Kale Date: Wed, 16 Oct 2019 18:18:09 -0700 Subject: [PATCH 02/15] fix: getTimeSeries and add more tests --- packages/opentelemetry-metrics/src/Handle.ts | 65 +++++++-------- packages/opentelemetry-metrics/src/Metric.ts | 11 +-- .../opentelemetry-metrics/src/export/types.ts | 2 +- .../opentelemetry-metrics/test/Meter.test.ts | 80 ++++++++++++------- 4 files changed, 89 insertions(+), 69 deletions(-) diff --git a/packages/opentelemetry-metrics/src/Handle.ts b/packages/opentelemetry-metrics/src/Handle.ts index 45bb34e390d..89fc99abe0c 100644 --- a/packages/opentelemetry-metrics/src/Handle.ts +++ b/packages/opentelemetry-metrics/src/Handle.ts @@ -17,20 +17,39 @@ import * as types from '@opentelemetry/types'; import { TimeSeries } from './export/types'; +export class BaseHandle { + protected _data = 0; + + constructor(private readonly _labelValues: string[]) {} + + /** + * Returns the TimeSeries with one or more Point. + * + * @param timestamp The time at which the counter is recorded. + * @returns The TimeSeries. + */ + getTimeSeries(timestamp: types.HrTime): TimeSeries { + return { + labelValues: this._labelValues.map(value => ({ value })), + points: [{ value: this._data, timestamp }], + }; + } +} + /** * CounterHandle allows the SDK to observe/record a single metric event. The * value of single handle in the `Counter` associated with specified label * values. */ -export class CounterHandle implements types.CounterHandle { - private _data = 0; - +export class CounterHandle extends BaseHandle implements types.CounterHandle { constructor( private readonly _disabled: boolean, private readonly _monotonic: boolean, - private readonly _labelValues: string[], + labelValues: string[], private readonly _logger: types.Logger - ) {} + ) { + super(labelValues); + } add(value: number): void { if (this._disabled) return; @@ -41,34 +60,21 @@ export class CounterHandle implements types.CounterHandle { } this._data = this._data + value; } - - /** - * Returns the TimeSeries with one or more Point. - * - * @param timestamp The time at which the counter is recorded. - * @returns The TimeSeries. - */ - getTimeSeries(timestamp: types.HrTime): TimeSeries { - return { - labelValues: this._labelValues.map(value => ({ value })), - points: [{ value: this._data, timestamp }], - }; - } } /** * GaugeHandle allows the SDK to observe/record a single metric event. The * value of single handle in the `Gauge` associated with specified label values. */ -export class GaugeHandle implements types.GaugeHandle { - private _data = 0; - +export class GaugeHandle extends BaseHandle implements types.GaugeHandle { constructor( private readonly _disabled: boolean, private readonly _monotonic: boolean, - private readonly _labelValues: string[], + labelValues: string[], private readonly _logger: types.Logger - ) {} + ) { + super(labelValues); + } set(value: number): void { if (this._disabled) return; @@ -79,19 +85,6 @@ export class GaugeHandle implements types.GaugeHandle { } this._data = value; } - - /** - * Returns the TimeSeries with one or more Point. - * - * @param timestamp The time at which the gauge is recorded. - * @returns The TimeSeries. - */ - getTimeSeries(timestamp: types.HrTime): TimeSeries { - return { - labelValues: this._labelValues.map(value => ({ value })), - points: [{ value: this._data, timestamp }], - }; - } } /** diff --git a/packages/opentelemetry-metrics/src/Metric.ts b/packages/opentelemetry-metrics/src/Metric.ts index 0b0de975b5d..55e88f95c80 100644 --- a/packages/opentelemetry-metrics/src/Metric.ts +++ b/packages/opentelemetry-metrics/src/Metric.ts @@ -15,8 +15,9 @@ */ import * as types from '@opentelemetry/types'; +import { hrTime } from '@opentelemetry/core'; import { hashLabelValues } from './Utils'; -import { CounterHandle, GaugeHandle } from './Handle'; +import { CounterHandle, GaugeHandle, BaseHandle } from './Handle'; import { MetricOptions } from './types'; import { ReadableMetric, @@ -95,12 +96,12 @@ export abstract class Metric implements types.Metric { getMetric(): ReadableMetric | null { if (this._handles.size === 0) return null; - //const timestamp = [20, 1000]; + const timestamp = hrTime(); return { descriptor: this._metricDescriptor, - // timeseries: Array.from(this._handles, ([_, handle]) => - // handle.getTimeSeries(timestamp) - // ), + timeseries: Array.from(this._handles, ([_, handle]) => + ((handle as unknown) as BaseHandle).getTimeSeries(timestamp) + ), }; } diff --git a/packages/opentelemetry-metrics/src/export/types.ts b/packages/opentelemetry-metrics/src/export/types.ts index 494010be51a..ccfbfdb53a4 100644 --- a/packages/opentelemetry-metrics/src/export/types.ts +++ b/packages/opentelemetry-metrics/src/export/types.ts @@ -39,7 +39,7 @@ export interface ReadableMetric { * One or more timeseries for a single metric, where each timeseries has * one or more points. */ - readonly timeseries?: TimeSeries[]; + readonly timeseries: TimeSeries[]; // The resource for the metric. If unset, it may be set to a default value // provided for a sequence of messages in an RPC stream. diff --git a/packages/opentelemetry-metrics/test/Meter.test.ts b/packages/opentelemetry-metrics/test/Meter.test.ts index 0feff98fb21..a4955d95673 100644 --- a/packages/opentelemetry-metrics/test/Meter.test.ts +++ b/packages/opentelemetry-metrics/test/Meter.test.ts @@ -17,7 +17,9 @@ import * as assert from 'assert'; import { Meter, Metric, CounterMetric, GaugeMetric, MetricDescriptorType } from '../src'; import * as types from '@opentelemetry/types'; -import { NoopLogger, NoopMetric } from '@opentelemetry/core'; +import { NoopLogger, NoopMetric, hrTime, hrTimeToMilliseconds } from '@opentelemetry/core'; + +const performanceTimeOrigin = hrTime(); describe('Meter', () => { let meter: Meter; @@ -322,20 +324,27 @@ describe('Meter', () => { description: 'test', labelKeys: ['key'], }); - const handle = counter.getHandle(labelValues); + const handle = counter.getHandle(['counter-value']); handle.add(10); - assert.deepStrictEqual(meter.getMetrics(), [ - { - descriptor: { - name: 'counter', - description: 'test', - unit: '1', - type: MetricDescriptorType.GAUGE_INT64, - labelKeys: ['key'], - }, - }, - ]); + assert.strictEqual(meter.getMetrics().length, 1); + const [{ descriptor, timeseries }] = meter.getMetrics(); + assert.deepStrictEqual(descriptor, { + name: 'counter', + description: 'test', + unit: '1', + type: MetricDescriptorType.GAUGE_INT64, + labelKeys: ['key'], + }); + assert.strictEqual(timeseries.length, 1); + const [{ labelValues, points }] = timeseries; + assert.deepStrictEqual(labelValues, [{ value: 'counter-value' }]); + assert.strictEqual(points.length, 1); + assert.strictEqual(points[0].value, 10); + assert.ok( + hrTimeToMilliseconds(points[0].timestamp) > + hrTimeToMilliseconds(performanceTimeOrigin) + ); }); it('should create a gauge', () => { @@ -343,20 +352,37 @@ describe('Meter', () => { labelKeys: ['gauge-key'], unit: 'ms', }); - const handle = gauge.getHandle(labelValues); - handle.set(200); - - assert.deepStrictEqual(meter.getMetrics(), [ - { - descriptor: { - name: 'gauge', - description: '', - unit: 'ms', - type: MetricDescriptorType.GAUGE_INT64, - labelKeys: ['gauge-key'], - }, - }, - ]); + gauge.getHandle(['gauge-value1']).set(200); + gauge.getHandle(['gauge-value2']).set(-10); + + assert.strictEqual(meter.getMetrics().length, 1); + const [{ descriptor, timeseries }] = meter.getMetrics(); + assert.deepStrictEqual(descriptor, { + name: 'gauge', + description: '', + unit: 'ms', + type: MetricDescriptorType.GAUGE_INT64, + labelKeys: ['gauge-key'], + }); + assert.strictEqual(timeseries.length, 2); + const [ + { labelValues: labelValues1, points: points1 }, + { labelValues: labelValues2, points: points2 }, + ] = timeseries; + assert.deepStrictEqual(labelValues1, [{ value: 'gauge-value1' }]); + assert.strictEqual(points1.length, 1); + assert.strictEqual(points1[0].value, 200); + assert.ok( + hrTimeToMilliseconds(points1[0].timestamp) > + hrTimeToMilliseconds(performanceTimeOrigin) + ); + assert.deepStrictEqual(labelValues2, [{ value: 'gauge-value2' }]); + assert.strictEqual(points2.length, 1); + assert.strictEqual(points2[0].value, -10); + assert.ok( + hrTimeToMilliseconds(points2[0].timestamp) > + hrTimeToMilliseconds(performanceTimeOrigin) + ); }); }); }); From 1b74ab9753fe35b7d84adc7e3ceb70eb74599969 Mon Sep 17 00:00:00 2001 From: Mayur Kale Date: Thu, 17 Oct 2019 11:33:40 -0700 Subject: [PATCH 03/15] fix: minor --- packages/opentelemetry-metrics/src/Handle.ts | 23 ++++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/opentelemetry-metrics/src/Handle.ts b/packages/opentelemetry-metrics/src/Handle.ts index 89fc99abe0c..169193dadcc 100644 --- a/packages/opentelemetry-metrics/src/Handle.ts +++ b/packages/opentelemetry-metrics/src/Handle.ts @@ -17,20 +17,21 @@ import * as types from '@opentelemetry/types'; import { TimeSeries } from './export/types'; +/** Base class that represents a TimeSeries. */ export class BaseHandle { protected _data = 0; - constructor(private readonly _labelValues: string[]) {} + constructor(private readonly _labels: string[]) {} /** * Returns the TimeSeries with one or more Point. * - * @param timestamp The time at which the counter is recorded. + * @param timestamp The time at which the handle is recorded. * @returns The TimeSeries. */ getTimeSeries(timestamp: types.HrTime): TimeSeries { return { - labelValues: this._labelValues.map(value => ({ value })), + labelValues: this._labels.map(value => ({ value })), points: [{ value: this._data, timestamp }], }; } @@ -45,17 +46,19 @@ export class CounterHandle extends BaseHandle implements types.CounterHandle { constructor( private readonly _disabled: boolean, private readonly _monotonic: boolean, - labelValues: string[], + private readonly _labelValues: string[], private readonly _logger: types.Logger ) { - super(labelValues); + super(_labelValues); } add(value: number): void { if (this._disabled) return; if (this._monotonic && value < 0) { - this._logger.error('Monotonic counter cannot descend.'); + this._logger.error( + `Monotonic counter cannot descend for ${this._labelValues}` + ); return; } this._data = this._data + value; @@ -70,17 +73,19 @@ export class GaugeHandle extends BaseHandle implements types.GaugeHandle { constructor( private readonly _disabled: boolean, private readonly _monotonic: boolean, - labelValues: string[], + private readonly _labelValues: string[], private readonly _logger: types.Logger ) { - super(labelValues); + super(_labelValues); } set(value: number): void { if (this._disabled) return; if (this._monotonic && value < this._data) { - this._logger.error('Monotonic gauge cannot descend.'); + this._logger.error( + `Monotonic gauge cannot descend for ${this._labelValues}` + ); return; } this._data = value; From d3e613f11a9f701e383c0862f213ed0d2bd465ce Mon Sep 17 00:00:00 2001 From: Mayur Kale Date: Thu, 17 Oct 2019 11:37:31 -0700 Subject: [PATCH 04/15] fix: add JSDoc --- packages/opentelemetry-metrics/src/Handle.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/opentelemetry-metrics/src/Handle.ts b/packages/opentelemetry-metrics/src/Handle.ts index 169193dadcc..88a39245d0f 100644 --- a/packages/opentelemetry-metrics/src/Handle.ts +++ b/packages/opentelemetry-metrics/src/Handle.ts @@ -17,7 +17,10 @@ import * as types from '@opentelemetry/types'; import { TimeSeries } from './export/types'; -/** Base class that represents a TimeSeries. */ +/** + * This class represent the base to handle, which is responsible for generating + * the TimeSeries. + */ export class BaseHandle { protected _data = 0; From 722275db69aa42cea1838252556d15e789f90a0c Mon Sep 17 00:00:00 2001 From: Mayur Kale Date: Thu, 17 Oct 2019 16:21:56 -0700 Subject: [PATCH 05/15] fix: minor --- packages/opentelemetry-metrics/src/Meter.ts | 2 +- packages/opentelemetry-metrics/src/Metric.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/opentelemetry-metrics/src/Meter.ts b/packages/opentelemetry-metrics/src/Meter.ts index 8a3237c92e1..56ce61f63aa 100644 --- a/packages/opentelemetry-metrics/src/Meter.ts +++ b/packages/opentelemetry-metrics/src/Meter.ts @@ -128,7 +128,7 @@ export class Meter implements types.Meter { */ getMetrics(): ReadableMetric[] { return Array.from(this._metrics.values()) - .map(metric => metric.getMetric()) + .map(metric => metric.get()) .filter(metric => !!metric) as ReadableMetric[]; } diff --git a/packages/opentelemetry-metrics/src/Metric.ts b/packages/opentelemetry-metrics/src/Metric.ts index 55e88f95c80..071e30e3101 100644 --- a/packages/opentelemetry-metrics/src/Metric.ts +++ b/packages/opentelemetry-metrics/src/Metric.ts @@ -93,7 +93,7 @@ export abstract class Metric implements types.Metric { * @returns The ReadableMetric, or null if TimeSeries is not present in * Metric. */ - getMetric(): ReadableMetric | null { + get(): ReadableMetric | null { if (this._handles.size === 0) return null; const timestamp = hrTime(); From 4952f4a3ccffd9d5d08b7238ff36e937e85403ff Mon Sep 17 00:00:00 2001 From: Mayur Kale Date: Fri, 18 Oct 2019 10:15:57 -0700 Subject: [PATCH 06/15] fix: remove --- packages/opentelemetry-metrics/src/Meter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-metrics/src/Meter.ts b/packages/opentelemetry-metrics/src/Meter.ts index 56ce61f63aa..7a9d7dddb22 100644 --- a/packages/opentelemetry-metrics/src/Meter.ts +++ b/packages/opentelemetry-metrics/src/Meter.ts @@ -129,7 +129,7 @@ export class Meter implements types.Meter { getMetrics(): ReadableMetric[] { return Array.from(this._metrics.values()) .map(metric => metric.get()) - .filter(metric => !!metric) as ReadableMetric[]; + .filter(metric => !!metric); } /** From 4e921bfe421c7487cd0d83af7d1b6cf1cc308c34 Mon Sep 17 00:00:00 2001 From: Mayur Kale Date: Mon, 21 Oct 2019 11:52:19 -0700 Subject: [PATCH 07/15] fix: replace String -> string --- packages/opentelemetry-metrics/src/Metric.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-metrics/src/Metric.ts b/packages/opentelemetry-metrics/src/Metric.ts index 071e30e3101..a20931631dc 100644 --- a/packages/opentelemetry-metrics/src/Metric.ts +++ b/packages/opentelemetry-metrics/src/Metric.ts @@ -31,7 +31,7 @@ export abstract class Metric implements types.Metric { protected readonly _disabled: boolean; protected readonly _logger: types.Logger; private readonly _metricDescriptor: MetricDescriptor; - private readonly _handles: Map = new Map(); + private readonly _handles: Map = new Map(); constructor( private readonly _name: string, From ba6db91851993d6e8af2737c92df53b70ecf2b49 Mon Sep 17 00:00:00 2001 From: Mayur Kale Date: Tue, 22 Oct 2019 17:31:27 -0700 Subject: [PATCH 08/15] fix: avoid casting --- packages/opentelemetry-metrics/src/Handle.ts | 2 +- packages/opentelemetry-metrics/src/Meter.ts | 13 +++++++++++-- packages/opentelemetry-metrics/src/Metric.ts | 4 ++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/opentelemetry-metrics/src/Handle.ts b/packages/opentelemetry-metrics/src/Handle.ts index 88a39245d0f..5d55ebbe08b 100644 --- a/packages/opentelemetry-metrics/src/Handle.ts +++ b/packages/opentelemetry-metrics/src/Handle.ts @@ -98,7 +98,7 @@ export class GaugeHandle extends BaseHandle implements types.GaugeHandle { /** * MeasureHandle is an implementation of the {@link MeasureHandle} interface. */ -export class MeasureHandle implements types.MeasureHandle { +export class MeasureHandle extends BaseHandle implements types.MeasureHandle { record( value: number, distContext?: types.DistributedContext, diff --git a/packages/opentelemetry-metrics/src/Meter.ts b/packages/opentelemetry-metrics/src/Meter.ts index 7a9d7dddb22..e3efd96e8a4 100644 --- a/packages/opentelemetry-metrics/src/Meter.ts +++ b/packages/opentelemetry-metrics/src/Meter.ts @@ -21,7 +21,13 @@ import { NOOP_GAUGE_METRIC, NOOP_MEASURE_METRIC, } from '@opentelemetry/core'; -import { CounterMetric, GaugeMetric } from './Metric'; +import { + CounterHandle, + GaugeHandle, + MeasureHandle, + BaseHandle, +} from './Handle'; +import { Metric, CounterMetric, GaugeMetric } from './Metric'; import { MetricOptions, DEFAULT_METRIC_OPTIONS, @@ -137,7 +143,10 @@ export class Meter implements types.Meter { * @param name The name of the metric. * @param metric The metric to register. */ - private _registerMetric(name: string, metric: Metric): void { + private _registerMetric( + name: string, + metric: Metric + ): void { if (this._metrics.has(name)) { throw new Error( `A metric with the name ${name} has already been registered.` diff --git a/packages/opentelemetry-metrics/src/Metric.ts b/packages/opentelemetry-metrics/src/Metric.ts index a20931631dc..d9ade423202 100644 --- a/packages/opentelemetry-metrics/src/Metric.ts +++ b/packages/opentelemetry-metrics/src/Metric.ts @@ -26,7 +26,7 @@ import { } from './export/types'; /** This is a SDK implementation of {@link Metric} interface. */ -export abstract class Metric implements types.Metric { +export abstract class Metric implements types.Metric { protected readonly _monotonic: boolean; protected readonly _disabled: boolean; protected readonly _logger: types.Logger; @@ -100,7 +100,7 @@ export abstract class Metric implements types.Metric { return { descriptor: this._metricDescriptor, timeseries: Array.from(this._handles, ([_, handle]) => - ((handle as unknown) as BaseHandle).getTimeSeries(timestamp) + handle.getTimeSeries(timestamp) ), }; } From 4bb0d1db27555fb45a284b17f0bddd1aa28616ca Mon Sep 17 00:00:00 2001 From: Mayur Kale Date: Thu, 24 Oct 2019 21:06:29 -0700 Subject: [PATCH 09/15] fix: use GAUGE_DOUBLE and COUNTER_DOUBLE type --- packages/opentelemetry-metrics/src/Metric.ts | 7 ++++--- packages/opentelemetry-metrics/test/Meter.test.ts | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/opentelemetry-metrics/src/Metric.ts b/packages/opentelemetry-metrics/src/Metric.ts index d9ade423202..4653480e8e6 100644 --- a/packages/opentelemetry-metrics/src/Metric.ts +++ b/packages/opentelemetry-metrics/src/Metric.ts @@ -35,6 +35,7 @@ export abstract class Metric implements types.Metric { constructor( private readonly _name: string, + private readonly _type: MetricDescriptorType, private readonly _options: MetricOptions ) { this._monotonic = _options.monotonic; @@ -111,7 +112,7 @@ export abstract class Metric implements types.Metric { description: this._options.description, unit: this._options.unit, labelKeys: this._options.labelKeys, - type: MetricDescriptorType.GAUGE_INT64, + type: this._type, }; } @@ -121,7 +122,7 @@ export abstract class Metric implements types.Metric { /** This is a SDK implementation of Counter Metric. */ export class CounterMetric extends Metric { constructor(name: string, options: MetricOptions) { - super(name, options); + super(name, MetricDescriptorType.COUNTER_DOUBLE, options); } protected _makeHandle(labelValues: string[]): CounterHandle { return new CounterHandle( @@ -136,7 +137,7 @@ export class CounterMetric extends Metric { /** This is a SDK implementation of Gauge Metric. */ export class GaugeMetric extends Metric { constructor(name: string, options: MetricOptions) { - super(name, options); + super(name, MetricDescriptorType.GAUGE_DOUBLE, options); } protected _makeHandle(labelValues: string[]): GaugeHandle { return new GaugeHandle( diff --git a/packages/opentelemetry-metrics/test/Meter.test.ts b/packages/opentelemetry-metrics/test/Meter.test.ts index a4955d95673..75bcc8616c8 100644 --- a/packages/opentelemetry-metrics/test/Meter.test.ts +++ b/packages/opentelemetry-metrics/test/Meter.test.ts @@ -333,7 +333,7 @@ describe('Meter', () => { name: 'counter', description: 'test', unit: '1', - type: MetricDescriptorType.GAUGE_INT64, + type: MetricDescriptorType.COUNTER_DOUBLE, labelKeys: ['key'], }); assert.strictEqual(timeseries.length, 1); @@ -361,7 +361,7 @@ describe('Meter', () => { name: 'gauge', description: '', unit: 'ms', - type: MetricDescriptorType.GAUGE_INT64, + type: MetricDescriptorType.GAUGE_DOUBLE, labelKeys: ['gauge-key'], }); assert.strictEqual(timeseries.length, 2); From 748b92cdf13303e9034727dae0e4dad01f9c2749 Mon Sep 17 00:00:00 2001 From: Mayur Kale Date: Wed, 30 Oct 2019 12:13:57 -0700 Subject: [PATCH 10/15] fix: add ValueType to indicate type of the metric --- packages/opentelemetry-metrics/src/Handle.ts | 15 ++++ packages/opentelemetry-metrics/src/Meter.ts | 2 + packages/opentelemetry-metrics/src/Metric.ts | 24 +++++- packages/opentelemetry-metrics/src/types.ts | 5 +- .../opentelemetry-metrics/test/Meter.test.ts | 78 +++++++++++++++++-- .../opentelemetry-types/src/metrics/Metric.ts | 12 +++ 6 files changed, 126 insertions(+), 10 deletions(-) diff --git a/packages/opentelemetry-metrics/src/Handle.ts b/packages/opentelemetry-metrics/src/Handle.ts index 5d55ebbe08b..65d01835ee9 100644 --- a/packages/opentelemetry-metrics/src/Handle.ts +++ b/packages/opentelemetry-metrics/src/Handle.ts @@ -49,6 +49,7 @@ export class CounterHandle extends BaseHandle implements types.CounterHandle { constructor( private readonly _disabled: boolean, private readonly _monotonic: boolean, + private readonly _valueType: types.ValueType, private readonly _labelValues: string[], private readonly _logger: types.Logger ) { @@ -64,6 +65,12 @@ export class CounterHandle extends BaseHandle implements types.CounterHandle { ); return; } + if (this._valueType === types.ValueType.INT && value % 1 != 0) { + this._logger.warn( + `INT counter cannot accept a floating-point value for ${this._labelValues}, ignoring the fractional digits.` + ); + value = Math.trunc(value); + } this._data = this._data + value; } } @@ -76,6 +83,7 @@ export class GaugeHandle extends BaseHandle implements types.GaugeHandle { constructor( private readonly _disabled: boolean, private readonly _monotonic: boolean, + private readonly _valueType: types.ValueType, private readonly _labelValues: string[], private readonly _logger: types.Logger ) { @@ -91,6 +99,13 @@ export class GaugeHandle extends BaseHandle implements types.GaugeHandle { ); return; } + + if (this._valueType === types.ValueType.INT && value % 1 != 0) { + this._logger.warn( + `INT gauge cannot accept a floating-point value for ${this._labelValues}, ignoring the fractional digits.` + ); + value = Math.trunc(value); + } this._data = value; } } diff --git a/packages/opentelemetry-metrics/src/Meter.ts b/packages/opentelemetry-metrics/src/Meter.ts index e3efd96e8a4..f67d92acbc8 100644 --- a/packages/opentelemetry-metrics/src/Meter.ts +++ b/packages/opentelemetry-metrics/src/Meter.ts @@ -89,6 +89,7 @@ export class Meter implements types.Meter { const opt: MetricOptions = { // Counters are defined as monotonic by default monotonic: true, + valueType: types.ValueType.DOUBLE, logger: this._logger, ...DEFAULT_METRIC_OPTIONS, ...options, @@ -119,6 +120,7 @@ export class Meter implements types.Meter { const opt: MetricOptions = { // Gauges are defined as non-monotonic by default monotonic: false, + valueType: types.ValueType.DOUBLE, logger: this._logger, ...DEFAULT_METRIC_OPTIONS, ...options, diff --git a/packages/opentelemetry-metrics/src/Metric.ts b/packages/opentelemetry-metrics/src/Metric.ts index 4653480e8e6..02bccdfadd9 100644 --- a/packages/opentelemetry-metrics/src/Metric.ts +++ b/packages/opentelemetry-metrics/src/Metric.ts @@ -29,17 +29,19 @@ import { export abstract class Metric implements types.Metric { protected readonly _monotonic: boolean; protected readonly _disabled: boolean; + protected readonly _valueType: types.ValueType; protected readonly _logger: types.Logger; private readonly _metricDescriptor: MetricDescriptor; private readonly _handles: Map = new Map(); constructor( private readonly _name: string, - private readonly _type: MetricDescriptorType, - private readonly _options: MetricOptions + private readonly _options: MetricOptions, + private readonly _type: MetricDescriptorType ) { this._monotonic = _options.monotonic; this._disabled = _options.disabled; + this._valueType = _options.valueType; this._logger = _options.logger; this._metricDescriptor = this._getMetricDescriptor(); } @@ -122,12 +124,19 @@ export abstract class Metric implements types.Metric { /** This is a SDK implementation of Counter Metric. */ export class CounterMetric extends Metric { constructor(name: string, options: MetricOptions) { - super(name, MetricDescriptorType.COUNTER_DOUBLE, options); + super( + name, + options, + options.valueType === types.ValueType.DOUBLE + ? MetricDescriptorType.COUNTER_DOUBLE + : MetricDescriptorType.COUNTER_INT64 + ); } protected _makeHandle(labelValues: string[]): CounterHandle { return new CounterHandle( this._disabled, this._monotonic, + this._valueType, labelValues, this._logger ); @@ -137,12 +146,19 @@ export class CounterMetric extends Metric { /** This is a SDK implementation of Gauge Metric. */ export class GaugeMetric extends Metric { constructor(name: string, options: MetricOptions) { - super(name, MetricDescriptorType.GAUGE_DOUBLE, options); + super( + name, + options, + options.valueType === types.ValueType.DOUBLE + ? MetricDescriptorType.GAUGE_DOUBLE + : MetricDescriptorType.GAUGE_INT64 + ); } protected _makeHandle(labelValues: string[]): GaugeHandle { return new GaugeHandle( this._disabled, this._monotonic, + this._valueType, labelValues, this._logger ); diff --git a/packages/opentelemetry-metrics/src/types.ts b/packages/opentelemetry-metrics/src/types.ts index d4078bbfcfd..f021a09e31c 100644 --- a/packages/opentelemetry-metrics/src/types.ts +++ b/packages/opentelemetry-metrics/src/types.ts @@ -15,7 +15,7 @@ */ import { LogLevel } from '@opentelemetry/core'; -import { Logger } from '@opentelemetry/types'; +import { Logger, ValueType } from '@opentelemetry/types'; /** Options needed for SDK metric creation. */ export interface MetricOptions { @@ -42,6 +42,9 @@ export interface MetricOptions { /** User provided logger. */ logger: Logger; + + /** Indicates the type of the recorded value. */ + valueType: ValueType; } /** MeterConfig provides an interface for configuring a Meter. */ diff --git a/packages/opentelemetry-metrics/test/Meter.test.ts b/packages/opentelemetry-metrics/test/Meter.test.ts index 75bcc8616c8..5df292da7a8 100644 --- a/packages/opentelemetry-metrics/test/Meter.test.ts +++ b/packages/opentelemetry-metrics/test/Meter.test.ts @@ -319,13 +319,13 @@ describe('Meter', () => { }); describe('#getMetrics', () => { - it('should create a counter', () => { + it('should create a DOUBLE counter', () => { const counter = meter.createCounter('counter', { description: 'test', labelKeys: ['key'], }); const handle = counter.getHandle(['counter-value']); - handle.add(10); + handle.add(10.45); assert.strictEqual(meter.getMetrics().length, 1); const [{ descriptor, timeseries }] = meter.getMetrics(); @@ -340,6 +340,35 @@ describe('Meter', () => { const [{ labelValues, points }] = timeseries; assert.deepStrictEqual(labelValues, [{ value: 'counter-value' }]); assert.strictEqual(points.length, 1); + assert.strictEqual(points[0].value, 10.45); + assert.ok( + hrTimeToMilliseconds(points[0].timestamp) > + hrTimeToMilliseconds(performanceTimeOrigin) + ); + }); + + it('should create a INT counter', () => { + const counter = meter.createCounter('counter', { + description: 'test', + labelKeys: ['key'], + valueType: types.ValueType.INT, + }); + const handle = counter.getHandle(['counter-value']); + handle.add(10.45); + + assert.strictEqual(meter.getMetrics().length, 1); + const [{ descriptor, timeseries }] = meter.getMetrics(); + assert.deepStrictEqual(descriptor, { + name: 'counter', + description: 'test', + unit: '1', + type: MetricDescriptorType.COUNTER_INT64, + labelKeys: ['key'], + }); + assert.strictEqual(timeseries.length, 1); + const [{ labelValues, points }] = timeseries; + assert.deepStrictEqual(labelValues, [{ value: 'counter-value' }]); + assert.strictEqual(points.length, 1); assert.strictEqual(points[0].value, 10); assert.ok( hrTimeToMilliseconds(points[0].timestamp) > @@ -347,13 +376,13 @@ describe('Meter', () => { ); }); - it('should create a gauge', () => { + it('should create a DOUBLE gauge', () => { const gauge = meter.createGauge('gauge', { labelKeys: ['gauge-key'], unit: 'ms', }); - gauge.getHandle(['gauge-value1']).set(200); - gauge.getHandle(['gauge-value2']).set(-10); + gauge.getHandle(['gauge-value1']).set(200.34); + gauge.getHandle(['gauge-value2']).set(-10.67); assert.strictEqual(meter.getMetrics().length, 1); const [{ descriptor, timeseries }] = meter.getMetrics(); @@ -371,6 +400,45 @@ describe('Meter', () => { ] = timeseries; assert.deepStrictEqual(labelValues1, [{ value: 'gauge-value1' }]); assert.strictEqual(points1.length, 1); + assert.strictEqual(points1[0].value, 200.34); + assert.ok( + hrTimeToMilliseconds(points1[0].timestamp) > + hrTimeToMilliseconds(performanceTimeOrigin) + ); + assert.deepStrictEqual(labelValues2, [{ value: 'gauge-value2' }]); + assert.strictEqual(points2.length, 1); + assert.strictEqual(points2[0].value, -10.67); + assert.ok( + hrTimeToMilliseconds(points2[0].timestamp) > + hrTimeToMilliseconds(performanceTimeOrigin) + ); + }); + + it('should create a INT gauge', () => { + const gauge = meter.createGauge('gauge', { + labelKeys: ['gauge-key'], + unit: 'ms', + valueType: types.ValueType.INT, + }); + gauge.getHandle(['gauge-value1']).set(200.34); + gauge.getHandle(['gauge-value2']).set(-10.67); + + assert.strictEqual(meter.getMetrics().length, 1); + const [{ descriptor, timeseries }] = meter.getMetrics(); + assert.deepStrictEqual(descriptor, { + name: 'gauge', + description: '', + unit: 'ms', + type: MetricDescriptorType.GAUGE_INT64, + labelKeys: ['gauge-key'], + }); + assert.strictEqual(timeseries.length, 2); + const [ + { labelValues: labelValues1, points: points1 }, + { labelValues: labelValues2, points: points2 }, + ] = timeseries; + assert.deepStrictEqual(labelValues1, [{ value: 'gauge-value1' }]); + assert.strictEqual(points1.length, 1); assert.strictEqual(points1[0].value, 200); assert.ok( hrTimeToMilliseconds(points1[0].timestamp) > diff --git a/packages/opentelemetry-types/src/metrics/Metric.ts b/packages/opentelemetry-types/src/metrics/Metric.ts index a34bd2d2f2d..f27633e9a4e 100644 --- a/packages/opentelemetry-types/src/metrics/Metric.ts +++ b/packages/opentelemetry-types/src/metrics/Metric.ts @@ -50,6 +50,18 @@ export interface MetricOptions { * non-negative values are expected. */ monotonic?: boolean; + + /** + * Indicates the type of the recorded value. + * @default {@link ValueType.DOUBLE} + */ + valueType?: ValueType; +} + +/** The Type of value. It describes how the data is reported. */ +export enum ValueType { + INT, + DOUBLE, } /** From f9da7d03108c13abffaedeb3496d2d41a7ed2d01 Mon Sep 17 00:00:00 2001 From: Mayur Kale Date: Wed, 30 Oct 2019 20:15:42 -0700 Subject: [PATCH 11/15] fix: move ValueType.DOUBLE to DEFAULT_METRIC_OPTIONS --- packages/opentelemetry-metrics/src/Meter.ts | 2 -- packages/opentelemetry-metrics/src/types.ts | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/opentelemetry-metrics/src/Meter.ts b/packages/opentelemetry-metrics/src/Meter.ts index f67d92acbc8..e3efd96e8a4 100644 --- a/packages/opentelemetry-metrics/src/Meter.ts +++ b/packages/opentelemetry-metrics/src/Meter.ts @@ -89,7 +89,6 @@ export class Meter implements types.Meter { const opt: MetricOptions = { // Counters are defined as monotonic by default monotonic: true, - valueType: types.ValueType.DOUBLE, logger: this._logger, ...DEFAULT_METRIC_OPTIONS, ...options, @@ -120,7 +119,6 @@ export class Meter implements types.Meter { const opt: MetricOptions = { // Gauges are defined as non-monotonic by default monotonic: false, - valueType: types.ValueType.DOUBLE, logger: this._logger, ...DEFAULT_METRIC_OPTIONS, ...options, diff --git a/packages/opentelemetry-metrics/src/types.ts b/packages/opentelemetry-metrics/src/types.ts index f021a09e31c..a20ef0afd9a 100644 --- a/packages/opentelemetry-metrics/src/types.ts +++ b/packages/opentelemetry-metrics/src/types.ts @@ -67,4 +67,5 @@ export const DEFAULT_METRIC_OPTIONS = { description: '', unit: '1', labelKeys: [], + valueType: ValueType.DOUBLE, }; From 52773fafdfac218db0afb513b59c642d1366671c Mon Sep 17 00:00:00 2001 From: Mayur Kale Date: Thu, 31 Oct 2019 14:43:53 -0700 Subject: [PATCH 12/15] fix: use Number.isInteger --- packages/opentelemetry-metrics/src/Handle.ts | 4 ++-- packages/opentelemetry-metrics/src/Meter.ts | 7 +------ packages/opentelemetry-metrics/test/Meter.test.ts | 15 +++++++++++++-- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/opentelemetry-metrics/src/Handle.ts b/packages/opentelemetry-metrics/src/Handle.ts index 65d01835ee9..2a79ba8ff64 100644 --- a/packages/opentelemetry-metrics/src/Handle.ts +++ b/packages/opentelemetry-metrics/src/Handle.ts @@ -65,7 +65,7 @@ export class CounterHandle extends BaseHandle implements types.CounterHandle { ); return; } - if (this._valueType === types.ValueType.INT && value % 1 != 0) { + if (this._valueType === types.ValueType.INT && !Number.isInteger(value)) { this._logger.warn( `INT counter cannot accept a floating-point value for ${this._labelValues}, ignoring the fractional digits.` ); @@ -100,7 +100,7 @@ export class GaugeHandle extends BaseHandle implements types.GaugeHandle { return; } - if (this._valueType === types.ValueType.INT && value % 1 != 0) { + if (this._valueType === types.ValueType.INT && !Number.isInteger(value)) { this._logger.warn( `INT gauge cannot accept a floating-point value for ${this._labelValues}, ignoring the fractional digits.` ); diff --git a/packages/opentelemetry-metrics/src/Meter.ts b/packages/opentelemetry-metrics/src/Meter.ts index e3efd96e8a4..a891e1ba40a 100644 --- a/packages/opentelemetry-metrics/src/Meter.ts +++ b/packages/opentelemetry-metrics/src/Meter.ts @@ -21,12 +21,7 @@ import { NOOP_GAUGE_METRIC, NOOP_MEASURE_METRIC, } from '@opentelemetry/core'; -import { - CounterHandle, - GaugeHandle, - MeasureHandle, - BaseHandle, -} from './Handle'; +import { BaseHandle } from './Handle'; import { Metric, CounterMetric, GaugeMetric } from './Metric'; import { MetricOptions, diff --git a/packages/opentelemetry-metrics/test/Meter.test.ts b/packages/opentelemetry-metrics/test/Meter.test.ts index 5df292da7a8..5ebad2d85b2 100644 --- a/packages/opentelemetry-metrics/test/Meter.test.ts +++ b/packages/opentelemetry-metrics/test/Meter.test.ts @@ -15,9 +15,20 @@ */ import * as assert from 'assert'; -import { Meter, Metric, CounterMetric, GaugeMetric, MetricDescriptorType } from '../src'; +import { + Meter, + Metric, + CounterMetric, + GaugeMetric, + MetricDescriptorType, +} from '../src'; import * as types from '@opentelemetry/types'; -import { NoopLogger, NoopMetric, hrTime, hrTimeToMilliseconds } from '@opentelemetry/core'; +import { + NoopLogger, + NoopMetric, + hrTime, + hrTimeToMilliseconds, +} from '@opentelemetry/core'; const performanceTimeOrigin = hrTime(); From 2674970ff7a4926d66b85be305ffb23985e6f730 Mon Sep 17 00:00:00 2001 From: Mayur Kale Date: Thu, 31 Oct 2019 15:45:18 -0700 Subject: [PATCH 13/15] fix: log an error instead of throw error --- packages/opentelemetry-metrics/src/Meter.ts | 3 ++- packages/opentelemetry-metrics/test/Meter.test.ts | 7 ------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/opentelemetry-metrics/src/Meter.ts b/packages/opentelemetry-metrics/src/Meter.ts index a891e1ba40a..e1d4a4394ec 100644 --- a/packages/opentelemetry-metrics/src/Meter.ts +++ b/packages/opentelemetry-metrics/src/Meter.ts @@ -143,9 +143,10 @@ export class Meter implements types.Meter { metric: Metric ): void { if (this._metrics.has(name)) { - throw new Error( + this._logger.error( `A metric with the name ${name} has already been registered.` ); + return; } this._metrics.set(name, metric); } diff --git a/packages/opentelemetry-metrics/test/Meter.test.ts b/packages/opentelemetry-metrics/test/Meter.test.ts index 5ebad2d85b2..91f9330cc5d 100644 --- a/packages/opentelemetry-metrics/test/Meter.test.ts +++ b/packages/opentelemetry-metrics/test/Meter.test.ts @@ -59,13 +59,6 @@ describe('Meter', () => { assert.ok(counter instanceof Metric); }); - it('should throw an error when create the same metric', () => { - meter.createCounter('test_metric'); - assert.throws(() => { - meter.createCounter('test_metric'); - }, /^Error: A metric with the name test_metric has already been registered.$/); - }); - describe('.getHandle()', () => { it('should create a counter handle', () => { const counter = meter.createCounter('name') as CounterMetric; From 0ec801afd6ad499a49d7c5ce48d3cdfa143a7b1e Mon Sep 17 00:00:00 2001 From: Mayur Kale Date: Fri, 1 Nov 2019 09:52:11 -0700 Subject: [PATCH 14/15] fix: add more test and @todo comment --- packages/opentelemetry-metrics/src/Meter.ts | 4 ++++ .../opentelemetry-metrics/test/Meter.test.ts | 24 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/packages/opentelemetry-metrics/src/Meter.ts b/packages/opentelemetry-metrics/src/Meter.ts index e1d4a4394ec..3c363e47a91 100644 --- a/packages/opentelemetry-metrics/src/Meter.ts +++ b/packages/opentelemetry-metrics/src/Meter.ts @@ -143,6 +143,10 @@ export class Meter implements types.Meter { metric: Metric ): void { if (this._metrics.has(name)) { + // @todo: decide how to handle already registered metric + // 1: Replace the old registered metric by the new + // 2. Throw error + // 3. skip duplicate metric (current approach) this._logger.error( `A metric with the name ${name} has already been registered.` ); diff --git a/packages/opentelemetry-metrics/test/Meter.test.ts b/packages/opentelemetry-metrics/test/Meter.test.ts index 91f9330cc5d..c3e0f96f920 100644 --- a/packages/opentelemetry-metrics/test/Meter.test.ts +++ b/packages/opentelemetry-metrics/test/Meter.test.ts @@ -143,6 +143,30 @@ describe('Meter', () => { }); }); + describe('.registerMetric()', () => { + it('skip already registered Metric', () => { + const counter1 = meter.createCounter('name1') as CounterMetric; + counter1.getHandle(labelValues).add(10); + + // should skip below metric + const counter2 = meter.createCounter('name1', { + valueType: types.ValueType.INT, + }) as CounterMetric; + counter2.getHandle(labelValues).add(500); + + assert.strictEqual(meter.getMetrics().length, 1); + const [{ descriptor, timeseries }] = meter.getMetrics(); + assert.deepStrictEqual(descriptor.name, 'name1'); + assert.deepStrictEqual( + descriptor.type, + MetricDescriptorType.COUNTER_DOUBLE + ); + assert.strictEqual(timeseries.length, 1); + assert.strictEqual(timeseries[0].points.length, 1); + assert.strictEqual(timeseries[0].points[0].value, 10); + }); + }); + describe('names', () => { it('should create counter with valid names', () => { const counter1 = meter.createCounter('name1'); From f3709eab0805af85448d1d413b75a56ed8f04130 Mon Sep 17 00:00:00 2001 From: Mayur Kale Date: Fri, 1 Nov 2019 12:01:47 -0700 Subject: [PATCH 15/15] fix: link #474 isssue --- packages/opentelemetry-metrics/src/Meter.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/opentelemetry-metrics/src/Meter.ts b/packages/opentelemetry-metrics/src/Meter.ts index 3c363e47a91..e66dd58ddd8 100644 --- a/packages/opentelemetry-metrics/src/Meter.ts +++ b/packages/opentelemetry-metrics/src/Meter.ts @@ -143,10 +143,7 @@ export class Meter implements types.Meter { metric: Metric ): void { if (this._metrics.has(name)) { - // @todo: decide how to handle already registered metric - // 1: Replace the old registered metric by the new - // 2. Throw error - // 3. skip duplicate metric (current approach) + // @todo (issue/474): decide how to handle already registered metric this._logger.error( `A metric with the name ${name} has already been registered.` );