diff --git a/README.md b/README.md index 7fbe787972..c07b4e5924 100644 --- a/README.md +++ b/README.md @@ -141,19 +141,27 @@ There may also be API packages for experimental signals in the experimental dire All stable packages are released with the same version, and all experimental packages are released with the same version. The below table describes which versions of each set of packages are expected to work together. -| API | Stable Packages | Experimental Packages | -| ----- | --------------- | --------------------- | -| 1.3.x | 1.9.x | 0.35.x | -| 1.3.x | 1.8.x | 0.34.x | -| 1.2.x | 1.7.x | 0.33.x | -| 1.2.x | 1.6.x | 0.32.x | -| 1.1.x | 1.5.x | 0.31.x | -| 1.1.x | 1.4.x | 0.30.x | -| 1.1.x | 1.3.x | 0.29.x | -| 1.1.x | 1.2.x | 0.29.x | -| 1.1.x | 1.1.x | 0.28.x | -| 1.0.x | 1.0.x | 0.27.x | -| 1.0.x | 1.0.x | 0.26.x | +| Stable Packages | Experimental Packages | +|-----------------------------------------------------------------|-----------------------| +| 1.17.x | 0.43.x | +| 1.16.x | 0.42.x | +| 1.15.x | 0.41.x | +| 1.14.x | 0.40.x | +| 1.13.x | 0.39.x | +| 1.12.x | 0.38.x | +| 1.11.x | 0.37.x | +| 1.10.x | 0.36.x | +| 1.9.x | 0.35.x | +| 1.8.x (this and later versions require API >=1.3.0 for metrics) | 0.34.x | +| 1.7.x | 0.33.x | +| 1.6.x | 0.32.x | +| 1.5.x | 0.31.x | +| 1.4.x | 0.30.x | +| 1.3.x | 0.29.x | +| 1.2.x | 0.29.x | +| 1.1.x | 0.28.x | +| 1.0.x | 0.27.x | +| 1.0.x (this and later versions require API >=1.0.0 for traces) | 0.26.x | ## Versioning diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 0c3598e32f..e03d24e8d3 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -44,6 +44,7 @@ All notable changes to experimental packages in this project will be documented ### :rocket: (Enhancement) * feat: update PeriodicExportingMetricReader and PrometheusExporter to accept optional metric producers [#4077](https://github.com/open-telemetry/opentelemetry-js/pull/4077) @aabmass +* feat(opencensus-shim): implement OpenCensus metric producer [#4066](https://github.com/open-telemetry/opentelemetry-js/pull/4066) @aabmass ### :bug: (Bug Fix) diff --git a/experimental/packages/exporter-logs-otlp-grpc/package.json b/experimental/packages/exporter-logs-otlp-grpc/package.json index 442b003c73..3f08acb370 100644 --- a/experimental/packages/exporter-logs-otlp-grpc/package.json +++ b/experimental/packages/exporter-logs-otlp-grpc/package.json @@ -48,7 +48,7 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@grpc/proto-loader": "^0.7.3", "@opentelemetry/api": "1.6.0", "@opentelemetry/api-logs": "0.43.0", diff --git a/experimental/packages/exporter-logs-otlp-http/package.json b/experimental/packages/exporter-logs-otlp-http/package.json index 04ed15daf3..57f0aeed83 100644 --- a/experimental/packages/exporter-logs-otlp-http/package.json +++ b/experimental/packages/exporter-logs-otlp-http/package.json @@ -71,7 +71,7 @@ ], "sideEffects": false, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@opentelemetry/api": "1.6.0", "@opentelemetry/resources": "1.17.0", "@types/mocha": "10.0.1", diff --git a/experimental/packages/exporter-logs-otlp-proto/package.json b/experimental/packages/exporter-logs-otlp-proto/package.json index 742db91b54..25de11cf26 100644 --- a/experimental/packages/exporter-logs-otlp-proto/package.json +++ b/experimental/packages/exporter-logs-otlp-proto/package.json @@ -63,7 +63,7 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@opentelemetry/api": "1.6.0", "@types/mocha": "10.0.1", "@types/node": "18.6.5", diff --git a/experimental/packages/exporter-trace-otlp-grpc/package.json b/experimental/packages/exporter-trace-otlp-grpc/package.json index 8267c2da21..bcfcd3ae94 100644 --- a/experimental/packages/exporter-trace-otlp-grpc/package.json +++ b/experimental/packages/exporter-trace-otlp-grpc/package.json @@ -47,7 +47,7 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@grpc/proto-loader": "^0.7.3", "@opentelemetry/api": "1.6.0", "@opentelemetry/otlp-exporter-base": "0.43.0", diff --git a/experimental/packages/exporter-trace-otlp-http/package.json b/experimental/packages/exporter-trace-otlp-http/package.json index 16a64b9c9f..d5a1713205 100644 --- a/experimental/packages/exporter-trace-otlp-http/package.json +++ b/experimental/packages/exporter-trace-otlp-http/package.json @@ -63,7 +63,7 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@opentelemetry/api": "1.6.0", "@types/mocha": "10.0.1", "@types/node": "18.6.5", diff --git a/experimental/packages/exporter-trace-otlp-proto/package.json b/experimental/packages/exporter-trace-otlp-proto/package.json index 5bc540da39..9214b7f84c 100644 --- a/experimental/packages/exporter-trace-otlp-proto/package.json +++ b/experimental/packages/exporter-trace-otlp-proto/package.json @@ -62,7 +62,7 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@opentelemetry/api": "1.6.0", "@types/mocha": "10.0.1", "@types/node": "18.6.5", diff --git a/experimental/packages/opentelemetry-browser-detector/package.json b/experimental/packages/opentelemetry-browser-detector/package.json index 8a96ab3a76..67330ddca3 100644 --- a/experimental/packages/opentelemetry-browser-detector/package.json +++ b/experimental/packages/opentelemetry-browser-detector/package.json @@ -53,7 +53,7 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@opentelemetry/api": "1.6.0", "@types/mocha": "10.0.1", "@types/node": "18.6.5", diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/package.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/package.json index a6adb42c93..74985a4766 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/package.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/package.json @@ -47,7 +47,7 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@grpc/proto-loader": "^0.7.3", "@opentelemetry/api": "1.6.0", "@types/mocha": "10.0.1", diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/package.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/package.json index 2fc757eb1c..46e60ea29e 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/package.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/package.json @@ -63,7 +63,7 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@opentelemetry/api": "1.6.0", "@types/mocha": "10.0.1", "@types/node": "18.6.5", diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json index fa2c674cea..92c799b26b 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json @@ -47,7 +47,7 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@opentelemetry/api": "1.6.0", "@types/mocha": "10.0.1", "@types/node": "18.6.5", diff --git a/experimental/packages/opentelemetry-instrumentation-fetch/package.json b/experimental/packages/opentelemetry-instrumentation-fetch/package.json index 009aea1240..4c155d752b 100644 --- a/experimental/packages/opentelemetry-instrumentation-fetch/package.json +++ b/experimental/packages/opentelemetry-instrumentation-fetch/package.json @@ -54,7 +54,7 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@opentelemetry/api": "1.6.0", "@opentelemetry/context-zone": "1.17.0", "@opentelemetry/propagator-b3": "1.17.0", diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/package.json b/experimental/packages/opentelemetry-instrumentation-grpc/package.json index 2845904d95..fbf07ae716 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/package.json +++ b/experimental/packages/opentelemetry-instrumentation-grpc/package.json @@ -59,7 +59,7 @@ "@protobuf-ts/runtime-rpc": "2.9.1", "@types/mocha": "10.0.1", "@types/node": "18.6.5", - "@types/semver": "7.5.1", + "@types/semver": "7.5.2", "@types/sinon": "10.0.16", "codecov": "3.8.3", "cross-var": "1.1.0", diff --git a/experimental/packages/opentelemetry-instrumentation-http/package.json b/experimental/packages/opentelemetry-instrumentation-http/package.json index 91ce71b634..aebe37781d 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/package.json +++ b/experimental/packages/opentelemetry-instrumentation-http/package.json @@ -53,7 +53,7 @@ "@types/mocha": "10.0.1", "@types/node": "18.6.5", "@types/request-promise-native": "1.0.18", - "@types/semver": "7.5.1", + "@types/semver": "7.5.2", "@types/sinon": "10.0.16", "@types/superagent": "4.1.18", "axios": "1.5.0", diff --git a/experimental/packages/opentelemetry-instrumentation-xml-http-request/package.json b/experimental/packages/opentelemetry-instrumentation-xml-http-request/package.json index 1dc318d326..41c42d1fdc 100644 --- a/experimental/packages/opentelemetry-instrumentation-xml-http-request/package.json +++ b/experimental/packages/opentelemetry-instrumentation-xml-http-request/package.json @@ -54,7 +54,7 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@opentelemetry/api": "1.6.0", "@opentelemetry/context-zone": "1.17.0", "@opentelemetry/propagator-b3": "1.17.0", diff --git a/experimental/packages/opentelemetry-instrumentation/package.json b/experimental/packages/opentelemetry-instrumentation/package.json index 99a6d83016..589aa1039e 100644 --- a/experimental/packages/opentelemetry-instrumentation/package.json +++ b/experimental/packages/opentelemetry-instrumentation/package.json @@ -81,12 +81,12 @@ "@opentelemetry/api": "^1.3.0" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@opentelemetry/api": "1.6.0", "@opentelemetry/sdk-metrics": "1.17.0", "@types/mocha": "10.0.1", "@types/node": "18.6.5", - "@types/semver": "7.5.1", + "@types/semver": "7.5.2", "@types/sinon": "10.0.16", "@types/webpack-env": "1.16.3", "babel-loader": "8.3.0", diff --git a/experimental/packages/opentelemetry-sdk-node/package.json b/experimental/packages/opentelemetry-sdk-node/package.json index 70a423c79a..8517a07617 100644 --- a/experimental/packages/opentelemetry-sdk-node/package.json +++ b/experimental/packages/opentelemetry-sdk-node/package.json @@ -69,7 +69,7 @@ "@opentelemetry/exporter-jaeger": "1.17.0", "@types/mocha": "10.0.1", "@types/node": "18.6.5", - "@types/semver": "7.5.1", + "@types/semver": "7.5.2", "@types/sinon": "10.0.16", "codecov": "3.8.3", "cross-var": "1.1.0", diff --git a/experimental/packages/otlp-exporter-base/package.json b/experimental/packages/otlp-exporter-base/package.json index 9e47068c28..9bfde78d9d 100644 --- a/experimental/packages/otlp-exporter-base/package.json +++ b/experimental/packages/otlp-exporter-base/package.json @@ -64,7 +64,7 @@ "@opentelemetry/core": "1.17.0" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@opentelemetry/api": "1.6.0", "@types/mocha": "10.0.1", "@types/node": "18.6.5", diff --git a/experimental/packages/otlp-grpc-exporter-base/package.json b/experimental/packages/otlp-grpc-exporter-base/package.json index 963fbe9fa4..81c8c4c30f 100644 --- a/experimental/packages/otlp-grpc-exporter-base/package.json +++ b/experimental/packages/otlp-grpc-exporter-base/package.json @@ -48,7 +48,7 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@opentelemetry/api": "1.6.0", "@opentelemetry/otlp-transformer": "0.43.0", "@opentelemetry/resources": "1.17.0", diff --git a/experimental/packages/otlp-proto-exporter-base/package.json b/experimental/packages/otlp-proto-exporter-base/package.json index 5ba695faee..5335765695 100644 --- a/experimental/packages/otlp-proto-exporter-base/package.json +++ b/experimental/packages/otlp-proto-exporter-base/package.json @@ -59,7 +59,7 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@opentelemetry/api": "1.6.0", "@types/mocha": "10.0.1", "@types/node": "18.6.5", diff --git a/experimental/packages/sdk-logs/package.json b/experimental/packages/sdk-logs/package.json index fc388cad02..8d783b481b 100644 --- a/experimental/packages/sdk-logs/package.json +++ b/experimental/packages/sdk-logs/package.json @@ -72,7 +72,7 @@ "@opentelemetry/api-logs": ">=0.39.1" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@opentelemetry/api": ">=1.4.0 <1.7.0", "@opentelemetry/api-logs": "0.43.0", "@types/mocha": "10.0.1", diff --git a/experimental/packages/shim-opencensus/package.json b/experimental/packages/shim-opencensus/package.json index e1390bb446..2e24690be8 100644 --- a/experimental/packages/shim-opencensus/package.json +++ b/experimental/packages/shim-opencensus/package.json @@ -70,6 +70,8 @@ }, "dependencies": { "@opentelemetry/core": "1.17.0", + "@opentelemetry/resources": "1.17.0", + "@opentelemetry/sdk-metrics": "1.17.0", "require-in-the-middle": "^7.1.1", "semver": "^7.5.2" }, diff --git a/experimental/packages/shim-opencensus/src/OpenCensusMetricProducer.ts b/experimental/packages/shim-opencensus/src/OpenCensusMetricProducer.ts new file mode 100644 index 0000000000..04d4a06738 --- /dev/null +++ b/experimental/packages/shim-opencensus/src/OpenCensusMetricProducer.ts @@ -0,0 +1,104 @@ +/* + * Copyright 2018, OpenCensus Authors + * 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 * as oc from '@opencensus/core'; +import { Resource } from '@opentelemetry/resources'; +import { + CollectionResult, + MetricData, + MetricProducer, + ScopeMetrics, +} from '@opentelemetry/sdk-metrics'; +import { mapOcMetric } from './metric-transform'; +import { VERSION } from './version'; + +const SCOPE = { + name: '@opentelemetry/shim-opencensus', + version: VERSION, +} as const; + +interface OpenCensusMetricProducerOptions { + /** + * An instance of OpenCensus MetricProducerManager. If not provided, + * `oc.Metrics.getMetricProducerManager()` will be used. + */ + openCensusMetricProducerManager?: oc.MetricProducerManager; +} + +/** + * A {@link MetricProducer} which collects metrics from OpenCensus. Provide an instance to your + * {@link MetricReader} when you create it to include all OpenCensus metrics in the collection + * result: + * + * @example + * ``` + * const meterProvider = new MeterProvider(); + * const reader = new PeriodicExportingMetricReader({ + * metricProducers: [new OpenCensusMetricProducer()], + * exporter: exporter, + * }); + * meterProvider.addMetricReader(reader); + * ``` + */ +export class OpenCensusMetricProducer implements MetricProducer { + private _openCensusMetricProducerManager: oc.MetricProducerManager; + + constructor(options?: OpenCensusMetricProducerOptions) { + this._openCensusMetricProducerManager = + options?.openCensusMetricProducerManager ?? + oc.Metrics.getMetricProducerManager(); + } + + async collect(): Promise { + const metrics = await this._collectOpenCensus(); + const scopeMetrics: ScopeMetrics[] = + metrics.length === 0 + ? [] + : [ + { + scope: SCOPE, + metrics, + }, + ]; + + return { + errors: [], + resourceMetrics: { + // Resource is ignored by the SDK, it just uses the SDK's resource + resource: Resource.EMPTY, + scopeMetrics, + }, + }; + } + + private async _collectOpenCensus(): Promise { + const metrics: MetricData[] = []; + + // The use of oc.Metrics.getMetricProducerManager() was adapted from + // https://github.com/census-instrumentation/opencensus-node/blob/d46c8891b15783803d724b717db9a8c22cb73d6a/packages/opencensus-exporter-stackdriver/src/stackdriver-monitoring.ts#L122 + for (const metricProducer of this._openCensusMetricProducerManager.getAllMetricProducer()) { + for (const metric of metricProducer.getMetrics()) { + const metricData = mapOcMetric(metric); + if (metricData !== null) { + metrics.push(metricData); + } + } + } + + return metrics; + } +} diff --git a/experimental/packages/shim-opencensus/src/ShimSpan.ts b/experimental/packages/shim-opencensus/src/ShimSpan.ts index 498c73e5e7..e4eaf47a2d 100644 --- a/experimental/packages/shim-opencensus/src/ShimSpan.ts +++ b/experimental/packages/shim-opencensus/src/ShimSpan.ts @@ -17,7 +17,7 @@ import * as oc from '@opencensus/core'; import { ShimTracer } from './ShimTracer'; import { AttributeValue, Span, SpanStatusCode, diag } from '@opentelemetry/api'; -import { mapMessageEvent, reverseMapSpanContext } from './transform'; +import { mapMessageEvent, reverseMapSpanContext } from './trace-transform'; // Copied from // https://github.com/census-instrumentation/opencensus-node/blob/v0.1.0/packages/opencensus-core/src/trace/model/span.ts#L61 diff --git a/experimental/packages/shim-opencensus/src/ShimTracer.ts b/experimental/packages/shim-opencensus/src/ShimTracer.ts index f5e8164e3c..c276b99e53 100644 --- a/experimental/packages/shim-opencensus/src/ShimTracer.ts +++ b/experimental/packages/shim-opencensus/src/ShimTracer.ts @@ -26,7 +26,7 @@ import { Tracer, } from '@opentelemetry/api'; import { DEFAULT_SPAN_NAME, ShimSpan } from './ShimSpan'; -import { mapSpanContext, mapSpanKind } from './transform'; +import { mapSpanContext, mapSpanKind } from './trace-transform'; import { shimPropagation } from './propagation'; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion diff --git a/experimental/packages/shim-opencensus/src/index.ts b/experimental/packages/shim-opencensus/src/index.ts index 5df2f6c315..8889608ec3 100644 --- a/experimental/packages/shim-opencensus/src/index.ts +++ b/experimental/packages/shim-opencensus/src/index.ts @@ -15,4 +15,5 @@ */ export { ShimTracer } from './ShimTracer'; +export { OpenCensusMetricProducer } from './OpenCensusMetricProducer'; export { installShim, uninstallShim } from './shim'; diff --git a/experimental/packages/shim-opencensus/src/metric-transform.ts b/experimental/packages/shim-opencensus/src/metric-transform.ts new file mode 100644 index 0000000000..bc136d0ed2 --- /dev/null +++ b/experimental/packages/shim-opencensus/src/metric-transform.ts @@ -0,0 +1,211 @@ +/* + * 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 * as oc from '@opencensus/core'; +import { Attributes, HrTime, ValueType, diag } from '@opentelemetry/api'; +import { + AggregationTemporality, + DataPoint, + DataPointType, + GaugeMetricData, + HistogramMetricData, + InstrumentType, + MetricData, + SumMetricData, +} from '@opentelemetry/sdk-metrics'; + +type BaseMetric = Omit; +interface MappedType { + type: InstrumentType; + valueType: ValueType; + dataPointType: + | DataPointType.GAUGE + | DataPointType.SUM + | DataPointType.HISTOGRAM; +} +const ZEROED_HRTIME: HrTime = [0, 0]; + +export function mapOcMetric(metric: oc.Metric): MetricData | null { + const { description, name, unit, type } = metric.descriptor; + const mappedType = mapOcMetricDescriptorType(type); + if (mappedType === null) { + return null; + } + + const baseMetric: BaseMetric = { + aggregationTemporality: AggregationTemporality.CUMULATIVE, + descriptor: { + description, + name, + unit, + type: mappedType.type, + valueType: mappedType.valueType, + }, + }; + + switch (mappedType.dataPointType) { + case DataPointType.GAUGE: + return gauge(metric, mappedType.dataPointType, baseMetric); + case DataPointType.SUM: + return sum(metric, mappedType.dataPointType, baseMetric); + case DataPointType.HISTOGRAM: + return histogram(metric, mappedType.dataPointType, baseMetric); + } +} + +function mapOcMetricDescriptorType( + type: oc.MetricDescriptorType +): MappedType | null { + switch (type) { + case oc.MetricDescriptorType.GAUGE_INT64: + return { + type: InstrumentType.OBSERVABLE_GAUGE, + valueType: ValueType.INT, + dataPointType: DataPointType.GAUGE, + }; + case oc.MetricDescriptorType.GAUGE_DOUBLE: + return { + type: InstrumentType.OBSERVABLE_GAUGE, + valueType: ValueType.DOUBLE, + dataPointType: DataPointType.GAUGE, + }; + + case oc.MetricDescriptorType.CUMULATIVE_INT64: + return { + type: InstrumentType.COUNTER, + valueType: ValueType.INT, + dataPointType: DataPointType.SUM, + }; + case oc.MetricDescriptorType.CUMULATIVE_DOUBLE: + return { + type: InstrumentType.COUNTER, + valueType: ValueType.DOUBLE, + dataPointType: DataPointType.SUM, + }; + + case oc.MetricDescriptorType.CUMULATIVE_DISTRIBUTION: + return { + type: InstrumentType.HISTOGRAM, + valueType: ValueType.DOUBLE, + dataPointType: DataPointType.HISTOGRAM, + }; + + case oc.MetricDescriptorType.SUMMARY: + case oc.MetricDescriptorType.GAUGE_DISTRIBUTION: + case oc.MetricDescriptorType.UNSPECIFIED: + diag.warn( + 'Got unsupported metric MetricDescriptorType from OpenCensus: %s', + type + ); + return null; + } +} + +function gauge( + metric: oc.Metric, + dataPointType: DataPointType.GAUGE, + baseMetric: BaseMetric +): GaugeMetricData { + return { + ...baseMetric, + dataPoints: dataPoints(metric, value => value as number), + dataPointType, + }; +} + +function sum( + metric: oc.Metric, + dataPointType: DataPointType.SUM, + baseMetric: BaseMetric +): SumMetricData { + return { + ...baseMetric, + dataPoints: dataPoints(metric, value => value as number), + isMonotonic: true, + dataPointType, + }; +} + +function histogram( + metric: oc.Metric, + dataPointType: DataPointType.HISTOGRAM, + baseMetric: BaseMetric +): HistogramMetricData { + return { + ...baseMetric, + dataPoints: dataPoints(metric, value => { + const { + bucketOptions: { + explicit: { bounds }, + }, + buckets, + count, + sum: distSum, + } = value as oc.DistributionValue; + + return { + buckets: { + boundaries: bounds, + counts: buckets.map(bucket => bucket.count), + }, + count, + sum: distSum, + }; + }), + dataPointType, + }; +} + +function dataPoints( + metric: oc.Metric, + valueMapper: (value: oc.TimeSeriesPoint['value']) => T +): DataPoint[] { + return metric.timeseries.flatMap(ts => { + const attributes = zipOcLabels(metric.descriptor.labelKeys, ts.labelValues); + + // use zeroed hrTime if it is undefined, which probably shouldn't happen + const startTime = ocTimestampToHrTime(ts.startTimestamp) ?? ZEROED_HRTIME; + + // points should be an array with a single value, so this will return a single point per + // attribute set. + return ts.points.map( + (point): DataPoint => ({ + startTime, + attributes, + value: valueMapper(point.value), + endTime: ocTimestampToHrTime(point.timestamp) ?? ZEROED_HRTIME, + }) + ); + }); +} + +function ocTimestampToHrTime(ts: oc.Timestamp | undefined): HrTime | null { + if (ts === undefined || ts.seconds === null) { + return null; + } + return [ts.seconds, ts.nanos ?? 0]; +} + +function zipOcLabels( + labelKeys: oc.LabelKey[], + labelValues: oc.LabelValue[] +): Attributes { + const attributes: Attributes = {}; + for (let i = 0; i < labelKeys.length; i++) { + attributes[labelKeys[i].key] = labelValues[i].value ?? ''; + } + return attributes; +} diff --git a/experimental/packages/shim-opencensus/src/propagation.ts b/experimental/packages/shim-opencensus/src/propagation.ts index deb2509fa7..e0045fe7d9 100644 --- a/experimental/packages/shim-opencensus/src/propagation.ts +++ b/experimental/packages/shim-opencensus/src/propagation.ts @@ -23,7 +23,7 @@ import { TextMapGetter, TextMapSetter, } from '@opentelemetry/api'; -import { mapSpanContext, reverseMapSpanContext } from './transform'; +import { mapSpanContext, reverseMapSpanContext } from './trace-transform'; class Getter implements TextMapGetter { constructor(private ocGetter: oc.HeaderGetter) {} diff --git a/experimental/packages/shim-opencensus/src/transform.ts b/experimental/packages/shim-opencensus/src/trace-transform.ts similarity index 100% rename from experimental/packages/shim-opencensus/src/transform.ts rename to experimental/packages/shim-opencensus/src/trace-transform.ts diff --git a/experimental/packages/shim-opencensus/test/OpenCensusMetricProducer.test.ts b/experimental/packages/shim-opencensus/test/OpenCensusMetricProducer.test.ts new file mode 100644 index 0000000000..15b9cc1a6b --- /dev/null +++ b/experimental/packages/shim-opencensus/test/OpenCensusMetricProducer.test.ts @@ -0,0 +1,118 @@ +/* + * 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 * as oc from '@opencensus/core'; +import { ValueType } from '@opentelemetry/api'; +import { Resource } from '@opentelemetry/resources'; +import { + AggregationTemporality, + DataPointType, + SumMetricData, +} from '@opentelemetry/sdk-metrics'; +import * as assert from 'assert'; +import * as sinon from 'sinon'; +import { OpenCensusMetricProducer } from '../src/OpenCensusMetricProducer'; + +describe('OpenCensusMetricProducer', () => { + beforeEach(() => { + oc.globalStats.clear(); + sinon.useFakeTimers(); + }); + + afterEach(() => { + sinon.restore(); + }); + + // Since the resource is replaced by the SDK anyway + it('should return an empty Resource', async () => { + const producer = new OpenCensusMetricProducer(); + const resourceMetrics = await producer.collect(); + + assert.deepStrictEqual( + resourceMetrics.resourceMetrics.resource, + Resource.EMPTY + ); + }); + + it('should return no errors when no metrics are collected from OpenCensus', async () => { + const producer = new OpenCensusMetricProducer(); + const resourceMetrics = await producer.collect(); + assert.strictEqual(resourceMetrics.errors.length, 0); + }); + + it('should elide the scope when no metrics are collected from OpenCensus', async () => { + // No OpenCensus setup so won't produce any metrics + const producer = new OpenCensusMetricProducer(); + const resourceMetrics = await producer.collect(); + assert.strictEqual(resourceMetrics.resourceMetrics.scopeMetrics.length, 0); + }); + + it('should include OpenCensus metrics', async () => { + // Initialize OC metrics with one counter, adapted from + // https://opencensus.io/quickstart/nodejs/metrics/ + const measure = oc.globalStats.createMeasureDouble( + 'measure', + oc.MeasureUnit.MS + ); + const tagKey = { name: 'label1' }; + oc.globalStats.registerView( + oc.globalStats.createView( + 'measure', + measure, + oc.AggregationType.SUM, + [tagKey], + 'Test OC description' + ) + ); + + const tagMap = new oc.TagMap(); + tagMap.set(tagKey, { value: 'tagvalue' }); + oc.globalStats.record([{ measure, value: 125 }], tagMap); + + const producer = new OpenCensusMetricProducer(); + const resourceMetrics = await producer.collect(); + + assert.strictEqual(resourceMetrics.errors.length, 0); + assert.strictEqual(resourceMetrics.resourceMetrics.scopeMetrics.length, 1); + assert.strictEqual( + resourceMetrics.resourceMetrics.scopeMetrics[0].scope.name, + '@opentelemetry/shim-opencensus' + ); + assert.strictEqual( + resourceMetrics.resourceMetrics.scopeMetrics[0].metrics.length, + 1 + ); + const ocMetric = resourceMetrics.resourceMetrics.scopeMetrics[0] + .metrics[0] as SumMetricData; + assert.deepStrictEqual(ocMetric.descriptor, { + description: 'Test OC description', + name: 'measure', + type: 'COUNTER', + unit: 'ms', + valueType: ValueType.DOUBLE, + }); + assert.strictEqual(ocMetric.dataPoints[0].value, 125); + assert.deepStrictEqual(ocMetric.dataPoints[0].attributes, { + label1: 'tagvalue', + }); + assert.strictEqual(ocMetric.dataPointType, DataPointType.SUM); + assert.strictEqual(ocMetric.isMonotonic, true); + assert.strictEqual( + ocMetric.aggregationTemporality, + AggregationTemporality.CUMULATIVE + ); + }); +}); diff --git a/experimental/packages/shim-opencensus/test/metric-transform.test.ts b/experimental/packages/shim-opencensus/test/metric-transform.test.ts new file mode 100644 index 0000000000..289fbefd0c --- /dev/null +++ b/experimental/packages/shim-opencensus/test/metric-transform.test.ts @@ -0,0 +1,321 @@ +/* + * 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 { mapOcMetric } from '../src/metric-transform'; + +import * as oc from '@opencensus/core'; +import { ValueType } from '@opentelemetry/api'; +import { + AggregationTemporality, + DataPointType, + GaugeMetricData, + HistogramMetricData, + InstrumentType, + SumMetricData, +} from '@opentelemetry/sdk-metrics'; +import * as assert from 'assert'; + +describe('metric-transform', () => { + it('should map OpenCensus CUMULATIVE_INT64 to Sum', () => { + const metricData = mapOcMetric({ + descriptor: { + description: 'ocDescription', + name: 'ocMetricName', + type: oc.MetricDescriptorType.CUMULATIVE_INT64, + unit: 'ocUnit', + labelKeys: [ + { key: 'key1', description: '' }, + { key: 'key2', description: '' }, + ], + }, + timeseries: [ + { + startTimestamp: { seconds: 10, nanos: 10 }, + labelValues: [{ value: 'value1' }, { value: 'value2' }], + points: [{ timestamp: { seconds: 20, nanos: 20 }, value: 5 }], + }, + ], + }); + + assert.deepStrictEqual(metricData, { + aggregationTemporality: AggregationTemporality.CUMULATIVE, + dataPointType: DataPointType.SUM, + dataPoints: [ + { + attributes: { key1: 'value1', key2: 'value2' }, + endTime: [20, 20], + startTime: [10, 10], + value: 5, + }, + ], + descriptor: { + description: 'ocDescription', + name: 'ocMetricName', + type: InstrumentType.COUNTER, + unit: 'ocUnit', + valueType: ValueType.INT, + }, + isMonotonic: true, + } as SumMetricData); + }); + + it('should map OpenCensus CUMULATIVE_DOUBLE to Sum', () => { + const metricData = mapOcMetric({ + descriptor: { + description: 'ocDescription', + name: 'ocMetricName', + type: oc.MetricDescriptorType.CUMULATIVE_DOUBLE, + unit: 'ocUnit', + labelKeys: [ + { key: 'key1', description: '' }, + { key: 'key2', description: '' }, + ], + }, + timeseries: [ + { + startTimestamp: { seconds: 10, nanos: 10 }, + labelValues: [{ value: 'value1' }, { value: 'value2' }], + points: [{ timestamp: { seconds: 20, nanos: 20 }, value: 5.5 }], + }, + ], + }); + + assert.deepStrictEqual(metricData, { + aggregationTemporality: AggregationTemporality.CUMULATIVE, + dataPointType: DataPointType.SUM, + dataPoints: [ + { + attributes: { key1: 'value1', key2: 'value2' }, + endTime: [20, 20], + startTime: [10, 10], + value: 5.5, + }, + ], + descriptor: { + description: 'ocDescription', + name: 'ocMetricName', + type: InstrumentType.COUNTER, + unit: 'ocUnit', + valueType: ValueType.DOUBLE, + }, + isMonotonic: true, + } as SumMetricData); + }); + + it('should map OpenCensus CUMULATIVE_DISTRIBUTION to Histogram', () => { + const metricData = mapOcMetric({ + descriptor: { + description: 'ocDescription', + name: 'ocMetricName', + type: oc.MetricDescriptorType.CUMULATIVE_DISTRIBUTION, + unit: 'ocUnit', + labelKeys: [ + { key: 'key1', description: '' }, + { key: 'key2', description: '' }, + ], + }, + timeseries: [ + { + startTimestamp: { seconds: 10, nanos: 10 }, + labelValues: [{ value: 'value1' }, { value: 'value2' }], + points: [ + { + timestamp: { seconds: 20, nanos: 20 }, + value: { + bucketOptions: { + explicit: { + bounds: [1, 10, 100], + }, + }, + buckets: [ + { count: 0 }, + { count: 1 }, + { count: 2 }, + { count: 3 }, + ], + count: 6, + sum: 121, + sumOfSquaredDeviation: 4, + }, + }, + ], + }, + ], + }); + + assert.deepStrictEqual(metricData, { + aggregationTemporality: AggregationTemporality.CUMULATIVE, + dataPointType: DataPointType.HISTOGRAM, + dataPoints: [ + { + attributes: { key1: 'value1', key2: 'value2' }, + endTime: [20, 20], + startTime: [10, 10], + value: { + buckets: { + boundaries: [1, 10, 100], + counts: [0, 1, 2, 3], + }, + count: 6, + sum: 121, + }, + }, + ], + descriptor: { + description: 'ocDescription', + name: 'ocMetricName', + type: InstrumentType.HISTOGRAM, + unit: 'ocUnit', + valueType: ValueType.DOUBLE, + }, + } as HistogramMetricData); + }); + + it('should map OpenCensus GAUGE_INT64 to Gauge', () => { + const metricData = mapOcMetric({ + descriptor: { + description: 'ocDescription', + name: 'ocMetricName', + type: oc.MetricDescriptorType.GAUGE_INT64, + unit: 'ocUnit', + labelKeys: [ + { key: 'key1', description: '' }, + { key: 'key2', description: '' }, + ], + }, + timeseries: [ + { + startTimestamp: { seconds: 10, nanos: 10 }, + labelValues: [{ value: 'value1' }, { value: 'value2' }], + points: [{ timestamp: { seconds: 20, nanos: 20 }, value: 5 }], + }, + ], + }); + + assert.deepStrictEqual(metricData, { + aggregationTemporality: AggregationTemporality.CUMULATIVE, + dataPointType: DataPointType.GAUGE, + dataPoints: [ + { + attributes: { key1: 'value1', key2: 'value2' }, + endTime: [20, 20], + startTime: [10, 10], + value: 5, + }, + ], + descriptor: { + description: 'ocDescription', + name: 'ocMetricName', + type: InstrumentType.OBSERVABLE_GAUGE, + unit: 'ocUnit', + valueType: ValueType.INT, + }, + } as GaugeMetricData); + }); + + it('should map OpenCensus GAUGE_DOUBLE to Gauge', () => { + const metricData = mapOcMetric({ + descriptor: { + description: 'ocDescription', + name: 'ocMetricName', + type: oc.MetricDescriptorType.GAUGE_DOUBLE, + unit: 'ocUnit', + labelKeys: [ + { key: 'key1', description: '' }, + { key: 'key2', description: '' }, + ], + }, + timeseries: [ + { + startTimestamp: { seconds: 10, nanos: 10 }, + labelValues: [{ value: 'value1' }, { value: 'value2' }], + points: [{ timestamp: { seconds: 20, nanos: 20 }, value: 5.5 }], + }, + ], + }); + + assert.deepStrictEqual(metricData, { + aggregationTemporality: AggregationTemporality.CUMULATIVE, + dataPointType: DataPointType.GAUGE, + dataPoints: [ + { + attributes: { key1: 'value1', key2: 'value2' }, + endTime: [20, 20], + startTime: [10, 10], + value: 5.5, + }, + ], + descriptor: { + description: 'ocDescription', + name: 'ocMetricName', + type: InstrumentType.OBSERVABLE_GAUGE, + unit: 'ocUnit', + valueType: ValueType.DOUBLE, + }, + } as GaugeMetricData); + }); + + it('should drop unsupported OpenCensus GAUGE_DISTRIBUTION', () => { + const metricData = mapOcMetric({ + descriptor: { + description: 'ocDescription', + name: 'ocMetricName', + type: oc.MetricDescriptorType.GAUGE_DISTRIBUTION, + unit: 'ocUnit', + labelKeys: [ + { key: 'key1', description: '' }, + { key: 'key2', description: '' }, + ], + }, + timeseries: [ + { + startTimestamp: { seconds: 10, nanos: 10 }, + labelValues: [{ value: 'value1' }, { value: 'value2' }], + points: [{ timestamp: { seconds: 20, nanos: 20 }, value: 5 }], + }, + ], + }); + assert.deepStrictEqual(metricData, null); + }); + + it('should drop unsupported OpenCensus SUMMARY', () => { + const metricData = mapOcMetric({ + descriptor: { + description: 'ocDescription', + name: 'ocMetricName', + type: oc.MetricDescriptorType.SUMMARY, + unit: 'ocUnit', + labelKeys: [ + { key: 'key1', description: '' }, + { key: 'key2', description: '' }, + ], + }, + timeseries: [ + { + startTimestamp: { seconds: 10, nanos: 10 }, + labelValues: [{ value: 'value1' }, { value: 'value2' }], + points: [ + { + timestamp: { seconds: 20, nanos: 20 }, + value: { count: 5, sum: 10 }, + }, + ], + }, + ], + }); + assert.deepStrictEqual(metricData, null); + }); +}); diff --git a/experimental/packages/shim-opencensus/test/transform.test.ts b/experimental/packages/shim-opencensus/test/trace-transform.test.ts similarity index 98% rename from experimental/packages/shim-opencensus/test/transform.test.ts rename to experimental/packages/shim-opencensus/test/trace-transform.test.ts index 9a7f90cda0..6aacd5e951 100644 --- a/experimental/packages/shim-opencensus/test/transform.test.ts +++ b/experimental/packages/shim-opencensus/test/trace-transform.test.ts @@ -19,14 +19,14 @@ import { mapSpanContext, mapSpanKind, reverseMapSpanContext, -} from '../src/transform'; +} from '../src/trace-transform'; import * as oc from '@opencensus/core'; import { SpanKind } from '@opentelemetry/api'; import { TraceState } from '@opentelemetry/core'; import * as assert from 'assert'; -describe('transform', () => { +describe('trace-transform', () => { describe('mapSpanKind', () => { it('should return undefined with undefined input', () => { assert.strictEqual(mapSpanKind(undefined), undefined); diff --git a/experimental/packages/shim-opencensus/tsconfig.json b/experimental/packages/shim-opencensus/tsconfig.json index 91cebb5ad5..145dd31760 100644 --- a/experimental/packages/shim-opencensus/tsconfig.json +++ b/experimental/packages/shim-opencensus/tsconfig.json @@ -18,8 +18,14 @@ { "path": "../../../packages/opentelemetry-core" }, + { + "path": "../../../packages/opentelemetry-resources" + }, { "path": "../../../packages/opentelemetry-sdk-trace-base" + }, + { + "path": "../../../packages/sdk-metrics" } ] } diff --git a/packages/opentelemetry-context-zone-peer-dep/package.json b/packages/opentelemetry-context-zone-peer-dep/package.json index 477cf0d75d..addc0bc388 100644 --- a/packages/opentelemetry-context-zone-peer-dep/package.json +++ b/packages/opentelemetry-context-zone-peer-dep/package.json @@ -53,7 +53,7 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@opentelemetry/api": ">=1.0.0 <1.7.0", "@types/mocha": "10.0.1", "@types/node": "18.6.5", diff --git a/packages/opentelemetry-context-zone/package.json b/packages/opentelemetry-context-zone/package.json index d716738c52..b61d91650e 100644 --- a/packages/opentelemetry-context-zone/package.json +++ b/packages/opentelemetry-context-zone/package.json @@ -50,7 +50,7 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@types/mocha": "10.0.1", "@types/node": "18.6.5", "@types/sinon": "10.0.16", diff --git a/packages/opentelemetry-exporter-zipkin/package.json b/packages/opentelemetry-exporter-zipkin/package.json index 942a9ee6f5..f5b6218603 100644 --- a/packages/opentelemetry-exporter-zipkin/package.json +++ b/packages/opentelemetry-exporter-zipkin/package.json @@ -60,7 +60,7 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@opentelemetry/api": "^1.0.0", "@types/mocha": "10.0.1", "@types/node": "18.6.5", diff --git a/packages/opentelemetry-sdk-trace-node/package.json b/packages/opentelemetry-sdk-trace-node/package.json index 9d7dfbbc9c..0bdcc4a090 100644 --- a/packages/opentelemetry-sdk-trace-node/package.json +++ b/packages/opentelemetry-sdk-trace-node/package.json @@ -50,7 +50,7 @@ "@opentelemetry/semantic-conventions": "1.17.0", "@types/mocha": "10.0.1", "@types/node": "18.6.5", - "@types/semver": "7.5.1", + "@types/semver": "7.5.2", "@types/sinon": "10.0.16", "codecov": "3.8.3", "cross-var": "1.1.0", diff --git a/packages/opentelemetry-sdk-trace-web/package.json b/packages/opentelemetry-sdk-trace-web/package.json index a1c9939a0b..7d467a09b4 100644 --- a/packages/opentelemetry-sdk-trace-web/package.json +++ b/packages/opentelemetry-sdk-trace-web/package.json @@ -55,7 +55,7 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@opentelemetry/api": ">=1.0.0 <1.7.0", "@opentelemetry/context-zone": "1.17.0", "@opentelemetry/propagator-b3": "1.17.0", diff --git a/packages/sdk-metrics/package.json b/packages/sdk-metrics/package.json index dd7963f1c8..d61505660a 100644 --- a/packages/sdk-metrics/package.json +++ b/packages/sdk-metrics/package.json @@ -54,7 +54,7 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@opentelemetry/api": ">=1.3.0 <1.7.0", "@types/lodash.merge": "4.6.7", "@types/mocha": "10.0.1", diff --git a/selenium-tests/package.json b/selenium-tests/package.json index a05172df31..31456eb93a 100644 --- a/selenium-tests/package.json +++ b/selenium-tests/package.json @@ -31,7 +31,7 @@ "access": "restricted" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.19", "@babel/plugin-proposal-class-properties": "7.18.6", "@babel/plugin-proposal-decorators": "7.22.15", "@babel/plugin-transform-runtime": "7.22.15",