diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c48f21a6e..ad5c780983 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,10 +13,14 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/ ### :bug: (Bug Fix) +* fix(sdk-metrics): allow instrument names to contain '/' [#4155](https://github.com/open-telemetry/opentelemetry-js/pull/4155) + ### :books: (Refine Doc) ### :house: (Internal) +* test: added a performance benchmark test for span creation [#4105](https://github.com/open-telemetry/opentelemetry-js/pull/4105) + ## 1.17.0 ### :bug: (Bug Fix) 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..bc0fb011da 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -9,11 +9,20 @@ All notable changes to experimental packages in this project will be documented ### :boom: Breaking Change * fix(exporter-logs-otlp-proto): change OTLPLogExporter to OTLPLogExporter [#4140](https://github.com/open-telemetry/opentelemetry-js/pull/4140) @Vunovati +* fix(sdk-node): remove explicit dependency on @opentelemetry/exporter-jaeger + * '@opentelemetry/exporter-jaeger' is no longer be a dependency of this package. To continue using '@opentelemetry/exporter-jaeger', please install it manually. + * NOTE: `@opentelemetry/exporter-jaeger` is deprecated, consider switching to one of the alternatives described [here](https://www.npmjs.com/package/@opentelemetry/exporter-jaeger) +* fix(sdk-logs): hide internal methods with internal shared state [#3865](https://github.com/open-telemetry/opentelemetry-js/pull/3865) @legendecas ### :rocket: (Enhancement) +* feat(exporter-metrics-otlp-proto): add esm build [#4099](https://github.com/open-telemetry/opentelemetry-js/pull/4099) @pichlermarc +* feat(opencensus-shim): implement OpenCensus metric producer [#4066](https://github.com/open-telemetry/opentelemetry-js/pull/4066) @aabmass + ### :bug: (Bug Fix) +* fix(otlp-exporter-base): replaced usage of window with _globalThis [#4157](https://github.com/open-telemetry/opentelemetry-js/pull/4157) @cristianmadularu + ### :books: (Refine Doc) ### :house: (Internal) @@ -48,7 +57,6 @@ All notable changes to experimental packages in this project will be documented ### :bug: (Bug Fix) * fix(exporter-logs-otlp-http): add @opentelemetry/api-logs as dependency -* fix(sdk-node): remove explicit dependency on @opentelemetry/exporter-jaeger. ## 0.41.2 diff --git a/experimental/examples/opencensus-shim/README.md b/experimental/examples/opencensus-shim/README.md index 0738d67d89..6d95a91356 100644 --- a/experimental/examples/opencensus-shim/README.md +++ b/experimental/examples/opencensus-shim/README.md @@ -9,6 +9,8 @@ The example has: - Root Spans (on client), instrumented with OpenCensus's HTTP instrumentation - Child Span from a remote parent (on server), instrumented with OpenCensus's HTTP instrumentation - Another Child Span created in the server representing some work being done, instrumented manually with OpenTelemetry. +- Server metrics coming from OpenCensus's HTTP instrumentation, available through the +OpenTelemetry's Prometheus exporter. ## Installation @@ -64,6 +66,14 @@ Go to Jaeger with your browser and click on the "Servi

+## Check the Prometheus metrics + +Load the Prometheus metrics endpoint of the server at in your +browser. You should see the `opencensus_io_http_server_*` related metrics in +the output. + +

