diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 0b286f0bc9..7a4547ffc6 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -26,6 +26,7 @@ and AnyValueMap types [#4575](https://github.com/open-telemetry/opentelemetry-js ### :rocket: (Enhancement) +* feat(metrics): added synchronous gauge to SDK [#4565](https://github.com/open-telemetry/opentelemetry-js/pull/4565) @clintonb * feat(opentelemetry-instrumentation-xhr): optionally ignore network events [#4571](https://github.com/open-telemetry/opentelemetry-js/pull/4571/) @mustafahaddara * refactor(instr-http): use exported strings for semconv. [#4573](https://github.com/open-telemetry/opentelemetry-js/pull/4573/) @JamieDanielson * perf(instrumentation-http): remove obvious temp allocations [#4576](https://github.com/open-telemetry/opentelemetry-js/pull/4576) @Samuron diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/OTLPMetricExporterBase.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/OTLPMetricExporterBase.ts index 66a5bfb596..91aaf67977 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/OTLPMetricExporterBase.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/OTLPMetricExporterBase.ts @@ -41,6 +41,7 @@ export const DeltaTemporalitySelector: AggregationTemporalitySelector = ( switch (instrumentType) { case InstrumentType.COUNTER: case InstrumentType.OBSERVABLE_COUNTER: + case InstrumentType.GAUGE: case InstrumentType.HISTOGRAM: case InstrumentType.OBSERVABLE_GAUGE: return AggregationTemporality.DELTA; @@ -57,6 +58,7 @@ export const LowMemoryTemporalitySelector: AggregationTemporalitySelector = ( case InstrumentType.COUNTER: case InstrumentType.HISTOGRAM: return AggregationTemporality.DELTA; + case InstrumentType.GAUGE: case InstrumentType.UP_DOWN_COUNTER: case InstrumentType.OBSERVABLE_UP_DOWN_COUNTER: case InstrumentType.OBSERVABLE_COUNTER: diff --git a/packages/sdk-metrics/src/InstrumentDescriptor.ts b/packages/sdk-metrics/src/InstrumentDescriptor.ts index b8797e2c4d..ef09dda8bb 100644 --- a/packages/sdk-metrics/src/InstrumentDescriptor.ts +++ b/packages/sdk-metrics/src/InstrumentDescriptor.ts @@ -23,6 +23,7 @@ import { equalsCaseInsensitive } from './utils'; */ export enum InstrumentType { COUNTER = 'COUNTER', + GAUGE = 'GAUGE', HISTOGRAM = 'HISTOGRAM', UP_DOWN_COUNTER = 'UP_DOWN_COUNTER', OBSERVABLE_COUNTER = 'OBSERVABLE_COUNTER', diff --git a/packages/sdk-metrics/src/Instruments.ts b/packages/sdk-metrics/src/Instruments.ts index f665952f05..0113abd190 100644 --- a/packages/sdk-metrics/src/Instruments.ts +++ b/packages/sdk-metrics/src/Instruments.ts @@ -36,6 +36,7 @@ import { AsyncWritableMetricStorage, WritableMetricStorage, } from './state/WritableMetricStorage'; +import { Gauge } from './types'; export class SyncInstrument { constructor( @@ -110,6 +111,18 @@ export class CounterInstrument extends SyncInstrument implements Counter { } } +/** + * The class implements {@link Gauge} interface. + */ +export class GaugeInstrument extends SyncInstrument implements Gauge { + /** + * Records a measurement. + */ + record(value: number, attributes?: MetricAttributes, ctx?: Context): void { + this._record(value, attributes, ctx); + } +} + /** * The class implements {@link Histogram} interface. */ diff --git a/packages/sdk-metrics/src/Meter.ts b/packages/sdk-metrics/src/Meter.ts index b3d69d0dfd..2af51dd82d 100644 --- a/packages/sdk-metrics/src/Meter.ts +++ b/packages/sdk-metrics/src/Meter.ts @@ -25,6 +25,7 @@ import { ObservableUpDownCounter, BatchObservableCallback, Observable, + Attributes, } from '@opentelemetry/api'; import { createInstrumentDescriptor, @@ -32,6 +33,7 @@ import { } from './InstrumentDescriptor'; import { CounterInstrument, + GaugeInstrument, HistogramInstrument, ObservableCounterInstrument, ObservableGaugeInstrument, @@ -39,6 +41,7 @@ import { UpDownCounterInstrument, } from './Instruments'; import { MeterSharedState } from './state/MeterSharedState'; +import { Gauge } from './types'; /** * This class implements the {@link IMeter} interface. @@ -46,6 +49,23 @@ import { MeterSharedState } from './state/MeterSharedState'; export class Meter implements IMeter { constructor(private _meterSharedState: MeterSharedState) {} + /** + * Create a {@link Gauge} instrument. + * @experimental + */ + createGauge( + name: string, + options?: MetricOptions + ): Gauge { + const descriptor = createInstrumentDescriptor( + name, + InstrumentType.GAUGE, + options + ); + const storage = this._meterSharedState.registerMetricStorage(descriptor); + return new GaugeInstrument(storage, descriptor); + } + /** * Create a {@link Histogram} instrument. */ diff --git a/packages/sdk-metrics/src/aggregator/ExponentialHistogram.ts b/packages/sdk-metrics/src/aggregator/ExponentialHistogram.ts index a0c438ee17..9eb296fb27 100644 --- a/packages/sdk-metrics/src/aggregator/ExponentialHistogram.ts +++ b/packages/sdk-metrics/src/aggregator/ExponentialHistogram.ts @@ -580,6 +580,7 @@ export class ExponentialHistogramAggregator // determine if instrument allows negative values. const allowsNegativeValues = + descriptor.type === InstrumentType.GAUGE || descriptor.type === InstrumentType.UP_DOWN_COUNTER || descriptor.type === InstrumentType.OBSERVABLE_GAUGE || descriptor.type === InstrumentType.OBSERVABLE_UP_DOWN_COUNTER; diff --git a/packages/sdk-metrics/src/aggregator/Histogram.ts b/packages/sdk-metrics/src/aggregator/Histogram.ts index 94cf906039..5c9390b81f 100644 --- a/packages/sdk-metrics/src/aggregator/Histogram.ts +++ b/packages/sdk-metrics/src/aggregator/Histogram.ts @@ -231,6 +231,7 @@ export class HistogramAggregator implements Aggregator { // determine if instrument allows negative values. const allowsNegativeValues = + descriptor.type === InstrumentType.GAUGE || descriptor.type === InstrumentType.UP_DOWN_COUNTER || descriptor.type === InstrumentType.OBSERVABLE_GAUGE || descriptor.type === InstrumentType.OBSERVABLE_UP_DOWN_COUNTER; diff --git a/packages/sdk-metrics/src/types.ts b/packages/sdk-metrics/src/types.ts index 89af1eea33..41c617ffc5 100644 --- a/packages/sdk-metrics/src/types.ts +++ b/packages/sdk-metrics/src/types.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Context, MetricAttributes } from '@opentelemetry/api'; export type CommonReaderOptions = { timeoutMillis?: number; @@ -23,3 +24,18 @@ export type CollectionOptions = CommonReaderOptions; 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. + */ +export interface Gauge< + AttributesTypes extends MetricAttributes = MetricAttributes, +> { + /** + * Records a measurement. Value of the measurement must not be negative. + */ + record(value: number, attributes?: AttributesTypes, context?: Context): void; +} diff --git a/packages/sdk-metrics/src/view/Aggregation.ts b/packages/sdk-metrics/src/view/Aggregation.ts index 2f74063ca2..f34bd1231a 100644 --- a/packages/sdk-metrics/src/view/Aggregation.ts +++ b/packages/sdk-metrics/src/view/Aggregation.ts @@ -182,6 +182,7 @@ export class DefaultAggregation extends Aggregation { case InstrumentType.OBSERVABLE_UP_DOWN_COUNTER: { return SUM_AGGREGATION; } + case InstrumentType.GAUGE: case InstrumentType.OBSERVABLE_GAUGE: { return LAST_VALUE_AGGREGATION; } diff --git a/packages/sdk-metrics/test/Meter.test.ts b/packages/sdk-metrics/test/Meter.test.ts index e40d6aa507..875f202862 100644 --- a/packages/sdk-metrics/test/Meter.test.ts +++ b/packages/sdk-metrics/test/Meter.test.ts @@ -19,6 +19,7 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; import { CounterInstrument, + GaugeInstrument, HistogramInstrument, ObservableCounterInstrument, ObservableGaugeInstrument, @@ -88,6 +89,18 @@ describe('Meter', () => { }); }); + describe('createGauge', () => { + testWithNames('Gauge', name => { + const meterSharedState = new MeterSharedState( + new MeterProviderSharedState(defaultResource), + defaultInstrumentationScope + ); + const meter = new Meter(meterSharedState); + const Gauge = meter.createGauge(name); + assert(Gauge instanceof GaugeInstrument); + }); + }); + describe('createObservableCounter', () => { testWithNames('ObservableCounter', name => { const meterSharedState = new MeterSharedState( diff --git a/packages/sdk-metrics/test/export/ConsoleMetricExporter.test.ts b/packages/sdk-metrics/test/export/ConsoleMetricExporter.test.ts index 6409f6aa04..258f0df00f 100644 --- a/packages/sdk-metrics/test/export/ConsoleMetricExporter.test.ts +++ b/packages/sdk-metrics/test/export/ConsoleMetricExporter.test.ts @@ -160,6 +160,7 @@ describe('ConsoleMetricExporter', () => { switch (instrumentType) { case InstrumentType.COUNTER: case InstrumentType.OBSERVABLE_COUNTER: + case InstrumentType.GAUGE: case InstrumentType.HISTOGRAM: case InstrumentType.OBSERVABLE_GAUGE: return AggregationTemporality.DELTA; diff --git a/packages/sdk-metrics/test/export/utils.ts b/packages/sdk-metrics/test/export/utils.ts index a5decfae41..a10021ee90 100644 --- a/packages/sdk-metrics/test/export/utils.ts +++ b/packages/sdk-metrics/test/export/utils.ts @@ -29,6 +29,7 @@ const instrumentTypes = [ InstrumentType.UP_DOWN_COUNTER, InstrumentType.OBSERVABLE_UP_DOWN_COUNTER, InstrumentType.HISTOGRAM, + InstrumentType.GAUGE, InstrumentType.OBSERVABLE_GAUGE, ];