From 5110fc9a82d86e694d2e6f24ff9ecf77c0355a41 Mon Sep 17 00:00:00 2001 From: Marc Pichler Date: Thu, 8 Aug 2024 15:15:18 +0200 Subject: [PATCH 1/6] feat(sdk-metrics)!: drop View and Aggregation for options --- CHANGELOG_NEXT.md | 1 + .../test/metricsHelper.ts | 12 +- .../src/OTLPMetricExporterBase.ts | 11 +- .../test/metricsHelper.ts | 17 ++- .../test/node/CollectorMetricExporter.test.ts | 24 ++-- .../test/metricsHelper.ts | 12 +- .../src/PrometheusExporter.ts | 8 +- .../test/PrometheusSerializer.test.ts | 75 +++++++----- .../opentelemetry-sdk-node/src/sdk.ts | 10 +- .../opentelemetry-sdk-node/src/types.ts | 4 +- .../opentelemetry-sdk-node/test/sdk.test.ts | 5 +- packages/sdk-metrics/src/MeterProvider.ts | 8 +- .../src/export/AggregationSelector.ts | 11 +- .../sdk-metrics/src/export/MetricExporter.ts | 4 +- .../sdk-metrics/src/export/MetricReader.ts | 6 +- packages/sdk-metrics/src/index.ts | 15 +-- .../src/state/MeterProviderSharedState.ts | 9 +- packages/sdk-metrics/src/view/Aggregation.ts | 61 +++------- .../sdk-metrics/src/view/AggregationOption.ts | 107 ++++++++++++++++++ packages/sdk-metrics/src/view/View.ts | 55 +++++---- .../sdk-metrics/test/MeterProvider.test.ts | 46 ++++---- .../test/export/MetricReader.test.ts | 14 ++- .../PeriodicExportingMetricReader.test.ts | 11 +- packages/sdk-metrics/test/export/utils.ts | 2 +- .../cumulative-exponential-histogram.test.ts | 11 +- .../histogram-recording-nan.test.ts | 6 +- .../test/state/MeterSharedState.test.ts | 26 ++--- .../sdk-metrics/test/view/Aggregation.test.ts | 27 ----- packages/sdk-metrics/test/view/View.test.ts | 16 +-- .../test/view/ViewRegistry.test.ts | 2 +- 30 files changed, 375 insertions(+), 241 deletions(-) create mode 100644 packages/sdk-metrics/src/view/AggregationOption.ts diff --git a/CHANGELOG_NEXT.md b/CHANGELOG_NEXT.md index a41332c0fc8..1e19dc449ad 100644 --- a/CHANGELOG_NEXT.md +++ b/CHANGELOG_NEXT.md @@ -10,6 +10,7 @@ * chore(otel-resources): replace deprecated SpanAttributes [#4428](https://github.com/open-telemetry/opentelemetry-js/pull/4428) @JamieDanielson * feat(sdk-metrics)!: remove MeterProvider.addMetricReader() in favor of constructor option [#4419](https://github.com/open-telemetry/opentelemetry-js/pull/4419) @pichlermarc * feat(sdk-metrics)!: replace attributeKeys with custom processors option [#4532](https://github.com/open-telemetry/opentelemetry-js/pull/4532) @pichlermarc +* feat(sdk-metrics)!: drop `View` and `Aggregation` in favor of `ViewOptions` and `AggregationOption` [#4931](https://github.com/open-telemetry/opentelemetry-js/pull/4931) @pichlermarc ### :rocket: (Enhancement) diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/metricsHelper.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/metricsHelper.ts index 7c9fbfd99c0..352169212a7 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/metricsHelper.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/metricsHelper.ts @@ -27,10 +27,9 @@ import * as assert from 'assert'; import * as grpc from '@grpc/grpc-js'; import { VERSION } from '@opentelemetry/core'; import { - ExplicitBucketHistogramAggregation, + AggregationType, MeterProvider, MetricReader, - View, } from '@opentelemetry/sdk-metrics'; import { encodeAsString, @@ -72,10 +71,13 @@ export function setUp() { meterProvider = new MeterProvider({ resource: testResource, views: [ - new View({ - aggregation: new ExplicitBucketHistogramAggregation([0, 100]), + { + aggregation: { + type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM, + options: { boundaries: [0, 100] }, + }, instrumentName: 'int-histogram', - }), + }, ], readers: [reader], }); 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 76b7f74dfda..78e6a8c19d4 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/OTLPMetricExporterBase.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/OTLPMetricExporterBase.ts @@ -21,8 +21,9 @@ import { InstrumentType, PushMetricExporter, ResourceMetrics, - Aggregation, AggregationSelector, + AggregationOption, + AggregationType, } from '@opentelemetry/sdk-metrics'; import { AggregationTemporalityPreference, @@ -113,7 +114,11 @@ function chooseAggregationSelector( if (config?.aggregationPreference) { return config.aggregationPreference; } else { - return (_instrumentType: any) => Aggregation.Default(); + return (_instrumentType: any) => { + return { + type: AggregationType.DEFAULT, + }; + }; } } @@ -148,7 +153,7 @@ export class OTLPMetricExporterBase< return Promise.resolve(); } - selectAggregation(instrumentType: InstrumentType): Aggregation { + selectAggregation(instrumentType: InstrumentType): AggregationOption { return this._aggregationSelector(instrumentType); } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/metricsHelper.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/metricsHelper.ts index 850cdae4d0f..62f749f1afe 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/metricsHelper.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/metricsHelper.ts @@ -28,10 +28,10 @@ import { Resource } from '@opentelemetry/resources'; import * as assert from 'assert'; import { InstrumentationScope, VERSION } from '@opentelemetry/core'; import { - ExplicitBucketHistogramAggregation, + AggregationType, MeterProvider, MetricReader, - View, + ViewOptions, } from '@opentelemetry/sdk-metrics'; import { encodeAsString, @@ -59,10 +59,15 @@ class TestMetricReader extends MetricReader { } } -export const HISTOGRAM_AGGREGATION_VIEW = new View({ - aggregation: new ExplicitBucketHistogramAggregation([0, 100]), +export const HISTOGRAM_AGGREGATION_VIEW: ViewOptions = { + aggregation: { + type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM, + options: { + boundaries: [0, 100], + }, + }, instrumentName: 'int-histogram', -}); +}; const defaultResource = Resource.default().merge( new Resource({ @@ -83,7 +88,7 @@ export async function collect() { return (await reader.collect())!; } -export function setUp(views?: View[]) { +export function setUp(views?: ViewOptions[]) { reader = new TestMetricReader(); meterProvider = new MeterProvider({ resource: defaultResource, diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/node/CollectorMetricExporter.test.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/node/CollectorMetricExporter.test.ts index 7b19b84f029..676b3860830 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/node/CollectorMetricExporter.test.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/node/CollectorMetricExporter.test.ts @@ -43,9 +43,9 @@ import { } from '../metricsHelper'; import { MockedResponse } from './nodeHelpers'; import { - Aggregation, + AggregationOption, AggregationTemporality, - ExplicitBucketHistogramAggregation, + AggregationType, InstrumentType, ResourceMetrics, } from '@opentelemetry/sdk-metrics'; @@ -225,9 +225,13 @@ describe('OTLPMetricExporter - node with json over http', () => { describe('aggregation', () => { it('aggregationSelector calls the selector supplied to the constructor', () => { - const aggregation = new ExplicitBucketHistogramAggregation([ - 0, 100, 100000, - ]); + const aggregation: AggregationOption = { + type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM, + options: { + boundaries: [0, 100, 100000], + }, + }; + const exporter = new OTLPMetricExporter({ aggregationPreference: _instrumentType => aggregation, }); @@ -239,11 +243,15 @@ describe('OTLPMetricExporter - node with json over http', () => { it('aggregationSelector returns the default aggregation preference when nothing is supplied', () => { const exporter = new OTLPMetricExporter({ - aggregationPreference: _instrumentType => Aggregation.Default(), + aggregationPreference: _instrumentType => { + return { type: AggregationType.DEFAULT }; + }, }); - assert.equal( + assert.deepStrictEqual( exporter.selectAggregation(InstrumentType.COUNTER), - Aggregation.Default() + { + type: AggregationType.DEFAULT, + } ); }); }); diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/metricsHelper.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/metricsHelper.ts index effc732b370..756d470bc97 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/metricsHelper.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/metricsHelper.ts @@ -25,10 +25,9 @@ import { import { Resource } from '@opentelemetry/resources'; import * as assert from 'assert'; import { - ExplicitBucketHistogramAggregation, + AggregationType, MeterProvider, MetricReader, - View, } from '@opentelemetry/sdk-metrics'; import { encodeAsString, @@ -71,10 +70,13 @@ export function setUp() { meterProvider = new MeterProvider({ resource: testResource, views: [ - new View({ - aggregation: new ExplicitBucketHistogramAggregation([0, 100]), + { + aggregation: { + type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM, + options: { boundaries: [0, 100] }, + }, instrumentName: 'int-histogram', - }), + }, ], readers: [reader], }); diff --git a/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusExporter.ts b/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusExporter.ts index bd763de3f0c..e9f95051f82 100644 --- a/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusExporter.ts +++ b/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusExporter.ts @@ -17,8 +17,8 @@ import { diag } from '@opentelemetry/api'; import { globalErrorHandler } from '@opentelemetry/core'; import { - Aggregation, AggregationTemporality, + AggregationType, MetricReader, } from '@opentelemetry/sdk-metrics'; import { createServer, IncomingMessage, Server, ServerResponse } from 'http'; @@ -60,7 +60,11 @@ export class PrometheusExporter extends MetricReader { callback: (error: Error | void) => void = () => {} ) { super({ - aggregationSelector: _instrumentType => Aggregation.Default(), + aggregationSelector: _instrumentType => { + return { + type: AggregationType.DEFAULT, + }; + }, aggregationTemporalitySelector: _instrumentType => AggregationTemporality.CUMULATIVE, metricProducers: config.metricProducers, diff --git a/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusSerializer.test.ts b/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusSerializer.test.ts index 6368cf82c30..3939d9ff410 100644 --- a/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusSerializer.test.ts +++ b/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusSerializer.test.ts @@ -17,17 +17,12 @@ import * as assert from 'assert'; import { MetricAttributes, UpDownCounter } from '@opentelemetry/api'; import { - Aggregation, AggregationTemporality, DataPoint, DataPointType, - ExplicitBucketHistogramAggregation, Histogram, - LastValueAggregation, MeterProvider, MetricReader, - SumAggregation, - View, } from '@opentelemetry/sdk-metrics'; import * as sinon from 'sinon'; import { PrometheusSerializer } from '../src'; @@ -40,6 +35,7 @@ import { serviceName, } from './util'; import { Resource } from '@opentelemetry/resources'; +import { AggregationType } from '@opentelemetry/sdk-metrics'; const attributes = { foo1: 'bar1', @@ -56,7 +52,9 @@ class TestMetricReader extends MetricReader { super({ aggregationTemporalitySelector: _instrumentType => AggregationTemporality.CUMULATIVE, - aggregationSelector: _instrumentType => Aggregation.Default(), + aggregationSelector: _instrumentType => { + return { type: AggregationType.DEFAULT }; + }, }); } @@ -86,10 +84,10 @@ describe('PrometheusSerializer', () => { const reader = new TestMetricReader(); const meterProvider = new MeterProvider({ views: [ - new View({ - aggregation: new SumAggregation(), + { + aggregation: { type: AggregationType.SUM }, instrumentName: '*', - }), + }, ], readers: [reader], }); @@ -136,10 +134,15 @@ describe('PrometheusSerializer', () => { const reader = new TestMetricReader(); const meterProvider = new MeterProvider({ views: [ - new View({ - aggregation: new ExplicitBucketHistogramAggregation([1, 10, 100]), + { + aggregation: { + type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM, + options: { + boundaries: [1, 10, 100], + }, + }, instrumentName: '*', - }), + }, ], readers: [reader], }); @@ -201,10 +204,10 @@ describe('PrometheusSerializer', () => { const reader = new TestMetricReader(); const meterProvider = new MeterProvider({ views: [ - new View({ - aggregation: new SumAggregation(), + { + aggregation: { type: AggregationType.SUM }, instrumentName: '*', - }), + }, ], readers: [reader], }); @@ -256,10 +259,10 @@ describe('PrometheusSerializer', () => { const reader = new TestMetricReader(); const meterProvider = new MeterProvider({ views: [ - new View({ - aggregation: new SumAggregation(), + { + aggregation: { type: AggregationType.SUM }, instrumentName: '*', - }), + }, ], readers: [reader], }); @@ -310,10 +313,12 @@ describe('PrometheusSerializer', () => { const reader = new TestMetricReader(); const meterProvider = new MeterProvider({ views: [ - new View({ - aggregation: new LastValueAggregation(), + { + aggregation: { + type: AggregationType.LAST_VALUE, + }, instrumentName: '*', - }), + }, ], readers: [reader], }); @@ -364,10 +369,13 @@ describe('PrometheusSerializer', () => { const reader = new TestMetricReader(); const meterProvider = new MeterProvider({ views: [ - new View({ - aggregation: new ExplicitBucketHistogramAggregation([1, 10, 100]), + { + aggregation: { + type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM, + options: { boundaries: [1, 10, 100] }, + }, instrumentName: '*', - }), + }, ], readers: [reader], }); @@ -419,10 +427,13 @@ describe('PrometheusSerializer', () => { const reader = new TestMetricReader(); const meterProvider = new MeterProvider({ views: [ - new View({ - aggregation: new ExplicitBucketHistogramAggregation([1, 10, 100]), + { + aggregation: { + type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM, + options: { boundaries: [1, 10, 100] }, + }, instrumentName: '*', - }), + }, ], readers: [reader], }); @@ -472,7 +483,10 @@ describe('PrometheusSerializer', () => { const reader = new TestMetricReader(); const meterProvider = new MeterProvider({ views: [ - new View({ aggregation: new SumAggregation(), instrumentName: '*' }), + { + aggregation: { type: AggregationType.SUM }, + instrumentName: '*', + }, ], readers: [reader], }); @@ -561,7 +575,10 @@ describe('PrometheusSerializer', () => { const reader = new TestMetricReader(); const meterProvider = new MeterProvider({ views: [ - new View({ aggregation: new SumAggregation(), instrumentName: '*' }), + { + aggregation: { type: AggregationType.SUM }, + instrumentName: '*', + }, ], readers: [reader], }); diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index 24a7d2332d0..f13de7514f4 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -38,7 +38,11 @@ import { ResourceDetectionConfig, } from '@opentelemetry/resources'; import { LogRecordProcessor, LoggerProvider } from '@opentelemetry/sdk-logs'; -import { MeterProvider, MetricReader, View } from '@opentelemetry/sdk-metrics'; +import { + MeterProvider, + MetricReader, + ViewOptions, +} from '@opentelemetry/sdk-metrics'; import { BatchSpanProcessor, SpanProcessor, @@ -61,9 +65,9 @@ export type MeterProviderConfig = { */ reader?: MetricReader; /** - * List of {@link View}s that should be passed to the MeterProvider + * List of {@link ViewOptions}s that should be passed to the MeterProvider */ - views?: View[]; + views?: ViewOptions[]; }; export type LoggerProviderConfig = { diff --git a/experimental/packages/opentelemetry-sdk-node/src/types.ts b/experimental/packages/opentelemetry-sdk-node/src/types.ts index c3b2a1cd84c..e420318418d 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/types.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/types.ts @@ -19,7 +19,7 @@ import { TextMapPropagator } from '@opentelemetry/api'; import { Instrumentation } from '@opentelemetry/instrumentation'; import { Detector, DetectorSync, IResource } from '@opentelemetry/resources'; import { LogRecordProcessor } from '@opentelemetry/sdk-logs'; -import { MetricReader, View } from '@opentelemetry/sdk-metrics'; +import { MetricReader, ViewOptions } from '@opentelemetry/sdk-metrics'; import { Sampler, SpanExporter, @@ -34,7 +34,7 @@ export interface NodeSDKConfiguration { textMapPropagator: TextMapPropagator; logRecordProcessor: LogRecordProcessor; metricReader: MetricReader; - views: View[]; + views: ViewOptions[]; instrumentations: (Instrumentation | Instrumentation[])[]; resource: IResource; resourceDetectors: Array; diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index 280564ff79f..3011ef01314 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -36,7 +36,6 @@ import { InstrumentType, MeterProvider, PeriodicExportingMetricReader, - View, } from '@opentelemetry/sdk-metrics'; import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { @@ -376,11 +375,11 @@ describe('Node SDK', () => { const sdk = new NodeSDK({ metricReader: metricReader, views: [ - new View({ + { name: 'test-view', instrumentName: 'test_counter', instrumentType: InstrumentType.COUNTER, - }), + }, ], autoDetectResources: false, }); diff --git a/packages/sdk-metrics/src/MeterProvider.ts b/packages/sdk-metrics/src/MeterProvider.ts index f4668d27cd5..3471f414d5d 100644 --- a/packages/sdk-metrics/src/MeterProvider.ts +++ b/packages/sdk-metrics/src/MeterProvider.ts @@ -26,7 +26,7 @@ import { MetricReader } from './export/MetricReader'; import { MeterProviderSharedState } from './state/MeterProviderSharedState'; import { MetricCollector } from './state/MetricCollector'; import { ForceFlushOptions, ShutdownOptions } from './types'; -import { View } from './view/View'; +import { View, ViewOptions } from './view/View'; /** * MeterProviderOptions provides an interface for configuring a MeterProvider. @@ -34,7 +34,7 @@ import { View } from './view/View'; export interface MeterProviderOptions { /** Resource associated with metric telemetry */ resource?: IResource; - views?: View[]; + views?: ViewOptions[]; readers?: MetricReader[]; } @@ -51,8 +51,8 @@ export class MeterProvider implements IMeterProvider { ); this._sharedState = new MeterProviderSharedState(resource); if (options?.views != null && options.views.length > 0) { - for (const view of options.views) { - this._sharedState.viewRegistry.addView(view); + for (const viewOption of options.views) { + this._sharedState.viewRegistry.addView(new View(viewOption)); } } diff --git a/packages/sdk-metrics/src/export/AggregationSelector.ts b/packages/sdk-metrics/src/export/AggregationSelector.ts index 7a4eaca9357..96d10daca4a 100644 --- a/packages/sdk-metrics/src/export/AggregationSelector.ts +++ b/packages/sdk-metrics/src/export/AggregationSelector.ts @@ -15,15 +15,15 @@ */ import { InstrumentType } from '../InstrumentDescriptor'; -import { Aggregation } from '../view/Aggregation'; import { AggregationTemporality } from './AggregationTemporality'; +import { AggregationOption, AggregationType } from '../view/AggregationOption'; /** * Aggregation selector based on metric instrument types. */ export type AggregationSelector = ( instrumentType: InstrumentType -) => Aggregation; +) => AggregationOption; /** * Aggregation temporality selector based on metric instrument types. @@ -33,6 +33,11 @@ export type AggregationTemporalitySelector = ( ) => AggregationTemporality; export const DEFAULT_AGGREGATION_SELECTOR: AggregationSelector = - _instrumentType => Aggregation.Default(); + _instrumentType => { + return { + type: AggregationType.DEFAULT, + }; + }; + export const DEFAULT_AGGREGATION_TEMPORALITY_SELECTOR: AggregationTemporalitySelector = _instrumentType => AggregationTemporality.CUMULATIVE; diff --git a/packages/sdk-metrics/src/export/MetricExporter.ts b/packages/sdk-metrics/src/export/MetricExporter.ts index bf9362bcdd1..38ca30ce67b 100644 --- a/packages/sdk-metrics/src/export/MetricExporter.ts +++ b/packages/sdk-metrics/src/export/MetricExporter.ts @@ -18,7 +18,7 @@ import { AggregationTemporality } from './AggregationTemporality'; import { ResourceMetrics } from './MetricData'; import { ExportResult } from '@opentelemetry/core'; import { InstrumentType } from '../InstrumentDescriptor'; -import { Aggregation } from '../view/Aggregation'; +import { AggregationOption } from '../view/AggregationOption'; /** * An interface that allows different metric services to export recorded data @@ -55,7 +55,7 @@ export interface PushMetricExporter { * Select the {@link Aggregation} for the given * {@link InstrumentType} for this exporter. */ - selectAggregation?(instrumentType: InstrumentType): Aggregation; + selectAggregation?(instrumentType: InstrumentType): AggregationOption; /** * Returns a promise which resolves when the last exportation is completed. diff --git a/packages/sdk-metrics/src/export/MetricReader.ts b/packages/sdk-metrics/src/export/MetricReader.ts index 8aad601d70f..9ebed6e2dca 100644 --- a/packages/sdk-metrics/src/export/MetricReader.ts +++ b/packages/sdk-metrics/src/export/MetricReader.ts @@ -25,13 +25,13 @@ import { ForceFlushOptions, ShutdownOptions, } from '../types'; -import { Aggregation } from '../view/Aggregation'; import { AggregationSelector, AggregationTemporalitySelector, DEFAULT_AGGREGATION_SELECTOR, DEFAULT_AGGREGATION_TEMPORALITY_SELECTOR, } from './AggregationSelector'; +import { AggregationOption } from '../view/AggregationOption'; export interface MetricReaderOptions { /** @@ -99,10 +99,10 @@ export abstract class MetricReader { } /** - * Select the {@link Aggregation} for the given {@link InstrumentType} for this + * Select the {@link AggregationOption} for the given {@link InstrumentType} for this * reader. */ - selectAggregation(instrumentType: InstrumentType): Aggregation { + selectAggregation(instrumentType: InstrumentType): AggregationOption { return this._aggregationSelector(instrumentType); } diff --git a/packages/sdk-metrics/src/index.ts b/packages/sdk-metrics/src/index.ts index e2f9ad2d0e4..4f359214bc8 100644 --- a/packages/sdk-metrics/src/index.ts +++ b/packages/sdk-metrics/src/index.ts @@ -67,18 +67,9 @@ export type InstrumentDescriptor = MetricDescriptor; export { MeterProvider, MeterProviderOptions } from './MeterProvider'; -export { - DefaultAggregation, - ExplicitBucketHistogramAggregation, - ExponentialHistogramAggregation, - DropAggregation, - HistogramAggregation, - LastValueAggregation, - SumAggregation, - Aggregation, -} from './view/Aggregation'; - -export { View, ViewOptions } from './view/View'; +export { AggregationOption, AggregationType } from './view/AggregationOption'; + +export { ViewOptions } from './view/View'; export { IAttributesProcessor, diff --git a/packages/sdk-metrics/src/state/MeterProviderSharedState.ts b/packages/sdk-metrics/src/state/MeterProviderSharedState.ts index fa7903b20e2..2e8bc57796d 100644 --- a/packages/sdk-metrics/src/state/MeterProviderSharedState.ts +++ b/packages/sdk-metrics/src/state/MeterProviderSharedState.ts @@ -16,11 +16,13 @@ import { InstrumentationScope } from '@opentelemetry/core'; import { IResource } from '@opentelemetry/resources'; -import { Aggregation, InstrumentType } from '..'; +import { InstrumentType } from '..'; import { instrumentationScopeId } from '../utils'; import { ViewRegistry } from '../view/ViewRegistry'; import { MeterSharedState } from './MeterSharedState'; import { MetricCollector, MetricCollectorHandle } from './MetricCollector'; +import { toAggregation } from '../view/AggregationOption'; +import { Aggregation } from '../view/Aggregation'; /** * An internal record for shared meter provider states. @@ -47,7 +49,10 @@ export class MeterProviderSharedState { selectAggregations(instrumentType: InstrumentType) { const result: [MetricCollectorHandle, Aggregation][] = []; for (const collector of this.metricCollectors) { - result.push([collector, collector.selectAggregation(instrumentType)]); + result.push([ + collector, + toAggregation(collector.selectAggregation(instrumentType)), + ]); } return result; } diff --git a/packages/sdk-metrics/src/view/Aggregation.ts b/packages/sdk-metrics/src/view/Aggregation.ts index f34bd1231a3..f3894489b17 100644 --- a/packages/sdk-metrics/src/view/Aggregation.ts +++ b/packages/sdk-metrics/src/view/Aggregation.ts @@ -32,40 +32,16 @@ import { Maybe } from '../utils'; * * Aggregation provides a set of built-in aggregations via static methods. */ -export abstract class Aggregation { - abstract createAggregator( +export interface Aggregation { + createAggregator( instrument: InstrumentDescriptor ): Aggregator>; - - static Drop(): Aggregation { - return DROP_AGGREGATION; - } - - static Sum(): Aggregation { - return SUM_AGGREGATION; - } - - static LastValue(): Aggregation { - return LAST_VALUE_AGGREGATION; - } - - static Histogram(): Aggregation { - return HISTOGRAM_AGGREGATION; - } - - static ExponentialHistogram(): Aggregation { - return EXPONENTIAL_HISTOGRAM_AGGREGATION; - } - - static Default(): Aggregation { - return DEFAULT_AGGREGATION; - } } /** * The default drop aggregation. */ -export class DropAggregation extends Aggregation { +export class DropAggregation implements Aggregation { private static DEFAULT_INSTANCE = new DropAggregator(); createAggregator(_instrument: InstrumentDescriptor) { return DropAggregation.DEFAULT_INSTANCE; @@ -75,7 +51,7 @@ export class DropAggregation extends Aggregation { /** * The default sum aggregation. */ -export class SumAggregation extends Aggregation { +export class SumAggregation implements Aggregation { private static MONOTONIC_INSTANCE = new SumAggregator(true); private static NON_MONOTONIC_INSTANCE = new SumAggregator(false); createAggregator(instrument: InstrumentDescriptor) { @@ -95,7 +71,7 @@ export class SumAggregation extends Aggregation { /** * The default last value aggregation. */ -export class LastValueAggregation extends Aggregation { +export class LastValueAggregation implements Aggregation { private static DEFAULT_INSTANCE = new LastValueAggregator(); createAggregator(_instrument: InstrumentDescriptor) { return LastValueAggregation.DEFAULT_INSTANCE; @@ -104,8 +80,9 @@ export class LastValueAggregation extends Aggregation { /** * The default histogram aggregation. + */ -export class HistogramAggregation extends Aggregation { +export class HistogramAggregation implements Aggregation { private static DEFAULT_INSTANCE = new HistogramAggregator( [0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000], true @@ -118,7 +95,7 @@ export class HistogramAggregation extends Aggregation { /** * The explicit bucket histogram aggregation. */ -export class ExplicitBucketHistogramAggregation extends Aggregation { +export class ExplicitBucketHistogramAggregation implements Aggregation { private _boundaries: number[]; /** @@ -129,7 +106,6 @@ export class ExplicitBucketHistogramAggregation extends Aggregation { boundaries: number[], private readonly _recordMinMax = true ) { - super(); if (boundaries == null) { throw new Error( 'ExplicitBucketHistogramAggregation should be created with explicit boundaries, if a single bucket histogram is required, please pass an empty array' @@ -154,13 +130,11 @@ export class ExplicitBucketHistogramAggregation extends Aggregation { } } -export class ExponentialHistogramAggregation extends Aggregation { +export class ExponentialHistogramAggregation implements Aggregation { constructor( private readonly _maxSize: number = 160, private readonly _recordMinMax = true - ) { - super(); - } + ) {} createAggregator(_instrument: InstrumentDescriptor) { return new ExponentialHistogramAggregator( this._maxSize, @@ -172,7 +146,7 @@ export class ExponentialHistogramAggregation extends Aggregation { /** * The default aggregation. */ -export class DefaultAggregation extends Aggregation { +export class DefaultAggregation implements Aggregation { private _resolve(instrument: InstrumentDescriptor): Aggregation { // cast to unknown to disable complaints on the (unreachable) fallback. switch (instrument.type as unknown) { @@ -206,9 +180,10 @@ export class DefaultAggregation extends Aggregation { } } -const DROP_AGGREGATION = new DropAggregation(); -const SUM_AGGREGATION = new SumAggregation(); -const LAST_VALUE_AGGREGATION = new LastValueAggregation(); -const HISTOGRAM_AGGREGATION = new HistogramAggregation(); -const EXPONENTIAL_HISTOGRAM_AGGREGATION = new ExponentialHistogramAggregation(); -const DEFAULT_AGGREGATION = new DefaultAggregation(); +export const DROP_AGGREGATION = new DropAggregation(); +export const SUM_AGGREGATION = new SumAggregation(); +export const LAST_VALUE_AGGREGATION = new LastValueAggregation(); +export const HISTOGRAM_AGGREGATION = new HistogramAggregation(); +export const EXPONENTIAL_HISTOGRAM_AGGREGATION = + new ExponentialHistogramAggregation(); +export const DEFAULT_AGGREGATION = new DefaultAggregation(); diff --git a/packages/sdk-metrics/src/view/AggregationOption.ts b/packages/sdk-metrics/src/view/AggregationOption.ts new file mode 100644 index 00000000000..255f523bcfa --- /dev/null +++ b/packages/sdk-metrics/src/view/AggregationOption.ts @@ -0,0 +1,107 @@ +/* + * Copyright The 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 { + Aggregation, + DEFAULT_AGGREGATION, + DROP_AGGREGATION, + ExplicitBucketHistogramAggregation, + ExponentialHistogramAggregation, + HISTOGRAM_AGGREGATION, + LAST_VALUE_AGGREGATION, + SUM_AGGREGATION, +} from './Aggregation'; + +export enum AggregationType { + DEFAULT = 0, + DROP = 1, + SUM = 2, + LAST_VALUE = 3, + EXPLICIT_BUCKET_HISTOGRAM = 4, + EXPONENTIAL_HISTOGRAM = 5, +} + +export type SumAggregationOption = { + type: AggregationType.SUM; +}; + +export type LastValueAggregationOption = { + type: AggregationType.LAST_VALUE; +}; + +export type DropAggregationOption = { + type: AggregationType.DROP; +}; + +export type DefaultAggregationOption = { + type: AggregationType.DEFAULT; +}; + +export type HistogramAggregationOption = { + type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM; + options?: { + recordMinMax?: boolean; + boundaries: number[]; + }; +}; + +export type ExponentialHistogramAggregationOption = { + type: AggregationType.EXPONENTIAL_HISTOGRAM; + options?: { + recordMinMax?: boolean; + maxSize?: number; + }; +}; + +export type AggregationOption = + | ExponentialHistogramAggregationOption + | HistogramAggregationOption + | SumAggregationOption + | DropAggregationOption + | DefaultAggregationOption + | LastValueAggregationOption; + +export function toAggregation(option: AggregationOption): Aggregation { + switch (option.type) { + case AggregationType.DEFAULT: + return DEFAULT_AGGREGATION; + case AggregationType.DROP: + return DROP_AGGREGATION; + case AggregationType.SUM: + return SUM_AGGREGATION; + case AggregationType.LAST_VALUE: + return LAST_VALUE_AGGREGATION; + case AggregationType.EXPONENTIAL_HISTOGRAM: { + const expOption = option as ExponentialHistogramAggregationOption; + return new ExponentialHistogramAggregation( + expOption.options?.maxSize, + expOption.options?.recordMinMax + ); + } + case AggregationType.EXPLICIT_BUCKET_HISTOGRAM: { + const expOption = option as HistogramAggregationOption; + if (expOption.options == null) { + return HISTOGRAM_AGGREGATION; + } else { + return new ExplicitBucketHistogramAggregation( + expOption.options?.boundaries, + expOption.options?.recordMinMax + ); + } + } + default: + throw new Error('Unsupported Aggregation'); + } +} diff --git a/packages/sdk-metrics/src/view/View.ts b/packages/sdk-metrics/src/view/View.ts index 4d46c868ba3..00227e918ba 100644 --- a/packages/sdk-metrics/src/view/View.ts +++ b/packages/sdk-metrics/src/view/View.ts @@ -24,6 +24,11 @@ import { InstrumentSelector } from './InstrumentSelector'; import { MeterSelector } from './MeterSelector'; import { Aggregation } from './Aggregation'; import { InstrumentType } from '../InstrumentDescriptor'; +import { + AggregationOption, + AggregationType, + toAggregation, +} from './AggregationOption'; export type ViewOptions = { /** @@ -57,14 +62,14 @@ export type ViewOptions = { attributesProcessors?: IAttributesProcessor[]; /** * Alters the metric stream: - * Alters the {@link Aggregation} of the metric stream. + * Alters the Aggregation of the metric stream. * * @example changes the aggregation of the selected instrument(s) to ExplicitBucketHistogramAggregation - * aggregation: new ExplicitBucketHistogramAggregation([1, 10, 100]) + * aggregation: { type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM, options: { boundaries: [1, 10, 100] } } * @example changes the aggregation of the selected instrument(s) to LastValueAggregation - * aggregation: new LastValueAggregation() + * aggregation: { type: AggregationType.LAST_VALUE, options: { boundaries: [1, 10, 100] } } */ - aggregation?: Aggregation; + aggregation?: AggregationOption; /** * Instrument selection criteria: * The original type of the Instrument(s). @@ -132,6 +137,26 @@ function isSelectorNotProvided(options: ViewOptions): boolean { ); } +function validateViewOptions(viewOptions: ViewOptions) { + // If no criteria is provided, the SDK SHOULD treat it as an error. + // It is recommended that the SDK implementations fail fast. + if (isSelectorNotProvided(viewOptions)) { + throw new Error('Cannot create view with no selector arguments supplied'); + } + + // the SDK SHOULD NOT allow Views with a specified name to be declared with instrument selectors that + // may select more than one instrument (e.g. wild card instrument name) in the same Meter. + if ( + viewOptions.name != null && + (viewOptions?.instrumentName == null || + PatternPredicate.hasWildcard(viewOptions.instrumentName)) + ) { + throw new Error( + 'Views with a specified name must be declared with an instrument selector that selects at most one instrument per meter.' + ); + } +} + /** * Can be passed to a {@link MeterProvider} to select instruments and alter their metric stream. */ @@ -196,23 +221,7 @@ export class View { * }) */ constructor(viewOptions: ViewOptions) { - // If no criteria is provided, the SDK SHOULD treat it as an error. - // It is recommended that the SDK implementations fail fast. - if (isSelectorNotProvided(viewOptions)) { - throw new Error('Cannot create view with no selector arguments supplied'); - } - - // the SDK SHOULD NOT allow Views with a specified name to be declared with instrument selectors that - // may select more than one instrument (e.g. wild card instrument name) in the same Meter. - if ( - viewOptions.name != null && - (viewOptions?.instrumentName == null || - PatternPredicate.hasWildcard(viewOptions.instrumentName)) - ) { - throw new Error( - 'Views with a specified name must be declared with an instrument selector that selects at most one instrument per meter.' - ); - } + validateViewOptions(viewOptions); // Create multi-processor if attributesProcessors are defined. if (viewOptions.attributesProcessors != null) { @@ -225,7 +234,9 @@ export class View { this.name = viewOptions.name; this.description = viewOptions.description; - this.aggregation = viewOptions.aggregation ?? Aggregation.Default(); + this.aggregation = toAggregation( + viewOptions.aggregation ?? { type: AggregationType.DEFAULT } + ); this.instrumentSelector = new InstrumentSelector({ name: viewOptions.instrumentName, type: viewOptions.instrumentType, diff --git a/packages/sdk-metrics/test/MeterProvider.test.ts b/packages/sdk-metrics/test/MeterProvider.test.ts index 364cd8779ff..c9a7eafd102 100644 --- a/packages/sdk-metrics/test/MeterProvider.test.ts +++ b/packages/sdk-metrics/test/MeterProvider.test.ts @@ -19,7 +19,6 @@ import { MeterProvider, InstrumentType, DataPointType, - ExplicitBucketHistogramAggregation, HistogramMetricData, } from '../src'; import { @@ -30,9 +29,9 @@ import { } from './util'; import { TestMetricReader } from './export/TestMetricReader'; import * as sinon from 'sinon'; -import { View } from '../src/view/View'; import { Meter } from '../src/Meter'; import { createAllowListAttributesProcessor } from '../src/view/AttributesProcessor'; +import { AggregationType } from '../src/view/AggregationOption'; describe('MeterProvider', () => { afterEach(() => { @@ -139,11 +138,11 @@ describe('MeterProvider', () => { resource: defaultResource, // Add view to rename 'non-renamed-instrument' to 'renamed-instrument' views: [ - new View({ + { name: 'renamed-instrument', description: 'my renamed instrument', instrumentName: 'non-renamed-instrument', - }), + }, ], readers: [reader], }); @@ -208,12 +207,12 @@ describe('MeterProvider', () => { const meterProvider = new MeterProvider({ resource: defaultResource, views: [ - new View({ + { attributesProcessors: [ createAllowListAttributesProcessor(['attrib1']), ], instrumentName: 'non-renamed-instrument', - }), + }, ], readers: [reader], }); @@ -276,10 +275,10 @@ describe('MeterProvider', () => { const meterProvider = new MeterProvider({ resource: defaultResource, views: [ - new View({ + { name: 'renamed-instrument', instrumentName: 'test-counter', - }), + }, ], readers: [reader], }); @@ -348,11 +347,11 @@ describe('MeterProvider', () => { resource: defaultResource, views: [ // Add view that renames 'test-counter' to 'renamed-instrument' on 'meter1' - new View({ + { name: 'renamed-instrument', instrumentName: 'test-counter', meterName: 'meter1', - }), + }, ], readers: [reader], }); @@ -421,16 +420,16 @@ describe('MeterProvider', () => { resource: defaultResource, // Add Views to rename both instruments (of different types) to the same name. views: [ - new View({ + { name: 'renamed-instrument', instrumentName: 'test-counter', meterName: 'meter1', - }), - new View({ + }, + { name: 'renamed-instrument', instrumentName: 'test-histogram', meterName: 'meter1', - }), + }, ], readers: [reader], }); @@ -489,14 +488,21 @@ describe('MeterProvider', () => { const meterProvider = new MeterProvider({ resource: defaultResource, views: [ - new View({ + { instrumentUnit: 'ms', - aggregation: new ExplicitBucketHistogramAggregation(msBoundaries), - }), - new View({ + // aggregation: new ExplicitBucketHistogramAggregation(msBoundaries), + aggregation: { + type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM, + options: { boundaries: msBoundaries }, + }, + }, + { instrumentUnit: 's', - aggregation: new ExplicitBucketHistogramAggregation(sBoundaries), - }), + aggregation: { + type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM, + options: { boundaries: sBoundaries }, + }, + }, ], readers: [reader], }); diff --git a/packages/sdk-metrics/test/export/MetricReader.test.ts b/packages/sdk-metrics/test/export/MetricReader.test.ts index b08cde5e38d..cc091c97977 100644 --- a/packages/sdk-metrics/test/export/MetricReader.test.ts +++ b/packages/sdk-metrics/test/export/MetricReader.test.ts @@ -21,8 +21,8 @@ import { assertRejects } from '../test-utils'; import { emptyResourceMetrics, TestMetricProducer } from './TestMetricProducer'; import { TestMetricReader } from './TestMetricReader'; import { - Aggregation, AggregationTemporality, + AggregationType, DataPointType, InstrumentType, ScopeMetrics, @@ -228,9 +228,17 @@ describe('MetricReader', () => { it('should override default when provided with a selector', () => { const reader = new TestMetricReader({ - aggregationSelector: _instrumentType => Aggregation.Sum(), + aggregationSelector: _instrumentType => { + return { + type: AggregationType.SUM, + }; + }, + }); + assertAggregationSelector(reader, _instrumentType => { + return { + type: AggregationType.SUM, + }; }); - assertAggregationSelector(reader, _instrumentType => Aggregation.Sum()); reader.shutdown(); }); }); diff --git a/packages/sdk-metrics/test/export/PeriodicExportingMetricReader.test.ts b/packages/sdk-metrics/test/export/PeriodicExportingMetricReader.test.ts index d5ab5531267..fcfc86511f2 100644 --- a/packages/sdk-metrics/test/export/PeriodicExportingMetricReader.test.ts +++ b/packages/sdk-metrics/test/export/PeriodicExportingMetricReader.test.ts @@ -16,7 +16,12 @@ import { PeriodicExportingMetricReader } from '../../src/export/PeriodicExportingMetricReader'; import { AggregationTemporality } from '../../src/export/AggregationTemporality'; -import { Aggregation, InstrumentType, PushMetricExporter } from '../../src'; +import { + AggregationOption, + AggregationType, + InstrumentType, + PushMetricExporter, +} from '../../src'; import { ResourceMetrics } from '../../src/export/MetricData'; import * as assert from 'assert'; import * as sinon from 'sinon'; @@ -107,8 +112,8 @@ class TestDeltaMetricExporter extends TestMetricExporter { } class TestDropMetricExporter extends TestMetricExporter { - selectAggregation(_instrumentType: InstrumentType): Aggregation { - return Aggregation.Drop(); + selectAggregation(_instrumentType: InstrumentType): AggregationOption { + return { type: AggregationType.DROP }; } } diff --git a/packages/sdk-metrics/test/export/utils.ts b/packages/sdk-metrics/test/export/utils.ts index a10021ee905..77b2791b203 100644 --- a/packages/sdk-metrics/test/export/utils.ts +++ b/packages/sdk-metrics/test/export/utils.ts @@ -43,7 +43,7 @@ export function assertAggregationSelector( expectedSelector: AggregationSelector ) { for (const instrumentType of instrumentTypes) { - assert.strictEqual( + assert.deepStrictEqual( reader.selectAggregation?.(instrumentType), expectedSelector(instrumentType), `incorrect aggregation selection for ${InstrumentType[instrumentType]}` diff --git a/packages/sdk-metrics/test/regression/cumulative-exponential-histogram.test.ts b/packages/sdk-metrics/test/regression/cumulative-exponential-histogram.test.ts index 2462ddf5c81..3787308d9ec 100644 --- a/packages/sdk-metrics/test/regression/cumulative-exponential-histogram.test.ts +++ b/packages/sdk-metrics/test/regression/cumulative-exponential-histogram.test.ts @@ -17,8 +17,9 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; import { - Aggregation, + AggregationOption, AggregationTemporality, + AggregationType, InstrumentType, MeterProvider, MetricReader, @@ -37,15 +38,15 @@ describe('cumulative-exponential-histogram', () => { it('Cumulative Histogram should have the same startTime every collection', async () => { // Works fine and passes - await doTest(Aggregation.Histogram()); + await doTest({ type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM }); }); it('Cumulative ExponentialHistogram should have the same startTime every collection', async () => { // Fails - await doTest(Aggregation.ExponentialHistogram()); + await doTest({ type: AggregationType.EXPONENTIAL_HISTOGRAM }); }); - const doTest = async (histogramAggregation: Aggregation) => { + const doTest = async (histogramAggregation: AggregationOption) => { const reader = new TestMetricReader({ aggregationTemporalitySelector() { return AggregationTemporality.CUMULATIVE; @@ -53,7 +54,7 @@ describe('cumulative-exponential-histogram', () => { aggregationSelector(type) { return type === InstrumentType.HISTOGRAM ? histogramAggregation - : Aggregation.Default(); + : { type: AggregationType.DEFAULT }; }, }); const meterProvider = new MeterProvider({ diff --git a/packages/sdk-metrics/test/regression/histogram-recording-nan.test.ts b/packages/sdk-metrics/test/regression/histogram-recording-nan.test.ts index a37c9c8e92e..4cb73ff4984 100644 --- a/packages/sdk-metrics/test/regression/histogram-recording-nan.test.ts +++ b/packages/sdk-metrics/test/regression/histogram-recording-nan.test.ts @@ -16,13 +16,13 @@ import * as assert from 'assert'; import { - Aggregation, AggregationTemporality, MeterProvider, MetricReader, DataPoint, ExponentialHistogram, Histogram, + AggregationType, } from '../../src'; import { TestMetricReader } from '../export/TestMetricReader'; @@ -33,7 +33,7 @@ describe('histogram-recording-nan', () => { return AggregationTemporality.CUMULATIVE; }, aggregationSelector(type) { - return Aggregation.ExponentialHistogram(); + return { type: AggregationType.EXPONENTIAL_HISTOGRAM }; }, }); const meterProvider = new MeterProvider({ @@ -65,7 +65,7 @@ describe('histogram-recording-nan', () => { return AggregationTemporality.CUMULATIVE; }, aggregationSelector(type) { - return Aggregation.Histogram(); + return { type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM }; }, }); const meterProvider = new MeterProvider({ diff --git a/packages/sdk-metrics/test/state/MeterSharedState.test.ts b/packages/sdk-metrics/test/state/MeterSharedState.test.ts index 9e814a4b439..74b4ff9a273 100644 --- a/packages/sdk-metrics/test/state/MeterSharedState.test.ts +++ b/packages/sdk-metrics/test/state/MeterSharedState.test.ts @@ -19,10 +19,10 @@ import * as sinon from 'sinon'; import { MeterProvider, DataPointType, - View, - Aggregation, MetricReader, InstrumentType, + AggregationType, + ViewOptions, } from '../../src'; import { assertMetricData, @@ -44,7 +44,7 @@ describe('MeterSharedState', () => { }); describe('registerMetricStorage', () => { - function setupMeter(views?: View[], readers?: MetricReader[]) { + function setupMeter(views?: ViewOptions[], readers?: MetricReader[]) { const meterProvider = new MeterProvider({ resource: defaultResource, views, @@ -69,7 +69,7 @@ describe('MeterSharedState', () => { }, }); const { meter, meterSharedState, collectors } = setupMeter( - [new View({ instrumentName: 'test-counter' })], + [{ instrumentName: 'test-counter' }], [reader] ); @@ -92,7 +92,7 @@ describe('MeterSharedState', () => { }, }); const { meter, meterSharedState, collectors } = setupMeter( - [new View({ instrumentName: 'test-counter' })], + [{ instrumentName: 'test-counter' }], [reader] ); @@ -111,7 +111,7 @@ describe('MeterSharedState', () => { it('should register metric storages with the collector', () => { const reader = new TestMetricReader({ aggregationSelector: (instrumentType: InstrumentType) => { - return Aggregation.Drop(); + return { type: AggregationType.DROP }; }, }); const readerAggregationSelectorSpy = sinon.spy( @@ -141,12 +141,12 @@ describe('MeterSharedState', () => { it('should register metric storages with collectors', () => { const reader = new TestMetricReader({ aggregationSelector: (instrumentType: InstrumentType) => { - return Aggregation.Drop(); + return { type: AggregationType.DROP }; }, }); const reader2 = new TestMetricReader({ aggregationSelector: (instrumentType: InstrumentType) => { - return Aggregation.LastValue(); + return { type: AggregationType.LAST_VALUE }; }, }); @@ -184,7 +184,7 @@ describe('MeterSharedState', () => { }); describe('collect', () => { - function setupInstruments(views?: View[]) { + function setupInstruments(views?: ViewOptions[]) { const cumulativeReader = new TestMetricReader(); const deltaReader = new TestDeltaMetricReader(); @@ -245,8 +245,8 @@ describe('MeterSharedState', () => { it('should collect sync metrics with views', async () => { /** preparing test instrumentations */ const { metricCollectors, meter } = setupInstruments([ - new View({ name: 'foo', instrumentName: 'test' }), - new View({ name: 'bar', instrumentName: 'test' }), + { name: 'foo', instrumentName: 'test' }, + { name: 'bar', instrumentName: 'test' }, ]); /** creating metric events */ @@ -314,8 +314,8 @@ describe('MeterSharedState', () => { it('should call observable callback once with view-ed async instruments', async () => { /** preparing test instrumentations */ const { metricCollectors, meter } = setupInstruments([ - new View({ name: 'foo', instrumentName: 'test' }), - new View({ name: 'bar', instrumentName: 'test' }), + { name: 'foo', instrumentName: 'test' }, + { name: 'bar', instrumentName: 'test' }, ]); /** creating metric events */ diff --git a/packages/sdk-metrics/test/view/Aggregation.test.ts b/packages/sdk-metrics/test/view/Aggregation.test.ts index 51595aa5907..a2b25b77f81 100644 --- a/packages/sdk-metrics/test/view/Aggregation.test.ts +++ b/packages/sdk-metrics/test/view/Aggregation.test.ts @@ -27,43 +27,16 @@ import { InstrumentType, } from '../../src/InstrumentDescriptor'; import { - Aggregation, DefaultAggregation, - DropAggregation, ExplicitBucketHistogramAggregation, HistogramAggregation, - LastValueAggregation, - SumAggregation, } from '../../src/view/Aggregation'; import { defaultInstrumentDescriptor } from '../util'; -interface AggregationConstructor { - new (...args: any[]): Aggregation; -} - interface AggregatorConstructor { new (...args: any[]): Aggregator; } -describe('Aggregation', () => { - it('static aggregations', () => { - const staticMembers: [keyof typeof Aggregation, AggregationConstructor][] = - [ - ['Drop', DropAggregation], - ['Sum', SumAggregation], - ['LastValue', LastValueAggregation], - ['Histogram', HistogramAggregation], - ['Default', DefaultAggregation], - ]; - - for (const [key, type] of staticMembers) { - const aggregation = (Aggregation[key] as () => Aggregation)(); - assert(aggregation instanceof type); - assert(aggregation.createAggregator(defaultInstrumentDescriptor)); - } - }); -}); - describe('DefaultAggregation', () => { describe('createAggregator', () => { it('should create aggregators for instrument descriptors', () => { diff --git a/packages/sdk-metrics/test/view/View.test.ts b/packages/sdk-metrics/test/view/View.test.ts index 7328372390a..f17a6ab3fcd 100644 --- a/packages/sdk-metrics/test/view/View.test.ts +++ b/packages/sdk-metrics/test/view/View.test.ts @@ -19,12 +19,9 @@ import { createAllowListAttributesProcessor, createNoopAttributesProcessor, } from '../../src/view/AttributesProcessor'; +import { InstrumentType, AggregationType } from '../../src'; +import { DEFAULT_AGGREGATION } from '../../src/view/Aggregation'; import { View } from '../../src/view/View'; -import { - InstrumentType, - Aggregation, - ExplicitBucketHistogramAggregation, -} from '../../src'; describe('View', () => { describe('constructor', () => { @@ -33,7 +30,7 @@ describe('View', () => { const view = new View({ instrumentName: '*' }); assert.strictEqual(view.name, undefined); assert.strictEqual(view.description, undefined); - assert.strictEqual(view.aggregation, Aggregation.Default()); + assert.strictEqual(view.aggregation, DEFAULT_AGGREGATION); assert.strictEqual( view.attributesProcessor, createNoopAttributesProcessor() @@ -43,7 +40,7 @@ describe('View', () => { const view = new View({ meterName: '*' }); assert.strictEqual(view.name, undefined); assert.strictEqual(view.description, undefined); - assert.strictEqual(view.aggregation, Aggregation.Default()); + assert.strictEqual(view.aggregation, DEFAULT_AGGREGATION); assert.strictEqual( view.attributesProcessor, createNoopAttributesProcessor() @@ -69,7 +66,10 @@ describe('View', () => { assert.throws( () => new View({ - aggregation: new ExplicitBucketHistogramAggregation([1, 100]), + aggregation: { + type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM, + options: { boundaries: [1, 100] }, + }, }) ); }); diff --git a/packages/sdk-metrics/test/view/ViewRegistry.test.ts b/packages/sdk-metrics/test/view/ViewRegistry.test.ts index b766c0fda51..bb1dadfbf01 100644 --- a/packages/sdk-metrics/test/view/ViewRegistry.test.ts +++ b/packages/sdk-metrics/test/view/ViewRegistry.test.ts @@ -21,7 +21,7 @@ import { defaultInstrumentationScope, defaultInstrumentDescriptor, } from '../util'; -import { View } from '../../src'; +import { View } from '../../src/view/View'; describe('ViewRegistry', () => { describe('findViews', () => { From e45cf128374c36fa7f0ab9f5e7ead871dc7b0f47 Mon Sep 17 00:00:00 2001 From: Marc Pichler Date: Tue, 20 Aug 2024 17:35:33 +0200 Subject: [PATCH 2/6] [draft] feat(sdk-metrics)!: use factory functions over classes --- doc/metrics.md | 2 +- .../examples/metrics/index.js | 2 +- examples/otlp-exporter-node/metrics.js | 2 +- .../README.md | 2 +- .../test/metricsHelper.ts | 6 +- .../README.md | 4 +- .../test/metricsHelper.ts | 6 +- .../README.md | 2 +- .../test/metricsHelper.ts | 6 +- .../test/PrometheusExporter.test.ts | 15 +++-- .../test/PrometheusSerializer.test.ts | 20 +++---- .../test/functionals/http-metrics.test.ts | 12 ++-- .../test/common/Instrumentation.test.ts | 5 +- .../opentelemetry-sdk-node/src/sdk.ts | 6 +- .../opentelemetry-sdk-node/test/sdk.test.ts | 42 +++++++------- .../src/OpenCensusMetricProducer.ts | 4 +- packages/sdk-metrics/src/MeterProvider.ts | 11 +++- .../src/export/ConsoleMetricExporter.ts | 8 ++- .../src/export/InMemoryMetricExporter.ts | 16 +++++- .../export/PeriodicExportingMetricReader.ts | 12 +++- packages/sdk-metrics/src/index.ts | 8 +-- packages/sdk-metrics/test/Instruments.test.ts | 6 +- .../sdk-metrics/test/MeterProvider.test.ts | 32 +++++------ .../test/export/ConsoleMetricExporter.test.ts | 32 ++++++----- .../export/InMemoryMetricExporter.test.ts | 22 ++++--- .../test/export/MetricReader.test.ts | 10 ++-- .../PeriodicExportingMetricReader.test.ts | 57 +++++++++---------- .../cumulative-exponential-histogram.test.ts | 4 +- .../histogram-recording-nan.test.ts | 6 +- ...wo-metric-readers-async-instrument.test.ts | 4 +- .../test/state/MeterSharedState.test.ts | 14 +++-- .../test/state/MetricCollector.test.ts | 4 +- 32 files changed, 221 insertions(+), 161 deletions(-) diff --git a/doc/metrics.md b/doc/metrics.md index 1eb873a11d6..9e1c642b47d 100644 --- a/doc/metrics.md +++ b/doc/metrics.md @@ -543,7 +543,7 @@ const collectorOptions = { const exporter = new OTLPMetricExporter(collectorOptions); const meterProvider = new MeterProvider({}); -meterProvider.addMetricReader(new PeriodicExportingMetricReader({ +meterProvider.addMetricReader(createPeriodicExportingMetricReader({ exporter: metricExporter, exportIntervalMillis: 1000, })); diff --git a/examples/opentelemetry-web/examples/metrics/index.js b/examples/opentelemetry-web/examples/metrics/index.js index c3f86cfbe50..543c1d22030 100644 --- a/examples/opentelemetry-web/examples/metrics/index.js +++ b/examples/opentelemetry-web/examples/metrics/index.js @@ -21,7 +21,7 @@ function startMetrics() { const meterProvider = new MeterProvider(); metrics.setGlobalMeterProvider(meterProvider); - meterProvider.addMetricReader(new PeriodicExportingMetricReader({ + meterProvider.addMetricReader(createPeriodicExportingMetricReader({ exporter: new OTLPMetricExporter(), exportIntervalMillis: 1000 })); diff --git a/examples/otlp-exporter-node/metrics.js b/examples/otlp-exporter-node/metrics.js index 22507ef528b..b5ed0af74f3 100644 --- a/examples/otlp-exporter-node/metrics.js +++ b/examples/otlp-exporter-node/metrics.js @@ -42,7 +42,7 @@ const meterProvider = new MeterProvider({ }); meterProvider.addMetricReader( - new PeriodicExportingMetricReader({ + createPeriodicExportingMetricReader({ exporter: metricExporter, // exporter: new ConsoleMetricExporter(), exportIntervalMillis: 1000, diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/README.md b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/README.md index 5c87ab2527a..1a9de77c2f1 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/README.md +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/README.md @@ -38,7 +38,7 @@ const collectorOptions = { const metricExporter = new OTLPMetricExporter(collectorOptions); const meterProvider = new MeterProvider({}); -meterProvider.addMetricReader(new PeriodicExportingMetricReader({ +meterProvider.addMetricReader(createPeriodicExportingMetricReader({ exporter: metricExporter, exportIntervalMillis: 1000, })); diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/metricsHelper.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/metricsHelper.ts index 352169212a7..1c6127388b2 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/metricsHelper.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/metricsHelper.ts @@ -28,7 +28,7 @@ import * as grpc from '@grpc/grpc-js'; import { VERSION } from '@opentelemetry/core'; import { AggregationType, - MeterProvider, + createMeterProvider, MetricReader, } from '@opentelemetry/sdk-metrics'; import { @@ -55,7 +55,7 @@ const testResource = new Resource({ }); let reader = new TestMetricReader(); -let meterProvider = new MeterProvider({ +let meterProvider = createMeterProvider({ resource: testResource, readers: [reader], }); @@ -68,7 +68,7 @@ export async function collect() { export function setUp() { reader = new TestMetricReader(); - meterProvider = new MeterProvider({ + meterProvider = createMeterProvider({ resource: testResource, views: [ { diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/README.md b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/README.md index 6458aaab672..417f4b74f70 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/README.md +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/README.md @@ -37,7 +37,7 @@ const collectorOptions = { const metricExporter = new OTLPMetricExporter(collectorOptions); const meterProvider = new MeterProvider({}); -meterProvider.addMetricReader(new PeriodicExportingMetricReader({ +meterProvider.addMetricReader(createPeriodicExportingMetricReader({ exporter: metricExporter, exportIntervalMillis: 1000, })); @@ -60,7 +60,7 @@ const collectorOptions = { const metricExporter = new OTLPMetricExporter(collectorOptions); const meterProvider = new MeterProvider({}); -meterProvider.addMetricReader(new PeriodicExportingMetricReader({ +meterProvider.addMetricReader(createPeriodicExportingMetricReader({ exporter: metricExporter, exportIntervalMillis: 1000, })); diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/metricsHelper.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/metricsHelper.ts index 62f749f1afe..b8e02f21a7b 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/metricsHelper.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/metricsHelper.ts @@ -29,7 +29,7 @@ import * as assert from 'assert'; import { InstrumentationScope, VERSION } from '@opentelemetry/core'; import { AggregationType, - MeterProvider, + createMeterProvider, MetricReader, ViewOptions, } from '@opentelemetry/sdk-metrics'; @@ -78,7 +78,7 @@ const defaultResource = Resource.default().merge( ); let reader = new TestMetricReader(); -let meterProvider = new MeterProvider({ +let meterProvider = createMeterProvider({ resource: defaultResource, readers: [reader], }); @@ -90,7 +90,7 @@ export async function collect() { export function setUp(views?: ViewOptions[]) { reader = new TestMetricReader(); - meterProvider = new MeterProvider({ + meterProvider = createMeterProvider({ resource: defaultResource, views, readers: [reader], diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/README.md b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/README.md index e1b3c9941a5..c08fdb93212 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/README.md +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/README.md @@ -30,7 +30,7 @@ const collectorOptions = { const metricExporter = new OTLPMetricExporter(collectorOptions); const meterProvider = new MeterProvider({}); -meterProvider.addMetricReader(new PeriodicExportingMetricReader({ +meterProvider.addMetricReader(createPeriodicExportingMetricReader({ exporter: metricExporter, exportIntervalMillis: 1000, })); diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/metricsHelper.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/metricsHelper.ts index 756d470bc97..e6f03774700 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/metricsHelper.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/metricsHelper.ts @@ -26,7 +26,7 @@ import { Resource } from '@opentelemetry/resources'; import * as assert from 'assert'; import { AggregationType, - MeterProvider, + createMeterProvider, MetricReader, } from '@opentelemetry/sdk-metrics'; import { @@ -54,7 +54,7 @@ const testResource = new Resource({ }); let reader = new TestMetricReader(); -let meterProvider = new MeterProvider({ +let meterProvider = createMeterProvider({ resource: testResource, readers: [reader], }); @@ -67,7 +67,7 @@ export async function collect() { export function setUp() { reader = new TestMetricReader(); - meterProvider = new MeterProvider({ + meterProvider = createMeterProvider({ resource: testResource, views: [ { diff --git a/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusExporter.test.ts b/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusExporter.test.ts index 66bab19ef8f..b2c28c58af9 100644 --- a/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusExporter.test.ts +++ b/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusExporter.test.ts @@ -14,8 +14,13 @@ * limitations under the License. */ -import { Counter, Meter, ObservableResult } from '@opentelemetry/api'; -import { MeterProvider } from '@opentelemetry/sdk-metrics'; +import { + Counter, + Meter, + MeterProvider, + ObservableResult, +} from '@opentelemetry/api'; +import { createMeterProvider } from '@opentelemetry/sdk-metrics'; import * as assert from 'assert'; import * as sinon from 'sinon'; import * as http from 'http'; @@ -200,12 +205,12 @@ describe('PrometheusExporter', () => { describe('export', () => { let exporter: PrometheusExporter; - let meterProvider: MeterProvider; + let meterProvider: ReturnType; let meter: Meter; beforeEach(async () => { exporter = new PrometheusExporter(); - meterProvider = new MeterProvider({ + meterProvider = createMeterProvider({ readers: [exporter], }); meter = meterProvider.getMeter('test-prometheus', '1'); @@ -471,7 +476,7 @@ describe('PrometheusExporter', () => { let exporter: PrometheusExporter; function setup(reader: PrometheusExporter) { - meterProvider = new MeterProvider({ + meterProvider = createMeterProvider({ readers: [exporter], }); diff --git a/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusSerializer.test.ts b/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusSerializer.test.ts index 3939d9ff410..6ff7958c9b1 100644 --- a/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusSerializer.test.ts +++ b/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusSerializer.test.ts @@ -18,10 +18,10 @@ import * as assert from 'assert'; import { MetricAttributes, UpDownCounter } from '@opentelemetry/api'; import { AggregationTemporality, + createMeterProvider, DataPoint, DataPointType, Histogram, - MeterProvider, MetricReader, } from '@opentelemetry/sdk-metrics'; import * as sinon from 'sinon'; @@ -82,7 +82,7 @@ describe('PrometheusSerializer', () => { describe('Singular', () => { async function testSerializer(serializer: PrometheusSerializer) { const reader = new TestMetricReader(); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ views: [ { aggregation: { type: AggregationType.SUM }, @@ -132,7 +132,7 @@ describe('PrometheusSerializer', () => { describe('Histogram', () => { async function testSerializer(serializer: PrometheusSerializer) { const reader = new TestMetricReader(); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ views: [ { aggregation: { @@ -202,7 +202,7 @@ describe('PrometheusSerializer', () => { describe('monotonic Sum', () => { async function testSerializer(serializer: PrometheusSerializer) { const reader = new TestMetricReader(); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ views: [ { aggregation: { type: AggregationType.SUM }, @@ -257,7 +257,7 @@ describe('PrometheusSerializer', () => { describe('non-monotonic Sum', () => { async function testSerializer(serializer: PrometheusSerializer) { const reader = new TestMetricReader(); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ views: [ { aggregation: { type: AggregationType.SUM }, @@ -311,7 +311,7 @@ describe('PrometheusSerializer', () => { describe('Gauge', () => { async function testSerializer(serializer: PrometheusSerializer) { const reader = new TestMetricReader(); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ views: [ { aggregation: { @@ -367,7 +367,7 @@ describe('PrometheusSerializer', () => { describe('with ExplicitBucketHistogramAggregation', () => { async function testSerializer(serializer: PrometheusSerializer) { const reader = new TestMetricReader(); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ views: [ { aggregation: { @@ -425,7 +425,7 @@ describe('PrometheusSerializer', () => { it('serialize cumulative metric record on instrument that allows negative values', async () => { const serializer = new PrometheusSerializer(); const reader = new TestMetricReader(); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ views: [ { aggregation: { @@ -481,7 +481,7 @@ describe('PrometheusSerializer', () => { options: Partial<{ unit: string; exportAll: boolean }> = {} ) { const reader = new TestMetricReader(); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ views: [ { aggregation: { type: AggregationType.SUM }, @@ -573,7 +573,7 @@ describe('PrometheusSerializer', () => { fn: (counter: UpDownCounter) => void ) { const reader = new TestMetricReader(); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ views: [ { aggregation: { type: AggregationType.SUM }, diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts index 599865107c6..35a182473b4 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-metrics.test.ts @@ -15,9 +15,9 @@ */ import { AggregationTemporality, + createInMemoryMetricExporter, + createMeterProvider, DataPointType, - InMemoryMetricExporter, - MeterProvider, } from '@opentelemetry/sdk-metrics'; import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { @@ -47,11 +47,11 @@ const protocol = 'http'; const hostname = 'localhost'; const pathname = '/test'; const tracerProvider = new NodeTracerProvider(); -const metricsMemoryExporter = new InMemoryMetricExporter( - AggregationTemporality.DELTA -); +const metricsMemoryExporter = createInMemoryMetricExporter({ + aggregationTemporality: AggregationTemporality.DELTA, +}); const metricReader = new TestMetricReader(metricsMemoryExporter); -const meterProvider = new MeterProvider({ readers: [metricReader] }); +const meterProvider = createMeterProvider({ readers: [metricReader] }); instrumentation.setTracerProvider(tracerProvider); instrumentation.setMeterProvider(meterProvider); diff --git a/experimental/packages/opentelemetry-instrumentation/test/common/Instrumentation.test.ts b/experimental/packages/opentelemetry-instrumentation/test/common/Instrumentation.test.ts index 9b0e916762a..1f53387ac39 100644 --- a/experimental/packages/opentelemetry-instrumentation/test/common/Instrumentation.test.ts +++ b/experimental/packages/opentelemetry-instrumentation/test/common/Instrumentation.test.ts @@ -23,8 +23,9 @@ import { SpanCustomizationHook, } from '../../src'; -import { MeterProvider } from '@opentelemetry/sdk-metrics'; +import { createMeterProvider } from '@opentelemetry/sdk-metrics'; import { LoggerProvider } from '@opentelemetry/sdk-logs'; +import { MeterProvider } from '@opentelemetry/api'; interface TestInstrumentationConfig extends InstrumentationConfig { isActive?: boolean; @@ -84,7 +85,7 @@ describe('BaseInstrumentation', () => { describe('setMeterProvider', () => { let otelTestingMeterProvider: MeterProvider; beforeEach(() => { - otelTestingMeterProvider = new MeterProvider(); + otelTestingMeterProvider = createMeterProvider(); }); it('should call _updateMetricInstruments', () => { let called = true; diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index f13de7514f4..dbbbee00ffb 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -39,7 +39,7 @@ import { } from '@opentelemetry/resources'; import { LogRecordProcessor, LoggerProvider } from '@opentelemetry/sdk-logs'; import { - MeterProvider, + createMeterProvider, MetricReader, ViewOptions, } from '@opentelemetry/sdk-metrics'; @@ -95,7 +95,7 @@ export class NodeSDK { private _tracerProvider?: NodeTracerProvider | TracerProviderWithEnvExporters; private _loggerProvider?: LoggerProvider; - private _meterProvider?: MeterProvider; + private _meterProvider?: ReturnType; private _serviceName?: string; private _configuration?: Partial; @@ -275,7 +275,7 @@ export class NodeSDK { if (this._meterProviderConfig.reader) { readers.push(this._meterProviderConfig.reader); } - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ resource: this._resource, views: this._meterProviderConfig?.views ?? [], readers: readers, diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index 3011ef01314..c556726f5fa 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -23,6 +23,7 @@ import { DiagLogLevel, metrics, DiagConsoleLogger, + MeterProvider, } from '@opentelemetry/api'; import { AsyncHooksContextManager, @@ -31,11 +32,10 @@ import { import { CompositePropagator } from '@opentelemetry/core'; import { AggregationTemporality, - ConsoleMetricExporter, - InMemoryMetricExporter, + createConsoleMetricExporter, InstrumentType, - MeterProvider, - PeriodicExportingMetricReader, + createPeriodicExportingMetricReader, + createInMemoryMetricExporter, } from '@opentelemetry/sdk-metrics'; import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { @@ -80,6 +80,10 @@ const DefaultContextManager = semver.gte(process.version, '14.8.0') ? AsyncLocalStorageContextManager : AsyncHooksContextManager; +function isSdkMeterProvider(obj: any) { + return typeof obj.shutdown === 'function'; +} + describe('Node SDK', () => { let ctxManager: any; let propagator: any; @@ -127,7 +131,7 @@ describe('Node SDK', () => { delegate, 'tracer provider should not have changed' ); - assert.ok(!(metrics.getMeterProvider() instanceof MeterProvider)); + assert.ok(!isSdkMeterProvider(metrics.getMeterProvider())); assert.ok(!(logs.getLoggerProvider() instanceof LoggerProvider)); delete env.OTEL_TRACES_EXPORTER; await sdk.shutdown(); @@ -175,7 +179,7 @@ describe('Node SDK', () => { sdk.start(); - assert.ok(!(metrics.getMeterProvider() instanceof MeterProvider)); + assert.ok(!isSdkMeterProvider(metrics.getMeterProvider())); assert.ok( context['_getContextManager']().constructor.name === @@ -198,7 +202,7 @@ describe('Node SDK', () => { sdk.start(); - assert.ok(!(metrics.getMeterProvider() instanceof MeterProvider)); + assert.ok(!isSdkMeterProvider(metrics.getMeterProvider())); assert.ok( context['_getContextManager']().constructor.name === @@ -228,7 +232,7 @@ describe('Node SDK', () => { sdk.start(); - assert.ok(!(metrics.getMeterProvider() instanceof MeterProvider)); + assert.ok(!isSdkMeterProvider(metrics.getMeterProvider())); assert.ok( context['_getContextManager']().constructor.name === @@ -256,8 +260,8 @@ describe('Node SDK', () => { // need to set OTEL_TRACES_EXPORTER to none since default value is otlp // which sets up an exporter and affects the context manager env.OTEL_TRACES_EXPORTER = 'none'; - const exporter = new ConsoleMetricExporter(); - const metricReader = new PeriodicExportingMetricReader({ + const exporter = createConsoleMetricExporter(); + const metricReader = createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: 100, exportTimeoutMillis: 100, @@ -286,7 +290,7 @@ describe('Node SDK', () => { 'tracer provider should not have changed' ); - assert.ok(metrics.getMeterProvider() instanceof MeterProvider); + assert.ok(isSdkMeterProvider(metrics.getMeterProvider())); await sdk.shutdown(); delete env.OTEL_TRACES_EXPORTER; @@ -344,7 +348,7 @@ describe('Node SDK', () => { }); async function waitForNumberOfMetrics( - exporter: InMemoryMetricExporter, + exporter: ReturnType, numberOfMetrics: number ): Promise { if (numberOfMetrics <= 0) { @@ -363,10 +367,10 @@ describe('Node SDK', () => { // need to set OTEL_TRACES_EXPORTER to none since default value is otlp // which sets up an exporter and affects the context manager env.OTEL_TRACES_EXPORTER = 'none'; - const exporter = new InMemoryMetricExporter( - AggregationTemporality.CUMULATIVE - ); - const metricReader = new PeriodicExportingMetricReader({ + const exporter = createInMemoryMetricExporter({ + aggregationTemporality: AggregationTemporality.CUMULATIVE, + }); + const metricReader = createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: 100, exportTimeoutMillis: 100, @@ -818,8 +822,8 @@ describe('Node SDK', () => { }); it('should not register a meter provider if a reader is provided', async () => { - const exporter = new ConsoleMetricExporter(); - const metricReader = new PeriodicExportingMetricReader({ + const exporter = createConsoleMetricExporter(); + const metricReader = createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: 100, exportTimeoutMillis: 100, @@ -831,7 +835,7 @@ describe('Node SDK', () => { }); sdk.start(); - assert.ok(!(metrics.getMeterProvider() instanceof MeterProvider)); + assert.ok(!isSdkMeterProvider(metrics.getMeterProvider())); await sdk.shutdown(); }); diff --git a/experimental/packages/shim-opencensus/src/OpenCensusMetricProducer.ts b/experimental/packages/shim-opencensus/src/OpenCensusMetricProducer.ts index 04d4a067384..c2b58c620c6 100644 --- a/experimental/packages/shim-opencensus/src/OpenCensusMetricProducer.ts +++ b/experimental/packages/shim-opencensus/src/OpenCensusMetricProducer.ts @@ -46,8 +46,8 @@ interface OpenCensusMetricProducerOptions { * * @example * ``` - * const meterProvider = new MeterProvider(); - * const reader = new PeriodicExportingMetricReader({ + * const meterProvider = createMeterProvider(); + * const reader = createPeriodicExportingMetricReader({ * metricProducers: [new OpenCensusMetricProducer()], * exporter: exporter, * }); diff --git a/packages/sdk-metrics/src/MeterProvider.ts b/packages/sdk-metrics/src/MeterProvider.ts index 3471f414d5d..0a1ea216c1f 100644 --- a/packages/sdk-metrics/src/MeterProvider.ts +++ b/packages/sdk-metrics/src/MeterProvider.ts @@ -41,7 +41,7 @@ export interface MeterProviderOptions { /** * This class implements the {@link MeterProvider} interface. */ -export class MeterProvider implements IMeterProvider { +class MeterProvider implements IMeterProvider { private _sharedState: MeterProviderSharedState; private _shutdown = false; @@ -122,3 +122,12 @@ export class MeterProvider implements IMeterProvider { ); } } + +export function createMeterProvider( + options?: MeterProviderOptions +): IMeterProvider & { + shutdown(options?: ShutdownOptions): Promise; + forceFlush(options?: ForceFlushOptions): Promise; +} { + return new MeterProvider(options); +} diff --git a/packages/sdk-metrics/src/export/ConsoleMetricExporter.ts b/packages/sdk-metrics/src/export/ConsoleMetricExporter.ts index 62973d8805f..0624c2e2103 100644 --- a/packages/sdk-metrics/src/export/ConsoleMetricExporter.ts +++ b/packages/sdk-metrics/src/export/ConsoleMetricExporter.ts @@ -28,7 +28,7 @@ interface ConsoleMetricExporterOptions { } /* eslint-disable no-console */ -export class ConsoleMetricExporter implements PushMetricExporter { +class ConsoleMetricExporter implements PushMetricExporter { protected _shutdown = false; protected _temporalitySelector: AggregationTemporalitySelector; @@ -85,3 +85,9 @@ export class ConsoleMetricExporter implements PushMetricExporter { done({ code: ExportResultCode.SUCCESS }); } } + +export function createConsoleMetricExporter( + options?: ConsoleMetricExporterOptions +): PushMetricExporter { + return new ConsoleMetricExporter(options); +} diff --git a/packages/sdk-metrics/src/export/InMemoryMetricExporter.ts b/packages/sdk-metrics/src/export/InMemoryMetricExporter.ts index 2a80973bb95..6bfda24ce55 100644 --- a/packages/sdk-metrics/src/export/InMemoryMetricExporter.ts +++ b/packages/sdk-metrics/src/export/InMemoryMetricExporter.ts @@ -26,7 +26,7 @@ import { PushMetricExporter } from './MetricExporter'; * which accumulates metrics data in the local memory and * allows to inspect it (useful for e.g. unit tests). */ -export class InMemoryMetricExporter implements PushMetricExporter { +class InMemoryMetricExporter implements PushMetricExporter { protected _shutdown = false; protected _aggregationTemporality: AggregationTemporality; private _metrics: ResourceMetrics[] = []; @@ -79,3 +79,17 @@ export class InMemoryMetricExporter implements PushMetricExporter { return Promise.resolve(); } } + +/** + * Creates an In-memory Metrics Exporter. A Push Metric Exporter + * which accumulates metrics data in the local memory and + * allows to inspect it (useful for e.g. unit tests). + */ +export function createInMemoryMetricExporter(options: { + aggregationTemporality: AggregationTemporality; +}): PushMetricExporter & { + reset(): void; + getMetrics(): ResourceMetrics[]; +} { + return new InMemoryMetricExporter(options.aggregationTemporality); +} diff --git a/packages/sdk-metrics/src/export/PeriodicExportingMetricReader.ts b/packages/sdk-metrics/src/export/PeriodicExportingMetricReader.ts index 5ecafff6822..53003db45e7 100644 --- a/packages/sdk-metrics/src/export/PeriodicExportingMetricReader.ts +++ b/packages/sdk-metrics/src/export/PeriodicExportingMetricReader.ts @@ -54,7 +54,7 @@ export type PeriodicExportingMetricReaderOptions = { * {@link MetricReader} which collects metrics based on a user-configurable time interval, and passes the metrics to * the configured {@link PushMetricExporter} */ -export class PeriodicExportingMetricReader extends MetricReader { +class PeriodicExportingMetricReader extends MetricReader { private _interval?: ReturnType; private _exporter: PushMetricExporter; private readonly _exportInterval: number; @@ -170,3 +170,13 @@ export class PeriodicExportingMetricReader extends MetricReader { await this._exporter.shutdown(); } } + +/** + * Creates a {@link MetricReader} which collects metrics based on a user-configurable time interval, and passes the + * metrics to the configured {@link PushMetricExporter} + */ +export function createPeriodicExportingMetricReader( + options: PeriodicExportingMetricReaderOptions +): MetricReader { + return new PeriodicExportingMetricReader(options); +} diff --git a/packages/sdk-metrics/src/index.ts b/packages/sdk-metrics/src/index.ts index 4f359214bc8..e3cbd752d58 100644 --- a/packages/sdk-metrics/src/index.ts +++ b/packages/sdk-metrics/src/index.ts @@ -49,13 +49,13 @@ export { PushMetricExporter } from './export/MetricExporter'; export { MetricReader, MetricReaderOptions } from './export/MetricReader'; export { - PeriodicExportingMetricReader, + createPeriodicExportingMetricReader, PeriodicExportingMetricReaderOptions, } from './export/PeriodicExportingMetricReader'; -export { InMemoryMetricExporter } from './export/InMemoryMetricExporter'; +export { createInMemoryMetricExporter } from './export/InMemoryMetricExporter'; -export { ConsoleMetricExporter } from './export/ConsoleMetricExporter'; +export { createConsoleMetricExporter } from './export/ConsoleMetricExporter'; export { MetricCollectOptions, MetricProducer } from './export/MetricProducer'; @@ -65,7 +65,7 @@ export { InstrumentType } from './InstrumentDescriptor'; */ export type InstrumentDescriptor = MetricDescriptor; -export { MeterProvider, MeterProviderOptions } from './MeterProvider'; +export { createMeterProvider, MeterProviderOptions } from './MeterProvider'; export { AggregationOption, AggregationType } from './view/AggregationOption'; diff --git a/packages/sdk-metrics/test/Instruments.test.ts b/packages/sdk-metrics/test/Instruments.test.ts index e02a393397d..c1d97fcc59c 100644 --- a/packages/sdk-metrics/test/Instruments.test.ts +++ b/packages/sdk-metrics/test/Instruments.test.ts @@ -23,7 +23,7 @@ import { DataPointType, Histogram, InstrumentType, - MeterProvider, + createMeterProvider, MetricDescriptor, MetricReader, } from '../src'; @@ -408,7 +408,7 @@ describe('Instruments', () => { }); it('should allow metric advice with empty explicit boundaries', function () { - const meter = new MeterProvider({ + const meter = createMeterProvider({ readers: [new TestMetricReader()], }).getMeter('meter'); @@ -804,7 +804,7 @@ describe('Instruments', () => { function setup() { const deltaReader = new TestDeltaMetricReader(); const cumulativeReader = new TestMetricReader(); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ resource: defaultResource, readers: [deltaReader, cumulativeReader], }); diff --git a/packages/sdk-metrics/test/MeterProvider.test.ts b/packages/sdk-metrics/test/MeterProvider.test.ts index c9a7eafd102..4692f467e26 100644 --- a/packages/sdk-metrics/test/MeterProvider.test.ts +++ b/packages/sdk-metrics/test/MeterProvider.test.ts @@ -16,7 +16,7 @@ import * as assert from 'assert'; import { - MeterProvider, + createMeterProvider, InstrumentType, DataPointType, HistogramMetricData, @@ -40,32 +40,30 @@ describe('MeterProvider', () => { describe('constructor', () => { it('should construct without exceptions', () => { - const meterProvider = new MeterProvider(); - assert(meterProvider instanceof MeterProvider); + createMeterProvider(); }); it('construct with resource', () => { - const meterProvider = new MeterProvider({ resource: defaultResource }); - assert(meterProvider instanceof MeterProvider); + createMeterProvider({ resource: defaultResource }); }); }); describe('getMeter', () => { it('should get a meter', () => { - const meterProvider = new MeterProvider(); + const meterProvider = createMeterProvider(); const meter = meterProvider.getMeter('meter1', '1.0.0'); assert(meter instanceof Meter); }); it('should get an identical meter on duplicated calls', () => { - const meterProvider = new MeterProvider(); + const meterProvider = createMeterProvider(); const meter1 = meterProvider.getMeter('meter1', '1.0.0'); const meter2 = meterProvider.getMeter('meter1', '1.0.0'); assert.strictEqual(meter1, meter2); }); it('get a noop meter on shutdown', () => { - const meterProvider = new MeterProvider(); + const meterProvider = createMeterProvider(); meterProvider.shutdown(); const meter = meterProvider.getMeter('meter1', '1.0.0'); // returned tracer should be no-op, not instance of Meter (from SDK) @@ -74,7 +72,7 @@ describe('MeterProvider', () => { it('get meter with same identity', async () => { const reader = new TestMetricReader(); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ resource: defaultResource, readers: [reader], }); @@ -134,7 +132,7 @@ describe('MeterProvider', () => { describe('addView', () => { it('with existing instrument should rename', async () => { const reader = new TestMetricReader(); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ resource: defaultResource, // Add view to rename 'non-renamed-instrument' to 'renamed-instrument' views: [ @@ -204,7 +202,7 @@ describe('MeterProvider', () => { const reader = new TestMetricReader(); // Add view to drop all attributes except 'attrib1' - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ resource: defaultResource, views: [ { @@ -272,7 +270,7 @@ describe('MeterProvider', () => { const reader = new TestMetricReader(); // Add view that renames 'test-counter' to 'renamed-instrument' - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ resource: defaultResource, views: [ { @@ -343,7 +341,7 @@ describe('MeterProvider', () => { it('with meter name should apply view to only the selected meter', async () => { const reader = new TestMetricReader(); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ resource: defaultResource, views: [ // Add view that renames 'test-counter' to 'renamed-instrument' on 'meter1' @@ -416,7 +414,7 @@ describe('MeterProvider', () => { it('with different instrument types does not throw', async () => { const reader = new TestMetricReader(); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ resource: defaultResource, // Add Views to rename both instruments (of different types) to the same name. views: [ @@ -485,7 +483,7 @@ describe('MeterProvider', () => { const reader = new TestMetricReader(); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ resource: defaultResource, views: [ { @@ -556,7 +554,7 @@ describe('MeterProvider', () => { const reader2 = new TestMetricReader(); const reader1ShutdownSpy = sinon.spy(reader1, 'shutdown'); const reader2ShutdownSpy = sinon.spy(reader2, 'shutdown'); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ resource: defaultResource, readers: [reader1, reader2], }); @@ -582,7 +580,7 @@ describe('MeterProvider', () => { const reader2 = new TestMetricReader(); const reader1ForceFlushSpy = sinon.spy(reader1, 'forceFlush'); const reader2ForceFlushSpy = sinon.spy(reader2, 'forceFlush'); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ resource: defaultResource, readers: [reader1, reader2], }); diff --git a/packages/sdk-metrics/test/export/ConsoleMetricExporter.test.ts b/packages/sdk-metrics/test/export/ConsoleMetricExporter.test.ts index 258f0df00f5..ae1033f3338 100644 --- a/packages/sdk-metrics/test/export/ConsoleMetricExporter.test.ts +++ b/packages/sdk-metrics/test/export/ConsoleMetricExporter.test.ts @@ -15,16 +15,22 @@ */ import * as metrics from '@opentelemetry/api'; import { ExportResult } from '@opentelemetry/core'; -import { ConsoleMetricExporter } from '../../src/export/ConsoleMetricExporter'; -import { PeriodicExportingMetricReader } from '../../src/export/PeriodicExportingMetricReader'; +import { createConsoleMetricExporter } from '../../src/export/ConsoleMetricExporter'; +import { createPeriodicExportingMetricReader } from '../../src/export/PeriodicExportingMetricReader'; import { ResourceMetrics } from '../../src/export/MetricData'; -import { MeterProvider } from '../../src/MeterProvider'; +import { createMeterProvider } from '../../src/MeterProvider'; import { defaultResource } from '../util'; import * as assert from 'assert'; import * as sinon from 'sinon'; import { assertAggregationTemporalitySelector } from './utils'; import { DEFAULT_AGGREGATION_TEMPORALITY_SELECTOR } from '../../src/export/AggregationSelector'; -import { AggregationTemporality, InstrumentType } from '../../src'; +import { + AggregationTemporality, + InstrumentType, + MetricReader, + PushMetricExporter, +} from '../../src'; +import { MeterProvider } from '@opentelemetry/api'; async function waitForNumberOfExports( exporter: sinon.SinonSpy< @@ -48,22 +54,22 @@ async function waitForNumberOfExports( describe('ConsoleMetricExporter', () => { describe('export', () => { let previousConsoleDir: any; - let exporter: ConsoleMetricExporter; + let exporter: PushMetricExporter; let meterProvider: MeterProvider; - let metricReader: PeriodicExportingMetricReader; + let metricReader: MetricReader; let meter: metrics.Meter; beforeEach(() => { previousConsoleDir = console.dir; console.dir = () => {}; - exporter = new ConsoleMetricExporter(); - metricReader = new PeriodicExportingMetricReader({ + exporter = createConsoleMetricExporter(); + metricReader = createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: 100, exportTimeoutMillis: 100, }); - meterProvider = new MeterProvider({ + meterProvider = createMeterProvider({ resource: defaultResource, readers: [metricReader], }); @@ -129,7 +135,7 @@ describe('ConsoleMetricExporter', () => { describe('constructor', () => { it('with no arguments should select cumulative temporality', () => { - const exporter = new ConsoleMetricExporter(); + const exporter = createConsoleMetricExporter(); assertAggregationTemporalitySelector( exporter, DEFAULT_AGGREGATION_TEMPORALITY_SELECTOR @@ -137,7 +143,7 @@ describe('ConsoleMetricExporter', () => { }); it('with empty options should select cumulative temporality', () => { - const exporter = new ConsoleMetricExporter({}); + const exporter = createConsoleMetricExporter({}); assertAggregationTemporalitySelector( exporter, DEFAULT_AGGREGATION_TEMPORALITY_SELECTOR @@ -145,7 +151,7 @@ describe('ConsoleMetricExporter', () => { }); it('with cumulative preference should select cumulative temporality', () => { - const exporter = new ConsoleMetricExporter({ + const exporter = createConsoleMetricExporter({ temporalitySelector: _ => AggregationTemporality.CUMULATIVE, }); assertAggregationTemporalitySelector( @@ -169,7 +175,7 @@ describe('ConsoleMetricExporter', () => { return AggregationTemporality.CUMULATIVE; } }; - const exporter = new ConsoleMetricExporter({ + const exporter = createConsoleMetricExporter({ temporalitySelector: selector, }); assertAggregationTemporalitySelector(exporter, selector); diff --git a/packages/sdk-metrics/test/export/InMemoryMetricExporter.test.ts b/packages/sdk-metrics/test/export/InMemoryMetricExporter.test.ts index 1e14a8ed5e1..25761fede79 100644 --- a/packages/sdk-metrics/test/export/InMemoryMetricExporter.test.ts +++ b/packages/sdk-metrics/test/export/InMemoryMetricExporter.test.ts @@ -18,14 +18,16 @@ import { Resource } from '@opentelemetry/resources'; import * as metrics from '@opentelemetry/api'; import assert = require('assert'); import { AggregationTemporality } from '../../src/export/AggregationTemporality'; -import { InMemoryMetricExporter } from '../../src/export/InMemoryMetricExporter'; +import { createInMemoryMetricExporter } from '../../src/export/InMemoryMetricExporter'; import { ResourceMetrics } from '../../src/export/MetricData'; -import { PeriodicExportingMetricReader } from '../../src/export/PeriodicExportingMetricReader'; -import { MeterProvider } from '../../src/MeterProvider'; +import { createPeriodicExportingMetricReader } from '../../src/export/PeriodicExportingMetricReader'; +import { createMeterProvider } from '../../src/MeterProvider'; import { defaultResource } from '../util'; +import { MetricReader } from '../../src'; +import { MeterProvider } from '@opentelemetry/api'; async function waitForNumberOfExports( - exporter: InMemoryMetricExporter, + exporter: ReturnType, numberOfExports: number ): Promise { if (numberOfExports <= 0) { @@ -43,19 +45,21 @@ async function waitForNumberOfExports( } describe('InMemoryMetricExporter', () => { - let exporter: InMemoryMetricExporter; + let exporter: ReturnType; let meterProvider: MeterProvider; - let metricReader: PeriodicExportingMetricReader; + let metricReader: MetricReader; let meter: metrics.Meter; beforeEach(() => { - exporter = new InMemoryMetricExporter(AggregationTemporality.CUMULATIVE); - metricReader = new PeriodicExportingMetricReader({ + exporter = createInMemoryMetricExporter({ + aggregationTemporality: AggregationTemporality.CUMULATIVE, + }); + metricReader = createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: 100, exportTimeoutMillis: 100, }); - meterProvider = new MeterProvider({ + meterProvider = createMeterProvider({ resource: defaultResource, readers: [metricReader], }); diff --git a/packages/sdk-metrics/test/export/MetricReader.test.ts b/packages/sdk-metrics/test/export/MetricReader.test.ts index cc091c97977..0596e8344b7 100644 --- a/packages/sdk-metrics/test/export/MetricReader.test.ts +++ b/packages/sdk-metrics/test/export/MetricReader.test.ts @@ -16,7 +16,7 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; -import { MeterProvider } from '../../src/MeterProvider'; +import { createMeterProvider } from '../../src/MeterProvider'; import { assertRejects } from '../test-utils'; import { emptyResourceMetrics, TestMetricProducer } from './TestMetricProducer'; import { TestMetricReader } from './TestMetricReader'; @@ -75,14 +75,14 @@ describe('MetricReader', () => { const reader = new TestMetricReader(); assert.throws( () => - new MeterProvider({ + createMeterProvider({ readers: [reader, reader], }), /MetricReader can not be bound to a MeterProvider again/ ); assert.throws( () => - new MeterProvider({ + createMeterProvider({ readers: [reader], }), /MetricReader can not be bound to a MeterProvider again/ @@ -151,7 +151,7 @@ describe('MetricReader', () => { const reader = new TestMetricReader({ metricProducers: [additionalProducer], }); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ resource: defaultResource, readers: [reader], }); @@ -192,7 +192,7 @@ describe('MetricReader', () => { new TestMetricProducer({ errors: ['err2'] }), ], }); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ readers: [reader], }); diff --git a/packages/sdk-metrics/test/export/PeriodicExportingMetricReader.test.ts b/packages/sdk-metrics/test/export/PeriodicExportingMetricReader.test.ts index fcfc86511f2..44cbc3ab6a1 100644 --- a/packages/sdk-metrics/test/export/PeriodicExportingMetricReader.test.ts +++ b/packages/sdk-metrics/test/export/PeriodicExportingMetricReader.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { PeriodicExportingMetricReader } from '../../src/export/PeriodicExportingMetricReader'; +import { createPeriodicExportingMetricReader } from '../../src/export/PeriodicExportingMetricReader'; import { AggregationTemporality } from '../../src/export/AggregationTemporality'; import { AggregationOption, @@ -123,15 +123,14 @@ describe('PeriodicExportingMetricReader', () => { }); describe('constructor', () => { - it('should construct PeriodicExportingMetricReader without exceptions', () => { + it('should constructcreatePeriodicExportingMetricReader without exceptions', () => { const exporter = new TestDeltaMetricExporter(); - assert.doesNotThrow( - () => - new PeriodicExportingMetricReader({ - exporter, - exportIntervalMillis: 4000, - exportTimeoutMillis: 3000, - }) + assert.doesNotThrow(() => + createPeriodicExportingMetricReader({ + exporter, + exportIntervalMillis: 4000, + exportTimeoutMillis: 3000, + }) ); }); @@ -139,7 +138,7 @@ describe('PeriodicExportingMetricReader', () => { const exporter = new TestDeltaMetricExporter(); assert.throws( () => - new PeriodicExportingMetricReader({ + createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: 0, exportTimeoutMillis: 0, @@ -152,7 +151,7 @@ describe('PeriodicExportingMetricReader', () => { const exporter = new TestDeltaMetricExporter(); assert.throws( () => - new PeriodicExportingMetricReader({ + createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: 1, exportTimeoutMillis: 0, @@ -165,7 +164,7 @@ describe('PeriodicExportingMetricReader', () => { const exporter = new TestDeltaMetricExporter(); assert.throws( () => - new PeriodicExportingMetricReader({ + createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: 100, exportTimeoutMillis: 200, @@ -179,7 +178,7 @@ describe('PeriodicExportingMetricReader', () => { const exporterMock = sinon.mock(exporter); exporterMock.expects('export').never(); - new PeriodicExportingMetricReader({ + createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: 1, exportTimeoutMillis: 1, @@ -193,7 +192,7 @@ describe('PeriodicExportingMetricReader', () => { describe('setMetricProducer', () => { it('should start exporting periodically', async () => { const exporter = new TestMetricExporter(); - const reader = new PeriodicExportingMetricReader({ + const reader = createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: 30, exportTimeoutMillis: 20, @@ -214,7 +213,7 @@ describe('PeriodicExportingMetricReader', () => { it('should keep running on export errors', async () => { const exporter = new TestMetricExporter(); exporter.throwExport = true; - const reader = new PeriodicExportingMetricReader({ + const reader = createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: 30, exportTimeoutMillis: 20, @@ -235,7 +234,7 @@ describe('PeriodicExportingMetricReader', () => { it('should keep running on export failure', async () => { const exporter = new TestMetricExporter(); exporter.rejectExport = true; - const reader = new PeriodicExportingMetricReader({ + const reader = createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: 30, exportTimeoutMillis: 20, @@ -257,7 +256,7 @@ describe('PeriodicExportingMetricReader', () => { const exporter = new TestMetricExporter(); // set time longer than timeout. exporter.exportTime = 40; - const reader = new PeriodicExportingMetricReader({ + const reader = createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: 30, exportTimeoutMillis: 20, @@ -285,7 +284,7 @@ describe('PeriodicExportingMetricReader', () => { const exporter = new TestMetricExporter(); const exporterMock = sinon.mock(exporter); exporterMock.expects('forceFlush').calledOnceWithExactly(); - const reader = new PeriodicExportingMetricReader({ + const reader = createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: MAX_32_BIT_INT, exportTimeoutMillis: 80, @@ -305,7 +304,7 @@ describe('PeriodicExportingMetricReader', () => { const exporter = new TestMetricExporter(); exporter.forceFlushTime = 60; - const reader = new PeriodicExportingMetricReader({ + const reader = createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: MAX_32_BIT_INT, exportTimeoutMillis: 80, @@ -322,7 +321,7 @@ describe('PeriodicExportingMetricReader', () => { it('should throw when exporter throws', async () => { const exporter = new TestMetricExporter(); exporter.throwFlush = true; - const reader = new PeriodicExportingMetricReader({ + const reader = createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: MAX_32_BIT_INT, exportTimeoutMillis: 80, @@ -337,7 +336,7 @@ describe('PeriodicExportingMetricReader', () => { const exporterMock = sinon.mock(exporter); // expect once on shutdown. exporterMock.expects('forceFlush').once(); - const reader = new PeriodicExportingMetricReader({ + const reader = createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: MAX_32_BIT_INT, exportTimeoutMillis: 80, @@ -355,7 +354,7 @@ describe('PeriodicExportingMetricReader', () => { it('should default to Cumulative with no exporter preference', () => { // Adding exporter without preference. const exporter = new TestMetricExporter(); - const reader = new PeriodicExportingMetricReader({ + const reader = createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: MAX_32_BIT_INT, }); @@ -370,7 +369,7 @@ describe('PeriodicExportingMetricReader', () => { it('should default to exporter preference', () => { // Adding exporter with DELTA preference. const exporter = new TestDeltaMetricExporter(); - const reader = new PeriodicExportingMetricReader({ + const reader = createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: MAX_32_BIT_INT, }); @@ -387,7 +386,7 @@ describe('PeriodicExportingMetricReader', () => { it('should use default aggregation with no exporter preference', () => { // Adding exporter without preference. const exporter = new TestMetricExporter(); - const reader = new PeriodicExportingMetricReader({ + const reader = createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: MAX_32_BIT_INT, }); @@ -400,7 +399,7 @@ describe('PeriodicExportingMetricReader', () => { it('should default to exporter preference', () => { // Adding exporter with Drop Aggregation preference. const exporter = new TestDropMetricExporter(); - const reader = new PeriodicExportingMetricReader({ + const reader = createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: MAX_32_BIT_INT, }); @@ -420,7 +419,7 @@ describe('PeriodicExportingMetricReader', () => { const exporter = new TestMetricExporter(); const exporterMock = sinon.mock(exporter); exporterMock.expects('forceFlush').calledOnceWithExactly(); - const reader = new PeriodicExportingMetricReader({ + const reader = createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: MAX_32_BIT_INT, exportTimeoutMillis: 80, @@ -435,7 +434,7 @@ describe('PeriodicExportingMetricReader', () => { const exporter = new TestMetricExporter(); exporter.forceFlushTime = 1000; - const reader = new PeriodicExportingMetricReader({ + const reader = createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: MAX_32_BIT_INT, exportTimeoutMillis: 80, @@ -452,7 +451,7 @@ describe('PeriodicExportingMetricReader', () => { const exporter = new TestMetricExporter(); const exporterMock = sinon.mock(exporter); exporterMock.expects('shutdown').calledOnceWithExactly(); - const reader = new PeriodicExportingMetricReader({ + const reader = createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: MAX_32_BIT_INT, exportTimeoutMillis: 80, @@ -470,7 +469,7 @@ describe('PeriodicExportingMetricReader', () => { it('should throw on non-initialized instance.', async () => { const exporter = new TestMetricExporter(); exporter.throwFlush = true; - const reader = new PeriodicExportingMetricReader({ + const reader = createPeriodicExportingMetricReader({ exporter: exporter, exportIntervalMillis: MAX_32_BIT_INT, exportTimeoutMillis: 80, diff --git a/packages/sdk-metrics/test/regression/cumulative-exponential-histogram.test.ts b/packages/sdk-metrics/test/regression/cumulative-exponential-histogram.test.ts index 3787308d9ec..8ba78ff1104 100644 --- a/packages/sdk-metrics/test/regression/cumulative-exponential-histogram.test.ts +++ b/packages/sdk-metrics/test/regression/cumulative-exponential-histogram.test.ts @@ -21,7 +21,7 @@ import { AggregationTemporality, AggregationType, InstrumentType, - MeterProvider, + createMeterProvider, MetricReader, } from '../../src'; import { TestMetricReader } from '../export/TestMetricReader'; @@ -57,7 +57,7 @@ describe('cumulative-exponential-histogram', () => { : { type: AggregationType.DEFAULT }; }, }); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ readers: [reader], }); diff --git a/packages/sdk-metrics/test/regression/histogram-recording-nan.test.ts b/packages/sdk-metrics/test/regression/histogram-recording-nan.test.ts index 4cb73ff4984..f34956ac9cb 100644 --- a/packages/sdk-metrics/test/regression/histogram-recording-nan.test.ts +++ b/packages/sdk-metrics/test/regression/histogram-recording-nan.test.ts @@ -17,7 +17,7 @@ import * as assert from 'assert'; import { AggregationTemporality, - MeterProvider, + createMeterProvider, MetricReader, DataPoint, ExponentialHistogram, @@ -36,7 +36,7 @@ describe('histogram-recording-nan', () => { return { type: AggregationType.EXPONENTIAL_HISTOGRAM }; }, }); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ readers: [reader], }); @@ -68,7 +68,7 @@ describe('histogram-recording-nan', () => { return { type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM }; }, }); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ readers: [reader], }); diff --git a/packages/sdk-metrics/test/regression/two-metric-readers-async-instrument.test.ts b/packages/sdk-metrics/test/regression/two-metric-readers-async-instrument.test.ts index 74913f9476b..b83e41a2b58 100644 --- a/packages/sdk-metrics/test/regression/two-metric-readers-async-instrument.test.ts +++ b/packages/sdk-metrics/test/regression/two-metric-readers-async-instrument.test.ts @@ -15,7 +15,7 @@ */ import * as assert from 'assert'; -import { DataPointType, MeterProvider, MetricReader } from '../../src'; +import { DataPointType, createMeterProvider, MetricReader } from '../../src'; import { TestDeltaMetricReader } from '../export/TestMetricReader'; import { assertDataPoint, assertMetricData } from '../util'; @@ -25,7 +25,7 @@ describe('two-metric-readers-async-instrument', () => { it('both metric readers should collect metrics', async () => { const reader1 = new TestDeltaMetricReader(); const reader2 = new TestDeltaMetricReader(); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ readers: [reader1, reader2], }); diff --git a/packages/sdk-metrics/test/state/MeterSharedState.test.ts b/packages/sdk-metrics/test/state/MeterSharedState.test.ts index 74b4ff9a273..c379fdf7e29 100644 --- a/packages/sdk-metrics/test/state/MeterSharedState.test.ts +++ b/packages/sdk-metrics/test/state/MeterSharedState.test.ts @@ -17,12 +17,12 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; import { - MeterProvider, DataPointType, MetricReader, InstrumentType, AggregationType, ViewOptions, + createMeterProvider, } from '../../src'; import { assertMetricData, @@ -45,7 +45,7 @@ describe('MeterSharedState', () => { describe('registerMetricStorage', () => { function setupMeter(views?: ViewOptions[], readers?: MetricReader[]) { - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ resource: defaultResource, views, readers: readers, @@ -55,10 +55,14 @@ describe('MeterSharedState', () => { return { meter, - meterSharedState: meterProvider['_sharedState'].getMeterSharedState({ + meterSharedState: (meterProvider as any)[ + '_sharedState' + ].getMeterSharedState({ name: 'test-meter', }), - collectors: Array.from(meterProvider['_sharedState'].metricCollectors), + collectors: Array.from( + (meterProvider as any)['_sharedState'].metricCollectors + ), }; } @@ -188,7 +192,7 @@ describe('MeterSharedState', () => { const cumulativeReader = new TestMetricReader(); const deltaReader = new TestDeltaMetricReader(); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ resource: defaultResource, views: views, readers: [cumulativeReader, deltaReader], diff --git a/packages/sdk-metrics/test/state/MetricCollector.test.ts b/packages/sdk-metrics/test/state/MetricCollector.test.ts index f173a48446d..531a6844a61 100644 --- a/packages/sdk-metrics/test/state/MetricCollector.test.ts +++ b/packages/sdk-metrics/test/state/MetricCollector.test.ts @@ -16,7 +16,7 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; -import { MeterProvider } from '../../src'; +import { createMeterProvider } from '../../src'; import { TimeoutError } from '../../src/utils'; import { DataPointType } from '../../src/export/MetricData'; import { MeterProviderSharedState } from '../../src/state/MeterProviderSharedState'; @@ -56,7 +56,7 @@ describe('MetricCollector', () => { describe('collect', () => { function setupInstruments() { const reader = new TestMetricReader(); - const meterProvider = new MeterProvider({ + const meterProvider = createMeterProvider({ resource: defaultResource, readers: [reader], }); From 89a6af624477b1fc39f35d447a76d43e9701f4cf Mon Sep 17 00:00:00 2001 From: Marc Pichler Date: Tue, 12 Nov 2024 10:29:39 +0100 Subject: [PATCH 3/6] Update packages/sdk-metrics/test/export/PeriodicExportingMetricReader.test.ts Co-authored-by: Trent Mick --- .../test/export/PeriodicExportingMetricReader.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sdk-metrics/test/export/PeriodicExportingMetricReader.test.ts b/packages/sdk-metrics/test/export/PeriodicExportingMetricReader.test.ts index 44cbc3ab6a1..d7203fdd325 100644 --- a/packages/sdk-metrics/test/export/PeriodicExportingMetricReader.test.ts +++ b/packages/sdk-metrics/test/export/PeriodicExportingMetricReader.test.ts @@ -123,7 +123,7 @@ describe('PeriodicExportingMetricReader', () => { }); describe('constructor', () => { - it('should constructcreatePeriodicExportingMetricReader without exceptions', () => { + it('should createPeriodicExportingMetricReader without exceptions', () => { const exporter = new TestDeltaMetricExporter(); assert.doesNotThrow(() => createPeriodicExportingMetricReader({ From e2a36a99d8c5b290c6f7c335963e8fb1e32b6b1f Mon Sep 17 00:00:00 2001 From: Marc Pichler Date: Tue, 12 Nov 2024 11:22:42 +0100 Subject: [PATCH 4/6] docs: align docs and examples with changes docs: align docs and examples with changes --- doc/metrics.md | 52 ++++++++++--------- .../examples/metrics/index.js | 15 +++--- examples/otlp-exporter-node/metrics.js | 39 ++++++-------- .../examples/opencensus-shim/setup.js | 27 +++++----- experimental/examples/prometheus/index.js | 7 +-- .../README.md | 16 +++--- .../README.md | 32 +++++++----- .../README.md | 16 +++--- .../README.md | 8 +-- packages/sdk-metrics/README.md | 10 ++-- 10 files changed, 117 insertions(+), 105 deletions(-) diff --git a/doc/metrics.md b/doc/metrics.md index 9e1c642b47d..ba4b4cf1632 100644 --- a/doc/metrics.md +++ b/doc/metrics.md @@ -397,17 +397,20 @@ Below an example is given how you can define explicit buckets for a histogram. ```typescript // Define view for the histogram metric -const histogramView = new View({ - aggregation: new ExplicitBucketHistogramAggregation([0, 1, 5, 10, 15, 20, 25, 30]), +const histogramView: ViewOptions = { + aggregation: { + type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM, + boundaries: [0, 1, 5, 10, 15, 20, 25, 30] + }, instrumentName: 'http.server.duration', instrumentType: InstrumentType.HISTOGRAM, -}); +}; // Note, the instrumentName is the same as the name that has been passed for // the Meter#createHistogram function // Create an instance of the metric provider -const meterProvider = new MeterProvider({ +const meterProvider = createMeterProvider({ views: [ histogramView ] @@ -441,20 +444,20 @@ instruments with a specific name: The following view drops all instruments that are associated with a meter named `pubsub`: ```typescript -const dropView = new View({ - aggregation: new DropAggregation(), +const dropView: ViewOptions = { + aggregation: { type: AggregationType.DROP }, meterName: 'pubsub', -}); +}; ``` Alternatively, you can also drop instruments with a specific instrument name, for example, all instruments of which the name starts with `http`: ```typescript -const dropView = new View({ - aggregation: new DropAggregation(), +const dropView: ViewOptions = { + aggregation: { type: AggregationType.DROP }, instrumentName: 'http*', -}); +}; ``` ### Customizing the metric attributes of instrument @@ -467,12 +470,12 @@ In the example below will drop all attributes except attribute `environment` for all instruments. ```typescript -new View({ +const attributeLimitingView: ViewOptions = { // only export the attribute 'environment' - attributeKeys: ['environment'], + attributeProcesssors: [createAllowListAttributesProcessor(['environment'])], // apply the view to all instruments instrumentName: '*', -}) +}; ``` ## Exporting measurements @@ -497,22 +500,24 @@ to use the Prometheus exporter `PrometheusExporter` which is included in the ```typescript const { PrometheusExporter } = require('@opentelemetry/exporter-prometheus'); -const { MeterProvider } = require('@opentelemetry/sdk-metrics'); +const { createMetricReader } = require('@opentelemetry/sdk-metrics'); // Add your port to the Prometheus options -const options = { port: 9464 }; +const options = {port: 9464}; const exporter = new PrometheusExporter(options); // Creates MeterProvider and installs the exporter as a MetricReader -const meterProvider = new MeterProvider(); -meterProvider.addMetricReader(exporter); +const meterProvider = createMeterProvider({ + readers: [exporter] +}); + const meter = meterProvider.getMeter('example-prometheus'); // Now, start recording data const counter = meter.createCounter('metric_name', { description: 'Example of a counter' }); -counter.add(10, { pid: process.pid }); +counter.add(10, {pid: process.pid}); ``` In the above example the instantiated `PrometheusExporter` is configured to expose @@ -534,19 +539,16 @@ The example below shows how you can configure OpenTelemetry JavaScript to use the OTLP exporter using http/protobuf. ```typescript -const { MeterProvider, PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics'); +const { createMeterProvider, PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics'); const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-proto'); const collectorOptions = { url: '', // url is optional and can be omitted - default is http://localhost:4318/v1/metrics concurrencyLimit: 1, // an optional limit on pending requests }; const exporter = new OTLPMetricExporter(collectorOptions); -const meterProvider = new MeterProvider({}); - -meterProvider.addMetricReader(createPeriodicExportingMetricReader({ - exporter: metricExporter, - exportIntervalMillis: 1000, -})); +const meterProvider = createMeterProvider({ + readers: [createPeriodicExportingMetricReader({ exporter: metricExporter, exportIntervalMillis: 1000 })], +}); // Now, start recording data const meter = meterProvider.getMeter('example-meter'); diff --git a/examples/opentelemetry-web/examples/metrics/index.js b/examples/opentelemetry-web/examples/metrics/index.js index 543c1d22030..77bce266472 100644 --- a/examples/opentelemetry-web/examples/metrics/index.js +++ b/examples/opentelemetry-web/examples/metrics/index.js @@ -1,6 +1,6 @@ const { DiagConsoleLogger, DiagLogLevel, diag, metrics } = require('@opentelemetry/api'); const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-http'); -const { MeterProvider, PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics'); +const { createMeterProvider, createPeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics'); // Optional and only needed to see the internal diagnostic logging (during development) diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG); @@ -18,13 +18,14 @@ function stopMetrics() { function startMetrics() { console.log('STARTING METRICS'); - const meterProvider = new MeterProvider(); - metrics.setGlobalMeterProvider(meterProvider); + const meterProvider = createMeterProvider({ + readers: [createPeriodicExportingMetricReader({ + exporter: new OTLPMetricExporter(), + exportIntervalMillis: 1000 + })] + }); - meterProvider.addMetricReader(createPeriodicExportingMetricReader({ - exporter: new OTLPMetricExporter(), - exportIntervalMillis: 1000 - })); + metrics.setGlobalMeterProvider(meterProvider); meter = meterProvider.getMeter('example-exporter-collector') diff --git a/examples/otlp-exporter-node/metrics.js b/examples/otlp-exporter-node/metrics.js index b5ed0af74f3..1a17704499f 100644 --- a/examples/otlp-exporter-node/metrics.js +++ b/examples/otlp-exporter-node/metrics.js @@ -4,12 +4,11 @@ const { DiagConsoleLogger, DiagLogLevel, diag } = require('@opentelemetry/api'); const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-http'); // const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-grpc'); // const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-proto'); -// const { ConsoleMetricExporter } = require('@opentelemetry/sdk-metrics'); +// const { createConsoleMetricExporter } = require('@opentelemetry/sdk-metrics'); const { - ExponentialHistogramAggregation, - MeterProvider, - PeriodicExportingMetricReader, - View, + createMeterProvider, + createPeriodicExportingMetricReader, + AggregationType, } = require('@opentelemetry/sdk-metrics'); const { Resource } = require('@opentelemetry/resources'); const { @@ -25,30 +24,26 @@ const metricExporter = new OTLPMetricExporter({ // }, }); -// Define view for the exponential histogram metric -const expHistogramView = new View({ - aggregation: new ExponentialHistogramAggregation(), - // Note, the instrumentName is the same as the name that has been passed for - // the Meter#createHistogram function for exponentialHistogram. - instrumentName: 'test_exponential_histogram', -}); // Create an instance of the metric provider -const meterProvider = new MeterProvider({ +const meterProvider = createMeterProvider({ + readers: [createPeriodicExportingMetricReader({ + exporter: metricExporter, + // exporter: createConsoleMetricExporter(), + exportIntervalMillis: 1000, + })], resource: new Resource({ [SEMRESATTRS_SERVICE_NAME]: 'basic-metric-service', }), - views: [expHistogramView], + // Define view for the exponential histogram metric + views: [{ + aggregation: { type: AggregationType.EXPONENTIAL_HISTOGRAM }, + // Note, the instrumentName is the same as the name that has been passed for + // the Meter#createHistogram function for exponentialHistogram. + instrumentName: 'test_exponential_histogram', + }], }); -meterProvider.addMetricReader( - createPeriodicExportingMetricReader({ - exporter: metricExporter, - // exporter: new ConsoleMetricExporter(), - exportIntervalMillis: 1000, - }) -); - const meter = meterProvider.getMeter('example-exporter-collector'); const requestCounter = meter.createCounter('requests', { diff --git a/experimental/examples/opencensus-shim/setup.js b/experimental/examples/opencensus-shim/setup.js index 1bb277eddb4..6d7ef63a164 100644 --- a/experimental/examples/opencensus-shim/setup.js +++ b/experimental/examples/opencensus-shim/setup.js @@ -20,7 +20,7 @@ const { NodeTracerProvider, BatchSpanProcessor, } = require('@opentelemetry/sdk-trace-node'); -const { MeterProvider } = require('@opentelemetry/sdk-metrics'); +const { createMeterProvider } = require('@opentelemetry/sdk-metrics'); const { OTLPTraceExporter, } = require('@opentelemetry/exporter-trace-otlp-grpc'); @@ -56,17 +56,20 @@ module.exports = function setup(serviceName) { ); tracerProvider.register(); - const meterProvider = new MeterProvider({ resource }); - meterProvider.addMetricReader( - new PrometheusExporter({ - metricProducers: [ - new OpenCensusMetricProducer({ - openCensusMetricProducerManager: - oc.Metrics.getMetricProducerManager(), - }), - ], - }) - ); + const meterProvider = createMeterProvider({ + readers: [ + new PrometheusExporter({ + metricProducers: [ + new OpenCensusMetricProducer({ + openCensusMetricProducerManager: + oc.Metrics.getMetricProducerManager(), + }), + ], + }) + ], + resource + }); + metrics.setGlobalMeterProvider(meterProvider); // Start OpenCensus tracing diff --git a/experimental/examples/prometheus/index.js b/experimental/examples/prometheus/index.js index 8f8dd96836d..e1557737d8d 100644 --- a/experimental/examples/prometheus/index.js +++ b/experimental/examples/prometheus/index.js @@ -1,7 +1,7 @@ 'use strict'; const { DiagConsoleLogger, DiagLogLevel, diag } = require('@opentelemetry/api'); -const { MeterProvider } = require('@opentelemetry/sdk-metrics'); +const { MeterProvider, createMeterProvider} = require('@opentelemetry/sdk-metrics'); const { PrometheusExporter } = require('@opentelemetry/exporter-prometheus'); // Optional and only needed to see the internal diagnostic logging (during development) @@ -16,8 +16,9 @@ const exporter = new PrometheusExporter({}, () => { }); // Creates MeterProvider and installs the exporter as a MetricReader -const meterProvider = new MeterProvider(); -meterProvider.addMetricReader(exporter); +const meterProvider = createMeterProvider({ + readers: [exporter] +}); const meter = meterProvider.getMeter('example-prometheus'); // Creates metric instruments diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/README.md b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/README.md index 1a9de77c2f1..2f7f9af7c49 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/README.md +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/README.md @@ -27,7 +27,7 @@ The OTLPMetricsExporter in Node expects the URL to only be the hostname. It will options that work with trace also work with metrics. ```js -const { MeterProvider, PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics'); +const { createPeriodicExportingMetricReader, createMeterProvider } = require('@opentelemetry/sdk-metrics'); const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-grpc'); const collectorOptions = { // url is optional and can be omitted - default is http://localhost:4317 @@ -36,12 +36,14 @@ const collectorOptions = { }; const metricExporter = new OTLPMetricExporter(collectorOptions); -const meterProvider = new MeterProvider({}); - -meterProvider.addMetricReader(createPeriodicExportingMetricReader({ - exporter: metricExporter, - exportIntervalMillis: 1000, -})); +const meterProvider = createMeterProvider({ + readers: [ + createPeriodicExportingMetricReader({ + exporter: metricExporter, + exportIntervalMillis: 1000, + }) + ] +}); ['SIGINT', 'SIGTERM'].forEach(signal => { process.on(signal, () => meterProvider.shutdown().catch(console.error)); diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/README.md b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/README.md index 417f4b74f70..c2404e828ca 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/README.md +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/README.md @@ -26,7 +26,7 @@ the [Collector Trace Exporter for web and node][trace-exporter-url]. The OTLPMetricExporter in Web expects the endpoint to end in `/v1/metrics`. ```js -import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'; +import { createMeterProvider, createPeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'; import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'; const collectorOptions = { @@ -35,12 +35,14 @@ const collectorOptions = { concurrencyLimit: 1, // an optional limit on pending requests }; const metricExporter = new OTLPMetricExporter(collectorOptions); -const meterProvider = new MeterProvider({}); - -meterProvider.addMetricReader(createPeriodicExportingMetricReader({ - exporter: metricExporter, - exportIntervalMillis: 1000, -})); +const meterProvider = createMeterProvider({ + readers: [ + createPeriodicExportingMetricReader({ + exporter: metricExporter, + exportIntervalMillis: 1000, + }) + ] +}); // Now, start recording data const meter = meterProvider.getMeter('example-meter'); @@ -51,19 +53,21 @@ counter.add(10, { 'key': 'value' }); ## Metrics in Node ```js -const { MeterProvider, PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics'); +const { createMeterProvider, createPeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics'); const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-http'); const collectorOptions = { url: '', // url is optional and can be omitted - default is http://localhost:4318/v1/metrics concurrencyLimit: 1, // an optional limit on pending requests }; const metricExporter = new OTLPMetricExporter(collectorOptions); -const meterProvider = new MeterProvider({}); - -meterProvider.addMetricReader(createPeriodicExportingMetricReader({ - exporter: metricExporter, - exportIntervalMillis: 1000, -})); +const meterProvider = createMeterProvider({ + readers: [ + createPeriodicExportingMetricReader({ + exporter: metricExporter, + exportIntervalMillis: 1000, + }) + ] +}); // Now, start recording data const meter = meterProvider.getMeter('example-meter'); diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/README.md b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/README.md index c08fdb93212..de948a46b1c 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/README.md +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/README.md @@ -22,18 +22,20 @@ To see sample code and documentation for the traces exporter, visit the [Collect ## Metrics in Node - PROTO over http ```js -const { MeterProvider, PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics'); +const { createPeriodicExportingMetricReader, createMeterProvider } = require('@opentelemetry/sdk-metrics'); const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-proto'); const collectorOptions = { url: '', // url is optional and can be omitted - default is http://localhost:4318/v1/metrics }; const metricExporter = new OTLPMetricExporter(collectorOptions); -const meterProvider = new MeterProvider({}); - -meterProvider.addMetricReader(createPeriodicExportingMetricReader({ - exporter: metricExporter, - exportIntervalMillis: 1000, -})); +const meterProvider = createMeterProvider({ + readers: [ + createPeriodicExportingMetricReader({ + exporter: metricExporter, + exportIntervalMillis: 1000, + }) + ] +}); // Now, start recording data const meter = meterProvider.getMeter('example-meter'); diff --git a/experimental/packages/opentelemetry-exporter-prometheus/README.md b/experimental/packages/opentelemetry-exporter-prometheus/README.md index 83c6ac14d5b..aa004f728af 100644 --- a/experimental/packages/opentelemetry-exporter-prometheus/README.md +++ b/experimental/packages/opentelemetry-exporter-prometheus/README.md @@ -22,15 +22,17 @@ Create & register the exporter on your application. ```js const { PrometheusExporter } = require('@opentelemetry/exporter-prometheus'); -const { MeterProvider } = require('@opentelemetry/sdk-metrics'); +const { createMeterProvider } = require('@opentelemetry/sdk-metrics'); // Add your port and startServer to the Prometheus options const options = {port: 9464}; const exporter = new PrometheusExporter(options); // Creates MeterProvider and installs the exporter as a MetricReader -const meterProvider = new MeterProvider(); -meterProvider.addMetricReader(exporter); +const meterProvider = createMeterProvider({ + readers: [exporter] +}); + const meter = meterProvider.getMeter('example-prometheus'); // Now, start recording data diff --git a/packages/sdk-metrics/README.md b/packages/sdk-metrics/README.md index af8f7eb0d56..8a3fa00a852 100644 --- a/packages/sdk-metrics/README.md +++ b/packages/sdk-metrics/README.md @@ -22,12 +22,12 @@ The basic setup of the SDK can be seen as followings: ```js const opentelemetry = require('@opentelemetry/api'); -const { MeterProvider } = require('@opentelemetry/sdk-metrics'); +const { createMeterProvider } = require('@opentelemetry/sdk-metrics'); // To create an instrument, you first need to initialize the Meter provider. // NOTE: The default OpenTelemetry meter provider does not record any metric instruments. // Registering a working meter provider allows the API methods to record instruments. -opentelemetry.metrics.setGlobalMeterProvider(new MeterProvider()); +opentelemetry.metrics.setGlobalMeterProvider(createMeterProvider()); // To record a metric event, we used the global singleton meter to create an instrument. const counter = opentelemetry.metrics.getMeter('default').createCounter('foo'); @@ -61,12 +61,12 @@ async function batchObservableCallback(batchObservableResult) { Views can be registered when instantiating a `MeterProvider`: ```js -const meterProvider = new MeterProvider({ +const meterProvider = new createMeterProvider({ views: [ // override the bucket boundaries on `my.histogram` to [0, 50, 100] - new View({ aggregation: new ExplicitBucketHistogramAggregation([0, 50, 100]), instrumentName: 'my.histogram'}), + { aggregation: { type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM, boundaries: [0, 50, 100] }, instrumentName: 'my.histogram'}, // rename 'my.counter' to 'my.renamed.counter' - new View({ name: 'my.renamed.counter', instrumentName: 'my.counter'}) + { name: 'my.renamed.counter', instrumentName: 'my.counter'}, ] }) ``` From 89c61165f247b10dce30b7955f9a5a4c2cd6f0f7 Mon Sep 17 00:00:00 2001 From: Marc Pichler Date: Tue, 12 Nov 2024 11:38:06 +0100 Subject: [PATCH 5/6] refactor(sdk-metrics): rename MeterProvider -> MeterProviderImpl --- packages/sdk-metrics/src/MeterProvider.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/sdk-metrics/src/MeterProvider.ts b/packages/sdk-metrics/src/MeterProvider.ts index 0a1ea216c1f..fb37a03651b 100644 --- a/packages/sdk-metrics/src/MeterProvider.ts +++ b/packages/sdk-metrics/src/MeterProvider.ts @@ -16,7 +16,7 @@ import { diag, - MeterProvider as IMeterProvider, + MeterProvider, Meter as IMeter, MeterOptions, createNoopMeter, @@ -41,7 +41,7 @@ export interface MeterProviderOptions { /** * This class implements the {@link MeterProvider} interface. */ -class MeterProvider implements IMeterProvider { +class MeterProviderImpl implements MeterProvider { private _sharedState: MeterProviderSharedState; private _shutdown = false; @@ -125,9 +125,9 @@ class MeterProvider implements IMeterProvider { export function createMeterProvider( options?: MeterProviderOptions -): IMeterProvider & { +): MeterProvider & { shutdown(options?: ShutdownOptions): Promise; forceFlush(options?: ForceFlushOptions): Promise; } { - return new MeterProvider(options); + return new MeterProviderImpl(options); } From a762455fbe986218e27b5a6739aa6fe4757c24e7 Mon Sep 17 00:00:00 2001 From: Marc Pichler Date: Tue, 12 Nov 2024 11:42:22 +0100 Subject: [PATCH 6/6] docs(sdk-metrics): clarify that View is now internal --- packages/sdk-metrics/src/view/View.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sdk-metrics/src/view/View.ts b/packages/sdk-metrics/src/view/View.ts index 00227e918ba..8ee0c30ab80 100644 --- a/packages/sdk-metrics/src/view/View.ts +++ b/packages/sdk-metrics/src/view/View.ts @@ -158,7 +158,7 @@ function validateViewOptions(viewOptions: ViewOptions) { } /** - * Can be passed to a {@link MeterProvider} to select instruments and alter their metric stream. + * Internal class to select instruments and alter their metric stream. */ export class View { readonly name?: string;