diff --git a/api/CHANGELOG.md b/api/CHANGELOG.md index 946d39be863..5f247781575 100644 --- a/api/CHANGELOG.md +++ b/api/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. ### :rocket: (Enhancement) +* feat(metrics): added synchronous gauge [#4528](https://github.com/open-telemetry/opentelemetry-js/pull/4528) @clintonb * feat(api): allow adding span links after span creation [#4536](https://github.com/open-telemetry/opentelemetry-js/pull/4536) @seemk * This change is non-breaking for end-users, but breaking for Trace SDK implmentations in accordance with the [specification](https://github.com/open-telemetry/opentelemetry-specification/blob/a03382ada8afa9415266a84dafac0510ec8c160f/specification/upgrading.md?plain=1#L97-L122) as new features need to be implemented. * feat: support node 22 [#4666](https://github.com/open-telemetry/opentelemetry-js/pull/4666) @dyladan diff --git a/api/src/index.ts b/api/src/index.ts index c5dbe1685bf..af483a9a416 100644 --- a/api/src/index.ts +++ b/api/src/index.ts @@ -43,6 +43,7 @@ export { MeterProvider } from './metrics/MeterProvider'; export { ValueType, Counter, + Gauge, Histogram, MetricOptions, Observable, diff --git a/api/src/metrics/Meter.ts b/api/src/metrics/Meter.ts index 1405ae74730..2cc0c2acd5f 100644 --- a/api/src/metrics/Meter.ts +++ b/api/src/metrics/Meter.ts @@ -17,6 +17,7 @@ import { BatchObservableCallback, Counter, + Gauge, Histogram, MetricAttributes, MetricOptions, @@ -45,6 +46,16 @@ export interface MeterOptions { * for the exported metric are deferred. */ export interface Meter { + /** + * Creates and returns a new `Gauge`. + * @param name the name of the metric. + * @param [options] the metric options. + */ + createGauge( + name: string, + options?: MetricOptions + ): Gauge; + /** * Creates and returns a new `Histogram`. * @param name the name of the metric. diff --git a/api/src/metrics/Metric.ts b/api/src/metrics/Metric.ts index e8abca3b1d7..b2a1b7326b8 100644 --- a/api/src/metrics/Metric.ts +++ b/api/src/metrics/Metric.ts @@ -98,6 +98,15 @@ export interface UpDownCounter< add(value: number, attributes?: AttributesTypes, context?: Context): void; } +export interface Gauge< + AttributesTypes extends MetricAttributes = MetricAttributes, +> { + /** + * Records a measurement. + */ + record(value: number, attributes?: AttributesTypes, context?: Context): void; +} + export interface Histogram< AttributesTypes extends MetricAttributes = MetricAttributes, > { diff --git a/api/src/metrics/NoopMeter.ts b/api/src/metrics/NoopMeter.ts index 81143c0dddb..7adea12f46a 100644 --- a/api/src/metrics/NoopMeter.ts +++ b/api/src/metrics/NoopMeter.ts @@ -18,15 +18,16 @@ import { Meter } from './Meter'; import { BatchObservableCallback, Counter, + Gauge, Histogram, + MetricAttributes, MetricOptions, + Observable, ObservableCallback, ObservableCounter, ObservableGauge, ObservableUpDownCounter, UpDownCounter, - MetricAttributes, - Observable, } from './Metric'; /** @@ -36,6 +37,13 @@ import { export class NoopMeter implements Meter { constructor() {} + /** + * @see {@link Meter.createGauge} + */ + createGauge(_name: string, _options?: MetricOptions): Gauge { + return NOOP_GAUGE_METRIC; + } + /** * @see {@link Meter.createHistogram} */ @@ -114,6 +122,10 @@ export class NoopUpDownCounterMetric add(_value: number, _attributes: MetricAttributes): void {} } +export class NoopGaugeMetric extends NoopMetric implements Gauge { + record(_value: number, _attributes: MetricAttributes): void {} +} + export class NoopHistogramMetric extends NoopMetric implements Histogram { record(_value: number, _attributes: MetricAttributes): void {} } @@ -140,6 +152,7 @@ export const NOOP_METER = new NoopMeter(); // Synchronous instruments export const NOOP_COUNTER_METRIC = new NoopCounterMetric(); +export const NOOP_GAUGE_METRIC = new NoopGaugeMetric(); export const NOOP_HISTOGRAM_METRIC = new NoopHistogramMetric(); export const NOOP_UP_DOWN_COUNTER_METRIC = new NoopUpDownCounterMetric(); diff --git a/api/test/common/noop-implementations/noop-meter.test.ts b/api/test/common/noop-implementations/noop-meter.test.ts index cde2b094de1..107e4f275a2 100644 --- a/api/test/common/noop-implementations/noop-meter.test.ts +++ b/api/test/common/noop-implementations/noop-meter.test.ts @@ -24,6 +24,7 @@ import { NOOP_OBSERVABLE_UP_DOWN_COUNTER_METRIC, NOOP_UP_DOWN_COUNTER_METRIC, createNoopMeter, + NOOP_GAUGE_METRIC, } from '../../../src/metrics/NoopMeter'; import { NoopMeterProvider } from '../../../src/metrics/NoopMeterProvider'; @@ -116,6 +117,17 @@ describe('NoopMeter', () => { ); }); + it('gauge should not crash', () => { + const meter = new NoopMeterProvider().getMeter('test-noop'); + const observableGauge = meter.createGauge('some-name'); + + // ensure the correct noop const is returned + assert.strictEqual(observableGauge, NOOP_GAUGE_METRIC); + + const gaugeWithOptions = meter.createGauge('some-name', options); + assert.strictEqual(gaugeWithOptions, NOOP_GAUGE_METRIC); + }); + it('observable up down counter should not crash', () => { const meter = new NoopMeterProvider().getMeter('test-noop'); const observableUpDownCounter = diff --git a/packages/sdk-metrics/src/Meter.ts b/packages/sdk-metrics/src/Meter.ts index 2af51dd82d6..cf0e25e56c1 100644 --- a/packages/sdk-metrics/src/Meter.ts +++ b/packages/sdk-metrics/src/Meter.ts @@ -25,7 +25,6 @@ import { ObservableUpDownCounter, BatchObservableCallback, Observable, - Attributes, } from '@opentelemetry/api'; import { createInstrumentDescriptor, @@ -51,12 +50,8 @@ export class Meter implements IMeter { /** * Create a {@link Gauge} instrument. - * @experimental */ - createGauge( - name: string, - options?: MetricOptions - ): Gauge { + createGauge(name: string, options?: MetricOptions): Gauge { const descriptor = createInstrumentDescriptor( name, InstrumentType.GAUGE, diff --git a/packages/sdk-metrics/src/types.ts b/packages/sdk-metrics/src/types.ts index 41c617ffc57..d89aa22399b 100644 --- a/packages/sdk-metrics/src/types.ts +++ b/packages/sdk-metrics/src/types.ts @@ -26,8 +26,6 @@ export type ShutdownOptions = CommonReaderOptions; export type ForceFlushOptions = CommonReaderOptions; /** - * @experimental - * * This is intentionally not using the API's type as it's only available from @opentelemetry/api 1.9.0 and up. * In SDK 2.0 we'll be able to bump the minimum API version and remove this workaround. */ diff --git a/packages/sdk-metrics/test/Instruments.test.ts b/packages/sdk-metrics/test/Instruments.test.ts index 71c69913a39..e02a393397d 100644 --- a/packages/sdk-metrics/test/Instruments.test.ts +++ b/packages/sdk-metrics/test/Instruments.test.ts @@ -19,25 +19,25 @@ import * as sinon from 'sinon'; import { InstrumentationScope } from '@opentelemetry/core'; import { Resource } from '@opentelemetry/resources'; import { - InstrumentType, - MeterProvider, - MetricReader, DataPoint, DataPointType, Histogram, + InstrumentType, + MeterProvider, MetricDescriptor, + MetricReader, } from '../src'; import { TestDeltaMetricReader, TestMetricReader, } from './export/TestMetricReader'; import { - assertMetricData, assertDataPoint, - commonValues, + assertMetricData, commonAttributes, - defaultResource, + commonValues, defaultInstrumentationScope, + defaultResource, } from './util'; import { ObservableResult, ValueType } from '@opentelemetry/api'; @@ -764,6 +764,41 @@ describe('Instruments', () => { }); }); }); + + describe('Gauge', () => { + it('should record common values and attributes without exceptions', async () => { + const { meter } = setup(); + const gauge = meter.createGauge('test'); + + for (const values of commonValues) { + for (const attributes of commonAttributes) { + gauge.record(values, attributes); + } + } + }); + + it('should record values', async () => { + const { meter, cumulativeReader } = setup(); + const gauge = meter.createGauge('test'); + + gauge.record(1, { foo: 'bar' }); + gauge.record(-1); + + await validateExport(cumulativeReader, { + dataPointType: DataPointType.GAUGE, + dataPoints: [ + { + attributes: { foo: 'bar' }, + value: 1, + }, + { + attributes: {}, + value: -1, + }, + ], + }); + }); + }); }); function setup() {