diff --git a/packages/opentelemetry-api/src/metrics/Meter.ts b/packages/opentelemetry-api/src/metrics/Meter.ts index 5ed8bbf3a9..687dea4603 100644 --- a/packages/opentelemetry-api/src/metrics/Meter.ts +++ b/packages/opentelemetry-api/src/metrics/Meter.ts @@ -14,7 +14,13 @@ * limitations under the License. */ -import { MetricOptions, Counter, ValueRecorder, Observer } from './Metric'; +import { + MetricOptions, + Counter, + ValueRecorder, + Observer, + UpDownCounter, +} from './Metric'; /** * An interface to allow the recording metrics. @@ -40,6 +46,25 @@ export interface Meter { */ createCounter(name: string, options?: MetricOptions): Counter; + /** + * Creates a new `UpDownCounter` metric. UpDownCounter is a synchronous + * instrument and very similar to Counter except that Add(increment) + * supports negative increments. It is generally useful for capturing changes + * in an amount of resources used, or any quantity that rises and falls + * during a request. + * Example uses for UpDownCounter: + *
    + *
  1. count the number of active requests.
  2. + *
  3. count memory in use by instrumenting new and delete.
  4. + *
  5. count queue size by instrumenting enqueue and dequeue.
  6. + *
  7. count semaphore up and down operations.
  8. + *
+ * + * @param name the name of the metric. + * @param [options] the metric options. + */ + createUpDownCounter(name: string, options?: MetricOptions): UpDownCounter; + /** * Creates a new `Observer` metric. * @param name the name of the metric. diff --git a/packages/opentelemetry-api/src/metrics/Metric.ts b/packages/opentelemetry-api/src/metrics/Metric.ts index 23118369fa..6effd30946 100644 --- a/packages/opentelemetry-api/src/metrics/Metric.ts +++ b/packages/opentelemetry-api/src/metrics/Metric.ts @@ -48,12 +48,7 @@ export interface MetricOptions { disabled?: boolean; /** - * Asserts that this metric may only increase (e.g. time spent). - */ - monotonic?: boolean; - - /** - * (ValueRecorder only, default true) Asserts that this metric will only accept + * (Measure only, default true) Asserts that this metric will only accept * non-negative values (e.g. disk usage). */ absolute?: boolean; @@ -125,6 +120,13 @@ export interface Counter extends UnboundMetric { add(value: number, labels?: Labels): void; } +export interface UpDownCounter extends UnboundMetric { + /** + * Adds the given value to the current value. Values can be negative. + */ + add(value: number, labels?: Labels): void; +} + export interface ValueRecorder extends UnboundMetric { /** * Records the given value to this value recorder. diff --git a/packages/opentelemetry-api/src/metrics/NoopMeter.ts b/packages/opentelemetry-api/src/metrics/NoopMeter.ts index eed8c4e9bf..3cd7e42ebd 100644 --- a/packages/opentelemetry-api/src/metrics/NoopMeter.ts +++ b/packages/opentelemetry-api/src/metrics/NoopMeter.ts @@ -22,6 +22,7 @@ import { Counter, ValueRecorder, Observer, + UpDownCounter, } from './Metric'; import { BoundValueRecorder, BoundCounter } from './BoundInstrument'; import { CorrelationContext } from '../correlation_context/CorrelationContext'; @@ -53,6 +54,15 @@ export class NoopMeter implements Meter { return NOOP_COUNTER_METRIC; } + /** + * Returns a constant noop UpDownCounter. + * @param name the name of the metric. + * @param [options] the metric options. + */ + createUpDownCounter(name: string, options?: MetricOptions): UpDownCounter { + return NOOP_COUNTER_METRIC; + } + /** * Returns constant noop observer. * @param name the name of the metric. diff --git a/packages/opentelemetry-exporter-prometheus/src/prometheus.ts b/packages/opentelemetry-exporter-prometheus/src/prometheus.ts index fa1bdc3a35..1532717b48 100644 --- a/packages/opentelemetry-exporter-prometheus/src/prometheus.ts +++ b/packages/opentelemetry-exporter-prometheus/src/prometheus.ts @@ -191,10 +191,9 @@ export class PrometheusExporter implements MetricExporter { switch (record.descriptor.metricKind) { case MetricKind.COUNTER: - // there is no such thing as a non-monotonic counter in prometheus - return record.descriptor.monotonic - ? new Counter(metricObject) - : new Gauge(metricObject); + return new Counter(metricObject); + case MetricKind.UP_DOWN_COUNTER: + return new Gauge(metricObject); case MetricKind.OBSERVER: return new Gauge(metricObject); default: diff --git a/packages/opentelemetry-exporter-prometheus/test/prometheus.test.ts b/packages/opentelemetry-exporter-prometheus/test/prometheus.test.ts index 5e1e426862..e31c154bae 100644 --- a/packages/opentelemetry-exporter-prometheus/test/prometheus.test.ts +++ b/packages/opentelemetry-exporter-prometheus/test/prometheus.test.ts @@ -384,10 +384,9 @@ describe('PrometheusExporter', () => { }); }); - it('should export a non-monotonic counter as a gauge', done => { - const counter = meter.createCounter('counter', { + it('should export a UpDownCounter as a gauge', done => { + const counter = meter.createUpDownCounter('counter', { description: 'a test description', - monotonic: false, }); counter.bind({ key1: 'labelValue1' }).add(20); diff --git a/packages/opentelemetry-metrics/src/BoundInstrument.ts b/packages/opentelemetry-metrics/src/BoundInstrument.ts index 8f660c3722..fd6966b1b3 100644 --- a/packages/opentelemetry-metrics/src/BoundInstrument.ts +++ b/packages/opentelemetry-metrics/src/BoundInstrument.ts @@ -24,19 +24,16 @@ import { Aggregator } from './export/types'; export class BaseBoundInstrument { protected _labels: api.Labels; protected _logger: api.Logger; - protected _monotonic: boolean; constructor( labels: api.Labels, logger: api.Logger, - monotonic: boolean, private readonly _disabled: boolean, private readonly _valueType: api.ValueType, private readonly _aggregator: Aggregator ) { this._labels = labels; this._logger = logger; - this._monotonic = monotonic; } update(value: number): void { @@ -72,18 +69,17 @@ export class BoundCounter extends BaseBoundInstrument constructor( labels: api.Labels, disabled: boolean, - monotonic: boolean, valueType: api.ValueType, logger: api.Logger, aggregator: Aggregator ) { - super(labels, logger, monotonic, disabled, valueType, aggregator); + super(labels, logger, disabled, valueType, aggregator); } add(value: number): void { - if (this._monotonic && value < 0) { + if (value < 0) { this._logger.error( - `Monotonic counter cannot descend for ${Object.values(this._labels)}` + `Counter cannot descend for ${Object.values(this._labels)}` ); return; } @@ -93,7 +89,29 @@ export class BoundCounter extends BaseBoundInstrument } /** - * BoundValueRecorder is an implementation of the {@link BoundValueRecorder} interface. + * BoundUpDownCounter allows the SDK to observe/record a single metric event. + * The value of single instrument in the `UpDownCounter` associated with + * specified Labels. + */ +export class BoundUpDownCounter extends BaseBoundInstrument + implements api.BoundCounter { + constructor( + labels: api.Labels, + disabled: boolean, + valueType: api.ValueType, + logger: api.Logger, + aggregator: Aggregator + ) { + super(labels, logger, disabled, valueType, aggregator); + } + + add(value: number): void { + this.update(value); + } +} + +/** + * BoundMeasure is an implementation of the {@link BoundMeasure} interface. */ export class BoundValueRecorder extends BaseBoundInstrument implements api.BoundValueRecorder { @@ -102,13 +120,12 @@ export class BoundValueRecorder extends BaseBoundInstrument constructor( labels: api.Labels, disabled: boolean, - monotonic: boolean, absolute: boolean, valueType: api.ValueType, logger: api.Logger, aggregator: Aggregator ) { - super(labels, logger, monotonic, disabled, valueType, aggregator); + super(labels, logger, disabled, valueType, aggregator); this._absolute = absolute; } @@ -137,11 +154,10 @@ export class BoundObserver extends BaseBoundInstrument { constructor( labels: api.Labels, disabled: boolean, - monotonic: boolean, valueType: api.ValueType, logger: api.Logger, aggregator: Aggregator ) { - super(labels, logger, monotonic, disabled, valueType, aggregator); + super(labels, logger, disabled, valueType, aggregator); } } diff --git a/packages/opentelemetry-metrics/src/Meter.ts b/packages/opentelemetry-metrics/src/Meter.ts index dd760cc5a2..a8678751df 100644 --- a/packages/opentelemetry-metrics/src/Meter.ts +++ b/packages/opentelemetry-metrics/src/Meter.ts @@ -18,6 +18,7 @@ import * as api from '@opentelemetry/api'; import { ConsoleLogger } from '@opentelemetry/core'; import { Resource } from '@opentelemetry/resources'; import { BaseBoundInstrument } from './BoundInstrument'; +import { UpDownCounterMetric } from './UpDownCounterMetric'; import { Metric, CounterMetric, @@ -72,10 +73,9 @@ export class Meter implements api.Meter { return api.NOOP_VALUE_RECORDER_METRIC; } const opt: MetricOptions = { - absolute: true, // value recorders are defined as absolute by default - monotonic: false, // not applicable to value recorder, set to false logger: this._logger, ...DEFAULT_METRIC_OPTIONS, + absolute: true, // value recorders are defined as absolute by default ...options, }; @@ -104,8 +104,6 @@ export class Meter implements api.Meter { return api.NOOP_COUNTER_METRIC; } const opt: MetricOptions = { - monotonic: true, // Counters are defined as monotonic by default - absolute: false, // not applicable to counter, set to false logger: this._logger, ...DEFAULT_METRIC_OPTIONS, ...options, @@ -115,6 +113,41 @@ export class Meter implements api.Meter { return counter; } + /** + * Creates a new `UpDownCounter` metric. UpDownCounter is a synchronous + * instrument and very similar to Counter except that Add(increment) + * supports negative increments. It is generally useful for capturing changes + * in an amount of resources used, or any quantity that rises and falls + * during a request. + * + * @param name the name of the metric. + * @param [options] the metric options. + */ + createUpDownCounter( + name: string, + options?: api.MetricOptions + ): api.UpDownCounter { + if (!this._isValidName(name)) { + this._logger.warn( + `Invalid metric name ${name}. Defaulting to noop metric implementation.` + ); + return api.NOOP_COUNTER_METRIC; + } + const opt: MetricOptions = { + logger: this._logger, + ...DEFAULT_METRIC_OPTIONS, + ...options, + }; + const upDownCounter = new UpDownCounterMetric( + name, + opt, + this._batcher, + this._resource + ); + this._registerMetric(name, upDownCounter); + return upDownCounter; + } + /** * Creates a new observer metric. * @param name the name of the metric. @@ -128,8 +161,6 @@ export class Meter implements api.Meter { return api.NOOP_OBSERVER_METRIC; } const opt: MetricOptions = { - monotonic: false, // Observers are defined as non-monotonic by default - absolute: false, // not applicable to observer, set to false logger: this._logger, ...DEFAULT_METRIC_OPTIONS, ...options, @@ -188,7 +219,8 @@ export class Meter implements api.Meter { * * 2. The first character must be non-numeric, non-space, non-punctuation * - * 3. Subsequent characters must be belong to the alphanumeric characters, '_', '.', and '-'. + * 3. Subsequent characters must be belong to the alphanumeric characters, + * '_', '.', and '-'. * * Names are case insensitive * diff --git a/packages/opentelemetry-metrics/src/Metric.ts b/packages/opentelemetry-metrics/src/Metric.ts index 09aa518028..194971ba5e 100644 --- a/packages/opentelemetry-metrics/src/Metric.ts +++ b/packages/opentelemetry-metrics/src/Metric.ts @@ -31,7 +31,6 @@ import { hashLabels } from './Utils'; /** This is a SDK implementation of {@link Metric} interface. */ export abstract class Metric implements api.UnboundMetric { - protected readonly _monotonic: boolean; protected readonly _disabled: boolean; protected readonly _valueType: api.ValueType; protected readonly _logger: api.Logger; @@ -44,7 +43,6 @@ export abstract class Metric private readonly _kind: MetricKind, readonly resource: Resource ) { - this._monotonic = _options.monotonic; this._disabled = _options.disabled; this._valueType = _options.valueType; this._logger = _options.logger; @@ -98,7 +96,6 @@ export abstract class Metric unit: this._options.unit, metricKind: this._kind, valueType: this._valueType, - monotonic: this._monotonic, }; } @@ -119,7 +116,6 @@ export class CounterMetric extends Metric implements api.Counter { return new BoundCounter( labels, this._disabled, - this._monotonic, this._valueType, this._logger, // @todo: consider to set to CounterSumAggregator always. @@ -130,8 +126,8 @@ export class CounterMetric extends Metric implements api.Counter { /** * Adds the given value to the current value. Values cannot be negative. * @param value the value to add. - * @param [labels = {}] key-values pairs that are associated with a specific metric - * that you want to record. + * @param [labels = {}] key-values pairs that are associated with a specific + * metric that you want to record. */ add(value: number, labels: api.Labels = {}) { this.bind(labels).add(value); @@ -156,7 +152,6 @@ export class ValueRecorderMetric extends Metric return new BoundValueRecorder( labels, this._disabled, - this._monotonic, this._absolute, this._valueType, this._logger, @@ -187,7 +182,6 @@ export class ObserverMetric extends Metric return new BoundObserver( labels, this._disabled, - this._monotonic, this._valueType, this._logger, this._batcher.aggregatorFor(this._descriptor) diff --git a/packages/opentelemetry-metrics/src/UpDownCounterMetric.ts b/packages/opentelemetry-metrics/src/UpDownCounterMetric.ts new file mode 100644 index 0000000000..03a4b1c277 --- /dev/null +++ b/packages/opentelemetry-metrics/src/UpDownCounterMetric.ts @@ -0,0 +1,55 @@ +/*! + * Copyright 2019, 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 * as api from '@opentelemetry/api'; +import { Resource } from '@opentelemetry/resources'; +import { BoundUpDownCounter } from './BoundInstrument'; +import { MetricOptions } from './types'; +import { MetricKind } from './export/types'; +import { Batcher } from './export/Batcher'; +import { Metric } from './Metric'; + +/** This is a SDK implementation of UpDownCounter Metric. */ +export class UpDownCounterMetric extends Metric + implements api.UpDownCounter { + constructor( + name: string, + options: MetricOptions, + private readonly _batcher: Batcher, + resource: Resource + ) { + super(name, options, MetricKind.UP_DOWN_COUNTER, resource); + } + protected _makeInstrument(labels: api.Labels): BoundUpDownCounter { + return new BoundUpDownCounter( + labels, + this._disabled, + this._valueType, + this._logger, + this._batcher.aggregatorFor(this._descriptor) + ); + } + + /** + * Adds the given value to the current value. Values cannot be negative. + * @param value the value to add. + * @param [labels = {}] key-values pairs that are associated with a specific + * metric that you want to record. + */ + add(value: number, labels: api.Labels = {}) { + this.bind(labels).add(value); + } +} diff --git a/packages/opentelemetry-metrics/src/export/Batcher.ts b/packages/opentelemetry-metrics/src/export/Batcher.ts index 8b3dfa5392..23f2d24fa2 100644 --- a/packages/opentelemetry-metrics/src/export/Batcher.ts +++ b/packages/opentelemetry-metrics/src/export/Batcher.ts @@ -55,6 +55,7 @@ export class UngroupedBatcher extends Batcher { aggregatorFor(metricDescriptor: MetricDescriptor): Aggregator { switch (metricDescriptor.metricKind) { case MetricKind.COUNTER: + case MetricKind.UP_DOWN_COUNTER: return new CounterSumAggregator(); case MetricKind.OBSERVER: return new ObserverAggregator(); diff --git a/packages/opentelemetry-metrics/src/export/types.ts b/packages/opentelemetry-metrics/src/export/types.ts index a07ca8224e..2dacf02a9f 100644 --- a/packages/opentelemetry-metrics/src/export/types.ts +++ b/packages/opentelemetry-metrics/src/export/types.ts @@ -82,7 +82,6 @@ export interface MetricDescriptor { readonly unit: string; readonly metricKind: MetricKind; readonly valueType: ValueType; - readonly monotonic: boolean; } /** diff --git a/packages/opentelemetry-metrics/src/index.ts b/packages/opentelemetry-metrics/src/index.ts index 6cb01ff9a8..bc2cce8475 100644 --- a/packages/opentelemetry-metrics/src/index.ts +++ b/packages/opentelemetry-metrics/src/index.ts @@ -22,3 +22,4 @@ export * from './MetricObservable'; export * from './export/aggregators'; export * from './export/ConsoleMetricExporter'; export * from './export/types'; +export * from './UpDownCounterMetric'; diff --git a/packages/opentelemetry-metrics/src/types.ts b/packages/opentelemetry-metrics/src/types.ts index 6f0fea05e7..d3a89cacb7 100644 --- a/packages/opentelemetry-metrics/src/types.ts +++ b/packages/opentelemetry-metrics/src/types.ts @@ -37,10 +37,7 @@ export interface MetricOptions { /** Indicates the metric is a verbose metric that is disabled by default. */ disabled: boolean; - /** Monotonic metrics may only increase. */ - monotonic: boolean; - - /** (ValueRecorder only) Asserts that this metric will only accept non-negative values. */ + /** (Measure only) Asserts that this metric will only accept non-negative values. */ absolute: boolean; /** User provided logger. */ @@ -79,6 +76,7 @@ export const DEFAULT_CONFIG = { /** The default metric creation options value. */ export const DEFAULT_METRIC_OPTIONS = { disabled: false, + absolute: false, description: '', unit: '1', valueType: ValueType.DOUBLE, diff --git a/packages/opentelemetry-metrics/test/Meter.test.ts b/packages/opentelemetry-metrics/test/Meter.test.ts index a71c82b41f..451ff27487 100644 --- a/packages/opentelemetry-metrics/test/Meter.test.ts +++ b/packages/opentelemetry-metrics/test/Meter.test.ts @@ -29,6 +29,7 @@ import { Aggregator, MetricObservable, MetricDescriptor, + UpDownCounterMetric, } from '../src'; import * as api from '@opentelemetry/api'; import { NoopLogger, hrTime, hrTimeToNanoseconds } from '@opentelemetry/core'; @@ -66,7 +67,6 @@ describe('Meter', () => { description: 'desc', unit: '1', disabled: false, - monotonic: false, }); assert.ok(counter instanceof Metric); }); @@ -97,7 +97,6 @@ describe('Meter', () => { description: 'desc', unit: '1', disabled: false, - monotonic: true, }); counter.add(1); meter.collect(); @@ -136,7 +135,7 @@ describe('Meter', () => { assert.strictEqual(boundCounter.getLabels(), labels); }); - it('should add positive values by default', () => { + it('should add positive values only', () => { const counter = meter.createCounter('name') as CounterMetric; const boundCounter = counter.bind(labels); boundCounter.add(10); @@ -160,17 +159,6 @@ describe('Meter', () => { assert.strictEqual(record1.aggregator.toPoint().value, 0); }); - it('should add negative value when monotonic is set to false', () => { - const counter = meter.createCounter('name', { - monotonic: false, - }) as CounterMetric; - const boundCounter = counter.bind(labels); - boundCounter.add(-10); - meter.collect(); - const [record1] = meter.getBatcher().checkPointSet(); - assert.strictEqual(record1.aggregator.toPoint().value, -10); - }); - it('should return same instrument on same label values', () => { const counter = meter.createCounter('name') as CounterMetric; const boundCounter = counter.bind(labels); @@ -229,7 +217,201 @@ describe('Meter', () => { assert.deepStrictEqual(record[0].descriptor, { description: '', metricKind: MetricKind.COUNTER, - monotonic: true, + name: 'name1', + unit: '1', + valueType: ValueType.DOUBLE, + }); + assert.strictEqual(record[0].aggregator.toPoint().value, 10); + }); + }); + + describe('names', () => { + it('should create counter with valid names', () => { + const counter1 = meter.createCounter('name1'); + const counter2 = meter.createCounter( + 'Name_with-all.valid_CharacterClasses' + ); + assert.ok(counter1 instanceof CounterMetric); + assert.ok(counter2 instanceof CounterMetric); + }); + + it('should return no op metric if name is an empty string', () => { + const counter = meter.createCounter(''); + assert.ok(counter instanceof api.NoopMetric); + }); + + it('should return no op metric if name does not start with a letter', () => { + const counter1 = meter.createCounter('1name'); + const counter_ = meter.createCounter('_name'); + assert.ok(counter1 instanceof api.NoopMetric); + assert.ok(counter_ instanceof api.NoopMetric); + }); + + it('should return no op metric if name is an empty string contain only letters, numbers, ".", "_", and "-"', () => { + const counter = meter.createCounter('name with invalid characters^&*('); + assert.ok(counter instanceof api.NoopMetric); + }); + }); + }); + + describe('#UpDownCounter', () => { + const performanceTimeOrigin = hrTime(); + + it('should create a UpDownCounter', () => { + const upDownCounter = meter.createUpDownCounter('name'); + assert.ok(upDownCounter instanceof Metric); + }); + + it('should create a UpDownCounter with options', () => { + const upDownCounter = meter.createUpDownCounter('name', { + description: 'desc', + unit: '1', + disabled: false, + }); + assert.ok(upDownCounter instanceof Metric); + }); + + it('should be able to call add() directly on UpDownCounter', () => { + const upDownCounter = meter.createUpDownCounter('name'); + upDownCounter.add(10, labels); + meter.collect(); + const [record1] = meter.getBatcher().checkPointSet(); + + assert.strictEqual(record1.aggregator.toPoint().value, 10); + const lastTimestamp = record1.aggregator.toPoint().timestamp; + assert.ok( + hrTimeToNanoseconds(lastTimestamp) > + hrTimeToNanoseconds(performanceTimeOrigin) + ); + upDownCounter.add(10, labels); + assert.strictEqual(record1.aggregator.toPoint().value, 20); + + assert.ok( + hrTimeToNanoseconds(record1.aggregator.toPoint().timestamp) > + hrTimeToNanoseconds(lastTimestamp) + ); + }); + + it('should be able to call add with no labels', () => { + const upDownCounter = meter.createUpDownCounter('name', { + description: 'desc', + unit: '1', + disabled: false, + }); + upDownCounter.add(1); + meter.collect(); + const [record1] = meter.getBatcher().checkPointSet(); + assert.strictEqual(record1.aggregator.toPoint().value, 1); + }); + + it('should pipe through resource', () => { + const upDownCounter = meter.createUpDownCounter( + 'name' + ) as UpDownCounterMetric; + assert.ok(upDownCounter.resource instanceof Resource); + + upDownCounter.add(1, { foo: 'bar' }); + + const [record] = upDownCounter.getMetricRecord(); + assert.ok(record.resource instanceof Resource); + }); + + describe('.bind()', () => { + it('should create a UpDownCounter instrument', () => { + const upDownCounter = meter.createUpDownCounter('name'); + const boundCounter = upDownCounter.bind(labels); + boundCounter.add(10); + meter.collect(); + const [record1] = meter.getBatcher().checkPointSet(); + + assert.strictEqual(record1.aggregator.toPoint().value, 10); + boundCounter.add(-200); + assert.strictEqual(record1.aggregator.toPoint().value, -190); + }); + + it('should return the aggregator', () => { + const upDownCounter = meter.createUpDownCounter( + 'name' + ) as UpDownCounterMetric; + const boundCounter = upDownCounter.bind(labels); + boundCounter.add(20); + assert.ok(boundCounter.getAggregator() instanceof CounterSumAggregator); + assert.strictEqual(boundCounter.getLabels(), labels); + }); + + it('should not add the instrument data when disabled', () => { + const upDownCounter = meter.createUpDownCounter('name', { + disabled: true, + }); + const boundCounter = upDownCounter.bind(labels); + boundCounter.add(10); + meter.collect(); + const [record1] = meter.getBatcher().checkPointSet(); + assert.strictEqual(record1.aggregator.toPoint().value, 0); + }); + + it('should return same instrument on same label values', () => { + const upDownCounter = meter.createUpDownCounter('name'); + const boundCounter = upDownCounter.bind(labels); + boundCounter.add(10); + const boundCounter1 = upDownCounter.bind(labels); + boundCounter1.add(10); + meter.collect(); + const [record1] = meter.getBatcher().checkPointSet(); + + assert.strictEqual(record1.aggregator.toPoint().value, 20); + assert.strictEqual(boundCounter, boundCounter1); + }); + }); + + describe('.unbind()', () => { + it('should remove a UpDownCounter instrument', () => { + const upDownCounter = meter.createUpDownCounter( + 'name' + ) as UpDownCounterMetric; + const boundCounter = upDownCounter.bind(labels); + assert.strictEqual(upDownCounter['_instruments'].size, 1); + upDownCounter.unbind(labels); + assert.strictEqual(upDownCounter['_instruments'].size, 0); + const boundCounter1 = upDownCounter.bind(labels); + assert.strictEqual(upDownCounter['_instruments'].size, 1); + assert.notStrictEqual(boundCounter, boundCounter1); + }); + + it('should not fail when removing non existing instrument', () => { + const upDownCounter = meter.createUpDownCounter('name'); + upDownCounter.unbind({}); + }); + + it('should clear all instruments', () => { + const upDownCounter = meter.createUpDownCounter( + 'name' + ) as CounterMetric; + upDownCounter.bind(labels); + assert.strictEqual(upDownCounter['_instruments'].size, 1); + upDownCounter.clear(); + assert.strictEqual(upDownCounter['_instruments'].size, 0); + }); + }); + + describe('.registerMetric()', () => { + it('skip already registered Metric', () => { + const counter1 = meter.createCounter('name1') as CounterMetric; + counter1.bind(labels).add(10); + + // should skip below metric + const counter2 = meter.createCounter('name1', { + valueType: api.ValueType.INT, + }) as CounterMetric; + counter2.bind(labels).add(500); + + meter.collect(); + const record = meter.getBatcher().checkPointSet(); + + assert.strictEqual(record.length, 1); + assert.deepStrictEqual(record[0].descriptor, { + description: '', + metricKind: MetricKind.COUNTER, name: 'name1', unit: '1', valueType: ValueType.DOUBLE, @@ -557,7 +739,6 @@ describe('Meter', () => { name: 'counter', description: 'test', metricKind: MetricKind.COUNTER, - monotonic: true, unit: '1', valueType: ValueType.DOUBLE, }); @@ -584,7 +765,6 @@ describe('Meter', () => { name: 'counter', description: 'test', metricKind: MetricKind.COUNTER, - monotonic: true, unit: '1', valueType: ValueType.INT, }); @@ -630,5 +810,4 @@ function ensureMetric(metric: MetricRecord) { assert.strictEqual(descriptor.unit, '1'); assert.strictEqual(descriptor.metricKind, MetricKind.OBSERVER); assert.strictEqual(descriptor.valueType, ValueType.DOUBLE); - assert.strictEqual(descriptor.monotonic, false); } diff --git a/packages/opentelemetry-metrics/test/export/ConsoleMetricExporter.test.ts b/packages/opentelemetry-metrics/test/export/ConsoleMetricExporter.test.ts index d47bc2981c..987f468212 100644 --- a/packages/opentelemetry-metrics/test/export/ConsoleMetricExporter.test.ts +++ b/packages/opentelemetry-metrics/test/export/ConsoleMetricExporter.test.ts @@ -57,7 +57,6 @@ describe('ConsoleMetricExporter', () => { { description: 'a test description', metricKind: MetricKind.COUNTER, - monotonic: true, name: 'counter', unit: '1', valueType: ValueType.DOUBLE,