diff --git a/.github/workflows/peer-api.yaml b/.github/workflows/peer-api.yaml index b767ff6813..80299fc879 100644 --- a/.github/workflows/peer-api.yaml +++ b/.github/workflows/peer-api.yaml @@ -18,7 +18,7 @@ jobs: - name: Install lerna run: npm install -g lerna - - name: Install semver + - name: Install semver run: npm install semver - name: Check API dependency semantics (stable) @@ -27,4 +27,4 @@ jobs: - name: Check API dependency semantics (experimental) working-directory: experimental - run: lerna exec --ignore propagation-validation-server --ignore @opentelemetry/selenium-tests --ignore @opentelemetry/api-metrics-wip --ignore @opentelemetry/otlp-transformer "node ../../../scripts/peer-api-check.js" + run: lerna exec --ignore propagation-validation-server --ignore @opentelemetry/selenium-tests --ignore @opentelemetry/api-metrics --ignore @opentelemetry/otlp-transformer "node ../../../scripts/peer-api-check.js" diff --git a/.gitmodules b/.gitmodules index 4a85c71995..817382141b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,7 @@ -[submodule "packages/exporter-trace-otlp-proto/protos"] +[submodule "experimental/packages/exporter-trace-otlp-proto/protos"] path = experimental/packages/exporter-trace-otlp-proto/protos url = https://github.com/open-telemetry/opentelemetry-proto.git -[submodule "packages/exporter-trace-otlp-grpc/protos"] +[submodule "experimental/packages/exporter-trace-otlp-grpc/protos"] path = experimental/packages/exporter-trace-otlp-grpc/protos url = https://github.com/open-telemetry/opentelemetry-proto.git [submodule "experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/protos"] diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 7a4bfd0d1f..3d81203862 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -9,6 +9,13 @@ All notable changes to experimental packages in this project will be documented * feat(sdk-metrics-base): update metric exporter interfaces #2707 @srikanthccv * feat(api-metrics): remove observable types #2687 @legendecas * fix(otlp-http-exporter): remove content length header #2879 @svetlanabrennan +* feat(experimental-packages): Update packages to latest SDK Version. #2871 @pichlermarc + * removed the -wip suffix from api-metrics and metrics-sdk-base. + * updated dependencies to stable packages to `1.1.1` for all "experimental" packages. + * updated Metrics Exporters to the latest Metrics SDK (`exporter-metrics-otlp-grpc`, `exporter-metrics-otlp-http`, `exporter-metrics-otlp-proto`) + * updated `opentelemetry-sdk-node` to the latest Metrics SDK. + * updated `otlp-transformer` to the latest Metrics SDK. + * updated all `instrumentation-*` packages to use local implementations of `parseUrl()` due to #2884 ### :rocket: (Enhancement) diff --git a/experimental/backwards-compatability/node10/package.json b/experimental/backwards-compatability/node10/package.json index b2ea0dc3c7..db101a9e63 100644 --- a/experimental/backwards-compatability/node10/package.json +++ b/experimental/backwards-compatability/node10/package.json @@ -9,7 +9,7 @@ }, "dependencies": { "@opentelemetry/sdk-node": "0.27.0", - "@opentelemetry/sdk-trace-base": "1.0.1" + "@opentelemetry/sdk-trace-base": "1.1.1" }, "devDependencies": { "@types/node": "10.17.60", diff --git a/experimental/backwards-compatability/node12/package.json b/experimental/backwards-compatability/node12/package.json index 1409706f4b..ed20c03d8f 100644 --- a/experimental/backwards-compatability/node12/package.json +++ b/experimental/backwards-compatability/node12/package.json @@ -9,7 +9,7 @@ }, "dependencies": { "@opentelemetry/sdk-node": "0.27.0", - "@opentelemetry/sdk-trace-base": "1.0.1" + "@opentelemetry/sdk-trace-base": "1.1.1" }, "devDependencies": { "@types/node": "12.20.37", diff --git a/experimental/backwards-compatability/node8/package.json b/experimental/backwards-compatability/node8/package.json index 5b180a3ed2..1cb27ff79c 100644 --- a/experimental/backwards-compatability/node8/package.json +++ b/experimental/backwards-compatability/node8/package.json @@ -9,7 +9,7 @@ }, "dependencies": { "@opentelemetry/sdk-node": "0.27.0", - "@opentelemetry/sdk-trace-base": "1.0.1" + "@opentelemetry/sdk-trace-base": "1.1.1" }, "devDependencies": { "@types/node": "8.10.66", diff --git a/experimental/packages/exporter-trace-otlp-grpc/package.json b/experimental/packages/exporter-trace-otlp-grpc/package.json index 3e62fe6e3c..b8173525af 100644 --- a/experimental/packages/exporter-trace-otlp-grpc/package.json +++ b/experimental/packages/exporter-trace-otlp-grpc/package.json @@ -49,7 +49,7 @@ }, "devDependencies": { "@babel/core": "7.16.0", - "@opentelemetry/api": "^1.0.3", + "@opentelemetry/api": "^1.1.0", "@types/mocha": "8.2.3", "@types/node": "14.17.33", "@types/sinon": "10.0.6", @@ -64,7 +64,7 @@ "typescript": "4.4.4" }, "peerDependencies": { - "@opentelemetry/api": "^1.0.3" + "@opentelemetry/api": "^1.1.0" }, "dependencies": { "@opentelemetry/core": "1.1.1", diff --git a/experimental/packages/exporter-trace-otlp-http/package.json b/experimental/packages/exporter-trace-otlp-http/package.json index 151ee9fd39..cba5e84058 100644 --- a/experimental/packages/exporter-trace-otlp-http/package.json +++ b/experimental/packages/exporter-trace-otlp-http/package.json @@ -62,7 +62,7 @@ }, "devDependencies": { "@babel/core": "7.16.0", - "@opentelemetry/api": "^1.0.3", + "@opentelemetry/api": "^1.1.0", "@types/mocha": "8.2.3", "@types/node": "14.17.33", "@types/sinon": "10.0.6", @@ -89,7 +89,7 @@ "webpack-merge": "5.8.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.0.3" + "@opentelemetry/api": "^1.1.0" }, "dependencies": { "@opentelemetry/core": "1.1.1", diff --git a/experimental/packages/exporter-trace-otlp-proto/package.json b/experimental/packages/exporter-trace-otlp-proto/package.json index be9403b780..eddf0c405a 100644 --- a/experimental/packages/exporter-trace-otlp-proto/package.json +++ b/experimental/packages/exporter-trace-otlp-proto/package.json @@ -49,7 +49,7 @@ }, "devDependencies": { "@babel/core": "7.16.0", - "@opentelemetry/api": "^1.0.3", + "@opentelemetry/api": "^1.1.0", "@types/mocha": "8.2.3", "@types/node": "14.17.33", "@types/sinon": "10.0.6", @@ -64,7 +64,7 @@ "typescript": "4.4.4" }, "peerDependencies": { - "@opentelemetry/api": "^1.0.3" + "@opentelemetry/api": "^1.1.0" }, "dependencies": { "@opentelemetry/core": "1.1.1", diff --git a/experimental/packages/opentelemetry-api-metrics/package.json b/experimental/packages/opentelemetry-api-metrics/package.json index 7dacc4a22c..514316c39e 100644 --- a/experimental/packages/opentelemetry-api-metrics/package.json +++ b/experimental/packages/opentelemetry-api-metrics/package.json @@ -1,5 +1,5 @@ { - "name": "@opentelemetry/api-metrics-wip", + "name": "@opentelemetry/api-metrics", "version": "0.27.0", "private": true, "description": "Public metrics API for OpenTelemetry", diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/package.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/package.json index 46ce3f1d5b..7d5bdfb287 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/package.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/package.json @@ -65,16 +65,16 @@ "typescript": "4.4.4" }, "peerDependencies": { - "@opentelemetry/api": "^1.0.3" + "@opentelemetry/api": "^1.1.0" }, "dependencies": { "@grpc/grpc-js": "^1.5.9", "@grpc/proto-loader": "^0.6.9", - "@opentelemetry/core": "1.0.1", + "@opentelemetry/core": "1.1.1", "@opentelemetry/exporter-metrics-otlp-http": "0.27.0", "@opentelemetry/exporter-trace-otlp-grpc": "0.27.0", "@opentelemetry/exporter-trace-otlp-http": "0.27.0", - "@opentelemetry/resources": "1.0.1", + "@opentelemetry/resources": "1.1.1", "@opentelemetry/sdk-metrics-base": "0.27.0" } } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/src/OTLPMetricExporter.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/src/OTLPMetricExporter.ts index 3311eef290..5ec71ef7ed 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/src/OTLPMetricExporter.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/src/OTLPMetricExporter.ts @@ -15,8 +15,13 @@ */ import { otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; -import { toOTLPExportMetricServiceRequest } from '@opentelemetry/exporter-metrics-otlp-http'; -import { MetricRecord, MetricExporter } from '@opentelemetry/sdk-metrics-base'; +import { + defaultExporterTemporality, + defaultOptions, + OTLPMetricExporterBase, OTLPMetricExporterOptions, + toOTLPExportMetricServiceRequest +} from '@opentelemetry/exporter-metrics-otlp-http'; +import { AggregationTemporality, ResourceMetrics } from '@opentelemetry/sdk-metrics-base'; import { OTLPExporterConfigNode, OTLPExporterNodeBase, @@ -28,52 +33,53 @@ import { Metadata } from '@grpc/grpc-js'; const DEFAULT_COLLECTOR_URL = 'localhost:4317'; -/** - * OTLP Metric Exporter for Node - */ -export class OTLPMetricExporter - extends OTLPExporterNodeBase< - MetricRecord, - otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest - > - implements MetricExporter { - // Converts time to nanoseconds - protected readonly _startTime = new Date().getTime() * 1000000; - constructor(config: OTLPExporterConfigNode = {}) { +class OTLPMetricExporterProxy extends OTLPExporterNodeBase { + protected readonly _aggregationTemporality: AggregationTemporality; + + constructor(config: OTLPExporterConfigNode & OTLPMetricExporterOptions= defaultOptions) { super(config); - const headers = baggageUtils.parseKeyPairsIntoRecord(getEnv().OTEL_EXPORTER_OTLP_METRICS_HEADERS); this.metadata ||= new Metadata(); + const headers = baggageUtils.parseKeyPairsIntoRecord(getEnv().OTEL_EXPORTER_OTLP_METRICS_HEADERS); for (const [k, v] of Object.entries(headers)) { this.metadata.set(k, v); } + this._aggregationTemporality = config.aggregationTemporality ?? defaultExporterTemporality; } - convert( - metrics: MetricRecord[] - ): otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest { - return toOTLPExportMetricServiceRequest( - metrics, - this._startTime, - this - ); + getServiceProtoPath(): string { + return 'opentelemetry/proto/collector/metrics/v1/metrics_service.proto'; + } + + getServiceClientType(): ServiceClientType { + return ServiceClientType.METRICS; } getDefaultUrl(config: OTLPExporterConfigNode): string { return typeof config.url === 'string' ? validateAndNormalizeUrl(config.url) : getEnv().OTEL_EXPORTER_OTLP_METRICS_ENDPOINT.length > 0 - ? validateAndNormalizeUrl(getEnv().OTEL_EXPORTER_OTLP_METRICS_ENDPOINT) - : getEnv().OTEL_EXPORTER_OTLP_ENDPOINT.length > 0 - ? validateAndNormalizeUrl(getEnv().OTEL_EXPORTER_OTLP_ENDPOINT) - : DEFAULT_COLLECTOR_URL; + ? validateAndNormalizeUrl(getEnv().OTEL_EXPORTER_OTLP_METRICS_ENDPOINT) + : getEnv().OTEL_EXPORTER_OTLP_ENDPOINT.length > 0 + ? validateAndNormalizeUrl(getEnv().OTEL_EXPORTER_OTLP_ENDPOINT) + : DEFAULT_COLLECTOR_URL; } - getServiceClientType(): ServiceClientType { - return ServiceClientType.METRICS; + convert(metrics: ResourceMetrics[]): otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest { + return toOTLPExportMetricServiceRequest( + metrics[0], + this._aggregationTemporality, + this + ); } +} - getServiceProtoPath(): string { - return 'opentelemetry/proto/collector/metrics/v1/metrics_service.proto'; +/** + * OTLP-gRPC metric exporter + */ +export class OTLPMetricExporter extends OTLPMetricExporterBase{ + constructor(config: OTLPExporterConfigNode & OTLPMetricExporterOptions = defaultOptions) { + super(new OTLPMetricExporterProxy(config), config); } } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/OTLPMetricExporter.test.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/OTLPMetricExporter.test.ts index 45f1b9fd31..fff613ec84 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/OTLPMetricExporter.test.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/OTLPMetricExporter.test.ts @@ -15,14 +15,8 @@ */ import * as protoLoader from '@grpc/proto-loader'; -import { - Counter, - ObservableGauge, - Histogram, -} from '@opentelemetry/api-metrics'; import { diag, DiagLogger } from '@opentelemetry/api'; import { otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; -import * as metrics from '@opentelemetry/sdk-metrics-base'; import * as assert from 'assert'; import * as fs from 'fs'; import * as grpc from '@grpc/grpc-js'; @@ -30,15 +24,17 @@ import * as path from 'path'; import * as sinon from 'sinon'; import { OTLPMetricExporter } from '../src'; import { + collect, ensureExportedCounterIsCorrect, - ensureExportedObservableGaugeIsCorrect, ensureExportedHistogramIsCorrect, + ensureExportedObservableGaugeIsCorrect, ensureMetadataIsCorrect, ensureResourceIsCorrect, mockCounter, - mockObservableGauge, mockHistogram, + mockObservableGauge, setUp, shutdown, } from './metricsHelper'; +import { AggregationTemporality, ResourceMetrics } from '@opentelemetry/sdk-metrics-base'; const metricsServiceProtoPath = 'opentelemetry/proto/collector/metrics/v1/metrics_service.proto'; @@ -63,7 +59,7 @@ const testOTLPMetricExporter = (params: TestParams) => let exportedData: | otlpTypes.opentelemetryProto.metrics.v1.ResourceMetrics[] | undefined; - let metrics: metrics.MetricRecord[]; + let metrics: ResourceMetrics; let reqMetadata: grpc.Metadata | undefined; before(done => { @@ -100,14 +96,14 @@ const testOTLPMetricExporter = (params: TestParams) => ); const credentials = params.useTLS ? grpc.ServerCredentials.createSsl( - fs.readFileSync('./test/certs/ca.crt'), - [ - { - cert_chain: fs.readFileSync('./test/certs/server.crt'), - private_key: fs.readFileSync('./test/certs/server.key'), - }, - ] - ) + fs.readFileSync('./test/certs/ca.crt'), + [ + { + cert_chain: fs.readFileSync('./test/certs/server.crt'), + private_key: fs.readFileSync('./test/certs/server.key'), + }, + ] + ) : grpc.ServerCredentials.createInsecure(); server.bindAsync(address, credentials, () => { server.start(); @@ -123,41 +119,36 @@ const testOTLPMetricExporter = (params: TestParams) => beforeEach(async () => { const credentials = params.useTLS ? grpc.credentials.createSsl( - fs.readFileSync('./test/certs/ca.crt'), - fs.readFileSync('./test/certs/client.key'), - fs.readFileSync('./test/certs/client.crt') - ) + fs.readFileSync('./test/certs/ca.crt'), + fs.readFileSync('./test/certs/client.key'), + fs.readFileSync('./test/certs/client.crt') + ) : undefined; collectorExporter = new OTLPMetricExporter({ url: 'grpcs://' + address, credentials, metadata: params.metadata, + aggregationTemporality: AggregationTemporality.CUMULATIVE }); - // Overwrites the start time to make tests consistent - Object.defineProperty(collectorExporter, '_startTime', { - value: 1592602232694000000, - }); - metrics = []; - const counter: metrics.Metric & - Counter = mockCounter(); - const observableGauge: metrics.Metric & - ObservableGauge = mockObservableGauge(observableResult => { + + setUp(); + + const counter = mockCounter(); + mockObservableGauge(observableResult => { observableResult.observe(3, {}); observableResult.observe(6, {}); }); - const histogram: metrics.Metric & - Histogram = mockHistogram(); + const histogram = mockHistogram(); counter.add(1); histogram.record(7); histogram.record(14); - metrics.push((await counter.getMetricRecord())[0]); - metrics.push((await observableGauge.getMetricRecord())[0]); - metrics.push((await histogram.getMetricRecord())[0]); + metrics = await collect(); }); - afterEach(() => { + afterEach(async () => { + await shutdown(); exportedData = undefined; reqMetadata = undefined; sinon.restore(); @@ -169,7 +160,8 @@ const testOTLPMetricExporter = (params: TestParams) => beforeEach(() => { // Need to stub/spy on the underlying logger as the "diag" instance is global warnStub = sinon.stub(); - const nop = () => {}; + const nop = () => { + }; const diagLogger: DiagLogger = { debug: nop, error: nop, @@ -190,13 +182,15 @@ const testOTLPMetricExporter = (params: TestParams) => headers: { foo: 'bar', }, + aggregationTemporality: AggregationTemporality.CUMULATIVE }); const args = warnStub.args[0]; assert.strictEqual(args[0], 'Headers cannot be set when using grpc'); }); it('should warn about path in url', () => { collectorExporter = new OTLPMetricExporter({ - url: `http://${address}/v1/metrics` + url: `http://${address}/v1/metrics`, + aggregationTemporality: AggregationTemporality.CUMULATIVE }); const args = warnStub.args[0]; assert.strictEqual( @@ -226,15 +220,18 @@ const testOTLPMetricExporter = (params: TestParams) => exportedData[0].instrumentationLibraryMetrics[0].metrics[2]; ensureExportedCounterIsCorrect( counter, - counter.intSum?.dataPoints[0].timeUnixNano + counter.intSum?.dataPoints[0].timeUnixNano, + counter.intSum?.dataPoints[0].startTimeUnixNano ); ensureExportedObservableGaugeIsCorrect( observableGauge, - observableGauge.doubleGauge?.dataPoints[0].timeUnixNano + observableGauge.doubleGauge?.dataPoints[0].timeUnixNano, + observableGauge.doubleGauge?.dataPoints[0].startTimeUnixNano ); ensureExportedHistogramIsCorrect( histogram, histogram.intHistogram?.dataPoints[0].timeUnixNano, + histogram.intHistogram?.dataPoints[0].startTimeUnixNano, [0, 100], ['0', '2', '0'] ); @@ -257,17 +254,20 @@ const testOTLPMetricExporter = (params: TestParams) => describe('OTLPMetricExporter - node (getDefaultUrl)', () => { it('should default to localhost', done => { - const collectorExporter = new OTLPMetricExporter({}); + const collectorExporter = new OTLPMetricExporter(); setTimeout(() => { - assert.strictEqual(collectorExporter['url'], 'localhost:4317'); + assert.strictEqual(collectorExporter._otlpExporter.url, 'localhost:4317'); done(); }); }); it('should keep the URL if included', done => { const url = 'http://foo.bar.com'; - const collectorExporter = new OTLPMetricExporter({ url }); + const collectorExporter = new OTLPMetricExporter({ + url, + aggregationTemporality: AggregationTemporality.CUMULATIVE + }); setTimeout(() => { - assert.strictEqual(collectorExporter['url'], 'foo.bar.com'); + assert.strictEqual(collectorExporter._otlpExporter.url, 'foo.bar.com'); done(); }); }); @@ -279,7 +279,7 @@ describe('when configuring via environment', () => { envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar'; const collectorExporter = new OTLPMetricExporter(); assert.strictEqual( - collectorExporter.url, + collectorExporter._otlpExporter.url, 'foo.bar' ); envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; @@ -289,7 +289,7 @@ describe('when configuring via environment', () => { envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = 'http://foo.metrics'; const collectorExporter = new OTLPMetricExporter(); assert.strictEqual( - collectorExporter.url, + collectorExporter._otlpExporter.url, 'foo.metrics' ); envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; @@ -298,7 +298,7 @@ describe('when configuring via environment', () => { it('should use headers defined via env', () => { envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar'; const collectorExporter = new OTLPMetricExporter(); - assert.deepStrictEqual(collectorExporter.metadata?.get('foo'), ['bar']); + assert.deepStrictEqual(collectorExporter._otlpExporter.metadata?.get('foo'), ['bar']); envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; }); it('should override global headers config with signal headers defined via env', () => { @@ -307,15 +307,21 @@ describe('when configuring via environment', () => { metadata.set('goo', 'lol'); envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=jar,bar=foo'; envSource.OTEL_EXPORTER_OTLP_METRICS_HEADERS = 'foo=boo'; - const collectorExporter = new OTLPMetricExporter({ metadata }); - assert.deepStrictEqual(collectorExporter.metadata?.get('foo'), ['boo']); - assert.deepStrictEqual(collectorExporter.metadata?.get('bar'), ['foo']); - assert.deepStrictEqual(collectorExporter.metadata?.get('goo'), ['lol']); + const collectorExporter = new OTLPMetricExporter({ + metadata, + aggregationTemporality: AggregationTemporality.CUMULATIVE + }); + assert.deepStrictEqual(collectorExporter._otlpExporter.metadata?.get('foo'), ['boo']); + assert.deepStrictEqual(collectorExporter._otlpExporter.metadata?.get('bar'), ['foo']); + assert.deepStrictEqual(collectorExporter._otlpExporter.metadata?.get('goo'), ['lol']); envSource.OTEL_EXPORTER_OTLP_METRICS_HEADERS = ''; envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; }); }); -testOTLPMetricExporter({ useTLS: true }); -testOTLPMetricExporter({ useTLS: false }); -testOTLPMetricExporter({ metadata }); +describe('', () => { + testOTLPMetricExporter({ useTLS: true }); + testOTLPMetricExporter({ useTLS: false }); + testOTLPMetricExporter({ metadata }); +}); + 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 7fd582968d..fcb0b4db50 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/metricsHelper.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/metricsHelper.ts @@ -14,76 +14,84 @@ * limitations under the License. */ -import { - Counter, - ObservableResult, - ObservableGauge, - Histogram, - ValueType, -} from '@opentelemetry/api-metrics'; +import { Counter, Histogram, ObservableResult, ValueType } from '@opentelemetry/api-metrics'; import { otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; -import * as metrics from '@opentelemetry/sdk-metrics-base'; import { Resource } from '@opentelemetry/resources'; import * as assert from 'assert'; import * as grpc from '@grpc/grpc-js'; import { VERSION } from '@opentelemetry/core'; +import { ExplicitBucketHistogramAggregation, MeterProvider, MetricReader } from '@opentelemetry/sdk-metrics-base'; -const meterProvider = new metrics.MeterProvider({ - interval: 30000, - resource: new Resource({ - service: 'ui', - version: 1, - cost: 112.12, - }), -}); +export class TestMetricReader extends MetricReader { + protected onForceFlush(): Promise { + return Promise.resolve(undefined); + } -const meter = meterProvider.getMeter('default', '0.0.1'); + protected onShutdown(): Promise { + return Promise.resolve(undefined); + } +} + +const testResource = Resource.default().merge(new Resource({ + service: 'ui', + version: 1, + cost: 112.12, +})); + +let meterProvider = new MeterProvider({ resource: testResource }); + +let reader = new TestMetricReader(); +meterProvider.addMetricReader(reader); + +let meter = meterProvider.getMeter('default', '0.0.1'); + +export async function collect() { + return (await reader.collect())!; +} + +export function setUp() { + meterProvider = new MeterProvider({ resource: testResource }); + reader = new TestMetricReader(); + meterProvider.addMetricReader( + reader + ); + meter = meterProvider.getMeter('default', '0.0.1'); +} + +export async function shutdown() { + await meterProvider.shutdown(); +} -export function mockCounter(): metrics.Metric & Counter { +export function mockCounter(): Counter { const name = 'int-counter'; - const metric = - meter['_metrics'].get(name) || - meter.createCounter(name, { - description: 'sample counter description', - valueType: ValueType.INT, - }); - metric.clear(); - metric.bind({}); - return metric; + return meter.createCounter(name, { + description: 'sample counter description', + valueType: ValueType.INT, + }); } export function mockObservableGauge( callback: (observableResult: ObservableResult) => void -): metrics.Metric & ObservableGauge { +): void { const name = 'double-observable-gauge'; - const metric = - meter['_metrics'].get(name) || - meter.createObservableGauge( - name, - { - description: 'sample observable gauge description', - valueType: ValueType.DOUBLE, - }, - callback - ); - metric.clear(); - metric.bind({}); - return metric; + return meter.createObservableGauge( + name, + callback, + { + description: 'sample observable gauge description', + valueType: ValueType.DOUBLE, + }, + ); } -export function mockHistogram(): metrics.Metric & - Histogram { +export function mockHistogram(): Histogram { const name = 'int-histogram'; - const metric = - meter['_metrics'].get(name) || - meter.createHistogram(name, { - description: 'sample histogram description', - valueType: ValueType.INT, - boundaries: [0, 100], - }); - metric.clear(); - metric.bind({}); - return metric; + meterProvider.addView({ aggregation: new ExplicitBucketHistogramAggregation([0, 100]) }); + + return meter.createHistogram(name, { + description: 'sample histogram description', + valueType: ValueType.INT, + }); } export function ensureExportedAttributesAreCorrect( @@ -106,7 +114,8 @@ export function ensureExportedAttributesAreCorrect( export function ensureExportedCounterIsCorrect( metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, - time?: number + time?: number, + startTime?: number ) { assert.deepStrictEqual(metric, { name: 'int-counter', @@ -119,7 +128,7 @@ export function ensureExportedCounterIsCorrect( labels: [], exemplars: [], value: '1', - startTimeUnixNano: '1592602232694000128', + startTimeUnixNano: String(startTime), timeUnixNano: String(time), }, ], @@ -131,7 +140,8 @@ export function ensureExportedCounterIsCorrect( export function ensureExportedObservableGaugeIsCorrect( metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, - time?: number + time?: number, + startTime?: number ) { assert.deepStrictEqual(metric, { name: 'double-observable-gauge', @@ -144,7 +154,7 @@ export function ensureExportedObservableGaugeIsCorrect( labels: [], exemplars: [], value: 6, - startTimeUnixNano: '1592602232694000128', + startTimeUnixNano: String(startTime), timeUnixNano: String(time), }, ], @@ -155,6 +165,7 @@ export function ensureExportedObservableGaugeIsCorrect( export function ensureExportedHistogramIsCorrect( metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, time?: number, + startTime?: number, explicitBounds: number[] = [Infinity], bucketCounts: string[] = ['2', '0'] ) { @@ -170,7 +181,7 @@ export function ensureExportedHistogramIsCorrect( exemplars: [], sum: '21', count: '2', - startTimeUnixNano: '1592602232694000128', + startTimeUnixNano: String(startTime), timeUnixNano: String(time), bucketCounts, explicitBounds, diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/tsconfig.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/tsconfig.json index caca784fe2..0c98be5092 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/tsconfig.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/tsconfig.json @@ -15,8 +15,14 @@ { "path": "../exporter-trace-otlp-http" }, + { + "path": "../opentelemetry-api-metrics" + }, { "path": "../opentelemetry-exporter-metrics-otlp-http" + }, + { + "path": "../opentelemetry-sdk-metrics-base" } ] } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/package.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/package.json index 450fa6ee68..38bb2c4ff1 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/package.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/package.json @@ -89,13 +89,13 @@ "webpack-merge": "5.8.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.0.3" + "@opentelemetry/api": "^1.1.0" }, "dependencies": { "@opentelemetry/api-metrics": "0.27.0", - "@opentelemetry/core": "1.0.1", + "@opentelemetry/core": "1.1.1", "@opentelemetry/exporter-trace-otlp-http": "0.27.0", - "@opentelemetry/resources": "1.0.1", + "@opentelemetry/resources": "1.1.1", "@opentelemetry/sdk-metrics-base": "0.27.0" } } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/OTLPMetricExporterBase.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/OTLPMetricExporterBase.ts new file mode 100644 index 0000000000..39b238c39b --- /dev/null +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/OTLPMetricExporterBase.ts @@ -0,0 +1,51 @@ +/* + * 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 { ExportResult } from '@opentelemetry/core'; +import { AggregationTemporality, PushMetricExporter, ResourceMetrics } from '@opentelemetry/sdk-metrics-base'; +import { OTLPExporterBase, otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; +import { defaultOptions, OTLPMetricExporterOptions } from './OTLPMetricExporterOptions'; + +export class OTLPMetricExporterBase> + implements PushMetricExporter { + public _otlpExporter: T; + protected _preferredAggregationTemporality: AggregationTemporality; + + constructor(exporter: T, + config: OTLPMetricExporterOptions = defaultOptions) { + this._otlpExporter = exporter; + this._preferredAggregationTemporality = config.aggregationTemporality ?? AggregationTemporality.CUMULATIVE; + } + + export(metrics: ResourceMetrics, resultCallback: (result: ExportResult) => void): void { + this._otlpExporter.export([metrics], resultCallback); + } + + async shutdown(): Promise { + await this._otlpExporter.shutdown(); + } + + forceFlush(): Promise { + return Promise.resolve(); + } + + getPreferredAggregationTemporality(): AggregationTemporality { + return this._preferredAggregationTemporality; + } + +} diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/OTLPMetricExporterOptions.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/OTLPMetricExporterOptions.ts new file mode 100644 index 0000000000..ee71cfc790 --- /dev/null +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/OTLPMetricExporterOptions.ts @@ -0,0 +1,23 @@ +/* + * 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 { otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; +import { AggregationTemporality } from '@opentelemetry/sdk-metrics-base'; + +export interface OTLPMetricExporterOptions extends otlpTypes.OTLPExporterConfigBase { + aggregationTemporality?: AggregationTemporality +} +export const defaultExporterTemporality = AggregationTemporality.CUMULATIVE; +export const defaultOptions = {aggregationTemporality: defaultExporterTemporality}; diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/index.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/index.ts index f4e0549034..c1deb8d977 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/index.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/index.ts @@ -15,4 +15,6 @@ */ export * from './platform'; +export * from './OTLPMetricExporterOptions'; +export * from './OTLPMetricExporterBase'; export { toOTLPExportMetricServiceRequest } from './transformMetrics'; diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/browser/OTLPMetricExporter.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/browser/OTLPMetricExporter.ts index 3eb49d9665..d6cf15ad08 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/browser/OTLPMetricExporter.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/browser/OTLPMetricExporter.ts @@ -14,27 +14,25 @@ * limitations under the License. */ -import { MetricRecord, MetricExporter } from '@opentelemetry/sdk-metrics-base'; -import { OTLPExporterBrowserBase, otlpTypes, appendResourcePathToUrlIfNotPresent } from '@opentelemetry/exporter-trace-otlp-http'; +import { AggregationTemporality, ResourceMetrics } from '@opentelemetry/sdk-metrics-base'; +import { + appendResourcePathToUrlIfNotPresent, + OTLPExporterBrowserBase, + otlpTypes +} from '@opentelemetry/exporter-trace-otlp-http'; import { toOTLPExportMetricServiceRequest } from '../../transformMetrics'; -import { getEnv, baggageUtils } from '@opentelemetry/core'; +import { baggageUtils, getEnv } from '@opentelemetry/core'; +import { defaultExporterTemporality, defaultOptions, OTLPMetricExporterOptions } from '../../OTLPMetricExporterOptions'; +import { OTLPMetricExporterBase } from '../../OTLPMetricExporterBase'; const DEFAULT_COLLECTOR_RESOURCE_PATH = '/v1/metrics'; -const DEFAULT_COLLECTOR_URL=`http://localhost:4318${DEFAULT_COLLECTOR_RESOURCE_PATH}`; +const DEFAULT_COLLECTOR_URL = `http://localhost:4318${DEFAULT_COLLECTOR_RESOURCE_PATH}`; -/** - * Collector Metric Exporter for Web - */ -export class OTLPMetricExporter - extends OTLPExporterBrowserBase< - MetricRecord, - otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest - > - implements MetricExporter { - // Converts time to nanoseconds - private readonly _startTime = new Date().getTime() * 1000000; +class OTLPExporterBrowserProxy extends OTLPExporterBrowserBase { + protected readonly _aggregationTemporality: AggregationTemporality; - constructor(config: otlpTypes.OTLPExporterConfigBase = {}) { + constructor(config: OTLPMetricExporterOptions & otlpTypes.OTLPExporterConfigBase = defaultOptions) { super(config); this._headers = Object.assign( this._headers, @@ -42,25 +40,35 @@ export class OTLPMetricExporter getEnv().OTEL_EXPORTER_OTLP_METRICS_HEADERS ) ); + this._aggregationTemporality = config.aggregationTemporality ?? defaultExporterTemporality; + } + + getDefaultUrl(config: otlpTypes.OTLPExporterConfigBase): string { + return typeof config.url === 'string' + ? config.url + : getEnv().OTEL_EXPORTER_OTLP_METRICS_ENDPOINT.length > 0 + ? getEnv().OTEL_EXPORTER_OTLP_METRICS_ENDPOINT + : getEnv().OTEL_EXPORTER_OTLP_ENDPOINT.length > 0 + ? appendResourcePathToUrlIfNotPresent(getEnv().OTEL_EXPORTER_OTLP_ENDPOINT, DEFAULT_COLLECTOR_RESOURCE_PATH) + : DEFAULT_COLLECTOR_URL; } convert( - metrics: MetricRecord[] + metrics: ResourceMetrics[] ): otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest { return toOTLPExportMetricServiceRequest( - metrics, - this._startTime, + metrics[0], + this._aggregationTemporality, this ); } +} - getDefaultUrl(config: otlpTypes.OTLPExporterConfigBase): string { - return typeof config.url === 'string' - ? config.url - : getEnv().OTEL_EXPORTER_OTLP_METRICS_ENDPOINT.length > 0 - ? getEnv().OTEL_EXPORTER_OTLP_METRICS_ENDPOINT - : getEnv().OTEL_EXPORTER_OTLP_ENDPOINT.length > 0 - ? appendResourcePathToUrlIfNotPresent(getEnv().OTEL_EXPORTER_OTLP_ENDPOINT, DEFAULT_COLLECTOR_RESOURCE_PATH) - : DEFAULT_COLLECTOR_URL; +/** + * Collector Metric Exporter for Web + */ +export class OTLPMetricExporter extends OTLPMetricExporterBase { + constructor(config: otlpTypes.OTLPExporterConfigBase & OTLPMetricExporterOptions = defaultOptions) { + super(new OTLPExporterBrowserProxy(config), config); } } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/node/OTLPMetricExporter.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/node/OTLPMetricExporter.ts index ebc2cc2294..59d3e339cd 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/node/OTLPMetricExporter.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/node/OTLPMetricExporter.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { MetricRecord, MetricExporter } from '@opentelemetry/sdk-metrics-base'; +import { ResourceMetrics, AggregationTemporality } from '@opentelemetry/sdk-metrics-base'; import { OTLPExporterNodeBase, OTLPExporterNodeConfigBase, @@ -22,24 +22,18 @@ import { appendResourcePathToUrlIfNotPresent } from '@opentelemetry/exporter-trace-otlp-http'; import { toOTLPExportMetricServiceRequest } from '../../transformMetrics'; -import { getEnv, baggageUtils } from '@opentelemetry/core'; +import { getEnv, baggageUtils} from '@opentelemetry/core'; +import { defaultExporterTemporality, defaultOptions, OTLPMetricExporterOptions } from '../../OTLPMetricExporterOptions'; +import { OTLPMetricExporterBase } from '../../OTLPMetricExporterBase'; const DEFAULT_COLLECTOR_RESOURCE_PATH = '/v1/metrics'; -const DEFAULT_COLLECTOR_URL=`http://localhost:4318${DEFAULT_COLLECTOR_RESOURCE_PATH}`; +const DEFAULT_COLLECTOR_URL = `http://localhost:4318${DEFAULT_COLLECTOR_RESOURCE_PATH}`; -/** - * Collector Metric Exporter for Node - */ -export class OTLPMetricExporter - extends OTLPExporterNodeBase< - MetricRecord, - otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest - > - implements MetricExporter { - // Converts time to nanoseconds - protected readonly _startTime = new Date().getTime() * 1000000; +class OTLPExporterNodeProxy extends OTLPExporterNodeBase { + protected readonly _aggregationTemporality: AggregationTemporality; - constructor(config: OTLPExporterNodeConfigBase = {}) { + constructor(config: OTLPExporterNodeConfigBase & OTLPMetricExporterOptions = defaultOptions) { super(config); this.headers = Object.assign( this.headers, @@ -47,14 +41,15 @@ export class OTLPMetricExporter getEnv().OTEL_EXPORTER_OTLP_METRICS_HEADERS ) ); + this._aggregationTemporality = config.aggregationTemporality ?? defaultExporterTemporality; } convert( - metrics: MetricRecord[] + metrics: ResourceMetrics[] ): otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest { return toOTLPExportMetricServiceRequest( - metrics, - this._startTime, + metrics[0], + this._aggregationTemporality, this ); } @@ -63,9 +58,18 @@ export class OTLPMetricExporter return typeof config.url === 'string' ? config.url : getEnv().OTEL_EXPORTER_OTLP_METRICS_ENDPOINT.length > 0 - ? getEnv().OTEL_EXPORTER_OTLP_METRICS_ENDPOINT - : getEnv().OTEL_EXPORTER_OTLP_ENDPOINT.length > 0 - ? appendResourcePathToUrlIfNotPresent(getEnv().OTEL_EXPORTER_OTLP_ENDPOINT, DEFAULT_COLLECTOR_RESOURCE_PATH) - : DEFAULT_COLLECTOR_URL; + ? getEnv().OTEL_EXPORTER_OTLP_METRICS_ENDPOINT + : getEnv().OTEL_EXPORTER_OTLP_ENDPOINT.length > 0 + ? appendResourcePathToUrlIfNotPresent(getEnv().OTEL_EXPORTER_OTLP_ENDPOINT, DEFAULT_COLLECTOR_RESOURCE_PATH) + : DEFAULT_COLLECTOR_URL; + } +} + +/** + * Collector Metric Exporter for Node + */ +export class OTLPMetricExporter extends OTLPMetricExporterBase { + constructor(config: OTLPExporterNodeConfigBase & OTLPMetricExporterOptions = defaultOptions) { + super(new OTLPExporterNodeProxy(config), config); } } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/transformMetrics.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/transformMetrics.ts index b63947c28b..128b28a1cf 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/transformMetrics.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/transformMetrics.ts @@ -14,246 +14,218 @@ * limitations under the License. */ -import { SpanAttributes, HrTime } from '@opentelemetry/api'; -import { Attributes as Labels, ValueType } from '@opentelemetry/api-metrics'; +import { SpanAttributes } from '@opentelemetry/api'; import * as core from '@opentelemetry/core'; +import { OTLPExporterBase, otlpTypes, toCollectorResource } from '@opentelemetry/exporter-trace-otlp-http'; import { - AggregatorKind, + AggregationTemporality, + DataPointType, Histogram, - MetricKind, - MetricRecord, + InstrumentType, + MetricData, + ResourceMetrics } from '@opentelemetry/sdk-metrics-base'; -import { Resource } from '@opentelemetry/resources'; -import { OTLPExporterBase, otlpTypes, toCollectorResource } from '@opentelemetry/exporter-trace-otlp-http'; +import { Attributes, ValueType } from '@opentelemetry/api-metrics'; /** - * Converts labels - * @param labels + * Converts {@link Attributes} to a collector-compatible format. + * @param attributes */ -export function toCollectorLabels( - labels: Labels +export function toCollectorAttributes( + attributes: Attributes ): otlpTypes.opentelemetryProto.common.v1.StringKeyValue[] { - return Object.entries(labels).map(([key, value]) => { + return Object.entries(attributes).map(([key, value]) => { return { key, value: String(value) }; }); } /** - * Given a MetricDescriptor, return its temporality in a compatible format with the collector - * @param descriptor + * Convert {@link AggregationTemporality} to a collector-compatible format. + * @param aggregationTemporality */ export function toAggregationTemporality( - metric: MetricRecord + aggregationTemporality: AggregationTemporality ): otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality { - if (metric.descriptor.metricKind === MetricKind.OBSERVABLE_GAUGE) { + if (aggregationTemporality === AggregationTemporality.CUMULATIVE) { return otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality - .AGGREGATION_TEMPORALITY_UNSPECIFIED; + .AGGREGATION_TEMPORALITY_CUMULATIVE; + } + if (aggregationTemporality === AggregationTemporality.DELTA) { + return otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality + .AGGREGATION_TEMPORALITY_DELTA; } - return metric.aggregationTemporality; + return otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality + .AGGREGATION_TEMPORALITY_UNSPECIFIED; } /** - * Returns an DataPoint which can have integers or double values - * @param metric - * @param startTime + * Convert {@link MetricData} of {@link DataPointType.SINGULAR} to a collector-compatible format. + * @param metricData */ -export function toDataPoint( - metric: MetricRecord, - startTime: number -): otlpTypes.opentelemetryProto.metrics.v1.DataPoint { - return { - labels: toCollectorLabels(metric.attributes), - value: metric.aggregator.toPoint().value as number, - startTimeUnixNano: startTime, - timeUnixNano: core.hrTimeToNanoseconds( - metric.aggregator.toPoint().timestamp - ), - }; +export function toSingularDataPoints( + metricData: MetricData +): otlpTypes.opentelemetryProto.metrics.v1.DataPoint[] { + return Array.from(metricData.dataPoints.map(dataPoint => { + return { + labels: toCollectorAttributes(dataPoint.attributes), + value: dataPoint.value as number, + startTimeUnixNano: core.hrTimeToNanoseconds( + dataPoint.startTime + ), + timeUnixNano: core.hrTimeToNanoseconds( + dataPoint.endTime + ), + }; + })); } /** - * Returns a HistogramPoint to the collector - * @param metric - * @param startTime + * Convert {@link MetricData} of {@link DataPointType.HISTOGRAM} to a collector-compatible format. + * @param metricData */ -export function toHistogramPoint( - metric: MetricRecord, - startTime: number -): otlpTypes.opentelemetryProto.metrics.v1.HistogramDataPoint { - const { value, timestamp } = metric.aggregator.toPoint() as { - value: Histogram; - timestamp: HrTime; - }; - return { - labels: toCollectorLabels(metric.attributes), - sum: value.sum, - count: value.count, - startTimeUnixNano: startTime, - timeUnixNano: core.hrTimeToNanoseconds(timestamp), - bucketCounts: value.buckets.counts, - explicitBounds: value.buckets.boundaries, - }; +export function toHistogramDataPoints( + metricData: MetricData +): otlpTypes.opentelemetryProto.metrics.v1.HistogramDataPoint[] { + return Array.from(metricData.dataPoints.map(dataPoints => { + const histogram = dataPoints.value as Histogram; + return { + labels: toCollectorAttributes(dataPoints.attributes), + sum: histogram.sum, + count: histogram.count, + startTimeUnixNano: core.hrTimeToNanoseconds( + dataPoints.startTime + ), + timeUnixNano: core.hrTimeToNanoseconds( + dataPoints.endTime + ), + bucketCounts: histogram.buckets.counts, + explicitBounds: histogram.buckets.boundaries, + }; + })); } /** - * Converts a metric to be compatible with the collector - * @param metric - * @param startTime start time in nanoseconds + * Converts {@link MetricData} to a collector-compatible format. + * @param metricData + * @param aggregationTemporality */ export function toCollectorMetric( - metric: MetricRecord, - startTime: number + metricData: MetricData, + aggregationTemporality: AggregationTemporality ): otlpTypes.opentelemetryProto.metrics.v1.Metric { const metricCollector: otlpTypes.opentelemetryProto.metrics.v1.Metric = { - name: metric.descriptor.name, - description: metric.descriptor.description, - unit: metric.descriptor.unit, + name: metricData.descriptor.name, + description: metricData.descriptor.description, + unit: metricData.descriptor.unit, }; - if ( - metric.aggregator.kind === AggregatorKind.SUM || - metric.descriptor.metricKind === MetricKind.OBSERVABLE_COUNTER || - metric.descriptor.metricKind === MetricKind.OBSERVABLE_UP_DOWN_COUNTER - ) { + if (metricData.dataPointType === DataPointType.SINGULAR) { const result = { - dataPoints: [toDataPoint(metric, startTime)], + dataPoints: toSingularDataPoints(metricData), isMonotonic: - metric.descriptor.metricKind === MetricKind.COUNTER || - metric.descriptor.metricKind === MetricKind.OBSERVABLE_COUNTER, - aggregationTemporality: toAggregationTemporality(metric), + metricData.descriptor.type === InstrumentType.COUNTER || + metricData.descriptor.type === InstrumentType.OBSERVABLE_COUNTER, + aggregationTemporality: toAggregationTemporality(aggregationTemporality), }; - if (metric.descriptor.valueType === ValueType.INT) { - metricCollector.intSum = result; - } else { - metricCollector.doubleSum = result; - } - } else if (metric.aggregator.kind === AggregatorKind.LAST_VALUE) { - const result = { - dataPoints: [toDataPoint(metric, startTime)], - }; - if (metric.descriptor.valueType === ValueType.INT) { - metricCollector.intGauge = result; - } else { - metricCollector.doubleGauge = result; + + if ( + metricData.descriptor.type === InstrumentType.COUNTER || + metricData.descriptor.type === InstrumentType.OBSERVABLE_COUNTER || + metricData.descriptor.type === InstrumentType.UP_DOWN_COUNTER || + metricData.descriptor.type === InstrumentType.OBSERVABLE_UP_DOWN_COUNTER + ) { + if (metricData.descriptor.valueType === ValueType.INT) { + metricCollector.intSum = result; + } else { + metricCollector.doubleSum = result; + } + } else{ + // Instrument is a gauge. + if (metricData.descriptor.valueType === ValueType.INT) { + metricCollector.intGauge = result; + } else { + metricCollector.doubleGauge = result; + } } - } else if (metric.aggregator.kind === AggregatorKind.HISTOGRAM) { + } else if (metricData.dataPointType === DataPointType.HISTOGRAM) { const result = { - dataPoints: [toHistogramPoint(metric, startTime)], - aggregationTemporality: toAggregationTemporality(metric), + dataPoints: toHistogramDataPoints(metricData), + aggregationTemporality: toAggregationTemporality(aggregationTemporality) }; - if (metric.descriptor.valueType === ValueType.INT) { + if (metricData.descriptor.valueType === ValueType.INT) { metricCollector.intHistogram = result; } else { metricCollector.doubleHistogram = result; } } + // TODO: Add support for exponential histograms when they're ready. + return metricCollector; } /** * Prepares metric service request to be sent to collector * @param metrics metrics - * @param startTime start time of the metric in nanoseconds - * @param collectorMetricExporterBase + * @param aggregationTemporality + * @param collectorExporterBase */ -export function toOTLPExportMetricServiceRequest< - T extends otlpTypes.OTLPExporterConfigBase ->( - metrics: MetricRecord[], - startTime: number, - collectorExporterBase: OTLPExporterBase< - T, - MetricRecord, - otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest - > +export function toOTLPExportMetricServiceRequest( + metrics: ResourceMetrics, + aggregationTemporality: AggregationTemporality, + collectorExporterBase: OTLPExporterBase ): otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest { - const groupedMetrics: Map< - Resource, - Map - > = groupMetricsByResourceAndLibrary(metrics); const additionalAttributes = Object.assign( {}, collectorExporterBase.attributes ); return { resourceMetrics: toCollectorResourceMetrics( - groupedMetrics, + metrics, additionalAttributes, - startTime + aggregationTemporality ), }; } -/** - * Takes an array of metrics and groups them by resource and instrumentation - * library - * @param metrics metrics - */ -export function groupMetricsByResourceAndLibrary( - metrics: MetricRecord[] -): Map> { - return metrics.reduce((metricMap, metric) => { - //group by resource - let resourceMetrics = metricMap.get(metric.resource); - if (!resourceMetrics) { - resourceMetrics = new Map(); - metricMap.set(metric.resource, resourceMetrics); - } - //group by instrumentation library - let libMetrics = resourceMetrics.get(metric.instrumentationLibrary); - if (!libMetrics) { - libMetrics = new Array(); - resourceMetrics.set(metric.instrumentationLibrary, libMetrics); - } - libMetrics.push(metric); - return metricMap; - }, new Map>()); -} - /** * Convert to InstrumentationLibraryMetrics * @param instrumentationLibrary * @param metrics - * @param startTime + * @param aggregationTemporality */ function toCollectorInstrumentationLibraryMetrics( instrumentationLibrary: core.InstrumentationLibrary, - metrics: MetricRecord[], - startTime: number + metrics: MetricData[], + aggregationTemporality: AggregationTemporality ): otlpTypes.opentelemetryProto.metrics.v1.InstrumentationLibraryMetrics { return { - metrics: metrics.map(metric => toCollectorMetric(metric, startTime)), + metrics: metrics.map(metric => toCollectorMetric(metric, aggregationTemporality)), instrumentationLibrary, }; } /** * Returns a list of resource metrics which will be exported to the collector - * @param groupedSpans + * @param resourceMetrics * @param baseAttributes + * @param aggregationTemporality */ function toCollectorResourceMetrics( - groupedMetrics: Map< - Resource, - Map - >, + resourceMetrics: ResourceMetrics, baseAttributes: SpanAttributes, - startTime: number + aggregationTemporality: AggregationTemporality ): otlpTypes.opentelemetryProto.metrics.v1.ResourceMetrics[] { - return Array.from(groupedMetrics, ([resource, libMetrics]) => { - return { - resource: toCollectorResource(resource, baseAttributes), - instrumentationLibraryMetrics: Array.from( - libMetrics, - ([instrumentationLibrary, metrics]) => - toCollectorInstrumentationLibraryMetrics( - instrumentationLibrary, - metrics, - startTime - ) - ), - }; - }); + return [{ + resource: toCollectorResource(resourceMetrics.resource, baseAttributes), + instrumentationLibraryMetrics: Array.from(resourceMetrics.instrumentationLibraryMetrics.map( + instrumentationLibraryMetrics => toCollectorInstrumentationLibraryMetrics( + instrumentationLibraryMetrics.instrumentationLibrary, + instrumentationLibraryMetrics.metrics, + aggregationTemporality + ))) + }]; } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/browser/CollectorMetricExporter.test.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/browser/CollectorMetricExporter.test.ts index 5eab43664b..f40b1cab84 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/browser/CollectorMetricExporter.test.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/browser/CollectorMetricExporter.test.ts @@ -15,70 +15,63 @@ */ import { diag, DiagLogger, DiagLogLevel } from '@opentelemetry/api'; -import { - Counter, - ObservableGauge, - Histogram, -} from '@opentelemetry/api-metrics'; +import { Counter, Histogram, } from '@opentelemetry/api-metrics'; import { ExportResultCode, hrTimeToNanoseconds } from '@opentelemetry/core'; -import { - BoundCounter, - BoundObservable, - BoundHistogram, - Metric, - MetricRecord, -} from '@opentelemetry/sdk-metrics-base'; +import { AggregationTemporality, ResourceMetrics, } from '@opentelemetry/sdk-metrics-base'; import * as assert from 'assert'; import * as sinon from 'sinon'; -import { OTLPMetricExporter } from '../../src/platform/browser/index'; +import { OTLPMetricExporter } from '../../src/platform/browser'; import { otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; import { + collect, ensureCounterIsCorrect, - ensureExportMetricsServiceRequestIsSet, - ensureHeadersContain, - ensureObservableGaugeIsCorrect, + ensureExportMetricsServiceRequestIsSet, ensureHeadersContain, ensureHistogramIsCorrect, + ensureObservableGaugeIsCorrect, ensureWebResourceIsCorrect, mockCounter, - mockObservableGauge, mockHistogram, + mockObservableGauge, + setUp, + shutdown, } from '../metricsHelper'; +import { OTLPMetricExporterOptions } from '../../src'; describe('OTLPMetricExporter - web', () => { let collectorExporter: OTLPMetricExporter; let stubOpen: sinon.SinonStub; let stubBeacon: sinon.SinonStub; - let metrics: MetricRecord[]; + let metrics: ResourceMetrics; let debugStub: sinon.SinonStub; let errorStub: sinon.SinonStub; beforeEach(async () => { + setUp(); stubOpen = sinon.stub(XMLHttpRequest.prototype, 'open'); sinon.stub(XMLHttpRequest.prototype, 'send'); stubBeacon = sinon.stub(navigator, 'sendBeacon'); - metrics = []; - const counter: Metric & Counter = mockCounter(); - const observableGauge: Metric & ObservableGauge = mockObservableGauge( + + const counter: Counter = mockCounter(); + mockObservableGauge( observableResult => { observableResult.observe(3, {}); observableResult.observe(6, {}); }, 'double-observable-gauge2' ); - const histogram: Metric & - Histogram = mockHistogram(); + const histogram: Histogram = mockHistogram(); + counter.add(1); histogram.record(7); histogram.record(14); - metrics.push((await counter.getMetricRecord())[0]); - metrics.push((await observableGauge.getMetricRecord())[0]); - metrics.push((await histogram.getMetricRecord())[0]); + metrics = await collect(); // Need to stub/spy on the underlying logger as the "diag" instance is global debugStub = sinon.stub(); errorStub = sinon.stub(); - const nop = () => {}; + const nop = () => { + }; const diagLogger: DiagLogger = { debug: debugStub, error: errorStub, @@ -89,7 +82,8 @@ describe('OTLPMetricExporter - web', () => { diag.setLogger(diagLogger, DiagLogLevel.DEBUG); }); - afterEach(() => { + afterEach(async () => { + await shutdown(); sinon.restore(); diag.disable(); }); @@ -99,14 +93,12 @@ describe('OTLPMetricExporter - web', () => { beforeEach(() => { collectorExporter = new OTLPMetricExporter({ url: 'http://foo.bar.com', - }); - // Overwrites the start time to make tests consistent - Object.defineProperty(collectorExporter, '_startTime', { - value: 1592602232694000000, + aggregationTemporality: AggregationTemporality.CUMULATIVE }); }); it('should successfully send metrics using sendBeacon', done => { - collectorExporter.export(metrics, () => {}); + collectorExporter.export(metrics, () => { + }); setTimeout(async () => { const args = stubBeacon.args[0]; @@ -127,7 +119,8 @@ describe('OTLPMetricExporter - web', () => { if (metric1) { ensureCounterIsCorrect( metric1, - hrTimeToNanoseconds(metrics[0].aggregator.toPoint().timestamp) + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[0].dataPoints[0].endTime), + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[0].dataPoints[0].startTime) ); } @@ -138,7 +131,8 @@ describe('OTLPMetricExporter - web', () => { if (metric2) { ensureObservableGaugeIsCorrect( metric2, - hrTimeToNanoseconds(metrics[1].aggregator.toPoint().timestamp), + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[1].dataPoints[0].endTime), + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[1].dataPoints[0].startTime), 6, 'double-observable-gauge2' ); @@ -151,7 +145,8 @@ describe('OTLPMetricExporter - web', () => { if (metric3) { ensureHistogramIsCorrect( metric3, - hrTimeToNanoseconds(metrics[2].aggregator.toPoint().timestamp), + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[2].dataPoints[0].endTime), + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[2].dataPoints[0].startTime), [0, 100], [0, 2, 0] ); @@ -177,7 +172,8 @@ describe('OTLPMetricExporter - web', () => { it('should log the successful message', done => { stubBeacon.returns(true); - collectorExporter.export(metrics, () => {}); + collectorExporter.export(metrics, () => { + }); setTimeout(() => { const response: any = debugStub.args[2][0]; @@ -205,6 +201,7 @@ describe('OTLPMetricExporter - web', () => { (window.navigator as any).sendBeacon = false; collectorExporter = new OTLPMetricExporter({ url: 'http://foo.bar.com', + aggregationTemporality: AggregationTemporality.CUMULATIVE }); // Overwrites the start time to make tests consistent Object.defineProperty(collectorExporter, '_startTime', { @@ -217,7 +214,8 @@ describe('OTLPMetricExporter - web', () => { }); it('should successfully send the metrics using XMLHttpRequest', done => { - collectorExporter.export(metrics, () => {}); + collectorExporter.export(metrics, () => { + }); setTimeout(() => { const request = server.requests[0]; @@ -238,7 +236,8 @@ describe('OTLPMetricExporter - web', () => { if (metric1) { ensureCounterIsCorrect( metric1, - hrTimeToNanoseconds(metrics[0].aggregator.toPoint().timestamp) + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[0].dataPoints[0].endTime), + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[0].dataPoints[0].startTime) ); } assert.ok( @@ -248,7 +247,8 @@ describe('OTLPMetricExporter - web', () => { if (metric2) { ensureObservableGaugeIsCorrect( metric2, - hrTimeToNanoseconds(metrics[1].aggregator.toPoint().timestamp), + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[1].dataPoints[0].endTime), + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[1].dataPoints[0].startTime), 6, 'double-observable-gauge2' ); @@ -261,7 +261,8 @@ describe('OTLPMetricExporter - web', () => { if (metric3) { ensureHistogramIsCorrect( metric3, - hrTimeToNanoseconds(metrics[2].aggregator.toPoint().timestamp), + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[2].dataPoints[0].endTime), + hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[2].dataPoints[0].startTime), [0, 100], [0, 2, 0] ); @@ -281,7 +282,8 @@ describe('OTLPMetricExporter - web', () => { }); it('should log the successful message', done => { - collectorExporter.export(metrics, () => {}); + collectorExporter.export(metrics, () => { + }); setTimeout(() => { const request = server.requests[0]; @@ -310,7 +312,8 @@ describe('OTLPMetricExporter - web', () => { }); }); it('should send custom headers', done => { - collectorExporter.export(metrics, () => {}); + collectorExporter.export(metrics, () => { + }); setTimeout(() => { const request = server.requests[0]; @@ -329,11 +332,12 @@ describe('OTLPMetricExporter - web', () => { foo: 'bar', bar: 'baz', }; - let collectorExporterConfig: otlpTypes.OTLPExporterConfigBase; + let collectorExporterConfig: (otlpTypes.OTLPExporterConfigBase & OTLPMetricExporterOptions) | undefined; beforeEach(() => { collectorExporterConfig = { headers: customHeaders, + aggregationTemporality: AggregationTemporality.CUMULATIVE }; server = sinon.fakeServer.create(); }); @@ -349,7 +353,8 @@ describe('OTLPMetricExporter - web', () => { ); }); it('should successfully send custom headers using XMLHTTPRequest', done => { - collectorExporter.export(metrics, () => {}); + collectorExporter.export(metrics, () => { + }); setTimeout(() => { const [{ requestHeaders }] = server.requests; @@ -372,7 +377,8 @@ describe('OTLPMetricExporter - web', () => { }); it('should successfully send metrics using XMLHttpRequest', done => { - collectorExporter.export(metrics, () => {}); + collectorExporter.export(metrics, () => { + }); setTimeout(() => { const [{ requestHeaders }] = server.requests; @@ -394,7 +400,7 @@ describe('when configuring via environment', () => { envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/v1/metrics'; const collectorExporter = new OTLPMetricExporter(); assert.strictEqual( - collectorExporter.url, + collectorExporter._otlpExporter.url, envSource.OTEL_EXPORTER_OTLP_ENDPOINT ); envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; @@ -403,7 +409,7 @@ describe('when configuring via environment', () => { envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar'; const collectorExporter = new OTLPMetricExporter(); assert.strictEqual( - collectorExporter.url, + collectorExporter._otlpExporter.url, `${envSource.OTEL_EXPORTER_OTLP_ENDPOINT}/v1/metrics` ); envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; @@ -413,7 +419,7 @@ describe('when configuring via environment', () => { envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = 'http://foo.metrics'; const collectorExporter = new OTLPMetricExporter(); assert.strictEqual( - collectorExporter.url, + collectorExporter._otlpExporter.url, envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT ); envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; @@ -421,19 +427,22 @@ describe('when configuring via environment', () => { }); it('should use headers defined via env', () => { envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar'; - const collectorExporter = new OTLPMetricExporter({ headers: {} }); - // @ts-expect-error access internal property for testing - assert.strictEqual(collectorExporter._headers.foo, 'bar'); + const collectorExporter = new OTLPMetricExporter({ + headers: {}, + aggregationTemporality: AggregationTemporality.CUMULATIVE + }); + assert.strictEqual(collectorExporter['_otlpExporter']['_headers'].foo, 'bar'); envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; }); it('should override global headers config with signal headers defined via env', () => { envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar,bar=foo'; envSource.OTEL_EXPORTER_OTLP_METRICS_HEADERS = 'foo=boo'; - const collectorExporter = new OTLPMetricExporter({ headers: {} }); - // @ts-expect-error access internal property for testing - assert.strictEqual(collectorExporter._headers.foo, 'boo'); - // @ts-expect-error access internal property for testing - assert.strictEqual(collectorExporter._headers.bar, 'foo'); + const collectorExporter = new OTLPMetricExporter({ + headers: {}, + aggregationTemporality: AggregationTemporality.CUMULATIVE + }); + assert.strictEqual(collectorExporter['_otlpExporter']['_headers'].foo, 'boo'); + assert.strictEqual(collectorExporter['_otlpExporter']['_headers'].bar, 'foo'); envSource.OTEL_EXPORTER_OTLP_METRICS_HEADERS = ''; envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; }); diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/CollectorMetricExporter.test.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/CollectorMetricExporter.test.ts index 91b678d2a2..0e941ed7e2 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/CollectorMetricExporter.test.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/CollectorMetricExporter.test.ts @@ -14,23 +14,19 @@ * limitations under the License. */ -import { Counter, ObservableGauge } from '@opentelemetry/api-metrics'; import { ExportResultCode } from '@opentelemetry/core'; import { - BoundCounter, - BoundObservable, - Metric, - MetricRecord, + ResourceMetrics, } from '@opentelemetry/sdk-metrics-base'; import * as assert from 'assert'; import * as sinon from 'sinon'; import { OTLPExporterBase, otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; -import { mockCounter, mockObservableGauge } from '../metricsHelper'; +import { collect, mockCounter, mockObservableGauge, setUp, shutdown } from '../metricsHelper'; type CollectorExporterConfig = otlpTypes.OTLPExporterConfigBase; class OTLPMetricExporter extends OTLPExporterBase< CollectorExporterConfig, - MetricRecord, + ResourceMetrics, otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest > { onInit() {} @@ -40,7 +36,7 @@ class OTLPMetricExporter extends OTLPExporterBase< return config.url || ''; } convert( - metrics: MetricRecord[] + metrics: ResourceMetrics[] ): otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest { return { resourceMetrics: [] }; } @@ -49,9 +45,14 @@ class OTLPMetricExporter extends OTLPExporterBase< describe('OTLPMetricExporter - common', () => { let collectorExporter: OTLPMetricExporter; let collectorExporterConfig: CollectorExporterConfig; - let metrics: MetricRecord[]; + let metrics: ResourceMetrics; - afterEach(() => { + beforeEach(() => { + setUp(); + }); + + afterEach(async () => { + await shutdown(); sinon.restore(); }); @@ -66,9 +67,8 @@ describe('OTLPMetricExporter - common', () => { url: 'http://foo.bar.com', }; collectorExporter = new OTLPMetricExporter(collectorExporterConfig); - metrics = []; - const counter: Metric & Counter = mockCounter(); - const observableGauge: Metric & ObservableGauge = mockObservableGauge( + const counter = mockCounter(); + mockObservableGauge( observableResult => { observableResult.observe(3, {}); observableResult.observe(6, {}); @@ -77,8 +77,7 @@ describe('OTLPMetricExporter - common', () => { ); counter.add(1); - metrics.push((await counter.getMetricRecord())[0]); - metrics.push((await observableGauge.getMetricRecord())[0]); + metrics = await collect(); }); it('should create an instance', () => { @@ -114,12 +113,10 @@ describe('OTLPMetricExporter - common', () => { }); it('should export metrics as otlpTypes.Metrics', done => { - collectorExporter.export(metrics, () => {}); + collectorExporter.export([metrics], () => {}); setTimeout(() => { - const metric1 = spySend.args[0][0][0] as MetricRecord; - assert.deepStrictEqual(metrics[0], metric1); - const metric2 = spySend.args[0][0][1] as MetricRecord; - assert.deepStrictEqual(metrics[1], metric2); + const metric1 = spySend.args[0][0][0] as ResourceMetrics; + assert.deepStrictEqual(metrics, metric1); done(); }); assert.strictEqual(spySend.callCount, 1); @@ -134,7 +131,7 @@ describe('OTLPMetricExporter - common', () => { spySend.resetHistory(); const callbackSpy = sinon.spy(); - collectorExporter.export(metrics, callbackSpy); + collectorExporter.export([metrics], callbackSpy); const returnCode = callbackSpy.args[0][0]; assert.strictEqual( returnCode.code, @@ -155,7 +152,7 @@ describe('OTLPMetricExporter - common', () => { stack: 'Stack', }); const callbackSpy = sinon.spy(); - collectorExporter.export(metrics, callbackSpy); + collectorExporter.export([metrics], callbackSpy); setTimeout(() => { const returnCode = callbackSpy.args[0][0]; assert.strictEqual( diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/transformMetrics.test.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/transformMetrics.test.ts index 3169c02c80..f5b401f304 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/transformMetrics.test.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/transformMetrics.test.ts @@ -13,150 +13,152 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { - Counter, - ObservableCounter, - ObservableUpDownCounter, - ObservableGauge, - Histogram, -} from '@opentelemetry/api-metrics'; -import { hrTimeToNanoseconds } from '@opentelemetry/core'; -import { - BoundCounter, - BoundObservable, - BoundHistogram, - Metric, - SumAggregator, -} from '@opentelemetry/sdk-metrics-base'; -import { Resource } from '@opentelemetry/resources'; + +import { hrTimeToNanoseconds, hrTime } from '@opentelemetry/core'; +import { AggregationTemporality, DataPointType, InstrumentType } from '@opentelemetry/sdk-metrics-base'; import * as assert from 'assert'; import * as transform from '../../src/transformMetrics'; import { + collect, ensureCounterIsCorrect, ensureDoubleCounterIsCorrect, - ensureObservableGaugeIsCorrect, + ensureHistogramIsCorrect, ensureObservableCounterIsCorrect, + ensureObservableGaugeIsCorrect, ensureObservableUpDownCounterIsCorrect, - ensureHistogramIsCorrect, mockCounter, mockDoubleCounter, - mockedInstrumentationLibraries, - mockedResources, - mockObservableGauge, + mockHistogram, mockObservableCounter, + mockObservableGauge, mockObservableUpDownCounter, - mockHistogram, - multiInstrumentationLibraryMetricsGet, - multiResourceMetricsGet, + setUp, } from '../metricsHelper'; describe('transformMetrics', () => { describe('toCollectorMetric', async () => { - let counter: Metric & Counter; - let doubleCounter: Metric & Counter; - let observableGauge: Metric & ObservableGauge; - let observableCounter: Metric & ObservableCounter; - let observableUpDownCounter: Metric & ObservableUpDownCounter; - let histogram: Metric & Histogram; - beforeEach(() => { - counter = mockCounter(); - doubleCounter = mockDoubleCounter(); - let count1 = 0; - let count2 = 0; - let count3 = 0; - - function getValue(count: number) { - if (count % 2 === 0) { - return 3; - } - return -1; - } - observableGauge = mockObservableGauge(observableResult => { - count1++; - observableResult.observe(getValue(count1), {}); - }); - - observableCounter = mockObservableCounter(observableResult => { - count2++; - observableResult.observe(getValue(count2), {}); - }); - - observableUpDownCounter = mockObservableUpDownCounter(observableResult => { - count3++; - observableResult.observe(getValue(count3), {}); - }); - - histogram = mockHistogram(); - - // Counter - counter.add(1); - - // Double Counter - doubleCounter.add(8); + function getValue(count: number) { + if (count % 2 === 0) { + return 3; + } + return -1; + } - // Histogram - histogram.record(7); - histogram.record(14); + beforeEach(() => { + setUp(); }); - it('should convert metric', async () => { - const counterMetric = (await counter.getMetricRecord())[0]; - ensureCounterIsCorrect( - transform.toCollectorMetric(counterMetric, 1592602232694000000), - hrTimeToNanoseconds(await counterMetric.aggregator.toPoint().timestamp) - ); - - const doubleCounterMetric = (await doubleCounter.getMetricRecord())[0]; - ensureDoubleCounterIsCorrect( - transform.toCollectorMetric(doubleCounterMetric, 1592602232694000000), - hrTimeToNanoseconds(doubleCounterMetric.aggregator.toPoint().timestamp) - ); - - await observableGauge.getMetricRecord(); - await observableGauge.getMetricRecord(); - const observableGaugeMetric = (await observableGauge.getMetricRecord())[0]; - ensureObservableGaugeIsCorrect( - transform.toCollectorMetric(observableGaugeMetric, 1592602232694000000), - hrTimeToNanoseconds(observableGaugeMetric.aggregator.toPoint().timestamp), - -1 - ); + it('should convert counter', async () => { + const counter = mockCounter(); + counter.add(1); + const metrics = (await collect()); + + const metric = metrics.instrumentationLibraryMetrics[0].metrics[0]; + ensureCounterIsCorrect( + transform.toCollectorMetric(metric, AggregationTemporality.CUMULATIVE), + hrTimeToNanoseconds(metric.dataPoints[0].endTime), + hrTimeToNanoseconds(metric.dataPoints[0].startTime) + ); + } + ); + + it('should convert double counter', async () => { + const doubleCounter = mockDoubleCounter(); + doubleCounter.add(8); + const metrics = (await collect()); + + const metric = metrics.instrumentationLibraryMetrics[0].metrics[0]; + ensureDoubleCounterIsCorrect( + transform.toCollectorMetric(metric, AggregationTemporality.CUMULATIVE), + hrTimeToNanoseconds(metric.dataPoints[0].endTime), + hrTimeToNanoseconds(metric.dataPoints[0].startTime), + ); + } + ); + + it('should convert observable gauge', async () => { + let count = 0; + mockObservableGauge(observableResult => { + count++; + observableResult.observe(getValue(count), {}); + }); + + // collect three times. + await collect(); + await collect(); + const metrics = (await collect()); + + const metric = metrics.instrumentationLibraryMetrics[0].metrics[0]; + ensureObservableGaugeIsCorrect( + transform.toCollectorMetric(metric, AggregationTemporality.CUMULATIVE), + hrTimeToNanoseconds(metric.dataPoints[0].endTime), + hrTimeToNanoseconds(metric.dataPoints[0].startTime), + -1, + ); + } + ); - // collect 3 times - await observableCounter.getMetricRecord(); - await observableCounter.getMetricRecord(); - const observableCounterMetric = (await observableCounter.getMetricRecord())[0]; - ensureObservableCounterIsCorrect( - transform.toCollectorMetric(observableCounterMetric, 1592602232694000000), - hrTimeToNanoseconds(observableCounterMetric.aggregator.toPoint().timestamp), - 3 - ); - // collect 3 times - await observableUpDownCounter.getMetricRecord(); - await observableUpDownCounter.getMetricRecord(); - const observableUpDownCounterMetric = ( - await observableUpDownCounter.getMetricRecord() - )[0]; - ensureObservableUpDownCounterIsCorrect( - transform.toCollectorMetric( - observableUpDownCounterMetric, - 1592602232694000000 - ), - hrTimeToNanoseconds( - observableUpDownCounterMetric.aggregator.toPoint().timestamp - ), - -1 - ); + it('should convert observable counter', async () => { + mockObservableCounter(observableResult => { + observableResult.observe(1, {}); + }); + + // collect three times. + await collect(); + await collect(); + const metrics = (await collect()); + // TODO: Collect seems to not deliver the last observation -> why? + + const metric = metrics.instrumentationLibraryMetrics[0].metrics[0]; + ensureObservableCounterIsCorrect( + transform.toCollectorMetric(metric, AggregationTemporality.CUMULATIVE), + hrTimeToNanoseconds(metric.dataPoints[0].endTime), + hrTimeToNanoseconds(metric.dataPoints[0].startTime), + 2, + ); + } + ); - const histogramMetric = (await histogram.getMetricRecord())[0]; - ensureHistogramIsCorrect( - transform.toCollectorMetric(histogramMetric, 1592602232694000000), - hrTimeToNanoseconds(histogramMetric.aggregator.toPoint().timestamp), - [0, 100], - [0, 2, 0] - ); - }); + it('should convert observable up-down counter', async () => { + mockObservableUpDownCounter(observableResult => { + observableResult.observe(1, {}); + }); + + // collect three times. + await collect(); + await collect(); + const metrics = (await collect()); + // TODO: Collect seems to not deliver the last observation -> why? + + const metric = metrics.instrumentationLibraryMetrics[0].metrics[0]; + ensureObservableUpDownCounterIsCorrect( + transform.toCollectorMetric(metric, AggregationTemporality.CUMULATIVE), + hrTimeToNanoseconds(metric.dataPoints[0].endTime), + hrTimeToNanoseconds(metric.dataPoints[0].startTime), + 2, + ); + } + ); + + it('should convert observable histogram', async () => { + const histogram = mockHistogram(); + histogram.record(7); + histogram.record(14); + + const metrics = (await collect()); + + const metric = metrics.instrumentationLibraryMetrics[0].metrics[0]; + ensureHistogramIsCorrect( + transform.toCollectorMetric(metric, AggregationTemporality.CUMULATIVE), + hrTimeToNanoseconds(metric.dataPoints[0].endTime), + hrTimeToNanoseconds(metric.dataPoints[0].startTime), + [0, 100], + [0, 2, 0] + ); + } + ); it('should convert metric attributes value to string', () => { const metric = transform.toCollectorMetric( @@ -165,69 +167,23 @@ describe('transformMetrics', () => { name: 'name', description: 'description', unit: 'unit', - metricKind: 0, + type: InstrumentType.COUNTER, valueType: 0, }, - attributes: { foo: (1 as unknown) as string }, - aggregator: new SumAggregator(), - resource: new Resource({}), - aggregationTemporality: 0, - instrumentationLibrary: { name: 'x', version: 'y' }, + dataPoints: [ + { + value: 1, + attributes: { foo: (1 as unknown) as string }, + startTime: hrTime(), + endTime: hrTime(), + } + ], + dataPointType: DataPointType.SINGULAR, }, - 1592602232694000000 + AggregationTemporality.CUMULATIVE ); const collectorMetric = metric.intSum?.dataPoints[0]; assert.strictEqual(collectorMetric?.labels[0].value, '1'); }); }); - - describe('groupMetricsByResourceAndLibrary', () => { - it('should group by resource', async () => { - const [resource1, resource2] = mockedResources; - const [library] = mockedInstrumentationLibraries; - const [metric1, metric2, metric3] = multiResourceMetricsGet( - observableResult => { - observableResult.observe(1, {}); - } - ); - - const expected = new Map([ - [resource1, new Map([[library, [metric1, metric3]]])], - [resource2, new Map([[library, [metric2]]])], - ]); - - const result = transform.groupMetricsByResourceAndLibrary( - multiResourceMetricsGet(observableResult => { - observableResult.observe(1, {}); - }) - ); - - assert.deepStrictEqual(result, expected); - }); - - it('should group by instrumentation library', async () => { - const [resource] = mockedResources; - const [lib1, lib2] = mockedInstrumentationLibraries; - const [ - metric1, - metric2, - metric3, - ] = multiInstrumentationLibraryMetricsGet(observableResult => {}); - const expected = new Map([ - [ - resource, - new Map([ - [lib1, [metric1, metric3]], - [lib2, [metric2]], - ]), - ], - ]); - - const result = transform.groupMetricsByResourceAndLibrary( - multiInstrumentationLibraryMetricsGet(observableResult => {}) - ); - - assert.deepStrictEqual(result, expected); - }); - }); }); 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 830be337f0..a2852771a4 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/metricsHelper.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/metricsHelper.ts @@ -14,32 +14,13 @@ * limitations under the License. */ -import { - Counter, - ObservableResult, - ObservableCounter, - ObservableUpDownCounter, - ObservableGauge, - Histogram, - ValueType, -} from '@opentelemetry/api-metrics'; +import { Counter, Histogram, ObservableResult, ValueType, } from '@opentelemetry/api-metrics'; import { InstrumentationLibrary, VERSION } from '@opentelemetry/core'; -import * as metrics from '@opentelemetry/sdk-metrics-base'; +import { ExplicitBucketHistogramAggregation, MeterProvider, MetricReader } from '@opentelemetry/sdk-metrics-base'; import { Resource } from '@opentelemetry/resources'; import * as assert from 'assert'; import { otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; -const meterProvider = new metrics.MeterProvider({ - interval: 30000, - resource: new Resource({ - service: 'ui', - version: 1, - cost: 112.12, - }), -}); - -const meter = meterProvider.getMeter('default', '0.0.1'); - if (typeof Buffer === 'undefined') { (window as any).Buffer = { from: function (arr: []) { @@ -48,103 +29,122 @@ if (typeof Buffer === 'undefined') { }; } -export function mockCounter(): metrics.Metric & Counter { - const name = 'int-counter'; - const metric = - meter['_metrics'].get(name) || - meter.createCounter(name, { - description: 'sample counter description', - valueType: ValueType.INT, - }); - metric.clear(); - metric.bind({}); - return metric; +export class TestMetricReader extends MetricReader { + protected onForceFlush(): Promise { + return Promise.resolve(undefined); + } + + protected onShutdown(): Promise { + return Promise.resolve(undefined); + } } -export function mockDoubleCounter(): metrics.Metric & - Counter { - const name = 'double-counter'; - const metric = - meter['_metrics'].get(name) || - meter.createCounter(name, { - description: 'sample counter description', - valueType: ValueType.DOUBLE, - }); - metric.clear(); - metric.bind({}); - return metric; +const defaultResource = Resource.default().merge(new Resource({ + service: 'ui', + version: 1, + cost: 112.12, +})); + +let meterProvider = new MeterProvider({ resource: defaultResource }); +let reader = new TestMetricReader(); +meterProvider.addMetricReader( + reader +); +let meter = meterProvider.getMeter('default', '0.0.1'); + +export async function collect() { + return (await reader.collect())!; +} + +export function setUp() { + meterProvider = new MeterProvider({ resource: defaultResource }); + reader = new TestMetricReader(); + meterProvider.addMetricReader( + reader + ); + meter = meterProvider.getMeter('default', '0.0.1'); +} + +export async function shutdown() { + await meterProvider.shutdown(); +} + +export function mockCounter(): Counter { + const name = 'int-counter'; + return meter.createCounter(name, { + description: 'sample counter description', + valueType: ValueType.INT, + }); } export function mockObservableGauge( - callback: (observableResult: ObservableResult) => unknown, + callback: (observableResult: ObservableResult) => void, name = 'double-observable-gauge' -): metrics.Metric & ObservableGauge { - const metric = - meter['_metrics'].get(name) || - meter.createObservableGauge( - name, - { - description: 'sample observable gauge description', - valueType: ValueType.DOUBLE, - }, - callback - ); - metric.clear(); - metric.bind({}); - return metric; +): void { + return meter.createObservableGauge( + name, + callback, + { + description: 'sample observable gauge description', + valueType: ValueType.DOUBLE, + } + ); +} + +export function mockDoubleCounter(): Counter { + const name = 'double-counter'; + return meter.createCounter(name, { + description: 'sample counter description', + valueType: ValueType.DOUBLE, + }); } + + export function mockObservableCounter( - callback: (observableResult: ObservableResult) => unknown, + callback: (observableResult: ObservableResult) => void, name = 'double-observable-counter' -): metrics.Metric & ObservableCounter { - const metric = - meter['_metrics'].get(name) || - meter.createObservableCounter( - name, - { - description: 'sample observable counter description', - valueType: ValueType.DOUBLE, - }, - callback - ); - metric.clear(); - metric.bind({}); - return metric; +): void { + meter.createObservableCounter( + name, + callback, + { + description: 'sample observable counter description', + valueType: ValueType.DOUBLE, + } + ); } export function mockObservableUpDownCounter( - callback: (observableResult: ObservableResult) => unknown, + callback: (observableResult: ObservableResult) => void, name = 'double-up-down-observable-counter' -): metrics.Metric & ObservableUpDownCounter { - const metric = - meter['_metrics'].get(name) || - meter.createObservableUpDownCounter( - name, - { - description: 'sample observable up down counter description', - valueType: ValueType.DOUBLE, - }, - callback - ); - metric.clear(); - metric.bind({}); - return metric; +): void { + meter.createObservableUpDownCounter( + name, + callback, + { + description: 'sample observable up down counter description', + valueType: ValueType.DOUBLE, + }, + ); } -export function mockHistogram(): metrics.Metric & - Histogram { +export function mockHistogram(): Histogram { const name = 'int-histogram'; - const metric = - meter['_metrics'].get(name) || - meter.createHistogram(name, { - description: 'sample histogram description', - valueType: ValueType.INT, - boundaries: [0, 100], + + meterProvider.addView({ + aggregation: new ExplicitBucketHistogramAggregation([0, 100]) + }, + { + instrument: { + name: name + } }); - metric.clear(); - metric.bind({}); - return metric; + + return meter.createHistogram(name, { + description: 'sample histogram description', + valueType: ValueType.INT, + }); } export const mockedResources: Resource[] = [ @@ -163,50 +163,6 @@ export const mockedInstrumentationLibraries: InstrumentationLibrary[] = [ }, ]; -export const multiResourceMetricsGet = function ( - callback: (observableResult: ObservableResult) => unknown -): any[] { - return [ - { - ...mockCounter(), - resource: mockedResources[0], - instrumentationLibrary: mockedInstrumentationLibraries[0], - }, - { - ...mockObservableGauge(callback), - resource: mockedResources[1], - instrumentationLibrary: mockedInstrumentationLibraries[0], - }, - { - ...mockCounter(), - resource: mockedResources[0], - instrumentationLibrary: mockedInstrumentationLibraries[0], - }, - ]; -}; - -export const multiInstrumentationLibraryMetricsGet = function ( - callback: (observableResult: ObservableResult) => unknown -): any[] { - return [ - { - ...mockCounter(), - resource: mockedResources[0], - instrumentationLibrary: mockedInstrumentationLibraries[0], - }, - { - ...mockObservableGauge(callback), - resource: mockedResources[0], - instrumentationLibrary: mockedInstrumentationLibraries[1], - }, - { - ...mockCounter(), - resource: mockedResources[0], - instrumentationLibrary: mockedInstrumentationLibraries[0], - }, - ]; -}; - export function ensureAttributesAreCorrect( attributes: otlpTypes.opentelemetryProto.common.v1.KeyValue[] ) { @@ -247,7 +203,8 @@ export function ensureWebResourceIsCorrect( export function ensureCounterIsCorrect( metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, - time: number + endTime: number, + startTime: number ) { assert.deepStrictEqual(metric, { name: 'int-counter', @@ -258,21 +215,22 @@ export function ensureCounterIsCorrect( { labels: [], value: 1, - startTimeUnixNano: 1592602232694000000, - timeUnixNano: time, + startTimeUnixNano: startTime, + timeUnixNano: endTime, }, ], isMonotonic: true, aggregationTemporality: - otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality - .AGGREGATION_TEMPORALITY_CUMULATIVE, + otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality + .AGGREGATION_TEMPORALITY_CUMULATIVE, }, }); } export function ensureDoubleCounterIsCorrect( metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, - time: number + time: number, + endTime: number ) { assert.deepStrictEqual(metric, { name: 'double-counter', @@ -283,14 +241,14 @@ export function ensureDoubleCounterIsCorrect( { labels: [], value: 8, - startTimeUnixNano: 1592602232694000000, + startTimeUnixNano: endTime, timeUnixNano: time, }, ], isMonotonic: true, aggregationTemporality: - otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality - .AGGREGATION_TEMPORALITY_CUMULATIVE, + otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality + .AGGREGATION_TEMPORALITY_CUMULATIVE, }, }); } @@ -298,6 +256,7 @@ export function ensureDoubleCounterIsCorrect( export function ensureObservableGaugeIsCorrect( metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, time: number, + startTime: number, value: number, name = 'double-observable-gauge' ) { @@ -310,10 +269,14 @@ export function ensureObservableGaugeIsCorrect( { labels: [], value, - startTimeUnixNano: 1592602232694000000, + startTimeUnixNano: startTime, timeUnixNano: time, }, ], + aggregationTemporality: + otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality + .AGGREGATION_TEMPORALITY_CUMULATIVE, + isMonotonic: false, }, }); } @@ -321,6 +284,7 @@ export function ensureObservableGaugeIsCorrect( export function ensureObservableCounterIsCorrect( metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, time: number, + startTime: number, value: number, name = 'double-observable-counter' ) { @@ -334,13 +298,13 @@ export function ensureObservableCounterIsCorrect( { labels: [], value, - startTimeUnixNano: 1592602232694000000, + startTimeUnixNano: startTime, timeUnixNano: time, }, ], aggregationTemporality: - otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality - .AGGREGATION_TEMPORALITY_CUMULATIVE, + otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality + .AGGREGATION_TEMPORALITY_CUMULATIVE, }, }); } @@ -348,6 +312,7 @@ export function ensureObservableCounterIsCorrect( export function ensureObservableUpDownCounterIsCorrect( metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, time: number, + startTime: number, value: number, name = 'double-up-down-observable-counter' ) { @@ -361,13 +326,13 @@ export function ensureObservableUpDownCounterIsCorrect( { labels: [], value, - startTimeUnixNano: 1592602232694000000, + startTimeUnixNano: startTime, timeUnixNano: time, }, ], aggregationTemporality: - otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality - .AGGREGATION_TEMPORALITY_CUMULATIVE, + otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality + .AGGREGATION_TEMPORALITY_CUMULATIVE, }, }); } @@ -375,6 +340,7 @@ export function ensureObservableUpDownCounterIsCorrect( export function ensureHistogramIsCorrect( metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, time: number, + startTime: number, explicitBounds: (number | null)[] = [Infinity], bucketCounts: number[] = [2, 0] ) { @@ -388,15 +354,15 @@ export function ensureHistogramIsCorrect( labels: [], sum: 21, count: 2, - startTimeUnixNano: 1592602232694000000, + startTimeUnixNano: startTime, timeUnixNano: time, bucketCounts, explicitBounds, }, ], aggregationTemporality: - otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality - .AGGREGATION_TEMPORALITY_CUMULATIVE, + otlpTypes.opentelemetryProto.metrics.v1.AggregationTemporality + .AGGREGATION_TEMPORALITY_CUMULATIVE, }, }); } 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 7287d93300..1e11f733d4 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 @@ -14,25 +14,18 @@ * limitations under the License. */ + import { diag, DiagLogger } from '@opentelemetry/api'; -import { - Counter, - ObservableGauge, - Histogram, -} from '@opentelemetry/api-metrics'; import * as core from '@opentelemetry/core'; -import { - BoundCounter, - BoundObservable, - BoundHistogram, - Metric, - MetricRecord, -} from '@opentelemetry/sdk-metrics-base'; import * as assert from 'assert'; import * as http from 'http'; import * as sinon from 'sinon'; import { - OTLPMetricExporter, + OTLPMetricExporterOptions +} from '../../src'; + +import { + OTLPMetricExporter } from '../../src/platform/node'; import { OTLPExporterNodeConfigBase, otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; import { @@ -43,25 +36,35 @@ import { mockCounter, mockObservableGauge, mockHistogram, + collect, shutdown, setUp, } from '../metricsHelper'; import { MockedResponse } from './nodeHelpers'; +import { AggregationTemporality, ResourceMetrics } from '@opentelemetry/sdk-metrics-base'; const fakeRequest = { - end: function () {}, - on: function () {}, - write: function () {}, + end: function () { + }, + on: function () { + }, + write: function () { + }, }; const address = 'localhost:1501'; describe('OTLPMetricExporter - node with json over http', () => { let collectorExporter: OTLPMetricExporter; - let collectorExporterConfig: OTLPExporterNodeConfigBase; + let collectorExporterConfig: OTLPExporterNodeConfigBase & OTLPMetricExporterOptions; let stubRequest: sinon.SinonStub; let stubWrite: sinon.SinonStub; - let metrics: MetricRecord[]; + let metrics: ResourceMetrics; + + beforeEach(async () => { + setUp(); + }); - afterEach(() => { + afterEach(async () => { + await shutdown(); sinon.restore(); }); @@ -71,7 +74,8 @@ describe('OTLPMetricExporter - node with json over http', () => { beforeEach(() => { // Need to stub/spy on the underlying logger as the "diag" instance is global warnStub = sinon.stub(); - const nop = () => {}; + const nop = () => { + }; const diagLogger: DiagLogger = { debug: nop, error: nop, @@ -103,7 +107,7 @@ describe('OTLPMetricExporter - node with json over http', () => { envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/v1/metrics'; const collectorExporter = new OTLPMetricExporter(); assert.strictEqual( - collectorExporter.url, + collectorExporter._otlpExporter.url, envSource.OTEL_EXPORTER_OTLP_ENDPOINT ); envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; @@ -112,7 +116,7 @@ describe('OTLPMetricExporter - node with json over http', () => { envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar'; const collectorExporter = new OTLPMetricExporter(); assert.strictEqual( - collectorExporter.url, + collectorExporter._otlpExporter.url, `${envSource.OTEL_EXPORTER_OTLP_ENDPOINT}/v1/metrics` ); envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; @@ -122,7 +126,7 @@ describe('OTLPMetricExporter - node with json over http', () => { envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = 'http://foo.metrics'; const collectorExporter = new OTLPMetricExporter(); assert.strictEqual( - collectorExporter.url, + collectorExporter._otlpExporter.url, envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT ); envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; @@ -131,15 +135,15 @@ describe('OTLPMetricExporter - node with json over http', () => { it('should use headers defined via env', () => { envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar'; const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual(collectorExporter.headers.foo, 'bar'); + assert.strictEqual(collectorExporter._otlpExporter.headers.foo, 'bar'); envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; }); it('should override global headers config with signal headers defined via env', () => { envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar,bar=foo'; envSource.OTEL_EXPORTER_OTLP_METRICS_HEADERS = 'foo=boo'; const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual(collectorExporter.headers.foo, 'boo'); - assert.strictEqual(collectorExporter.headers.bar, 'foo'); + assert.strictEqual(collectorExporter._otlpExporter.headers.foo, 'boo'); + assert.strictEqual(collectorExporter._otlpExporter.headers.bar, 'foo'); envSource.OTEL_EXPORTER_OTLP_METRICS_HEADERS = ''; envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; }); @@ -158,33 +162,29 @@ describe('OTLPMetricExporter - node with json over http', () => { url: 'http://foo.bar.com', keepAlive: true, httpAgentOptions: { keepAliveMsecs: 2000 }, + aggregationTemporality: AggregationTemporality.CUMULATIVE }; + collectorExporter = new OTLPMetricExporter(collectorExporterConfig); - // Overwrites the start time to make tests consistent - Object.defineProperty(collectorExporter, '_startTime', { - value: 1592602232694000000, - }); - metrics = []; - const counter: Metric & Counter = mockCounter(); - const observableGauge: Metric & ObservableGauge = mockObservableGauge( + + const counter = mockCounter(); + mockObservableGauge( observableResult => { observableResult.observe(6, {}); }, 'double-observable-gauge2' ); - const histogram: Metric & - Histogram = mockHistogram(); + const histogram = mockHistogram(); counter.add(1); histogram.record(7); histogram.record(14); - metrics.push((await counter.getMetricRecord())[0]); - metrics.push((await observableGauge.getMetricRecord())[0]); - metrics.push((await histogram.getMetricRecord())[0]); + metrics = await collect(); }); it('should open the connection', done => { - collectorExporter.export(metrics, () => {}); + collectorExporter.export(metrics, () => { + }); setTimeout(() => { const args = stubRequest.args[0]; @@ -198,7 +198,8 @@ describe('OTLPMetricExporter - node with json over http', () => { }); it('should set custom headers', done => { - collectorExporter.export(metrics, () => {}); + collectorExporter.export(metrics, () => { + }); setTimeout(() => { const args = stubRequest.args[0]; @@ -209,7 +210,8 @@ describe('OTLPMetricExporter - node with json over http', () => { }); it('should have keep alive and keepAliveMsecs option set', done => { - collectorExporter.export(metrics, () => {}); + collectorExporter.export(metrics, () => { + }); setTimeout(() => { const args = stubRequest.args[0]; @@ -222,7 +224,8 @@ describe('OTLPMetricExporter - node with json over http', () => { }); it('should successfully send metrics', done => { - collectorExporter.export(metrics, () => {}); + collectorExporter.export(metrics, () => { + }); setTimeout(() => { const writeArgs = stubWrite.args[0]; @@ -239,19 +242,22 @@ describe('OTLPMetricExporter - node with json over http', () => { assert.ok(typeof metric1 !== 'undefined', "counter doesn't exist"); ensureCounterIsCorrect( metric1, - core.hrTimeToNanoseconds(metrics[0].aggregator.toPoint().timestamp) + core.hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[0].dataPoints[0].endTime), + core.hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[0].dataPoints[0].startTime) ); assert.ok(typeof metric2 !== 'undefined', "observable gauge doesn't exist"); ensureObservableGaugeIsCorrect( metric2, - core.hrTimeToNanoseconds(metrics[1].aggregator.toPoint().timestamp), + core.hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[1].dataPoints[0].endTime), + core.hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[1].dataPoints[0].startTime), 6, 'double-observable-gauge2' ); assert.ok(typeof metric3 !== 'undefined', "histogram doesn't exist"); ensureHistogramIsCorrect( metric3, - core.hrTimeToNanoseconds(metrics[2].aggregator.toPoint().timestamp), + core.hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[1].dataPoints[0].endTime), + core.hrTimeToNanoseconds(metrics.instrumentationLibraryMetrics[0].metrics[1].dataPoints[0].startTime), [0, 100], [0, 2, 0] ); @@ -316,7 +322,7 @@ describe('OTLPMetricExporter - node with json over http', () => { const collectorExporter = new OTLPMetricExporter(); setTimeout(() => { assert.strictEqual( - collectorExporter['url'], + collectorExporter._otlpExporter.url, 'http://localhost:4318/v1/metrics' ); done(); @@ -325,9 +331,12 @@ describe('OTLPMetricExporter - node with json over http', () => { it('should keep the URL if included', done => { const url = 'http://foo.bar.com'; - const collectorExporter = new OTLPMetricExporter({ url }); + const collectorExporter = new OTLPMetricExporter({ + url, + aggregationTemporality: AggregationTemporality.CUMULATIVE + }); setTimeout(() => { - assert.strictEqual(collectorExporter['url'], url); + assert.strictEqual(collectorExporter._otlpExporter.url, url); done(); }); }); diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/tsconfig.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/tsconfig.json index 8597d3eb6b..7672d5f0aa 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/tsconfig.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/tsconfig.json @@ -11,6 +11,12 @@ "references": [ { "path": "../exporter-trace-otlp-http" + }, + { + "path": "../opentelemetry-api-metrics" + }, + { + "path": "../opentelemetry-sdk-metrics-base" } ] } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json index 88b4c3b209..ebc9046432 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json @@ -49,7 +49,7 @@ }, "devDependencies": { "@babel/core": "7.16.0", - "@opentelemetry/api": "^1.0.3", + "@opentelemetry/api": "^1.1.0", "@opentelemetry/api-metrics": "0.27.0", "@types/mocha": "8.2.3", "@types/node": "14.17.33", @@ -65,15 +65,15 @@ "typescript": "4.4.4" }, "peerDependencies": { - "@opentelemetry/api": "^1.0.3" + "@opentelemetry/api": "^1.1.0" }, "dependencies": { "@grpc/proto-loader": "0.6.9", - "@opentelemetry/core": "1.0.1", + "@opentelemetry/core": "1.1.1", "@opentelemetry/exporter-metrics-otlp-http": "0.27.0", "@opentelemetry/exporter-trace-otlp-http": "0.27.0", "@opentelemetry/exporter-trace-otlp-proto": "0.27.0", - "@opentelemetry/resources": "1.0.1", + "@opentelemetry/resources": "1.1.1", "@opentelemetry/sdk-metrics-base": "0.27.0", "protobufjs": "^6.9.0" } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/src/OTLPMetricExporter.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/src/OTLPMetricExporter.ts index e7916a34e7..95648bb35e 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/src/OTLPMetricExporter.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/src/OTLPMetricExporter.ts @@ -19,27 +19,26 @@ import { OTLPExporterNodeConfigBase, appendResourcePathToUrlIfNotPresent, } from '@opentelemetry/exporter-trace-otlp-http'; -import { toOTLPExportMetricServiceRequest } from '@opentelemetry/exporter-metrics-otlp-http'; -import { MetricRecord, MetricExporter } from '@opentelemetry/sdk-metrics-base'; +import { + defaultExporterTemporality, + defaultOptions, + OTLPMetricExporterOptions, + toOTLPExportMetricServiceRequest +} from '@opentelemetry/exporter-metrics-otlp-http'; import { ServiceClientType, OTLPExporterNodeBase } from '@opentelemetry/exporter-trace-otlp-proto'; -import { getEnv, baggageUtils } from '@opentelemetry/core'; +import { getEnv, baggageUtils} from '@opentelemetry/core'; +import { AggregationTemporality, ResourceMetrics} from '@opentelemetry/sdk-metrics-base'; +import { OTLPMetricExporterBase } from '@opentelemetry/exporter-metrics-otlp-http'; const DEFAULT_COLLECTOR_RESOURCE_PATH = '/v1/metrics'; -const DEFAULT_COLLECTOR_URL=`http://localhost:4318${DEFAULT_COLLECTOR_RESOURCE_PATH}`; +const DEFAULT_COLLECTOR_URL = `http://localhost:4318${DEFAULT_COLLECTOR_RESOURCE_PATH}`; -/** - * OTLP Metric Exporter for Node with protobuf - */ -export class OTLPMetricExporter - extends OTLPExporterNodeBase< - MetricRecord, - otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest - > - implements MetricExporter { - // Converts time to nanoseconds - protected readonly _startTime = new Date().getTime() * 1000000; - constructor(config: OTLPExporterNodeConfigBase = {}) { +class OTLPMetricExporterNodeProxy extends OTLPExporterNodeBase { + protected readonly _aggregationTemporality: AggregationTemporality; + + constructor(config: OTLPExporterNodeConfigBase & OTLPMetricExporterOptions = defaultOptions) { super(config); this.headers = Object.assign( this.headers, @@ -47,14 +46,15 @@ export class OTLPMetricExporter getEnv().OTEL_EXPORTER_OTLP_METRICS_HEADERS ) ); + this._aggregationTemporality = config.aggregationTemporality ?? defaultExporterTemporality; } convert( - metrics: MetricRecord[] + metrics: ResourceMetrics[] ): otlpTypes.opentelemetryProto.collector.metrics.v1.ExportMetricsServiceRequest { return toOTLPExportMetricServiceRequest( - metrics, - this._startTime, + metrics[0], + this._aggregationTemporality, this ); } @@ -63,13 +63,19 @@ export class OTLPMetricExporter return typeof config.url === 'string' ? config.url : getEnv().OTEL_EXPORTER_OTLP_METRICS_ENDPOINT.length > 0 - ? getEnv().OTEL_EXPORTER_OTLP_METRICS_ENDPOINT - : getEnv().OTEL_EXPORTER_OTLP_ENDPOINT.length > 0 - ? appendResourcePathToUrlIfNotPresent(getEnv().OTEL_EXPORTER_OTLP_ENDPOINT, DEFAULT_COLLECTOR_RESOURCE_PATH) - : DEFAULT_COLLECTOR_URL; + ? getEnv().OTEL_EXPORTER_OTLP_METRICS_ENDPOINT + : getEnv().OTEL_EXPORTER_OTLP_ENDPOINT.length > 0 + ? appendResourcePathToUrlIfNotPresent(getEnv().OTEL_EXPORTER_OTLP_ENDPOINT, DEFAULT_COLLECTOR_RESOURCE_PATH) + : DEFAULT_COLLECTOR_URL; } getServiceClientType() { return ServiceClientType.METRICS; } } + +export class OTLPMetricExporter extends OTLPMetricExporterBase { + constructor(config: OTLPExporterNodeConfigBase & OTLPMetricExporterOptions = defaultOptions) { + super(new OTLPMetricExporterNodeProxy(config), config); + } +} diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/OTLPMetricExporter.test.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/OTLPMetricExporter.test.ts index 6c7e78507d..429096aff0 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/OTLPMetricExporter.test.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/OTLPMetricExporter.test.ts @@ -15,18 +15,12 @@ */ import { diag } from '@opentelemetry/api'; -import { - Counter, - ObservableGauge, - Histogram, -} from '@opentelemetry/api-metrics'; import { ExportResultCode } from '@opentelemetry/core'; import { OTLPExporterNodeConfigBase, otlpTypes, } from '@opentelemetry/exporter-trace-otlp-http'; import { getExportRequestProto } from '@opentelemetry/exporter-trace-otlp-proto'; -import * as metrics from '@opentelemetry/sdk-metrics-base'; import * as assert from 'assert'; import * as http from 'http'; import * as sinon from 'sinon'; @@ -40,19 +34,24 @@ import { mockCounter, MockedResponse, mockObservableGauge, - mockHistogram, + mockHistogram, collect, setUp, shutdown, } from './metricsHelper'; +import { AggregationTemporality, ResourceMetrics } from '@opentelemetry/sdk-metrics-base'; +import { OTLPMetricExporterOptions } from '@opentelemetry/exporter-metrics-otlp-http'; const fakeRequest = { - end: function () {}, - on: function () {}, - write: function () {}, + end: function () { + }, + on: function () { + }, + write: function () { + }, }; describe('OTLPMetricExporter - node with proto over http', () => { let collectorExporter: OTLPMetricExporter; - let collectorExporterConfig: OTLPExporterNodeConfigBase; - let metrics: metrics.MetricRecord[]; + let collectorExporterConfig: OTLPExporterNodeConfigBase & OTLPMetricExporterOptions; + let metrics: ResourceMetrics; describe('when configuring via environment', () => { const envSource = process.env; @@ -60,7 +59,7 @@ describe('OTLPMetricExporter - node with proto over http', () => { envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/v1/metrics'; const collectorExporter = new OTLPMetricExporter(); assert.strictEqual( - collectorExporter.url, + collectorExporter._otlpExporter.url, envSource.OTEL_EXPORTER_OTLP_ENDPOINT ); envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; @@ -69,7 +68,7 @@ describe('OTLPMetricExporter - node with proto over http', () => { envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar'; const collectorExporter = new OTLPMetricExporter(); assert.strictEqual( - collectorExporter.url, + collectorExporter._otlpExporter.url, `${envSource.OTEL_EXPORTER_OTLP_ENDPOINT}/v1/metrics` ); envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; @@ -79,7 +78,7 @@ describe('OTLPMetricExporter - node with proto over http', () => { envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = 'http://foo.metrics'; const collectorExporter = new OTLPMetricExporter(); assert.strictEqual( - collectorExporter.url, + collectorExporter._otlpExporter.url, envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT ); envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; @@ -88,15 +87,15 @@ describe('OTLPMetricExporter - node with proto over http', () => { it('should use headers defined via env', () => { envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar'; const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual(collectorExporter.headers.foo, 'bar'); + assert.strictEqual(collectorExporter._otlpExporter.headers.foo, 'bar'); envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; }); it('should override global headers config with signal headers defined via env', () => { envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar,bar=foo'; envSource.OTEL_EXPORTER_OTLP_METRICS_HEADERS = 'foo=boo'; const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual(collectorExporter.headers.foo, 'boo'); - assert.strictEqual(collectorExporter.headers.bar, 'foo'); + assert.strictEqual(collectorExporter._otlpExporter.headers.foo, 'boo'); + assert.strictEqual(collectorExporter._otlpExporter.headers.bar, 'foo'); envSource.OTEL_EXPORTER_OTLP_METRICS_HEADERS = ''; envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; }); @@ -113,37 +112,33 @@ describe('OTLPMetricExporter - node with proto over http', () => { url: 'http://foo.bar.com', keepAlive: true, httpAgentOptions: { keepAliveMsecs: 2000 }, + aggregationTemporality: AggregationTemporality.CUMULATIVE }; collectorExporter = new OTLPMetricExporter(collectorExporterConfig); - // Overwrites the start time to make tests consistent - Object.defineProperty(collectorExporter, '_startTime', { - value: 1592602232694000000, - }); - metrics = []; - const counter: metrics.Metric & - Counter = mockCounter(); - const observableGauge: metrics.Metric & - ObservableGauge = mockObservableGauge(observableResult => { + setUp(); + + const counter = mockCounter(); + mockObservableGauge(observableResult => { observableResult.observe(3, {}); observableResult.observe(6, {}); }); - const histogram: metrics.Metric & - Histogram = mockHistogram(); + const histogram = mockHistogram(); counter.add(1); histogram.record(7); histogram.record(14); - metrics.push((await counter.getMetricRecord())[0]); - metrics.push((await observableGauge.getMetricRecord())[0]); - metrics.push((await histogram.getMetricRecord())[0]); + metrics = await collect(); }); - afterEach(() => { + + afterEach(async () => { + await shutdown(); sinon.restore(); }); it('should open the connection', done => { - collectorExporter.export(metrics, () => {}); + collectorExporter.export(metrics, () => { + }); sinon.stub(http, 'request').callsFake((options: any) => { assert.strictEqual(options.hostname, 'foo.bar.com'); @@ -155,7 +150,8 @@ describe('OTLPMetricExporter - node with proto over http', () => { }); it('should set custom headers', done => { - collectorExporter.export(metrics, () => {}); + collectorExporter.export(metrics, () => { + }); sinon.stub(http, 'request').callsFake((options: any) => { assert.strictEqual(options.headers['foo'], 'bar'); @@ -165,7 +161,8 @@ describe('OTLPMetricExporter - node with proto over http', () => { }); it('should have keep alive and keepAliveMsecs option set', done => { - collectorExporter.export(metrics, () => {}); + collectorExporter.export(metrics, () => { + }); sinon.stub(http, 'request').callsFake((options: any) => { assert.strictEqual(options.agent.keepAlive, true); @@ -176,7 +173,8 @@ describe('OTLPMetricExporter - node with proto over http', () => { }); it('should successfully send metrics', done => { - collectorExporter.export(metrics, () => {}); + collectorExporter.export(metrics, () => { + }); sinon.stub(http, 'request').returns({ write: () => {}, @@ -196,12 +194,14 @@ describe('OTLPMetricExporter - node with proto over http', () => { assert.ok(typeof metric1 !== 'undefined', "counter doesn't exist"); ensureExportedCounterIsCorrect( metric1, - metric1.intSum?.dataPoints[0].timeUnixNano + metric1.intSum?.dataPoints[0].timeUnixNano, + metric1.intSum?.dataPoints[0].startTimeUnixNano ); assert.ok(typeof metric2 !== 'undefined', "observable gauge doesn't exist"); ensureExportedObservableGaugeIsCorrect( metric2, - metric2.doubleGauge?.dataPoints[0].timeUnixNano + metric2.doubleGauge?.dataPoints[0].timeUnixNano, + metric2.doubleGauge?.dataPoints[0].startTimeUnixNano ); assert.ok( typeof metric3 !== 'undefined', @@ -210,6 +210,7 @@ describe('OTLPMetricExporter - node with proto over http', () => { ensureExportedHistogramIsCorrect( metric3, metric3.intHistogram?.dataPoints[0].timeUnixNano, + metric3.intHistogram?.dataPoints[0].startTimeUnixNano, [0, 100], ['0', '2', '0'] ); 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 b400f76a33..8daa508726 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/metricsHelper.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/metricsHelper.ts @@ -17,72 +17,89 @@ import { Counter, ObservableResult, - ObservableGauge, Histogram, ValueType, } from '@opentelemetry/api-metrics'; import { otlpTypes } from '@opentelemetry/exporter-trace-otlp-http'; -import * as metrics from '@opentelemetry/sdk-metrics-base'; import { Resource } from '@opentelemetry/resources'; import * as assert from 'assert'; import { Stream } from 'stream'; +import { + ExplicitBucketHistogramAggregation, + MeterProvider, + MetricReader +} from '@opentelemetry/sdk-metrics-base'; + +export class TestMetricReader extends MetricReader { + protected onForceFlush(): Promise { + return Promise.resolve(undefined); + } + + protected onShutdown(): Promise { + return Promise.resolve(undefined); + } +} + +const testResource = Resource.default().merge(new Resource({ + service: 'ui', + version: 1, + cost: 112.12, +})); + +let meterProvider = new MeterProvider({ resource: testResource }); -const meterProvider = new metrics.MeterProvider({ - interval: 30000, - resource: new Resource({ - service: 'ui', - version: 1, - cost: 112.12, - }), -}); +let reader = new TestMetricReader(); +meterProvider.addMetricReader(reader); + +let meter = meterProvider.getMeter('default', '0.0.1'); + +export async function collect() { + return (await reader.collect())!; +} + +export function setUp() { + meterProvider = new MeterProvider({ resource: testResource }); + reader = new TestMetricReader(); + meterProvider.addMetricReader( + reader + ); + meter = meterProvider.getMeter('default', '0.0.1'); +} -const meter = meterProvider.getMeter('default', '0.0.1'); +export async function shutdown() { + await meterProvider.shutdown(); +} -export function mockCounter(): metrics.Metric & Counter { +export function mockCounter(): Counter { const name = 'int-counter'; - const metric = - meter['_metrics'].get(name) || - meter.createCounter(name, { - description: 'sample counter description', - valueType: ValueType.INT, - }); - metric.clear(); - metric.bind({}); - return metric; + return meter.createCounter(name, { + description: 'sample counter description', + valueType: ValueType.INT, + }); } export function mockObservableGauge( callback: (observableResult: ObservableResult) => void -): metrics.Metric & ObservableGauge { +): void { const name = 'double-observable-gauge'; - const metric = - meter['_metrics'].get(name) || - meter.createObservableGauge( - name, - { - description: 'sample observable gauge description', - valueType: ValueType.DOUBLE, - }, - callback - ); - metric.clear(); - metric.bind({}); - return metric; + return meter.createObservableGauge( + name, + callback, + { + description: 'sample observable gauge description', + valueType: ValueType.DOUBLE, + }, + ); } -export function mockHistogram(): metrics.Metric & - Histogram { +export function mockHistogram(): Histogram { const name = 'int-histogram'; - const metric = - meter['_metrics'].get(name) || - meter.createHistogram(name, { - description: 'sample histogram description', - valueType: ValueType.INT, - boundaries: [0, 100], - }); - metric.clear(); - metric.bind({}); - return metric; + meterProvider.addView({ aggregation: new ExplicitBucketHistogramAggregation([0, 100]) }); + + return meter.createHistogram(name, { + description: 'sample histogram description', + valueType: ValueType.INT, + }); } export function ensureProtoAttributesAreCorrect( @@ -104,7 +121,8 @@ export function ensureProtoAttributesAreCorrect( export function ensureExportedCounterIsCorrect( metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, - time?: number + time?: number, + startTime?: number ) { assert.deepStrictEqual(metric, { name: 'int-counter', @@ -114,7 +132,7 @@ export function ensureExportedCounterIsCorrect( dataPoints: [ { value: '1', - startTimeUnixNano: '1592602232694000128', + startTimeUnixNano: String(startTime), timeUnixNano: String(time), }, ], @@ -126,7 +144,8 @@ export function ensureExportedCounterIsCorrect( export function ensureExportedObservableGaugeIsCorrect( metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, - time?: number + time?: number, + startTime?: number ) { assert.deepStrictEqual(metric, { name: 'double-observable-gauge', @@ -136,7 +155,7 @@ export function ensureExportedObservableGaugeIsCorrect( dataPoints: [ { value: 6, - startTimeUnixNano: '1592602232694000128', + startTimeUnixNano: String(startTime), timeUnixNano: String(time), }, ], @@ -147,6 +166,7 @@ export function ensureExportedObservableGaugeIsCorrect( export function ensureExportedHistogramIsCorrect( metric: otlpTypes.opentelemetryProto.metrics.v1.Metric, time?: number, + startTime?: number, explicitBounds: number[] = [Infinity], bucketCounts: string[] = ['2', '0'] ) { @@ -159,8 +179,8 @@ export function ensureExportedHistogramIsCorrect( { sum: '21', count: '2', - startTimeUnixNano: '1592602232694000128', - timeUnixNano: time, + startTimeUnixNano: String(startTime), + timeUnixNano: String(time), bucketCounts, explicitBounds, }, diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/tsconfig.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/tsconfig.json index 86457b9d6a..7564c309e6 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/tsconfig.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/tsconfig.json @@ -15,8 +15,14 @@ { "path": "../exporter-trace-otlp-proto" }, + { + "path": "../opentelemetry-api-metrics" + }, { "path": "../opentelemetry-exporter-metrics-otlp-http" + }, + { + "path": "../opentelemetry-sdk-metrics-base" } ] } diff --git a/experimental/packages/opentelemetry-exporter-prometheus/package.json b/experimental/packages/opentelemetry-exporter-prometheus/package.json index 008489a4a6..bd909e78ba 100644 --- a/experimental/packages/opentelemetry-exporter-prometheus/package.json +++ b/experimental/packages/opentelemetry-exporter-prometheus/package.json @@ -59,8 +59,8 @@ "@opentelemetry/api": "^1.0.3" }, "dependencies": { - "@opentelemetry/api-metrics-wip": "0.27.0", - "@opentelemetry/core": "1.0.1", - "@opentelemetry/sdk-metrics-base-wip": "0.27.0" + "@opentelemetry/api-metrics": "0.27.0", + "@opentelemetry/core": "1.1.1", + "@opentelemetry/sdk-metrics-base": "0.27.0" } } diff --git a/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusExporter.ts b/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusExporter.ts index bf7da294b0..0be01d6c75 100644 --- a/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusExporter.ts +++ b/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusExporter.ts @@ -18,7 +18,7 @@ import { diag } from '@opentelemetry/api'; import { globalErrorHandler, } from '@opentelemetry/core'; -import { AggregationTemporality, MetricReader } from '@opentelemetry/sdk-metrics-base-wip'; +import { AggregationTemporality, MetricReader } from '@opentelemetry/sdk-metrics-base'; import { createServer, IncomingMessage, Server, ServerResponse } from 'http'; import { ExporterConfig } from './export/types'; import { PrometheusSerializer } from './PrometheusSerializer'; diff --git a/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusSerializer.ts b/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusSerializer.ts index 287666b3c9..360208c8a6 100644 --- a/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusSerializer.ts +++ b/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusSerializer.ts @@ -23,8 +23,8 @@ import { MetricData, DataPoint, Histogram, -} from '@opentelemetry/sdk-metrics-base-wip'; -import { Attributes } from '@opentelemetry/api-metrics-wip'; +} from '@opentelemetry/sdk-metrics-base'; +import { Attributes } from '@opentelemetry/api-metrics'; import { hrTimeToMilliseconds } from '@opentelemetry/core'; type PrometheusDataTypeLiteral = diff --git a/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusExporter.test.ts b/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusExporter.test.ts index 0b3482622d..0f3d507de9 100644 --- a/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusExporter.test.ts +++ b/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusExporter.test.ts @@ -14,17 +14,17 @@ * limitations under the License. */ -import { Meter, ObservableResult } from '@opentelemetry/api-metrics-wip'; +import { Meter, ObservableResult } from '@opentelemetry/api-metrics'; import { MeterProvider, -} from '@opentelemetry/sdk-metrics-base-wip'; +} from '@opentelemetry/sdk-metrics-base'; import * as assert from 'assert'; import * as sinon from 'sinon'; import * as http from 'http'; import { PrometheusExporter } from '../src'; import { mockedHrTimeMs, mockHrTime } from './util'; import { SinonStubbedInstance } from 'sinon'; -import { Counter } from '@opentelemetry/api-metrics-wip'; +import { Counter } from '@opentelemetry/api-metrics'; describe('PrometheusExporter', () => { beforeEach(() => { diff --git a/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusSerializer.test.ts b/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusSerializer.test.ts index 23f836c039..55a5ee9b6a 100644 --- a/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusSerializer.test.ts +++ b/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusSerializer.test.ts @@ -15,7 +15,7 @@ */ import * as assert from 'assert'; -import { Attributes, UpDownCounter } from '@opentelemetry/api-metrics-wip'; +import { Attributes, UpDownCounter } from '@opentelemetry/api-metrics'; import { AggregationTemporality, MeterProvider, @@ -25,7 +25,7 @@ import { ExplicitBucketHistogramAggregation, SumAggregation, Histogram, -} from '@opentelemetry/sdk-metrics-base-wip'; +} from '@opentelemetry/sdk-metrics-base'; import * as sinon from 'sinon'; import { PrometheusSerializer } from '../src/PrometheusSerializer'; import { mockedHrTimeMs, mockHrTime } from './util'; diff --git a/experimental/packages/opentelemetry-instrumentation-fetch/package.json b/experimental/packages/opentelemetry-instrumentation-fetch/package.json index 62505e2425..2b7c88bff8 100644 --- a/experimental/packages/opentelemetry-instrumentation-fetch/package.json +++ b/experimental/packages/opentelemetry-instrumentation-fetch/package.json @@ -54,10 +54,10 @@ }, "devDependencies": { "@babel/core": "7.16.0", - "@opentelemetry/api": "^1.0.3", - "@opentelemetry/context-zone": "1.0.1", - "@opentelemetry/propagator-b3": "1.0.1", - "@opentelemetry/sdk-trace-base": "1.0.1", + "@opentelemetry/api": "^1.1.0", + "@opentelemetry/context-zone": "1.1.1", + "@opentelemetry/propagator-b3": "1.1.1", + "@opentelemetry/sdk-trace-base": "1.1.1", "@types/mocha": "8.2.3", "@types/node": "14.17.33", "@types/sinon": "10.0.6", @@ -83,12 +83,12 @@ "webpack-merge": "5.8.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.0.3" + "@opentelemetry/api": "^1.1.0" }, "dependencies": { - "@opentelemetry/core": "1.0.1", + "@opentelemetry/core": "1.1.1", "@opentelemetry/instrumentation": "0.27.0", - "@opentelemetry/sdk-trace-web": "1.0.1", - "@opentelemetry/semantic-conventions": "1.0.1" + "@opentelemetry/sdk-trace-web": "1.1.1", + "@opentelemetry/semantic-conventions": "1.1.1" } } diff --git a/experimental/packages/opentelemetry-instrumentation-fetch/src/fetch.ts b/experimental/packages/opentelemetry-instrumentation-fetch/src/fetch.ts index 939da135f2..8c02cc1ede 100644 --- a/experimental/packages/opentelemetry-instrumentation-fetch/src/fetch.ts +++ b/experimental/packages/opentelemetry-instrumentation-fetch/src/fetch.ts @@ -29,6 +29,12 @@ import { FetchError, FetchResponse, SpanData } from './types'; import { VERSION } from './version'; import { _globalThis } from '@opentelemetry/core'; +function parseUrl(url: string): web.URLLike { + const element = document.createElement('a'); + element.href = url; + return element; +} + // how long to wait for observer to collect information about resources // this is needed as event "load" is called before observer // hard to say how long it should really wait, seems like 300ms is @@ -68,9 +74,7 @@ export interface FetchInstrumentationConfig extends InstrumentationConfig { /** * This class represents a fetch plugin for auto instrumentation */ -export class FetchInstrumentation extends InstrumentationBase< - Promise -> { +export class FetchInstrumentation extends InstrumentationBase> { readonly component: string = 'fetch'; readonly version: string = VERSION; moduleName = this.component; @@ -122,7 +126,7 @@ export class FetchInstrumentation extends InstrumentationBase< span: api.Span, response: FetchResponse ): void { - const parsedUrl = web.parseUrl(response.url); + const parsedUrl = parseUrl(response.url); span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, response.status); if (response.statusText != null) { span.setAttribute(AttributeNames.HTTP_STATUS_TEXT, response.statusText); @@ -159,7 +163,7 @@ export class FetchInstrumentation extends InstrumentationBase< api.propagation.inject(api.context.active(), options.headers, { set: (h, k, v) => h.set(k, typeof v === 'string' ? v : String(v)), }); - } else if(options.headers instanceof Headers) { + } else if (options.headers instanceof Headers) { api.propagation.inject(api.context.active(), options.headers, { set: (h, k, v) => h.set(k, typeof v === 'string' ? v : String(v)), }); @@ -297,7 +301,8 @@ export class FetchInstrumentation extends InstrumentationBase< ...args: Parameters ): Promise { const self = this; - const url = args[0] instanceof Request ? args[0].url : args[0]; + const url = parseUrl(args[0] instanceof Request ? args[0].url : args[0]).href; + const options = args[0] instanceof Request ? args[0] : args[1] || {}; const createdSpan = plugin._createSpan(url, options); if (!createdSpan) { @@ -316,16 +321,24 @@ export class FetchInstrumentation extends InstrumentationBase< function endSpanOnSuccess(span: api.Span, response: Response) { plugin._applyAttributesAfterFetch(span, options, response); + const spanResponse = { + status: response.status, + statusText: response.statusText, + headers: response.headers, + url + }; if (response.status >= 200 && response.status < 400) { - plugin._endSpan(span, spanData, response); - } else { - plugin._endSpan(span, spanData, { - status: response.status, - statusText: response.statusText, - url, - }); + if (response.url != null && response.url !== '') { + spanResponse.url = url; + } } + plugin._endSpan(span, spanData, { + status: response.status, + statusText: response.statusText, + url, + }); } + function onSuccess( span: api.Span, resolve: (value: Response | PromiseLike) => void, @@ -430,7 +443,7 @@ export class FetchInstrumentation extends InstrumentationBase< const observer: PerformanceObserver = new PerformanceObserver(list => { const perfObsEntries = list.getEntries() as PerformanceResourceTiming[]; - const parsedUrl = web.parseUrl(spanUrl); + const parsedUrl = parseUrl(spanUrl); perfObsEntries.forEach(entry => { if ( entry.initiatorType === 'fetch' && diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/package.json b/experimental/packages/opentelemetry-instrumentation-grpc/package.json index 74da3d8def..4e35650bc8 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/package.json +++ b/experimental/packages/opentelemetry-instrumentation-grpc/package.json @@ -46,11 +46,11 @@ "devDependencies": { "@grpc/grpc-js": "1.5.9", "@grpc/proto-loader": "0.6.9", - "@opentelemetry/api": "^1.0.3", - "@opentelemetry/context-async-hooks": "1.0.1", - "@opentelemetry/core": "1.0.1", - "@opentelemetry/sdk-trace-base": "1.0.1", - "@opentelemetry/sdk-trace-node": "1.0.1", + "@opentelemetry/api": "^1.1.0", + "@opentelemetry/context-async-hooks": "1.1.1", + "@opentelemetry/core": "1.1.1", + "@opentelemetry/sdk-trace-base": "1.1.1", + "@opentelemetry/sdk-trace-node": "1.1.1", "@types/mocha": "8.2.3", "@types/node": "14.17.33", "@types/semver": "7.3.9", @@ -67,11 +67,11 @@ "typescript": "4.4.4" }, "peerDependencies": { - "@opentelemetry/api": "^1.0.3" + "@opentelemetry/api": "^1.1.0" }, "dependencies": { "@opentelemetry/api-metrics": "0.27.0", "@opentelemetry/instrumentation": "0.27.0", - "@opentelemetry/semantic-conventions": "1.0.1" + "@opentelemetry/semantic-conventions": "1.1.1" } } diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/tsconfig.json b/experimental/packages/opentelemetry-instrumentation-grpc/tsconfig.json index b172f492db..b0e33797f4 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/tsconfig.json +++ b/experimental/packages/opentelemetry-instrumentation-grpc/tsconfig.json @@ -9,6 +9,9 @@ "test/**/*.ts" ], "references": [ + { + "path": "../opentelemetry-api-metrics" + }, { "path": "../opentelemetry-instrumentation" } diff --git a/experimental/packages/opentelemetry-instrumentation-http/package.json b/experimental/packages/opentelemetry-instrumentation-http/package.json index 3db5b2478a..6337d43068 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/package.json +++ b/experimental/packages/opentelemetry-instrumentation-http/package.json @@ -44,10 +44,10 @@ "access": "public" }, "devDependencies": { - "@opentelemetry/api": "^1.0.3", - "@opentelemetry/context-async-hooks": "1.0.1", - "@opentelemetry/sdk-trace-base": "1.0.1", - "@opentelemetry/sdk-trace-node": "1.0.1", + "@opentelemetry/api": "^1.1.0", + "@opentelemetry/context-async-hooks": "1.1.1", + "@opentelemetry/sdk-trace-base": "1.1.1", + "@opentelemetry/sdk-trace-node": "1.1.1", "@types/got": "9.6.12", "@types/mocha": "8.2.3", "@types/node": "14.17.33", @@ -70,12 +70,12 @@ "typescript": "4.4.4" }, "peerDependencies": { - "@opentelemetry/api": "^1.0.3" + "@opentelemetry/api": "^1.1.0" }, "dependencies": { - "@opentelemetry/core": "1.0.1", + "@opentelemetry/core": "1.1.1", "@opentelemetry/instrumentation": "0.27.0", - "@opentelemetry/semantic-conventions": "1.0.1", + "@opentelemetry/semantic-conventions": "1.1.1", "semver": "^7.3.5" } } diff --git a/experimental/packages/opentelemetry-instrumentation-xml-http-request/package.json b/experimental/packages/opentelemetry-instrumentation-xml-http-request/package.json index d863e6aa9d..1505e20f4b 100644 --- a/experimental/packages/opentelemetry-instrumentation-xml-http-request/package.json +++ b/experimental/packages/opentelemetry-instrumentation-xml-http-request/package.json @@ -54,10 +54,10 @@ }, "devDependencies": { "@babel/core": "7.16.0", - "@opentelemetry/api": "^1.0.3", - "@opentelemetry/context-zone": "1.0.1", - "@opentelemetry/propagator-b3": "1.0.1", - "@opentelemetry/sdk-trace-base": "1.0.1", + "@opentelemetry/api": "^1.1.0", + "@opentelemetry/context-zone": "1.1.1", + "@opentelemetry/propagator-b3": "1.1.1", + "@opentelemetry/sdk-trace-base": "1.1.1", "@types/mocha": "8.2.3", "@types/node": "14.17.33", "@types/sinon": "10.0.6", @@ -83,12 +83,12 @@ "webpack-merge": "5.8.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.0.3" + "@opentelemetry/api": "^1.1.0" }, "dependencies": { - "@opentelemetry/core": "1.0.1", + "@opentelemetry/core": "1.1.1", "@opentelemetry/instrumentation": "0.27.0", - "@opentelemetry/sdk-trace-web": "1.0.1", - "@opentelemetry/semantic-conventions": "1.0.1" + "@opentelemetry/sdk-trace-web": "1.1.1", + "@opentelemetry/semantic-conventions": "1.1.1" } } diff --git a/experimental/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts b/experimental/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts index dfadaed386..011dcfe8d1 100644 --- a/experimental/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts +++ b/experimental/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts @@ -26,9 +26,9 @@ import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import { addSpanNetworkEvents, getResource, - parseUrl, PerformanceTimingNames as PTN, shouldPropagateTraceHeaders, + URLLike } from '@opentelemetry/sdk-trace-web'; import { EventNames } from './enums/EventNames'; import { @@ -40,6 +40,12 @@ import { import { VERSION } from './version'; import { AttributeNames } from './enums/AttributeNames'; +function parseUrl(url: string): URLLike { + const element = document.createElement('a'); + element.href = url; + return element; +} + // how long to wait for observer to collect information about resources // this is needed as event "load" is called before observer // hard to say how long it should really wait, seems like 300ms is @@ -105,13 +111,14 @@ export class XMLHttpRequestInstrumentation extends InstrumentationBase { describe( 'AND origin does NOT match window.location but match with' + - ' propagateTraceHeaderCorsUrls', + ' propagateTraceHeaderCorsUrls', () => { beforeEach(done => { clearData(); @@ -590,7 +602,7 @@ describe('xhr', () => { ); describe( 'AND origin does NOT match window.location And does NOT match' + - ' with propagateTraceHeaderCorsUrls', + ' with propagateTraceHeaderCorsUrls', () => { let spyDebug: sinon.SinonSpy; beforeEach(done => { @@ -866,7 +878,8 @@ describe('xhr', () => { function abortedRequest(done: any) { api.context.with(api.trace.setSpan(api.context.active(), rootSpan), () => { - void getData(new XMLHttpRequest(), url, () => {}, testAsync).then( + void getData(new XMLHttpRequest(), url, () => { + }, testAsync).then( () => { fakeNow = 0; sinon.clock.tick(1000); @@ -904,7 +917,8 @@ describe('xhr', () => { function networkErrorRequest(done: any) { api.context.with(api.trace.setSpan(api.context.active(), rootSpan), () => { - void getData(new XMLHttpRequest(), url, () => {}, testAsync).then( + void getData(new XMLHttpRequest(), url, () => { + }, testAsync).then( () => { fakeNow = 0; sinon.clock.tick(1000); diff --git a/experimental/packages/opentelemetry-instrumentation/tsconfig.json b/experimental/packages/opentelemetry-instrumentation/tsconfig.json index ed9d0830bd..948abef3ce 100644 --- a/experimental/packages/opentelemetry-instrumentation/tsconfig.json +++ b/experimental/packages/opentelemetry-instrumentation/tsconfig.json @@ -7,5 +7,10 @@ "include": [ "src/**/*.ts", "test/**/*.ts" + ], + "references": [ + { + "path": "../opentelemetry-api-metrics" + } ] } diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/package.json b/experimental/packages/opentelemetry-sdk-metrics-base/package.json index 7ec1182922..8ccf00b691 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/package.json +++ b/experimental/packages/opentelemetry-sdk-metrics-base/package.json @@ -1,5 +1,5 @@ { - "name": "@opentelemetry/sdk-metrics-base-wip", + "name": "@opentelemetry/sdk-metrics-base", "version": "0.27.0", "private": true, "description": "Work in progress OpenTelemetry metrics SDK", @@ -78,9 +78,9 @@ }, "todo": "Move API metrics to peer dependencies. While it is using an unpublished name, lerna doesn't properly link it if it is in peer dependencies", "dependencies": { - "@opentelemetry/api-metrics-wip": "0.27.0", - "@opentelemetry/core": "1.0.1", - "@opentelemetry/resources": "1.0.1", + "@opentelemetry/api-metrics": "0.27.0", + "@opentelemetry/core": "1.1.1", + "@opentelemetry/resources": "1.1.1", "lodash.merge": "4.6.2" } } diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/InstrumentDescriptor.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/InstrumentDescriptor.ts index 8b4347e3e4..81b19b7ec1 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/InstrumentDescriptor.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/InstrumentDescriptor.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { MetricOptions, ValueType } from '@opentelemetry/api-metrics-wip'; +import { MetricOptions, ValueType } from '@opentelemetry/api-metrics'; import { View } from './view/View'; /** diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/Instruments.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/Instruments.ts index 97d31712bc..7b3d51b7ec 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/Instruments.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/Instruments.ts @@ -15,7 +15,7 @@ */ import * as api from '@opentelemetry/api'; -import * as metrics from '@opentelemetry/api-metrics-wip'; +import * as metrics from '@opentelemetry/api-metrics'; import { InstrumentDescriptor } from './InstrumentDescriptor'; import { WritableMetricStorage } from './state/WritableMetricStorage'; diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/Measurement.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/Measurement.ts index a83b42843c..2beb5959a5 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/Measurement.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/Measurement.ts @@ -15,7 +15,7 @@ */ import * as api from '@opentelemetry/api'; -import { Attributes } from '@opentelemetry/api-metrics-wip'; +import { Attributes } from '@opentelemetry/api-metrics'; // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#measurement diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/Meter.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/Meter.ts index 4cc1236e18..bd298c7335 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/Meter.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/Meter.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import * as metrics from '@opentelemetry/api-metrics-wip'; +import * as metrics from '@opentelemetry/api-metrics'; import { InstrumentationLibrary } from '@opentelemetry/core'; import { createInstrumentDescriptor, InstrumentDescriptor, InstrumentType } from './InstrumentDescriptor'; import { CounterInstrument, HistogramInstrument, UpDownCounterInstrument } from './Instruments'; diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/MeterProvider.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/MeterProvider.ts index c4215f2de1..e4030087ce 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/MeterProvider.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/MeterProvider.ts @@ -15,7 +15,7 @@ */ import * as api from '@opentelemetry/api'; -import * as metrics from '@opentelemetry/api-metrics-wip'; +import * as metrics from '@opentelemetry/api-metrics'; import { Resource } from '@opentelemetry/resources'; import { Meter } from './Meter'; import { MetricReader } from './export/MetricReader'; diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/ObservableResult.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/ObservableResult.ts index 7ca9f9aea1..3251027d89 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/ObservableResult.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/ObservableResult.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import * as metrics from '@opentelemetry/api-metrics-wip'; +import * as metrics from '@opentelemetry/api-metrics'; import { AttributeHashMap } from './state/HashMap'; /** diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/AlignedHistogramBucketExemplarReservoir.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/AlignedHistogramBucketExemplarReservoir.ts index f69487ec06..bfd0a524e4 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/AlignedHistogramBucketExemplarReservoir.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/AlignedHistogramBucketExemplarReservoir.ts @@ -16,7 +16,7 @@ import { Context, HrTime } from '@opentelemetry/api'; -import { Attributes } from '@opentelemetry/api-metrics-wip'; +import { Attributes } from '@opentelemetry/api-metrics'; import { FixedSizeExemplarReservoirBase } from './ExemplarReservoir'; diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/AlwaysSampleExemplarFilter.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/AlwaysSampleExemplarFilter.ts index 349cf9fea7..154025df60 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/AlwaysSampleExemplarFilter.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/AlwaysSampleExemplarFilter.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Attributes } from '@opentelemetry/api-metrics-wip'; +import { Attributes } from '@opentelemetry/api-metrics'; import { Context, HrTime } from '@opentelemetry/api'; import { ExemplarFilter } from './ExemplarFilter'; diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/Exemplar.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/Exemplar.ts index 1b22c086e2..4c7b809baa 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/Exemplar.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/Exemplar.ts @@ -15,7 +15,7 @@ */ import { HrTime } from '@opentelemetry/api'; -import { Attributes } from '@opentelemetry/api-metrics-wip'; +import { Attributes } from '@opentelemetry/api-metrics'; /** * A representation of an exemplar, which is a sample input measurement. diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/ExemplarFilter.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/ExemplarFilter.ts index e3f89b4305..9f0e236350 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/ExemplarFilter.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/ExemplarFilter.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Attributes } from '@opentelemetry/api-metrics-wip'; +import { Attributes } from '@opentelemetry/api-metrics'; import { Context, HrTime } from '@opentelemetry/api'; /** diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/ExemplarReservoir.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/ExemplarReservoir.ts index a6eb587722..8e8fd61bcd 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/ExemplarReservoir.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/ExemplarReservoir.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Attributes } from '@opentelemetry/api-metrics-wip'; +import { Attributes } from '@opentelemetry/api-metrics'; import { Context, HrTime, isSpanContextValid, trace } from '@opentelemetry/api'; import { Exemplar } from './Exemplar'; diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/NeverSampleExemplarFilter.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/NeverSampleExemplarFilter.ts index a66a755fe2..815d8a97dd 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/NeverSampleExemplarFilter.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/NeverSampleExemplarFilter.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Attributes } from '@opentelemetry/api-metrics-wip'; +import { Attributes } from '@opentelemetry/api-metrics'; import { Context, HrTime } from '@opentelemetry/api'; import { ExemplarFilter } from './ExemplarFilter'; diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/SimpleFixedSizeExemplarReservoir.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/SimpleFixedSizeExemplarReservoir.ts index ddf7ef268d..1dac0b8a53 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/SimpleFixedSizeExemplarReservoir.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/SimpleFixedSizeExemplarReservoir.ts @@ -15,7 +15,7 @@ */ import { Context, HrTime } from '@opentelemetry/api'; -import { Attributes } from '@opentelemetry/api-metrics-wip'; +import { Attributes } from '@opentelemetry/api-metrics'; import { FixedSizeExemplarReservoirBase } from './ExemplarReservoir'; /** diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/WithTraceExemplarFilter.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/WithTraceExemplarFilter.ts index 77f3fb02af..3e48171184 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/WithTraceExemplarFilter.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/exemplar/WithTraceExemplarFilter.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Attributes } from '@opentelemetry/api-metrics-wip'; +import { Attributes } from '@opentelemetry/api-metrics'; import { Context, HrTime, isSpanContextValid, trace, TraceFlags } from '@opentelemetry/api'; import { ExemplarFilter } from './ExemplarFilter'; diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/index.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/index.ts index 49bcf00505..2b718a6f24 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/index.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/index.ts @@ -23,10 +23,10 @@ export * from './export/MetricProducer'; export * from './export/MetricReader'; export * from './export/PeriodicExportingMetricReader'; export { InstrumentDescriptor, InstrumentType } from './InstrumentDescriptor'; -export * from './Instruments'; export * from './Meter'; export * from './MeterProvider'; export * from './ObservableResult'; export { TimeoutError } from './utils'; export * from './view/Aggregation'; export { FilteringAttributesProcessor } from './view/AttributesProcessor'; +export * from './aggregator/types'; diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/state/AsyncMetricStorage.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/state/AsyncMetricStorage.ts index b842f1d24b..44f95b3c4c 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/state/AsyncMetricStorage.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/state/AsyncMetricStorage.ts @@ -15,7 +15,7 @@ */ import { HrTime } from '@opentelemetry/api'; -import { ObservableCallback } from '@opentelemetry/api-metrics-wip'; +import { ObservableCallback } from '@opentelemetry/api-metrics'; import { Accumulation, Aggregator } from '../aggregator/types'; import { View } from '../view/View'; import { diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/state/DeltaMetricProcessor.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/state/DeltaMetricProcessor.ts index 590a78521e..3e25ce0ade 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/state/DeltaMetricProcessor.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/state/DeltaMetricProcessor.ts @@ -15,7 +15,7 @@ */ import { Context } from '@opentelemetry/api'; -import { Attributes } from '@opentelemetry/api-metrics-wip'; +import { Attributes } from '@opentelemetry/api-metrics'; import { Maybe } from '../utils'; import { Accumulation, Aggregator } from '../aggregator/types'; import { AttributeHashMap } from './HashMap'; diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/state/HashMap.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/state/HashMap.ts index 879e5db737..be30996325 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/state/HashMap.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/state/HashMap.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Attributes } from '@opentelemetry/api-metrics-wip'; +import { Attributes } from '@opentelemetry/api-metrics'; import { hashAttributes } from '../utils'; export interface Hash { diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/state/MultiWritableMetricStorage.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/state/MultiWritableMetricStorage.ts index 50e3dbc103..ad51708d55 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/state/MultiWritableMetricStorage.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/state/MultiWritableMetricStorage.ts @@ -15,7 +15,7 @@ */ import { Context } from '@opentelemetry/api'; -import { Attributes } from '@opentelemetry/api-metrics-wip'; +import { Attributes } from '@opentelemetry/api-metrics'; import { WritableMetricStorage } from './WritableMetricStorage'; /** diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/state/SyncMetricStorage.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/state/SyncMetricStorage.ts index 4812bab756..a22530f7b1 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/state/SyncMetricStorage.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/state/SyncMetricStorage.ts @@ -15,7 +15,7 @@ */ import { Context, HrTime } from '@opentelemetry/api'; -import { Attributes } from '@opentelemetry/api-metrics-wip'; +import { Attributes } from '@opentelemetry/api-metrics'; import { WritableMetricStorage } from './WritableMetricStorage'; import { Accumulation, Aggregator } from '../aggregator/types'; import { View } from '../view/View'; diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/state/WritableMetricStorage.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/state/WritableMetricStorage.ts index 4233dedd24..7d30d0ee90 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/state/WritableMetricStorage.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/state/WritableMetricStorage.ts @@ -15,7 +15,7 @@ */ import { Context } from '@opentelemetry/api'; -import { Attributes } from '@opentelemetry/api-metrics-wip'; +import { Attributes } from '@opentelemetry/api-metrics'; /** * Internal interface. diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/utils.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/utils.ts index 5ae4e0ee10..4fa84fd4ca 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/utils.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/utils.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Attributes } from '@opentelemetry/api-metrics-wip'; +import { Attributes } from '@opentelemetry/api-metrics'; export type Maybe = T | undefined; diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/src/view/AttributesProcessor.ts b/experimental/packages/opentelemetry-sdk-metrics-base/src/view/AttributesProcessor.ts index 0a6aa4706e..562bfcef92 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/src/view/AttributesProcessor.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/src/view/AttributesProcessor.ts @@ -15,7 +15,7 @@ */ import { Context } from '@opentelemetry/api'; -import { Attributes } from '@opentelemetry/api-metrics-wip'; +import { Attributes } from '@opentelemetry/api-metrics'; /** * The {@link AttributesProcessor} is responsible for customizing which diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/test/Instruments.test.ts b/experimental/packages/opentelemetry-sdk-metrics-base/test/Instruments.test.ts index fd5b39fed1..56c16dbac4 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/test/Instruments.test.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/test/Instruments.test.ts @@ -22,7 +22,7 @@ import { AggregationTemporality, InstrumentDescriptor, InstrumentType, MeterProv import { TestMetricReader } from './export/TestMetricReader'; import { assertMetricData, assertDataPoint, commonValues, commonAttributes, defaultResource, defaultInstrumentationLibrary } from './util'; import { Histogram } from '../src/aggregator/types'; -import { ObservableResult, ValueType } from '@opentelemetry/api-metrics-wip'; +import { ObservableResult, ValueType } from '@opentelemetry/api-metrics'; describe('Instruments', () => { describe('Counter', () => { diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/test/Meter.test.ts b/experimental/packages/opentelemetry-sdk-metrics-base/test/Meter.test.ts index 6f5373046c..40d22831cd 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/test/Meter.test.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/test/Meter.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { ObservableCallback } from '@opentelemetry/api-metrics-wip'; +import { ObservableCallback } from '@opentelemetry/api-metrics'; import * as assert from 'assert'; import { CounterInstrument, HistogramInstrument, UpDownCounterInstrument } from '../src/Instruments'; import { Meter } from '../src/Meter'; diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/test/MeterProvider.test.ts b/experimental/packages/opentelemetry-sdk-metrics-base/test/MeterProvider.test.ts index 846174b179..93d6aebdc2 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/test/MeterProvider.test.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/test/MeterProvider.test.ts @@ -15,7 +15,7 @@ */ import * as assert from 'assert'; -import { NOOP_METER } from '@opentelemetry/api-metrics-wip'; +import { NOOP_METER } from '@opentelemetry/api-metrics'; import { Meter, MeterProvider, InstrumentType, DataPointType } from '../src'; import { assertInstrumentationLibraryMetrics, diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/test/state/AsyncMetricStorage.test.ts b/experimental/packages/opentelemetry-sdk-metrics-base/test/state/AsyncMetricStorage.test.ts index 6ccc5b41dd..aa6e824666 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/test/state/AsyncMetricStorage.test.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/test/state/AsyncMetricStorage.test.ts @@ -24,7 +24,7 @@ import { MetricCollectorHandle } from '../../src/state/MetricCollector'; import { AsyncMetricStorage } from '../../src/state/AsyncMetricStorage'; import { NoopAttributesProcessor } from '../../src/view/AttributesProcessor'; import { assertMetricData, assertDataPoint, defaultInstrumentDescriptor } from '../util'; -import { ObservableCallback } from '@opentelemetry/api-metrics-wip'; +import { ObservableCallback } from '@opentelemetry/api-metrics'; const deltaCollector: MetricCollectorHandle = { aggregatorTemporality: AggregationTemporality.DELTA, diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/test/state/HashMap.test.ts b/experimental/packages/opentelemetry-sdk-metrics-base/test/state/HashMap.test.ts index 94d399d5ab..8a4ac7da01 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/test/state/HashMap.test.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/test/state/HashMap.test.ts @@ -15,7 +15,7 @@ */ import * as assert from 'assert'; -import { Attributes } from '@opentelemetry/api-metrics-wip'; +import { Attributes } from '@opentelemetry/api-metrics'; import { HashMap } from '../../src/state/HashMap'; import { hashAttributes } from '../../src/utils'; diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/test/state/MetricStorageRegistry.test.ts b/experimental/packages/opentelemetry-sdk-metrics-base/test/state/MetricStorageRegistry.test.ts index 066d1d26ff..cd355a6d54 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/test/state/MetricStorageRegistry.test.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/test/state/MetricStorageRegistry.test.ts @@ -15,7 +15,7 @@ */ import { MetricStorageRegistry } from '../../src/state/MetricStorageRegistry'; -import { ValueType } from '@opentelemetry/api-metrics-wip'; +import { ValueType } from '@opentelemetry/api-metrics'; import { MetricStorage } from '../../src/state/MetricStorage'; import { HrTime } from '@opentelemetry/api'; import { MetricCollectorHandle } from '../../src/state/MetricCollector'; diff --git a/experimental/packages/opentelemetry-sdk-metrics-base/test/state/MultiWritableMetricStorage.test.ts b/experimental/packages/opentelemetry-sdk-metrics-base/test/state/MultiWritableMetricStorage.test.ts index 174c12f727..41b1b9b6d8 100644 --- a/experimental/packages/opentelemetry-sdk-metrics-base/test/state/MultiWritableMetricStorage.test.ts +++ b/experimental/packages/opentelemetry-sdk-metrics-base/test/state/MultiWritableMetricStorage.test.ts @@ -15,7 +15,7 @@ */ import * as api from '@opentelemetry/api'; -import { Attributes } from '@opentelemetry/api-metrics-wip'; +import { Attributes } from '@opentelemetry/api-metrics'; import * as assert from 'assert'; import { Measurement } from '../../src/Measurement'; import { MultiMetricStorage } from '../../src/state/MultiWritableMetricStorage'; diff --git a/experimental/packages/opentelemetry-sdk-node/package.json b/experimental/packages/opentelemetry-sdk-node/package.json index b7ce8be638..c2ede4f8d7 100644 --- a/experimental/packages/opentelemetry-sdk-node/package.json +++ b/experimental/packages/opentelemetry-sdk-node/package.json @@ -44,14 +44,14 @@ }, "dependencies": { "@opentelemetry/api-metrics": "0.27.0", - "@opentelemetry/core": "~1.1.0", + "@opentelemetry/core": "~1.1.1", "@opentelemetry/instrumentation": "0.27.0", "@opentelemetry/resource-detector-aws": "~1.0.0", "@opentelemetry/resource-detector-gcp": "~0.26.0", - "@opentelemetry/resources": "~1.1.0", + "@opentelemetry/resources": "~1.1.1", "@opentelemetry/sdk-metrics-base": "0.27.0", - "@opentelemetry/sdk-trace-base": "~1.1.0", - "@opentelemetry/sdk-trace-node": "~1.1.0" + "@opentelemetry/sdk-trace-base": "~1.1.1", + "@opentelemetry/sdk-trace-node": "~1.1.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.1.0 <1.2.0" diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index cf014d6bd2..aa3df3247c 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -17,7 +17,7 @@ import { TextMapPropagator } from '@opentelemetry/api'; import { metrics } from '@opentelemetry/api-metrics'; import { ContextManager } from '@opentelemetry/api'; -import { MeterConfig, MeterProvider } from '@opentelemetry/sdk-metrics-base'; +import { MeterProvider, MetricReader } from '@opentelemetry/sdk-metrics-base'; import { InstrumentationOption, registerInstrumentations, @@ -44,7 +44,7 @@ export class NodeSDK { textMapPropagator?: TextMapPropagator; }; private _instrumentations: InstrumentationOption[]; - private _meterProviderConfig?: MeterConfig; + private _metricReader?: MetricReader; private _resource: Resource; @@ -83,19 +83,8 @@ export class NodeSDK { ); } - if (configuration.metricExporter) { - const meterConfig: MeterConfig = { - exporter: configuration.metricExporter, - }; - - if (configuration.metricProcessor) { - meterConfig.processor = configuration.metricProcessor; - } - if (typeof configuration.metricInterval === 'number') { - meterConfig.interval = configuration.metricInterval; - } - - this.configureMeterProvider(meterConfig); + if (configuration.metricReader) { + this.configureMeterProvider(configuration.metricReader); } let instrumentations: InstrumentationOption[] = []; @@ -104,6 +93,7 @@ export class NodeSDK { } this._instrumentations = instrumentations; } + /** Set configurations required to register a NodeTracerProvider */ public configureTracerProvider( tracerConfig: NodeTracerConfig, @@ -120,8 +110,8 @@ export class NodeSDK { } /** Set configurations needed to register a MeterProvider */ - public configureMeterProvider(config: MeterConfig): void { - this._meterProviderConfig = config; + public configureMeterProvider(reader: MetricReader): void { + this._metricReader = reader; } /** Detect resource attributes */ @@ -162,12 +152,13 @@ export class NodeSDK { }); } - if (this._meterProviderConfig) { + if (this._metricReader) { const meterProvider = new MeterProvider({ - ...this._meterProviderConfig, resource: this._resource, }); + meterProvider.addMetricReader(this._metricReader); + this._meterProvider = meterProvider; metrics.setGlobalMeterProvider(meterProvider); @@ -190,7 +181,8 @@ export class NodeSDK { return ( Promise.all(promises) // return void instead of the array from Promise.all - .then(() => {}) + .then(() => { + }) ); } } diff --git a/experimental/packages/opentelemetry-sdk-node/src/types.ts b/experimental/packages/opentelemetry-sdk-node/src/types.ts index 7ecc3f0c7e..ea93ddae16 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/types.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/types.ts @@ -17,7 +17,7 @@ import { SpanAttributes, TextMapPropagator, Sampler } from '@opentelemetry/api'; import type { ContextManager } from '@opentelemetry/api'; import { InstrumentationOption } from '@opentelemetry/instrumentation'; -import { MetricExporter, Processor } from '@opentelemetry/sdk-metrics-base'; +import { MetricReader } from '@opentelemetry/sdk-metrics-base'; import { Resource } from '@opentelemetry/resources'; import { SpanExporter, @@ -30,9 +30,7 @@ export interface NodeSDKConfiguration { contextManager: ContextManager; defaultAttributes: SpanAttributes; textMapPropagator: TextMapPropagator; - metricProcessor: Processor; - metricExporter: MetricExporter; - metricInterval: number; + metricReader: MetricReader; instrumentations: InstrumentationOption[]; resource: Resource; sampler: Sampler; diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index 7bfc913ce1..2237828534 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -28,7 +28,7 @@ import { AsyncLocalStorageContextManager, } from '@opentelemetry/context-async-hooks'; import { CompositePropagator } from '@opentelemetry/core'; -import { ConsoleMetricExporter, MeterProvider } from '@opentelemetry/sdk-metrics-base'; +import { ConsoleMetricExporter, MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics-base'; import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { awsEc2Detector } from '@opentelemetry/resource-detector-aws'; import { resetIsAvailableCache } from '@opentelemetry/resource-detector-gcp'; @@ -163,11 +163,16 @@ describe('Node SDK', () => { assert.ok(apiTracerProvider.getDelegate() instanceof NodeTracerProvider); }); - it('should register a meter provider if an exporter is provided', async () => { + it('should register a meter provider if a reader is provided', async () => { const exporter = new ConsoleMetricExporter(); + const metricReader = new PeriodicExportingMetricReader({ + exporter: exporter, + exportIntervalMillis: 100, + exportTimeoutMillis: 100 + }); const sdk = new NodeSDK({ - metricExporter: exporter, + metricReader: metricReader, autoDetectResources: false, }); @@ -178,6 +183,8 @@ describe('Node SDK', () => { assert.strictEqual((trace.getTracerProvider() as ProxyTracerProvider).getDelegate(), delegate, 'tracer provider should not have changed'); assert.ok(metrics.getMeterProvider() instanceof MeterProvider); + + await sdk.shutdown(); }); }); diff --git a/experimental/packages/opentelemetry-sdk-node/tsconfig.json b/experimental/packages/opentelemetry-sdk-node/tsconfig.json index b172f492db..36c71e90d8 100644 --- a/experimental/packages/opentelemetry-sdk-node/tsconfig.json +++ b/experimental/packages/opentelemetry-sdk-node/tsconfig.json @@ -9,8 +9,14 @@ "test/**/*.ts" ], "references": [ + { + "path": "../opentelemetry-api-metrics" + }, { "path": "../opentelemetry-instrumentation" + }, + { + "path": "../opentelemetry-sdk-metrics-base" } ] } diff --git a/experimental/packages/otlp-transformer/package.json b/experimental/packages/otlp-transformer/package.json index ac94aedbc5..740b1cc345 100644 --- a/experimental/packages/otlp-transformer/package.json +++ b/experimental/packages/otlp-transformer/package.json @@ -42,12 +42,12 @@ "README.md" ], "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.1.0", + "@opentelemetry/api": ">=1.1.0 <1.2.0", "@opentelemetry/api-metrics": "~0.27.0" }, "devDependencies": { - "@opentelemetry/api": "1.0.4", - "@opentelemetry/api-metrics": "0.27.0", + "@opentelemetry/api": "^1.1.0", + "@opentelemetry/api-metrics": "~0.27.0", "@types/mocha": "8.2.3", "@types/webpack-env": "1.16.3", "codecov": "3.8.3", @@ -69,9 +69,9 @@ "webpack": "4.46.0" }, "dependencies": { - "@opentelemetry/core": "1.0.1", - "@opentelemetry/resources": "1.0.1", + "@opentelemetry/core": "1.1.1", + "@opentelemetry/resources": "1.1.1", "@opentelemetry/sdk-metrics-base": "0.27.0", - "@opentelemetry/sdk-trace-base": "1.0.1" + "@opentelemetry/sdk-trace-base": "1.1.1" } } diff --git a/experimental/packages/otlp-transformer/src/metrics/index.ts b/experimental/packages/otlp-transformer/src/metrics/index.ts index 634dd2146e..e5a8560ded 100644 --- a/experimental/packages/otlp-transformer/src/metrics/index.ts +++ b/experimental/packages/otlp-transformer/src/metrics/index.ts @@ -13,92 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { InstrumentationLibrary } from '@opentelemetry/core'; -import type { Resource } from '@opentelemetry/resources'; -import type { MetricRecord } from '@opentelemetry/sdk-metrics-base'; +import type { ResourceMetrics } from '@opentelemetry/sdk-metrics-base'; import { toAttributes } from '../common/internal'; import { toMetric } from './internal'; import type { IExportMetricsServiceRequest } from './types'; +import { AggregationTemporality } from '@opentelemetry/sdk-metrics-base'; -export function createExportMetricsServiceRequest(metricRecords: MetricRecord[], startTime: number): IExportMetricsServiceRequest | null { - if (metricRecords.length === 0) { - return null; - } - +export function createExportMetricsServiceRequest(resourceMetrics: ResourceMetrics, + aggregationTemporality: AggregationTemporality): IExportMetricsServiceRequest | null { return { - resourceMetrics: metricRecordsToResourceMetrics(metricRecords).map(({ resource, resourceMetrics, resourceSchemaUrl }) => ({ + resourceMetrics: [{ resource: { - attributes: toAttributes(resource.attributes), - droppedAttributesCount: 0, + attributes: toAttributes(resourceMetrics.resource.attributes), + droppedAttributesCount: 0 }, - instrumentationLibraryMetrics: resourceMetrics.map(({ instrumentationLibrary, instrumentationLibraryMetrics, librarySchemaUrl }) => ({ - instrumentationLibrary: { - name: instrumentationLibrary.name, - version: instrumentationLibrary.version, - }, - metrics: instrumentationLibraryMetrics.map(m => toMetric(m, startTime)), - schemaUrl: librarySchemaUrl, - })), - schemaUrl: resourceSchemaUrl, - })) + schemaUrl: undefined, // TODO: Schema Url does not exist yet in the SDK. + instrumentationLibraryMetrics: Array.from(resourceMetrics.instrumentationLibraryMetrics.map(metrics => { + return { + instrumentationLibrary: { + name: metrics.instrumentationLibrary.name, + version: metrics.instrumentationLibrary.version, + }, + metrics: metrics.metrics.map(metricData => toMetric(metricData, aggregationTemporality)), + schemaUrl: metrics.instrumentationLibrary.schemaUrl + }; + })) + }] }; } - -type IntermediateResourceMetrics = { - resource: Resource, - resourceMetrics: IntermediateInstrumentationLibraryMetrics[], - resourceSchemaUrl?: string, -}; - -type IntermediateInstrumentationLibraryMetrics = { - instrumentationLibrary: InstrumentationLibrary, - instrumentationLibraryMetrics: MetricRecord[], - librarySchemaUrl?: string, -}; - -function metricRecordsToResourceMetrics(metricRecords: MetricRecord[]): IntermediateResourceMetrics[] { - const resourceMap: Map> = new Map(); - - for (const record of metricRecords) { - let ilmMap = resourceMap.get(record.resource); - - if (!ilmMap) { - ilmMap = new Map(); - resourceMap.set(record.resource, ilmMap); - } - - const instrumentationLibraryKey = `${record.instrumentationLibrary.name}@${record.instrumentationLibrary.name || ''}:${record.instrumentationLibrary.schemaUrl || ''}`; - let records = ilmMap.get(instrumentationLibraryKey); - - if (!records) { - records = []; - ilmMap.set(instrumentationLibraryKey, records); - } - - records.push(record); - } - - const out: IntermediateResourceMetrics[] = []; - - const resourceMapEntryIterator = resourceMap.entries(); - let resourceMapEntry = resourceMapEntryIterator.next(); - while (!resourceMapEntry.done) { - const [resource, ilmMap] = resourceMapEntry.value; - const resourceMetrics: IntermediateInstrumentationLibraryMetrics[] = []; - const ilmIterator = ilmMap.values(); - let ilmEntry = ilmIterator.next(); - while (!ilmEntry.done) { - const instrumentationLibraryMetrics = ilmEntry.value; - if (instrumentationLibraryMetrics.length > 0) { - const lib = instrumentationLibraryMetrics[0].instrumentationLibrary; - resourceMetrics.push({ instrumentationLibrary: lib, instrumentationLibraryMetrics, librarySchemaUrl: lib.schemaUrl }); - } - ilmEntry = ilmIterator.next(); - } - // TODO SDK types don't provide resource schema URL at this time - out.push({ resource, resourceMetrics }); - resourceMapEntry = resourceMapEntryIterator.next(); - } - - return out; -} diff --git a/experimental/packages/otlp-transformer/src/metrics/internal.ts b/experimental/packages/otlp-transformer/src/metrics/internal.ts index dbcd6ec540..1dab2f4417 100644 --- a/experimental/packages/otlp-transformer/src/metrics/internal.ts +++ b/experimental/packages/otlp-transformer/src/metrics/internal.ts @@ -13,125 +13,124 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { AggregationTemporality, ValueType } from '@opentelemetry/api-metrics'; +import { ValueType } from '@opentelemetry/api-metrics'; import { hrTimeToNanoseconds } from '@opentelemetry/core'; -import type { Histogram, MetricRecord, Point } from '@opentelemetry/sdk-metrics-base'; -import { AggregatorKind, MetricKind } from '@opentelemetry/sdk-metrics-base'; +import { + AggregationTemporality, + DataPoint, + DataPointType, + Histogram, + InstrumentType, + MetricData +} from '@opentelemetry/sdk-metrics-base'; import { toAttributes } from '../common/internal'; -import { EAggregationTemporality, IGauge, IHistogram, IHistogramDataPoint, IMetric, INumberDataPoint, ISum } from './types'; +import { EAggregationTemporality, IHistogramDataPoint, IMetric, INumberDataPoint } from './types'; -export function toMetric(metric: MetricRecord, startTime: number): IMetric { +export function toMetric(metricData: MetricData, metricTemporality: AggregationTemporality): IMetric { const out: IMetric = { - description: metric.descriptor.description, - name: metric.descriptor.name, - unit: metric.descriptor.unit, + name: metricData.descriptor.name, + description: metricData.descriptor.description, + unit: metricData.descriptor.unit, }; - if (isSum(metric)) { - out.sum = toSum(metric, startTime); - } else if (metric.aggregator.kind === AggregatorKind.LAST_VALUE) { - out.gauge = toGauge(metric, startTime); - } else if (metric.aggregator.kind === AggregatorKind.HISTOGRAM) { - out.histogram = toHistogram(metric, startTime); + const aggregationTemporality = toAggregationTemporality(metricTemporality); + + if (metricData.dataPointType === DataPointType.SINGULAR) { + const dataPoints = toSingularDataPoints(metricData); + const isMonotonic = metricData.descriptor.type === InstrumentType.COUNTER || + metricData.descriptor.type === InstrumentType.OBSERVABLE_COUNTER; + if (isSum(metricData)) { + out.sum = { + aggregationTemporality, + isMonotonic, + dataPoints + }; + + } else { + // Instrument is a gauge. + out.gauge = { + dataPoints + }; + } + } else if (isHistogram(metricData)) { + out.histogram = { + aggregationTemporality, + dataPoints: toHistogramDataPoints(metricData) + }; } return out; } -function isSum(metric: MetricRecord) { - return metric.aggregator.kind === AggregatorKind.SUM || - metric.descriptor.metricKind === MetricKind.OBSERVABLE_COUNTER || - metric.descriptor.metricKind === MetricKind.OBSERVABLE_UP_DOWN_COUNTER; -} - -function toAggregationTemporality( - metric: MetricRecord -): EAggregationTemporality { - if (metric.descriptor.metricKind === MetricKind.OBSERVABLE_GAUGE) { - return EAggregationTemporality.AGGREGATION_TEMPORALITY_UNSPECIFIED; - } - - if (metric.aggregationTemporality === AggregationTemporality.AGGREGATION_TEMPORALITY_DELTA) { - return EAggregationTemporality.AGGREGATION_TEMPORALITY_DELTA; - } +function toSingularDataPoint(dataPoint: DataPoint | DataPoint, valueType: ValueType) { + const out: INumberDataPoint = { + attributes: toAttributes(dataPoint.attributes), + startTimeUnixNano: hrTimeToNanoseconds( + dataPoint.startTime + ), + timeUnixNano: hrTimeToNanoseconds( + dataPoint.endTime + ), + }; - if (metric.aggregationTemporality === AggregationTemporality.AGGREGATION_TEMPORALITY_CUMULATIVE) { - return EAggregationTemporality.AGGREGATION_TEMPORALITY_CUMULATIVE; + if (valueType === ValueType.INT) { + out.asInt = dataPoint.value as number; + } else if (valueType === ValueType.DOUBLE) { + out.asDouble = dataPoint.value as number; } - return EAggregationTemporality.AGGREGATION_TEMPORALITY_UNSPECIFIED; + return out; } -function toSum( - metric: MetricRecord, - startTime: number, -): ISum { - return { - dataPoints: [toNumberDataPoint(metric, startTime)], - isMonotonic: - metric.descriptor.metricKind === MetricKind.COUNTER || - metric.descriptor.metricKind === MetricKind.OBSERVABLE_COUNTER, - aggregationTemporality: toAggregationTemporality(metric), - }; +function toSingularDataPoints( + metricData: MetricData +): INumberDataPoint[] { + return metricData.dataPoints.map(dataPoint => { + return toSingularDataPoint(dataPoint, metricData.descriptor.valueType); + }); } -function toGauge( - metric: MetricRecord, - startTime: number, -): IGauge { - return { - dataPoints: [toNumberDataPoint(metric, startTime)], - }; +function toHistogramDataPoints( + metricData: MetricData +): IHistogramDataPoint[] { + return metricData.dataPoints.map(dataPoint => { + const histogram = dataPoint.value as Histogram; + return { + attributes: toAttributes(dataPoint.attributes), + bucketCounts: histogram.buckets.counts, + explicitBounds: histogram.buckets.boundaries, + count: histogram.count, + sum: histogram.sum, + startTimeUnixNano: hrTimeToNanoseconds(dataPoint.startTime), + timeUnixNano: hrTimeToNanoseconds( + dataPoint.endTime + ), + }; + }); } -function toHistogram( - metric: MetricRecord, - startTime: number, -): IHistogram { - return { - dataPoints: [toHistogramDataPoint(metric, startTime)], - aggregationTemporality: toAggregationTemporality(metric), - }; +function isSum(metric: MetricData) { + return (metric.descriptor.type === InstrumentType.COUNTER || + metric.descriptor.type === InstrumentType.UP_DOWN_COUNTER || + metric.descriptor.type === InstrumentType.OBSERVABLE_COUNTER || + metric.descriptor.type === InstrumentType.OBSERVABLE_UP_DOWN_COUNTER); } -function toNumberDataPoint( - metric: MetricRecord, - startTime: number, -): INumberDataPoint { - const out: INumberDataPoint = { - attributes: toAttributes(metric.attributes), - startTimeUnixNano: startTime, - timeUnixNano: hrTimeToNanoseconds( - metric.aggregator.toPoint().timestamp - ), - }; +function isHistogram(metric: MetricData) { + return metric.dataPointType === DataPointType.HISTOGRAM; +} - if (metric.descriptor.valueType === ValueType.INT) { - out.asInt = metric.aggregator.toPoint().value as number; +function toAggregationTemporality( + temporality: AggregationTemporality, +): EAggregationTemporality { + if (temporality === AggregationTemporality.DELTA) { + return EAggregationTemporality.AGGREGATION_TEMPORALITY_DELTA; } - if (metric.descriptor.valueType === ValueType.DOUBLE) { - out.asDouble = metric.aggregator.toPoint().value as number; + if (temporality === AggregationTemporality.CUMULATIVE) { + return EAggregationTemporality.AGGREGATION_TEMPORALITY_CUMULATIVE; } - return out; -} - -function toHistogramDataPoint( - metric: MetricRecord, - startTime: number, -): IHistogramDataPoint { - const point = metric.aggregator.toPoint() as Point; - return { - attributes: toAttributes(metric.attributes), - bucketCounts: point.value.buckets.counts, - explicitBounds: point.value.buckets.boundaries, - count: point.value.count, - sum: point.value.sum, - startTimeUnixNano: startTime, - timeUnixNano: hrTimeToNanoseconds( - metric.aggregator.toPoint().timestamp - ), - }; + return EAggregationTemporality.AGGREGATION_TEMPORALITY_UNSPECIFIED; } diff --git a/experimental/packages/otlp-transformer/test/metrics.test.ts b/experimental/packages/otlp-transformer/test/metrics.test.ts index 4156aeec1f..37ecdaf560 100644 --- a/experimental/packages/otlp-transformer/test/metrics.test.ts +++ b/experimental/packages/otlp-transformer/test/metrics.test.ts @@ -13,130 +13,141 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { AggregationTemporality, ValueType } from '@opentelemetry/api-metrics'; +import { ValueType } from '@opentelemetry/api-metrics'; import { Resource } from '@opentelemetry/resources'; import { - HistogramAggregator, - LastValueAggregator, - MetricKind, - MetricRecord, - SumAggregator + AggregationTemporality, + DataPointType, + InstrumentType, + MetricData, + ResourceMetrics } from '@opentelemetry/sdk-metrics-base'; import * as assert from 'assert'; import { createExportMetricsServiceRequest } from '../src/metrics'; import { EAggregationTemporality } from '../src/metrics/types'; +import { hrTime, hrTimeToNanoseconds } from '@opentelemetry/core'; -const START_TIME = 1640715235584374000; +const START_TIME = hrTime(); +const END_TIME = hrTime(); describe('Metrics', () => { describe('createExportMetricsServiceRequest', () => { - let sumRecord: MetricRecord; - let sumAggregator: SumAggregator; - let observableSumRecord: MetricRecord; - let observableSumAggregator: SumAggregator; - let gaugeRecord: MetricRecord; - let gaugeAggregator: LastValueAggregator; - let histRecord: MetricRecord; - let histAggregator: HistogramAggregator; - let resource: Resource; - - beforeEach(() => { - resource = new Resource({ - 'resource-attribute': 'resource attribute value', - }); - sumAggregator = new SumAggregator(); - sumRecord = { - aggregationTemporality: - AggregationTemporality.AGGREGATION_TEMPORALITY_DELTA, - attributes: { 'string-attribute': 'some attribute value' }, + function createCounterData(value: number): MetricData { + return { descriptor: { description: 'this is a description', - metricKind: MetricKind.COUNTER, + type: InstrumentType.COUNTER, name: 'counter', unit: '1', valueType: ValueType.INT, }, - aggregator: sumAggregator, - instrumentationLibrary: { - name: 'mylib', - version: '0.1.0', - schemaUrl: 'http://url.to.schema' - }, - resource, + dataPointType: DataPointType.SINGULAR, + dataPoints: [ + { + value: value, + startTime: START_TIME, + endTime: END_TIME, + attributes: { 'string-attribute': 'some attribute value' } + } + ] }; - observableSumAggregator = new SumAggregator(); - observableSumRecord = { - aggregationTemporality: - AggregationTemporality.AGGREGATION_TEMPORALITY_DELTA, - attributes: { 'string-attribute': 'some attribute value' }, + } + + function createObservableCounterData(value: number): MetricData { + return { descriptor: { description: 'this is a description', - metricKind: MetricKind.OBSERVABLE_COUNTER, - name: 'counter', + type: InstrumentType.OBSERVABLE_COUNTER, + name: 'observable-counter', unit: '1', valueType: ValueType.INT, }, - aggregator: observableSumAggregator, - instrumentationLibrary: { - name: 'mylib', - version: '0.1.0', - schemaUrl: 'http://url.to.schema' - }, - resource, + dataPointType: DataPointType.SINGULAR, + dataPoints: [ + { + value: value, + startTime: START_TIME, + endTime: END_TIME, + attributes: { 'string-attribute': 'some attribute value' } + } + ] }; - gaugeAggregator = new LastValueAggregator(); - gaugeRecord = { - aggregationTemporality: - AggregationTemporality.AGGREGATION_TEMPORALITY_UNSPECIFIED, - attributes: { 'string-attribute': 'some attribute value' }, + } + + function createObservableGaugeData(value: number): MetricData { + return { descriptor: { description: 'this is a description', - metricKind: MetricKind.OBSERVABLE_GAUGE, + type: InstrumentType.OBSERVABLE_GAUGE, name: 'gauge', unit: '1', valueType: ValueType.DOUBLE, }, - aggregator: gaugeAggregator, - instrumentationLibrary: { - name: 'mylib', - version: '0.1.0', - schemaUrl: 'http://url.to.schema' - }, - resource, + dataPointType: DataPointType.SINGULAR, + dataPoints: [ + { + value: value, + startTime: START_TIME, + endTime: END_TIME, + attributes: { 'string-attribute': 'some attribute value' } + } + ] }; - histAggregator = new HistogramAggregator([5]); - histRecord = { - aggregationTemporality: - AggregationTemporality.AGGREGATION_TEMPORALITY_UNSPECIFIED, - attributes: { 'string-attribute': 'some attribute value' }, + } + + function createHistogramMetrics(count: number, sum: number, boundaries: number[], counts: number[]): MetricData { + return { descriptor: { description: 'this is a description', - metricKind: MetricKind.HISTOGRAM, + type: InstrumentType.HISTOGRAM, name: 'hist', unit: '1', valueType: ValueType.INT, }, - aggregator: histAggregator, - instrumentationLibrary: { - name: 'mylib', - version: '0.1.0', - schemaUrl: 'http://url.to.schema' - }, - resource, + dataPointType: DataPointType.HISTOGRAM, + dataPoints: [ + { + value: { + sum: sum, + count: count, + buckets: { + boundaries: boundaries, + counts: counts + } + }, + startTime: START_TIME, + endTime: END_TIME, + attributes: { 'string-attribute': 'some attribute value' }, + } + ] }; - }); + } - it('returns null on an empty list', () => { - assert.strictEqual(createExportMetricsServiceRequest([], 0), null); - }); + function createResourceMetrics(metricData: MetricData[]): ResourceMetrics { + const resource = new Resource({ + 'resource-attribute': 'resource attribute value', + }); + return { + resource: resource, + instrumentationLibraryMetrics: + [ + { + instrumentationLibrary: { + name: 'mylib', + version: '0.1.0', + schemaUrl: 'http://url.to.schema' + }, + metrics: metricData + } + ] + }; + } it('serializes a sum metric record', () => { - sumAggregator.update(10); - // spoof the update time - sumAggregator['_lastUpdateTime'] = [1640715557, 342725388]; + const metrics = createResourceMetrics([createCounterData(10)]); const exportRequest = createExportMetricsServiceRequest( - [sumRecord], - START_TIME + metrics, + AggregationTemporality.DELTA ); assert.ok(exportRequest); @@ -178,9 +189,8 @@ describe('Metrics', () => { }, }, ], - startTimeUnixNano: START_TIME, - // eslint-disable-next-line @typescript-eslint/no-loss-of-precision - timeUnixNano: 1640715557342725388, + startTimeUnixNano: hrTimeToNanoseconds(START_TIME), + timeUnixNano: hrTimeToNanoseconds(END_TIME), asInt: 10, }, ], @@ -197,12 +207,9 @@ describe('Metrics', () => { }); it('serializes an observable sum metric record', () => { - observableSumAggregator.update(10); - // spoof the update time - observableSumAggregator['_lastUpdateTime'] = [1640715557, 342725388]; const exportRequest = createExportMetricsServiceRequest( - [observableSumRecord], - START_TIME + createResourceMetrics([createObservableCounterData(10)]), + AggregationTemporality.DELTA ); assert.ok(exportRequest); @@ -230,7 +237,7 @@ describe('Metrics', () => { schemaUrl: 'http://url.to.schema', metrics: [ { - name: 'counter', + name: 'observable-counter', description: 'this is a description', unit: '1', sum: { @@ -244,9 +251,8 @@ describe('Metrics', () => { }, }, ], - startTimeUnixNano: START_TIME, - // eslint-disable-next-line @typescript-eslint/no-loss-of-precision - timeUnixNano: 1640715557342725388, + startTimeUnixNano: hrTimeToNanoseconds(START_TIME), + timeUnixNano: hrTimeToNanoseconds(END_TIME), asInt: 10, }, ], @@ -263,12 +269,9 @@ describe('Metrics', () => { }); it('serializes a gauge metric record', () => { - gaugeAggregator.update(10.5); - // spoof the update time - gaugeAggregator['_lastUpdateTime'] = [1640715557, 342725388]; const exportRequest = createExportMetricsServiceRequest( - [gaugeRecord], - START_TIME + createResourceMetrics([createObservableGaugeData(10.5)]), + AggregationTemporality.DELTA ); assert.ok(exportRequest); @@ -310,9 +313,8 @@ describe('Metrics', () => { }, }, ], - startTimeUnixNano: START_TIME, - // eslint-disable-next-line @typescript-eslint/no-loss-of-precision - timeUnixNano: 1640715557342725388, + startTimeUnixNano: hrTimeToNanoseconds(START_TIME), + timeUnixNano: hrTimeToNanoseconds(END_TIME), asDouble: 10.5, }, ], @@ -327,13 +329,9 @@ describe('Metrics', () => { }); it('serializes a histogram metric record', () => { - histAggregator.update(2); - histAggregator.update(7); - // spoof the update time - histAggregator['_lastUpdateTime'] = [1640715557, 342725388]; const exportRequest = createExportMetricsServiceRequest( - [histRecord], - START_TIME + createResourceMetrics([createHistogramMetrics(2, 9, [5], [1,1])]), + AggregationTemporality.CUMULATIVE ); assert.ok(exportRequest); @@ -365,7 +363,7 @@ describe('Metrics', () => { description: 'this is a description', unit: '1', histogram: { - aggregationTemporality: EAggregationTemporality.AGGREGATION_TEMPORALITY_UNSPECIFIED, + aggregationTemporality: EAggregationTemporality.AGGREGATION_TEMPORALITY_CUMULATIVE, dataPoints: [ { attributes: [ @@ -380,9 +378,8 @@ describe('Metrics', () => { count: 2, explicitBounds: [5], sum: 9, - startTimeUnixNano: START_TIME, - // eslint-disable-next-line @typescript-eslint/no-loss-of-precision - timeUnixNano: 1640715557342725388, + startTimeUnixNano: hrTimeToNanoseconds(START_TIME), + timeUnixNano: hrTimeToNanoseconds(END_TIME), }, ], }, diff --git a/experimental/packages/otlp-transformer/tsconfig.json b/experimental/packages/otlp-transformer/tsconfig.json index ed9d0830bd..3c062d3feb 100644 --- a/experimental/packages/otlp-transformer/tsconfig.json +++ b/experimental/packages/otlp-transformer/tsconfig.json @@ -7,5 +7,13 @@ "include": [ "src/**/*.ts", "test/**/*.ts" + ], + "references": [ + { + "path": "../opentelemetry-api-metrics" + }, + { + "path": "../opentelemetry-sdk-metrics-base" + } ] }