+ ## Useful links - For more information on OpenTelemetry, visit: diff --git a/experimental/examples/opencensus-shim/images/prom-metrics.png b/experimental/examples/opencensus-shim/images/prom-metrics.png new file mode 100644 index 0000000000..953e12253d Binary files /dev/null and b/experimental/examples/opencensus-shim/images/prom-metrics.png differ diff --git a/experimental/examples/opencensus-shim/package.json b/experimental/examples/opencensus-shim/package.json index d8615a6b74..bd97c726c1 100644 --- a/experimental/examples/opencensus-shim/package.json +++ b/experimental/examples/opencensus-shim/package.json @@ -28,10 +28,13 @@ }, "dependencies": { "@opencensus/core": "0.1.0", + "@opencensus/instrumentation-http": "0.1.0", "@opencensus/nodejs-base": "0.1.0", "@opentelemetry/api": "1.6.0", + "@opentelemetry/exporter-prometheus": "0.43.0", "@opentelemetry/exporter-trace-otlp-grpc": "0.43.0", "@opentelemetry/resources": "1.17.0", + "@opentelemetry/sdk-metrics": "1.17.0", "@opentelemetry/sdk-trace-node": "1.17.0", "@opentelemetry/semantic-conventions": "1.17.0", "@opentelemetry/shim-opencensus": "0.43.0" diff --git a/experimental/examples/opencensus-shim/server.js b/experimental/examples/opencensus-shim/server.js index 98ddcf5b60..00893bafe9 100644 --- a/experimental/examples/opencensus-shim/server.js +++ b/experimental/examples/opencensus-shim/server.js @@ -5,6 +5,8 @@ const setup = require('./setup'); const utils = require('./utils'); const { trace } = require('@opentelemetry/api'); +const oc = require('@opencensus/core'); + setup('opencensus-shim-example-server'); const http = require('http'); diff --git a/experimental/examples/opencensus-shim/setup.js b/experimental/examples/opencensus-shim/setup.js index 37206971e7..99bf8bf8b4 100644 --- a/experimental/examples/opencensus-shim/setup.js +++ b/experimental/examples/opencensus-shim/setup.js @@ -15,37 +15,64 @@ */ 'use strict'; -const { DiagConsoleLogger, diag, DiagLogLevel } = require('@opentelemetry/api'); +const { diag, metrics } = require('@opentelemetry/api'); const { NodeTracerProvider, BatchSpanProcessor, } = require('@opentelemetry/sdk-trace-node'); +const { MeterProvider } = require('@opentelemetry/sdk-metrics'); const { OTLPTraceExporter, } = require('@opentelemetry/exporter-trace-otlp-grpc'); +const { PrometheusExporter } = require('@opentelemetry/exporter-prometheus'); const { Resource } = require('@opentelemetry/resources'); const { SemanticResourceAttributes, } = require('@opentelemetry/semantic-conventions'); +const { OpenCensusMetricProducer } = require('@opentelemetry/shim-opencensus'); +const instrumentationHttp = require('@opencensus/instrumentation-http'); +const { TracingBase } = require('@opencensus/nodejs-base'); +const oc = require('@opencensus/core'); module.exports = function setup(serviceName) { - const tracing = require('@opencensus/nodejs-base'); + /** + * You can alternatively just use the @opentelemetry/nodejs package directly: + * + * ```js + * const tracing = require('@opencensus/nodejs'); + * ``` + */ + const tracing = new TracingBase(['http']); + tracing.tracer = new oc.CoreTracer(); - diag.setLogger(new DiagConsoleLogger(), { logLevel: DiagLogLevel.ALL }); - const provider = new NodeTracerProvider({ - resource: new Resource({ - [SemanticResourceAttributes.SERVICE_NAME]: serviceName, - }), + const resource = new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: serviceName, }); - provider.addSpanProcessor( + const tracerProvider = new NodeTracerProvider({ resource }); + tracerProvider.addSpanProcessor( new BatchSpanProcessor(new OTLPTraceExporter(), { scheduledDelayMillis: 5000, }) ); - provider.register(); + tracerProvider.register(); + + const meterProvider = new MeterProvider({ resource }); + meterProvider.addMetricReader( + new PrometheusExporter({ + metricProducers: [ + new OpenCensusMetricProducer({ + openCensusMetricProducerManager: + oc.Metrics.getMetricProducerManager(), + }), + ], + }) + ); + metrics.setGlobalMeterProvider(meterProvider); // Start OpenCensus tracing - tracing.start({ samplingRate: 1, logger: diag }); + tracing.start({ samplingRate: 1, logger: diag, stats: oc.globalStats }); + // Register OpenCensus HTTP stats views + instrumentationHttp.registerAllViews(oc.globalStats); - return provider; + return tracerProvider; }; diff --git a/experimental/packages/exporter-logs-otlp-grpc/package.json b/experimental/packages/exporter-logs-otlp-grpc/package.json index 442b003c73..82146ce439 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.20", "@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..05b217da2d 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.20", "@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..4aa74b7eb2 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.20", "@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..80c2ea3a0c 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.20", "@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..cfc2522a53 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.20", "@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..935b79963f 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.20", "@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..d983979a06 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.20", "@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..3c316d301f 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.20", "@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..8db6655f59 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.20", "@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..52ce8df0a2 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json @@ -3,18 +3,20 @@ "version": "0.43.0", "description": "OpenTelemetry Collector Metrics Exporter allows user to send collected metrics to the OpenTelemetry Collector using protobuf over HTTP", "main": "build/src/index.js", + "module": "build/esm/index.js", + "esnext": "build/esnext/index.js", "types": "build/src/index.d.ts", "repository": "open-telemetry/opentelemetry-js", "scripts": { "prepublishOnly": "npm run compile", - "compile": "tsc --build", - "clean": "tsc --build --clean", + "compile": "tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json", + "clean": "tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json", "lint": "eslint . --ext .ts", "lint:fix": "eslint . --ext .ts --fix", "tdd": "npm run test -- --watch-extensions ts --watch", - "test": "nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'", + "test": "nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'", "version": "node ../../../scripts/version-update.js", - "watch": "tsc -w", + "watch": "tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json", "precompile": "cross-var lerna run version --scope $npm_package_name --include-dependencies", "prewatch": "npm run precompile", "peer-api-check": "node ../../../scripts/peer-api-check.js", @@ -35,6 +37,12 @@ "node": ">=14" }, "files": [ + "build/esm/**/*.js", + "build/esm/**/*.js.map", + "build/esm/**/*.d.ts", + "build/esnext/**/*.js", + "build/esnext/**/*.js.map", + "build/esnext/**/*.d.ts", "build/src/**/*.js", "build/src/**/*.js.map", "build/src/**/*.d.ts", @@ -47,7 +55,7 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.20", "@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/tsconfig.esm.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/tsconfig.esm.json new file mode 100644 index 0000000000..b657f54211 --- /dev/null +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/tsconfig.esm.json @@ -0,0 +1,37 @@ +{ + "extends": "../../../tsconfig.base.esm.json", + "compilerOptions": { + "outDir": "build/esm", + "rootDir": "src", + "tsBuildInfoFile": "build/esm/tsconfig.esm.tsbuildinfo" + }, + "include": [ + "src/**/*.ts" + ], + "references": [ + { + "path": "../../../api" + }, + { + "path": "../../../packages/opentelemetry-core" + }, + { + "path": "../../../packages/opentelemetry-resources" + }, + { + "path": "../../../packages/sdk-metrics" + }, + { + "path": "../opentelemetry-exporter-metrics-otlp-http" + }, + { + "path": "../otlp-exporter-base" + }, + { + "path": "../otlp-proto-exporter-base" + }, + { + "path": "../otlp-transformer" + } + ] +} diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/tsconfig.esnext.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/tsconfig.esnext.json new file mode 100644 index 0000000000..31e46faeda --- /dev/null +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/tsconfig.esnext.json @@ -0,0 +1,37 @@ +{ + "extends": "../../../tsconfig.base.esnext.json", + "compilerOptions": { + "outDir": "build/esnext", + "rootDir": "src", + "tsBuildInfoFile": "build/esnext/tsconfig.esnext.tsbuildinfo" + }, + "include": [ + "src/**/*.ts" + ], + "references": [ + { + "path": "../../../api" + }, + { + "path": "../../../packages/opentelemetry-core" + }, + { + "path": "../../../packages/opentelemetry-resources" + }, + { + "path": "../../../packages/sdk-metrics" + }, + { + "path": "../opentelemetry-exporter-metrics-otlp-http" + }, + { + "path": "../otlp-exporter-base" + }, + { + "path": "../otlp-proto-exporter-base" + }, + { + "path": "../otlp-transformer" + } + ] +} diff --git a/experimental/packages/opentelemetry-instrumentation-fetch/package.json b/experimental/packages/opentelemetry-instrumentation-fetch/package.json index 009aea1240..948b357d5a 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.20", "@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..511f4fe3e0 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.20", "@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..ca9e9df4e8 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.20", "@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..7981365842 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.20", "@opentelemetry/api": "1.6.0", "@types/mocha": "10.0.1", "@types/node": "18.6.5", diff --git a/experimental/packages/otlp-exporter-base/src/platform/browser/OTLPExporterBrowserBase.ts b/experimental/packages/otlp-exporter-base/src/platform/browser/OTLPExporterBrowserBase.ts index 2888e317df..dbe02b222d 100644 --- a/experimental/packages/otlp-exporter-base/src/platform/browser/OTLPExporterBrowserBase.ts +++ b/experimental/packages/otlp-exporter-base/src/platform/browser/OTLPExporterBrowserBase.ts @@ -20,7 +20,7 @@ import * as otlpTypes from '../../types'; import { parseHeaders } from '../../util'; import { sendWithBeacon, sendWithXhr } from './util'; import { diag } from '@opentelemetry/api'; -import { getEnv, baggageUtils } from '@opentelemetry/core'; +import { getEnv, baggageUtils, _globalThis } from '@opentelemetry/core'; /** * Collector Metric Exporter abstract base class @@ -53,11 +53,11 @@ export abstract class OTLPExporterBrowserBase< } onInit(): void { - window.addEventListener('unload', this.shutdown); + _globalThis.addEventListener('unload', this.shutdown); } onShutdown(): void { - window.removeEventListener('unload', this.shutdown); + _globalThis.removeEventListener('unload', this.shutdown); } send( diff --git a/experimental/packages/otlp-grpc-exporter-base/package.json b/experimental/packages/otlp-grpc-exporter-base/package.json index 963fbe9fa4..cfa48c56e8 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.20", "@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..d350c263f6 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.20", "@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..f35a643c07 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.20", "@opentelemetry/api": ">=1.4.0 <1.7.0", "@opentelemetry/api-logs": "0.43.0", "@types/mocha": "10.0.1", diff --git a/experimental/packages/sdk-logs/src/LogRecord.ts b/experimental/packages/sdk-logs/src/LogRecord.ts index d184004e6b..ed76d5eb07 100644 --- a/experimental/packages/sdk-logs/src/LogRecord.ts +++ b/experimental/packages/sdk-logs/src/LogRecord.ts @@ -26,8 +26,8 @@ import type { IResource } from '@opentelemetry/resources'; import type { ReadableLogRecord } from './export/ReadableLogRecord'; import type { LogRecordLimits } from './types'; -import { Logger } from './Logger'; import { LogAttributes } from '@opentelemetry/api-logs'; +import { LoggerProviderSharedState } from './internal/LoggerProviderSharedState'; export class LogRecord implements ReadableLogRecord { readonly hrTime: api.HrTime; @@ -41,7 +41,7 @@ export class LogRecord implements ReadableLogRecord { private _body?: string; private _isReadonly: boolean = false; - private readonly _logRecordLimits: LogRecordLimits; + private readonly _logRecordLimits: Required; set severityText(severityText: string | undefined) { if (this._isLogRecordReadonly()) { @@ -73,7 +73,11 @@ export class LogRecord implements ReadableLogRecord { return this._body; } - constructor(logger: Logger, logRecord: logsAPI.LogRecord) { + constructor( + _sharedState: LoggerProviderSharedState, + instrumentationScope: InstrumentationScope, + logRecord: logsAPI.LogRecord + ) { const { timestamp, observedTimestamp, @@ -97,9 +101,9 @@ export class LogRecord implements ReadableLogRecord { this.severityNumber = severityNumber; this.severityText = severityText; this.body = body; - this.resource = logger.resource; - this.instrumentationScope = logger.instrumentationScope; - this._logRecordLimits = logger.getLogRecordLimits(); + this.resource = _sharedState.resource; + this.instrumentationScope = instrumentationScope; + this._logRecordLimits = _sharedState.logRecordLimits; this.setAttributes(attributes); } @@ -127,7 +131,7 @@ export class LogRecord implements ReadableLogRecord { } if ( Object.keys(this.attributes).length >= - this._logRecordLimits.attributeCountLimit! && + this._logRecordLimits.attributeCountLimit && !Object.prototype.hasOwnProperty.call(this.attributes, key) ) { return this; @@ -159,15 +163,16 @@ export class LogRecord implements ReadableLogRecord { } /** + * @internal * A LogRecordProcessor may freely modify logRecord for the duration of the OnEmit call. * If logRecord is needed after OnEmit returns (i.e. for asynchronous processing) only reads are permitted. */ - public makeReadonly() { + _makeReadonly() { this._isReadonly = true; } private _truncateToSize(value: AttributeValue): AttributeValue { - const limit = this._logRecordLimits.attributeValueLengthLimit || 0; + const limit = this._logRecordLimits.attributeValueLengthLimit; // Check limit if (limit <= 0) { // Negative values are invalid, so do not truncate diff --git a/experimental/packages/sdk-logs/src/Logger.ts b/experimental/packages/sdk-logs/src/Logger.ts index 5ea4e8f28f..7694955e62 100644 --- a/experimental/packages/sdk-logs/src/Logger.ts +++ b/experimental/packages/sdk-logs/src/Logger.ts @@ -15,28 +15,17 @@ */ import type * as logsAPI from '@opentelemetry/api-logs'; -import type { IResource } from '@opentelemetry/resources'; import type { InstrumentationScope } from '@opentelemetry/core'; import { context } from '@opentelemetry/api'; -import type { LoggerConfig, LogRecordLimits } from './types'; import { LogRecord } from './LogRecord'; -import { LoggerProvider } from './LoggerProvider'; -import { mergeConfig } from './config'; -import { LogRecordProcessor } from './LogRecordProcessor'; +import { LoggerProviderSharedState } from './internal/LoggerProviderSharedState'; export class Logger implements logsAPI.Logger { - public readonly resource: IResource; - private readonly _loggerConfig: Required; - constructor( public readonly instrumentationScope: InstrumentationScope, - config: LoggerConfig, - private _loggerProvider: LoggerProvider - ) { - this._loggerConfig = mergeConfig(config); - this.resource = _loggerProvider.resource; - } + private _sharedState: LoggerProviderSharedState + ) {} public emit(logRecord: logsAPI.LogRecord): void { const currentContext = logRecord.context || context.active(); @@ -45,30 +34,23 @@ export class Logger implements logsAPI.Logger { * the LogRecords it emits MUST automatically include the Trace Context from the active Context, * if Context has not been explicitly set. */ - const logRecordInstance = new LogRecord(this, { - context: currentContext, - ...logRecord, - }); + const logRecordInstance = new LogRecord( + this._sharedState, + this.instrumentationScope, + { + context: currentContext, + ...logRecord, + } + ); /** * the explicitly passed Context, * the current Context, or an empty Context if the Logger was obtained with include_trace_context=false */ - this.getActiveLogRecordProcessor().onEmit( - logRecordInstance, - currentContext - ); + this._sharedState.activeProcessor.onEmit(logRecordInstance, currentContext); /** * A LogRecordProcessor may freely modify logRecord for the duration of the OnEmit call. * If logRecord is needed after OnEmit returns (i.e. for asynchronous processing) only reads are permitted. */ - logRecordInstance.makeReadonly(); - } - - public getLogRecordLimits(): LogRecordLimits { - return this._loggerConfig.logRecordLimits; - } - - public getActiveLogRecordProcessor(): LogRecordProcessor { - return this._loggerProvider.getActiveLogRecordProcessor(); + logRecordInstance._makeReadonly(); } } diff --git a/experimental/packages/sdk-logs/src/LoggerProvider.ts b/experimental/packages/sdk-logs/src/LoggerProvider.ts index 81515dab86..b8fd9851f8 100644 --- a/experimental/packages/sdk-logs/src/LoggerProvider.ts +++ b/experimental/packages/sdk-logs/src/LoggerProvider.ts @@ -16,7 +16,7 @@ import { diag } from '@opentelemetry/api'; import type * as logsAPI from '@opentelemetry/api-logs'; import { NOOP_LOGGER } from '@opentelemetry/api-logs'; -import { IResource, Resource } from '@opentelemetry/resources'; +import { Resource } from '@opentelemetry/resources'; import { BindOnceFuture, merge } from '@opentelemetry/core'; import type { LoggerProviderConfig } from './types'; @@ -24,39 +24,26 @@ import type { LogRecordProcessor } from './LogRecordProcessor'; import { Logger } from './Logger'; import { loadDefaultConfig, reconfigureLimits } from './config'; import { MultiLogRecordProcessor } from './MultiLogRecordProcessor'; -import { NoopLogRecordProcessor } from './export/NoopLogRecordProcessor'; +import { LoggerProviderSharedState } from './internal/LoggerProviderSharedState'; export const DEFAULT_LOGGER_NAME = 'unknown'; export class LoggerProvider implements logsAPI.LoggerProvider { - public readonly resource: IResource; - - private readonly _loggers: Map = new Map(); - private _activeProcessor: MultiLogRecordProcessor; - private readonly _registeredLogRecordProcessors: LogRecordProcessor[] = []; - private readonly _config: LoggerProviderConfig; private _shutdownOnce: BindOnceFuture; + private readonly _sharedState: LoggerProviderSharedState; constructor(config: LoggerProviderConfig = {}) { const { - resource = Resource.empty(), + resource = Resource.default(), logRecordLimits, forceFlushTimeoutMillis, - } = merge({}, loadDefaultConfig(), reconfigureLimits(config)); - this.resource = Resource.default().merge(resource); - this._config = { - logRecordLimits, - resource: this.resource, + } = merge({}, loadDefaultConfig(), config); + this._sharedState = new LoggerProviderSharedState( + resource, forceFlushTimeoutMillis, - }; - - this._shutdownOnce = new BindOnceFuture(this._shutdown, this); - - // add a default processor: NoopLogRecordProcessor - this._activeProcessor = new MultiLogRecordProcessor( - [new NoopLogRecordProcessor()], - forceFlushTimeoutMillis + reconfigureLimits(logRecordLimits) ); + this._shutdownOnce = new BindOnceFuture(this._shutdown, this); } /** @@ -77,19 +64,17 @@ export class LoggerProvider implements logsAPI.LoggerProvider { } const loggerName = name || DEFAULT_LOGGER_NAME; const key = `${loggerName}@${version || ''}:${options?.schemaUrl || ''}`; - if (!this._loggers.has(key)) { - this._loggers.set( + if (!this._sharedState.loggers.has(key)) { + this._sharedState.loggers.set( key, new Logger( { name: loggerName, version, schemaUrl: options?.schemaUrl }, - { - logRecordLimits: this._config.logRecordLimits, - }, - this + this._sharedState ) ); } - return this._loggers.get(key)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return this._sharedState.loggers.get(key)!; } /** @@ -97,10 +82,10 @@ export class LoggerProvider implements logsAPI.LoggerProvider { * @param processor the new LogRecordProcessor to be added. */ public addLogRecordProcessor(processor: LogRecordProcessor) { - if (this._registeredLogRecordProcessors.length === 0) { + if (this._sharedState.registeredLogRecordProcessors.length === 0) { // since we might have enabled by default a batchProcessor, we disable it // before adding the new one - this._activeProcessor + this._sharedState.activeProcessor .shutdown() .catch(err => diag.error( @@ -109,10 +94,10 @@ export class LoggerProvider implements logsAPI.LoggerProvider { ) ); } - this._registeredLogRecordProcessors.push(processor); - this._activeProcessor = new MultiLogRecordProcessor( - this._registeredLogRecordProcessors, - this._config.forceFlushTimeoutMillis! + this._sharedState.registeredLogRecordProcessors.push(processor); + this._sharedState.activeProcessor = new MultiLogRecordProcessor( + this._sharedState.registeredLogRecordProcessors, + this._sharedState.forceFlushTimeoutMillis ); } @@ -127,7 +112,7 @@ export class LoggerProvider implements logsAPI.LoggerProvider { diag.warn('invalid attempt to force flush after LoggerProvider shutdown'); return this._shutdownOnce.promise; } - return this._activeProcessor.forceFlush(); + return this._sharedState.activeProcessor.forceFlush(); } /** @@ -144,15 +129,7 @@ export class LoggerProvider implements logsAPI.LoggerProvider { return this._shutdownOnce.call(); } - public getActiveLogRecordProcessor(): MultiLogRecordProcessor { - return this._activeProcessor; - } - - public getActiveLoggers(): Map { - return this._loggers; - } - private _shutdown(): Promise { - return this._activeProcessor.shutdown(); + return this._sharedState.activeProcessor.shutdown(); } } diff --git a/experimental/packages/sdk-logs/src/MultiLogRecordProcessor.ts b/experimental/packages/sdk-logs/src/MultiLogRecordProcessor.ts index c4e5031261..353caefe0f 100644 --- a/experimental/packages/sdk-logs/src/MultiLogRecordProcessor.ts +++ b/experimental/packages/sdk-logs/src/MultiLogRecordProcessor.ts @@ -15,7 +15,7 @@ */ import { callWithTimeout } from '@opentelemetry/core'; - +import type { Context } from '@opentelemetry/api'; import type { LogRecordProcessor } from './LogRecordProcessor'; import type { LogRecord } from './LogRecord'; @@ -38,8 +38,10 @@ export class MultiLogRecordProcessor implements LogRecordProcessor { ); } - public onEmit(logRecord: LogRecord): void { - this.processors.forEach(processors => processors.onEmit(logRecord)); + public onEmit(logRecord: LogRecord, context?: Context): void { + this.processors.forEach(processors => + processors.onEmit(logRecord, context) + ); } public async shutdown(): Promise { diff --git a/experimental/packages/sdk-logs/src/config.ts b/experimental/packages/sdk-logs/src/config.ts index af908f1650..91b2c3e488 100644 --- a/experimental/packages/sdk-logs/src/config.ts +++ b/experimental/packages/sdk-logs/src/config.ts @@ -20,7 +20,7 @@ import { getEnv, getEnvWithoutDefaults, } from '@opentelemetry/core'; -import { LoggerConfig } from './types'; +import { LogRecordLimits } from './types'; export function loadDefaultConfig() { return { @@ -37,50 +37,29 @@ export function loadDefaultConfig() { /** * When general limits are provided and model specific limits are not, * configures the model specific limits by using the values from the general ones. - * @param userConfig User provided tracer configuration + * @param logRecordLimits User provided limits configuration */ -export function reconfigureLimits(userConfig: LoggerConfig): LoggerConfig { - const logRecordLimits = Object.assign({}, userConfig.logRecordLimits); - +export function reconfigureLimits( + logRecordLimits: LogRecordLimits +): Required { const parsedEnvConfig = getEnvWithoutDefaults(); - /** - * Reassign log record attribute count limit to use first non null value defined by user or use default value - */ - logRecordLimits.attributeCountLimit = - userConfig.logRecordLimits?.attributeCountLimit ?? - parsedEnvConfig.OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT ?? - parsedEnvConfig.OTEL_ATTRIBUTE_COUNT_LIMIT ?? - DEFAULT_ATTRIBUTE_COUNT_LIMIT; - - /** - * Reassign log record attribute value length limit to use first non null value defined by user or use default value - */ - logRecordLimits.attributeValueLengthLimit = - userConfig.logRecordLimits?.attributeValueLengthLimit ?? - parsedEnvConfig.OTEL_LOGRECORD_ATTRIBUTE_VALUE_LENGTH_LIMIT ?? - parsedEnvConfig.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT ?? - DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT; - - return Object.assign({}, userConfig, { logRecordLimits }); -} - -/** - * Function to merge Default configuration (as specified in './config') with - * user provided configurations. - */ -export function mergeConfig(userConfig: LoggerConfig): Required { - const DEFAULT_CONFIG = loadDefaultConfig(); - - const target = Object.assign({}, DEFAULT_CONFIG, userConfig); - - target.logRecordLimits = Object.assign( - {}, - DEFAULT_CONFIG.logRecordLimits, - userConfig.logRecordLimits || {} - ); - - return target; + return { + /** + * Reassign log record attribute count limit to use first non null value defined by user or use default value + */ + attributeCountLimit: + logRecordLimits.attributeCountLimit ?? + parsedEnvConfig.OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT ?? + parsedEnvConfig.OTEL_ATTRIBUTE_COUNT_LIMIT ?? + DEFAULT_ATTRIBUTE_COUNT_LIMIT, + /** + * Reassign log record attribute value length limit to use first non null value defined by user or use default value + */ + attributeValueLengthLimit: + logRecordLimits.attributeValueLengthLimit ?? + parsedEnvConfig.OTEL_LOGRECORD_ATTRIBUTE_VALUE_LENGTH_LIMIT ?? + parsedEnvConfig.OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT ?? + DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT, + }; } - -export const DEFAULT_EVENT_DOMAIN = 'default'; diff --git a/experimental/packages/sdk-logs/src/export/NoopLogRecordProcessor.ts b/experimental/packages/sdk-logs/src/export/NoopLogRecordProcessor.ts index 91f277e8ab..c1f62ed8ca 100644 --- a/experimental/packages/sdk-logs/src/export/NoopLogRecordProcessor.ts +++ b/experimental/packages/sdk-logs/src/export/NoopLogRecordProcessor.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { Context } from '@opentelemetry/api'; import { LogRecordProcessor } from '../LogRecordProcessor'; import { ReadableLogRecord } from './ReadableLogRecord'; @@ -22,7 +23,7 @@ export class NoopLogRecordProcessor implements LogRecordProcessor { return Promise.resolve(); } - onEmit(_logRecord: ReadableLogRecord): void {} + onEmit(_logRecord: ReadableLogRecord, _context: Context): void {} shutdown(): Promise { return Promise.resolve(); diff --git a/experimental/packages/sdk-logs/src/index.ts b/experimental/packages/sdk-logs/src/index.ts index e718ae069e..b7347a2845 100644 --- a/experimental/packages/sdk-logs/src/index.ts +++ b/experimental/packages/sdk-logs/src/index.ts @@ -15,14 +15,12 @@ */ export { - LoggerConfig, LoggerProviderConfig, LogRecordLimits, BufferConfig, BatchLogRecordProcessorBrowserConfig, } from './types'; export { LoggerProvider } from './LoggerProvider'; -export { Logger } from './Logger'; export { LogRecord } from './LogRecord'; export { LogRecordProcessor } from './LogRecordProcessor'; export { ReadableLogRecord } from './export/ReadableLogRecord'; diff --git a/experimental/packages/sdk-logs/src/internal/LoggerProviderSharedState.ts b/experimental/packages/sdk-logs/src/internal/LoggerProviderSharedState.ts new file mode 100644 index 0000000000..16b208f72c --- /dev/null +++ b/experimental/packages/sdk-logs/src/internal/LoggerProviderSharedState.ts @@ -0,0 +1,35 @@ +/* + * 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 { Logger } from '@opentelemetry/api-logs'; +import { IResource } from '@opentelemetry/resources'; +import { LogRecordProcessor } from '../LogRecordProcessor'; +import { LogRecordLimits } from '../types'; +import { NoopLogRecordProcessor } from '../export/NoopLogRecordProcessor'; + +export class LoggerProviderSharedState { + readonly loggers: Map = new Map(); + activeProcessor: LogRecordProcessor; + readonly registeredLogRecordProcessors: LogRecordProcessor[] = []; + + constructor( + readonly resource: IResource, + readonly forceFlushTimeoutMillis: number, + readonly logRecordLimits: Required + ) { + this.activeProcessor = new NoopLogRecordProcessor(); + } +} diff --git a/experimental/packages/sdk-logs/src/types.ts b/experimental/packages/sdk-logs/src/types.ts index 026843dc15..27aefa540f 100644 --- a/experimental/packages/sdk-logs/src/types.ts +++ b/experimental/packages/sdk-logs/src/types.ts @@ -30,11 +30,6 @@ export interface LoggerProviderConfig { logRecordLimits?: LogRecordLimits; } -export interface LoggerConfig { - /** Log Record Limits*/ - logRecordLimits?: LogRecordLimits; -} - export interface LogRecordLimits { /** attributeValueLengthLimit is maximum allowed attribute value size */ attributeValueLengthLimit?: number; diff --git a/experimental/packages/sdk-logs/test/common/LogRecord.test.ts b/experimental/packages/sdk-logs/test/common/LogRecord.test.ts index cc7ce8bafd..e32ab39838 100644 --- a/experimental/packages/sdk-logs/test/common/LogRecord.test.ts +++ b/experimental/packages/sdk-logs/test/common/LogRecord.test.ts @@ -33,30 +33,32 @@ import { LogRecordLimits, LogRecordProcessor, LogRecord, - Logger, LoggerProvider, } from './../../src'; import { invalidAttributes, validAttributes } from './utils'; +import { LoggerProviderSharedState } from '../../src/internal/LoggerProviderSharedState'; +import { reconfigureLimits } from '../../src/config'; const performanceTimeOrigin: HrTime = [1, 1]; -const setup = (limits?: LogRecordLimits, data?: logsAPI.LogRecord) => { +const setup = (logRecordLimits?: LogRecordLimits, data?: logsAPI.LogRecord) => { const instrumentationScope = { name: 'test name', version: 'test version', schemaUrl: 'test schema url', }; const resource = Resource.default(); - const loggerProvider = new LoggerProvider({ resource }); - const logger = new Logger( + const sharedState = new LoggerProviderSharedState( + resource, + Infinity, + reconfigureLimits(logRecordLimits ?? {}) + ); + const logRecord = new LogRecord( + sharedState, instrumentationScope, - { - logRecordLimits: limits, - }, - loggerProvider + data ?? {} ); - const logRecord = new LogRecord(logger, data || {}); - return { logger, logRecord, instrumentationScope, resource }; + return { logRecord, instrumentationScope, resource }; }; describe('LogRecord', () => { @@ -320,7 +322,7 @@ describe('LogRecord', () => { it('should not rewrite directly through the property method', () => { const warnStub = sinon.spy(diag, 'warn'); const { logRecord } = setup(undefined, logRecordData); - logRecord.makeReadonly(); + logRecord._makeReadonly(); logRecord.body = newBody; logRecord.severityNumber = newSeverityNumber; @@ -346,7 +348,7 @@ describe('LogRecord', () => { it('should not rewrite using the set method', () => { const warnStub = sinon.spy(diag, 'warn'); const { logRecord } = setup(undefined, logRecordData); - logRecord.makeReadonly(); + logRecord._makeReadonly(); logRecord.setBody(newBody); logRecord.setSeverityNumber(newSeverityNumber); diff --git a/experimental/packages/sdk-logs/test/common/Logger.test.ts b/experimental/packages/sdk-logs/test/common/Logger.test.ts index a5f690a4b7..f78bda2d5e 100644 --- a/experimental/packages/sdk-logs/test/common/Logger.test.ts +++ b/experimental/packages/sdk-logs/test/common/Logger.test.ts @@ -17,21 +17,19 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; -import { LogRecord, Logger, LoggerConfig, LoggerProvider } from '../../src'; +import { LogRecord, LoggerProvider, NoopLogRecordProcessor } from '../../src'; import { ROOT_CONTEXT, TraceFlags, context, trace } from '@opentelemetry/api'; import { LogRecord as ApiLogRecord } from '@opentelemetry/api-logs'; +import { Logger } from '../../src/Logger'; -const setup = (loggerConfig: LoggerConfig = {}) => { - const logger = new Logger( - { - name: 'test name', - version: 'test version', - schemaUrl: 'test schema url', - }, - loggerConfig, - new LoggerProvider() - ); - return { logger }; +const setup = () => { + const loggerProvider = new LoggerProvider(); + const logProcessor = new NoopLogRecordProcessor(); + loggerProvider.addLogRecordProcessor(logProcessor); + const logger = loggerProvider.getLogger('test name', 'test version', { + schemaUrl: 'test schema url', + }) as Logger; + return { logger, logProcessor }; }; describe('Logger', () => { @@ -44,8 +42,8 @@ describe('Logger', () => { describe('emit', () => { it('should emit a logRecord instance', () => { - const { logger } = setup(); - const callSpy = sinon.spy(logger.getActiveLogRecordProcessor(), 'onEmit'); + const { logger, logProcessor } = setup(); + const callSpy = sinon.spy(logProcessor, 'onEmit'); logger.emit({ body: 'test log body', }); @@ -54,7 +52,7 @@ describe('Logger', () => { it('should make log record instance readonly after emit it', () => { const { logger } = setup(); - const makeOnlySpy = sinon.spy(LogRecord.prototype, 'makeReadonly'); + const makeOnlySpy = sinon.spy(LogRecord.prototype, '_makeReadonly'); logger.emit({ body: 'test log body', }); @@ -62,8 +60,8 @@ describe('Logger', () => { }); it('should emit with current Context', () => { - const { logger } = setup({}); - const callSpy = sinon.spy(logger.getActiveLogRecordProcessor(), 'onEmit'); + const { logger, logProcessor } = setup(); + const callSpy = sinon.spy(logProcessor, 'onEmit'); logger.emit({ body: 'test log body', }); @@ -71,7 +69,7 @@ describe('Logger', () => { }); it('should emit with Context specified in LogRecord', () => { - const { logger } = setup({}); + const { logger, logProcessor } = setup(); const spanContext = { traceId: 'd4cda95b652f4a1592b449d5929fda1b', spanId: '6e0c63257de34c92', @@ -82,7 +80,7 @@ describe('Logger', () => { context: activeContext, }; - const callSpy = sinon.spy(logger.getActiveLogRecordProcessor(), 'onEmit'); + const callSpy = sinon.spy(logProcessor, 'onEmit'); logger.emit(logRecordData); assert.ok(callSpy.calledWith(sinon.match.any, activeContext)); }); diff --git a/experimental/packages/sdk-logs/test/common/LoggerProvider.test.ts b/experimental/packages/sdk-logs/test/common/LoggerProvider.test.ts index d6032515a7..e4d80265c6 100644 --- a/experimental/packages/sdk-logs/test/common/LoggerProvider.test.ts +++ b/experimental/packages/sdk-logs/test/common/LoggerProvider.test.ts @@ -19,9 +19,11 @@ import { Resource } from '@opentelemetry/resources'; import * as assert from 'assert'; import * as sinon from 'sinon'; -import { Logger, LoggerProvider, NoopLogRecordProcessor } from '../../src'; +import { LoggerProvider, NoopLogRecordProcessor } from '../../src'; import { loadDefaultConfig } from '../../src/config'; import { DEFAULT_LOGGER_NAME } from './../../src/LoggerProvider'; +import { MultiLogRecordProcessor } from '../../src/MultiLogRecordProcessor'; +import { Logger } from '../../src/Logger'; describe('LoggerProvider', () => { let envSource: Record; @@ -48,45 +50,35 @@ describe('LoggerProvider', () => { assert.ok(provider instanceof LoggerProvider); }); - it('should use noop log record processor by default and no diag error', () => { - const errorStub = sinon.spy(diag, 'error'); + it('should use noop log record processor by default', () => { const provider = new LoggerProvider(); - const processors = provider.getActiveLogRecordProcessor().processors; - assert.ok(processors.length === 1); - assert.ok(processors[0] instanceof NoopLogRecordProcessor); - sinon.assert.notCalled(errorStub); + const sharedState = provider['_sharedState']; + const processor = sharedState.activeProcessor; + assert.ok(processor instanceof NoopLogRecordProcessor); }); it('should have default resource if not pass', () => { const provider = new LoggerProvider(); - const { resource } = provider; + const { resource } = provider['_sharedState']; assert.deepStrictEqual(resource, Resource.default()); }); it('should have default forceFlushTimeoutMillis if not pass', () => { const provider = new LoggerProvider(); - const activeProcessor = provider.getActiveLogRecordProcessor(); + const sharedState = provider['_sharedState']; assert.ok( - activeProcessor.forceFlushTimeoutMillis === + sharedState.forceFlushTimeoutMillis === loadDefaultConfig().forceFlushTimeoutMillis ); }); }); - describe('when user sets unavailable exporter', () => { - it('should use noop log record processor by default', () => { - const provider = new LoggerProvider(); - const processors = provider.getActiveLogRecordProcessor().processors; - assert.ok(processors.length === 1); - assert.ok(processors[0] instanceof NoopLogRecordProcessor); - }); - }); - describe('logRecordLimits', () => { describe('when not defined default values', () => { it('should have logger with default values', () => { - const logger = new LoggerProvider({}).getLogger('default') as Logger; - assert.deepStrictEqual(logger.getLogRecordLimits(), { + const loggerProvider = new LoggerProvider(); + const sharedState = loggerProvider['_sharedState']; + assert.deepStrictEqual(sharedState.logRecordLimits, { attributeValueLengthLimit: Infinity, attributeCountLimit: 128, }); @@ -95,34 +87,37 @@ describe('LoggerProvider', () => { describe('when "attributeCountLimit" is defined', () => { it('should have logger with defined value', () => { - const logger = new LoggerProvider({ + const loggerProvider = new LoggerProvider({ logRecordLimits: { attributeCountLimit: 100, }, - }).getLogger('default') as Logger; - const logRecordLimits = logger.getLogRecordLimits(); + }); + const logRecordLimits = + loggerProvider['_sharedState'].logRecordLimits; assert.strictEqual(logRecordLimits.attributeCountLimit, 100); }); }); describe('when "attributeValueLengthLimit" is defined', () => { it('should have logger with defined value', () => { - const logger = new LoggerProvider({ + const loggerProvider = new LoggerProvider({ logRecordLimits: { attributeValueLengthLimit: 10, }, - }).getLogger('default') as Logger; - const logRecordLimits = logger.getLogRecordLimits(); + }); + const logRecordLimits = + loggerProvider['_sharedState'].logRecordLimits; assert.strictEqual(logRecordLimits.attributeValueLengthLimit, 10); }); it('should have logger with negative "attributeValueLengthLimit" value', () => { - const logger = new LoggerProvider({ + const loggerProvider = new LoggerProvider({ logRecordLimits: { attributeValueLengthLimit: -10, }, - }).getLogger('default') as Logger; - const logRecordLimits = logger.getLogRecordLimits(); + }); + const logRecordLimits = + loggerProvider['_sharedState'].logRecordLimits; assert.strictEqual(logRecordLimits.attributeValueLengthLimit, -10); }); }); @@ -130,8 +125,9 @@ describe('LoggerProvider', () => { describe('when attribute value length limit is defined via env', () => { it('should have attribute value length limit as default of Infinity', () => { envSource.OTEL_LOGRECORD_ATTRIBUTE_VALUE_LENGTH_LIMIT = 'Infinity'; - const logger = new LoggerProvider().getLogger('default') as Logger; - const logRecordLimits = logger.getLogRecordLimits(); + const loggerProvider = new LoggerProvider(); + const logRecordLimits = + loggerProvider['_sharedState'].logRecordLimits; assert.strictEqual( logRecordLimits.attributeValueLengthLimit, Infinity @@ -142,8 +138,9 @@ describe('LoggerProvider', () => { describe('when attribute value length limit is not defined via env', () => { it('should use default value of Infinity', () => { - const logger = new LoggerProvider().getLogger('default') as Logger; - const logRecordLimits = logger.getLogRecordLimits(); + const loggerProvider = new LoggerProvider(); + const logRecordLimits = + loggerProvider['_sharedState'].logRecordLimits; assert.strictEqual( logRecordLimits.attributeValueLengthLimit, Infinity @@ -154,15 +151,17 @@ describe('LoggerProvider', () => { describe('when attribute count limit is defined via env', () => { it('should have attribute count limits as defined in env', () => { envSource.OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT = '35'; - const logger = new LoggerProvider().getLogger('default') as Logger; - const logRecordLimits = logger.getLogRecordLimits(); + const loggerProvider = new LoggerProvider(); + const logRecordLimits = + loggerProvider['_sharedState'].logRecordLimits; assert.strictEqual(logRecordLimits.attributeCountLimit, 35); delete envSource.OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT; }); it('should have attribute count limit as default of 128', () => { envSource.OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT = '128'; - const logger = new LoggerProvider().getLogger('default') as Logger; - const logRecordLimits = logger.getLogRecordLimits(); + const loggerProvider = new LoggerProvider(); + const logRecordLimits = + loggerProvider['_sharedState'].logRecordLimits; assert.strictEqual(logRecordLimits.attributeCountLimit, 128); delete envSource.OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT; }); @@ -170,8 +169,9 @@ describe('LoggerProvider', () => { describe('when attribute count limit is not defined via env', () => { it('should use default value of 128', () => { - const logger = new LoggerProvider().getLogger('default') as Logger; - const logRecordLimits = logger.getLogRecordLimits(); + const loggerProvider = new LoggerProvider(); + const logRecordLimits = + loggerProvider['_sharedState'].logRecordLimits; assert.strictEqual(logRecordLimits.attributeCountLimit, 128); }); }); @@ -186,55 +186,61 @@ describe('LoggerProvider', () => { it('should create a logger instance with default name if the name is invalid ', () => { const provider = new LoggerProvider(); const logger = provider.getLogger('') as Logger; - assert.ok(logger.instrumentationScope.name === DEFAULT_LOGGER_NAME); + assert.strictEqual(logger.instrumentationScope.name, DEFAULT_LOGGER_NAME); }); it("should create a logger instance if the name doesn't exist", () => { const provider = new LoggerProvider(); - assert.ok(provider.getActiveLoggers().size === 0); + const sharedState = provider['_sharedState']; + assert.strictEqual(sharedState.loggers.size, 0); provider.getLogger(testName); - assert.ok(provider.getActiveLoggers().size === 1); + assert.strictEqual(sharedState.loggers.size, 1); }); it('should create A new object if the name & version & schemaUrl are not unique', () => { const provider = new LoggerProvider(); - assert.ok(provider.getActiveLoggers().size === 0); + const sharedState = provider['_sharedState']; + assert.strictEqual(sharedState.loggers.size, 0); provider.getLogger(testName); - assert.ok(provider.getActiveLoggers().size === 1); + assert.strictEqual(sharedState.loggers.size, 1); provider.getLogger(testName, testVersion); - assert.ok(provider.getActiveLoggers().size === 2); + assert.strictEqual(sharedState.loggers.size, 2); provider.getLogger(testName, testVersion, { schemaUrl: testSchemaURL }); - assert.ok(provider.getActiveLoggers().size === 3); + assert.strictEqual(sharedState.loggers.size, 3); }); it('should not create A new object if the name & version & schemaUrl are unique', () => { const provider = new LoggerProvider(); + const sharedState = provider['_sharedState']; - assert.ok(provider.getActiveLoggers().size === 0); + assert.strictEqual(sharedState.loggers.size, 0); provider.getLogger(testName); - assert.ok(provider.getActiveLoggers().size === 1); + assert.strictEqual(sharedState.loggers.size, 1); const logger1 = provider.getLogger(testName, testVersion, { schemaUrl: testSchemaURL, }); - assert.ok(provider.getActiveLoggers().size === 2); + assert.strictEqual(sharedState.loggers.size, 2); const logger2 = provider.getLogger(testName, testVersion, { schemaUrl: testSchemaURL, }); - assert.ok(provider.getActiveLoggers().size === 2); + assert.strictEqual(sharedState.loggers.size, 2); assert.ok(logger2 instanceof Logger); - assert.ok(logger1 === logger2); + assert.strictEqual(logger1, logger2); }); }); describe('addLogRecordProcessor', () => { it('should add logRecord processor', () => { - const logRecordProcessor = new NoopLogRecordProcessor(); const provider = new LoggerProvider(); + const sharedState = provider['_sharedState']; + const logRecordProcessor = new NoopLogRecordProcessor(); provider.addLogRecordProcessor(logRecordProcessor); + assert.ok(sharedState.activeProcessor instanceof MultiLogRecordProcessor); + assert.strictEqual(sharedState.activeProcessor.processors.length, 1); assert.strictEqual( - provider.getActiveLogRecordProcessor().processors.length, - 1 + sharedState.activeProcessor.processors[0], + logRecordProcessor ); }); }); @@ -301,10 +307,9 @@ describe('LoggerProvider', () => { describe('.shutdown()', () => { it('should trigger shutdown when manually invoked', () => { const provider = new LoggerProvider(); - const shutdownStub = sinon.stub( - provider.getActiveLogRecordProcessor(), - 'shutdown' - ); + const processor = new NoopLogRecordProcessor(); + provider.addLogRecordProcessor(processor); + const shutdownStub = sinon.stub(processor, 'shutdown'); provider.shutdown(); sinon.assert.calledOnce(shutdownStub); }); @@ -321,10 +326,7 @@ describe('LoggerProvider', () => { const provider = new LoggerProvider(); const logRecordProcessor = new NoopLogRecordProcessor(); provider.addLogRecordProcessor(logRecordProcessor); - const forceFlushStub = sinon.stub( - provider.getActiveLogRecordProcessor(), - 'forceFlush' - ); + const forceFlushStub = sinon.stub(logRecordProcessor, 'forceFlush'); const warnStub = sinon.spy(diag, 'warn'); provider.shutdown(); provider.forceFlush(); @@ -336,10 +338,7 @@ describe('LoggerProvider', () => { const provider = new LoggerProvider(); const logRecordProcessor = new NoopLogRecordProcessor(); provider.addLogRecordProcessor(logRecordProcessor); - const shutdownStub = sinon.stub( - provider.getActiveLogRecordProcessor(), - 'shutdown' - ); + const shutdownStub = sinon.stub(logRecordProcessor, 'shutdown'); const warnStub = sinon.spy(diag, 'warn'); provider.shutdown(); provider.shutdown(); diff --git a/experimental/packages/sdk-logs/test/common/export/BatchLogRecordProcessor.test.ts b/experimental/packages/sdk-logs/test/common/export/BatchLogRecordProcessor.test.ts index 2d0fab7a2c..70859a314c 100644 --- a/experimental/packages/sdk-logs/test/common/export/BatchLogRecordProcessor.test.ts +++ b/experimental/packages/sdk-logs/test/common/export/BatchLogRecordProcessor.test.ts @@ -28,10 +28,11 @@ import { LogRecordLimits, LogRecord, InMemoryLogRecordExporter, - LoggerProvider, - Logger, } from '../../../src'; import { BatchLogRecordProcessorBase } from '../../../src/export/BatchLogRecordProcessorBase'; +import { reconfigureLimits } from '../../../src/config'; +import { LoggerProviderSharedState } from '../../../src/internal/LoggerProviderSharedState'; +import { Resource } from '@opentelemetry/resources'; class BatchLogRecordProcessor extends BatchLogRecordProcessorBase { onInit() {} @@ -39,18 +40,22 @@ class BatchLogRecordProcessor extends BatchLogRecordProcessorBase } const createLogRecord = (limits?: LogRecordLimits): LogRecord => { - const logger = new Logger( + const sharedState = new LoggerProviderSharedState( + Resource.default(), + Infinity, + reconfigureLimits(limits ?? {}) + ); + const logRecord = new LogRecord( + sharedState, { name: 'test name', version: 'test version', schemaUrl: 'test schema url', }, { - logRecordLimits: limits, - }, - new LoggerProvider() + body: 'body', + } ); - const logRecord = new LogRecord(logger, { body: 'body' }); return logRecord; }; diff --git a/experimental/packages/sdk-logs/test/common/export/SimpleLogRecordProcessor.test.ts b/experimental/packages/sdk-logs/test/common/export/SimpleLogRecordProcessor.test.ts index 202554dde9..27eacc9195 100644 --- a/experimental/packages/sdk-logs/test/common/export/SimpleLogRecordProcessor.test.ts +++ b/experimental/packages/sdk-logs/test/common/export/SimpleLogRecordProcessor.test.ts @@ -27,13 +27,30 @@ import { LogRecordExporter, SimpleLogRecordProcessor, LogRecord, - LoggerProvider, - Logger, } from './../../../src'; +import { LoggerProviderSharedState } from '../../../src/internal/LoggerProviderSharedState'; +import { Resource } from '@opentelemetry/resources'; +import { reconfigureLimits } from '../../../src/config'; const setup = (exporter: LogRecordExporter) => { + const sharedState = new LoggerProviderSharedState( + Resource.default(), + Infinity, + reconfigureLimits({}) + ); + const logRecord = new LogRecord( + sharedState, + { + name: 'test name', + version: 'test version', + schemaUrl: 'test schema url', + }, + { + body: 'body', + } + ); const processor = new SimpleLogRecordProcessor(exporter); - return { exporter, processor }; + return { exporter, processor, logRecord }; }; describe('SimpleLogRecordProcessor', () => { @@ -49,21 +66,9 @@ describe('SimpleLogRecordProcessor', () => { describe('onEmit', () => { it('should handle onEmit', async () => { const exporter = new InMemoryLogRecordExporter(); - const { processor } = setup(exporter); + const { processor, logRecord } = setup(exporter); assert.strictEqual(exporter.getFinishedLogRecords().length, 0); - const logger = new Logger( - { - name: 'test name', - version: 'test version', - schemaUrl: 'test schema url', - }, - {}, - new LoggerProvider() - ); - const logRecord = new LogRecord(logger, { - body: 'body', - }); processor.onEmit(logRecord); assert.strictEqual(exporter.getFinishedLogRecords().length, 1); @@ -82,20 +87,8 @@ describe('SimpleLogRecordProcessor', () => { ), shutdown: () => Promise.resolve(), }; - const { processor } = setup(exporter); + const { processor, logRecord } = setup(exporter); - const logger = new Logger( - { - name: 'test name', - version: 'test version', - schemaUrl: 'test schema url', - }, - {}, - new LoggerProvider() - ); - const logRecord = new LogRecord(logger, { - body: 'body', - }); const errorHandlerSpy = sinon.spy(); setGlobalErrorHandler(errorHandlerSpy); processor.onEmit(logRecord); diff --git a/experimental/packages/shim-opencensus/README.md b/experimental/packages/shim-opencensus/README.md index e79cfc0f43..66e4d6e3d7 100644 --- a/experimental/packages/shim-opencensus/README.md +++ b/experimental/packages/shim-opencensus/README.md @@ -13,11 +13,11 @@ More details are available in the [OpenCensus Compatibility Specification](https npm install --save @opentelemetry/shim-opencensus ``` -## Usage +## Tracing usage ### Installing the shim's require-in-the-middle hook -This is the recommended way to use the shim. +This is the recommended way to use the shim for tracing. This package provides a `require-in-the-middle` hook which replaces OpenCensus's `CoreTracer` class with a shim implementation that writes to the OpenTelemetry API. This will cause all @@ -72,6 +72,25 @@ tracer.startRootSpan({name: 'main'}, rootSpan => { }); ``` +## Metrics usage + +OpenCensus metrics can be collected and sent to an OpenTelemetry exporter by providing the +`OpenCensusMetricProducer` to your `MetricReader`. For example, to export OpenCensus metrics +through the OpenTelemetry Prometheus exporter: + +```js +meterProvider.addMetricReader( + new PrometheusExporter({ + metricProducers: [ + new OpenCensusMetricProducer({ + openCensusMetricProducerManager: + oc.Metrics.getMetricProducerManager(), + }), + ], + }) +); +``` + ## Example See [examples/opencensus-shim](https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/examples/opencensus-shim) for a short example. 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/package.json b/package.json index c1b6fc0280..6d5460207d 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "test:browser": "lerna run test:browser", "test:webworker": "lerna run test:webworker", "test:backcompat": "lerna run test:backcompat", + "test:bench": "lerna run test:bench", "bootstrap": "lerna bootstrap --hoist --nohoist='zone.js'", "changelog": "lerna-changelog", "codecov": "lerna run codecov", @@ -65,8 +66,9 @@ "devDependencies": { "@typescript-eslint/eslint-plugin": "5.59.11", "@typescript-eslint/parser": "5.59.11", + "benchmark": "2.1.4", "eslint": "8.44.0", - "eslint-config-prettier": "8.5.0", + "eslint-config-prettier": "9.0.0", "eslint-plugin-header": "3.1.1", "eslint-plugin-node": "11.1.0", "eslint-plugin-prettier": "5.0.0", diff --git a/packages/opentelemetry-context-zone-peer-dep/package.json b/packages/opentelemetry-context-zone-peer-dep/package.json index 477cf0d75d..0a673ab480 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.20", "@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..76adff9578 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.20", "@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..681d997e34 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.20", "@opentelemetry/api": "^1.0.0", "@types/mocha": "10.0.1", "@types/node": "18.6.5", diff --git a/packages/opentelemetry-sdk-trace-base/package.json b/packages/opentelemetry-sdk-trace-base/package.json index 77e7a64c28..600caa4294 100644 --- a/packages/opentelemetry-sdk-trace-base/package.json +++ b/packages/opentelemetry-sdk-trace-base/package.json @@ -20,6 +20,7 @@ "test": "nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'", "test:browser": "karma start --single-run", "test:webworker": "karma start karma.worker.js --single-run", + "test:bench": "node test/performance/benchmark/index.js", "tdd": "npm run tdd:node", "tdd:node": "npm run test -- --watch-extensions ts --watch", "tdd:browser": "karma start", diff --git a/packages/opentelemetry-sdk-trace-base/test/performance/benchmark/index.js b/packages/opentelemetry-sdk-trace-base/test/performance/benchmark/index.js new file mode 100644 index 0000000000..83558ea01f --- /dev/null +++ b/packages/opentelemetry-sdk-trace-base/test/performance/benchmark/index.js @@ -0,0 +1,17 @@ +/* + * 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. + */ + +require('./span'); diff --git a/packages/opentelemetry-sdk-trace-base/test/performance/benchmark/span.js b/packages/opentelemetry-sdk-trace-base/test/performance/benchmark/span.js new file mode 100644 index 0000000000..ac978a6140 --- /dev/null +++ b/packages/opentelemetry-sdk-trace-base/test/performance/benchmark/span.js @@ -0,0 +1,43 @@ +/* + * 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. + */ + +const Benchmark = require('benchmark'); +const { BasicTracerProvider } = require('../../../build/src'); + +const tracerProvider = new BasicTracerProvider(); +const tracer = tracerProvider.getTracer('test') + +const suite = new Benchmark.Suite(); + +suite.on('cycle', event => { + console.log(String(event.target)); +}); + +suite.add('create spans (10 attributes)', function() { + const span = tracer.startSpan('span'); + span.setAttribute('aaaaaaaaaaaaaaaaaaaa', 'aaaaaaaaaaaaaaaaaaaa'); + span.setAttribute('bbbbbbbbbbbbbbbbbbbb', 'aaaaaaaaaaaaaaaaaaaa'); + span.setAttribute('cccccccccccccccccccc', 'aaaaaaaaaaaaaaaaaaaa'); + span.setAttribute('dddddddddddddddddddd', 'aaaaaaaaaaaaaaaaaaaa'); + span.setAttribute('eeeeeeeeeeeeeeeeeeee', 'aaaaaaaaaaaaaaaaaaaa'); + span.setAttribute('ffffffffffffffffffff', 'aaaaaaaaaaaaaaaaaaaa'); + span.setAttribute('gggggggggggggggggggg', 'aaaaaaaaaaaaaaaaaaaa'); + span.setAttribute('hhhhhhhhhhhhhhhhhhhh', 'aaaaaaaaaaaaaaaaaaaa'); + span.setAttribute('iiiiiiiiiiiiiiiiiiii', 'aaaaaaaaaaaaaaaaaaaa'); + span.setAttribute('jjjjjjjjjjjjjjjjjjjj', 'aaaaaaaaaaaaaaaaaaaa'); +}); + +suite.run(); 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..ebbf2a4025 100644 --- a/packages/opentelemetry-sdk-trace-web/package.json +++ b/packages/opentelemetry-sdk-trace-web/package.json @@ -55,12 +55,12 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.20", "@opentelemetry/api": ">=1.0.0 <1.7.0", "@opentelemetry/context-zone": "1.17.0", "@opentelemetry/propagator-b3": "1.17.0", "@opentelemetry/resources": "1.17.0", - "@types/jquery": "3.5.18", + "@types/jquery": "3.5.19", "@types/mocha": "10.0.1", "@types/node": "18.6.5", "@types/sinon": "10.0.16", diff --git a/packages/sdk-metrics/package.json b/packages/sdk-metrics/package.json index dd7963f1c8..3d4f5b299e 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.20", "@opentelemetry/api": ">=1.3.0 <1.7.0", "@types/lodash.merge": "4.6.7", "@types/mocha": "10.0.1", diff --git a/packages/sdk-metrics/src/InstrumentDescriptor.ts b/packages/sdk-metrics/src/InstrumentDescriptor.ts index ac742fc0ee..f0053f9c39 100644 --- a/packages/sdk-metrics/src/InstrumentDescriptor.ts +++ b/packages/sdk-metrics/src/InstrumentDescriptor.ts @@ -88,7 +88,7 @@ export function isDescriptorCompatibleWith( // ASCII string with a length no greater than 255 characters. // NB: the first character counted separately from the rest. -const NAME_REGEXP = /^[a-z][a-z0-9_.-]{0,254}$/i; +const NAME_REGEXP = /^[a-z][a-z0-9_.\-/]{0,254}$/i; export function isValidName(name: string): boolean { return name.match(NAME_REGEXP) != null; } diff --git a/packages/sdk-metrics/test/util.ts b/packages/sdk-metrics/test/util.ts index ef081cf67a..338a2f3978 100644 --- a/packages/sdk-metrics/test/util.ts +++ b/packages/sdk-metrics/test/util.ts @@ -67,7 +67,15 @@ export const defaultInstrumentationScope: InstrumentationScope = { }; export const invalidNames = ['', 'a'.repeat(256), '1a', '-a', '.a', '_a']; -export const validNames = ['a', 'a'.repeat(255), 'a1', 'a-1', 'a.1', 'a_1']; +export const validNames = [ + 'a', + 'a'.repeat(255), + 'a1', + 'a-1', + 'a.1', + 'a_1', + 'a/1', +]; export const commonValues: number[] = [1, -1, 1.0, Infinity, -Infinity, NaN]; export const commonAttributes: MetricAttributes[] = [ diff --git a/selenium-tests/package.json b/selenium-tests/package.json index a05172df31..9592f050de 100644 --- a/selenium-tests/package.json +++ b/selenium-tests/package.json @@ -31,11 +31,11 @@ "access": "restricted" }, "devDependencies": { - "@babel/core": "7.22.17", + "@babel/core": "7.22.20", "@babel/plugin-proposal-class-properties": "7.18.6", "@babel/plugin-proposal-decorators": "7.22.15", "@babel/plugin-transform-runtime": "7.22.15", - "@babel/preset-env": "7.22.15", + "@babel/preset-env": "7.22.20", "@opentelemetry/api": "^1.0.0", "babel-loader": "8.3.0", "babel-polyfill": "6.26.0", diff --git a/tsconfig.esm.json b/tsconfig.esm.json index 2fec210b9a..29491b46ef 100644 --- a/tsconfig.esm.json +++ b/tsconfig.esm.json @@ -29,6 +29,9 @@ { "path": "experimental/packages/opentelemetry-exporter-metrics-otlp-http/tsconfig.esm.json" }, + { + "path": "experimental/packages/opentelemetry-exporter-metrics-otlp-proto/tsconfig.esm.json" + }, { "path": "experimental/packages/opentelemetry-instrumentation/tsconfig.esm.json" }, diff --git a/tsconfig.esnext.json b/tsconfig.esnext.json index c5970c9c5c..031a2e5609 100644 --- a/tsconfig.esnext.json +++ b/tsconfig.esnext.json @@ -29,6 +29,9 @@ { "path": "experimental/packages/opentelemetry-exporter-metrics-otlp-http/tsconfig.esnext.json" }, + { + "path": "experimental/packages/opentelemetry-exporter-metrics-otlp-proto/tsconfig.esnext.json" + }, { "path": "experimental/packages/opentelemetry-instrumentation/tsconfig.esnext.json" },