From 49787433b66a17a1788a20f3a7edda3aa2580890 Mon Sep 17 00:00:00 2001 From: Marcin Jahn Date: Mon, 30 Jan 2023 07:51:52 +0100 Subject: [PATCH 01/37] fix(http): remove outgoing headers normalization (#3557) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daniel Dyla Co-authored-by: Gerhard Stöbich --- experimental/CHANGELOG.md | 1 + .../opentelemetry-instrumentation-http/src/utils.ts | 8 -------- .../test/functionals/http-enable.test.ts | 5 ----- .../test/functionals/https-enable.test.ts | 5 ----- 4 files changed, 1 insertion(+), 18 deletions(-) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 57eccab24b..2f3f725b6e 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -14,6 +14,7 @@ All notable changes to experimental packages in this project will be documented * Fixes an error where the generated JS files were not included in the esnext package due to a failure of the tsconfig generation * fix(sdk-node): register instrumentations early [#3502](https://github.com/open-telemetry/opentelemetry-js/pull/3502) @flarna * fix: include tracestate in export [#3569](https://github.com/open-telemetry/opentelemetry-js/pull/3569) @flarna +* fix(http) Remove outgoing headers normalization [#3557](https://github.com/open-telemetry/opentelemetry-js/pull/3557) @marcinjahn ### :books: (Refine Doc) diff --git a/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts b/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts index 4f91ad4cfd..c9a178653e 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts @@ -276,14 +276,6 @@ export const getRequestInfo = ( origin = `${optionsParsed.protocol || 'http:'}//${hostname}`; } - const headers = optionsParsed.headers ?? {}; - optionsParsed.headers = Object.keys(headers).reduce( - (normalizedHeader, key) => { - normalizedHeader[key.toLowerCase()] = headers[key]; - return normalizedHeader; - }, - {} as OutgoingHttpHeaders - ); // some packages return method in lowercase.. // ensure upperCase for consistency const method = optionsParsed.method diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts index fb10580739..9ebfe41c80 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts @@ -508,11 +508,6 @@ describe('HttpInstrumentation', () => { 'user-agent': testValue, }, }), - httpRequest.get(`${protocol}://${hostname}:${serverPort}`, { - headers: { - 'uSeR-aGeNt': testValue, - }, - }), ]); const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts index 8ca18c5e98..2f3af2aaeb 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts @@ -460,11 +460,6 @@ describe('HttpsInstrumentation', () => { 'user-agent': testValue, }, }), - httpsRequest.get(`${protocol}://${hostname}:${serverPort}`, { - headers: { - 'uSeR-aGeNt': testValue, - }, - }), ]); const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); From 279458e7ddf16f7ddca5fe60c78672e05fafce66 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Mon, 30 Jan 2023 08:43:13 -0500 Subject: [PATCH 02/37] Release 1.9.1 / 0.35.1 (#3573) * Release 1.9.1 / 0.35.1 * clean up unreleased changelog --- CHANGELOG.md | 11 +++++-- examples/https/package.json | 18 +++++------ examples/opentelemetry-web/package.json | 30 +++++++++---------- examples/otlp-exporter-node/package.json | 24 +++++++-------- experimental/CHANGELOG.md | 12 +++++--- .../node14/package.json | 6 ++-- .../node16/package.json | 6 ++-- experimental/examples/prometheus/package.json | 6 ++-- experimental/packages/api-logs/package.json | 2 +- .../exporter-trace-otlp-grpc/package.json | 14 ++++----- .../exporter-trace-otlp-http/package.json | 12 ++++---- .../exporter-trace-otlp-proto/package.json | 14 ++++----- .../package.json | 6 ++-- .../package.json | 14 ++++----- .../package.json | 12 ++++---- .../package.json | 16 +++++----- .../package.json | 10 +++---- .../package.json | 16 +++++----- .../package.json | 14 ++++----- .../package.json | 16 +++++----- .../package.json | 16 +++++----- .../package.json | 4 +-- .../opentelemetry-sdk-node/package.json | 28 ++++++++--------- .../packages/otlp-exporter-base/package.json | 4 +-- .../otlp-grpc-exporter-base/package.json | 12 ++++---- .../otlp-proto-exporter-base/package.json | 6 ++-- .../packages/otlp-transformer/package.json | 10 +++---- .../package.json | 8 ++--- .../package.json | 2 +- .../package.json | 2 +- .../opentelemetry-context-zone/package.json | 4 +-- packages/opentelemetry-core/package.json | 4 +-- .../package.json | 10 +++---- .../package.json | 10 +++---- .../opentelemetry-propagator-b3/package.json | 4 +-- .../package.json | 4 +-- packages/opentelemetry-resources/package.json | 6 ++-- .../opentelemetry-sdk-trace-base/package.json | 8 ++--- .../opentelemetry-sdk-trace-node/package.json | 16 +++++----- .../opentelemetry-sdk-trace-web/package.json | 14 ++++----- .../package.json | 2 +- .../package.json | 12 ++++---- packages/sdk-metrics/package.json | 6 ++-- packages/template/package.json | 2 +- selenium-tests/package.json | 22 +++++++------- 45 files changed, 242 insertions(+), 233 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91960a3c5b..8623279f76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,13 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/ ### :rocket: (Enhancement) -* feat(sdk-metrics): add exponential histogram mapping functions [#3504](https://github.com/open-telemetry/opentelemetry-js/pull/3504) @mwear +### :bug: (Bug Fix) + +### :books: (Refine Doc) + +### :house: (Internal) + +## 1.9.1 ### :bug: (Bug Fix) @@ -20,11 +26,10 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/ [#3562](https://github.com/open-telemetry/opentelemetry-js/pull/3562) @scheler * fix: include tracestate in export [#3569](https://github.com/open-telemetry/opentelemetry-js/pull/3569) @flarna -### :books: (Refine Doc) - ### :house: (Internal) * chore: fix cross project links and missing implicitly exported types [#3533](https://github.com/open-telemetry/opentelemetry-js/pull/3533) @legendecas +* feat(sdk-metrics): add exponential histogram mapping functions [#3504](https://github.com/open-telemetry/opentelemetry-js/pull/3504) @mwear ## 1.9.0 diff --git a/examples/https/package.json b/examples/https/package.json index 944e31678c..1c5b2c3179 100644 --- a/examples/https/package.json +++ b/examples/https/package.json @@ -1,7 +1,7 @@ { "name": "https-example", "private": true, - "version": "0.35.0", + "version": "0.35.1", "description": "Example of HTTPs integration with OpenTelemetry", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -33,14 +33,14 @@ }, "dependencies": { "@opentelemetry/api": "^1.0.0", - "@opentelemetry/exporter-jaeger": "1.9.0", - "@opentelemetry/exporter-zipkin": "1.9.0", - "@opentelemetry/instrumentation": "0.35.0", - "@opentelemetry/instrumentation-http": "0.35.0", - "@opentelemetry/resources": "1.9.0", - "@opentelemetry/sdk-trace-base": "1.9.0", - "@opentelemetry/sdk-trace-node": "1.9.0", - "@opentelemetry/semantic-conventions": "1.9.0" + "@opentelemetry/exporter-jaeger": "1.9.1", + "@opentelemetry/exporter-zipkin": "1.9.1", + "@opentelemetry/instrumentation": "0.35.1", + "@opentelemetry/instrumentation-http": "0.35.1", + "@opentelemetry/resources": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1", + "@opentelemetry/sdk-trace-node": "1.9.1", + "@opentelemetry/semantic-conventions": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/https", "devDependencies": { diff --git a/examples/opentelemetry-web/package.json b/examples/opentelemetry-web/package.json index 42f011e15c..4967b0eeff 100644 --- a/examples/opentelemetry-web/package.json +++ b/examples/opentelemetry-web/package.json @@ -1,7 +1,7 @@ { "name": "web-opentelemetry-example", "private": true, - "version": "0.35.0", + "version": "0.35.1", "description": "Example of using @opentelemetry/sdk-trace-web and @opentelemetry/sdk-metrics in browser", "main": "index.js", "scripts": { @@ -43,20 +43,20 @@ }, "dependencies": { "@opentelemetry/api": "^1.3.0", - "@opentelemetry/context-zone": "1.9.0", - "@opentelemetry/core": "1.9.0", - "@opentelemetry/exporter-metrics-otlp-http": "0.35.0", - "@opentelemetry/exporter-trace-otlp-http": "0.35.0", - "@opentelemetry/exporter-trace-otlp-proto": "0.35.0", - "@opentelemetry/exporter-zipkin": "1.9.0", - "@opentelemetry/instrumentation": "0.35.0", - "@opentelemetry/instrumentation-fetch": "0.35.0", - "@opentelemetry/instrumentation-xml-http-request": "0.35.0", - "@opentelemetry/propagator-b3": "1.9.0", - "@opentelemetry/sdk-metrics": "1.9.0", - "@opentelemetry/sdk-trace-base": "1.9.0", - "@opentelemetry/sdk-trace-web": "1.9.0", - "@opentelemetry/semantic-conventions": "1.9.0" + "@opentelemetry/context-zone": "1.9.1", + "@opentelemetry/core": "1.9.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.35.1", + "@opentelemetry/exporter-trace-otlp-http": "0.35.1", + "@opentelemetry/exporter-trace-otlp-proto": "0.35.1", + "@opentelemetry/exporter-zipkin": "1.9.1", + "@opentelemetry/instrumentation": "0.35.1", + "@opentelemetry/instrumentation-fetch": "0.35.1", + "@opentelemetry/instrumentation-xml-http-request": "0.35.1", + "@opentelemetry/propagator-b3": "1.9.1", + "@opentelemetry/sdk-metrics": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1", + "@opentelemetry/sdk-trace-web": "1.9.1", + "@opentelemetry/semantic-conventions": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/tracer-web" } diff --git a/examples/otlp-exporter-node/package.json b/examples/otlp-exporter-node/package.json index 7eee8b3479..67425a5379 100644 --- a/examples/otlp-exporter-node/package.json +++ b/examples/otlp-exporter-node/package.json @@ -1,7 +1,7 @@ { "name": "example-otlp-exporter-node", "private": true, - "version": "0.35.0", + "version": "0.35.1", "description": "Example of using @opentelemetry/collector-exporter in Node.js", "main": "index.js", "scripts": { @@ -29,17 +29,17 @@ }, "dependencies": { "@opentelemetry/api": "^1.3.0", - "@opentelemetry/core": "1.9.0", - "@opentelemetry/exporter-metrics-otlp-grpc": "0.35.0", - "@opentelemetry/exporter-metrics-otlp-http": "0.35.0", - "@opentelemetry/exporter-metrics-otlp-proto": "0.35.0", - "@opentelemetry/exporter-trace-otlp-grpc": "0.35.0", - "@opentelemetry/exporter-trace-otlp-http": "0.35.0", - "@opentelemetry/exporter-trace-otlp-proto": "0.35.0", - "@opentelemetry/resources": "1.9.0", - "@opentelemetry/sdk-metrics": "1.9.0", - "@opentelemetry/sdk-trace-base": "1.9.0", - "@opentelemetry/semantic-conventions": "1.9.0" + "@opentelemetry/core": "1.9.1", + "@opentelemetry/exporter-metrics-otlp-grpc": "0.35.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.35.1", + "@opentelemetry/exporter-metrics-otlp-proto": "0.35.1", + "@opentelemetry/exporter-trace-otlp-grpc": "0.35.1", + "@opentelemetry/exporter-trace-otlp-http": "0.35.1", + "@opentelemetry/exporter-trace-otlp-proto": "0.35.1", + "@opentelemetry/resources": "1.9.1", + "@opentelemetry/sdk-metrics": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1", + "@opentelemetry/semantic-conventions": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/otlp-exporter-node" } diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 2f3f725b6e..8d34640976 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -10,16 +10,20 @@ All notable changes to experimental packages in this project will be documented ### :bug: (Bug Fix) +### :books: (Refine Doc) + +### :house: (Internal) + +## 0.35.1 + +### :bug: (Bug Fix) + * fix: remove JSON syntax error and regenerate tsconfig files [#3566](https://github.com/open-telemetry/opentelemetry-js/pull/3566) @Flarna * Fixes an error where the generated JS files were not included in the esnext package due to a failure of the tsconfig generation * fix(sdk-node): register instrumentations early [#3502](https://github.com/open-telemetry/opentelemetry-js/pull/3502) @flarna * fix: include tracestate in export [#3569](https://github.com/open-telemetry/opentelemetry-js/pull/3569) @flarna * fix(http) Remove outgoing headers normalization [#3557](https://github.com/open-telemetry/opentelemetry-js/pull/3557) @marcinjahn -### :books: (Refine Doc) - -### :house: (Internal) - ## 0.35.0 ### :rocket: (Enhancement) diff --git a/experimental/backwards-compatability/node14/package.json b/experimental/backwards-compatability/node14/package.json index 1479ec527b..1d40058ead 100644 --- a/experimental/backwards-compatability/node14/package.json +++ b/experimental/backwards-compatability/node14/package.json @@ -1,6 +1,6 @@ { "name": "backcompat-node14", - "version": "0.35.0", + "version": "0.35.1", "private": true, "description": "Backwards compatability app for node 14 types and the OpenTelemetry Node.js SDK", "main": "index.js", @@ -9,8 +9,8 @@ "peer-api-check": "node ../../../scripts/peer-api-check.js" }, "dependencies": { - "@opentelemetry/sdk-node": "0.35.0", - "@opentelemetry/sdk-trace-base": "1.9.0" + "@opentelemetry/sdk-node": "0.35.1", + "@opentelemetry/sdk-trace-base": "1.9.1" }, "devDependencies": { "@types/node": "14.18.25", diff --git a/experimental/backwards-compatability/node16/package.json b/experimental/backwards-compatability/node16/package.json index c25735166c..25e8e60913 100644 --- a/experimental/backwards-compatability/node16/package.json +++ b/experimental/backwards-compatability/node16/package.json @@ -1,6 +1,6 @@ { "name": "backcompat-node16", - "version": "0.35.0", + "version": "0.35.1", "private": true, "description": "Backwards compatability app for node 16 types and the OpenTelemetry Node.js SDK", "main": "index.js", @@ -9,8 +9,8 @@ "peer-api-check": "node ../../../scripts/peer-api-check.js" }, "dependencies": { - "@opentelemetry/sdk-node": "0.35.0", - "@opentelemetry/sdk-trace-base": "1.9.0" + "@opentelemetry/sdk-node": "0.35.1", + "@opentelemetry/sdk-trace-base": "1.9.1" }, "devDependencies": { "@types/node": "16.11.52", diff --git a/experimental/examples/prometheus/package.json b/experimental/examples/prometheus/package.json index f5a991d5bb..a649056fa3 100644 --- a/experimental/examples/prometheus/package.json +++ b/experimental/examples/prometheus/package.json @@ -1,6 +1,6 @@ { "name": "prometheus-example", - "version": "0.35.0", + "version": "0.35.1", "private": true, "description": "Example of using @opentelemetry/sdk-metrics and @opentelemetry/exporter-prometheus", "main": "index.js", @@ -11,7 +11,7 @@ "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.3.0", - "@opentelemetry/exporter-prometheus": "0.35.0", - "@opentelemetry/sdk-metrics": "1.9.0" + "@opentelemetry/exporter-prometheus": "0.35.1", + "@opentelemetry/sdk-metrics": "1.9.1" } } diff --git a/experimental/packages/api-logs/package.json b/experimental/packages/api-logs/package.json index 822949a3db..7f6f846e2f 100644 --- a/experimental/packages/api-logs/package.json +++ b/experimental/packages/api-logs/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/api-logs", - "version": "0.35.0", + "version": "0.35.1", "description": "Public logs API for OpenTelemetry", "main": "build/src/index.js", "module": "build/esm/index.js", diff --git a/experimental/packages/exporter-trace-otlp-grpc/package.json b/experimental/packages/exporter-trace-otlp-grpc/package.json index d9e43e09a5..4600780b4a 100644 --- a/experimental/packages/exporter-trace-otlp-grpc/package.json +++ b/experimental/packages/exporter-trace-otlp-grpc/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/exporter-trace-otlp-grpc", - "version": "0.35.0", + "version": "0.35.1", "description": "OpenTelemetry Collector Exporter allows user to send collected traces to the OpenTelemetry Collector", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -50,7 +50,7 @@ "@babel/core": "7.16.0", "@grpc/proto-loader": "^0.7.3", "@opentelemetry/api": "^1.0.0", - "@opentelemetry/otlp-exporter-base": "0.35.0", + "@opentelemetry/otlp-exporter-base": "0.35.1", "@types/mocha": "10.0.0", "@types/node": "18.6.5", "@types/sinon": "10.0.13", @@ -69,11 +69,11 @@ }, "dependencies": { "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "1.9.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.35.0", - "@opentelemetry/otlp-transformer": "0.35.0", - "@opentelemetry/resources": "1.9.0", - "@opentelemetry/sdk-trace-base": "1.9.0" + "@opentelemetry/core": "1.9.1", + "@opentelemetry/otlp-grpc-exporter-base": "0.35.1", + "@opentelemetry/otlp-transformer": "0.35.1", + "@opentelemetry/resources": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/exporter-trace-otlp-grpc", "sideEffects": false diff --git a/experimental/packages/exporter-trace-otlp-http/package.json b/experimental/packages/exporter-trace-otlp-http/package.json index f5720ba500..90ac43cab7 100644 --- a/experimental/packages/exporter-trace-otlp-http/package.json +++ b/experimental/packages/exporter-trace-otlp-http/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/exporter-trace-otlp-http", - "version": "0.35.0", + "version": "0.35.1", "description": "OpenTelemetry Collector Trace Exporter allows user to send collected traces to the OpenTelemetry Collector", "main": "build/src/index.js", "module": "build/esm/index.js", @@ -94,11 +94,11 @@ "@opentelemetry/api": "^1.0.0" }, "dependencies": { - "@opentelemetry/core": "1.9.0", - "@opentelemetry/otlp-exporter-base": "0.35.0", - "@opentelemetry/otlp-transformer": "0.35.0", - "@opentelemetry/resources": "1.9.0", - "@opentelemetry/sdk-trace-base": "1.9.0" + "@opentelemetry/core": "1.9.1", + "@opentelemetry/otlp-exporter-base": "0.35.1", + "@opentelemetry/otlp-transformer": "0.35.1", + "@opentelemetry/resources": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/exporter-trace-otlp-http", "sideEffects": false diff --git a/experimental/packages/exporter-trace-otlp-proto/package.json b/experimental/packages/exporter-trace-otlp-proto/package.json index 5404c19d07..920f533084 100644 --- a/experimental/packages/exporter-trace-otlp-proto/package.json +++ b/experimental/packages/exporter-trace-otlp-proto/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/exporter-trace-otlp-proto", - "version": "0.35.0", + "version": "0.35.1", "description": "OpenTelemetry Collector Exporter allows user to send collected traces to the OpenTelemetry Collector using protobuf over HTTP", "main": "build/src/index.js", "module": "build/esm/index.js", @@ -81,12 +81,12 @@ "@opentelemetry/api": "^1.0.0" }, "dependencies": { - "@opentelemetry/core": "1.9.0", - "@opentelemetry/otlp-exporter-base": "0.35.0", - "@opentelemetry/otlp-proto-exporter-base": "0.35.0", - "@opentelemetry/otlp-transformer": "0.35.0", - "@opentelemetry/resources": "1.9.0", - "@opentelemetry/sdk-trace-base": "1.9.0" + "@opentelemetry/core": "1.9.1", + "@opentelemetry/otlp-exporter-base": "0.35.1", + "@opentelemetry/otlp-proto-exporter-base": "0.35.1", + "@opentelemetry/otlp-transformer": "0.35.1", + "@opentelemetry/resources": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/exporter-trace-otlp-proto", "sideEffects": false diff --git a/experimental/packages/opentelemetry-browser-detector/package.json b/experimental/packages/opentelemetry-browser-detector/package.json index a04c6cf78b..a1b604d274 100644 --- a/experimental/packages/opentelemetry-browser-detector/package.json +++ b/experimental/packages/opentelemetry-browser-detector/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/opentelemetry-browser-detector", - "version": "0.35.0", + "version": "0.35.1", "description": "OpenTelemetry Resource Detector for Browser", "main": "build/src/index.js", "module": "build/esm/index.js", @@ -70,8 +70,8 @@ "@opentelemetry/api": "^1.0.0" }, "dependencies": { - "@opentelemetry/resources": "1.9.0", - "@opentelemetry/semantic-conventions": "1.9.0" + "@opentelemetry/resources": "1.9.1", + "@opentelemetry/semantic-conventions": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/browser-detector" } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/package.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/package.json index fc331adc3a..ddefef4862 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/package.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/exporter-metrics-otlp-grpc", - "version": "0.35.0", + "version": "0.35.1", "description": "OpenTelemetry Collector Metrics Exporter allows user to send collected metrics to the OpenTelemetry Collector", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -68,12 +68,12 @@ }, "dependencies": { "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "1.9.0", - "@opentelemetry/exporter-metrics-otlp-http": "0.35.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.35.0", - "@opentelemetry/otlp-transformer": "0.35.0", - "@opentelemetry/resources": "1.9.0", - "@opentelemetry/sdk-metrics": "1.9.0" + "@opentelemetry/core": "1.9.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.35.1", + "@opentelemetry/otlp-grpc-exporter-base": "0.35.1", + "@opentelemetry/otlp-transformer": "0.35.1", + "@opentelemetry/resources": "1.9.1", + "@opentelemetry/sdk-metrics": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc", "sideEffects": false diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/package.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/package.json index b7a22b0580..01c9e5f742 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/package.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/exporter-metrics-otlp-http", - "version": "0.35.0", + "version": "0.35.1", "description": "OpenTelemetry Collector Metrics Exporter allows user to send collected metrics to the OpenTelemetry Collector", "main": "build/src/index.js", "module": "build/esm/index.js", @@ -94,11 +94,11 @@ "@opentelemetry/api": "^1.3.0" }, "dependencies": { - "@opentelemetry/core": "1.9.0", - "@opentelemetry/otlp-exporter-base": "0.35.0", - "@opentelemetry/otlp-transformer": "0.35.0", - "@opentelemetry/resources": "1.9.0", - "@opentelemetry/sdk-metrics": "1.9.0" + "@opentelemetry/core": "1.9.1", + "@opentelemetry/otlp-exporter-base": "0.35.1", + "@opentelemetry/otlp-transformer": "0.35.1", + "@opentelemetry/resources": "1.9.1", + "@opentelemetry/sdk-metrics": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-exporter-metrics-otlp-http", "sideEffects": false diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json index cfffa241ff..df98d761fa 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/exporter-metrics-otlp-proto", - "version": "0.35.0", + "version": "0.35.1", "description": "OpenTelemetry Collector Metrics Exporter allows user to send collected metrics to the OpenTelemetry Collector using protobuf over HTTP", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -66,13 +66,13 @@ "@opentelemetry/api": "^1.3.0" }, "dependencies": { - "@opentelemetry/core": "1.9.0", - "@opentelemetry/exporter-metrics-otlp-http": "0.35.0", - "@opentelemetry/otlp-exporter-base": "0.35.0", - "@opentelemetry/otlp-proto-exporter-base": "0.35.0", - "@opentelemetry/otlp-transformer": "0.35.0", - "@opentelemetry/resources": "1.9.0", - "@opentelemetry/sdk-metrics": "1.9.0" + "@opentelemetry/core": "1.9.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.35.1", + "@opentelemetry/otlp-exporter-base": "0.35.1", + "@opentelemetry/otlp-proto-exporter-base": "0.35.1", + "@opentelemetry/otlp-transformer": "0.35.1", + "@opentelemetry/resources": "1.9.1", + "@opentelemetry/sdk-metrics": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-exporter-metrics-otlp-proto", "sideEffects": false diff --git a/experimental/packages/opentelemetry-exporter-prometheus/package.json b/experimental/packages/opentelemetry-exporter-prometheus/package.json index ebb460276d..685ea68a1e 100644 --- a/experimental/packages/opentelemetry-exporter-prometheus/package.json +++ b/experimental/packages/opentelemetry-exporter-prometheus/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/exporter-prometheus", - "version": "0.35.0", + "version": "0.35.1", "description": "OpenTelemetry Exporter Prometheus provides a metrics endpoint for Prometheus", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -44,7 +44,7 @@ }, "devDependencies": { "@opentelemetry/api": "^1.3.0", - "@opentelemetry/semantic-conventions": "1.9.0", + "@opentelemetry/semantic-conventions": "1.9.1", "@types/mocha": "10.0.0", "@types/node": "18.6.5", "@types/sinon": "10.0.13", @@ -60,9 +60,9 @@ "@opentelemetry/api": "^1.3.0" }, "dependencies": { - "@opentelemetry/core": "1.9.0", - "@opentelemetry/resources": "1.9.0", - "@opentelemetry/sdk-metrics": "1.9.0" + "@opentelemetry/core": "1.9.1", + "@opentelemetry/resources": "1.9.1", + "@opentelemetry/sdk-metrics": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-exporter-prometheus", "sideEffects": false diff --git a/experimental/packages/opentelemetry-instrumentation-fetch/package.json b/experimental/packages/opentelemetry-instrumentation-fetch/package.json index 9dbaacd237..7e3a2719a8 100644 --- a/experimental/packages/opentelemetry-instrumentation-fetch/package.json +++ b/experimental/packages/opentelemetry-instrumentation-fetch/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/instrumentation-fetch", - "version": "0.35.0", + "version": "0.35.1", "description": "OpenTelemetry fetch automatic instrumentation package.", "main": "build/src/index.js", "module": "build/esm/index.js", @@ -56,9 +56,9 @@ "devDependencies": { "@babel/core": "7.16.0", "@opentelemetry/api": "^1.0.0", - "@opentelemetry/context-zone": "1.9.0", - "@opentelemetry/propagator-b3": "1.9.0", - "@opentelemetry/sdk-trace-base": "1.9.0", + "@opentelemetry/context-zone": "1.9.1", + "@opentelemetry/propagator-b3": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1", "@types/mocha": "10.0.0", "@types/node": "18.6.5", "@types/sinon": "10.0.13", @@ -87,10 +87,10 @@ "@opentelemetry/api": "^1.0.0" }, "dependencies": { - "@opentelemetry/core": "1.9.0", - "@opentelemetry/instrumentation": "0.35.0", - "@opentelemetry/sdk-trace-web": "1.9.0", - "@opentelemetry/semantic-conventions": "1.9.0" + "@opentelemetry/core": "1.9.1", + "@opentelemetry/instrumentation": "0.35.1", + "@opentelemetry/sdk-trace-web": "1.9.1", + "@opentelemetry/semantic-conventions": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation-fetch", "sideEffects": false diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/package.json b/experimental/packages/opentelemetry-instrumentation-grpc/package.json index ecb0891d77..5bc49bcc37 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/package.json +++ b/experimental/packages/opentelemetry-instrumentation-grpc/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/instrumentation-grpc", - "version": "0.35.0", + "version": "0.35.1", "description": "OpenTelemetry grpc automatic instrumentation package.", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -48,10 +48,10 @@ "@grpc/grpc-js": "^1.7.1", "@grpc/proto-loader": "^0.7.3", "@opentelemetry/api": "^1.3.0", - "@opentelemetry/context-async-hooks": "1.9.0", - "@opentelemetry/core": "1.9.0", - "@opentelemetry/sdk-trace-base": "1.9.0", - "@opentelemetry/sdk-trace-node": "1.9.0", + "@opentelemetry/context-async-hooks": "1.9.1", + "@opentelemetry/core": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1", + "@opentelemetry/sdk-trace-node": "1.9.1", "@types/mocha": "10.0.0", "@types/node": "18.6.5", "@types/semver": "7.3.9", @@ -71,8 +71,8 @@ "@opentelemetry/api": "^1.3.0" }, "dependencies": { - "@opentelemetry/instrumentation": "0.35.0", - "@opentelemetry/semantic-conventions": "1.9.0" + "@opentelemetry/instrumentation": "0.35.1", + "@opentelemetry/semantic-conventions": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation-grpc", "sideEffects": false diff --git a/experimental/packages/opentelemetry-instrumentation-http/package.json b/experimental/packages/opentelemetry-instrumentation-http/package.json index da3da4412f..7cae297fe3 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/package.json +++ b/experimental/packages/opentelemetry-instrumentation-http/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/instrumentation-http", - "version": "0.35.0", + "version": "0.35.1", "description": "OpenTelemetry http/https automatic instrumentation package.", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -46,10 +46,10 @@ }, "devDependencies": { "@opentelemetry/api": "^1.3.0", - "@opentelemetry/context-async-hooks": "1.9.0", - "@opentelemetry/sdk-metrics": "1.9.0", - "@opentelemetry/sdk-trace-base": "1.9.0", - "@opentelemetry/sdk-trace-node": "1.9.0", + "@opentelemetry/context-async-hooks": "1.9.1", + "@opentelemetry/sdk-metrics": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1", + "@opentelemetry/sdk-trace-node": "1.9.1", "@types/mocha": "10.0.0", "@types/node": "18.6.5", "@types/request-promise-native": "1.0.18", @@ -73,9 +73,9 @@ "@opentelemetry/api": "^1.3.0" }, "dependencies": { - "@opentelemetry/core": "1.9.0", - "@opentelemetry/instrumentation": "0.35.0", - "@opentelemetry/semantic-conventions": "1.9.0", + "@opentelemetry/core": "1.9.1", + "@opentelemetry/instrumentation": "0.35.1", + "@opentelemetry/semantic-conventions": "1.9.1", "semver": "^7.3.5" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation-http", diff --git a/experimental/packages/opentelemetry-instrumentation-xml-http-request/package.json b/experimental/packages/opentelemetry-instrumentation-xml-http-request/package.json index e9df68a706..99813efd39 100644 --- a/experimental/packages/opentelemetry-instrumentation-xml-http-request/package.json +++ b/experimental/packages/opentelemetry-instrumentation-xml-http-request/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/instrumentation-xml-http-request", - "version": "0.35.0", + "version": "0.35.1", "description": "OpenTelemetry XMLHttpRequest automatic instrumentation package.", "main": "build/src/index.js", "module": "build/esm/index.js", @@ -56,9 +56,9 @@ "devDependencies": { "@babel/core": "7.16.0", "@opentelemetry/api": "^1.0.0", - "@opentelemetry/context-zone": "1.9.0", - "@opentelemetry/propagator-b3": "1.9.0", - "@opentelemetry/sdk-trace-base": "1.9.0", + "@opentelemetry/context-zone": "1.9.1", + "@opentelemetry/propagator-b3": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1", "@types/mocha": "10.0.0", "@types/node": "18.6.5", "@types/sinon": "10.0.13", @@ -87,10 +87,10 @@ "@opentelemetry/api": "^1.0.0" }, "dependencies": { - "@opentelemetry/core": "1.9.0", - "@opentelemetry/instrumentation": "0.35.0", - "@opentelemetry/sdk-trace-web": "1.9.0", - "@opentelemetry/semantic-conventions": "1.9.0" + "@opentelemetry/core": "1.9.1", + "@opentelemetry/instrumentation": "0.35.1", + "@opentelemetry/sdk-trace-web": "1.9.1", + "@opentelemetry/semantic-conventions": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation-xml-http-request", "sideEffects": false diff --git a/experimental/packages/opentelemetry-instrumentation/package.json b/experimental/packages/opentelemetry-instrumentation/package.json index 00d787d530..05cc761e75 100644 --- a/experimental/packages/opentelemetry-instrumentation/package.json +++ b/experimental/packages/opentelemetry-instrumentation/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/instrumentation", - "version": "0.35.0", + "version": "0.35.1", "description": "Base class for node which OpenTelemetry instrumentation modules extend", "author": "OpenTelemetry Authors", "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation", @@ -78,7 +78,7 @@ "devDependencies": { "@babel/core": "7.16.0", "@opentelemetry/api": "^1.3.0", - "@opentelemetry/sdk-metrics": "1.9.0", + "@opentelemetry/sdk-metrics": "1.9.1", "@types/mocha": "10.0.0", "@types/node": "18.6.5", "@types/semver": "7.3.9", diff --git a/experimental/packages/opentelemetry-sdk-node/package.json b/experimental/packages/opentelemetry-sdk-node/package.json index 4388986fb3..bb629ad0b5 100644 --- a/experimental/packages/opentelemetry-sdk-node/package.json +++ b/experimental/packages/opentelemetry-sdk-node/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/sdk-node", - "version": "0.35.0", + "version": "0.35.1", "description": "OpenTelemetry SDK for Node.js", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -44,25 +44,25 @@ "access": "public" }, "dependencies": { - "@opentelemetry/core": "1.9.0", - "@opentelemetry/exporter-jaeger": "1.9.0", - "@opentelemetry/exporter-trace-otlp-grpc": "0.35.0", - "@opentelemetry/exporter-trace-otlp-http": "0.35.0", - "@opentelemetry/exporter-trace-otlp-proto": "0.35.0", - "@opentelemetry/exporter-zipkin": "1.9.0", - "@opentelemetry/instrumentation": "0.35.0", - "@opentelemetry/resources": "1.9.0", - "@opentelemetry/sdk-metrics": "1.9.0", - "@opentelemetry/sdk-trace-base": "1.9.0", - "@opentelemetry/sdk-trace-node": "1.9.0", - "@opentelemetry/semantic-conventions": "1.9.0" + "@opentelemetry/core": "1.9.1", + "@opentelemetry/exporter-jaeger": "1.9.1", + "@opentelemetry/exporter-trace-otlp-grpc": "0.35.1", + "@opentelemetry/exporter-trace-otlp-http": "0.35.1", + "@opentelemetry/exporter-trace-otlp-proto": "0.35.1", + "@opentelemetry/exporter-zipkin": "1.9.1", + "@opentelemetry/instrumentation": "0.35.1", + "@opentelemetry/resources": "1.9.1", + "@opentelemetry/sdk-metrics": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1", + "@opentelemetry/sdk-trace-node": "1.9.1", + "@opentelemetry/semantic-conventions": "1.9.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.5.0" }, "devDependencies": { "@opentelemetry/api": ">=1.3.0 <1.5.0", - "@opentelemetry/context-async-hooks": "1.9.0", + "@opentelemetry/context-async-hooks": "1.9.1", "@types/mocha": "10.0.0", "@types/node": "18.6.5", "@types/semver": "7.3.9", diff --git a/experimental/packages/otlp-exporter-base/package.json b/experimental/packages/otlp-exporter-base/package.json index 52435a0dfd..22c390b1bb 100644 --- a/experimental/packages/otlp-exporter-base/package.json +++ b/experimental/packages/otlp-exporter-base/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/otlp-exporter-base", - "version": "0.35.0", + "version": "0.35.1", "description": "OpenTelemetry OTLP Exporter base (for internal use only)", "main": "build/src/index.js", "module": "build/esm/index.js", @@ -61,7 +61,7 @@ "access": "public" }, "dependencies": { - "@opentelemetry/core": "1.9.0" + "@opentelemetry/core": "1.9.1" }, "devDependencies": { "@opentelemetry/api": "^1.0.0", diff --git a/experimental/packages/otlp-grpc-exporter-base/package.json b/experimental/packages/otlp-grpc-exporter-base/package.json index 19c799ddb6..c64506e064 100644 --- a/experimental/packages/otlp-grpc-exporter-base/package.json +++ b/experimental/packages/otlp-grpc-exporter-base/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/otlp-grpc-exporter-base", - "version": "0.35.0", + "version": "0.35.1", "description": "OpenTelemetry OTLP-gRPC Exporter base (for internal use only)", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -51,9 +51,9 @@ "devDependencies": { "@babel/core": "7.16.0", "@opentelemetry/api": "^1.0.0", - "@opentelemetry/otlp-transformer": "0.35.0", - "@opentelemetry/resources": "1.9.0", - "@opentelemetry/sdk-trace-base": "1.9.0", + "@opentelemetry/otlp-transformer": "0.35.1", + "@opentelemetry/resources": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1", "@types/mocha": "10.0.0", "@types/node": "18.6.5", "@types/sinon": "10.0.13", @@ -73,8 +73,8 @@ "dependencies": { "@grpc/grpc-js": "^1.7.1", "@grpc/proto-loader": "^0.7.3", - "@opentelemetry/core": "1.9.0", - "@opentelemetry/otlp-exporter-base": "0.35.0" + "@opentelemetry/core": "1.9.1", + "@opentelemetry/otlp-exporter-base": "0.35.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/otlp-grpc-exporter-base", "sideEffects": false diff --git a/experimental/packages/otlp-proto-exporter-base/package.json b/experimental/packages/otlp-proto-exporter-base/package.json index aeedeab199..a3ae550587 100644 --- a/experimental/packages/otlp-proto-exporter-base/package.json +++ b/experimental/packages/otlp-proto-exporter-base/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/otlp-proto-exporter-base", - "version": "0.35.0", + "version": "0.35.1", "description": "OpenTelemetry OTLP-HTTP-protobuf Exporter base (for internal use only)", "main": "build/src/index.js", "module": "build/esm/index.js", @@ -77,8 +77,8 @@ "@opentelemetry/api": "^1.0.0" }, "dependencies": { - "@opentelemetry/core": "1.9.0", - "@opentelemetry/otlp-exporter-base": "0.35.0", + "@opentelemetry/core": "1.9.1", + "@opentelemetry/otlp-exporter-base": "0.35.1", "protobufjs": "^7.1.2" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/otlp-proto-exporter-base", diff --git a/experimental/packages/otlp-transformer/package.json b/experimental/packages/otlp-transformer/package.json index 1667e88cca..d4596fddba 100644 --- a/experimental/packages/otlp-transformer/package.json +++ b/experimental/packages/otlp-transformer/package.json @@ -4,7 +4,7 @@ "publishConfig": { "access": "public" }, - "version": "0.35.0", + "version": "0.35.1", "description": "Transform OpenTelemetry SDK data into OTLP", "module": "build/esm/index.js", "esnext": "build/esnext/index.js", @@ -76,10 +76,10 @@ "webpack": "4.46.0" }, "dependencies": { - "@opentelemetry/core": "1.9.0", - "@opentelemetry/resources": "1.9.0", - "@opentelemetry/sdk-metrics": "1.9.0", - "@opentelemetry/sdk-trace-base": "1.9.0" + "@opentelemetry/core": "1.9.1", + "@opentelemetry/resources": "1.9.1", + "@opentelemetry/sdk-metrics": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/otlp-transformer", "sideEffects": false diff --git a/integration-tests/propagation-validation-server/package.json b/integration-tests/propagation-validation-server/package.json index 8e102338ea..e49ef1ac83 100644 --- a/integration-tests/propagation-validation-server/package.json +++ b/integration-tests/propagation-validation-server/package.json @@ -1,6 +1,6 @@ { "name": "propagation-validation-server", - "version": "1.9.0", + "version": "1.9.1", "description": "server for w3c tests", "main": "validation_server.js", "private": true, @@ -12,9 +12,9 @@ }, "dependencies": { "@opentelemetry/api": "^1.0.0", - "@opentelemetry/context-async-hooks": "1.9.0", - "@opentelemetry/core": "1.9.0", - "@opentelemetry/sdk-trace-base": "1.9.0", + "@opentelemetry/context-async-hooks": "1.9.1", + "@opentelemetry/core": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1", "axios": "0.24.0", "body-parser": "1.19.0", "express": "4.17.3" diff --git a/packages/opentelemetry-context-async-hooks/package.json b/packages/opentelemetry-context-async-hooks/package.json index 8994955e2c..c44f6ea93c 100644 --- a/packages/opentelemetry-context-async-hooks/package.json +++ b/packages/opentelemetry-context-async-hooks/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/context-async-hooks", - "version": "1.9.0", + "version": "1.9.1", "description": "OpenTelemetry AsyncHooks-based Context Manager", "main": "build/src/index.js", "types": "build/src/index.d.ts", diff --git a/packages/opentelemetry-context-zone-peer-dep/package.json b/packages/opentelemetry-context-zone-peer-dep/package.json index c67006dd05..ff1f4aa78f 100644 --- a/packages/opentelemetry-context-zone-peer-dep/package.json +++ b/packages/opentelemetry-context-zone-peer-dep/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/context-zone-peer-dep", - "version": "1.9.0", + "version": "1.9.1", "description": "OpenTelemetry Context Zone with peer dependency for zone.js", "main": "build/src/index.js", "module": "build/esm/index.js", diff --git a/packages/opentelemetry-context-zone/package.json b/packages/opentelemetry-context-zone/package.json index 097b7c81e3..b31be1757c 100644 --- a/packages/opentelemetry-context-zone/package.json +++ b/packages/opentelemetry-context-zone/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/context-zone", - "version": "1.9.0", + "version": "1.9.1", "description": "OpenTelemetry Context Zone", "main": "build/src/index.js", "module": "build/esm/index.js", @@ -74,7 +74,7 @@ "webpack-merge": "5.8.0" }, "dependencies": { - "@opentelemetry/context-zone-peer-dep": "1.9.0", + "@opentelemetry/context-zone-peer-dep": "1.9.1", "zone.js": "^0.11.0" }, "sideEffects": true, diff --git a/packages/opentelemetry-core/package.json b/packages/opentelemetry-core/package.json index e8bc5a0917..f9cd044ce6 100644 --- a/packages/opentelemetry-core/package.json +++ b/packages/opentelemetry-core/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/core", - "version": "1.9.0", + "version": "1.9.1", "description": "OpenTelemetry Core provides constants and utilities shared by all OpenTelemetry SDK packages.", "main": "build/src/index.js", "module": "build/esm/index.js", @@ -91,7 +91,7 @@ "@opentelemetry/api": ">=1.0.0 <1.5.0" }, "dependencies": { - "@opentelemetry/semantic-conventions": "1.9.0" + "@opentelemetry/semantic-conventions": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-core", "sideEffects": false diff --git a/packages/opentelemetry-exporter-jaeger/package.json b/packages/opentelemetry-exporter-jaeger/package.json index f347f2199f..fbfbf01170 100644 --- a/packages/opentelemetry-exporter-jaeger/package.json +++ b/packages/opentelemetry-exporter-jaeger/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/exporter-jaeger", - "version": "1.9.0", + "version": "1.9.1", "description": "OpenTelemetry Exporter Jaeger allows user to send collected traces to Jaeger", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -45,7 +45,7 @@ }, "devDependencies": { "@opentelemetry/api": "^1.0.0", - "@opentelemetry/resources": "1.9.0", + "@opentelemetry/resources": "1.9.1", "@types/mocha": "10.0.0", "@types/node": "18.6.5", "@types/sinon": "10.0.13", @@ -62,9 +62,9 @@ "@opentelemetry/api": "^1.0.0" }, "dependencies": { - "@opentelemetry/core": "1.9.0", - "@opentelemetry/sdk-trace-base": "1.9.0", - "@opentelemetry/semantic-conventions": "1.9.0", + "@opentelemetry/core": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1", + "@opentelemetry/semantic-conventions": "1.9.1", "jaeger-client": "^3.15.0" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-exporter-jaeger", diff --git a/packages/opentelemetry-exporter-zipkin/package.json b/packages/opentelemetry-exporter-zipkin/package.json index 3a8ee9d9a6..76a0bbc4d7 100644 --- a/packages/opentelemetry-exporter-zipkin/package.json +++ b/packages/opentelemetry-exporter-zipkin/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/exporter-zipkin", - "version": "1.9.0", + "version": "1.9.1", "description": "OpenTelemetry Zipkin Exporter allows the user to send collected traces to Zipkin.", "main": "build/src/index.js", "module": "build/esm/index.js", @@ -91,10 +91,10 @@ "@opentelemetry/api": "^1.0.0" }, "dependencies": { - "@opentelemetry/core": "1.9.0", - "@opentelemetry/resources": "1.9.0", - "@opentelemetry/sdk-trace-base": "1.9.0", - "@opentelemetry/semantic-conventions": "1.9.0" + "@opentelemetry/core": "1.9.1", + "@opentelemetry/resources": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1", + "@opentelemetry/semantic-conventions": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-exporter-zipkin", "sideEffects": false diff --git a/packages/opentelemetry-propagator-b3/package.json b/packages/opentelemetry-propagator-b3/package.json index b222af6472..f9d3cad28d 100644 --- a/packages/opentelemetry-propagator-b3/package.json +++ b/packages/opentelemetry-propagator-b3/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/propagator-b3", - "version": "1.9.0", + "version": "1.9.1", "description": "OpenTelemetry B3 propagator provides context propagation for systems that are using the B3 header format", "main": "build/src/index.js", "module": "build/esm/index.js", @@ -51,7 +51,7 @@ "access": "public" }, "dependencies": { - "@opentelemetry/core": "1.9.0" + "@opentelemetry/core": "1.9.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.5.0" diff --git a/packages/opentelemetry-propagator-jaeger/package.json b/packages/opentelemetry-propagator-jaeger/package.json index 030a14d54c..32bc7760cf 100644 --- a/packages/opentelemetry-propagator-jaeger/package.json +++ b/packages/opentelemetry-propagator-jaeger/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/propagator-jaeger", - "version": "1.9.0", + "version": "1.9.1", "description": "OpenTelemetry Jaeger propagator provides HTTP header propagation for systems that are using Jaeger HTTP header format.", "main": "build/src/index.js", "module": "build/esm/index.js", @@ -80,7 +80,7 @@ "@opentelemetry/api": ">=1.0.0 <1.5.0" }, "dependencies": { - "@opentelemetry/core": "1.9.0" + "@opentelemetry/core": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-propagator-jaeger", "sideEffects": false diff --git a/packages/opentelemetry-resources/package.json b/packages/opentelemetry-resources/package.json index 6f9dc03473..59c1054cb2 100644 --- a/packages/opentelemetry-resources/package.json +++ b/packages/opentelemetry-resources/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/resources", - "version": "1.9.0", + "version": "1.9.1", "description": "OpenTelemetry SDK resources", "main": "build/src/index.js", "module": "build/esm/index.js", @@ -89,8 +89,8 @@ "@opentelemetry/api": ">=1.0.0 <1.5.0" }, "dependencies": { - "@opentelemetry/core": "1.9.0", - "@opentelemetry/semantic-conventions": "1.9.0" + "@opentelemetry/core": "1.9.1", + "@opentelemetry/semantic-conventions": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-resources", "sideEffects": false diff --git a/packages/opentelemetry-sdk-trace-base/package.json b/packages/opentelemetry-sdk-trace-base/package.json index c3e7b93239..b0f0967ead 100644 --- a/packages/opentelemetry-sdk-trace-base/package.json +++ b/packages/opentelemetry-sdk-trace-base/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/sdk-trace-base", - "version": "1.9.0", + "version": "1.9.1", "description": "OpenTelemetry Tracing", "main": "build/src/index.js", "module": "build/esm/index.js", @@ -91,9 +91,9 @@ "@opentelemetry/api": ">=1.0.0 <1.5.0" }, "dependencies": { - "@opentelemetry/core": "1.9.0", - "@opentelemetry/resources": "1.9.0", - "@opentelemetry/semantic-conventions": "1.9.0" + "@opentelemetry/core": "1.9.1", + "@opentelemetry/resources": "1.9.1", + "@opentelemetry/semantic-conventions": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base", "sideEffects": false diff --git a/packages/opentelemetry-sdk-trace-node/package.json b/packages/opentelemetry-sdk-trace-node/package.json index 108d8c975b..53a01967b3 100644 --- a/packages/opentelemetry-sdk-trace-node/package.json +++ b/packages/opentelemetry-sdk-trace-node/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/sdk-trace-node", - "version": "1.9.0", + "version": "1.9.1", "description": "OpenTelemetry Node SDK provides automatic telemetry (tracing, metrics, etc) for Node.js applications", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -46,8 +46,8 @@ }, "devDependencies": { "@opentelemetry/api": ">=1.0.0 <1.5.0", - "@opentelemetry/resources": "1.9.0", - "@opentelemetry/semantic-conventions": "1.9.0", + "@opentelemetry/resources": "1.9.1", + "@opentelemetry/semantic-conventions": "1.9.1", "@types/mocha": "10.0.0", "@types/node": "18.6.5", "@types/semver": "7.3.9", @@ -64,11 +64,11 @@ "@opentelemetry/api": ">=1.0.0 <1.5.0" }, "dependencies": { - "@opentelemetry/context-async-hooks": "1.9.0", - "@opentelemetry/core": "1.9.0", - "@opentelemetry/propagator-b3": "1.9.0", - "@opentelemetry/propagator-jaeger": "1.9.0", - "@opentelemetry/sdk-trace-base": "1.9.0", + "@opentelemetry/context-async-hooks": "1.9.1", + "@opentelemetry/core": "1.9.1", + "@opentelemetry/propagator-b3": "1.9.1", + "@opentelemetry/propagator-jaeger": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1", "semver": "^7.3.5" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-node", diff --git a/packages/opentelemetry-sdk-trace-web/package.json b/packages/opentelemetry-sdk-trace-web/package.json index b6c64e8974..285ac03779 100644 --- a/packages/opentelemetry-sdk-trace-web/package.json +++ b/packages/opentelemetry-sdk-trace-web/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/sdk-trace-web", - "version": "1.9.0", + "version": "1.9.1", "description": "OpenTelemetry Web Tracer", "main": "build/src/index.js", "module": "build/esm/index.js", @@ -57,9 +57,9 @@ "devDependencies": { "@babel/core": "7.16.0", "@opentelemetry/api": ">=1.0.0 <1.5.0", - "@opentelemetry/context-zone": "1.9.0", - "@opentelemetry/propagator-b3": "1.9.0", - "@opentelemetry/resources": "1.9.0", + "@opentelemetry/context-zone": "1.9.1", + "@opentelemetry/propagator-b3": "1.9.1", + "@opentelemetry/resources": "1.9.1", "@types/jquery": "3.5.8", "@types/mocha": "10.0.0", "@types/node": "18.6.5", @@ -91,9 +91,9 @@ "@opentelemetry/api": ">=1.0.0 <1.5.0" }, "dependencies": { - "@opentelemetry/core": "1.9.0", - "@opentelemetry/sdk-trace-base": "1.9.0", - "@opentelemetry/semantic-conventions": "1.9.0" + "@opentelemetry/core": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1", + "@opentelemetry/semantic-conventions": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-web", "sideEffects": false diff --git a/packages/opentelemetry-semantic-conventions/package.json b/packages/opentelemetry-semantic-conventions/package.json index 90faab299d..7887147efc 100644 --- a/packages/opentelemetry-semantic-conventions/package.json +++ b/packages/opentelemetry-semantic-conventions/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/semantic-conventions", - "version": "1.9.0", + "version": "1.9.1", "description": "OpenTelemetry semantic conventions", "main": "build/src/index.js", "module": "build/esm/index.js", diff --git a/packages/opentelemetry-shim-opentracing/package.json b/packages/opentelemetry-shim-opentracing/package.json index 7062f307ed..1262559717 100644 --- a/packages/opentelemetry-shim-opentracing/package.json +++ b/packages/opentelemetry-shim-opentracing/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/shim-opentracing", - "version": "1.9.0", + "version": "1.9.1", "description": "OpenTracing to OpenTelemetry shim", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -43,9 +43,9 @@ }, "devDependencies": { "@opentelemetry/api": ">=1.0.0 <1.5.0", - "@opentelemetry/propagator-b3": "1.9.0", - "@opentelemetry/propagator-jaeger": "1.9.0", - "@opentelemetry/sdk-trace-base": "1.9.0", + "@opentelemetry/propagator-b3": "1.9.1", + "@opentelemetry/propagator-jaeger": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1", "@types/mocha": "10.0.0", "@types/node": "18.6.5", "codecov": "3.8.3", @@ -59,8 +59,8 @@ "@opentelemetry/api": ">=1.0.0 <1.5.0" }, "dependencies": { - "@opentelemetry/core": "1.9.0", - "@opentelemetry/semantic-conventions": "1.9.0", + "@opentelemetry/core": "1.9.1", + "@opentelemetry/semantic-conventions": "1.9.1", "opentracing": "^0.14.4" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-shim-opentracing", diff --git a/packages/sdk-metrics/package.json b/packages/sdk-metrics/package.json index 3a4f653ac7..bcd67dfb2a 100644 --- a/packages/sdk-metrics/package.json +++ b/packages/sdk-metrics/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/sdk-metrics", - "version": "1.9.0", + "version": "1.9.1", "description": "OpenTelemetry metrics SDK", "main": "build/src/index.js", "module": "build/esm/index.js", @@ -77,8 +77,8 @@ "@opentelemetry/api": ">=1.3.0 <1.5.0" }, "dependencies": { - "@opentelemetry/core": "1.9.0", - "@opentelemetry/resources": "1.9.0", + "@opentelemetry/core": "1.9.1", + "@opentelemetry/resources": "1.9.1", "lodash.merge": "4.6.2" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics", diff --git a/packages/template/package.json b/packages/template/package.json index 3f0e9e70c7..ff6de03cfb 100644 --- a/packages/template/package.json +++ b/packages/template/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/template", - "version": "1.9.0", + "version": "1.9.1", "private": true, "publishConfig": { "access": "restricted" diff --git a/selenium-tests/package.json b/selenium-tests/package.json index 388d4ccadf..be4f1d42a1 100644 --- a/selenium-tests/package.json +++ b/selenium-tests/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/selenium-tests", - "version": "1.9.0", + "version": "1.9.1", "private": true, "description": "OpenTelemetry Selenium Tests", "main": "index.js", @@ -56,16 +56,16 @@ "@opentelemetry/api": "^1.0.0" }, "dependencies": { - "@opentelemetry/context-zone-peer-dep": "1.9.0", - "@opentelemetry/core": "1.9.0", - "@opentelemetry/exporter-trace-otlp-http": "0.35.0", - "@opentelemetry/exporter-zipkin": "1.9.0", - "@opentelemetry/instrumentation": "0.35.0", - "@opentelemetry/instrumentation-fetch": "0.35.0", - "@opentelemetry/instrumentation-xml-http-request": "0.35.0", - "@opentelemetry/sdk-metrics": "1.9.0", - "@opentelemetry/sdk-trace-base": "1.9.0", - "@opentelemetry/sdk-trace-web": "1.9.0", + "@opentelemetry/context-zone-peer-dep": "1.9.1", + "@opentelemetry/core": "1.9.1", + "@opentelemetry/exporter-trace-otlp-http": "0.35.1", + "@opentelemetry/exporter-zipkin": "1.9.1", + "@opentelemetry/instrumentation": "0.35.1", + "@opentelemetry/instrumentation-fetch": "0.35.1", + "@opentelemetry/instrumentation-xml-http-request": "0.35.1", + "@opentelemetry/sdk-metrics": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1", + "@opentelemetry/sdk-trace-web": "1.9.1", "zone.js": "0.11.4" } } From 2a76d881e4758275b00cb80a4c01500744509f73 Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Tue, 31 Jan 2023 14:45:49 +0800 Subject: [PATCH 03/37] feat(sdk-metrics): apply binary search in histogram (#3539) --- CHANGELOG.md | 2 ++ .../sdk-metrics/src/aggregator/Histogram.ts | 14 +++------ packages/sdk-metrics/src/utils.ts | 27 ++++++++++++++++ packages/sdk-metrics/test/utils.test.ts | 31 ++++++++++++++++++- 4 files changed, 63 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8623279f76..33c3c17e8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/ ### :rocket: (Enhancement) +* feat(sdk-metrics): apply binary search in histogram recording [#3539](https://github.com/open-telemetry/opentelemetry-js/pull/3539) @legendecas + ### :bug: (Bug Fix) ### :books: (Refine Doc) diff --git a/packages/sdk-metrics/src/aggregator/Histogram.ts b/packages/sdk-metrics/src/aggregator/Histogram.ts index a916e18248..f20784b054 100644 --- a/packages/sdk-metrics/src/aggregator/Histogram.ts +++ b/packages/sdk-metrics/src/aggregator/Histogram.ts @@ -23,7 +23,7 @@ import { import { DataPointType, HistogramMetricData } from '../export/MetricData'; import { HrTime } from '@opentelemetry/api'; import { InstrumentDescriptor, InstrumentType } from '../InstrumentDescriptor'; -import { Maybe } from '../utils'; +import { binarySearchLB, Maybe } from '../utils'; import { AggregationTemporality } from '../export/AggregationTemporality'; /** @@ -77,14 +77,8 @@ export class HistogramAccumulation implements Accumulation { this._current.hasMinMax = true; } - for (let i = 0; i < this._boundaries.length; i++) { - if (value < this._boundaries[i]) { - this._current.buckets.counts[i] += 1; - return; - } - } - // value is above all observed boundaries - this._current.buckets.counts[this._boundaries.length] += 1; + const idx = binarySearchLB(this._boundaries, value); + this._current.buckets.counts[idx + 1] += 1; } setStartTime(startTime: HrTime): void { @@ -104,7 +98,7 @@ export class HistogramAggregator implements Aggregator { public kind: AggregatorKind.HISTOGRAM = AggregatorKind.HISTOGRAM; /** - * @param _boundaries upper bounds of recorded values. + * @param _boundaries sorted upper bounds of recorded values. * @param _recordMinMax If set to true, min and max will be recorded. Otherwise, min and max will not be recorded. */ constructor( diff --git a/packages/sdk-metrics/src/utils.ts b/packages/sdk-metrics/src/utils.ts index c2bc440849..835de92fe1 100644 --- a/packages/sdk-metrics/src/utils.ts +++ b/packages/sdk-metrics/src/utils.ts @@ -163,3 +163,30 @@ export function setEquals(lhs: Set, rhs: Set): boolean { } return true; } + +/** + * Binary search the sorted array to the find lower bound for the value. + * @param arr + * @param value + * @returns + */ +export function binarySearchLB(arr: number[], value: number): number { + let lo = 0; + let hi = arr.length - 1; + + while (hi - lo > 1) { + const mid = Math.trunc((hi + lo) / 2); + if (arr[mid] <= value) { + lo = mid; + } else { + hi = mid - 1; + } + } + + if (arr[hi] <= value) { + return hi; + } else if (arr[lo] <= value) { + return lo; + } + return -1; +} diff --git a/packages/sdk-metrics/test/utils.test.ts b/packages/sdk-metrics/test/utils.test.ts index 04633a14ba..16ec0a2b3a 100644 --- a/packages/sdk-metrics/test/utils.test.ts +++ b/packages/sdk-metrics/test/utils.test.ts @@ -16,7 +16,12 @@ import * as sinon from 'sinon'; import * as assert from 'assert'; -import { callWithTimeout, hashAttributes, TimeoutError } from '../src/utils'; +import { + binarySearchLB, + callWithTimeout, + hashAttributes, + TimeoutError, +} from '../src/utils'; import { assertRejects } from './test-utils'; import { MetricAttributes } from '@opentelemetry/api'; @@ -60,4 +65,28 @@ describe('utils', () => { } }); }); + + describe('binarySearchLB', () => { + const tests = [ + /** [ arr, value, expected lb idx ] */ + [[0, 10, 100, 1000], -1, -1], + [[0, 10, 100, 1000], 0, 0], + [[0, 10, 100, 1000], 1, 0], + [[0, 10, 100, 1000], 10, 1], + [[0, 10, 100, 1000], 1000, 3], + [[0, 10, 100, 1000], 1001, 3], + + [[0, 10, 100, 1000, 10_000], -1, -1], + [[0, 10, 100, 1000, 10_000], 0, 0], + [[0, 10, 100, 1000, 10_000], 10, 1], + [[0, 10, 100, 1000, 10_000], 1001, 3], + [[0, 10, 100, 1000, 10_000], 10_001, 4], + ] as [number[], number, number][]; + + for (const [idx, test] of tests.entries()) { + it(`test idx(${idx}): find lb of ${test[1]} in [${test[0]}]`, () => { + assert.strictEqual(binarySearchLB(test[0], test[1]), test[2]); + }); + } + }); }); From 2b59c2820c99e7f65012e01b6a310b8438b79e89 Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Wed, 1 Feb 2023 04:11:08 +0800 Subject: [PATCH 04/37] chore(tsconfig): abort on invalid json content (#3578) Co-authored-by: Daniel Dyla --- style.md => doc/contributing/style.md | 0 .../backwards-compatability/node14/tsconfig.json | 2 +- .../backwards-compatability/node16/tsconfig.json | 2 +- .../packages/otlp-grpc-exporter-base/tsconfig.json | 1 + scripts/update-ts-configs.js | 14 ++++++-------- tsconfig.es5.json => tsconfig.base.es5.json | 0 tsconfig.base.esm.json | 2 +- 7 files changed, 10 insertions(+), 11 deletions(-) rename style.md => doc/contributing/style.md (100%) rename tsconfig.es5.json => tsconfig.base.es5.json (100%) diff --git a/style.md b/doc/contributing/style.md similarity index 100% rename from style.md rename to doc/contributing/style.md diff --git a/experimental/backwards-compatability/node14/tsconfig.json b/experimental/backwards-compatability/node14/tsconfig.json index 93d86afe21..5151549589 100644 --- a/experimental/backwards-compatability/node14/tsconfig.json +++ b/experimental/backwards-compatability/node14/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../tsconfig.es5.json", + "extends": "../../../tsconfig.base.es5.json", "compilerOptions": { "rootDir": ".", "outDir": "build" diff --git a/experimental/backwards-compatability/node16/tsconfig.json b/experimental/backwards-compatability/node16/tsconfig.json index 93d86afe21..5151549589 100644 --- a/experimental/backwards-compatability/node16/tsconfig.json +++ b/experimental/backwards-compatability/node16/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../tsconfig.es5.json", + "extends": "../../../tsconfig.base.es5.json", "compilerOptions": { "rootDir": ".", "outDir": "build" diff --git a/experimental/packages/otlp-grpc-exporter-base/tsconfig.json b/experimental/packages/otlp-grpc-exporter-base/tsconfig.json index 417e1ad468..bbfbbc57b0 100644 --- a/experimental/packages/otlp-grpc-exporter-base/tsconfig.json +++ b/experimental/packages/otlp-grpc-exporter-base/tsconfig.json @@ -6,6 +6,7 @@ }, "include": [ "src/**/*.ts", + "src/generated/*.js", "test/**/*.ts" ], "references": [ diff --git a/scripts/update-ts-configs.js b/scripts/update-ts-configs.js index bbe3bd2b35..0242a54668 100644 --- a/scripts/update-ts-configs.js +++ b/scripts/update-ts-configs.js @@ -247,12 +247,7 @@ function resolvePackageMeta(pkgDir) { } function readAndMaybeMergeTsConfig(tsconfigPath, updates) { - let tsconfig; - try { - tsconfig = readJSON(tsconfigPath); - } catch { - return updates; - } + const tsconfig = readJSON(tsconfigPath); updates = mergeTsConfig(tsconfig, updates); return updates; } @@ -284,8 +279,11 @@ function hasEsTargets(pjson) { function readJSON(filepath) { const fileContent = fs.readFileSync(filepath, 'utf8'); - const json = JSON.parse(fileContent); - return json; + try { + return JSON.parse(fileContent); + } catch (e) { + throw new Error(`Invalid JSON ${filepath}: ${e.message}`); + } } function writeJSON(filepath, content, dry) { diff --git a/tsconfig.es5.json b/tsconfig.base.es5.json similarity index 100% rename from tsconfig.es5.json rename to tsconfig.base.es5.json diff --git a/tsconfig.base.esm.json b/tsconfig.base.esm.json index e65651a426..d63786958e 100644 --- a/tsconfig.base.esm.json +++ b/tsconfig.base.esm.json @@ -1,5 +1,5 @@ { - "extends": "./tsconfig.es5.json", + "extends": "./tsconfig.base.es5.json", "compilerOptions": { "module": "ES6", "moduleResolution": "node" From 461dc4ba9ed486b41aa83e804efacb55fccd29ab Mon Sep 17 00:00:00 2001 From: Marc Pichler Date: Mon, 6 Feb 2023 22:55:01 +0100 Subject: [PATCH 05/37] fix(sdk-metrics): fix flaky LastValueAggregator test by using fake timer (#3587) * fix(sdk-metrics): fix flaky LastValueAggregator test by using fake timers * fix(changelog): add changelog entry. * fix(sdk-metrics): change LastValueAggregation timer increment to 100ms --- CHANGELOG.md | 2 ++ .../test/aggregator/LastValue.test.ts | 23 ++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33c3c17e8d..8184b771a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/ ### :house: (Internal) +* fix(sdk-metrics): ix flaky LastValueAggregator test by using fake timer [#3587](https://github.com/open-telemetry/opentelemetry-js/pull/3587) @pichlermarc + ## 1.9.1 ### :bug: (Bug Fix) diff --git a/packages/sdk-metrics/test/aggregator/LastValue.test.ts b/packages/sdk-metrics/test/aggregator/LastValue.test.ts index 206b4c4acc..2fd35156c5 100644 --- a/packages/sdk-metrics/test/aggregator/LastValue.test.ts +++ b/packages/sdk-metrics/test/aggregator/LastValue.test.ts @@ -16,15 +16,26 @@ import { HrTime } from '@opentelemetry/api'; import * as assert from 'assert'; +import * as sinon from 'sinon'; import { AggregationTemporality } from '../../src'; import { LastValueAccumulation, LastValueAggregator, } from '../../src/aggregator'; import { MetricData, DataPointType } from '../../src/export/MetricData'; -import { commonValues, defaultInstrumentDescriptor, sleep } from '../util'; +import { commonValues, defaultInstrumentDescriptor } from '../util'; describe('LastValueAggregator', () => { + let clock: sinon.SinonFakeTimers; + + beforeEach(() => { + clock = sinon.useFakeTimers(); + }); + + afterEach(() => { + sinon.restore(); + }); + describe('createAccumulation', () => { it('no exceptions on createAccumulation', () => { const aggregator = new LastValueAggregator(); @@ -47,16 +58,16 @@ describe('LastValueAggregator', () => { assert.deepStrictEqual(aggregator.merge(prev, delta), expected); }); - it('return the newly sampled accumulation', async () => { + it('return the newly sampled accumulation', () => { const aggregator = new LastValueAggregator(); const accumulation1 = aggregator.createAccumulation([0, 0]); const accumulation2 = aggregator.createAccumulation([1, 1]); accumulation1.record(2); - await sleep(1); + clock.tick(100); accumulation2.record(3); // refresh the accumulation1 - await sleep(1); + clock.tick(100); accumulation1.record(4); assert.deepStrictEqual( @@ -92,7 +103,7 @@ describe('LastValueAggregator', () => { assert.deepStrictEqual(aggregator.diff(prev, curr), expected); }); - it('return the newly sampled accumulation', async () => { + it('return the newly sampled accumulation', () => { const aggregator = new LastValueAggregator(); const accumulation1 = aggregator.createAccumulation([0, 0]); const accumulation2 = aggregator.createAccumulation([1, 1]); @@ -100,7 +111,7 @@ describe('LastValueAggregator', () => { accumulation1.record(2); accumulation2.record(3); // refresh the accumulation1 - await sleep(1); + clock.tick(100); accumulation1.record(4); assert.deepStrictEqual( From ae3b4282f814c437b0d55a012ad6ada544cf62ea Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Tue, 7 Feb 2023 15:53:12 +0000 Subject: [PATCH 06/37] fix(deps): update dependency require-in-the-middle to v6 (#3584) Co-authored-by: Daniel Dyla --- .../packages/opentelemetry-instrumentation/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/packages/opentelemetry-instrumentation/package.json b/experimental/packages/opentelemetry-instrumentation/package.json index 05cc761e75..ca15993c47 100644 --- a/experimental/packages/opentelemetry-instrumentation/package.json +++ b/experimental/packages/opentelemetry-instrumentation/package.json @@ -68,7 +68,7 @@ "url": "https://github.com/open-telemetry/opentelemetry-js/issues" }, "dependencies": { - "require-in-the-middle": "^5.0.3", + "require-in-the-middle": "^6.0.0", "semver": "^7.3.2", "shimmer": "^1.2.1" }, From 92fbec99ec35f6d2db351b3c4fec26bd4d1c86a3 Mon Sep 17 00:00:00 2001 From: Martin Kuba Date: Tue, 7 Feb 2023 10:23:05 -0800 Subject: [PATCH 07/37] Separate Events API from Logs API (#3550) * added events API package * removed events from Logs API * updated logs tests * added tooling and tests to api-events * fixed package name, updated changelog, updated tsconfig * lint * added back precompile script, minor updates * tsconfig updates * changed logger to emitter in a test * added domain as a required parameter for creating an emitter, removed from Event * lint * Update experimental/packages/api-events/package.json Co-authored-by: Chengzhong Wu --------- Co-authored-by: Daniel Dyla Co-authored-by: Nev <54870357+MSNev@users.noreply.github.com> Co-authored-by: Chengzhong Wu --- CHANGELOG.md | 1 + .../packages/api-events/.eslintignore | 1 + experimental/packages/api-events/.eslintrc.js | 8 + experimental/packages/api-events/LICENSE | 201 ++++++++++++++++++ experimental/packages/api-events/README.md | 60 ++++++ .../packages/api-events/karma.conf.js | 24 +++ experimental/packages/api-events/package.json | 87 ++++++++ .../api-events/src/NoopEventEmitter.ts | 22 ++ .../src/NoopEventEmitterProvider.ts | 33 +++ .../packages/api-events/src/api/events.ts | 92 ++++++++ experimental/packages/api-events/src/index.ts | 23 ++ .../api-events/src/internal/global-utils.ts | 55 +++++ .../src/platform/browser/globalThis.ts | 39 ++++ .../api-events/src/platform/browser/index.ts | 17 ++ .../packages/api-events/src/platform/index.ts | 17 ++ .../src/platform/node/globalThis.ts | 19 ++ .../api-events/src/platform/node/index.ts | 17 ++ .../src/types/Event.ts} | 7 +- .../api-events/src/types/EventEmitter.ts | 26 +++ .../src/types/EventEmitterOptions.ts | 30 +++ .../src/types/EventEmitterProvider.ts | 40 ++++ .../packages/api-events/test/api/api.test.ts | 71 +++++++ .../packages/api-events/test/index-webpack.ts | 20 ++ .../api-events/test/internal/global.test.ts | 82 +++++++ .../noop-event-emitter-provider.test.ts | 42 ++++ .../noop-event-emitter.test.ts | 39 ++++ .../packages/api-events/tsconfig.esm.json | 16 ++ .../packages/api-events/tsconfig.esnext.json | 16 ++ .../packages/api-events/tsconfig.json | 17 ++ experimental/packages/api-logs/README.md | 7 +- experimental/packages/api-logs/package.json | 1 - .../packages/api-logs/src/NoopLogger.ts | 4 +- experimental/packages/api-logs/src/index.ts | 1 - .../packages/api-logs/src/types/Logger.ts | 10 +- .../api-logs/src/types/LoggerOptions.ts | 12 -- .../noop-implementations/noop-logger.test.ts | 9 +- tsconfig.esm.json | 3 + tsconfig.esnext.json | 3 + tsconfig.json | 4 + 39 files changed, 1132 insertions(+), 44 deletions(-) create mode 100644 experimental/packages/api-events/.eslintignore create mode 100644 experimental/packages/api-events/.eslintrc.js create mode 100644 experimental/packages/api-events/LICENSE create mode 100644 experimental/packages/api-events/README.md create mode 100644 experimental/packages/api-events/karma.conf.js create mode 100644 experimental/packages/api-events/package.json create mode 100644 experimental/packages/api-events/src/NoopEventEmitter.ts create mode 100644 experimental/packages/api-events/src/NoopEventEmitterProvider.ts create mode 100644 experimental/packages/api-events/src/api/events.ts create mode 100644 experimental/packages/api-events/src/index.ts create mode 100644 experimental/packages/api-events/src/internal/global-utils.ts create mode 100644 experimental/packages/api-events/src/platform/browser/globalThis.ts create mode 100644 experimental/packages/api-events/src/platform/browser/index.ts create mode 100644 experimental/packages/api-events/src/platform/index.ts create mode 100644 experimental/packages/api-events/src/platform/node/globalThis.ts create mode 100644 experimental/packages/api-events/src/platform/node/index.ts rename experimental/packages/{api-logs/src/types/LogEvent.ts => api-events/src/types/Event.ts} (92%) create mode 100644 experimental/packages/api-events/src/types/EventEmitter.ts create mode 100644 experimental/packages/api-events/src/types/EventEmitterOptions.ts create mode 100644 experimental/packages/api-events/src/types/EventEmitterProvider.ts create mode 100644 experimental/packages/api-events/test/api/api.test.ts create mode 100644 experimental/packages/api-events/test/index-webpack.ts create mode 100644 experimental/packages/api-events/test/internal/global.test.ts create mode 100644 experimental/packages/api-events/test/noop-implementations/noop-event-emitter-provider.test.ts create mode 100644 experimental/packages/api-events/test/noop-implementations/noop-event-emitter.test.ts create mode 100644 experimental/packages/api-events/tsconfig.esm.json create mode 100644 experimental/packages/api-events/tsconfig.esnext.json create mode 100644 experimental/packages/api-events/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 8184b771a5..c482e93702 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/ ### :rocket: (Enhancement) +* feat (api-logs): separate Events API into its own package [3550](https://github.com/open-telemetry/opentelemetry-js/pull/3550) @martinkuba * feat(sdk-metrics): apply binary search in histogram recording [#3539](https://github.com/open-telemetry/opentelemetry-js/pull/3539) @legendecas ### :bug: (Bug Fix) diff --git a/experimental/packages/api-events/.eslintignore b/experimental/packages/api-events/.eslintignore new file mode 100644 index 0000000000..378eac25d3 --- /dev/null +++ b/experimental/packages/api-events/.eslintignore @@ -0,0 +1 @@ +build diff --git a/experimental/packages/api-events/.eslintrc.js b/experimental/packages/api-events/.eslintrc.js new file mode 100644 index 0000000000..7654abb6ac --- /dev/null +++ b/experimental/packages/api-events/.eslintrc.js @@ -0,0 +1,8 @@ +module.exports = { + "env": { + "mocha": true, + "commonjs": true, + "shared-node-browser": true + }, + ...require('../../../eslint.config.js') +} diff --git a/experimental/packages/api-events/LICENSE b/experimental/packages/api-events/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/experimental/packages/api-events/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://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. diff --git a/experimental/packages/api-events/README.md b/experimental/packages/api-events/README.md new file mode 100644 index 0000000000..3aff88eff6 --- /dev/null +++ b/experimental/packages/api-events/README.md @@ -0,0 +1,60 @@ +# OpenTelemetry API for JavaScript + +[![NPM Published Version][npm-img]][npm-url] +[![Apache License][license-image]][license-image] + +This package provides everything needed to interact with the unstable OpenTelemetry Events API, including all TypeScript interfaces, enums, and no-op implementations. It is intended for use both on the server and in the browser. + +## Beta Software - Use at your own risk + +The events API is considered alpha software and there is no guarantee of stability or long-term support. When the API is stabilized, it will be made available and supported long-term in the `@opentelemetry/api` package and this package will be deprecated. + +## Quick Start + +Purposefully left blank until SDK is available. + +## Version Compatibility + +Because the npm installer and node module resolution algorithm could potentially allow two or more copies of any given package to exist within the same `node_modules` structure, the OpenTelemetry API takes advantage of a variable on the `global` object to store the global API. When an API method in the API package is called, it checks if this `global` API exists and proxies calls to it if and only if it is a compatible API version. This means if a package has a dependency on an OpenTelemetry API version which is not compatible with the API used by the end user, the package will receive a no-op implementation of the API. + +## Advanced Use + +### API Methods + +If you are writing an instrumentation library, or prefer to call the API methods directly rather than using the `register` method on the Tracer/Meter/Logger Provider, OpenTelemetry provides direct access to the underlying API methods through the `@opentelemetry/api-events` package. API entry points are defined as global singleton objects `trace`, `metrics`, `logs`, `events`, `propagation`, and `context` which contain methods used to initialize SDK implementations and acquire resources from the API. + +- [Events API Documentation][events-api-docs] + +```javascript +const api = require("@opentelemetry/api-events"); + +/* A specific implementation of EventEmitterProvider comes from an SDK */ +const eventEmitterProvider = createEventEmitterProvider(); + +/* Initialize EventEmitterProvider */ +api.events.setGlobalEventEmitterProvider(eventEmitterProvider); +/* returns eventEmitterProvider (no-op if a working provider has not been initialized) */ +api.events.getEventEmitterProvider(); +/* returns an event emitter from the registered global event emitter provider (no-op if a working provider has not been initialized) */ +const eventEmitter = api.events.getEventEmitter(name, version); + +// logging an event in an instrumentation library +eventEmitter.emit({ name: 'event-name', domain: 'event-domain' }); +``` + +## Useful links + +- For more information on OpenTelemetry, visit: +- For more about OpenTelemetry JavaScript: +- For help or feedback on this project, join us in [GitHub Discussions][discussions-url] + +## License + +Apache 2.0 - See [LICENSE][license-url] for more information. + +[discussions-url]: https://github.com/open-telemetry/opentelemetry-js/discussions +[license-url]: https://github.com/open-telemetry/opentelemetry-js/blob/main/LICENSE +[license-image]: https://img.shields.io/badge/license-Apache_2.0-green.svg?style=flat +[npm-url]: https://www.npmjs.com/package/@opentelemetry/api-logs +[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Fapi-logs.svg +[logs-api-docs]: https://open-telemetry.github.io/opentelemetry-js/modules/_opentelemetry_api_logs.html diff --git a/experimental/packages/api-events/karma.conf.js b/experimental/packages/api-events/karma.conf.js new file mode 100644 index 0000000000..6174839d65 --- /dev/null +++ b/experimental/packages/api-events/karma.conf.js @@ -0,0 +1,24 @@ +/*! + * 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 + * + * http://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 karmaWebpackConfig = require('../../../karma.webpack'); +const karmaBaseConfig = require('../../../karma.base'); + +module.exports = (config) => { + config.set(Object.assign({}, karmaBaseConfig, { + webpack: karmaWebpackConfig + })) +}; diff --git a/experimental/packages/api-events/package.json b/experimental/packages/api-events/package.json new file mode 100644 index 0000000000..da3732de78 --- /dev/null +++ b/experimental/packages/api-events/package.json @@ -0,0 +1,87 @@ +{ + "name": "@opentelemetry/api-events", + "version": "0.35.1", + "description": "Public events API for OpenTelemetry", + "main": "build/src/index.js", + "module": "build/esm/index.js", + "esnext": "build/esnext/index.js", + "types": "build/src/index.d.ts", + "browser": { + "./src/platform/index.ts": "./src/platform/browser/index.ts", + "./build/esm/platform/index.js": "./build/esm/platform/browser/index.js", + "./build/esnext/platform/index.js": "./build/esnext/platform/browser/index.js", + "./build/src/platform/index.js": "./build/src/platform/browser/index.js" + }, + "repository": "open-telemetry/opentelemetry-js", + "scripts": { + "prepublishOnly": "npm run compile", + "compile": "tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json", + "clean": "tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json", + "test": "nyc ts-mocha -p tsconfig.json test/**/*.test.ts", + "test:browser": "nyc karma start --single-run", + "codecov": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../../", + "codecov:browser": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../../", + "build": "npm run compile", + "lint": "eslint . --ext .ts", + "lint:fix": "eslint . --ext .ts --fix", + "version": "node ../../../scripts/version-update.js", + "watch": "tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json", + "precompile": "lerna run version --scope $(npm pkg get name) --include-dependencies", + "prewatch": "node ../../../scripts/version-update.js" + }, + "keywords": [ + "opentelemetry", + "nodejs", + "browser", + "profiling", + "events", + "stats", + "monitoring" + ], + "author": "OpenTelemetry Authors", + "license": "Apache-2.0", + "engines": { + "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", + "doc", + "LICENSE", + "README.md" + ], + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "devDependencies": { + "@types/mocha": "10.0.0", + "@types/node": "18.6.5", + "@types/webpack-env": "1.16.3", + "codecov": "3.8.3", + "istanbul-instrumenter-loader": "3.0.1", + "karma": "6.3.16", + "karma-chrome-launcher": "3.1.0", + "karma-coverage-istanbul-reporter": "3.0.3", + "karma-mocha": "2.0.1", + "karma-spec-reporter": "0.0.32", + "karma-webpack": "4.0.2", + "mocha": "10.0.0", + "nyc": "15.1.0", + "ts-loader": "8.4.0", + "ts-mocha": "10.0.0", + "typescript": "4.4.4", + "webpack": "4.46.0" + }, + "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/api-events", + "sideEffects": false +} diff --git a/experimental/packages/api-events/src/NoopEventEmitter.ts b/experimental/packages/api-events/src/NoopEventEmitter.ts new file mode 100644 index 0000000000..b576e02bcf --- /dev/null +++ b/experimental/packages/api-events/src/NoopEventEmitter.ts @@ -0,0 +1,22 @@ +/* + * 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 { EventEmitter } from './types/EventEmitter'; +import { Event } from './types/Event'; + +export class NoopEventEmitter implements EventEmitter { + emit(_event: Event): void {} +} diff --git a/experimental/packages/api-events/src/NoopEventEmitterProvider.ts b/experimental/packages/api-events/src/NoopEventEmitterProvider.ts new file mode 100644 index 0000000000..95731b0a8d --- /dev/null +++ b/experimental/packages/api-events/src/NoopEventEmitterProvider.ts @@ -0,0 +1,33 @@ +/* + * 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 { EventEmitterProvider } from './types/EventEmitterProvider'; +import { EventEmitter } from './types/EventEmitter'; +import { EventEmitterOptions } from './types/EventEmitterOptions'; +import { NoopEventEmitter } from './NoopEventEmitter'; + +export class NoopEventEmitterProvider implements EventEmitterProvider { + getEventEmitter( + _name: string, + _domain: string, + _version?: string | undefined, + _options?: EventEmitterOptions | undefined + ): EventEmitter { + return new NoopEventEmitter(); + } +} + +export const NOOP_EVENT_EMITTER_PROVIDER = new NoopEventEmitterProvider(); diff --git a/experimental/packages/api-events/src/api/events.ts b/experimental/packages/api-events/src/api/events.ts new file mode 100644 index 0000000000..f17c93a2d3 --- /dev/null +++ b/experimental/packages/api-events/src/api/events.ts @@ -0,0 +1,92 @@ +/* + * 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 { + API_BACKWARDS_COMPATIBILITY_VERSION, + GLOBAL_EVENTS_API_KEY, + _global, + makeGetter, +} from '../internal/global-utils'; +import { EventEmitterProvider } from '../types/EventEmitterProvider'; +import { NOOP_EVENT_EMITTER_PROVIDER } from '../NoopEventEmitterProvider'; +import { EventEmitter } from '../types/EventEmitter'; +import { EventEmitterOptions } from '../types/EventEmitterOptions'; + +export class EventsAPI { + private static _instance?: EventsAPI; + + private constructor() {} + + public static getInstance(): EventsAPI { + if (!this._instance) { + this._instance = new EventsAPI(); + } + + return this._instance; + } + + public setGlobalEventEmitterProvider( + provider: EventEmitterProvider + ): EventEmitterProvider { + if (_global[GLOBAL_EVENTS_API_KEY]) { + return this.getEventEmitterProvider(); + } + + _global[GLOBAL_EVENTS_API_KEY] = makeGetter( + API_BACKWARDS_COMPATIBILITY_VERSION, + provider, + NOOP_EVENT_EMITTER_PROVIDER + ); + + return provider; + } + + /** + * Returns the global event emitter provider. + * + * @returns EventEmitterProvider + */ + public getEventEmitterProvider(): EventEmitterProvider { + return ( + _global[GLOBAL_EVENTS_API_KEY]?.(API_BACKWARDS_COMPATIBILITY_VERSION) ?? + NOOP_EVENT_EMITTER_PROVIDER + ); + } + + /** + * Returns a event emitter from the global event emitter provider. + * + * @returns EventEmitter + */ + public getEventEmitter( + name: string, + domain: string, + version?: string, + options?: EventEmitterOptions + ): EventEmitter { + return this.getEventEmitterProvider().getEventEmitter( + name, + domain, + version, + options + ); + } + + /** Remove the global event emitter provider */ + public disable(): void { + delete _global[GLOBAL_EVENTS_API_KEY]; + } +} diff --git a/experimental/packages/api-events/src/index.ts b/experimental/packages/api-events/src/index.ts new file mode 100644 index 0000000000..63fadffd48 --- /dev/null +++ b/experimental/packages/api-events/src/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './types/EventEmitter'; +export * from './types/EventEmitterProvider'; +export * from './types/Event'; +export * from './types/EventEmitterOptions'; + +import { EventsAPI } from './api/events'; +export const events = EventsAPI.getInstance(); diff --git a/experimental/packages/api-events/src/internal/global-utils.ts b/experimental/packages/api-events/src/internal/global-utils.ts new file mode 100644 index 0000000000..d58b890127 --- /dev/null +++ b/experimental/packages/api-events/src/internal/global-utils.ts @@ -0,0 +1,55 @@ +/* + * 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 { EventEmitterProvider } from '../types/EventEmitterProvider'; +import { _globalThis } from '../platform'; + +export const GLOBAL_EVENTS_API_KEY = Symbol.for( + 'io.opentelemetry.js.api.events' +); + +type Get = (version: number) => T; +type OtelGlobal = Partial<{ + [GLOBAL_EVENTS_API_KEY]: Get; +}>; + +export const _global = _globalThis as OtelGlobal; + +/** + * Make a function which accepts a version integer and returns the instance of an API if the version + * is compatible, or a fallback version (usually NOOP) if it is not. + * + * @param requiredVersion Backwards compatibility version which is required to return the instance + * @param instance Instance which should be returned if the required version is compatible + * @param fallback Fallback instance, usually NOOP, which will be returned if the required version is not compatible + */ +export function makeGetter( + requiredVersion: number, + instance: T, + fallback: T +): Get { + return (version: number): T => + version === requiredVersion ? instance : fallback; +} + +/** + * A number which should be incremented each time a backwards incompatible + * change is made to the API. This number is used when an API package + * attempts to access the global API to ensure it is getting a compatible + * version. If the global API is not compatible with the API package + * attempting to get it, a NOOP API implementation will be returned. + */ +export const API_BACKWARDS_COMPATIBILITY_VERSION = 1; diff --git a/experimental/packages/api-events/src/platform/browser/globalThis.ts b/experimental/packages/api-events/src/platform/browser/globalThis.ts new file mode 100644 index 0000000000..e8a79351b2 --- /dev/null +++ b/experimental/packages/api-events/src/platform/browser/globalThis.ts @@ -0,0 +1,39 @@ +/* + * 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. + */ + +// Updates to this file should also be replicated to @opentelemetry/api and +// @opentelemetry/core too. + +/** + * - globalThis (New standard) + * - self (Will return the current window instance for supported browsers) + * - window (fallback for older browser implementations) + * - global (NodeJS implementation) + * - (When all else fails) + */ + +/** only globals that common to node and browsers are allowed */ +// eslint-disable-next-line node/no-unsupported-features/es-builtins, no-undef +export const _globalThis: typeof globalThis = + typeof globalThis === 'object' + ? globalThis + : typeof self === 'object' + ? self + : typeof window === 'object' + ? window + : typeof global === 'object' + ? global + : ({} as typeof globalThis); diff --git a/experimental/packages/api-events/src/platform/browser/index.ts b/experimental/packages/api-events/src/platform/browser/index.ts new file mode 100644 index 0000000000..e9d6ebed71 --- /dev/null +++ b/experimental/packages/api-events/src/platform/browser/index.ts @@ -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. + */ + +export * from './globalThis'; diff --git a/experimental/packages/api-events/src/platform/index.ts b/experimental/packages/api-events/src/platform/index.ts new file mode 100644 index 0000000000..cdaf8858ce --- /dev/null +++ b/experimental/packages/api-events/src/platform/index.ts @@ -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. + */ + +export * from './node'; diff --git a/experimental/packages/api-events/src/platform/node/globalThis.ts b/experimental/packages/api-events/src/platform/node/globalThis.ts new file mode 100644 index 0000000000..36e97e2732 --- /dev/null +++ b/experimental/packages/api-events/src/platform/node/globalThis.ts @@ -0,0 +1,19 @@ +/* + * 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. + */ + +/** only globals that common to node and browsers are allowed */ +// eslint-disable-next-line node/no-unsupported-features/es-builtins +export const _globalThis = typeof globalThis === 'object' ? globalThis : global; diff --git a/experimental/packages/api-events/src/platform/node/index.ts b/experimental/packages/api-events/src/platform/node/index.ts new file mode 100644 index 0000000000..e9d6ebed71 --- /dev/null +++ b/experimental/packages/api-events/src/platform/node/index.ts @@ -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. + */ + +export * from './globalThis'; diff --git a/experimental/packages/api-logs/src/types/LogEvent.ts b/experimental/packages/api-events/src/types/Event.ts similarity index 92% rename from experimental/packages/api-logs/src/types/LogEvent.ts rename to experimental/packages/api-events/src/types/Event.ts index 7b37572c6c..03e4ef89db 100644 --- a/experimental/packages/api-logs/src/types/LogEvent.ts +++ b/experimental/packages/api-events/src/types/Event.ts @@ -16,7 +16,7 @@ import { Attributes } from '@opentelemetry/api'; -export interface LogEvent { +export interface Event { /** * The time when the event occurred as UNIX Epoch time in nanoseconds. */ @@ -27,11 +27,6 @@ export interface LogEvent { */ name: string; - /** - * The domain the event belongs to. - */ - domain?: string; - /** * Additional attributes that describe the event. */ diff --git a/experimental/packages/api-events/src/types/EventEmitter.ts b/experimental/packages/api-events/src/types/EventEmitter.ts new file mode 100644 index 0000000000..1222339516 --- /dev/null +++ b/experimental/packages/api-events/src/types/EventEmitter.ts @@ -0,0 +1,26 @@ +/* + * 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 { Event } from './Event'; + +export interface EventEmitter { + /** + * Emit an event. This method should only be used by instrumentations emitting events. + * + * @param event + */ + emit(event: Event): void; +} diff --git a/experimental/packages/api-events/src/types/EventEmitterOptions.ts b/experimental/packages/api-events/src/types/EventEmitterOptions.ts new file mode 100644 index 0000000000..f5b983884e --- /dev/null +++ b/experimental/packages/api-events/src/types/EventEmitterOptions.ts @@ -0,0 +1,30 @@ +/* + * 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 { Attributes } from '@opentelemetry/api'; + +export interface EventEmitterOptions { + /** + * The schemaUrl of the tracer or instrumentation library + * @default '' + */ + schemaUrl?: string; + + /** + * The instrumentation scope attributes to associate with emitted telemetry + */ + scopeAttributes?: Attributes; +} diff --git a/experimental/packages/api-events/src/types/EventEmitterProvider.ts b/experimental/packages/api-events/src/types/EventEmitterProvider.ts new file mode 100644 index 0000000000..276b2e627d --- /dev/null +++ b/experimental/packages/api-events/src/types/EventEmitterProvider.ts @@ -0,0 +1,40 @@ +/* + * 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 { EventEmitter } from './EventEmitter'; +import { EventEmitterOptions } from './EventEmitterOptions'; + +/** + * A registry for creating named {@link EventEmitter}s. + */ +export interface EventEmitterProvider { + /** + * Returns an EventEmitter, creating one if one with the given name, version, and + * schemaUrl pair is not already created. + * + * @param name The name of the event emitter or instrumentation library. + * @param domain The domain for events created by the event emitter. + * @param version The version of the event emitter or instrumentation library. + * @param options The options of the event emitter or instrumentation library. + * @returns EventEmitter An event emitter with the given name and version. + */ + getEventEmitter( + name: string, + domain: string, + version?: string, + options?: EventEmitterOptions + ): EventEmitter; +} diff --git a/experimental/packages/api-events/test/api/api.test.ts b/experimental/packages/api-events/test/api/api.test.ts new file mode 100644 index 0000000000..e091688ff8 --- /dev/null +++ b/experimental/packages/api-events/test/api/api.test.ts @@ -0,0 +1,71 @@ +/* + * 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 assert from 'assert'; +import { EventEmitter, events } from '../../src'; +import { NoopEventEmitter } from '../../src/NoopEventEmitter'; +import { NoopEventEmitterProvider } from '../../src/NoopEventEmitterProvider'; + +describe('API', () => { + const dummyEventEmitter = new NoopEventEmitter(); + + it('should expose a event emitter provider via getEventEmitterProvider', () => { + const provider = events.getEventEmitterProvider(); + assert.ok(provider); + assert.strictEqual(typeof provider, 'object'); + }); + + describe('GlobalEventEmitterProvider', () => { + beforeEach(() => { + events.disable(); + }); + + it('should use the global event emitter provider', () => { + events.setGlobalEventEmitterProvider(new TestEventEmitterProvider()); + const eventEmitter = events + .getEventEmitterProvider() + .getEventEmitter('name', 'domain'); + assert.deepStrictEqual(eventEmitter, dummyEventEmitter); + }); + + it('should not allow overriding global provider if already set', () => { + const provider1 = new TestEventEmitterProvider(); + const provider2 = new TestEventEmitterProvider(); + events.setGlobalEventEmitterProvider(provider1); + assert.equal(events.getEventEmitterProvider(), provider1); + events.setGlobalEventEmitterProvider(provider2); + assert.equal(events.getEventEmitterProvider(), provider1); + }); + }); + + describe('getEventEmitter', () => { + beforeEach(() => { + events.disable(); + }); + + it('should return a event emitter instance from global provider', () => { + events.setGlobalEventEmitterProvider(new TestEventEmitterProvider()); + const eventEmitter = events.getEventEmitter('myEventEmitter', 'domain'); + assert.deepStrictEqual(eventEmitter, dummyEventEmitter); + }); + }); + + class TestEventEmitterProvider extends NoopEventEmitterProvider { + override getEventEmitter(): EventEmitter { + return dummyEventEmitter; + } + } +}); diff --git a/experimental/packages/api-events/test/index-webpack.ts b/experimental/packages/api-events/test/index-webpack.ts new file mode 100644 index 0000000000..061a48ccfa --- /dev/null +++ b/experimental/packages/api-events/test/index-webpack.ts @@ -0,0 +1,20 @@ +/* + * 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 testsContext = require.context('.', true, /test$/); +testsContext.keys().forEach(testsContext); + +const srcContext = require.context('.', true, /src$/); +srcContext.keys().forEach(srcContext); diff --git a/experimental/packages/api-events/test/internal/global.test.ts b/experimental/packages/api-events/test/internal/global.test.ts new file mode 100644 index 0000000000..0a4eb04f57 --- /dev/null +++ b/experimental/packages/api-events/test/internal/global.test.ts @@ -0,0 +1,82 @@ +/* + * 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 assert from 'assert'; +import { + _global, + GLOBAL_EVENTS_API_KEY, +} from '../../src/internal/global-utils'; +import { NoopEventEmitterProvider } from '../../src/NoopEventEmitterProvider'; + +const api1 = require('../../src') as typeof import('../../src'); + +// clear cache and load a second instance of the api +for (const key of Object.keys(require.cache)) { + delete require.cache[key]; +} +const api2 = require('../../src') as typeof import('../../src'); + +describe('Global Utils', () => { + // prove they are separate instances + assert.notStrictEqual(api1, api2); + // that return separate noop instances to start + assert.notStrictEqual( + api1.events.getEventEmitterProvider(), + api2.events.getEventEmitterProvider() + ); + + beforeEach(() => { + api1.events.disable(); + api2.events.disable(); + }); + + it('should change the global event emitter provider', () => { + const original = api1.events.getEventEmitterProvider(); + const newEventEmitterProvider = new NoopEventEmitterProvider(); + api1.events.setGlobalEventEmitterProvider(newEventEmitterProvider); + assert.notStrictEqual(api1.events.getEventEmitterProvider(), original); + assert.strictEqual( + api1.events.getEventEmitterProvider(), + newEventEmitterProvider + ); + }); + + it('should load an instance from one which was set in the other', () => { + api1.events.setGlobalEventEmitterProvider(new NoopEventEmitterProvider()); + assert.strictEqual( + api1.events.getEventEmitterProvider(), + api2.events.getEventEmitterProvider() + ); + }); + + it('should disable both if one is disabled', () => { + const original = api1.events.getEventEmitterProvider(); + + api1.events.setGlobalEventEmitterProvider(new NoopEventEmitterProvider()); + + assert.notStrictEqual(original, api1.events.getEventEmitterProvider()); + api2.events.disable(); + assert.strictEqual(original, api1.events.getEventEmitterProvider()); + }); + + it('should return the module NoOp implementation if the version is a mismatch', () => { + const original = api1.events.getEventEmitterProvider(); + api1.events.setGlobalEventEmitterProvider(new NoopEventEmitterProvider()); + const afterSet = _global[GLOBAL_EVENTS_API_KEY]!(-1); + + assert.strictEqual(original, afterSet); + }); +}); diff --git a/experimental/packages/api-events/test/noop-implementations/noop-event-emitter-provider.test.ts b/experimental/packages/api-events/test/noop-implementations/noop-event-emitter-provider.test.ts new file mode 100644 index 0000000000..ad88d37058 --- /dev/null +++ b/experimental/packages/api-events/test/noop-implementations/noop-event-emitter-provider.test.ts @@ -0,0 +1,42 @@ +/* + * 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 assert from 'assert'; +import { NoopEventEmitter } from '../../src/NoopEventEmitter'; +import { NoopEventEmitterProvider } from '../../src/NoopEventEmitterProvider'; + +describe('NoopLoggerProvider', () => { + it('should not crash', () => { + const eventEmitterProvider = new NoopEventEmitterProvider(); + + assert.ok( + eventEmitterProvider.getEventEmitter('emitter-name', 'domain') instanceof + NoopEventEmitter + ); + assert.ok( + eventEmitterProvider.getEventEmitter( + 'emitter-name', + 'domain', + 'v1' + ) instanceof NoopEventEmitter + ); + assert.ok( + eventEmitterProvider.getEventEmitter('emitter-name', 'domain', 'v1', { + schemaUrl: 'https://opentelemetry.io/schemas/1.7.0', + }) instanceof NoopEventEmitter + ); + }); +}); diff --git a/experimental/packages/api-events/test/noop-implementations/noop-event-emitter.test.ts b/experimental/packages/api-events/test/noop-implementations/noop-event-emitter.test.ts new file mode 100644 index 0000000000..933a3e6e88 --- /dev/null +++ b/experimental/packages/api-events/test/noop-implementations/noop-event-emitter.test.ts @@ -0,0 +1,39 @@ +/* + * 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 assert from 'assert'; +import { NoopEventEmitter } from '../../src/NoopEventEmitter'; +import { NoopEventEmitterProvider } from '../../src/NoopEventEmitterProvider'; + +describe('NoopEventEmitter', () => { + it('constructor should not crash', () => { + const logger = new NoopEventEmitterProvider().getEventEmitter( + 'test-noop', + 'test-domain' + ); + assert(logger instanceof NoopEventEmitter); + }); + + it('calling emit should not crash', () => { + const emitter = new NoopEventEmitterProvider().getEventEmitter( + 'test-noop', + 'test-domain' + ); + emitter.emit({ + name: 'event name', + }); + }); +}); diff --git a/experimental/packages/api-events/tsconfig.esm.json b/experimental/packages/api-events/tsconfig.esm.json new file mode 100644 index 0000000000..f0383c0042 --- /dev/null +++ b/experimental/packages/api-events/tsconfig.esm.json @@ -0,0 +1,16 @@ +{ + "extends": "../../../tsconfig.base.esm.json", + "compilerOptions": { + "outDir": "build/esm", + "rootDir": "src", + "tsBuildInfoFile": "build/esm/tsconfig.esm.tsbuildinfo" + }, + "include": [ + "src/**/*.ts" + ], + "references": [ + { + "path": "../../../api" + } + ] +} diff --git a/experimental/packages/api-events/tsconfig.esnext.json b/experimental/packages/api-events/tsconfig.esnext.json new file mode 100644 index 0000000000..218899ff2d --- /dev/null +++ b/experimental/packages/api-events/tsconfig.esnext.json @@ -0,0 +1,16 @@ +{ + "extends": "../../../tsconfig.base.esnext.json", + "compilerOptions": { + "outDir": "build/esnext", + "rootDir": "src", + "tsBuildInfoFile": "build/esnext/tsconfig.esnext.tsbuildinfo" + }, + "include": [ + "src/**/*.ts" + ], + "references": [ + { + "path": "../../../api" + } + ] +} diff --git a/experimental/packages/api-events/tsconfig.json b/experimental/packages/api-events/tsconfig.json new file mode 100644 index 0000000000..5849e79c03 --- /dev/null +++ b/experimental/packages/api-events/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "build", + "rootDir": "." + }, + "files": [], + "include": [ + "src/**/*.ts", + "test/**/*.ts" + ], + "references": [ + { + "path": "../../../api" + } + ] +} diff --git a/experimental/packages/api-logs/README.md b/experimental/packages/api-logs/README.md index 1d5a17fa76..b36d5bf06e 100644 --- a/experimental/packages/api-logs/README.md +++ b/experimental/packages/api-logs/README.md @@ -38,11 +38,8 @@ api.logs.getLoggerProvider(); /* returns a logger from the registered global logger provider (no-op if a working provider has not been initialized) */ const logger = api.logs.getLogger(name, version); -// logging an event in an instrumentation library -logger.emitEvent({ name: 'event-name', domain: 'event-domain' }); - -// logging an event in a log appender -logger.emitLogRecord({ severityNumber: SeverityNumber.TRACE, body: 'log data' }); +// logging a log record in a log appender +logger.emit({ severityNumber: SeverityNumber.TRACE, body: 'log data' }); ``` ## Useful links diff --git a/experimental/packages/api-logs/package.json b/experimental/packages/api-logs/package.json index 7f6f846e2f..d4f54600c3 100644 --- a/experimental/packages/api-logs/package.json +++ b/experimental/packages/api-logs/package.json @@ -35,7 +35,6 @@ "browser", "profiling", "logs", - "events", "stats", "monitoring" ], diff --git a/experimental/packages/api-logs/src/NoopLogger.ts b/experimental/packages/api-logs/src/NoopLogger.ts index 3f2227154c..dab79439ee 100644 --- a/experimental/packages/api-logs/src/NoopLogger.ts +++ b/experimental/packages/api-logs/src/NoopLogger.ts @@ -15,10 +15,8 @@ */ import { Logger } from './types/Logger'; -import { LogEvent } from './types/LogEvent'; import { LogRecord } from './types/LogRecord'; export class NoopLogger implements Logger { - emitLogRecord(_logRecord: LogRecord): void {} - emitEvent(_event: LogEvent): void {} + emit(_logRecord: LogRecord): void {} } diff --git a/experimental/packages/api-logs/src/index.ts b/experimental/packages/api-logs/src/index.ts index cf22a2e5eb..ce158ba323 100644 --- a/experimental/packages/api-logs/src/index.ts +++ b/experimental/packages/api-logs/src/index.ts @@ -17,7 +17,6 @@ export * from './types/Logger'; export * from './types/LoggerProvider'; export * from './types/LogRecord'; -export * from './types/LogEvent'; export * from './types/LoggerOptions'; import { LogsAPI } from './api/logs'; diff --git a/experimental/packages/api-logs/src/types/Logger.ts b/experimental/packages/api-logs/src/types/Logger.ts index b30f3aff93..e6d63940aa 100644 --- a/experimental/packages/api-logs/src/types/Logger.ts +++ b/experimental/packages/api-logs/src/types/Logger.ts @@ -15,7 +15,6 @@ */ import { LogRecord } from './LogRecord'; -import { LogEvent } from './LogEvent'; export interface Logger { /** @@ -23,12 +22,5 @@ export interface Logger { * * @param logRecord */ - emitLogRecord(logRecord: LogRecord): void; - - /** - * Emit an event. This method should only be used by instrumentations emitting events. - * - * @param event - */ - emitEvent(event: LogEvent): void; + emit(logRecord: LogRecord): void; } diff --git a/experimental/packages/api-logs/src/types/LoggerOptions.ts b/experimental/packages/api-logs/src/types/LoggerOptions.ts index 9b2fe06fc8..9a1c6e7c0c 100644 --- a/experimental/packages/api-logs/src/types/LoggerOptions.ts +++ b/experimental/packages/api-logs/src/types/LoggerOptions.ts @@ -23,18 +23,6 @@ export interface LoggerOptions { */ schemaUrl?: string; - /** - * The default domain for events created by the Logger. - * - * The combination of event name and event domain uiquely identifies an event. - * By supplying an event domain, it is possible to use the same event name across - * different domains / use cases. - * - * The default domain can be overridden when emitting an individual event. - * @default '' - */ - eventDomain?: string; - /** * The instrumentation scope attributes to associate with emitted telemetry */ diff --git a/experimental/packages/api-logs/test/noop-implementations/noop-logger.test.ts b/experimental/packages/api-logs/test/noop-implementations/noop-logger.test.ts index 85537d59f0..52ac38976b 100644 --- a/experimental/packages/api-logs/test/noop-implementations/noop-logger.test.ts +++ b/experimental/packages/api-logs/test/noop-implementations/noop-logger.test.ts @@ -25,14 +25,9 @@ describe('NoopLogger', () => { assert(logger instanceof NoopLogger); }); - it('calling emitEvent should not crash', () => { + it('calling emit should not crash', () => { const logger = new NoopLoggerProvider().getLogger('test-noop'); - logger.emitEvent({ name: 'event-name', domain: 'event-domain' }); - }); - - it('calling emitLogRecord should not crash', () => { - const logger = new NoopLoggerProvider().getLogger('test-noop'); - logger.emitLogRecord({ + logger.emit({ severityNumber: SeverityNumber.TRACE, body: 'log body', }); diff --git a/tsconfig.esm.json b/tsconfig.esm.json index afd8742243..67713e33e4 100644 --- a/tsconfig.esm.json +++ b/tsconfig.esm.json @@ -5,6 +5,9 @@ { "path": "api/tsconfig.esm.json" }, + { + "path": "experimental/packages/api-events/tsconfig.esm.json" + }, { "path": "experimental/packages/api-logs/tsconfig.esm.json" }, diff --git a/tsconfig.esnext.json b/tsconfig.esnext.json index c826225f26..7d10cc5f52 100644 --- a/tsconfig.esnext.json +++ b/tsconfig.esnext.json @@ -5,6 +5,9 @@ { "path": "api/tsconfig.esnext.json" }, + { + "path": "experimental/packages/api-events/tsconfig.esnext.json" + }, { "path": "experimental/packages/api-logs/tsconfig.esnext.json" }, diff --git a/tsconfig.json b/tsconfig.json index 347ccca34d..1948d987b2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "entryPointStrategy": "packages", "entryPoints": [ "api", + "experimental/packages/api-events", "experimental/packages/api-logs", "experimental/packages/exporter-trace-otlp-grpc", "experimental/packages/exporter-trace-otlp-http", @@ -54,6 +55,9 @@ { "path": "api" }, + { + "path": "experimental/packages/api-events" + }, { "path": "experimental/packages/api-logs" }, From dd2af92054b46c0f314a2e857434d3e8c5df53fa Mon Sep 17 00:00:00 2001 From: Marc Pichler Date: Wed, 8 Feb 2023 13:35:42 +0100 Subject: [PATCH 08/37] fix(prometheus-exporter): add possibility to respond to errors returned by `server.listen()` (#3552) --- experimental/CHANGELOG.md | 2 ++ .../README.md | 2 +- .../src/PrometheusExporter.ts | 17 +++++++++------ .../test/PrometheusExporter.test.ts | 21 +++++++++++++++++++ 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 8d34640976..e2725a170a 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -10,6 +10,8 @@ All notable changes to experimental packages in this project will be documented ### :bug: (Bug Fix) +* fix(prometheus-exporter): add possibility to respond to errors returned by `server.listen()` [#3552](https://github.com/open-telemetry/opentelemetry-js/pull/3402) @pichlermarc + ### :books: (Refine Doc) ### :house: (Internal) diff --git a/experimental/packages/opentelemetry-exporter-prometheus/README.md b/experimental/packages/opentelemetry-exporter-prometheus/README.md index 97ea063d3c..d963acf877 100644 --- a/experimental/packages/opentelemetry-exporter-prometheus/README.md +++ b/experimental/packages/opentelemetry-exporter-prometheus/README.md @@ -25,7 +25,7 @@ const { PrometheusExporter } = require('@opentelemetry/exporter-prometheus'); const { MeterProvider } = require('@opentelemetry/sdk-metrics'); // Add your port and startServer to the Prometheus options -const options = {port: 9464, startServer: true}; +const options = {port: 9464}; const exporter = new PrometheusExporter(options); // Creates MeterProvider and installs the exporter as a MetricReader diff --git a/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusExporter.ts b/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusExporter.ts index 63c00c4d6f..2a7cb5fb43 100644 --- a/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusExporter.ts +++ b/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusExporter.ts @@ -54,7 +54,10 @@ export class PrometheusExporter extends MetricReader { * @param config Exporter configuration * @param callback Callback to be called after a server was started */ - constructor(config: ExporterConfig = {}, callback?: () => void) { + constructor( + config: ExporterConfig = {}, + callback: (error: Error | void) => void = () => {} + ) { super({ aggregationSelector: _instrumentType => Aggregation.Default(), aggregationTemporalitySelector: _instrumentType => @@ -86,9 +89,10 @@ export class PrometheusExporter extends MetricReader { ).replace(/^([^/])/, '/$1'); if (config.preventServerStart !== true) { - this.startServer() - .then(callback) - .catch(err => diag.error(err)); + this.startServer().then(callback, err => { + diag.error(err); + callback(err); + }); } else if (callback) { callback(); } @@ -137,7 +141,8 @@ export class PrometheusExporter extends MetricReader { * Starts the Prometheus export server */ startServer(): Promise { - return new Promise(resolve => { + return new Promise((resolve, reject) => { + this._server.once('error', reject); this._server.listen( { port: this._port, @@ -156,7 +161,7 @@ export class PrometheusExporter extends MetricReader { /** * Request handler that responds with the current state of metrics * @param _request Incoming HTTP request of server instance - * @param response HTTP response objet used to response to request + * @param response HTTP response object used to response to request */ public getMetricsRequestHandler( _request: IncomingMessage, diff --git a/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusExporter.test.ts b/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusExporter.test.ts index 0b0365b5e7..55061be083 100644 --- a/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusExporter.test.ts +++ b/experimental/packages/opentelemetry-exporter-prometheus/test/PrometheusExporter.test.ts @@ -74,6 +74,27 @@ describe('PrometheusExporter', () => { }); }); + it('should pass server error to callback when port is already in use', done => { + const firstExporter = new PrometheusExporter({}, error => { + if (error) { + // This should not happen as the port should not be already in use when the test starts. + done(error); + } + }); + const secondExporter = new PrometheusExporter({}, error => { + firstExporter + .shutdown() + .then(() => secondExporter.shutdown()) + .then(() => + done( + error + ? undefined + : 'Second exporter should respond with EADDRINUSE but did not pass it to callback' + ) + ); + }); + }); + it('should not start the server if preventServerStart is passed as an option', () => { const exporter = new PrometheusExporter({ preventServerStart: true }); assert.ok(exporter['_server'].listening === false); From cb88e4ea11895b463c1a33df8156d2907e79c830 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Wed, 8 Feb 2023 12:32:59 -0500 Subject: [PATCH 09/37] Add martin kuba to approvers list (#3594) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f1c4de9a46..482500bf5c 100644 --- a/README.md +++ b/README.md @@ -181,6 +181,7 @@ Approvers ([@open-telemetry/js-approvers](https://github.com/orgs/open-telemetry - [John Bley](https://github.com/johnbley), Splunk - [Marc Pichler](https://github.com/pichlermarc), Dynatrace - [Mark Wolff](https://github.com/markwolff), Microsoft +- [Martin Kuba](https://github.com/martinkuba), Lightstep - [Matthew Wear](https://github.com/mwear), LightStep - [Naseem K. Ullah](https://github.com/naseemkullah), Transit - [Neville Wylie](https://github.com/MSNev), Microsoft From c32a8457262102c4590693e9f8bf5cd7c5e161ce Mon Sep 17 00:00:00 2001 From: Amir Blum Date: Wed, 8 Feb 2023 21:40:19 +0200 Subject: [PATCH 10/37] Add haddasbronfman as approver (#3593) * Add haddasbronfman as approver * Delete performance.ts delete unrelated file * remove unrelated file --------- Co-authored-by: Daniel Dyla --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 482500bf5c..72c52f9b39 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,7 @@ We have a weekly SIG meeting! See the [community page](https://github.com/open-t Approvers ([@open-telemetry/js-approvers](https://github.com/orgs/open-telemetry/teams/javascript-approvers)): - [Gerhard Stöbich](https://github.com/Flarna), Dynatrace +- [Haddas Bronfman](https://github.com/haddasbronfman), Cisco - [John Bley](https://github.com/johnbley), Splunk - [Marc Pichler](https://github.com/pichlermarc), Dynatrace - [Mark Wolff](https://github.com/markwolff), Microsoft From 65e83d4f0a3fcae9115a62183108a21c08ec171b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Th=C3=A9riault?= <113933910+raphael-theriault-swi@users.noreply.github.com> Date: Thu, 9 Feb 2023 00:34:25 -0800 Subject: [PATCH 11/37] feat: support TraceState in SamplingResult (#3530) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daniel Dyla Co-authored-by: Gerhard Stöbich --- CHANGELOG.md | 1 + api/src/trace/SamplingResult.ts | 8 +++++++ .../src/Sampler.ts | 15 ++++++++++++- .../src/Tracer.ts | 2 ++ .../test/common/Tracer.test.ts | 21 ++++++++++++++++++- 5 files changed, 45 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c482e93702..8776a2cc89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/ * feat (api-logs): separate Events API into its own package [3550](https://github.com/open-telemetry/opentelemetry-js/pull/3550) @martinkuba * feat(sdk-metrics): apply binary search in histogram recording [#3539](https://github.com/open-telemetry/opentelemetry-js/pull/3539) @legendecas +* feat: support TraceState in SamplingResult [#3530](https://github.com/open-telemetry/opentelemetry-js/pull/3530) @raphael-theriault-swi ### :bug: (Bug Fix) diff --git a/api/src/trace/SamplingResult.ts b/api/src/trace/SamplingResult.ts index 3038eec077..c2573d5b82 100644 --- a/api/src/trace/SamplingResult.ts +++ b/api/src/trace/SamplingResult.ts @@ -15,6 +15,7 @@ */ import { SpanAttributes } from './attributes'; +import { TraceState } from './trace_state'; /** * @deprecated use the one declared in @opentelemetry/sdk-trace-base instead. @@ -55,4 +56,11 @@ export interface SamplingResult { * can safely cache the returned value. */ attributes?: Readonly; + /** + * A {@link TraceState} that will be associated with the {@link Span} through + * the new {@link SpanContext}. Samplers SHOULD return the TraceState from + * the passed-in {@link Context} if they do not intend to change it. Leaving + * the value undefined will also leave the TraceState unchanged. + */ + traceState?: TraceState; } diff --git a/packages/opentelemetry-sdk-trace-base/src/Sampler.ts b/packages/opentelemetry-sdk-trace-base/src/Sampler.ts index 8c610180a4..0a4236e882 100644 --- a/packages/opentelemetry-sdk-trace-base/src/Sampler.ts +++ b/packages/opentelemetry-sdk-trace-base/src/Sampler.ts @@ -14,7 +14,13 @@ * limitations under the License. */ -import { Context, Link, SpanAttributes, SpanKind } from '@opentelemetry/api'; +import { + Context, + Link, + SpanAttributes, + SpanKind, + TraceState, +} from '@opentelemetry/api'; /** * A sampling decision that determines how a {@link Span} will be recorded @@ -53,6 +59,13 @@ export interface SamplingResult { * can safely cache the returned value. */ attributes?: Readonly; + /** + * A {@link TraceState} that will be associated with the {@link Span} through + * the new {@link SpanContext}. Samplers SHOULD return the TraceState from + * the passed-in {@link Context} if they do not intend to change it. Leaving + * the value undefined will also leave the TraceState unchanged. + */ + traceState?: TraceState; } /** diff --git a/packages/opentelemetry-sdk-trace-base/src/Tracer.ts b/packages/opentelemetry-sdk-trace-base/src/Tracer.ts index 6fc0102359..75443cea88 100644 --- a/packages/opentelemetry-sdk-trace-base/src/Tracer.ts +++ b/packages/opentelemetry-sdk-trace-base/src/Tracer.ts @@ -117,6 +117,8 @@ export class Tracer implements api.Tracer { links ); + traceState = samplingResult.traceState ?? traceState; + const traceFlags = samplingResult.decision === api.SamplingDecision.RECORD_AND_SAMPLED ? api.TraceFlags.SAMPLED diff --git a/packages/opentelemetry-sdk-trace-base/test/common/Tracer.test.ts b/packages/opentelemetry-sdk-trace-base/test/common/Tracer.test.ts index 1dcbffff58..359816874d 100644 --- a/packages/opentelemetry-sdk-trace-base/test/common/Tracer.test.ts +++ b/packages/opentelemetry-sdk-trace-base/test/common/Tracer.test.ts @@ -18,6 +18,7 @@ import { Context, context, createContextKey, + createTraceState, INVALID_TRACEID, Link, ROOT_CONTEXT, @@ -25,6 +26,7 @@ import { SpanKind, trace, TraceFlags, + TraceState, } from '@opentelemetry/api'; import { getSpan } from '@opentelemetry/api/build/src/trace/context-utils'; import { @@ -57,6 +59,8 @@ describe('Tracer', () => { } class TestSampler implements Sampler { + constructor(private readonly traceState?: TraceState) {} + shouldSample( _context: Context, _traceId: string, @@ -80,6 +84,7 @@ describe('Tracer', () => { // invalid attributes should be sanitized. ...invalidAttributes, } as unknown as SpanAttributes, + traceState: this.traceState, }; } } @@ -160,6 +165,17 @@ describe('Tracer', () => { span.end(); }); + it('should start a span with traceState in sampling result', () => { + const traceState = createTraceState(); + const tracer = new Tracer( + { name: 'default', version: '0.0.1' }, + { sampler: new TestSampler(traceState) }, + tracerProvider + ); + const span = tracer.startSpan('stateSpan'); + assert.strictEqual(span.spanContext().traceState, traceState); + }); + it('should have an instrumentationLibrary', () => { const tracer = new Tracer( { name: 'default', version: '0.0.1' }, @@ -192,11 +208,13 @@ describe('Tracer', () => { }); }); - it('should use traceId and spanId from parent', () => { + it('should use traceId, spanId and traceState from parent', () => { + const traceState = createTraceState(); const parent: SpanContext = { traceId: '00112233445566778899001122334455', spanId: '0011223344556677', traceFlags: TraceFlags.SAMPLED, + traceState, }; const tracer = new Tracer( { name: 'default', version: '0.0.1' }, @@ -210,6 +228,7 @@ describe('Tracer', () => { ); assert.strictEqual((span as Span).parentSpanId, parent.spanId); assert.strictEqual(span.spanContext().traceId, parent.traceId); + assert.strictEqual(span.spanContext().traceState, traceState); }); it('should not use spanId from invalid parent', () => { From e0d6e14bfdf5ea15bbe26e7e9fed03fa01673d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herm=C3=B3genes=20Ferreira?= Date: Thu, 9 Feb 2023 15:03:00 +0100 Subject: [PATCH 12/37] feat: add HTTP_ROUTE attribute to http incoming metrics if present (#3581) * feat: add HTTP_ROUTE attribute to http incoming metrics if present * fix: lint errors * docs: add changelog entry * fix: test name Co-authored-by: Marc Pichler --------- Co-authored-by: Marc Pichler --- experimental/CHANGELOG.md | 2 ++ .../src/utils.ts | 4 +++ .../test/functionals/utils.test.ts | 26 +++++++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index e2725a170a..5f14e88c50 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -8,6 +8,8 @@ All notable changes to experimental packages in this project will be documented ### :rocket: (Enhancement) +* feat: add HTTP_ROUTE attribute to http incoming metrics if present [#3581](https://github.com/open-telemetry/opentelemetry-js/pull/3581) @hermogenes + ### :bug: (Bug Fix) * fix(prometheus-exporter): add possibility to respond to errors returned by `server.listen()` [#3552](https://github.com/open-telemetry/opentelemetry-js/pull/3402) @pichlermarc diff --git a/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts b/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts index c9a178653e..7801a7626c 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts @@ -559,6 +559,10 @@ export const getIncomingRequestMetricAttributesOnResponse = ( spanAttributes[SemanticAttributes.HTTP_STATUS_CODE]; metricAttributes[SemanticAttributes.NET_HOST_PORT] = spanAttributes[SemanticAttributes.NET_HOST_PORT]; + if (spanAttributes[SemanticAttributes.HTTP_ROUTE] !== undefined) { + metricAttributes[SemanticAttributes.HTTP_ROUTE] = + spanAttributes[SemanticAttributes.HTTP_ROUTE]; + } return metricAttributes; }; diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts index 125585b75a..700498cfb6 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts @@ -20,6 +20,7 @@ import { SpanKind, TraceFlags, context, + Attributes, } from '@opentelemetry/api'; import { BasicTracerProvider, Span } from '@opentelemetry/sdk-trace-base'; import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; @@ -320,6 +321,31 @@ describe('Utility', () => { assert.deepEqual(attributes[SemanticAttributes.HTTP_ROUTE], undefined); }); }); + + describe('getIncomingRequestMetricAttributesOnResponse()', () => { + it('should correctly add http_route if span has it', () => { + const spanAttributes: Attributes = { + [SemanticAttributes.HTTP_ROUTE]: '/user/:id', + }; + const metricAttributes = + utils.getIncomingRequestMetricAttributesOnResponse(spanAttributes); + + assert.deepStrictEqual( + metricAttributes[SemanticAttributes.HTTP_ROUTE], + '/user/:id' + ); + }); + + it('should skip http_route if span does not have it', () => { + const spanAttributes: Attributes = {}; + const metricAttributes = + utils.getIncomingRequestMetricAttributesOnResponse(spanAttributes); + assert.deepEqual( + metricAttributes[SemanticAttributes.HTTP_ROUTE], + undefined + ); + }); + }); // Verify the key in the given attributes is set to the given value, // and that no other HTTP Content Length attributes are set. function verifyValueInAttributes( From d1aa906889405f31f7a4a16495aca846f4bed26a Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Thu, 9 Feb 2023 13:10:29 -0500 Subject: [PATCH 13/37] Add pichlermarc to maintainers (#3596) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 72c52f9b39..93d49a049b 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,6 @@ Approvers ([@open-telemetry/js-approvers](https://github.com/orgs/open-telemetry - [Gerhard Stöbich](https://github.com/Flarna), Dynatrace - [Haddas Bronfman](https://github.com/haddasbronfman), Cisco - [John Bley](https://github.com/johnbley), Splunk -- [Marc Pichler](https://github.com/pichlermarc), Dynatrace - [Mark Wolff](https://github.com/markwolff), Microsoft - [Martin Kuba](https://github.com/martinkuba), Lightstep - [Matthew Wear](https://github.com/mwear), LightStep @@ -196,6 +195,7 @@ Maintainers ([@open-telemetry/js-maintainers](https://github.com/orgs/open-telem - [Amir Blum](https://github.com/blumamir), Aspecto - [Chengzhong Wu](https://github.com/legendecas), Alibaba - [Daniel Dyla](https://github.com/dyladan), Dynatrace +- [Marc Pichler](https://github.com/pichlermarc), Dynatrace - [Rauno Viskus](https://github.com/Rauno56), Splunk - [Valentin Marchaud](https://github.com/vmarchaud), Open Source Contributor From 326fd557cb1e40e2dd0bf042e472a963410053ca Mon Sep 17 00:00:00 2001 From: Aleksei Androsov Date: Fri, 10 Feb 2023 15:40:55 +0300 Subject: [PATCH 14/37] perf(propagator-jaeger): improve deserializeSpanContext performance (#3541) --- CHANGELOG.md | 1 + .../opentelemetry-propagator-jaeger/src/JaegerPropagator.ts | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8776a2cc89..f1670b19c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/ * feat (api-logs): separate Events API into its own package [3550](https://github.com/open-telemetry/opentelemetry-js/pull/3550) @martinkuba * feat(sdk-metrics): apply binary search in histogram recording [#3539](https://github.com/open-telemetry/opentelemetry-js/pull/3539) @legendecas +* perf(propagator-jaeger): improve deserializeSpanContext performance [#3541](https://github.com/open-telemetry/opentelemetry-js/pull/3541) @doochik * feat: support TraceState in SamplingResult [#3530](https://github.com/open-telemetry/opentelemetry-js/pull/3530) @raphael-theriault-swi ### :bug: (Bug Fix) diff --git a/packages/opentelemetry-propagator-jaeger/src/JaegerPropagator.ts b/packages/opentelemetry-propagator-jaeger/src/JaegerPropagator.ts index b4421e395b..7b11d7290b 100644 --- a/packages/opentelemetry-propagator-jaeger/src/JaegerPropagator.ts +++ b/packages/opentelemetry-propagator-jaeger/src/JaegerPropagator.ts @@ -134,6 +134,8 @@ export class JaegerPropagator implements TextMapPropagator { } } +const VALID_HEX_RE = /^[0-9a-f]{1,2}$/i; + /** * @param {string} serializedString - a serialized span context. * @return {SpanContext} - returns a span context represented by the serializedString. @@ -148,9 +150,7 @@ function deserializeSpanContext(serializedString: string): SpanContext | null { const traceId = _traceId.padStart(32, '0'); const spanId = _spanId.padStart(16, '0'); - const traceFlags = flags.match(/^[0-9a-f]{1,2}$/i) - ? parseInt(flags, 16) & 1 - : 1; + const traceFlags = VALID_HEX_RE.test(flags) ? parseInt(flags, 16) & 1 : 1; return { traceId, spanId, isRemote: true, traceFlags }; } From 2c51cc26d1d4338e4966d4387560623a3a3e3c86 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Fri, 10 Feb 2023 12:02:28 -0500 Subject: [PATCH 15/37] chore: enable merge queues (#3599) --- .github/repository-settings.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/repository-settings.md b/.github/repository-settings.md index 1391de5f17..c5f00a2e3e 100644 --- a/.github/repository-settings.md +++ b/.github/repository-settings.md @@ -20,6 +20,12 @@ No changes ### `main` * Uncheck "Restrict who can push to matching branches" +* Check "Require merge queue" + * Build concurrency: 5 + * Minimum pull requests to merge: 1 or after 5 minutes + * Maximum pull requests to merge: 5 + * Check "Only merge non-failing pull requests" + * Status check timeout: 60 minutes ### `dependabot/**/**` From 3bc080731dd923723fb9595263d6c357d4a7ce08 Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Mon, 13 Feb 2023 22:55:44 +0800 Subject: [PATCH 16/37] chore: enable merge queue checks (#3607) --- .github/workflows/lint.yml | 1 + .github/workflows/peer-api.yml | 1 + .github/workflows/unit-test.yml | 1 + .github/workflows/w3c-integration-test.yml | 1 + 4 files changed, 4 insertions(+) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a7ed8138d4..16b9d4b4cd 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -5,6 +5,7 @@ on: branches: - main pull_request: + merge_group: jobs: build: diff --git a/.github/workflows/peer-api.yml b/.github/workflows/peer-api.yml index d3239068d5..ac90efe981 100644 --- a/.github/workflows/peer-api.yml +++ b/.github/workflows/peer-api.yml @@ -5,6 +5,7 @@ on: branches: - main pull_request: + merge_group: jobs: peer-api-check: diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 76e3c51505..8c262144ec 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -4,6 +4,7 @@ on: branches: - main pull_request: + merge_group: jobs: node-tests: diff --git a/.github/workflows/w3c-integration-test.yml b/.github/workflows/w3c-integration-test.yml index 971ea6ee7f..443fe85fac 100644 --- a/.github/workflows/w3c-integration-test.yml +++ b/.github/workflows/w3c-integration-test.yml @@ -5,6 +5,7 @@ on: branches: - main pull_request: + merge_group: jobs: build-and-test: From 47444f2a4c13a80f3328ffafc5b4c8db9e9a0886 Mon Sep 17 00:00:00 2001 From: Sami Musallam Date: Mon, 13 Feb 2023 19:52:09 +0200 Subject: [PATCH 17/37] feat(resource): create sync resource with some attributes that resolve asynchronously (#3460) Co-authored-by: Amir Blum Co-authored-by: Daniel Dyla --- CHANGELOG.md | 1 + README.md | 9 +- .../src/BrowserDetector.ts | 3 +- .../test/BrowserDetector.test.ts | 8 +- .../test/util.ts | 6 +- .../src/PrometheusSerializer.ts | 4 +- .../opentelemetry-sdk-node/src/sdk.ts | 18 +- .../opentelemetry-sdk-node/src/types.ts | 4 +- .../opentelemetry-sdk-node/test/sdk.test.ts | 67 +++--- .../test/util/resource-assertions.ts | 4 +- .../otlp-transformer/src/trace/index.ts | 4 +- .../src/transform.ts | 4 +- packages/opentelemetry-resources/package.json | 1 + .../opentelemetry-resources/src/IResource.ts | 54 +++++ .../opentelemetry-resources/src/Resource.ts | 98 ++++++-- .../opentelemetry-resources/src/config.ts | 4 +- .../src/detect-resources.ts | 126 +++++++++++ .../src/detectors/BrowserDetector.ts | 47 +--- .../src/detectors/BrowserDetectorSync.ts | 64 ++++++ .../src/detectors/EnvDetector.ts | 130 +---------- .../src/detectors/EnvDetectorSync.ts | 160 +++++++++++++ .../src/detectors/NoopDetector.ts | 7 +- .../src/detectors/NoopDetectorSync.ts | 27 +++ .../src/detectors/ProcessDetector.ts | 56 +---- .../src/detectors/ProcessDetectorSync.ts | 79 +++++++ .../src/detectors/index.ts | 3 + packages/opentelemetry-resources/src/index.ts | 2 + .../src/platform/browser/HostDetectorSync.ts | 19 ++ .../src/platform/browser/OSDetectorSync.ts | 19 ++ .../src/platform/browser/detect-resources.ts | 49 ---- .../src/platform/browser/index.ts | 3 +- .../src/platform/node/HostDetector.ts | 30 +-- .../src/platform/node/HostDetectorSync.ts | 38 ++++ .../src/platform/node/OSDetector.ts | 28 +-- .../src/platform/node/OSDetectorSync.ts | 38 ++++ .../src/platform/node/detect-resources.ts | 73 ------ .../src/platform/node/index.ts | 3 +- .../src/platform/node/utils.ts | 42 ++++ packages/opentelemetry-resources/src/types.ts | 15 +- packages/opentelemetry-resources/src/utils.ts | 21 ++ .../test/Resource.test.ts | 213 +++++++++++++++++- .../test/detect-resources.test.ts | 101 +++++++++ .../detectors/browser/BrowserDetector.test.ts | 6 +- .../detectors/browser/EnvDetector.test.ts | 10 +- .../detectors/browser/HostDetector.test.ts | 4 +- .../test/detectors/browser/OSDetector.test.ts | 4 +- .../detectors/browser/ProcessDetector.test.ts | 4 +- .../detectors/node/BrowserDetector.test.ts | 4 +- .../test/detectors/node/EnvDetector.test.ts | 8 +- .../test/detectors/node/HostDetector.test.ts | 6 +- .../test/detectors/node/OSDetector.test.ts | 6 +- .../detectors/node/ProcessDetector.test.ts | 6 +- .../test/util/resource-assertions.ts | 22 +- .../opentelemetry-sdk-trace-base/package.json | 1 + .../src/BasicTracerProvider.ts | 4 +- .../opentelemetry-sdk-trace-base/src/Span.ts | 4 +- .../src/Tracer.ts | 4 +- .../src/export/BatchSpanProcessorBase.ts | 29 ++- .../src/export/ReadableSpan.ts | 4 +- .../src/export/SimpleSpanProcessor.ts | 59 +++-- .../opentelemetry-sdk-trace-base/src/types.ts | 4 +- .../export/BatchSpanProcessorBase.test.ts | 22 ++ .../common/export/SimpleSpanProcessor.test.ts | 112 +++++++++ .../common/export/TestExporterWithDelay.ts | 51 +++++ packages/sdk-metrics/src/MeterProvider.ts | 4 +- packages/sdk-metrics/src/export/MetricData.ts | 4 +- .../export/PeriodicExportingMetricReader.ts | 24 +- .../src/state/MeterProviderSharedState.ts | 4 +- 68 files changed, 1537 insertions(+), 555 deletions(-) create mode 100644 packages/opentelemetry-resources/src/IResource.ts create mode 100644 packages/opentelemetry-resources/src/detect-resources.ts create mode 100644 packages/opentelemetry-resources/src/detectors/BrowserDetectorSync.ts create mode 100644 packages/opentelemetry-resources/src/detectors/EnvDetectorSync.ts create mode 100644 packages/opentelemetry-resources/src/detectors/NoopDetectorSync.ts create mode 100644 packages/opentelemetry-resources/src/detectors/ProcessDetectorSync.ts create mode 100644 packages/opentelemetry-resources/src/platform/browser/HostDetectorSync.ts create mode 100644 packages/opentelemetry-resources/src/platform/browser/OSDetectorSync.ts delete mode 100644 packages/opentelemetry-resources/src/platform/browser/detect-resources.ts create mode 100644 packages/opentelemetry-resources/src/platform/node/HostDetectorSync.ts create mode 100644 packages/opentelemetry-resources/src/platform/node/OSDetectorSync.ts delete mode 100644 packages/opentelemetry-resources/src/platform/node/detect-resources.ts create mode 100644 packages/opentelemetry-resources/src/platform/node/utils.ts create mode 100644 packages/opentelemetry-resources/src/utils.ts create mode 100644 packages/opentelemetry-resources/test/detect-resources.test.ts create mode 100644 packages/opentelemetry-sdk-trace-base/test/common/export/TestExporterWithDelay.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index f1670b19c7..0ed63cceff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/ ### :rocket: (Enhancement) +* feat(resource): create sync resource with some attributes that resolve asynchronously [#3460](https://github.com/open-telemetry/opentelemetry-js/pull/3460) @samimusallam * feat (api-logs): separate Events API into its own package [3550](https://github.com/open-telemetry/opentelemetry-js/pull/3550) @martinkuba * feat(sdk-metrics): apply binary search in histogram recording [#3539](https://github.com/open-telemetry/opentelemetry-js/pull/3539) @legendecas * perf(propagator-jaeger): improve deserializeSpanContext performance [#3541](https://github.com/open-telemetry/opentelemetry-js/pull/3541) @doochik diff --git a/README.md b/README.md index 93d49a049b..fb766e55ce 100644 --- a/README.md +++ b/README.md @@ -84,9 +84,7 @@ const sdk = new opentelemetry.NodeSDK({ // initialize the SDK and register with the OpenTelemetry API // this enables the API to record telemetry -sdk.start() - .then(() => console.log('Tracing initialized')) - .catch((error) => console.log('Error initializing tracing', error)); +sdk.start(); // gracefully shut down the SDK on process exit process.on('SIGTERM', () => { @@ -277,6 +275,11 @@ These instrumentations are hosted at { + async detect(config?: ResourceDetectionConfig): Promise { const isBrowser = typeof navigator !== 'undefined'; if (!isBrowser) { return Resource.empty(); diff --git a/experimental/packages/opentelemetry-browser-detector/test/BrowserDetector.test.ts b/experimental/packages/opentelemetry-browser-detector/test/BrowserDetector.test.ts index 7eb3928161..727007aa55 100644 --- a/experimental/packages/opentelemetry-browser-detector/test/BrowserDetector.test.ts +++ b/experimental/packages/opentelemetry-browser-detector/test/BrowserDetector.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import * as sinon from 'sinon'; -import { Resource } from '@opentelemetry/resources'; +import { IResource } from '@opentelemetry/resources'; import { browserDetector } from '../src/BrowserDetector'; import { describeBrowser, assertResource, assertEmptyResource } from './util'; @@ -47,7 +47,7 @@ describeBrowser('browserDetector()', () => { }, }); - const resource: Resource = await browserDetector.detect(); + const resource: IResource = await browserDetector.detect(); assertResource(resource, { platform: 'platform', brands: ['Chromium 106', 'Google Chrome 106', 'Not;A=Brand 99'], @@ -63,7 +63,7 @@ describeBrowser('browserDetector()', () => { userAgentData: undefined, }); - const resource: Resource = await browserDetector.detect(); + const resource: IResource = await browserDetector.detect(); assertResource(resource, { language: 'en-US', user_agent: 'dddd', @@ -74,7 +74,7 @@ describeBrowser('browserDetector()', () => { sinon.stub(globalThis, 'navigator').value({ userAgent: '', }); - const resource: Resource = await browserDetector.detect(); + const resource: IResource = await browserDetector.detect(); assertEmptyResource(resource); }); }); diff --git a/experimental/packages/opentelemetry-browser-detector/test/util.ts b/experimental/packages/opentelemetry-browser-detector/test/util.ts index b74fd89ced..3318f48910 100644 --- a/experimental/packages/opentelemetry-browser-detector/test/util.ts +++ b/experimental/packages/opentelemetry-browser-detector/test/util.ts @@ -16,7 +16,7 @@ import { Suite } from 'mocha'; import * as assert from 'assert'; import { BROWSER_ATTRIBUTES } from '../src/types'; -import { Resource } from '@opentelemetry/resources'; +import { IResource } from '@opentelemetry/resources'; export function describeBrowser(title: string, fn: (this: Suite) => void) { title = `Browser: ${title}`; @@ -27,7 +27,7 @@ export function describeBrowser(title: string, fn: (this: Suite) => void) { } export const assertResource = ( - resource: Resource, + resource: IResource, validations: { platform?: string; brands?: string[]; @@ -74,6 +74,6 @@ export const assertResource = ( * * @param resource the Resource to validate */ -export const assertEmptyResource = (resource: Resource) => { +export const assertEmptyResource = (resource: IResource) => { assert.strictEqual(Object.keys(resource.attributes).length, 0); }; diff --git a/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusSerializer.ts b/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusSerializer.ts index 2d9808bf35..75b29bb61b 100644 --- a/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusSerializer.ts +++ b/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusSerializer.ts @@ -29,7 +29,7 @@ import { Histogram, } from '@opentelemetry/sdk-metrics'; import { hrTimeToMilliseconds } from '@opentelemetry/core'; -import { Resource } from '@opentelemetry/resources'; +import { IResource } from '@opentelemetry/resources'; type PrometheusDataTypeLiteral = | 'counter' @@ -340,7 +340,7 @@ export class PrometheusSerializer { return results; } - protected _serializeResource(resource: Resource): string { + protected _serializeResource(resource: IResource): string { const name = 'target_info'; const help = `# HELP ${name} Target metadata`; const type = `# TYPE ${name} gauge`; diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index caad9b85e2..fa14043cd3 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -21,8 +21,10 @@ import { } from '@opentelemetry/instrumentation'; import { Detector, - detectResources, + DetectorSync, + detectResourcesSync, envDetector, + IResource, processDetector, Resource, ResourceDetectionConfig, @@ -63,8 +65,8 @@ export class NodeSDK { private _meterProviderConfig?: MeterProviderConfig; private _instrumentations: InstrumentationOption[]; - private _resource: Resource; - private _resourceDetectors: Detector[]; + private _resource: IResource; + private _resourceDetectors: Array; private _autoDetectResources: boolean; @@ -183,7 +185,7 @@ export class NodeSDK { } /** Detect resource attributes */ - public async detectResources(): Promise { + public detectResources(): void { if (this._disabled) { return; } @@ -192,18 +194,18 @@ export class NodeSDK { detectors: this._resourceDetectors, }; - this.addResource(await detectResources(internalConfig)); + this.addResource(detectResourcesSync(internalConfig)); } /** Manually add a resource */ - public addResource(resource: Resource): void { + public addResource(resource: IResource): void { this._resource = this._resource.merge(resource); } /** * Once the SDK has been configured, call this method to construct SDK components and register them with the OpenTelemetry API. */ - public async start(): Promise { + public start(): void { if (this._disabled) { return; } @@ -213,7 +215,7 @@ export class NodeSDK { }); if (this._autoDetectResources) { - await this.detectResources(); + this.detectResources(); } this._resource = diff --git a/experimental/packages/opentelemetry-sdk-node/src/types.ts b/experimental/packages/opentelemetry-sdk-node/src/types.ts index 4c816516dc..49dfae8f7c 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/types.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/types.ts @@ -17,7 +17,7 @@ import type { ContextManager, SpanAttributes } from '@opentelemetry/api'; import { TextMapPropagator } from '@opentelemetry/api'; import { InstrumentationOption } from '@opentelemetry/instrumentation'; -import { Detector, Resource } from '@opentelemetry/resources'; +import { Detector, DetectorSync, Resource } from '@opentelemetry/resources'; import { MetricReader, View } from '@opentelemetry/sdk-metrics'; import { Sampler, @@ -35,7 +35,7 @@ export interface NodeSDKConfiguration { views: View[]; instrumentations: InstrumentationOption[]; resource: Resource; - resourceDetectors: Detector[]; + resourceDetectors: Array; sampler: Sampler; serviceName?: string; spanProcessor: SpanProcessor; diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index 2f7957a8ec..60b493435b 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -87,7 +87,7 @@ describe('Node SDK', () => { autoDetectResources: false, }); - await sdk.start(); + sdk.start(); assert.strictEqual( context['_getContextManager'](), @@ -114,7 +114,7 @@ describe('Node SDK', () => { autoDetectResources: false, }); - await sdk.start(); + sdk.start(); assert.ok(!(metrics.getMeterProvider() instanceof MeterProvider)); @@ -139,7 +139,7 @@ describe('Node SDK', () => { autoDetectResources: false, }); - await sdk.start(); + sdk.start(); assert.ok(!(metrics.getMeterProvider() instanceof MeterProvider)); @@ -171,7 +171,7 @@ describe('Node SDK', () => { autoDetectResources: false, }); - await sdk.start(); + sdk.start(); assert.strictEqual( context['_getContextManager'](), @@ -237,7 +237,7 @@ describe('Node SDK', () => { autoDetectResources: false, }); - await sdk.start(); + sdk.start(); assert.strictEqual( context['_getContextManager'](), @@ -391,8 +391,9 @@ describe('Node SDK', () => { envDetector, ], }); - await sdk.detectResources(); + sdk.detectResources(); const resource = sdk['_resource']; + await resource.waitForAsyncAttributes?.(); assert.strictEqual(resource.attributes['customAttr'], 'someValue'); @@ -420,8 +421,9 @@ describe('Node SDK', () => { ], }); - await sdk.detectResources(); + sdk.detectResources(); const resource = sdk['_resource']; + await resource.waitForAsyncAttributes?.(); assertServiceResource(resource, { instanceId: '627cc493', @@ -460,6 +462,7 @@ describe('Node SDK', () => { // This test depends on the env detector to be functioning as intended const mockedLoggerMethod = Sinon.fake(); const mockedVerboseLoggerMethod = Sinon.fake(); + diag.setLogger( { debug: mockedLoggerMethod, @@ -468,7 +471,8 @@ describe('Node SDK', () => { DiagLogLevel.VERBOSE ); - await sdk.detectResources(); + sdk.detectResources(); + await sdk['_resource'].waitForAsyncAttributes?.(); // Test that the Env Detector successfully found its resource and populated it with the right values. assert.ok( @@ -478,7 +482,7 @@ describe('Node SDK', () => { assert.ok( callArgsMatches( mockedVerboseLoggerMethod, - /{\s+'service\.instance\.id':\s+'627cc493',\s+'service\.name':\s+'my-service',\s+'service\.namespace':\s+'default',\s+'service\.version':\s+'0\.0\.1'\s+}\s*/ + /{\s+"service\.instance\.id":\s+"627cc493",\s+"service\.name":\s+"my-service",\s+"service\.namespace":\s+"default",\s+"service\.version":\s+"0.0.1"\s+}\s*/gm ) ); }); @@ -500,7 +504,7 @@ describe('Node SDK', () => { DiagLogLevel.DEBUG ); - await sdk.detectResources(); + sdk.detectResources(); assert.ok( callArgsContains( @@ -519,7 +523,7 @@ describe('Node SDK', () => { serviceName: 'config-set-name', }); - await sdk.start(); + sdk.start(); const resource = sdk['_resource']; assertServiceResource(resource, { @@ -531,8 +535,9 @@ describe('Node SDK', () => { process.env.OTEL_SERVICE_NAME = 'env-set-name'; const sdk = new NodeSDK(); - await sdk.start(); + sdk.start(); const resource = sdk['_resource']; + await resource.waitForAsyncAttributes?.(); assertServiceResource(resource, { name: 'env-set-name', @@ -546,8 +551,9 @@ describe('Node SDK', () => { serviceName: 'config-set-name', }); - await sdk.start(); + sdk.start(); const resource = sdk['_resource']; + await resource.waitForAsyncAttributes?.(); assertServiceResource(resource, { name: 'config-set-name', @@ -560,8 +566,9 @@ describe('Node SDK', () => { 'service.name=resource-env-set-name'; const sdk = new NodeSDK(); - await sdk.start(); + sdk.start(); const resource = sdk['_resource']; + await resource.waitForAsyncAttributes?.(); assertServiceResource(resource, { name: 'resource-env-set-name', @@ -576,8 +583,9 @@ describe('Node SDK', () => { serviceName: 'config-set-name', }); - await sdk.start(); + sdk.start(); const resource = sdk['_resource']; + await resource.waitForAsyncAttributes?.(); assertServiceResource(resource, { name: 'config-set-name', @@ -650,8 +658,9 @@ describe('Node SDK', () => { envDetector, ], }); - await sdk.detectResources(); + sdk.detectResources(); const resource = sdk['_resource']; + await resource.waitForAsyncAttributes?.(); assert.deepStrictEqual(resource, Resource.empty()); }); @@ -676,7 +685,7 @@ describe('setup exporter from env', () => { }); it('use default exporter TracerProviderWithEnvExporters when user does not provide span processor or trace exporter to sdk config', async () => { const sdk = new NodeSDK(); - await sdk.start(); + sdk.start(); const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!; @@ -689,7 +698,7 @@ describe('setup exporter from env', () => { const sdk = new NodeSDK({ traceExporter, }); - await sdk.start(); + sdk.start(); const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!; @@ -706,7 +715,7 @@ describe('setup exporter from env', () => { const sdk = new NodeSDK({ spanProcessor, }); - await sdk.start(); + sdk.start(); const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!; @@ -723,7 +732,7 @@ describe('setup exporter from env', () => { const sdk = new NodeSDK({ traceExporter, }); - await sdk.start(); + sdk.start(); const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!; @@ -739,7 +748,7 @@ describe('setup exporter from env', () => { env.OTEL_TRACES_EXPORTER = 'otlp'; env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; const sdk = new NodeSDK(); - await sdk.start(); + sdk.start(); const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!; @@ -752,7 +761,7 @@ describe('setup exporter from env', () => { it('use noop span processor when user sets env exporter to none', async () => { env.OTEL_TRACES_EXPORTER = 'none'; const sdk = new NodeSDK(); - await sdk.start(); + sdk.start(); const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!; @@ -765,7 +774,7 @@ describe('setup exporter from env', () => { it('log warning that sdk will not be initalized when exporter is set to none', async () => { env.OTEL_TRACES_EXPORTER = 'none'; const sdk = new NodeSDK(); - await sdk.start(); + sdk.start(); assert.strictEqual( stubLoggerError.args[0][0], @@ -786,7 +795,7 @@ describe('setup exporter from env', () => { it('use default otlp exporter when empty value is provided for exporter via env', async () => { env.OTEL_TRACES_EXPORTER = ''; const sdk = new NodeSDK(); - await sdk.start(); + sdk.start(); const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!; @@ -799,7 +808,7 @@ describe('setup exporter from env', () => { it('use only default exporter when none value is provided with other exporters', async () => { env.OTEL_TRACES_EXPORTER = 'otlp,zipkin,none'; const sdk = new NodeSDK(); - await sdk.start(); + sdk.start(); const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!; @@ -812,7 +821,7 @@ describe('setup exporter from env', () => { it('log warning that only default exporter will be used since exporter list contains none with other exports ', async () => { env.OTEL_TRACES_EXPORTER = 'otlp,zipkin,none'; const sdk = new NodeSDK(); - await sdk.start(); + sdk.start(); assert.strictEqual( stubLoggerError.args[0][0], @@ -823,7 +832,7 @@ describe('setup exporter from env', () => { it('should warn that provided exporter value is unrecognized and not able to be set up', async () => { env.OTEL_TRACES_EXPORTER = 'invalid'; const sdk = new NodeSDK(); - await sdk.start(); + sdk.start(); assert.strictEqual( stubLoggerError.args[0][0], @@ -841,7 +850,7 @@ describe('setup exporter from env', () => { env.OTEL_TRACES_EXPORTER = 'zipkin, otlp, jaeger'; env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = 'grpc'; const sdk = new NodeSDK(); - await sdk.start(); + sdk.start(); const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!; @@ -857,7 +866,7 @@ describe('setup exporter from env', () => { it('use the console exporter', async () => { env.OTEL_TRACES_EXPORTER = 'console, otlp'; const sdk = new NodeSDK(); - await sdk.start(); + sdk.start(); const listOfProcessors = sdk['_tracerProvider']!['_registeredSpanProcessors']!; diff --git a/experimental/packages/opentelemetry-sdk-node/test/util/resource-assertions.ts b/experimental/packages/opentelemetry-sdk-node/test/util/resource-assertions.ts index cd6b272433..bcbdceadc0 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/util/resource-assertions.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/util/resource-assertions.ts @@ -16,7 +16,7 @@ import { SDK_INFO } from '@opentelemetry/core'; import * as assert from 'assert'; -import { Resource } from '@opentelemetry/resources'; +import { IResource, Resource } from '@opentelemetry/resources'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; /** @@ -229,7 +229,7 @@ export const assertTelemetrySDKResource = ( * @param validations validations for the resource attributes */ export const assertServiceResource = ( - resource: Resource, + resource: IResource, validations: { name: string; instanceId?: string; diff --git a/experimental/packages/otlp-transformer/src/trace/index.ts b/experimental/packages/otlp-transformer/src/trace/index.ts index 8d20181221..ad06612a11 100644 --- a/experimental/packages/otlp-transformer/src/trace/index.ts +++ b/experimental/packages/otlp-transformer/src/trace/index.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { Resource } from '@opentelemetry/resources'; +import type { IResource } from '@opentelemetry/resources'; import type { ReadableSpan } from '@opentelemetry/sdk-trace-base'; import { toAttributes } from '../common/internal'; import { sdkSpanToOtlpSpan } from './internal'; @@ -33,7 +33,7 @@ export function createExportTraceServiceRequest( } function createResourceMap(readableSpans: ReadableSpan[]) { - const resourceMap: Map> = new Map(); + const resourceMap: Map> = new Map(); for (const record of readableSpans) { let ilmMap = resourceMap.get(record.resource); diff --git a/packages/opentelemetry-exporter-zipkin/src/transform.ts b/packages/opentelemetry-exporter-zipkin/src/transform.ts index 9ffd48ecce..c3c1887616 100644 --- a/packages/opentelemetry-exporter-zipkin/src/transform.ts +++ b/packages/opentelemetry-exporter-zipkin/src/transform.ts @@ -18,7 +18,7 @@ import * as api from '@opentelemetry/api'; import { ReadableSpan, TimedEvent } from '@opentelemetry/sdk-trace-base'; import { hrTimeToMicroseconds } from '@opentelemetry/core'; import * as zipkinTypes from './types'; -import { Resource } from '@opentelemetry/resources'; +import { IResource } from '@opentelemetry/resources'; const ZIPKIN_SPAN_KIND_MAPPING = { [api.SpanKind.CLIENT]: zipkinTypes.SpanKind.CLIENT, @@ -72,7 +72,7 @@ export function _toZipkinTags( status: api.SpanStatus, statusCodeTagName: string, statusErrorTagName: string, - resource: Resource + resource: IResource ): zipkinTypes.Tags { const tags: { [key: string]: string } = {}; for (const key of Object.keys(attributes)) { diff --git a/packages/opentelemetry-resources/package.json b/packages/opentelemetry-resources/package.json index 59c1054cb2..35dca15ecd 100644 --- a/packages/opentelemetry-resources/package.json +++ b/packages/opentelemetry-resources/package.json @@ -62,6 +62,7 @@ }, "devDependencies": { "@opentelemetry/api": ">=1.0.0 <1.5.0", + "@opentelemetry/resources_1.9.0": "npm:@opentelemetry/resources@1.9.0", "@types/mocha": "10.0.0", "@types/node": "18.6.5", "@types/sinon": "10.0.13", diff --git a/packages/opentelemetry-resources/src/IResource.ts b/packages/opentelemetry-resources/src/IResource.ts new file mode 100644 index 0000000000..b53a0e0244 --- /dev/null +++ b/packages/opentelemetry-resources/src/IResource.ts @@ -0,0 +1,54 @@ +/* + * 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 { ResourceAttributes } from './types'; + +/** + * An interface that represents a resource. A Resource describes the entity for which signals (metrics or trace) are + * collected. + * + */ +export interface IResource { + /** + * Check if async attributes have resolved. This is useful to avoid awaiting + * waitForAsyncAttributes (which will introduce asynchronous behavior) when not necessary. + * + * @returns true if the resource "attributes" property is not yet settled to its final value + */ + asyncAttributesPending?: boolean; + + /** + * @returns the Resource's attributes. + */ + readonly attributes: ResourceAttributes; + + /** + * Returns a promise that will never be rejected. Resolves when all async attributes have finished being added to + * this Resource's attributes. This is useful in exporters to block until resource detection + * has finished. + */ + waitForAsyncAttributes?(): Promise; + + /** + * Returns a new, merged {@link Resource} by merging the current Resource + * with the other Resource. In case of a collision, other Resource takes + * precedence. + * + * @param other the Resource that will be merged with this. + * @returns the newly merged Resource. + */ + merge(other: IResource | null): IResource; +} diff --git a/packages/opentelemetry-resources/src/Resource.ts b/packages/opentelemetry-resources/src/Resource.ts index 0dc2072a2f..2ef82468de 100644 --- a/packages/opentelemetry-resources/src/Resource.ts +++ b/packages/opentelemetry-resources/src/Resource.ts @@ -14,29 +14,41 @@ * limitations under the License. */ +import { diag } from '@opentelemetry/api'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; import { SDK_INFO } from '@opentelemetry/core'; import { ResourceAttributes } from './types'; import { defaultServiceName } from './platform'; +import { IResource } from './IResource'; /** * A Resource describes the entity for which a signals (metrics or trace) are * collected. */ -export class Resource { +export class Resource implements IResource { static readonly EMPTY = new Resource({}); + private _syncAttributes: ResourceAttributes; + private _asyncAttributesPromise: Promise | undefined; + + /** + * Check if async attributes have resolved. This is useful to avoid awaiting + * waitForAsyncAttributes (which will introduce asynchronous behavior) when not necessary. + * + * @returns true if the resource "attributes" property is not yet settled to its final value + */ + public asyncAttributesPending: boolean; /** * Returns an empty Resource */ - static empty(): Resource { + static empty(): IResource { return Resource.EMPTY; } /** * Returns a Resource that identifies the SDK in use. */ - static default(): Resource { + static default(): IResource { return new Resource({ [SemanticResourceAttributes.SERVICE_NAME]: defaultServiceName(), [SemanticResourceAttributes.TELEMETRY_SDK_LANGUAGE]: @@ -54,8 +66,45 @@ export class Resource { * information about the entity as numbers, strings or booleans * TODO: Consider to add check/validation on attributes. */ - readonly attributes: ResourceAttributes - ) {} + private _attributes: ResourceAttributes, + asyncAttributesPromise?: Promise + ) { + this.asyncAttributesPending = asyncAttributesPromise != null; + this._syncAttributes = _attributes; + this._asyncAttributesPromise = asyncAttributesPromise?.then( + asyncAttributes => { + this._attributes = Object.assign({}, this._attributes, asyncAttributes); + this.asyncAttributesPending = false; + return asyncAttributes; + }, + err => { + diag.debug("a resource's async attributes promise rejected: %s", err); + this.asyncAttributesPending = false; + return {}; + } + ); + } + + get attributes(): ResourceAttributes { + if (this.asyncAttributesPending) { + diag.error( + 'Accessing resource attributes before async attributes settled' + ); + } + + return this._attributes; + } + + /** + * Returns a promise that will never be rejected. Resolves when all async attributes have finished being added to + * this Resource's attributes. This is useful in exporters to block until resource detection + * has finished. + */ + async waitForAsyncAttributes(): Promise { + if (this.asyncAttributesPending) { + await this._asyncAttributesPromise; + } + } /** * Returns a new, merged {@link Resource} by merging the current Resource @@ -65,15 +114,36 @@ export class Resource { * @param other the Resource that will be merged with this. * @returns the newly merged Resource. */ - merge(other: Resource | null): Resource { - if (!other || !Object.keys(other.attributes).length) return this; + merge(other: IResource | null): IResource { + if (!other) return this; - // SpanAttributes from resource overwrite attributes from other resource. - const mergedAttributes = Object.assign( - {}, - this.attributes, - other.attributes - ); - return new Resource(mergedAttributes); + // SpanAttributes from other resource overwrite attributes from this resource. + const mergedSyncAttributes = { + ...this._syncAttributes, + //Support for old resource implementation where _syncAttributes is not defined + ...((other as Resource)._syncAttributes ?? other.attributes), + }; + + if ( + !this._asyncAttributesPromise && + !(other as Resource)._asyncAttributesPromise + ) { + return new Resource(mergedSyncAttributes); + } + + const mergedAttributesPromise = Promise.all([ + this._asyncAttributesPromise, + (other as Resource)._asyncAttributesPromise, + ]).then(([thisAsyncAttributes, otherAsyncAttributes]) => { + return { + ...this._syncAttributes, + ...thisAsyncAttributes, + //Support for old resource implementation where _syncAttributes is not defined + ...((other as Resource)._syncAttributes ?? other.attributes), + ...otherAsyncAttributes, + }; + }); + + return new Resource(mergedSyncAttributes, mergedAttributesPromise); } } diff --git a/packages/opentelemetry-resources/src/config.ts b/packages/opentelemetry-resources/src/config.ts index 915aad0764..239d596e6d 100644 --- a/packages/opentelemetry-resources/src/config.ts +++ b/packages/opentelemetry-resources/src/config.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import type { Detector } from './types'; +import type { Detector, DetectorSync } from './types'; /** * ResourceDetectionConfig provides an interface for configuring resource auto-detection. */ export interface ResourceDetectionConfig { - detectors?: Array; + detectors?: Array; } diff --git a/packages/opentelemetry-resources/src/detect-resources.ts b/packages/opentelemetry-resources/src/detect-resources.ts new file mode 100644 index 0000000000..be6943f81d --- /dev/null +++ b/packages/opentelemetry-resources/src/detect-resources.ts @@ -0,0 +1,126 @@ +/* + * 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 { Resource } from './Resource'; +import { ResourceDetectionConfig } from './config'; +import { diag } from '@opentelemetry/api'; +import { isPromiseLike } from './utils'; +import { Detector, DetectorSync } from './types'; +import { IResource } from './IResource'; + +/** + * Runs all resource detectors and returns the results merged into a single Resource. Promise + * does not resolve until all the underlying detectors have resolved, unlike + * detectResourcesSync. + * + * @deprecated use detectResourceSync() instead. + * @param config Configuration for resource detection + */ +export const detectResources = async ( + config: ResourceDetectionConfig = {} +): Promise => { + const resources: IResource[] = await Promise.all( + (config.detectors || []).map(async d => { + try { + const resource = await d.detect(config); + diag.debug(`${d.constructor.name} found resource.`, resource); + return resource; + } catch (e) { + diag.debug(`${d.constructor.name} failed: ${e.message}`); + return Resource.empty(); + } + }) + ); + + // Future check if verbose logging is enabled issue #1903 + logResources(resources); + + return resources.reduce( + (acc, resource) => acc.merge(resource), + Resource.empty() + ); +}; + +/** + * Runs all resource detectors synchronously, merging their results. In case of attribute collision later resources will take precedence. + * + * @param config Configuration for resource detection + */ +export const detectResourcesSync = ( + config: ResourceDetectionConfig = {} +): IResource => { + const resources: IResource[] = (config.detectors ?? []).map( + (d: Detector | DetectorSync) => { + try { + const resourceOrPromise = d.detect(config); + let resource: IResource; + if (isPromiseLike(resourceOrPromise)) { + const createPromise = async () => { + const resolvedResource = await resourceOrPromise; + return resolvedResource.attributes; + }; + resource = new Resource({}, createPromise()); + } else { + resource = resourceOrPromise as IResource; + } + + if (resource.waitForAsyncAttributes) { + void resource + .waitForAsyncAttributes() + .then(() => + diag.debug(`${d.constructor.name} found resource.`, resource) + ); + } else { + diag.debug(`${d.constructor.name} found resource.`, resource); + } + + return resource; + } catch (e) { + diag.error(`${d.constructor.name} failed: ${e.message}`); + return Resource.empty(); + } + } + ); + + const mergedResources = resources.reduce( + (acc, resource) => acc.merge(resource), + Resource.empty() + ); + + if (mergedResources.waitForAsyncAttributes) { + void mergedResources.waitForAsyncAttributes().then(() => { + // Future check if verbose logging is enabled issue #1903 + logResources(resources); + }); + } + + return mergedResources; +}; + +/** + * Writes debug information about the detected resources to the logger defined in the resource detection config, if one is provided. + * + * @param resources The array of {@link Resource} that should be logged. Empty entries will be ignored. + */ +const logResources = (resources: Array) => { + resources.forEach(resource => { + // Print only populated resources + if (Object.keys(resource.attributes).length > 0) { + const resourceDebugString = JSON.stringify(resource.attributes, null, 4); + diag.verbose(resourceDebugString); + } + }); +}; diff --git a/packages/opentelemetry-resources/src/detectors/BrowserDetector.ts b/packages/opentelemetry-resources/src/detectors/BrowserDetector.ts index b9271d7e56..e50cd9b50b 100644 --- a/packages/opentelemetry-resources/src/detectors/BrowserDetector.ts +++ b/packages/opentelemetry-resources/src/detectors/BrowserDetector.ts @@ -14,50 +14,19 @@ * limitations under the License. */ -import { diag } from '@opentelemetry/api'; -import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; -import { Detector, Resource, ResourceDetectionConfig } from '..'; -import { ResourceAttributes } from '../types'; +import { + browserDetectorSync, + Detector, + IResource, + ResourceDetectionConfig, +} from '..'; /** * BrowserDetector will be used to detect the resources related to browser. */ class BrowserDetector implements Detector { - async detect(config?: ResourceDetectionConfig): Promise { - const isBrowser = typeof navigator !== 'undefined'; - if (!isBrowser) { - return Resource.empty(); - } - const browserResource: ResourceAttributes = { - [SemanticResourceAttributes.PROCESS_RUNTIME_NAME]: 'browser', - [SemanticResourceAttributes.PROCESS_RUNTIME_DESCRIPTION]: 'Web Browser', - [SemanticResourceAttributes.PROCESS_RUNTIME_VERSION]: navigator.userAgent, - }; - return this._getResourceAttributes(browserResource, config); - } - /** - * Validates process resource attribute map from process variables - * - * @param browserResource The un-sanitized resource attributes from process as key/value pairs. - * @param config: Config - * @returns The sanitized resource attributes. - */ - private _getResourceAttributes( - browserResource: ResourceAttributes, - _config?: ResourceDetectionConfig - ) { - if ( - browserResource[SemanticResourceAttributes.PROCESS_RUNTIME_VERSION] === '' - ) { - diag.debug( - 'BrowserDetector failed: Unable to find required browser resources. ' - ); - return Resource.empty(); - } else { - return new Resource({ - ...browserResource, - }); - } + detect(config?: ResourceDetectionConfig): Promise { + return Promise.resolve(browserDetectorSync.detect(config)); } } diff --git a/packages/opentelemetry-resources/src/detectors/BrowserDetectorSync.ts b/packages/opentelemetry-resources/src/detectors/BrowserDetectorSync.ts new file mode 100644 index 0000000000..b58fea94c8 --- /dev/null +++ b/packages/opentelemetry-resources/src/detectors/BrowserDetectorSync.ts @@ -0,0 +1,64 @@ +/* + * 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 { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; +import { DetectorSync, IResource, Resource, ResourceDetectionConfig } from '..'; +import { ResourceAttributes } from '../types'; +import { diag } from '@opentelemetry/api'; + +/** + * BrowserDetectorSync will be used to detect the resources related to browser. + */ +class BrowserDetectorSync implements DetectorSync { + detect(config?: ResourceDetectionConfig): IResource { + const isBrowser = typeof navigator !== 'undefined'; + if (!isBrowser) { + return Resource.empty(); + } + const browserResource: ResourceAttributes = { + [SemanticResourceAttributes.PROCESS_RUNTIME_NAME]: 'browser', + [SemanticResourceAttributes.PROCESS_RUNTIME_DESCRIPTION]: 'Web Browser', + [SemanticResourceAttributes.PROCESS_RUNTIME_VERSION]: navigator.userAgent, + }; + return this._getResourceAttributes(browserResource, config); + } + /** + * Validates process resource attribute map from process variables + * + * @param browserResource The un-sanitized resource attributes from process as key/value pairs. + * @param config: Config + * @returns The sanitized resource attributes. + */ + private _getResourceAttributes( + browserResource: ResourceAttributes, + _config?: ResourceDetectionConfig + ) { + if ( + browserResource[SemanticResourceAttributes.PROCESS_RUNTIME_VERSION] === '' + ) { + diag.debug( + 'BrowserDetector failed: Unable to find required browser resources. ' + ); + return Resource.empty(); + } else { + return new Resource({ + ...browserResource, + }); + } + } +} + +export const browserDetectorSync = new BrowserDetectorSync(); diff --git a/packages/opentelemetry-resources/src/detectors/EnvDetector.ts b/packages/opentelemetry-resources/src/detectors/EnvDetector.ts index 0f5cd65209..3467a8123f 100644 --- a/packages/opentelemetry-resources/src/detectors/EnvDetector.ts +++ b/packages/opentelemetry-resources/src/detectors/EnvDetector.ts @@ -14,37 +14,16 @@ * limitations under the License. */ -import { diag } from '@opentelemetry/api'; -import { getEnv } from '@opentelemetry/core'; -import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; -import { Resource } from '../Resource'; -import { Detector, ResourceAttributes } from '../types'; +import { Detector } from '../types'; import { ResourceDetectionConfig } from '../config'; +import { IResource } from '../IResource'; +import { envDetectorSync } from './EnvDetectorSync'; /** * EnvDetector can be used to detect the presence of and create a Resource * from the OTEL_RESOURCE_ATTRIBUTES environment variable. */ class EnvDetector implements Detector { - // Type, attribute keys, and attribute values should not exceed 256 characters. - private readonly _MAX_LENGTH = 255; - - // OTEL_RESOURCE_ATTRIBUTES is a comma-separated list of attributes. - private readonly _COMMA_SEPARATOR = ','; - - // OTEL_RESOURCE_ATTRIBUTES contains key value pair separated by '='. - private readonly _LABEL_KEY_VALUE_SPLITTER = '='; - - private readonly _ERROR_MESSAGE_INVALID_CHARS = - 'should be a ASCII string with a length greater than 0 and not exceed ' + - this._MAX_LENGTH + - ' characters.'; - - private readonly _ERROR_MESSAGE_INVALID_VALUE = - 'should be a ASCII string with a length not exceed ' + - this._MAX_LENGTH + - ' characters.'; - /** * Returns a {@link Resource} populated with attributes from the * OTEL_RESOURCE_ATTRIBUTES environment variable. Note this is an async @@ -52,107 +31,8 @@ class EnvDetector implements Detector { * * @param config The resource detection config */ - async detect(_config?: ResourceDetectionConfig): Promise { - const attributes: ResourceAttributes = {}; - const env = getEnv(); - - const rawAttributes = env.OTEL_RESOURCE_ATTRIBUTES; - const serviceName = env.OTEL_SERVICE_NAME; - - if (rawAttributes) { - try { - const parsedAttributes = this._parseResourceAttributes(rawAttributes); - Object.assign(attributes, parsedAttributes); - } catch (e) { - diag.debug(`EnvDetector failed: ${e.message}`); - } - } - - if (serviceName) { - attributes[SemanticResourceAttributes.SERVICE_NAME] = serviceName; - } - - return new Resource(attributes); - } - - /** - * Creates an attribute map from the OTEL_RESOURCE_ATTRIBUTES environment - * variable. - * - * OTEL_RESOURCE_ATTRIBUTES: A comma-separated list of attributes describing - * the source in more detail, e.g. “key1=val1,key2=val2”. Domain names and - * paths are accepted as attribute keys. Values may be quoted or unquoted in - * general. If a value contains whitespaces, =, or " characters, it must - * always be quoted. - * - * @param rawEnvAttributes The resource attributes as a comma-seperated list - * of key/value pairs. - * @returns The sanitized resource attributes. - */ - private _parseResourceAttributes( - rawEnvAttributes?: string - ): ResourceAttributes { - if (!rawEnvAttributes) return {}; - - const attributes: ResourceAttributes = {}; - const rawAttributes: string[] = rawEnvAttributes.split( - this._COMMA_SEPARATOR, - -1 - ); - for (const rawAttribute of rawAttributes) { - const keyValuePair: string[] = rawAttribute.split( - this._LABEL_KEY_VALUE_SPLITTER, - -1 - ); - if (keyValuePair.length !== 2) { - continue; - } - let [key, value] = keyValuePair; - // Leading and trailing whitespaces are trimmed. - key = key.trim(); - value = value.trim().split(/^"|"$/).join(''); - if (!this._isValidAndNotEmpty(key)) { - throw new Error(`Attribute key ${this._ERROR_MESSAGE_INVALID_CHARS}`); - } - if (!this._isValid(value)) { - throw new Error(`Attribute value ${this._ERROR_MESSAGE_INVALID_VALUE}`); - } - attributes[key] = decodeURIComponent(value); - } - return attributes; - } - - /** - * Determines whether the given String is a valid printable ASCII string with - * a length not exceed _MAX_LENGTH characters. - * - * @param str The String to be validated. - * @returns Whether the String is valid. - */ - private _isValid(name: string): boolean { - return name.length <= this._MAX_LENGTH && this._isBaggageOctetString(name); - } - - // https://www.w3.org/TR/baggage/#definition - private _isBaggageOctetString(str: string): boolean { - for (let i = 0; i < str.length; i++) { - const ch = str.charCodeAt(i); - if (ch < 0x21 || ch === 0x2c || ch === 0x3b || ch === 0x5c || ch > 0x7e) { - return false; - } - } - return true; - } - - /** - * Determines whether the given String is a valid printable ASCII string with - * a length greater than 0 and not exceed _MAX_LENGTH characters. - * - * @param str The String to be validated. - * @returns Whether the String is valid and not empty. - */ - private _isValidAndNotEmpty(str: string): boolean { - return str.length > 0 && this._isValid(str); + detect(config?: ResourceDetectionConfig): Promise { + return Promise.resolve(envDetectorSync.detect(config)); } } diff --git a/packages/opentelemetry-resources/src/detectors/EnvDetectorSync.ts b/packages/opentelemetry-resources/src/detectors/EnvDetectorSync.ts new file mode 100644 index 0000000000..1230657e19 --- /dev/null +++ b/packages/opentelemetry-resources/src/detectors/EnvDetectorSync.ts @@ -0,0 +1,160 @@ +/* + * 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 { diag } from '@opentelemetry/api'; +import { getEnv } from '@opentelemetry/core'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; +import { Resource } from '../Resource'; +import { DetectorSync, ResourceAttributes } from '../types'; +import { ResourceDetectionConfig } from '../config'; +import { IResource } from '../IResource'; + +/** + * EnvDetectorSync can be used to detect the presence of and create a Resource + * from the OTEL_RESOURCE_ATTRIBUTES environment variable. + */ +class EnvDetectorSync implements DetectorSync { + // Type, attribute keys, and attribute values should not exceed 256 characters. + private readonly _MAX_LENGTH = 255; + + // OTEL_RESOURCE_ATTRIBUTES is a comma-separated list of attributes. + private readonly _COMMA_SEPARATOR = ','; + + // OTEL_RESOURCE_ATTRIBUTES contains key value pair separated by '='. + private readonly _LABEL_KEY_VALUE_SPLITTER = '='; + + private readonly _ERROR_MESSAGE_INVALID_CHARS = + 'should be a ASCII string with a length greater than 0 and not exceed ' + + this._MAX_LENGTH + + ' characters.'; + + private readonly _ERROR_MESSAGE_INVALID_VALUE = + 'should be a ASCII string with a length not exceed ' + + this._MAX_LENGTH + + ' characters.'; + + /** + * Returns a {@link Resource} populated with attributes from the + * OTEL_RESOURCE_ATTRIBUTES environment variable. Note this is an async + * function to conform to the Detector interface. + * + * @param config The resource detection config + */ + detect(_config?: ResourceDetectionConfig): IResource { + const attributes: ResourceAttributes = {}; + const env = getEnv(); + + const rawAttributes = env.OTEL_RESOURCE_ATTRIBUTES; + const serviceName = env.OTEL_SERVICE_NAME; + + if (rawAttributes) { + try { + const parsedAttributes = this._parseResourceAttributes(rawAttributes); + Object.assign(attributes, parsedAttributes); + } catch (e) { + diag.debug(`EnvDetector failed: ${e.message}`); + } + } + + if (serviceName) { + attributes[SemanticResourceAttributes.SERVICE_NAME] = serviceName; + } + + return new Resource(attributes); + } + + /** + * Creates an attribute map from the OTEL_RESOURCE_ATTRIBUTES environment + * variable. + * + * OTEL_RESOURCE_ATTRIBUTES: A comma-separated list of attributes describing + * the source in more detail, e.g. “key1=val1,key2=val2”. Domain names and + * paths are accepted as attribute keys. Values may be quoted or unquoted in + * general. If a value contains whitespaces, =, or " characters, it must + * always be quoted. + * + * @param rawEnvAttributes The resource attributes as a comma-seperated list + * of key/value pairs. + * @returns The sanitized resource attributes. + */ + private _parseResourceAttributes( + rawEnvAttributes?: string + ): ResourceAttributes { + if (!rawEnvAttributes) return {}; + + const attributes: ResourceAttributes = {}; + const rawAttributes: string[] = rawEnvAttributes.split( + this._COMMA_SEPARATOR, + -1 + ); + for (const rawAttribute of rawAttributes) { + const keyValuePair: string[] = rawAttribute.split( + this._LABEL_KEY_VALUE_SPLITTER, + -1 + ); + if (keyValuePair.length !== 2) { + continue; + } + let [key, value] = keyValuePair; + // Leading and trailing whitespaces are trimmed. + key = key.trim(); + value = value.trim().split(/^"|"$/).join(''); + if (!this._isValidAndNotEmpty(key)) { + throw new Error(`Attribute key ${this._ERROR_MESSAGE_INVALID_CHARS}`); + } + if (!this._isValid(value)) { + throw new Error(`Attribute value ${this._ERROR_MESSAGE_INVALID_VALUE}`); + } + attributes[key] = decodeURIComponent(value); + } + return attributes; + } + + /** + * Determines whether the given String is a valid printable ASCII string with + * a length not exceed _MAX_LENGTH characters. + * + * @param str The String to be validated. + * @returns Whether the String is valid. + */ + private _isValid(name: string): boolean { + return name.length <= this._MAX_LENGTH && this._isBaggageOctetString(name); + } + + // https://www.w3.org/TR/baggage/#definition + private _isBaggageOctetString(str: string): boolean { + for (let i = 0; i < str.length; i++) { + const ch = str.charCodeAt(i); + if (ch < 0x21 || ch === 0x2c || ch === 0x3b || ch === 0x5c || ch > 0x7e) { + return false; + } + } + return true; + } + + /** + * Determines whether the given String is a valid printable ASCII string with + * a length greater than 0 and not exceed _MAX_LENGTH characters. + * + * @param str The String to be validated. + * @returns Whether the String is valid and not empty. + */ + private _isValidAndNotEmpty(str: string): boolean { + return str.length > 0 && this._isValid(str); + } +} + +export const envDetectorSync = new EnvDetectorSync(); diff --git a/packages/opentelemetry-resources/src/detectors/NoopDetector.ts b/packages/opentelemetry-resources/src/detectors/NoopDetector.ts index 61cf954bf7..463d8c629f 100644 --- a/packages/opentelemetry-resources/src/detectors/NoopDetector.ts +++ b/packages/opentelemetry-resources/src/detectors/NoopDetector.ts @@ -14,12 +14,13 @@ * limitations under the License. */ -import { Resource } from '../Resource'; import { Detector } from '../types'; +import { IResource } from '../IResource'; +import { noopDetectorSync } from './NoopDetectorSync'; export class NoopDetector implements Detector { - async detect(): Promise { - return new Resource({}); + detect(): Promise { + return Promise.resolve(noopDetectorSync.detect()); } } diff --git a/packages/opentelemetry-resources/src/detectors/NoopDetectorSync.ts b/packages/opentelemetry-resources/src/detectors/NoopDetectorSync.ts new file mode 100644 index 0000000000..a52473b9c3 --- /dev/null +++ b/packages/opentelemetry-resources/src/detectors/NoopDetectorSync.ts @@ -0,0 +1,27 @@ +/* + * 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 { Resource } from '../Resource'; +import { DetectorSync } from '../types'; +import { IResource } from '../IResource'; + +export class NoopDetectorSync implements DetectorSync { + detect(): IResource { + return new Resource({}); + } +} + +export const noopDetectorSync = new NoopDetectorSync(); diff --git a/packages/opentelemetry-resources/src/detectors/ProcessDetector.ts b/packages/opentelemetry-resources/src/detectors/ProcessDetector.ts index 304a67f358..b1165f2c5b 100644 --- a/packages/opentelemetry-resources/src/detectors/ProcessDetector.ts +++ b/packages/opentelemetry-resources/src/detectors/ProcessDetector.ts @@ -14,64 +14,18 @@ * limitations under the License. */ -import { diag } from '@opentelemetry/api'; -import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; -import { Resource } from '../Resource'; -import { Detector, ResourceAttributes } from '../types'; +import { Detector } from '../types'; import { ResourceDetectionConfig } from '../config'; +import { IResource } from '../IResource'; +import { processDetectorSync } from './ProcessDetectorSync'; /** * ProcessDetector will be used to detect the resources related current process running * and being instrumented from the NodeJS Process module. */ class ProcessDetector implements Detector { - async detect(config?: ResourceDetectionConfig): Promise { - // Skip if not in Node.js environment. - if (typeof process !== 'object') { - return Resource.empty(); - } - const processResource: ResourceAttributes = { - [SemanticResourceAttributes.PROCESS_PID]: process.pid, - [SemanticResourceAttributes.PROCESS_EXECUTABLE_NAME]: process.title || '', - [SemanticResourceAttributes.PROCESS_COMMAND]: process.argv[1] || '', - [SemanticResourceAttributes.PROCESS_COMMAND_LINE]: - process.argv.join(' ') || '', - [SemanticResourceAttributes.PROCESS_RUNTIME_VERSION]: - process.versions.node, - [SemanticResourceAttributes.PROCESS_RUNTIME_NAME]: 'nodejs', - [SemanticResourceAttributes.PROCESS_RUNTIME_DESCRIPTION]: 'Node.js', - }; - return this._getResourceAttributes(processResource, config); - } - /** - * Validates process resource attribute map from process varaibls - * - * @param processResource The unsantized resource attributes from process as key/value pairs. - * @param config: Config - * @returns The sanitized resource attributes. - */ - private _getResourceAttributes( - processResource: ResourceAttributes, - _config?: ResourceDetectionConfig - ) { - if ( - processResource[SemanticResourceAttributes.PROCESS_EXECUTABLE_NAME] === - '' || - processResource[SemanticResourceAttributes.PROCESS_EXECUTABLE_PATH] === - '' || - processResource[SemanticResourceAttributes.PROCESS_COMMAND] === '' || - processResource[SemanticResourceAttributes.PROCESS_COMMAND_LINE] === '' || - processResource[SemanticResourceAttributes.PROCESS_RUNTIME_VERSION] === '' - ) { - diag.debug( - 'ProcessDetector failed: Unable to find required process resources. ' - ); - return Resource.empty(); - } else { - return new Resource({ - ...processResource, - }); - } + detect(config?: ResourceDetectionConfig): Promise { + return Promise.resolve(processDetectorSync.detect(config)); } } diff --git a/packages/opentelemetry-resources/src/detectors/ProcessDetectorSync.ts b/packages/opentelemetry-resources/src/detectors/ProcessDetectorSync.ts new file mode 100644 index 0000000000..d63b856e3e --- /dev/null +++ b/packages/opentelemetry-resources/src/detectors/ProcessDetectorSync.ts @@ -0,0 +1,79 @@ +/* + * 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 { diag } from '@opentelemetry/api'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; +import { Resource } from '../Resource'; +import { DetectorSync, ResourceAttributes } from '../types'; +import { ResourceDetectionConfig } from '../config'; +import { IResource } from '../IResource'; + +/** + * ProcessDetectorSync will be used to detect the resources related current process running + * and being instrumented from the NodeJS Process module. + */ +class ProcessDetectorSync implements DetectorSync { + detect(config?: ResourceDetectionConfig): IResource { + // Skip if not in Node.js environment. + if (typeof process !== 'object') { + return Resource.empty(); + } + const processResource: ResourceAttributes = { + [SemanticResourceAttributes.PROCESS_PID]: process.pid, + [SemanticResourceAttributes.PROCESS_EXECUTABLE_NAME]: process.title || '', + [SemanticResourceAttributes.PROCESS_COMMAND]: process.argv[1] || '', + [SemanticResourceAttributes.PROCESS_COMMAND_LINE]: + process.argv.join(' ') || '', + [SemanticResourceAttributes.PROCESS_RUNTIME_VERSION]: + process.versions.node, + [SemanticResourceAttributes.PROCESS_RUNTIME_NAME]: 'nodejs', + [SemanticResourceAttributes.PROCESS_RUNTIME_DESCRIPTION]: 'Node.js', + }; + return this._getResourceAttributes(processResource, config); + } + /** + * Validates process resource attribute map from process variables + * + * @param processResource The unsantized resource attributes from process as key/value pairs. + * @param config: Config + * @returns The sanitized resource attributes. + */ + private _getResourceAttributes( + processResource: ResourceAttributes, + _config?: ResourceDetectionConfig + ) { + if ( + processResource[SemanticResourceAttributes.PROCESS_EXECUTABLE_NAME] === + '' || + processResource[SemanticResourceAttributes.PROCESS_EXECUTABLE_PATH] === + '' || + processResource[SemanticResourceAttributes.PROCESS_COMMAND] === '' || + processResource[SemanticResourceAttributes.PROCESS_COMMAND_LINE] === '' || + processResource[SemanticResourceAttributes.PROCESS_RUNTIME_VERSION] === '' + ) { + diag.debug( + 'ProcessDetector failed: Unable to find required process resources. ' + ); + return Resource.empty(); + } else { + return new Resource({ + ...processResource, + }); + } + } +} + +export const processDetectorSync = new ProcessDetectorSync(); diff --git a/packages/opentelemetry-resources/src/detectors/index.ts b/packages/opentelemetry-resources/src/detectors/index.ts index 2a16563da5..5ed2b3f868 100644 --- a/packages/opentelemetry-resources/src/detectors/index.ts +++ b/packages/opentelemetry-resources/src/detectors/index.ts @@ -17,3 +17,6 @@ export * from './BrowserDetector'; export * from './EnvDetector'; export * from './ProcessDetector'; +export * from './BrowserDetectorSync'; +export * from './EnvDetectorSync'; +export * from './ProcessDetectorSync'; diff --git a/packages/opentelemetry-resources/src/index.ts b/packages/opentelemetry-resources/src/index.ts index 4cff4181e3..3f66901fbb 100644 --- a/packages/opentelemetry-resources/src/index.ts +++ b/packages/opentelemetry-resources/src/index.ts @@ -15,7 +15,9 @@ */ export * from './Resource'; +export * from './IResource'; export * from './platform'; export * from './types'; export * from './config'; export * from './detectors'; +export * from './detect-resources'; diff --git a/packages/opentelemetry-resources/src/platform/browser/HostDetectorSync.ts b/packages/opentelemetry-resources/src/platform/browser/HostDetectorSync.ts new file mode 100644 index 0000000000..1962f53028 --- /dev/null +++ b/packages/opentelemetry-resources/src/platform/browser/HostDetectorSync.ts @@ -0,0 +1,19 @@ +/* + * 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 { noopDetectorSync } from '../../detectors/NoopDetectorSync'; + +export const hostDetectorSync = noopDetectorSync; diff --git a/packages/opentelemetry-resources/src/platform/browser/OSDetectorSync.ts b/packages/opentelemetry-resources/src/platform/browser/OSDetectorSync.ts new file mode 100644 index 0000000000..416fc3b4ee --- /dev/null +++ b/packages/opentelemetry-resources/src/platform/browser/OSDetectorSync.ts @@ -0,0 +1,19 @@ +/* + * 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 { noopDetectorSync } from '../../detectors/NoopDetectorSync'; + +export const osDetectorSync = noopDetectorSync; diff --git a/packages/opentelemetry-resources/src/platform/browser/detect-resources.ts b/packages/opentelemetry-resources/src/platform/browser/detect-resources.ts deleted file mode 100644 index 457965fadf..0000000000 --- a/packages/opentelemetry-resources/src/platform/browser/detect-resources.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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 { Resource } from '../../Resource'; -import { ResourceDetectionConfig } from '../../config'; -import { diag } from '@opentelemetry/api'; - -/** - * Runs all resource detectors and returns the results merged into a single - * Resource. - * - * @param config Configuration for resource detection - */ -export const detectResources = async ( - config: ResourceDetectionConfig = {} -): Promise => { - const internalConfig: ResourceDetectionConfig = Object.assign(config); - - const resources: Resource[] = await Promise.all( - (internalConfig.detectors || []).map(async d => { - try { - const resource = await d.detect(internalConfig); - diag.debug(`${d.constructor.name} found resource.`, resource); - return resource; - } catch (e) { - diag.debug(`${d.constructor.name} failed: ${e.message}`); - return Resource.empty(); - } - }) - ); - - return resources.reduce( - (acc, resource) => acc.merge(resource), - Resource.empty() - ); -}; diff --git a/packages/opentelemetry-resources/src/platform/browser/index.ts b/packages/opentelemetry-resources/src/platform/browser/index.ts index 6f3af40a45..b18be97c9c 100644 --- a/packages/opentelemetry-resources/src/platform/browser/index.ts +++ b/packages/opentelemetry-resources/src/platform/browser/index.ts @@ -15,6 +15,7 @@ */ export * from './default-service-name'; -export * from './detect-resources'; export * from './HostDetector'; export * from './OSDetector'; +export * from './HostDetectorSync'; +export * from './OSDetectorSync'; diff --git a/packages/opentelemetry-resources/src/platform/node/HostDetector.ts b/packages/opentelemetry-resources/src/platform/node/HostDetector.ts index c27766bb92..daf0b8e045 100644 --- a/packages/opentelemetry-resources/src/platform/node/HostDetector.ts +++ b/packages/opentelemetry-resources/src/platform/node/HostDetector.ts @@ -14,38 +14,18 @@ * limitations under the License. */ -import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; -import { Resource } from '../../Resource'; -import { Detector, ResourceAttributes } from '../../types'; +import { Detector } from '../../types'; import { ResourceDetectionConfig } from '../../config'; -import { arch, hostname } from 'os'; +import { IResource } from '../../IResource'; +import { hostDetectorSync } from './HostDetectorSync'; /** * HostDetector detects the resources related to the host current process is * running on. Currently only non-cloud-based attributes are included. */ class HostDetector implements Detector { - async detect(_config?: ResourceDetectionConfig): Promise { - const attributes: ResourceAttributes = { - [SemanticResourceAttributes.HOST_NAME]: hostname(), - [SemanticResourceAttributes.HOST_ARCH]: this._normalizeArch(arch()), - }; - return new Resource(attributes); - } - - private _normalizeArch(nodeArchString: string): string { - // Maps from https://nodejs.org/api/os.html#osarch to arch values in spec: - // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/host.md - switch (nodeArchString) { - case 'arm': - return 'arm32'; - case 'ppc': - return 'ppc32'; - case 'x64': - return 'amd64'; - default: - return nodeArchString; - } + detect(_config?: ResourceDetectionConfig): Promise { + return Promise.resolve(hostDetectorSync.detect(_config)); } } diff --git a/packages/opentelemetry-resources/src/platform/node/HostDetectorSync.ts b/packages/opentelemetry-resources/src/platform/node/HostDetectorSync.ts new file mode 100644 index 0000000000..85bd717e54 --- /dev/null +++ b/packages/opentelemetry-resources/src/platform/node/HostDetectorSync.ts @@ -0,0 +1,38 @@ +/* + * 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 { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; +import { Resource } from '../../Resource'; +import { DetectorSync, ResourceAttributes } from '../../types'; +import { ResourceDetectionConfig } from '../../config'; +import { arch, hostname } from 'os'; +import { normalizeArch } from './utils'; + +/** + * HostDetectorSync detects the resources related to the host current process is + * running on. Currently only non-cloud-based attributes are included. + */ +class HostDetectorSync implements DetectorSync { + detect(_config?: ResourceDetectionConfig): Resource { + const attributes: ResourceAttributes = { + [SemanticResourceAttributes.HOST_NAME]: hostname(), + [SemanticResourceAttributes.HOST_ARCH]: normalizeArch(arch()), + }; + return new Resource(attributes); + } +} + +export const hostDetectorSync = new HostDetectorSync(); diff --git a/packages/opentelemetry-resources/src/platform/node/OSDetector.ts b/packages/opentelemetry-resources/src/platform/node/OSDetector.ts index befa9fb1f6..7ee528d497 100644 --- a/packages/opentelemetry-resources/src/platform/node/OSDetector.ts +++ b/packages/opentelemetry-resources/src/platform/node/OSDetector.ts @@ -14,36 +14,18 @@ * limitations under the License. */ -import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; -import { Resource } from '../../Resource'; -import { Detector, ResourceAttributes } from '../../types'; +import { Detector } from '../../types'; import { ResourceDetectionConfig } from '../../config'; -import { platform, release } from 'os'; +import { IResource } from '../../IResource'; +import { osDetectorSync } from './OSDetectorSync'; /** * OSDetector detects the resources related to the operating system (OS) on * which the process represented by this resource is running. */ class OSDetector implements Detector { - async detect(_config?: ResourceDetectionConfig): Promise { - const attributes: ResourceAttributes = { - [SemanticResourceAttributes.OS_TYPE]: this._normalizeType(platform()), - [SemanticResourceAttributes.OS_VERSION]: release(), - }; - return new Resource(attributes); - } - - private _normalizeType(nodePlatform: string): string { - // Maps from https://nodejs.org/api/os.html#osplatform to arch values in spec: - // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/os.md - switch (nodePlatform) { - case 'sunos': - return 'solaris'; - case 'win32': - return 'windows'; - default: - return nodePlatform; - } + detect(_config?: ResourceDetectionConfig): Promise { + return Promise.resolve(osDetectorSync.detect(_config)); } } diff --git a/packages/opentelemetry-resources/src/platform/node/OSDetectorSync.ts b/packages/opentelemetry-resources/src/platform/node/OSDetectorSync.ts new file mode 100644 index 0000000000..9cb6a0385d --- /dev/null +++ b/packages/opentelemetry-resources/src/platform/node/OSDetectorSync.ts @@ -0,0 +1,38 @@ +/* + * 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 { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; +import { Resource } from '../../Resource'; +import { DetectorSync, ResourceAttributes } from '../../types'; +import { ResourceDetectionConfig } from '../../config'; +import { platform, release } from 'os'; +import { normalizeType } from './utils'; + +/** + * OSDetectorSync detects the resources related to the operating system (OS) on + * which the process represented by this resource is running. + */ +class OSDetectorSync implements DetectorSync { + detect(_config?: ResourceDetectionConfig): Resource { + const attributes: ResourceAttributes = { + [SemanticResourceAttributes.OS_TYPE]: normalizeType(platform()), + [SemanticResourceAttributes.OS_VERSION]: release(), + }; + return new Resource(attributes); + } +} + +export const osDetectorSync = new OSDetectorSync(); diff --git a/packages/opentelemetry-resources/src/platform/node/detect-resources.ts b/packages/opentelemetry-resources/src/platform/node/detect-resources.ts deleted file mode 100644 index abe1584dc7..0000000000 --- a/packages/opentelemetry-resources/src/platform/node/detect-resources.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 { Resource } from '../../Resource'; -import { ResourceDetectionConfig } from '../../config'; -import { diag } from '@opentelemetry/api'; -import * as util from 'util'; - -/** - * Runs all resource detectors and returns the results merged into a single - * Resource. - * - * @param config Configuration for resource detection - */ -export const detectResources = async ( - config: ResourceDetectionConfig = {} -): Promise => { - const internalConfig: ResourceDetectionConfig = Object.assign(config); - - const resources: Resource[] = await Promise.all( - (internalConfig.detectors || []).map(async d => { - try { - const resource = await d.detect(internalConfig); - diag.debug(`${d.constructor.name} found resource.`, resource); - return resource; - } catch (e) { - diag.debug(`${d.constructor.name} failed: ${e.message}`); - return Resource.empty(); - } - }) - ); - - // Future check if verbose logging is enabled issue #1903 - logResources(resources); - - return resources.reduce( - (acc, resource) => acc.merge(resource), - Resource.empty() - ); -}; - -/** - * Writes debug information about the detected resources to the logger defined in the resource detection config, if one is provided. - * - * @param resources The array of {@link Resource} that should be logged. Empty entries will be ignored. - */ -const logResources = (resources: Array) => { - resources.forEach(resource => { - // Print only populated resources - if (Object.keys(resource.attributes).length > 0) { - const resourceDebugString = util.inspect(resource.attributes, { - depth: 2, - breakLength: Infinity, - sorted: true, - compact: false, - }); - diag.verbose(resourceDebugString); - } - }); -}; diff --git a/packages/opentelemetry-resources/src/platform/node/index.ts b/packages/opentelemetry-resources/src/platform/node/index.ts index 6f3af40a45..b18be97c9c 100644 --- a/packages/opentelemetry-resources/src/platform/node/index.ts +++ b/packages/opentelemetry-resources/src/platform/node/index.ts @@ -15,6 +15,7 @@ */ export * from './default-service-name'; -export * from './detect-resources'; export * from './HostDetector'; export * from './OSDetector'; +export * from './HostDetectorSync'; +export * from './OSDetectorSync'; diff --git a/packages/opentelemetry-resources/src/platform/node/utils.ts b/packages/opentelemetry-resources/src/platform/node/utils.ts new file mode 100644 index 0000000000..52f01eaa00 --- /dev/null +++ b/packages/opentelemetry-resources/src/platform/node/utils.ts @@ -0,0 +1,42 @@ +/* + * 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. + */ +export const normalizeArch = (nodeArchString: string): string => { + // Maps from https://nodejs.org/api/os.html#osarch to arch values in spec: + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/host.md + switch (nodeArchString) { + case 'arm': + return 'arm32'; + case 'ppc': + return 'ppc32'; + case 'x64': + return 'amd64'; + default: + return nodeArchString; + } +}; + +export const normalizeType = (nodePlatform: string): string => { + // Maps from https://nodejs.org/api/os.html#osplatform to arch values in spec: + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/os.md + switch (nodePlatform) { + case 'sunos': + return 'solaris'; + case 'win32': + return 'windows'; + default: + return nodePlatform; + } +}; diff --git a/packages/opentelemetry-resources/src/types.ts b/packages/opentelemetry-resources/src/types.ts index 717f71381d..d20c09faa2 100644 --- a/packages/opentelemetry-resources/src/types.ts +++ b/packages/opentelemetry-resources/src/types.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { Resource } from './Resource'; import { ResourceDetectionConfig } from './config'; import { SpanAttributes } from '@opentelemetry/api'; +import { IResource } from './IResource'; /** * Interface for Resource attributes. @@ -26,9 +26,16 @@ import { SpanAttributes } from '@opentelemetry/api'; export type ResourceAttributes = SpanAttributes; /** - * Interface for a Resource Detector. In order to detect resources in parallel - * a detector returns a Promise containing a Resource. + * @deprecated please use {@link DetectorSync} */ export interface Detector { - detect(config?: ResourceDetectionConfig): Promise; + detect(config?: ResourceDetectionConfig): Promise; +} + +/** + * Interface for a synchronous Resource Detector. In order to detect attributes asynchronously, a detector + * can pass a Promise as the second parameter to the Resource constructor. + */ +export interface DetectorSync { + detect(config?: ResourceDetectionConfig): IResource; } diff --git a/packages/opentelemetry-resources/src/utils.ts b/packages/opentelemetry-resources/src/utils.ts new file mode 100644 index 0000000000..73d81040b3 --- /dev/null +++ b/packages/opentelemetry-resources/src/utils.ts @@ -0,0 +1,21 @@ +/* + * 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. + */ + +export const isPromiseLike = (val: any): val is PromiseLike => { + return ( + val !== null && typeof val === 'object' && typeof val.then === 'function' + ); +}; diff --git a/packages/opentelemetry-resources/test/Resource.test.ts b/packages/opentelemetry-resources/test/Resource.test.ts index 7d4bcae248..df9430af75 100644 --- a/packages/opentelemetry-resources/test/Resource.test.ts +++ b/packages/opentelemetry-resources/test/Resource.test.ts @@ -14,11 +14,14 @@ * limitations under the License. */ +import * as sinon from 'sinon'; import * as assert from 'assert'; import { SDK_INFO } from '@opentelemetry/core'; -import { Resource } from '../src'; +import { Resource, ResourceAttributes } from '../src'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; import { describeBrowser, describeNode } from './util'; +import { diag } from '@opentelemetry/api'; +import { Resource as Resource190 } from '@opentelemetry/resources_1.9.0'; describe('Resource', () => { const resource1 = new Resource({ @@ -90,6 +93,24 @@ describe('Resource', () => { assert.strictEqual(resource.attributes['custom.boolean'], true); }); + it('should log when accessing attributes before async attributes promise has settled', () => { + const debugStub = sinon.spy(diag, 'error'); + const resource = new Resource( + {}, + new Promise(resolve => { + setTimeout(resolve, 1); + }) + ); + + resource.attributes; + + assert.ok( + debugStub.calledWithMatch( + 'Accessing resource attributes before async attributes settled' + ) + ); + }); + describe('.empty()', () => { it('should return an empty resource (except required service name)', () => { const resource = Resource.empty(); @@ -99,6 +120,160 @@ describe('Resource', () => { it('should return the same empty resource', () => { assert.strictEqual(Resource.empty(), Resource.empty()); }); + + it('should return false for asyncAttributesPending immediately', () => { + assert.ok(!Resource.empty().asyncAttributesPending); + }); + }); + + describe('asynchronous attributes', () => { + afterEach(() => { + sinon.restore(); + }); + + it('should return false for asyncAttributesPending if no promise provided', () => { + assert.ok(!new Resource({ foo: 'bar' }).asyncAttributesPending); + assert.ok(!Resource.empty().asyncAttributesPending); + assert.ok(!Resource.default().asyncAttributesPending); + }); + + it('should return false for asyncAttributesPending once promise settles', async () => { + const clock = sinon.useFakeTimers(); + const resourceResolve = new Resource( + {}, + new Promise(resolve => { + setTimeout(resolve, 1); + }) + ); + const resourceReject = new Resource( + {}, + new Promise((_, reject) => { + setTimeout(reject, 1); + }) + ); + + for (const resource of [resourceResolve, resourceReject]) { + assert.ok(resource.asyncAttributesPending); + await clock.nextAsync(); + await resource.waitForAsyncAttributes(); + assert.ok(!resource.asyncAttributesPending); + } + }); + + it('should merge async attributes into sync attributes once resolved', async () => { + //async attributes that resolve after 1 ms + const asyncAttributes = new Promise(resolve => { + setTimeout( + () => resolve({ async: 'fromasync', shared: 'fromasync' }), + 1 + ); + }); + + const resource = new Resource( + { sync: 'fromsync', shared: 'fromsync' }, + asyncAttributes + ); + + await resource.waitForAsyncAttributes(); + assert.deepStrictEqual(resource.attributes, { + sync: 'fromsync', + // async takes precedence + shared: 'fromasync', + async: 'fromasync', + }); + }); + + it('should merge async attributes when both resources have promises', async () => { + const resource1 = new Resource( + {}, + Promise.resolve({ promise1: 'promise1val', shared: 'promise1val' }) + ); + const resource2 = new Resource( + {}, + Promise.resolve({ promise2: 'promise2val', shared: 'promise2val' }) + ); + // this one rejects + const resource3 = new Resource({}, Promise.reject(new Error('reject'))); + const resource4 = new Resource( + {}, + Promise.resolve({ promise4: 'promise4val', shared: 'promise4val' }) + ); + + const merged = resource1 + .merge(resource2) + .merge(resource3) + .merge(resource4); + + await merged.waitForAsyncAttributes?.(); + + assert.deepStrictEqual(merged.attributes, { + promise1: 'promise1val', + promise2: 'promise2val', + promise4: 'promise4val', + shared: 'promise4val', + }); + }); + + it('should merge async attributes correctly when resource1 fulfils after resource2', async () => { + const resource1 = new Resource( + {}, + Promise.resolve({ promise1: 'promise1val', shared: 'promise1val' }) + ); + + const resource2 = new Resource({ + promise2: 'promise2val', + shared: 'promise2val', + }); + + const merged = resource1.merge(resource2); + + await merged.waitForAsyncAttributes?.(); + + assert.deepStrictEqual(merged.attributes, { + promise1: 'promise1val', + promise2: 'promise2val', + shared: 'promise2val', + }); + }); + + it('should merge async attributes correctly when resource2 fulfils after resource1', async () => { + const resource1 = new Resource( + { shared: 'promise1val' }, + Promise.resolve({ promise1: 'promise1val' }) + ); + + //async attributes that resolve after 1 ms + const asyncAttributes = new Promise(resolve => { + setTimeout( + () => resolve({ promise2: 'promise2val', shared: 'promise2val' }), + 1 + ); + }); + const resource2 = new Resource({}, asyncAttributes); + + const merged = resource1.merge(resource2); + + await merged.waitForAsyncAttributes?.(); + + assert.deepStrictEqual(merged.attributes, { + promise1: 'promise1val', + promise2: 'promise2val', + shared: 'promise2val', + }); + }); + + it('should log when promise rejects', async () => { + const debugStub = sinon.spy(diag, 'debug'); + + const resource = new Resource({}, Promise.reject(new Error('rejected'))); + await resource.waitForAsyncAttributes(); + + assert.ok( + debugStub.calledWithMatch( + "a resource's async attributes promise rejected" + ) + ); + }); }); describeNode('.default()', () => { @@ -144,4 +319,40 @@ describe('Resource', () => { ); }); }); + + describe('compatibility', () => { + it('should merge resource with old implementation', () => { + const resource = Resource.EMPTY; + const oldResource = new Resource190({ fromold: 'fromold' }); + + //TODO: find a solution for ts-ignore + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const mergedResource = resource.merge(oldResource); + + assert.strictEqual(mergedResource.attributes['fromold'], 'fromold'); + }); + + it('should merge resource containing async attributes with old implementation', async () => { + const resource = new Resource( + {}, + Promise.resolve({ fromnew: 'fromnew' }) + ); + + const oldResource = new Resource190({ fromold: 'fromold' }); + + //TODO: find a solution for ts-ignore + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const mergedResource = resource.merge(oldResource); + + assert.strictEqual(mergedResource.attributes['fromold'], 'fromold'); + + await mergedResource.waitForAsyncAttributes?.(); + + assert.strictEqual(mergedResource.attributes['fromnew'], 'fromnew'); + }); + }); }); diff --git a/packages/opentelemetry-resources/test/detect-resources.test.ts b/packages/opentelemetry-resources/test/detect-resources.test.ts new file mode 100644 index 0000000000..0db97057db --- /dev/null +++ b/packages/opentelemetry-resources/test/detect-resources.test.ts @@ -0,0 +1,101 @@ +/* + * 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 { diag } from '@opentelemetry/api'; +import * as assert from 'assert'; +import * as sinon from 'sinon'; +import { Resource, Detector, detectResourcesSync, DetectorSync } from '../src'; +import { describeNode } from './util'; + +describe('detectResourcesSync', () => { + afterEach(() => { + sinon.restore(); + }); + + it('handles resource detectors which return Promise', async () => { + const detector: Detector = { + async detect() { + return new Resource({ sync: 'fromsync' }); + }, + }; + const resource = detectResourcesSync({ + detectors: [detector], + }); + + await resource.waitForAsyncAttributes?.(); + assert.deepStrictEqual(resource.attributes, { + sync: 'fromsync', + }); + }); + + it('handles resource detectors which return Resource with a promise inside', async () => { + const detector: DetectorSync = { + detect() { + return new Resource( + { sync: 'fromsync' }, + Promise.resolve({ async: 'fromasync' }) + ); + }, + }; + const resource = detectResourcesSync({ + detectors: [detector], + }); + + // before waiting, it should already have the sync resources + assert.deepStrictEqual(resource.attributes, { sync: 'fromsync' }); + await resource.waitForAsyncAttributes?.(); + assert.deepStrictEqual(resource.attributes, { + sync: 'fromsync', + async: 'fromasync', + }); + }); + + describeNode('logging', () => { + it("logs when a detector's async attributes promise rejects", async () => { + const debugStub = sinon.spy(diag, 'debug'); + + // use a class so it has a name + class DetectorRejects implements DetectorSync { + detect() { + return new Resource( + { sync: 'fromsync' }, + Promise.reject(new Error('reject')) + ); + } + } + class DetectorOk implements DetectorSync { + detect() { + return new Resource( + { sync: 'fromsync' }, + Promise.resolve({ async: 'fromasync' }) + ); + } + } + + const resource = detectResourcesSync({ + detectors: [new DetectorRejects(), new DetectorOk()], + }); + + await resource.waitForAsyncAttributes?.(); + + assert.ok( + debugStub.calledWithMatch( + "a resource's async attributes promise rejected" + ) + ); + }); + }); +}); diff --git a/packages/opentelemetry-resources/test/detectors/browser/BrowserDetector.test.ts b/packages/opentelemetry-resources/test/detectors/browser/BrowserDetector.test.ts index ee6a92ccce..95c1b95b44 100644 --- a/packages/opentelemetry-resources/test/detectors/browser/BrowserDetector.test.ts +++ b/packages/opentelemetry-resources/test/detectors/browser/BrowserDetector.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import * as sinon from 'sinon'; -import { Resource } from '../../../src'; +import { IResource } from '../../../src'; import { browserDetector } from '../../../src/detectors/BrowserDetector'; import { describeBrowser } from '../../util'; import { @@ -32,7 +32,7 @@ describeBrowser('browserDetector()', () => { userAgent: 'dddd', }); - const resource: Resource = await browserDetector.detect(); + const resource: IResource = await browserDetector.detect(); assertResource(resource, { version: 'dddd', runtimeDescription: 'Web Browser', @@ -43,7 +43,7 @@ describeBrowser('browserDetector()', () => { sinon.stub(globalThis, 'navigator').value({ userAgent: '', }); - const resource: Resource = await browserDetector.detect(); + const resource: IResource = await browserDetector.detect(); assertEmptyResource(resource); }); }); diff --git a/packages/opentelemetry-resources/test/detectors/browser/EnvDetector.test.ts b/packages/opentelemetry-resources/test/detectors/browser/EnvDetector.test.ts index 0e8894ef93..8901595773 100644 --- a/packages/opentelemetry-resources/test/detectors/browser/EnvDetector.test.ts +++ b/packages/opentelemetry-resources/test/detectors/browser/EnvDetector.test.ts @@ -17,7 +17,7 @@ import * as assert from 'assert'; import { RAW_ENVIRONMENT } from '@opentelemetry/core'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; -import { envDetector, Resource } from '../../../src'; +import { envDetector, IResource } from '../../../src'; import { assertEmptyResource, assertWebEngineResource, @@ -39,7 +39,7 @@ describeBrowser('envDetector() on web browser', () => { }); it('should return resource information from environment variable', async () => { - const resource: Resource = await envDetector.detect(); + const resource: IResource = await envDetector.detect(); assertWebEngineResource(resource, { [SemanticResourceAttributes.WEBENGINE_NAME]: 'chromium', [SemanticResourceAttributes.WEBENGINE_VERSION]: '99', @@ -66,7 +66,7 @@ describeBrowser('envDetector() on web browser', () => { }); it('should return empty resource', async () => { - const resource: Resource = await envDetector.detect(); + const resource: IResource = await envDetector.detect(); assertEmptyResource(resource); }); }); @@ -75,14 +75,14 @@ describeBrowser('envDetector() on web browser', () => { describe('with empty env', () => { it('should return empty resource', async () => { - const resource: Resource = await envDetector.detect(); + const resource: IResource = await envDetector.detect(); assertEmptyResource(resource); }); }); describe('with empty env', () => { it('should return empty resource', async () => { - const resource: Resource = await envDetector.detect(); + const resource: IResource = await envDetector.detect(); assertEmptyResource(resource); }); }); diff --git a/packages/opentelemetry-resources/test/detectors/browser/HostDetector.test.ts b/packages/opentelemetry-resources/test/detectors/browser/HostDetector.test.ts index a1299541d9..6f1df6cd23 100644 --- a/packages/opentelemetry-resources/test/detectors/browser/HostDetector.test.ts +++ b/packages/opentelemetry-resources/test/detectors/browser/HostDetector.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import * as sinon from 'sinon'; -import { hostDetector, Resource } from '../../../src'; +import { hostDetector, IResource } from '../../../src'; import { assertEmptyResource } from '../../util/resource-assertions'; import { describeBrowser } from '../../util'; @@ -24,7 +24,7 @@ describeBrowser('hostDetector() on web browser', () => { }); it('should return empty resource', async () => { - const resource: Resource = await hostDetector.detect(); + const resource: IResource = await hostDetector.detect(); assertEmptyResource(resource); }); }); diff --git a/packages/opentelemetry-resources/test/detectors/browser/OSDetector.test.ts b/packages/opentelemetry-resources/test/detectors/browser/OSDetector.test.ts index 991aa05271..bb69f73954 100644 --- a/packages/opentelemetry-resources/test/detectors/browser/OSDetector.test.ts +++ b/packages/opentelemetry-resources/test/detectors/browser/OSDetector.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import * as sinon from 'sinon'; -import { osDetector, Resource } from '../../../src'; +import { osDetector, IResource } from '../../../src'; import { assertEmptyResource } from '../../util/resource-assertions'; import { describeBrowser } from '../../util'; @@ -24,7 +24,7 @@ describeBrowser('osDetector() on web browser', () => { }); it('should return empty resource', async () => { - const resource: Resource = await osDetector.detect(); + const resource: IResource = await osDetector.detect(); assertEmptyResource(resource); }); }); diff --git a/packages/opentelemetry-resources/test/detectors/browser/ProcessDetector.test.ts b/packages/opentelemetry-resources/test/detectors/browser/ProcessDetector.test.ts index ae4c824ce0..7c404e67ed 100644 --- a/packages/opentelemetry-resources/test/detectors/browser/ProcessDetector.test.ts +++ b/packages/opentelemetry-resources/test/detectors/browser/ProcessDetector.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import * as sinon from 'sinon'; -import { processDetector, Resource } from '../../../src'; +import { processDetector, IResource } from '../../../src'; import { assertEmptyResource } from '../../util/resource-assertions'; import { describeBrowser } from '../../util'; @@ -24,7 +24,7 @@ describeBrowser('processDetector() on web browser', () => { }); it('should return empty resource', async () => { - const resource: Resource = await processDetector.detect(); + const resource: IResource = await processDetector.detect(); assertEmptyResource(resource); }); }); diff --git a/packages/opentelemetry-resources/test/detectors/node/BrowserDetector.test.ts b/packages/opentelemetry-resources/test/detectors/node/BrowserDetector.test.ts index 73873b1e1a..e8fff65750 100644 --- a/packages/opentelemetry-resources/test/detectors/node/BrowserDetector.test.ts +++ b/packages/opentelemetry-resources/test/detectors/node/BrowserDetector.test.ts @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Resource } from '../../../src'; +import { IResource } from '../../../src'; import { browserDetector } from '../../../src/detectors/BrowserDetector'; import { describeNode } from '../../util'; import { assertEmptyResource } from '../../util/resource-assertions'; describeNode('browserDetector()', () => { it('should return empty resources if window.document is missing', async () => { - const resource: Resource = await browserDetector.detect(); + const resource: IResource = await browserDetector.detect(); assertEmptyResource(resource); }); }); diff --git a/packages/opentelemetry-resources/test/detectors/node/EnvDetector.test.ts b/packages/opentelemetry-resources/test/detectors/node/EnvDetector.test.ts index 1397978377..b4ccdd1ad3 100644 --- a/packages/opentelemetry-resources/test/detectors/node/EnvDetector.test.ts +++ b/packages/opentelemetry-resources/test/detectors/node/EnvDetector.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { envDetector, Resource } from '../../../src'; +import { envDetector, IResource } from '../../../src'; import { assertK8sResource, assertEmptyResource, @@ -33,7 +33,7 @@ describeNode('envDetector() on Node.js', () => { }); it('should return resource information from environment variable', async () => { - const resource: Resource = await envDetector.detect(); + const resource: IResource = await envDetector.detect(); assertK8sResource(resource, { podName: 'pod-xyz-123', clusterName: 'c1', @@ -57,7 +57,7 @@ describeNode('envDetector() on Node.js', () => { }); it('should return empty resource', async () => { - const resource: Resource = await envDetector.detect(); + const resource: IResource = await envDetector.detect(); assertEmptyResource(resource); }); }); @@ -66,7 +66,7 @@ describeNode('envDetector() on Node.js', () => { describe('with empty env', () => { it('should return empty resource', async () => { - const resource: Resource = await envDetector.detect(); + const resource: IResource = await envDetector.detect(); assertEmptyResource(resource); }); }); diff --git a/packages/opentelemetry-resources/test/detectors/node/HostDetector.test.ts b/packages/opentelemetry-resources/test/detectors/node/HostDetector.test.ts index 3c809d500d..287b98b10b 100644 --- a/packages/opentelemetry-resources/test/detectors/node/HostDetector.test.ts +++ b/packages/opentelemetry-resources/test/detectors/node/HostDetector.test.ts @@ -18,7 +18,7 @@ import * as sinon from 'sinon'; import * as assert from 'assert'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; import { describeNode } from '../../util'; -import { hostDetector, Resource } from '../../../src'; +import { hostDetector, IResource } from '../../../src'; describeNode('hostDetector() on Node.js', () => { afterEach(() => { @@ -31,7 +31,7 @@ describeNode('hostDetector() on Node.js', () => { sinon.stub(os, 'arch').returns('x64'); sinon.stub(os, 'hostname').returns('opentelemetry-test'); - const resource: Resource = await hostDetector.detect(); + const resource: IResource = await hostDetector.detect(); assert.strictEqual( resource.attributes[SemanticResourceAttributes.HOST_NAME], @@ -48,7 +48,7 @@ describeNode('hostDetector() on Node.js', () => { sinon.stub(os, 'arch').returns('some-unknown-arch'); - const resource: Resource = await hostDetector.detect(); + const resource: IResource = await hostDetector.detect(); assert.strictEqual( resource.attributes[SemanticResourceAttributes.HOST_ARCH], diff --git a/packages/opentelemetry-resources/test/detectors/node/OSDetector.test.ts b/packages/opentelemetry-resources/test/detectors/node/OSDetector.test.ts index dd3ec3e24b..58b1989ec3 100644 --- a/packages/opentelemetry-resources/test/detectors/node/OSDetector.test.ts +++ b/packages/opentelemetry-resources/test/detectors/node/OSDetector.test.ts @@ -18,7 +18,7 @@ import * as sinon from 'sinon'; import * as assert from 'assert'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; import { describeNode } from '../../util'; -import { osDetector, Resource } from '../../../src'; +import { osDetector, IResource } from '../../../src'; describeNode('osDetector() on Node.js', () => { afterEach(() => { @@ -31,7 +31,7 @@ describeNode('osDetector() on Node.js', () => { sinon.stub(os, 'platform').returns('win32'); sinon.stub(os, 'release').returns('2.2.1(0.289/5/3)'); - const resource: Resource = await osDetector.detect(); + const resource: IResource = await osDetector.detect(); assert.strictEqual( resource.attributes[SemanticResourceAttributes.OS_TYPE], @@ -48,7 +48,7 @@ describeNode('osDetector() on Node.js', () => { sinon.stub(os, 'platform').returns('some-unknown-platform'); - const resource: Resource = await osDetector.detect(); + const resource: IResource = await osDetector.detect(); assert.strictEqual( resource.attributes[SemanticResourceAttributes.OS_TYPE], diff --git a/packages/opentelemetry-resources/test/detectors/node/ProcessDetector.test.ts b/packages/opentelemetry-resources/test/detectors/node/ProcessDetector.test.ts index dc1a3473a4..6f5793b6ae 100644 --- a/packages/opentelemetry-resources/test/detectors/node/ProcessDetector.test.ts +++ b/packages/opentelemetry-resources/test/detectors/node/ProcessDetector.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import * as sinon from 'sinon'; -import { processDetector, Resource } from '../../../src'; +import { processDetector, IResource } from '../../../src'; import { assertResource, assertEmptyResource, @@ -34,7 +34,7 @@ describeNode('processDetector() on Node.js', () => { .value(['/tmp/node', '/home/ot/test.js', 'arg1', 'arg2']); sinon.stub(process, 'versions').value({ node: '1.4.1' }); - const resource: Resource = await processDetector.detect(); + const resource: IResource = await processDetector.detect(); assertResource(resource, { pid: 1234, name: 'otProcess', @@ -49,7 +49,7 @@ describeNode('processDetector() on Node.js', () => { sinon.stub(process, 'pid').value(1234); sinon.stub(process, 'title').value(undefined); sinon.stub(process, 'argv').value([]); - const resource: Resource = await processDetector.detect(); + const resource: IResource = await processDetector.detect(); assertEmptyResource(resource); }); }); diff --git a/packages/opentelemetry-resources/test/util/resource-assertions.ts b/packages/opentelemetry-resources/test/util/resource-assertions.ts index 70ab2c8c33..fdf541305e 100644 --- a/packages/opentelemetry-resources/test/util/resource-assertions.ts +++ b/packages/opentelemetry-resources/test/util/resource-assertions.ts @@ -16,7 +16,7 @@ import { SDK_INFO } from '@opentelemetry/core'; import * as assert from 'assert'; -import { Resource } from '../../src/Resource'; +import { IResource } from '../../src/IResource'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; /** @@ -26,7 +26,7 @@ import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions' * @param validations validations for the resource attributes */ export const assertCloudResource = ( - resource: Resource, + resource: IResource, validations: { provider?: string; accountId?: string; @@ -64,7 +64,7 @@ export const assertCloudResource = ( * @param validations validations for the resource attributes */ export const assertContainerResource = ( - resource: Resource, + resource: IResource, validations: { name?: string; id?: string; @@ -102,7 +102,7 @@ export const assertContainerResource = ( * @param validations validations for the resource attributes */ export const assertHostResource = ( - resource: Resource, + resource: IResource, validations: { hostName?: string; id?: string; @@ -153,7 +153,7 @@ export const assertHostResource = ( * @param validations validations for the resource attributes */ export const assertK8sResource = ( - resource: Resource, + resource: IResource, validations: { clusterName?: string; namespaceName?: string; @@ -191,7 +191,7 @@ export const assertK8sResource = ( * @param validations validations for the resource attributes */ export const assertTelemetrySDKResource = ( - resource: Resource, + resource: IResource, validations: { name?: string; language?: string; @@ -229,7 +229,7 @@ export const assertTelemetrySDKResource = ( * @param validations validations for the resource attributes */ export const assertServiceResource = ( - resource: Resource, + resource: IResource, validations: { name: string; instanceId: string; @@ -264,7 +264,7 @@ export const assertServiceResource = ( * @param validations validations for the resource attributes */ export const assertResource = ( - resource: Resource, + resource: IResource, validations: { pid?: number; name?: string; @@ -320,7 +320,7 @@ export const assertResource = ( }; export const assertWebEngineResource = ( - resource: Resource, + resource: IResource, validations: { name?: string; version?: string; @@ -352,11 +352,11 @@ export const assertWebEngineResource = ( * * @param resource the Resource to validate */ -export const assertEmptyResource = (resource: Resource) => { +export const assertEmptyResource = (resource: IResource) => { assert.strictEqual(Object.keys(resource.attributes).length, 0); }; -const assertHasOneLabel = (prefix: string, resource: Resource): void => { +const assertHasOneLabel = (prefix: string, resource: IResource): void => { const hasOne = Object.entries(SemanticResourceAttributes).find( ([key, value]) => { return ( diff --git a/packages/opentelemetry-sdk-trace-base/package.json b/packages/opentelemetry-sdk-trace-base/package.json index b0f0967ead..a76944c137 100644 --- a/packages/opentelemetry-sdk-trace-base/package.json +++ b/packages/opentelemetry-sdk-trace-base/package.json @@ -65,6 +65,7 @@ }, "devDependencies": { "@opentelemetry/api": ">=1.0.0 <1.5.0", + "@opentelemetry/resources_1.9.0": "npm:@opentelemetry/resources@1.9.0", "@types/mocha": "10.0.0", "@types/node": "18.6.5", "@types/sinon": "10.0.13", diff --git a/packages/opentelemetry-sdk-trace-base/src/BasicTracerProvider.ts b/packages/opentelemetry-sdk-trace-base/src/BasicTracerProvider.ts index 179b6b1d3e..c06f5cdcb6 100644 --- a/packages/opentelemetry-sdk-trace-base/src/BasicTracerProvider.ts +++ b/packages/opentelemetry-sdk-trace-base/src/BasicTracerProvider.ts @@ -29,7 +29,7 @@ import { getEnv, merge, } from '@opentelemetry/core'; -import { Resource } from '@opentelemetry/resources'; +import { IResource, Resource } from '@opentelemetry/resources'; import { SpanProcessor, Tracer } from '.'; import { loadDefaultConfig } from './config'; import { MultiSpanProcessor } from './MultiSpanProcessor'; @@ -71,7 +71,7 @@ export class BasicTracerProvider implements TracerProvider { private readonly _tracers: Map = new Map(); activeSpanProcessor: SpanProcessor; - readonly resource: Resource; + readonly resource: IResource; constructor(config: TracerConfig = {}) { const mergedConfig = merge( diff --git a/packages/opentelemetry-sdk-trace-base/src/Span.ts b/packages/opentelemetry-sdk-trace-base/src/Span.ts index 7677e6f5ce..fb2b717d48 100644 --- a/packages/opentelemetry-sdk-trace-base/src/Span.ts +++ b/packages/opentelemetry-sdk-trace-base/src/Span.ts @@ -42,7 +42,7 @@ import { otperformance, sanitizeAttributes, } from '@opentelemetry/core'; -import { Resource } from '@opentelemetry/resources'; +import { IResource } from '@opentelemetry/resources'; import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import { ExceptionEventName } from './enums'; import { ReadableSpan } from './export/ReadableSpan'; @@ -64,7 +64,7 @@ export class Span implements APISpan, ReadableSpan { readonly links: Link[] = []; readonly events: TimedEvent[] = []; readonly startTime: HrTime; - readonly resource: Resource; + readonly resource: IResource; readonly instrumentationLibrary: InstrumentationLibrary; name: string; status: SpanStatus = { diff --git a/packages/opentelemetry-sdk-trace-base/src/Tracer.ts b/packages/opentelemetry-sdk-trace-base/src/Tracer.ts index 75443cea88..b77a9427ec 100644 --- a/packages/opentelemetry-sdk-trace-base/src/Tracer.ts +++ b/packages/opentelemetry-sdk-trace-base/src/Tracer.ts @@ -20,7 +20,7 @@ import { sanitizeAttributes, isTracingSuppressed, } from '@opentelemetry/core'; -import { Resource } from '@opentelemetry/resources'; +import { IResource } from '@opentelemetry/resources'; import { BasicTracerProvider } from './BasicTracerProvider'; import { Span } from './Span'; import { GeneralLimits, SpanLimits, TracerConfig } from './types'; @@ -38,7 +38,7 @@ export class Tracer implements api.Tracer { private readonly _generalLimits: GeneralLimits; private readonly _spanLimits: SpanLimits; private readonly _idGenerator: IdGenerator; - readonly resource: Resource; + readonly resource: IResource; readonly instrumentationLibrary: InstrumentationLibrary; /** diff --git a/packages/opentelemetry-sdk-trace-base/src/export/BatchSpanProcessorBase.ts b/packages/opentelemetry-sdk-trace-base/src/export/BatchSpanProcessorBase.ts index 1a0968642d..2f14b77c74 100644 --- a/packages/opentelemetry-sdk-trace-base/src/export/BatchSpanProcessorBase.ts +++ b/packages/opentelemetry-sdk-trace-base/src/export/BatchSpanProcessorBase.ts @@ -160,10 +160,11 @@ export abstract class BatchSpanProcessorBase context.with(suppressTracing(context.active()), () => { // Reset the finished spans buffer here because the next invocations of the _flush method // could pass the same finished spans to the exporter if the buffer is cleared - // outside of the execution of this callback. - this._exporter.export( - this._finishedSpans.splice(0, this._maxExportBatchSize), - result => { + // outside the execution of this callback. + const spans = this._finishedSpans.splice(0, this._maxExportBatchSize); + + const doExport = () => + this._exporter.export(spans, result => { clearTimeout(timer); if (result.code === ExportResultCode.SUCCESS) { resolve(); @@ -173,8 +174,24 @@ export abstract class BatchSpanProcessorBase new Error('BatchSpanProcessor: span export failed') ); } - } - ); + }); + const pendingResources = spans + .map(span => span.resource) + .filter(resource => resource.asyncAttributesPending); + + // Avoid scheduling a promise to make the behavior more predictable and easier to test + if (pendingResources.length === 0) { + doExport(); + } else { + Promise.all( + pendingResources.map(resource => + resource.waitForAsyncAttributes?.() + ) + ).then(doExport, err => { + globalErrorHandler(err); + reject(err); + }); + } }); }); } diff --git a/packages/opentelemetry-sdk-trace-base/src/export/ReadableSpan.ts b/packages/opentelemetry-sdk-trace-base/src/export/ReadableSpan.ts index 8552134a56..aa19891099 100644 --- a/packages/opentelemetry-sdk-trace-base/src/export/ReadableSpan.ts +++ b/packages/opentelemetry-sdk-trace-base/src/export/ReadableSpan.ts @@ -22,7 +22,7 @@ import { Link, SpanContext, } from '@opentelemetry/api'; -import { Resource } from '@opentelemetry/resources'; +import { IResource } from '@opentelemetry/resources'; import { InstrumentationLibrary } from '@opentelemetry/core'; import { TimedEvent } from '../TimedEvent'; @@ -39,6 +39,6 @@ export interface ReadableSpan { readonly events: TimedEvent[]; readonly duration: HrTime; readonly ended: boolean; - readonly resource: Resource; + readonly resource: IResource; readonly instrumentationLibrary: InstrumentationLibrary; } diff --git a/packages/opentelemetry-sdk-trace-base/src/export/SimpleSpanProcessor.ts b/packages/opentelemetry-sdk-trace-base/src/export/SimpleSpanProcessor.ts index 5e6064d03e..673e631c8d 100644 --- a/packages/opentelemetry-sdk-trace-base/src/export/SimpleSpanProcessor.ts +++ b/packages/opentelemetry-sdk-trace-base/src/export/SimpleSpanProcessor.ts @@ -26,6 +26,7 @@ import { Span } from '../Span'; import { SpanProcessor } from '../SpanProcessor'; import { ReadableSpan } from './ReadableSpan'; import { SpanExporter } from './SpanExporter'; +import { Resource } from '@opentelemetry/resources'; /** * An implementation of the {@link SpanProcessor} that converts the {@link Span} @@ -35,17 +36,18 @@ import { SpanExporter } from './SpanExporter'; */ export class SimpleSpanProcessor implements SpanProcessor { private _shutdownOnce: BindOnceFuture; + private _unresolvedExports: Set>; constructor(private readonly _exporter: SpanExporter) { this._shutdownOnce = new BindOnceFuture(this._shutdown, this); + this._unresolvedExports = new Set>(); } - forceFlush(): Promise { - // do nothing as all spans are being exported without waiting - return Promise.resolve(); + async forceFlush(): Promise { + // await unresolved resources before resolving + await Promise.all(Array.from(this._unresolvedExports)); } - // does nothing. onStart(_span: Span, _parentContext: Context): void {} onEnd(span: ReadableSpan): void { @@ -57,21 +59,40 @@ export class SimpleSpanProcessor implements SpanProcessor { return; } - internal - ._export(this._exporter, [span]) - .then((result: ExportResult) => { - if (result.code !== ExportResultCode.SUCCESS) { - globalErrorHandler( - result.error ?? - new Error( - `SimpleSpanProcessor: span export failed (status ${result})` - ) - ); - } - }) - .catch(error => { - globalErrorHandler(error); - }); + const doExport = () => + internal + ._export(this._exporter, [span]) + .then((result: ExportResult) => { + if (result.code !== ExportResultCode.SUCCESS) { + globalErrorHandler( + result.error ?? + new Error( + `SimpleSpanProcessor: span export failed (status ${result})` + ) + ); + } + }) + .catch(error => { + globalErrorHandler(error); + }); + + // Avoid scheduling a promise to make the behavior more predictable and easier to test + if (span.resource.asyncAttributesPending) { + const exportPromise = (span.resource as Resource) + .waitForAsyncAttributes() + .then( + () => { + this._unresolvedExports.delete(exportPromise); + return doExport(); + }, + err => globalErrorHandler(err) + ); + + // store the unresolved exports + this._unresolvedExports.add(exportPromise); + } else { + void doExport(); + } } shutdown(): Promise { diff --git a/packages/opentelemetry-sdk-trace-base/src/types.ts b/packages/opentelemetry-sdk-trace-base/src/types.ts index 54eaaf97d9..6854f0315a 100644 --- a/packages/opentelemetry-sdk-trace-base/src/types.ts +++ b/packages/opentelemetry-sdk-trace-base/src/types.ts @@ -15,7 +15,7 @@ */ import { ContextManager, TextMapPropagator } from '@opentelemetry/api'; -import { Resource } from '@opentelemetry/resources'; +import { IResource } from '@opentelemetry/resources'; import { IdGenerator } from './IdGenerator'; import { Sampler } from './Sampler'; @@ -35,7 +35,7 @@ export interface TracerConfig { spanLimits?: SpanLimits; /** Resource associated with trace telemetry */ - resource?: Resource; + resource?: IResource; /** * Generator of trace and span IDs diff --git a/packages/opentelemetry-sdk-trace-base/test/common/export/BatchSpanProcessorBase.test.ts b/packages/opentelemetry-sdk-trace-base/test/common/export/BatchSpanProcessorBase.test.ts index 9149e6cbf8..fd9574537b 100644 --- a/packages/opentelemetry-sdk-trace-base/test/common/export/BatchSpanProcessorBase.test.ts +++ b/packages/opentelemetry-sdk-trace-base/test/common/export/BatchSpanProcessorBase.test.ts @@ -34,6 +34,7 @@ import { TestRecordOnlySampler } from './TestRecordOnlySampler'; import { TestTracingSpanExporter } from './TestTracingSpanExporter'; import { TestStackContextManager } from './TestStackContextManager'; import { BatchSpanProcessorBase } from '../../../src/export/BatchSpanProcessorBase'; +import { Resource, ResourceAttributes } from '@opentelemetry/resources'; function createSampledSpan(spanName: string): Span { const tracer = new BasicTracerProvider({ @@ -390,6 +391,27 @@ describe('BatchSpanProcessorBase', () => { done(); }); }); + + it('should wait for pending resource on flush', async () => { + const tracer = new BasicTracerProvider({ + resource: new Resource( + {}, + new Promise(resolve => { + setTimeout(() => resolve({ async: 'fromasync' }), 1); + }) + ), + }).getTracer('default'); + + const span = tracer.startSpan('test') as Span; + span.end(); + + processor.onStart(span, ROOT_CONTEXT); + processor.onEnd(span); + + await processor.forceFlush(); + + assert.strictEqual(exporter.getFinishedSpans().length, 1); + }); }); describe('flushing spans with exporter triggering instrumentation', () => { diff --git a/packages/opentelemetry-sdk-trace-base/test/common/export/SimpleSpanProcessor.test.ts b/packages/opentelemetry-sdk-trace-base/test/common/export/SimpleSpanProcessor.test.ts index fda60f500e..88490c527b 100644 --- a/packages/opentelemetry-sdk-trace-base/test/common/export/SimpleSpanProcessor.test.ts +++ b/packages/opentelemetry-sdk-trace-base/test/common/export/SimpleSpanProcessor.test.ts @@ -36,6 +36,9 @@ import { } from '../../../src'; import { TestStackContextManager } from './TestStackContextManager'; import { TestTracingSpanExporter } from './TestTracingSpanExporter'; +import { Resource, ResourceAttributes } from '@opentelemetry/resources'; +import { Resource as Resource190 } from '@opentelemetry/resources_1.9.0'; +import { TestExporterWithDelay } from './TestExporterWithDelay'; describe('SimpleSpanProcessor', () => { let provider: BasicTracerProvider; @@ -149,6 +152,83 @@ describe('SimpleSpanProcessor', () => { }); describe('force flush', () => { + it('should await unresolved resources', async () => { + const processor = new SimpleSpanProcessor(exporter); + const providerWithAsyncResource = new BasicTracerProvider({ + resource: new Resource( + {}, + new Promise(resolve => { + setTimeout(() => resolve({ async: 'fromasync' }), 1); + }) + ), + }); + const spanContext: SpanContext = { + traceId: 'a3cda95b652f4a1592b449d5929fda1b', + spanId: '5e0c63257de34c92', + traceFlags: TraceFlags.SAMPLED, + }; + const span = new Span( + providerWithAsyncResource.getTracer('default'), + ROOT_CONTEXT, + 'span-name', + spanContext, + SpanKind.CLIENT + ); + processor.onStart(span, ROOT_CONTEXT); + assert.strictEqual(exporter.getFinishedSpans().length, 0); + + processor.onEnd(span); + assert.strictEqual(exporter.getFinishedSpans().length, 0); + + await processor.forceFlush(); + + const exportedSpans = exporter.getFinishedSpans(); + + assert.strictEqual(exportedSpans.length, 1); + assert.strictEqual( + exportedSpans[0].resource.attributes['async'], + 'fromasync' + ); + }); + + it('should await doExport() and delete from _unresolvedExports', async () => { + const testExporterWithDelay = new TestExporterWithDelay(); + const processor = new SimpleSpanProcessor(testExporterWithDelay); + + const providerWithAsyncResource = new BasicTracerProvider({ + resource: new Resource( + {}, + new Promise(resolve => { + setTimeout(() => resolve({ async: 'fromasync' }), 1); + }) + ), + }); + const spanContext: SpanContext = { + traceId: 'a3cda95b652f4a1592b449d5929fda1b', + spanId: '5e0c63257de34c92', + traceFlags: TraceFlags.SAMPLED, + }; + const span = new Span( + providerWithAsyncResource.getTracer('default'), + ROOT_CONTEXT, + 'span-name', + spanContext, + SpanKind.CLIENT + ); + processor.onStart(span, ROOT_CONTEXT); + processor.onEnd(span); + + assert.strictEqual(processor['_unresolvedExports'].size, 1); + + await processor.forceFlush(); + + assert.strictEqual(processor['_unresolvedExports'].size, 0); + + const exportedSpans = testExporterWithDelay.getFinishedSpans(); + + assert.strictEqual(exportedSpans.length, 1); + }); + describe('when flushing complete', () => { it('should call an async callback', done => { const processor = new SimpleSpanProcessor(exporter); @@ -203,4 +283,36 @@ describe('SimpleSpanProcessor', () => { assert.equal(exporterCreatedSpans.length, 0); }); }); + + describe('compatibility', () => { + it('should export when using old resource implementation', async () => { + const processor = new SimpleSpanProcessor(exporter); + const providerWithAsyncResource = new BasicTracerProvider({ + resource: new Resource190({ fromold: 'fromold' }), + }); + const spanContext: SpanContext = { + traceId: 'a3cda95b652f4a1592b449d5929fda1b', + spanId: '5e0c63257de34c92', + traceFlags: TraceFlags.SAMPLED, + }; + const span = new Span( + providerWithAsyncResource.getTracer('default'), + ROOT_CONTEXT, + 'span-name', + spanContext, + SpanKind.CLIENT + ); + processor.onStart(span, ROOT_CONTEXT); + assert.strictEqual(exporter.getFinishedSpans().length, 0); + processor.onEnd(span); + + const exportedSpans = exporter.getFinishedSpans(); + + assert.strictEqual(exportedSpans.length, 1); + assert.strictEqual( + exportedSpans[0].resource.attributes['fromold'], + 'fromold' + ); + }); + }); }); diff --git a/packages/opentelemetry-sdk-trace-base/test/common/export/TestExporterWithDelay.ts b/packages/opentelemetry-sdk-trace-base/test/common/export/TestExporterWithDelay.ts new file mode 100644 index 0000000000..d10dab5271 --- /dev/null +++ b/packages/opentelemetry-sdk-trace-base/test/common/export/TestExporterWithDelay.ts @@ -0,0 +1,51 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ExportResult } from '@opentelemetry/core'; +import { InMemorySpanExporter, ReadableSpan } from '../../../src'; + +/** + * A test-only exporter that delays during export to mimic a real exporter. + */ +export class TestExporterWithDelay extends InMemorySpanExporter { + private _exporterCreatedSpans: ReadableSpan[] = []; + + constructor() { + super(); + } + + override export( + spans: ReadableSpan[], + resultCallback: (result: ExportResult) => void + ): void { + super.export(spans, () => setTimeout(resultCallback, 1)); + } + + override shutdown(): Promise { + return super.shutdown().then(() => { + this._exporterCreatedSpans = []; + }); + } + + override reset() { + super.reset(); + this._exporterCreatedSpans = []; + } + + getExporterCreatedSpans(): ReadableSpan[] { + return this._exporterCreatedSpans; + } +} diff --git a/packages/sdk-metrics/src/MeterProvider.ts b/packages/sdk-metrics/src/MeterProvider.ts index 72f0945b9f..f10cf42b9b 100644 --- a/packages/sdk-metrics/src/MeterProvider.ts +++ b/packages/sdk-metrics/src/MeterProvider.ts @@ -21,7 +21,7 @@ import { MeterOptions, createNoopMeter, } from '@opentelemetry/api'; -import { Resource } from '@opentelemetry/resources'; +import { IResource, Resource } from '@opentelemetry/resources'; import { MetricReader } from './export/MetricReader'; import { MeterProviderSharedState } from './state/MeterProviderSharedState'; import { MetricCollector } from './state/MetricCollector'; @@ -33,7 +33,7 @@ import { View } from './view/View'; */ export interface MeterProviderOptions { /** Resource associated with metric telemetry */ - resource?: Resource; + resource?: IResource; views?: View[]; } diff --git a/packages/sdk-metrics/src/export/MetricData.ts b/packages/sdk-metrics/src/export/MetricData.ts index e7adfa0357..931be0c05b 100644 --- a/packages/sdk-metrics/src/export/MetricData.ts +++ b/packages/sdk-metrics/src/export/MetricData.ts @@ -16,7 +16,7 @@ import { HrTime, MetricAttributes } from '@opentelemetry/api'; import { InstrumentationScope } from '@opentelemetry/core'; -import { Resource } from '@opentelemetry/resources'; +import { IResource } from '@opentelemetry/resources'; import { InstrumentDescriptor } from '../InstrumentDescriptor'; import { AggregationTemporality } from './AggregationTemporality'; import { Histogram } from '../aggregator/types'; @@ -67,7 +67,7 @@ export interface ScopeMetrics { } export interface ResourceMetrics { - resource: Resource; + resource: IResource; scopeMetrics: ScopeMetrics[]; } diff --git a/packages/sdk-metrics/src/export/PeriodicExportingMetricReader.ts b/packages/sdk-metrics/src/export/PeriodicExportingMetricReader.ts index c156e883ad..2371ecb67f 100644 --- a/packages/sdk-metrics/src/export/PeriodicExportingMetricReader.ts +++ b/packages/sdk-metrics/src/export/PeriodicExportingMetricReader.ts @@ -24,6 +24,7 @@ import { import { MetricReader } from './MetricReader'; import { PushMetricExporter } from './MetricExporter'; import { callWithTimeout, TimeoutError } from '../utils'; +import { diag } from '@opentelemetry/api'; export type PeriodicExportingMetricReaderOptions = { /** @@ -117,11 +118,24 @@ export class PeriodicExportingMetricReader extends MetricReader { ); } - const result = await internal._export(this._exporter, resourceMetrics); - if (result.code !== ExportResultCode.SUCCESS) { - throw new Error( - `PeriodicExportingMetricReader: metrics export failed (error ${result.error})` - ); + const doExport = async () => { + const result = await internal._export(this._exporter, resourceMetrics); + if (result.code !== ExportResultCode.SUCCESS) { + throw new Error( + `PeriodicExportingMetricReader: metrics export failed (error ${result.error})` + ); + } + }; + + // Avoid scheduling a promise to make the behavior more predictable and easier to test + if (resourceMetrics.resource.asyncAttributesPending) { + resourceMetrics.resource + .waitForAsyncAttributes?.() + .then(doExport, err => + diag.debug('Error while resolving async portion of resource: ', err) + ); + } else { + await doExport(); } } diff --git a/packages/sdk-metrics/src/state/MeterProviderSharedState.ts b/packages/sdk-metrics/src/state/MeterProviderSharedState.ts index a63f53d51d..fa7903b20e 100644 --- a/packages/sdk-metrics/src/state/MeterProviderSharedState.ts +++ b/packages/sdk-metrics/src/state/MeterProviderSharedState.ts @@ -15,7 +15,7 @@ */ import { InstrumentationScope } from '@opentelemetry/core'; -import { Resource } from '@opentelemetry/resources'; +import { IResource } from '@opentelemetry/resources'; import { Aggregation, InstrumentType } from '..'; import { instrumentationScopeId } from '../utils'; import { ViewRegistry } from '../view/ViewRegistry'; @@ -32,7 +32,7 @@ export class MeterProviderSharedState { meterSharedStates: Map = new Map(); - constructor(public resource: Resource) {} + constructor(public resource: IResource) {} getMeterSharedState(instrumentationScope: InstrumentationScope) { const id = instrumentationScopeId(instrumentationScope); From 86973eb79bdbd03f4826b799ca2b7a388dbbe951 Mon Sep 17 00:00:00 2001 From: Marcus Griep Date: Tue, 21 Feb 2023 23:19:30 -0700 Subject: [PATCH 18/37] feat(sdk-trace-base): add diagnostic logging when spans are dropped (#3610) --- CHANGELOG.md | 1 + .../src/export/BatchSpanProcessorBase.ts | 16 +++++++++ .../export/BatchSpanProcessorBase.test.ts | 34 +++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ed63cceff..43aa4482e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/ * feat(sdk-metrics): apply binary search in histogram recording [#3539](https://github.com/open-telemetry/opentelemetry-js/pull/3539) @legendecas * perf(propagator-jaeger): improve deserializeSpanContext performance [#3541](https://github.com/open-telemetry/opentelemetry-js/pull/3541) @doochik * feat: support TraceState in SamplingResult [#3530](https://github.com/open-telemetry/opentelemetry-js/pull/3530) @raphael-theriault-swi +* feat(sdk-trace-base): add diagnostic logging when spans are dropped [#3610](https://github.com/open-telemetry/opentelemetry-js/pull/3610) @neoeinstein ### :bug: (Bug Fix) diff --git a/packages/opentelemetry-sdk-trace-base/src/export/BatchSpanProcessorBase.ts b/packages/opentelemetry-sdk-trace-base/src/export/BatchSpanProcessorBase.ts index 2f14b77c74..d760ff5809 100644 --- a/packages/opentelemetry-sdk-trace-base/src/export/BatchSpanProcessorBase.ts +++ b/packages/opentelemetry-sdk-trace-base/src/export/BatchSpanProcessorBase.ts @@ -44,6 +44,7 @@ export abstract class BatchSpanProcessorBase private _finishedSpans: ReadableSpan[] = []; private _timer: NodeJS.Timeout | undefined; private _shutdownOnce: BindOnceFuture; + private _droppedSpansCount: number = 0; constructor(private readonly _exporter: SpanExporter, config?: T) { const env = getEnv(); @@ -117,8 +118,23 @@ export abstract class BatchSpanProcessorBase private _addToBuffer(span: ReadableSpan) { if (this._finishedSpans.length >= this._maxQueueSize) { // limit reached, drop span + + if (this._droppedSpansCount === 0) { + diag.debug('maxQueueSize reached, dropping spans'); + } + this._droppedSpansCount++; + return; } + + if (this._droppedSpansCount > 0) { + // some spans were dropped, log once with count of spans dropped + diag.warn( + `Dropped ${this._droppedSpansCount} spans because maxQueueSize reached` + ); + this._droppedSpansCount = 0; + } + this._finishedSpans.push(span); this._maybeStartTimer(); } diff --git a/packages/opentelemetry-sdk-trace-base/test/common/export/BatchSpanProcessorBase.test.ts b/packages/opentelemetry-sdk-trace-base/test/common/export/BatchSpanProcessorBase.test.ts index fd9574537b..069287fc59 100644 --- a/packages/opentelemetry-sdk-trace-base/test/common/export/BatchSpanProcessorBase.test.ts +++ b/packages/opentelemetry-sdk-trace-base/test/common/export/BatchSpanProcessorBase.test.ts @@ -462,6 +462,40 @@ describe('BatchSpanProcessorBase', () => { } assert.equal(processor['_finishedSpans'].length, 6); }); + it('should count and report dropped spans', done => { + const debugStub = sinon.spy(diag, 'debug'); + const warnStub = sinon.spy(diag, 'warn'); + const span = createSampledSpan('test'); + for (let i = 0, j = 6; i < j; i++) { + processor.onStart(span, ROOT_CONTEXT); + processor.onEnd(span); + } + assert.equal(processor['_finishedSpans'].length, 6); + assert.equal(processor['_droppedSpansCount'], 0); + sinon.assert.notCalled(debugStub); + + processor.onStart(span, ROOT_CONTEXT); + processor.onEnd(span); + + assert.equal(processor['_finishedSpans'].length, 6); + assert.equal(processor['_droppedSpansCount'], 1); + sinon.assert.calledOnce(debugStub); + + processor.forceFlush().then(() => { + processor.onStart(span, ROOT_CONTEXT); + processor.onEnd(span); + + assert.equal(processor['_finishedSpans'].length, 1); + assert.equal(processor['_droppedSpansCount'], 0); + + sinon.assert.calledOnceWithExactly( + warnStub, + 'Dropped 1 spans because maxQueueSize reached' + ); + + done(); + }); + }); }); }); From 9b4f2a516d91d2d2df0a6717ad93f378c298d6ac Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Wed, 22 Feb 2023 21:26:04 +0800 Subject: [PATCH 19/37] feat(sdk-node): install diag log with OTEL_LOG_LEVEL (#3627) --- experimental/CHANGELOG.md | 1 + .../packages/opentelemetry-sdk-node/README.md | 16 +++++++++++- .../opentelemetry-sdk-node/src/sdk.ts | 16 ++++++++++-- .../opentelemetry-sdk-node/test/sdk.test.ts | 25 +++++++++++++++++++ 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 5f14e88c50..c8bddd915c 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to experimental packages in this project will be documented ### :rocket: (Enhancement) * feat: add HTTP_ROUTE attribute to http incoming metrics if present [#3581](https://github.com/open-telemetry/opentelemetry-js/pull/3581) @hermogenes +* feat(sdk-node): install diag logger with OTEL_LOG_LEVEL [#3627](https://github.com/open-telemetry/opentelemetry-js/pull/3627) @legendecas ### :bug: (Bug Fix) diff --git a/experimental/packages/opentelemetry-sdk-node/README.md b/experimental/packages/opentelemetry-sdk-node/README.md index 0bac781143..693f91ae74 100644 --- a/experimental/packages/opentelemetry-sdk-node/README.md +++ b/experimental/packages/opentelemetry-sdk-node/README.md @@ -143,7 +143,21 @@ Configure the [service name](https://github.com/open-telemetry/opentelemetry-spe Disable the SDK by setting the `OTEL_SDK_DISABLED` environment variable to `true`. -## Configure Trace Exporter from Environment +## Configure log level from the environment + +Set the log level by setting the `OTEL_LOG_LEVEL` environment variable to enums: + +- `NONE`, +- `ERROR`, +- `WARN`, +- `INFO`, +- `DEBUG`, +- `VERBOSE`, +- `ALL`. + +The default level is `INFO`. + +## Configure Trace Exporter from environment This is an alternative to programmatically configuring an exporter or span processor. This package will auto setup the default `otlp` exporter with `http/protobuf` protocol if `traceExporter` or `spanProcessor` hasn't been passed into the `NodeSDK` constructor. diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index fa14043cd3..faae12dc7d 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -14,7 +14,13 @@ * limitations under the License. */ -import { ContextManager, TextMapPropagator, metrics } from '@opentelemetry/api'; +import { + ContextManager, + TextMapPropagator, + metrics, + diag, + DiagConsoleLogger, +} from '@opentelemetry/api'; import { InstrumentationOption, registerInstrumentations, @@ -80,11 +86,17 @@ export class NodeSDK { * Create a new NodeJS SDK instance */ public constructor(configuration: Partial = {}) { - if (getEnv().OTEL_SDK_DISABLED) { + const env = getEnv(); + if (env.OTEL_SDK_DISABLED) { this._disabled = true; // Functions with possible side-effects are set // to no-op via the _disabled flag } + if (env.OTEL_LOG_LEVEL) { + diag.setLogger(new DiagConsoleLogger(), { + logLevel: env.OTEL_LOG_LEVEL, + }); + } this._resource = configuration.resource ?? new Resource({}); this._resourceDetectors = configuration.resourceDetectors ?? [ diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index 60b493435b..fb43901710 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -22,6 +22,7 @@ import { diag, DiagLogLevel, metrics, + DiagConsoleLogger, } from '@opentelemetry/api'; import { AsyncHooksContextManager, @@ -68,6 +69,7 @@ describe('Node SDK', () => { let delegate: any; beforeEach(() => { + diag.disable(); context.disable(); trace.disable(); propagation.disable(); @@ -78,6 +80,10 @@ describe('Node SDK', () => { delegate = (trace.getTracerProvider() as ProxyTracerProvider).getDelegate(); }); + afterEach(() => { + Sinon.restore(); + }); + describe('Basic Registration', () => { it('should not register any unconfigured SDK components', async () => { // need to set OTEL_TRACES_EXPORTER to none since default value is otlp @@ -108,6 +114,25 @@ describe('Node SDK', () => { delete env.OTEL_TRACES_EXPORTER; }); + it('should register a diag logger with OTEL_LOG_LEVEL', () => { + env.OTEL_LOG_LEVEL = 'ERROR'; + + const spy = Sinon.spy(diag, 'setLogger'); + const sdk = new NodeSDK({ + autoDetectResources: false, + }); + + sdk.start(); + + assert.strictEqual(spy.callCount, 1); + assert.ok(spy.args[0][0] instanceof DiagConsoleLogger); + assert.deepStrictEqual(spy.args[0][1], { + logLevel: DiagLogLevel.ERROR, + }); + + delete env.OTEL_LOG_LEVEL; + }); + it('should register a tracer provider if an exporter is provided', async () => { const sdk = new NodeSDK({ traceExporter: new ConsoleSpanExporter(), From b73ba9c398e296fcfd3a22754e639306e6e04a5a Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Wed, 22 Feb 2023 15:35:50 +0100 Subject: [PATCH 20/37] chore: remove unused argument (#3626) Co-authored-by: Daniel Dyla --- .../packages/opentelemetry-instrumentation-http/src/http.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts index 23a3d2ee98..ca5e3e8382 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts @@ -300,14 +300,12 @@ export class HttpInstrumentation extends InstrumentationBase { * Attach event listeners to a client request to end span and add span attributes. * * @param request The original request object. - * @param options The arguments to the original function. * @param span representing the current operation * @param startTime representing the start time of the request to calculate duration in Metric * @param metricAttributes metric attributes */ private _traceClientRequest( request: http.ClientRequest, - hostname: string, span: Span, startTime: HrTime, metricAttributes: MetricAttributes @@ -687,7 +685,6 @@ export class HttpInstrumentation extends InstrumentationBase { context.bind(parentContext, request); return instrumentation._traceClientRequest( request, - hostname, span, startTime, metricAttributes From 99524a10fb2a63aaf30862e1cf21666a8eefa485 Mon Sep 17 00:00:00 2001 From: John Bley Date: Wed, 22 Feb 2023 11:26:16 -0500 Subject: [PATCH 21/37] chore: improve logging for api registration mismatches (#3629) * chore: improve logging for api registration mismatches * chore: Update api/src/internal/global-utils.ts Co-authored-by: Daniel Dyla --------- Co-authored-by: Daniel Dyla --- api/src/internal/global-utils.ts | 2 +- api/test/common/internal/global.test.ts | 23 +++++++++++------------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/api/src/internal/global-utils.ts b/api/src/internal/global-utils.ts index 0753bc2775..b8c5fb16db 100644 --- a/api/src/internal/global-utils.ts +++ b/api/src/internal/global-utils.ts @@ -54,7 +54,7 @@ export function registerGlobal( if (api.version !== VERSION) { // All registered APIs must be of the same version exactly const err = new Error( - '@opentelemetry/api: All API registration versions must match' + `@opentelemetry/api: Registration of version v${api.version} for ${type} does not match previously registered API v${VERSION}` ); diag.error(err.stack || err.message); return false; diff --git a/api/test/common/internal/global.test.ts b/api/test/common/internal/global.test.ts index 291f707233..7a630c22ce 100644 --- a/api/test/common/internal/global.test.ts +++ b/api/test/common/internal/global.test.ts @@ -84,23 +84,22 @@ describe('Global Utils', () => { assert.notStrictEqual(manager, api1.context['_getContextManager']()); }); - it('should return the module NoOp implementation if the version is a mismatch', () => { - const newContextManager = new NoopContextManager(); - api1.context.setGlobalContextManager(newContextManager); - - // ensure new context manager is returned - assert.strictEqual(api1.context['_getContextManager'](), newContextManager); + it('should not register if the version is a mismatch', () => { + const logger1 = getMockLogger(); + const logger2 = getMockLogger(); - const globalInstance = getGlobal('context'); + api1.diag.setLogger(logger1); + const globalInstance = getGlobal('diag'); assert.ok(globalInstance); // @ts-expect-error we are modifying internals for testing purposes here _globalThis[Symbol.for(GLOBAL_API_SYMBOL_KEY)].version = '0.0.1'; - // ensure new context manager is not returned because version above is incompatible - assert.notStrictEqual( - api1.context['_getContextManager'](), - newContextManager - ); + assert.equal(false, api1.diag.setLogger(logger2)); // won't happen + + api1.diag.info('message'); + sinon.assert.notCalled(logger2.error); + sinon.assert.notCalled(logger2.info); + sinon.assert.notCalled(logger2.warn); }); it('should debug log registrations', () => { From 1fbc828607a770cffc868a3dd12ef1d7b5f865b0 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Fri, 24 Feb 2023 18:56:29 +0000 Subject: [PATCH 22/37] chore(deps): update dependency chromedriver to v110 (#3600) --- selenium-tests/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selenium-tests/package.json b/selenium-tests/package.json index be4f1d42a1..f18c070564 100644 --- a/selenium-tests/package.json +++ b/selenium-tests/package.json @@ -40,7 +40,7 @@ "babel-loader": "8.2.3", "babel-polyfill": "6.26.0", "browserstack-local": "1.4.8", - "chromedriver": "107.0.3", + "chromedriver": "110.0.0", "dotenv": "16.0.0", "fast-safe-stringify": "2.1.1", "geckodriver": "3.0.1", From 7f63a6fa5e1f52ab79bde3dda7ff04ca19688214 Mon Sep 17 00:00:00 2001 From: ravindra-dyte <99713383+ravindra-dyte@users.noreply.github.com> Date: Mon, 27 Feb 2023 16:12:37 +0530 Subject: [PATCH 23/37] fix(core): added falsy check to make otel core work with browser where webpack config had process as false or null (#3617) Co-authored-by: Chengzhong Wu Co-authored-by: Daniel Dyla --- CHANGELOG.md | 2 + .../src/utils/environment.ts | 2 +- .../test/NodeGlobalsFoolProofing.test.ts | 84 +++++++++++++++++++ 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 packages/opentelemetry-sdk-trace-web/test/NodeGlobalsFoolProofing.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 43aa4482e2..eca414a612 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/ ### :bug: (Bug Fix) +* fix(core): added falsy check to make otel core work with browser where webpack config had process as false or null [#3613](https://github.com/open-telemetry/opentelemetry-js/issues/3613) @ravindra-dyte + ### :books: (Refine Doc) ### :house: (Internal) diff --git a/packages/opentelemetry-core/src/utils/environment.ts b/packages/opentelemetry-core/src/utils/environment.ts index 01da17f293..5d529d08a9 100644 --- a/packages/opentelemetry-core/src/utils/environment.ts +++ b/packages/opentelemetry-core/src/utils/environment.ts @@ -333,7 +333,7 @@ export function parseEnvironment(values: RAW_ENVIRONMENT): ENVIRONMENT { * populating default values. */ export function getEnvWithoutDefaults(): ENVIRONMENT { - return typeof process !== 'undefined' + return typeof process !== 'undefined' && process && process.env ? parseEnvironment(process.env as RAW_ENVIRONMENT) : parseEnvironment(_globalThis as typeof globalThis & RAW_ENVIRONMENT); } diff --git a/packages/opentelemetry-sdk-trace-web/test/NodeGlobalsFoolProofing.test.ts b/packages/opentelemetry-sdk-trace-web/test/NodeGlobalsFoolProofing.test.ts new file mode 100644 index 0000000000..19090be565 --- /dev/null +++ b/packages/opentelemetry-sdk-trace-web/test/NodeGlobalsFoolProofing.test.ts @@ -0,0 +1,84 @@ +/* + * 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 { + context, + propagation, + trace, + ProxyTracerProvider, +} from '@opentelemetry/api'; +import { Resource } from '@opentelemetry/resources'; +import { Tracer } from '@opentelemetry/sdk-trace-base'; +import * as assert from 'assert'; +import { StackContextManager, WebTracerProvider } from '../src'; + +describe('Node Globals Foolproofing', () => { + const originalProcess = globalThis?.process; + before(() => { + Object.assign(globalThis, { process: false }); + }); + + after(() => { + Object.assign(globalThis, { process: originalProcess }); + }); + + beforeEach(() => { + context.disable(); + trace.disable(); + propagation.disable(); + }); + + it('Can get TraceProvider without node globals such as process', () => { + const propagator = propagation['_getGlobalPropagator'](); + const tracerProvider = new WebTracerProvider(); + tracerProvider.register({ + propagator: null, + }); + + assert.strictEqual( + propagation['_getGlobalPropagator'](), + propagator, + 'propagator should not change' + ); + + assert.ok(context['_getContextManager']() instanceof StackContextManager); + const apiTracerProvider = trace.getTracerProvider() as ProxyTracerProvider; + assert.ok(apiTracerProvider.getDelegate() === tracerProvider); + }); + + it('Can get TraceProvider with custom id generator and without node globals such as process', () => { + const getRandomString = (length: number) => { + const alphanumericsList = 'abcdefghijklmnopqrstuvwxyz0123456789'.split( + '' + ); + alphanumericsList.sort(() => 0.5 - Math.random()); + + return alphanumericsList.slice(0, length).join(''); + }; + + const tracer = new WebTracerProvider({ + resource: new Resource({ + 'service.name': 'web-core', + }), + idGenerator: { + generateTraceId: () => getRandomString(32), + generateSpanId: () => getRandomString(16), + }, + }).getTracer('default'); + + assert.ok(tracer instanceof Tracer); + }); +}); From 708afd0e9e4831f117b3f7561d1b13c7709439d6 Mon Sep 17 00:00:00 2001 From: Nev <54870357+MSNev@users.noreply.github.com> Date: Mon, 27 Feb 2023 13:38:46 -0800 Subject: [PATCH 24/37] fix: batchspanprocessor.test for TS > 4.6.0 (#3636) --- .../test/browser/export/BatchSpanProcessor.test.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/opentelemetry-sdk-trace-base/test/browser/export/BatchSpanProcessor.test.ts b/packages/opentelemetry-sdk-trace-base/test/browser/export/BatchSpanProcessor.test.ts index c4575fde2c..f510d518ff 100644 --- a/packages/opentelemetry-sdk-trace-base/test/browser/export/BatchSpanProcessor.test.ts +++ b/packages/opentelemetry-sdk-trace-base/test/browser/export/BatchSpanProcessor.test.ts @@ -20,11 +20,17 @@ import { SpanExporter } from '../../../src'; import { BatchSpanProcessor } from '../../../src/platform/browser/export/BatchSpanProcessor'; import { TestTracingSpanExporter } from '../../common/export/TestTracingSpanExporter'; +/** + * VisibilityState has been removed from TypeScript 4.6.0+ + * So just defining a simple replacement + */ +type WebVisibilityState = 'visible' | 'hidden'; + const describeDocument = typeof document === 'object' ? describe : describe.skip; describeDocument('BatchSpanProcessor - web main context', () => { - let visibilityState: VisibilityState = 'visible'; + let visibilityState: WebVisibilityState = 'visible'; let exporter: SpanExporter; let processor: BatchSpanProcessor; let forceFlushSpy: sinon.SinonStub; From abfe059d72d2a50d993ee7fb5ea50a1c29851a31 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Mon, 27 Feb 2023 23:11:08 -0600 Subject: [PATCH 25/37] feat(otlp-exporter-base): add retries (#3207) * feat(otlp-exporter-base): add retries to sendWithHttp Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> * feat(otlp-exporter-base): add tests and update abort logic Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> * feat(otlp-exporter-base): fix lint Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> * feat(otlp-exporter-base): add retry test Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> * feat(otlp-exporter-base): add retry to browser exporter and add tests Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> * feat(otlp-exporter-base): refactor Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> * feat(otlp-exporter-base): add jitter Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> * feat(otlp-exporter-base): initialize reqIsDestroyed to false Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> * feat(otlp-exporter-base): add throttle logic Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> * feat(otlp-exporter-base): add retry to readme Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> * feat(otlp-exporter-base): add changelog Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> * feat(otlp-exporter-base): update throttle time function Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> * feat(otlp-exporter-base): refactor sec difference in throttle fun Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> * feat(otlp-exporter-base): fix lint Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> * feat(otlp-exporter-base): fix lint Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> * feat(otlp-exporter-base): fix lint Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> * feat(otlp-exporter-base): refactor retrieve throttle time func Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> * feat(otlp-exporter-base): fix lint Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> * feat(otlp-exporter-base): move parseRetryAfterToMills to utils file Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> --------- Signed-off-by: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Co-authored-by: Chengzhong Wu --- experimental/CHANGELOG.md | 1 + .../exporter-trace-otlp-http/README.md | 15 ++ .../browser/CollectorTraceExporter.test.ts | 120 ++++++++++++++ .../test/node/CollectorTraceExporter.test.ts | 35 ---- .../exporter-trace-otlp-proto/README.md | 15 ++ .../src/platform/browser/util.ts | 124 ++++++++++---- .../src/platform/node/util.ts | 156 ++++++++++++------ .../packages/otlp-exporter-base/src/util.ts | 27 +++ .../test/common/util.test.ts | 28 ++++ .../otlp-exporter-base/test/node/util.test.ts | 15 ++ 10 files changed, 419 insertions(+), 117 deletions(-) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index c8bddd915c..e11fb70b01 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -72,6 +72,7 @@ All notable changes to experimental packages in this project will be documented * deps: remove unused proto-loader dependencies and update grpc-js and proto-loader versions [#3337](https://github.com/open-telemetry/opentelemetry-js/pull/3337) @seemk * feat(metrics-exporters): configure temporality via environment variable [#3305](https://github.com/open-telemetry/opentelemetry-js/pull/3305) @pichlermarc * feat(console-metric-exporter): add temporality configuration [#3387](https://github.com/open-telemetry/opentelemetry-js/pull/3387) @pichlermarc +* feat(otlp-exporter-base): add retries [#3207](https://github.com/open-telemetry/opentelemetry-js/pull/3207) @svetlanabrennan ### :bug: (Bug Fix) diff --git a/experimental/packages/exporter-trace-otlp-http/README.md b/experimental/packages/exporter-trace-otlp-http/README.md index ef4259fcd1..f5944a047b 100644 --- a/experimental/packages/exporter-trace-otlp-http/README.md +++ b/experimental/packages/exporter-trace-otlp-http/README.md @@ -143,6 +143,21 @@ To override the default timeout duration, use the following options: > Providing `timeoutMillis` with `collectorOptions` takes precedence and overrides timeout set with environment variables. +## OTLP Exporter Retry + +OTLP requires that transient errors be handled with a [retry strategy](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#retry). + +This retry policy has the following configuration, which there is currently no way to customize. + ++ `DEFAULT_EXPORT_MAX_ATTEMPTS`: The maximum number of attempts, including the original request. Defaults to 5. ++ `DEFAULT_EXPORT_INITIAL_BACKOFF`: The initial backoff duration. Defaults to 1 second. ++ `DEFAULT_EXPORT_MAX_BACKOFF`: The maximum backoff duration. Defaults to 5 seconds. ++ `DEFAULT_EXPORT_BACKOFF_MULTIPLIER`: The backoff multiplier. Defaults to 1.5. + +This retry policy first checks if the response has a `'Retry-After'` header. If there is a `'Retry-After'` header, the exporter will wait the amount specified in the `'Retry-After'` header before retrying. If there is no `'Retry-After'` header, the exporter will use an exponential backoff with jitter retry strategy. + + > The exporter will retry exporting within the [exporter timeout configuration](#Exporter-Timeout-Configuration) time. + ## Running opentelemetry-collector locally to see the traces 1. Go to `examples/otlp-exporter-node` diff --git a/experimental/packages/exporter-trace-otlp-http/test/browser/CollectorTraceExporter.test.ts b/experimental/packages/exporter-trace-otlp-http/test/browser/CollectorTraceExporter.test.ts index 81bc6c6b49..4e8bc1d6e1 100644 --- a/experimental/packages/exporter-trace-otlp-http/test/browser/CollectorTraceExporter.test.ts +++ b/experimental/packages/exporter-trace-otlp-http/test/browser/CollectorTraceExporter.test.ts @@ -582,3 +582,123 @@ describe('when configuring via environment', () => { envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; }); }); + +describe('export with retry - real http request destroyed', () => { + let server: any; + let collectorTraceExporter: OTLPTraceExporter; + let collectorExporterConfig: OTLPExporterConfigBase; + let spans: ReadableSpan[]; + + beforeEach(() => { + server = sinon.fakeServer.create({ + autoRespond: true, + }); + collectorExporterConfig = { + timeoutMillis: 1500, + }; + }); + + afterEach(() => { + server.restore(); + }); + + describe('when "sendBeacon" is NOT available', () => { + beforeEach(() => { + (window.navigator as any).sendBeacon = false; + collectorTraceExporter = new OTLPTraceExporter(collectorExporterConfig); + }); + it('should log the timeout request error message when retrying with exponential backoff with jitter', done => { + spans = []; + spans.push(Object.assign({}, mockedReadableSpan)); + + let retry = 0; + server.respondWith( + 'http://localhost:4318/v1/traces', + function (xhr: any) { + retry++; + xhr.respond(503); + } + ); + + collectorTraceExporter.export(spans, result => { + assert.strictEqual(result.code, core.ExportResultCode.FAILED); + const error = result.error as OTLPExporterError; + assert.ok(error !== undefined); + assert.strictEqual(error.message, 'Request Timeout'); + assert.strictEqual(retry, 1); + done(); + }); + }).timeout(3000); + + it('should log the timeout request error message when retry-after header is set to 3 seconds', done => { + spans = []; + spans.push(Object.assign({}, mockedReadableSpan)); + + let retry = 0; + server.respondWith( + 'http://localhost:4318/v1/traces', + function (xhr: any) { + retry++; + xhr.respond(503, { 'Retry-After': 3 }); + } + ); + + collectorTraceExporter.export(spans, result => { + assert.strictEqual(result.code, core.ExportResultCode.FAILED); + const error = result.error as OTLPExporterError; + assert.ok(error !== undefined); + assert.strictEqual(error.message, 'Request Timeout'); + assert.strictEqual(retry, 1); + done(); + }); + }).timeout(3000); + it('should log the timeout request error message when retry-after header is a date', done => { + spans = []; + spans.push(Object.assign({}, mockedReadableSpan)); + + let retry = 0; + server.respondWith( + 'http://localhost:4318/v1/traces', + function (xhr: any) { + retry++; + const d = new Date(); + d.setSeconds(d.getSeconds() + 1); + xhr.respond(503, { 'Retry-After': d }); + } + ); + + collectorTraceExporter.export(spans, result => { + assert.strictEqual(result.code, core.ExportResultCode.FAILED); + const error = result.error as OTLPExporterError; + assert.ok(error !== undefined); + assert.strictEqual(error.message, 'Request Timeout'); + assert.strictEqual(retry, 2); + done(); + }); + }).timeout(3000); + it('should log the timeout request error message when retry-after header is a date with long delay', done => { + spans = []; + spans.push(Object.assign({}, mockedReadableSpan)); + + let retry = 0; + server.respondWith( + 'http://localhost:4318/v1/traces', + function (xhr: any) { + retry++; + const d = new Date(); + d.setSeconds(d.getSeconds() + 120); + xhr.respond(503, { 'Retry-After': d }); + } + ); + + collectorTraceExporter.export(spans, result => { + assert.strictEqual(result.code, core.ExportResultCode.FAILED); + const error = result.error as OTLPExporterError; + assert.ok(error !== undefined); + assert.strictEqual(error.message, 'Request Timeout'); + assert.strictEqual(retry, 1); + done(); + }); + }).timeout(3000); + }); +}); diff --git a/experimental/packages/exporter-trace-otlp-http/test/node/CollectorTraceExporter.test.ts b/experimental/packages/exporter-trace-otlp-http/test/node/CollectorTraceExporter.test.ts index e7006dbda5..426aed4431 100644 --- a/experimental/packages/exporter-trace-otlp-http/test/node/CollectorTraceExporter.test.ts +++ b/experimental/packages/exporter-trace-otlp-http/test/node/CollectorTraceExporter.test.ts @@ -551,38 +551,3 @@ describe('export - real http request destroyed before response received', () => }, 0); }); }); - -describe('export - real http request destroyed after response received', () => { - let collectorExporter: OTLPTraceExporter; - let collectorExporterConfig: OTLPExporterNodeConfigBase; - let spans: ReadableSpan[]; - - const server = http.createServer((_, res) => { - res.write('writing something'); - }); - before(done => { - server.listen(8081, done); - }); - after(done => { - server.close(done); - }); - it('should log the timeout request error message', done => { - collectorExporterConfig = { - url: 'http://localhost:8081', - timeoutMillis: 300, - }; - collectorExporter = new OTLPTraceExporter(collectorExporterConfig); - spans = []; - spans.push(Object.assign({}, mockedReadableSpan)); - - setTimeout(() => { - collectorExporter.export(spans, result => { - assert.strictEqual(result.code, core.ExportResultCode.FAILED); - const error = result.error as OTLPExporterError; - assert.ok(error !== undefined); - assert.strictEqual(error.message, 'Request Timeout'); - done(); - }); - }, 0); - }); -}); diff --git a/experimental/packages/exporter-trace-otlp-proto/README.md b/experimental/packages/exporter-trace-otlp-proto/README.md index 0338b4cd93..efd22d2abf 100644 --- a/experimental/packages/exporter-trace-otlp-proto/README.md +++ b/experimental/packages/exporter-trace-otlp-proto/README.md @@ -72,6 +72,21 @@ To override the default timeout duration, use the following options: > Providing `timeoutMillis` with `collectorOptions` takes precedence and overrides timeout set with environment variables. +## OTLP Exporter Retry + +OTLP requires that transient errors be handled with a [retry strategy](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#retry). + +This retry policy has the following configuration, which there is currently no way to customize. + ++ `DEFAULT_EXPORT_MAX_ATTEMPTS`: The maximum number of attempts, including the original request. Defaults to 5. ++ `DEFAULT_EXPORT_INITIAL_BACKOFF`: The initial backoff duration. Defaults to 1 second. ++ `DEFAULT_EXPORT_MAX_BACKOFF`: The maximum backoff duration. Defaults to 5 seconds. ++ `DEFAULT_EXPORT_BACKOFF_MULTIPLIER`: The backoff multiplier. Defaults to 1.5. + +This retry policy first checks if the response has a `'Retry-After'` header. If there is a `'Retry-After'` header, the exporter will wait the amount specified in the `'Retry-After'` header before retrying. If there is no `'Retry-After'` header, the exporter will use an exponential backoff with jitter retry strategy. + + > The exporter will retry exporting within the [exporter timeout configuration](#Exporter-Timeout-Configuration) time. + ## Running opentelemetry-collector locally to see the traces 1. Go to examples/otlp-exporter-node diff --git a/experimental/packages/otlp-exporter-base/src/platform/browser/util.ts b/experimental/packages/otlp-exporter-base/src/platform/browser/util.ts index a271a3bf5f..fade4afa88 100644 --- a/experimental/packages/otlp-exporter-base/src/platform/browser/util.ts +++ b/experimental/packages/otlp-exporter-base/src/platform/browser/util.ts @@ -15,6 +15,14 @@ */ import { diag } from '@opentelemetry/api'; import { OTLPExporterError } from '../../types'; +import { + DEFAULT_EXPORT_MAX_ATTEMPTS, + DEFAULT_EXPORT_INITIAL_BACKOFF, + DEFAULT_EXPORT_BACKOFF_MULTIPLIER, + DEFAULT_EXPORT_MAX_BACKOFF, + isExportRetryable, + parseRetryAfterToMills, +} from '../../util'; /** * Send metrics/spans using browser navigator.sendBeacon @@ -57,47 +65,99 @@ export function sendWithXhr( onSuccess: () => void, onError: (error: OTLPExporterError) => void ): void { - let reqIsDestroyed: boolean; + let retryTimer: ReturnType; + let xhr: XMLHttpRequest; + let reqIsDestroyed = false; const exporterTimer = setTimeout(() => { + clearTimeout(retryTimer); reqIsDestroyed = true; - xhr.abort(); + + if (xhr.readyState === XMLHttpRequest.DONE) { + const err = new OTLPExporterError('Request Timeout'); + onError(err); + } else { + xhr.abort(); + } }, exporterTimeout); - const xhr = new XMLHttpRequest(); - xhr.open('POST', url); + const sendWithRetry = ( + retries = DEFAULT_EXPORT_MAX_ATTEMPTS, + minDelay = DEFAULT_EXPORT_INITIAL_BACKOFF + ) => { + xhr = new XMLHttpRequest(); + xhr.open('POST', url); - const defaultHeaders = { - Accept: 'application/json', - 'Content-Type': 'application/json', - }; + const defaultHeaders = { + Accept: 'application/json', + 'Content-Type': 'application/json', + }; - Object.entries({ - ...defaultHeaders, - ...headers, - }).forEach(([k, v]) => { - xhr.setRequestHeader(k, v); - }); + Object.entries({ + ...defaultHeaders, + ...headers, + }).forEach(([k, v]) => { + xhr.setRequestHeader(k, v); + }); - xhr.send(body); + xhr.send(body); - xhr.onreadystatechange = () => { - if (xhr.readyState === XMLHttpRequest.DONE) { - if (xhr.status >= 200 && xhr.status <= 299) { - clearTimeout(exporterTimer); - diag.debug('xhr success', body); - onSuccess(); - } else if (reqIsDestroyed) { - const error = new OTLPExporterError('Request Timeout', xhr.status); - onError(error); - } else { - const error = new OTLPExporterError( - `Failed to export with XHR (status: ${xhr.status})`, - xhr.status - ); - clearTimeout(exporterTimer); - onError(error); + xhr.onreadystatechange = () => { + if (xhr.readyState === XMLHttpRequest.DONE && reqIsDestroyed === false) { + if (xhr.status >= 200 && xhr.status <= 299) { + diag.debug('xhr success', body); + onSuccess(); + clearTimeout(exporterTimer); + clearTimeout(retryTimer); + } else if (xhr.status && isExportRetryable(xhr.status) && retries > 0) { + let retryTime: number; + minDelay = DEFAULT_EXPORT_BACKOFF_MULTIPLIER * minDelay; + + // retry after interval specified in Retry-After header + if (xhr.getResponseHeader('Retry-After')) { + retryTime = parseRetryAfterToMills( + xhr.getResponseHeader('Retry-After')! + ); + } else { + // exponential backoff with jitter + retryTime = Math.round( + Math.random() * (DEFAULT_EXPORT_MAX_BACKOFF - minDelay) + minDelay + ); + } + + retryTimer = setTimeout(() => { + sendWithRetry(retries - 1, minDelay); + }, retryTime); + } else { + const error = new OTLPExporterError( + `Failed to export with XHR (status: ${xhr.status})`, + xhr.status + ); + onError(error); + clearTimeout(exporterTimer); + clearTimeout(retryTimer); + } } - } + }; + + xhr.onabort = () => { + if (reqIsDestroyed) { + const err = new OTLPExporterError('Request Timeout'); + onError(err); + } + clearTimeout(exporterTimer); + clearTimeout(retryTimer); + }; + + xhr.onerror = () => { + if (reqIsDestroyed) { + const err = new OTLPExporterError('Request Timeout'); + onError(err); + } + clearTimeout(exporterTimer); + clearTimeout(retryTimer); + }; }; + + sendWithRetry(); } diff --git a/experimental/packages/otlp-exporter-base/src/platform/node/util.ts b/experimental/packages/otlp-exporter-base/src/platform/node/util.ts index d5636d14e6..fd40981e85 100644 --- a/experimental/packages/otlp-exporter-base/src/platform/node/util.ts +++ b/experimental/packages/otlp-exporter-base/src/platform/node/util.ts @@ -24,6 +24,14 @@ import { diag } from '@opentelemetry/api'; import { CompressionAlgorithm } from './types'; import { getEnv } from '@opentelemetry/core'; import { OTLPExporterError } from '../../types'; +import { + DEFAULT_EXPORT_MAX_ATTEMPTS, + DEFAULT_EXPORT_INITIAL_BACKOFF, + DEFAULT_EXPORT_BACKOFF_MULTIPLIER, + DEFAULT_EXPORT_MAX_BACKOFF, + isExportRetryable, + parseRetryAfterToMills, +} from '../../util'; /** * Sends data using http @@ -42,16 +50,21 @@ export function sendWithHttp( ): void { const exporterTimeout = collector.timeoutMillis; const parsedUrl = new url.URL(collector.url); - let reqIsDestroyed: boolean; const nodeVersion = Number(process.versions.node.split('.')[0]); + let retryTimer: ReturnType; + let req: http.ClientRequest; + let reqIsDestroyed = false; const exporterTimer = setTimeout(() => { + clearTimeout(retryTimer); reqIsDestroyed = true; - // req.abort() was deprecated since v14 - if (nodeVersion >= 14) { - req.destroy(); + + if (req.destroyed) { + const err = new OTLPExporterError('Request Timeout'); + onError(err); } else { - req.abort(); + // req.abort() was deprecated since v14 + nodeVersion >= 14 ? req.destroy() : req.abort(); } }, exporterTimeout); @@ -69,61 +82,104 @@ export function sendWithHttp( const request = parsedUrl.protocol === 'http:' ? http.request : https.request; - const req = request(options, (res: http.IncomingMessage) => { - let responseData = ''; - res.on('data', chunk => (responseData += chunk)); + const sendWithRetry = ( + retries = DEFAULT_EXPORT_MAX_ATTEMPTS, + minDelay = DEFAULT_EXPORT_INITIAL_BACKOFF + ) => { + req = request(options, (res: http.IncomingMessage) => { + let responseData = ''; + res.on('data', chunk => (responseData += chunk)); + + res.on('aborted', () => { + if (reqIsDestroyed) { + const err = new OTLPExporterError('Request Timeout'); + onError(err); + } + }); + + res.on('end', () => { + if (reqIsDestroyed === false) { + if (res.statusCode && res.statusCode < 299) { + diag.debug(`statusCode: ${res.statusCode}`, responseData); + onSuccess(); + // clear all timers since request was completed and promise was resolved + clearTimeout(exporterTimer); + clearTimeout(retryTimer); + } else if ( + res.statusCode && + isExportRetryable(res.statusCode) && + retries > 0 + ) { + let retryTime: number; + minDelay = DEFAULT_EXPORT_BACKOFF_MULTIPLIER * minDelay; + + // retry after interval specified in Retry-After header + if (res.headers['retry-after']) { + retryTime = parseRetryAfterToMills(res.headers['retry-after']!); + } else { + // exponential backoff with jitter + retryTime = Math.round( + Math.random() * (DEFAULT_EXPORT_MAX_BACKOFF - minDelay) + + minDelay + ); + } + + retryTimer = setTimeout(() => { + sendWithRetry(retries - 1, minDelay); + }, retryTime); + } else { + const error = new OTLPExporterError( + res.statusMessage, + res.statusCode, + responseData + ); + onError(error); + // clear all timers since request was completed and promise was resolved + clearTimeout(exporterTimer); + clearTimeout(retryTimer); + } + } + }); + }); - res.on('aborted', () => { + req.on('error', (error: Error | any) => { if (reqIsDestroyed) { - const err = new OTLPExporterError('Request Timeout'); + const err = new OTLPExporterError('Request Timeout', error.code); onError(err); + } else { + onError(error); } + clearTimeout(exporterTimer); + clearTimeout(retryTimer); }); - res.on('end', () => { - if (!reqIsDestroyed) { - if (res.statusCode && res.statusCode < 299) { - diag.debug(`statusCode: ${res.statusCode}`, responseData); - onSuccess(); - } else { - const error = new OTLPExporterError( - res.statusMessage, - res.statusCode, - responseData - ); - onError(error); - } - clearTimeout(exporterTimer); + req.on('abort', () => { + if (reqIsDestroyed) { + const err = new OTLPExporterError('Request Timeout'); + onError(err); } + clearTimeout(exporterTimer); + clearTimeout(retryTimer); }); - }); - req.on('error', (error: Error | any) => { - if (reqIsDestroyed) { - const err = new OTLPExporterError('Request Timeout', error.code); - onError(err); - } else { - clearTimeout(exporterTimer); - onError(error); - } - }); - - switch (collector.compression) { - case CompressionAlgorithm.GZIP: { - req.setHeader('Content-Encoding', 'gzip'); - const dataStream = readableFromBuffer(data); - dataStream - .on('error', onError) - .pipe(zlib.createGzip()) - .on('error', onError) - .pipe(req); - - break; + switch (collector.compression) { + case CompressionAlgorithm.GZIP: { + req.setHeader('Content-Encoding', 'gzip'); + const dataStream = readableFromBuffer(data); + dataStream + .on('error', onError) + .pipe(zlib.createGzip()) + .on('error', onError) + .pipe(req); + + break; + } + default: + req.end(data); + break; } - default: - req.end(data); - break; - } + }; + sendWithRetry(); } function readableFromBuffer(buff: string | Buffer): Readable { diff --git a/experimental/packages/otlp-exporter-base/src/util.ts b/experimental/packages/otlp-exporter-base/src/util.ts index 99a9f6e333..f5dc70c9e8 100644 --- a/experimental/packages/otlp-exporter-base/src/util.ts +++ b/experimental/packages/otlp-exporter-base/src/util.ts @@ -18,6 +18,10 @@ import { diag } from '@opentelemetry/api'; import { getEnv } from '@opentelemetry/core'; const DEFAULT_TRACE_TIMEOUT = 10000; +export const DEFAULT_EXPORT_MAX_ATTEMPTS = 5; +export const DEFAULT_EXPORT_INITIAL_BACKOFF = 1000; +export const DEFAULT_EXPORT_MAX_BACKOFF = 5000; +export const DEFAULT_EXPORT_BACKOFF_MULTIPLIER = 1.5; /** * Parses headers from config leaving only those that have defined values @@ -110,3 +114,26 @@ export function invalidTimeout( return defaultTimeout; } + +export function isExportRetryable(statusCode: number): boolean { + const retryCodes = [429, 502, 503, 504]; + + return retryCodes.includes(statusCode); +} + +export function parseRetryAfterToMills(retryAfter?: string | null): number { + if (retryAfter == null) { + return -1; + } + const seconds = Number.parseInt(retryAfter, 10); + if (Number.isInteger(seconds)) { + return seconds > 0 ? seconds * 1000 : -1; + } + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After#directives + const delay = new Date(retryAfter).getTime() - Date.now(); + + if (delay >= 0) { + return delay; + } + return 0; +} diff --git a/experimental/packages/otlp-exporter-base/test/common/util.test.ts b/experimental/packages/otlp-exporter-base/test/common/util.test.ts index d78b719faa..b00d1f36a5 100644 --- a/experimental/packages/otlp-exporter-base/test/common/util.test.ts +++ b/experimental/packages/otlp-exporter-base/test/common/util.test.ts @@ -21,6 +21,7 @@ import { parseHeaders, appendResourcePathToUrl, appendRootPathToUrlIfNeeded, + parseRetryAfterToMills, } from '../../src/util'; describe('utils', () => { @@ -117,3 +118,30 @@ describe('utils', () => { }); }); }); + +describe('parseRetryAfterToMills', () => { + // now: 2023-01-20T00:00:00.000Z + const tests = [ + [null, -1], + // duration + ['-100', -1], + ['1000', 1000 * 1000], + // future timestamp + ['Fri, 20 Jan 2023 00:00:01 GMT', 1000], + // Past timestamp + ['Fri, 19 Jan 2023 23:59:59 GMT', 0], + ] as [string | null, number][]; + + afterEach(() => { + sinon.restore(); + }); + + for (const [value, expect] of tests) { + it(`test ${value}`, () => { + sinon.useFakeTimers({ + now: new Date('2023-01-20T00:00:00.000Z'), + }); + assert.strictEqual(parseRetryAfterToMills(value), expect); + }); + } +}); diff --git a/experimental/packages/otlp-exporter-base/test/node/util.test.ts b/experimental/packages/otlp-exporter-base/test/node/util.test.ts index 7d5b03d3eb..86c8df40ab 100644 --- a/experimental/packages/otlp-exporter-base/test/node/util.test.ts +++ b/experimental/packages/otlp-exporter-base/test/node/util.test.ts @@ -225,6 +225,9 @@ describe('sendWithHttp', () => { assert.strictEqual(requestData, data); }); + // use fake timers to replace setTimeout in sendWithHttp function + const clock = sinon.useFakeTimers(); + sendWithHttp( exporter, data, @@ -237,6 +240,8 @@ describe('sendWithHttp', () => { assert.fail(err); } ); + + clock.restore(); }); it('should send with gzip compression if configured to do so', () => { @@ -255,6 +260,9 @@ describe('sendWithHttp', () => { assert(Buffer.concat(buffers).equals(compressedData)); }); + // use fake timers to replace setTimeout in sendWithHttp function + const clock = sinon.useFakeTimers(); + sendWithHttp( exporter, data, @@ -267,6 +275,8 @@ describe('sendWithHttp', () => { assert.fail(err); } ); + + clock.restore(); }); it('should work with gzip compression enabled even after multiple requests', () => { @@ -297,6 +307,9 @@ describe('sendWithHttp', () => { assert(Buffer.concat(buffers).equals(compressedData)); }); + // use fake timers to replace setTimeout in sendWithHttp function + const clock = sinon.useFakeTimers(); + sendWithHttp( exporter, data, @@ -309,6 +322,8 @@ describe('sendWithHttp', () => { assert.fail(err); } ); + + clock.restore(); } }); }); From 79b06a470af701a92327032ddb968a4386d33282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerhard=20St=C3=B6bich?= Date: Tue, 28 Feb 2023 18:08:07 +0100 Subject: [PATCH 26/37] feat: improve http span name (#3603) --- experimental/CHANGELOG.md | 3 +++ .../src/http.ts | 15 ++++++----- .../test/functionals/http-enable.test.ts | 27 ++++++++++++++++--- .../test/functionals/http-package.test.ts | 2 +- .../test/functionals/https-enable.test.ts | 6 ++--- .../test/functionals/https-package.test.ts | 2 +- .../test/integrations/http-enable.test.ts | 16 +++++------ .../test/integrations/https-enable.test.ts | 12 ++++----- .../test/utils/assertSpan.ts | 5 +--- 9 files changed, 55 insertions(+), 33 deletions(-) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index e11fb70b01..b85f4c1d5f 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -6,8 +6,11 @@ All notable changes to experimental packages in this project will be documented ### :boom: Breaking Change +* feat: remove HTTP/HTTPS prefix from span name [#3603](https://github.com/open-telemetry/opentelemetry-js/pull/3603) @Flarna + ### :rocket: (Enhancement) +* feat: use HTTP_ROUTE in span name [#3603](https://github.com/open-telemetry/opentelemetry-js/pull/3603) @Flarna * feat: add HTTP_ROUTE attribute to http incoming metrics if present [#3581](https://github.com/open-telemetry/opentelemetry-js/pull/3581) @hermogenes * feat(sdk-node): install diag logger with OTEL_LOG_LEVEL [#3627](https://github.com/open-telemetry/opentelemetry-js/pull/3627) @legendecas diff --git a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts index ca5e3e8382..7291780e86 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts @@ -58,6 +58,7 @@ import { } from '@opentelemetry/instrumentation'; import { RPCMetadata, RPCType, setRPCMetadata } from '@opentelemetry/core'; import { errorMonitor } from 'events'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; /** * Http instrumentation instrumentation for Opentelemetry @@ -489,11 +490,7 @@ export class HttpInstrumentation extends InstrumentationBase { utils.getIncomingRequestMetricAttributes(spanAttributes); const ctx = propagation.extract(ROOT_CONTEXT, headers); - const span = instrumentation._startHttpSpan( - `${component.toLocaleUpperCase()} ${method}`, - spanOptions, - ctx - ); + const span = instrumentation._startHttpSpan(method, spanOptions, ctx); const rpcMetadata: RPCMetadata = { type: RPCType.HTTP, span, @@ -622,7 +619,6 @@ export class HttpInstrumentation extends InstrumentationBase { return original.apply(this, [optionsParsed, ...args]); } - const operationName = `${component.toUpperCase()} ${method}`; const { hostname, port } = utils.extractHostnameAndPort(optionsParsed); const attributes = utils.getOutgoingRequestAttributes(optionsParsed, { @@ -643,7 +639,7 @@ export class HttpInstrumentation extends InstrumentationBase { kind: SpanKind.CLIENT, attributes, }; - const span = instrumentation._startHttpSpan(operationName, spanOptions); + const span = instrumentation._startHttpSpan(method, spanOptions); const parentContext = context.active(); const requestContext = trace.setSpan(parentContext, span); @@ -717,6 +713,11 @@ export class HttpInstrumentation extends InstrumentationBase { code: utils.parseResponseStatus(SpanKind.SERVER, response.statusCode), }); + const route = attributes[SemanticAttributes.HTTP_ROUTE]; + if (route) { + span.updateName(`${request.method || 'GET'} ${route}`); + } + if (this._getConfig().applyCustomAttributesOnSpan) { safeExecuteInTheMiddle( () => diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts index 9ebfe41c80..5a5588993f 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts @@ -272,6 +272,13 @@ describe('HttpInstrumentation', () => { if (request.url?.includes('/ignored')) { provider.getTracer('test').startSpan('some-span').end(); } + if (request.url?.includes('/setroute')) { + const rpcData = getRPCMetadata(context.active()); + assert.ok(rpcData != null); + assert.strictEqual(rpcData.type, RPCType.HTTP); + assert.strictEqual(rpcData.route, undefined); + rpcData.route = 'TheRoute'; + } response.end('Test Server Response'); }); @@ -339,6 +346,20 @@ describe('HttpInstrumentation', () => { }); }); + it('should respect HTTP_ROUTE', async () => { + await httpRequest.get( + `${protocol}://${hostname}:${serverPort}/setroute` + ); + const span = memoryExporter.getFinishedSpans()[0]; + + assert.strictEqual(span.kind, SpanKind.SERVER); + assert.strictEqual( + span.attributes[SemanticAttributes.HTTP_ROUTE], + 'TheRoute' + ); + assert.strictEqual(span.name, 'GET TheRoute'); + }); + const httpErrorCodes = [ 400, 401, 403, 404, 429, 501, 503, 504, 500, 505, 597, ]; @@ -404,7 +425,7 @@ describe('HttpInstrumentation', () => { assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, 'HTTP GET'); + assert.strictEqual(reqSpan.name, 'GET'); assert.strictEqual( localSpan.spanContext().traceId, reqSpan.spanContext().traceId @@ -449,7 +470,7 @@ describe('HttpInstrumentation', () => { assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, 'HTTP GET'); + assert.strictEqual(reqSpan.name, 'GET'); assert.strictEqual( localSpan.spanContext().traceId, reqSpan.spanContext().traceId @@ -474,7 +495,7 @@ describe('HttpInstrumentation', () => { for (let i = 0; i < num; i++) { await httpRequest.get(`${protocol}://${hostname}${testPath}`); const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans[i].name, 'HTTP GET'); + assert.strictEqual(spans[i].name, 'GET'); assert.strictEqual( span.spanContext().traceId, spans[i].spanContext().traceId diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-package.test.ts b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-package.test.ts index 51b15dd6f3..39b288ccdb 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-package.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/http-package.test.ts @@ -119,7 +119,7 @@ describe('Packages', () => { }; assert.strictEqual(spans.length, 1); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, 'GET'); switch (name) { case 'axios': diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts index 2f3af2aaeb..63a0ec00f5 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts @@ -356,7 +356,7 @@ describe('HttpsInstrumentation', () => { assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, 'HTTPS GET'); + assert.strictEqual(reqSpan.name, 'GET'); assert.strictEqual( localSpan.spanContext().traceId, reqSpan.spanContext().traceId @@ -401,7 +401,7 @@ describe('HttpsInstrumentation', () => { assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, 'HTTPS GET'); + assert.strictEqual(reqSpan.name, 'GET'); assert.strictEqual( localSpan.spanContext().traceId, reqSpan.spanContext().traceId @@ -426,7 +426,7 @@ describe('HttpsInstrumentation', () => { for (let i = 0; i < num; i++) { await httpsRequest.get(`${protocol}://${hostname}${testPath}`); const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans[i].name, 'HTTPS GET'); + assert.strictEqual(spans[i].name, 'GET'); assert.strictEqual( span.spanContext().traceId, spans[i].spanContext().traceId diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/https-package.test.ts b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/https-package.test.ts index 28e9fb9804..dada803e45 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/https-package.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/https-package.test.ts @@ -125,7 +125,7 @@ describe('Packages', () => { }; assert.strictEqual(spans.length, 1); - assert.strictEqual(span.name, 'HTTPS GET'); + assert.strictEqual(span.name, 'GET'); switch (name) { case 'axios': diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/integrations/http-enable.test.ts b/experimental/packages/opentelemetry-instrumentation-http/test/integrations/http-enable.test.ts index b6a8d18d87..625b8b6549 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/integrations/http-enable.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/integrations/http-enable.test.ts @@ -176,7 +176,7 @@ describe('HttpInstrumentation Integration tests', () => { }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, 'GET'); assertSpan(span, SpanKind.CLIENT, validations); }); @@ -203,7 +203,7 @@ describe('HttpInstrumentation Integration tests', () => { }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, 'GET'); assertSpan(span, SpanKind.CLIENT, validations); }); @@ -233,7 +233,7 @@ describe('HttpInstrumentation Integration tests', () => { }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, 'GET'); assert.strictEqual(result.reqHeaders['x-foo'], 'foo'); assert.strictEqual( span.attributes[SemanticAttributes.HTTP_FLAVOR], @@ -264,7 +264,7 @@ describe('HttpInstrumentation Integration tests', () => { }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, 'GET'); assert.strictEqual(span.attributes['span kind'], SpanKind.CLIENT); assertSpan(span, SpanKind.CLIENT, validations); }); @@ -292,7 +292,7 @@ describe('HttpInstrumentation Integration tests', () => { }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, 'GET'); assertSpan(span, SpanKind.CLIENT, validations); }); for (const headers of [ @@ -347,7 +347,7 @@ describe('HttpInstrumentation Integration tests', () => { const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, 'GET'); assert.ok(data); assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]); assert.ok(validations.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY]); @@ -362,7 +362,7 @@ describe('HttpInstrumentation Integration tests', () => { const span = spans.find((s: any) => s.kind === SpanKind.SERVER); assert.ok(span); assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, 'GET'); }); it('should have correct spans even when request timeout', async () => { @@ -381,7 +381,7 @@ describe('HttpInstrumentation Integration tests', () => { spans = memoryExporter.getFinishedSpans(); const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, 'GET'); assert.strictEqual( span.attributes[SemanticAttributes.HTTP_HOST], `localhost:${mockServerPort}` diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/integrations/https-enable.test.ts b/experimental/packages/opentelemetry-instrumentation-http/test/integrations/https-enable.test.ts index 08d636b164..10afe76a80 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/integrations/https-enable.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/integrations/https-enable.test.ts @@ -180,7 +180,7 @@ describe('HttpsInstrumentation Integration tests', () => { }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTPS GET'); + assert.strictEqual(span.name, 'GET'); assertSpan(span, SpanKind.CLIENT, validations); }); @@ -207,7 +207,7 @@ describe('HttpsInstrumentation Integration tests', () => { }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTPS GET'); + assert.strictEqual(span.name, 'GET'); assertSpan(span, SpanKind.CLIENT, validations); }); @@ -237,7 +237,7 @@ describe('HttpsInstrumentation Integration tests', () => { }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTPS GET'); + assert.strictEqual(span.name, 'GET'); assert.strictEqual(result.reqHeaders['x-foo'], 'foo'); assert.strictEqual( span.attributes[SemanticAttributes.HTTP_FLAVOR], @@ -268,7 +268,7 @@ describe('HttpsInstrumentation Integration tests', () => { }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTPS GET'); + assert.strictEqual(span.name, 'GET'); assert.strictEqual(span.attributes['span kind'], SpanKind.CLIENT); assertSpan(span, SpanKind.CLIENT, validations); }); @@ -296,7 +296,7 @@ describe('HttpsInstrumentation Integration tests', () => { }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTPS GET'); + assert.strictEqual(span.name, 'GET'); assertSpan(span, SpanKind.CLIENT, validations); }); for (const headers of [ @@ -351,7 +351,7 @@ describe('HttpsInstrumentation Integration tests', () => { const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTPS GET'); + assert.strictEqual(span.name, 'GET'); assert.ok(data); assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]); assert.ok(validations.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY]); diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts b/experimental/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts index 2576b599d6..1f1b0518c8 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts @@ -49,10 +49,7 @@ export const assertSpan = ( assert.strictEqual(span.spanContext().traceId.length, 32); assert.strictEqual(span.spanContext().spanId.length, 16); assert.strictEqual(span.kind, kind); - assert.strictEqual( - span.name, - `${validations.component.toUpperCase()} ${validations.httpMethod}` - ); + assert.strictEqual(span.name, validations.httpMethod); assert.strictEqual( span.attributes[AttributeNames.HTTP_ERROR_MESSAGE], span.status.message From 0d373bd8b05ad5ae8cb733a51407834320ba83e2 Mon Sep 17 00:00:00 2001 From: Matthew Wear Date: Tue, 28 Feb 2023 21:59:10 -0800 Subject: [PATCH 27/37] feat: detect host id for non-cloud environments (#3575) --- CHANGELOG.md | 2 + .../src/platform/node/HostDetectorSync.ts | 14 ++- .../src/platform/node/machine-id/execAsync.ts | 20 +++++ .../node/machine-id/getMachineId-bsd.ts | 37 ++++++++ .../node/machine-id/getMachineId-darwin.ts | 41 +++++++++ .../node/machine-id/getMachineId-linux.ts | 32 +++++++ .../machine-id/getMachineId-unsupported.ts | 22 +++++ .../node/machine-id/getMachineId-win.ts | 40 +++++++++ .../platform/node/machine-id/getMachineId.ts | 37 ++++++++ .../test/detectors/node/HostDetector.test.ts | 34 ++++++++ .../node/machine-id/getMachineId-bsd.test.ts | 80 +++++++++++++++++ .../machine-id/getMachineId-darwin.test.ts | 86 +++++++++++++++++++ .../machine-id/getMachineId-linux.test.ts | 66 ++++++++++++++ .../node/machine-id/getMachineId-win.test.ts | 59 +++++++++++++ 14 files changed, 569 insertions(+), 1 deletion(-) create mode 100644 packages/opentelemetry-resources/src/platform/node/machine-id/execAsync.ts create mode 100644 packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId-bsd.ts create mode 100644 packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId-darwin.ts create mode 100644 packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId-linux.ts create mode 100644 packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId-unsupported.ts create mode 100644 packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId-win.ts create mode 100644 packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId.ts create mode 100644 packages/opentelemetry-resources/test/detectors/node/machine-id/getMachineId-bsd.test.ts create mode 100644 packages/opentelemetry-resources/test/detectors/node/machine-id/getMachineId-darwin.test.ts create mode 100644 packages/opentelemetry-resources/test/detectors/node/machine-id/getMachineId-linux.test.ts create mode 100644 packages/opentelemetry-resources/test/detectors/node/machine-id/getMachineId-win.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index eca414a612..2772fb4f78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/ ## Unreleased +* feat: collect host id for non-cloud environments [#3575](https://github.com/open-telemetry/opentelemetry-js/pull/3575) @mwear + ### :boom: Breaking Change ### :rocket: (Enhancement) diff --git a/packages/opentelemetry-resources/src/platform/node/HostDetectorSync.ts b/packages/opentelemetry-resources/src/platform/node/HostDetectorSync.ts index 85bd717e54..9bd5e6cb5f 100644 --- a/packages/opentelemetry-resources/src/platform/node/HostDetectorSync.ts +++ b/packages/opentelemetry-resources/src/platform/node/HostDetectorSync.ts @@ -20,6 +20,7 @@ import { DetectorSync, ResourceAttributes } from '../../types'; import { ResourceDetectionConfig } from '../../config'; import { arch, hostname } from 'os'; import { normalizeArch } from './utils'; +import { getMachineId } from './machine-id/getMachineId'; /** * HostDetectorSync detects the resources related to the host current process is @@ -31,7 +32,18 @@ class HostDetectorSync implements DetectorSync { [SemanticResourceAttributes.HOST_NAME]: hostname(), [SemanticResourceAttributes.HOST_ARCH]: normalizeArch(arch()), }; - return new Resource(attributes); + + return new Resource(attributes, this._getAsyncAttributes()); + } + + private _getAsyncAttributes(): Promise { + return getMachineId().then(machineId => { + const attributes: ResourceAttributes = {}; + if (machineId) { + attributes[SemanticResourceAttributes.HOST_ID] = machineId; + } + return attributes; + }); } } diff --git a/packages/opentelemetry-resources/src/platform/node/machine-id/execAsync.ts b/packages/opentelemetry-resources/src/platform/node/machine-id/execAsync.ts new file mode 100644 index 0000000000..c362a41862 --- /dev/null +++ b/packages/opentelemetry-resources/src/platform/node/machine-id/execAsync.ts @@ -0,0 +1,20 @@ +/* + * 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 child_process from 'child_process'; +import * as util from 'util'; + +export const execAsync = util.promisify(child_process.exec); diff --git a/packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId-bsd.ts b/packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId-bsd.ts new file mode 100644 index 0000000000..edd04c7f1b --- /dev/null +++ b/packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId-bsd.ts @@ -0,0 +1,37 @@ +/* + * 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 fs from 'fs/promises'; +import { execAsync } from './execAsync'; +import { diag } from '@opentelemetry/api'; + +export async function getMachineId(): Promise { + try { + const result = await fs.readFile('/etc/hostid', { encoding: 'utf8' }); + return result.trim(); + } catch (e) { + diag.debug(`error reading machine id: ${e}`); + } + + try { + const result = await execAsync('kenv -q smbios.system.uuid'); + return result.stdout.trim(); + } catch (e) { + diag.debug(`error reading machine id: ${e}`); + } + + return ''; +} diff --git a/packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId-darwin.ts b/packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId-darwin.ts new file mode 100644 index 0000000000..7f6975ef54 --- /dev/null +++ b/packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId-darwin.ts @@ -0,0 +1,41 @@ +/* + * 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 { execAsync } from './execAsync'; +import { diag } from '@opentelemetry/api'; + +export async function getMachineId(): Promise { + try { + const result = await execAsync('ioreg -rd1 -c "IOPlatformExpertDevice"'); + + const idLine = result.stdout + .split('\n') + .find(line => line.includes('IOPlatformUUID')); + + if (!idLine) { + return ''; + } + + const parts = idLine.split('" = "'); + if (parts.length === 2) { + return parts[1].slice(0, -1); + } + } catch (e) { + diag.debug(`error reading machine id: ${e}`); + } + + return ''; +} diff --git a/packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId-linux.ts b/packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId-linux.ts new file mode 100644 index 0000000000..74b145a2f4 --- /dev/null +++ b/packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId-linux.ts @@ -0,0 +1,32 @@ +/* + * 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 fs from 'fs/promises'; +import { diag } from '@opentelemetry/api'; + +export async function getMachineId(): Promise { + const paths = ['/etc/machine-id', '/var/lib/dbus/machine-id']; + + for (const path of paths) { + try { + const result = await fs.readFile(path, { encoding: 'utf8' }); + return result.trim(); + } catch (e) { + diag.debug(`error reading machine id: ${e}`); + } + } + + return ''; +} diff --git a/packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId-unsupported.ts b/packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId-unsupported.ts new file mode 100644 index 0000000000..cc05ca1c2e --- /dev/null +++ b/packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId-unsupported.ts @@ -0,0 +1,22 @@ +/* + * 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 { diag } from '@opentelemetry/api'; + +export async function getMachineId(): Promise { + diag.debug('could not read machine-id: unsupported platform'); + return ''; +} diff --git a/packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId-win.ts b/packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId-win.ts new file mode 100644 index 0000000000..05e9d3b7c1 --- /dev/null +++ b/packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId-win.ts @@ -0,0 +1,40 @@ +/* + * 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 process from 'process'; +import { execAsync } from './execAsync'; +import { diag } from '@opentelemetry/api'; + +export async function getMachineId(): Promise { + const args = + 'QUERY HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography /v MachineGuid'; + let command = '%windir%\\System32\\REG.exe'; + if (process.arch === 'ia32' && 'PROCESSOR_ARCHITEW6432' in process.env) { + command = '%windir%\\sysnative\\cmd.exe /c ' + command; + } + + try { + const result = await execAsync(`${command} ${args}`); + const parts = result.stdout.split('REG_SZ'); + if (parts.length === 2) { + return parts[1].trim(); + } + } catch (e) { + diag.debug(`error reading machine id: ${e}`); + } + + return ''; +} diff --git a/packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId.ts b/packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId.ts new file mode 100644 index 0000000000..47de5913b4 --- /dev/null +++ b/packages/opentelemetry-resources/src/platform/node/machine-id/getMachineId.ts @@ -0,0 +1,37 @@ +/* + * 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 process from 'process'; + +let getMachineId: () => Promise; + +switch (process.platform) { + case 'darwin': + ({ getMachineId } = require('./getMachineId-darwin')); + break; + case 'linux': + ({ getMachineId } = require('./getMachineId-linux')); + break; + case 'freebsd': + ({ getMachineId } = require('./getMachineId-bsd')); + break; + case 'win32': + ({ getMachineId } = require('./getMachineId-win')); + break; + default: + ({ getMachineId } = require('./getMachineId-unsupported')); +} + +export { getMachineId }; diff --git a/packages/opentelemetry-resources/test/detectors/node/HostDetector.test.ts b/packages/opentelemetry-resources/test/detectors/node/HostDetector.test.ts index 287b98b10b..2e69de7e19 100644 --- a/packages/opentelemetry-resources/test/detectors/node/HostDetector.test.ts +++ b/packages/opentelemetry-resources/test/detectors/node/HostDetector.test.ts @@ -27,11 +27,16 @@ describeNode('hostDetector() on Node.js', () => { it('should return resource information about the host', async () => { const os = require('os'); + const mid = require('../../../src/platform/node/machine-id/getMachineId'); + + const expectedHostId = 'f2c668b579780554f70f72a063dc0864'; sinon.stub(os, 'arch').returns('x64'); sinon.stub(os, 'hostname').returns('opentelemetry-test'); + sinon.stub(mid, 'getMachineId').returns(Promise.resolve(expectedHostId)); const resource: IResource = await hostDetector.detect(); + await resource.waitForAsyncAttributes?.(); assert.strictEqual( resource.attributes[SemanticResourceAttributes.HOST_NAME], @@ -41,6 +46,10 @@ describeNode('hostDetector() on Node.js', () => { resource.attributes[SemanticResourceAttributes.HOST_ARCH], 'amd64' ); + assert.strictEqual( + resource.attributes[SemanticResourceAttributes.HOST_ID], + expectedHostId + ); }); it('should pass through arch string if unknown', async () => { @@ -55,4 +64,29 @@ describeNode('hostDetector() on Node.js', () => { 'some-unknown-arch' ); }); + + it('should handle missing machine id', async () => { + const os = require('os'); + const mid = require('../../../src/platform/node/machine-id/getMachineId'); + + sinon.stub(os, 'arch').returns('x64'); + sinon.stub(os, 'hostname').returns('opentelemetry-test'); + sinon.stub(mid, 'getMachineId').returns(Promise.resolve('')); + + const resource: IResource = await hostDetector.detect(); + await resource.waitForAsyncAttributes?.(); + + assert.strictEqual( + resource.attributes[SemanticResourceAttributes.HOST_NAME], + 'opentelemetry-test' + ); + assert.strictEqual( + resource.attributes[SemanticResourceAttributes.HOST_ARCH], + 'amd64' + ); + assert.strictEqual( + false, + SemanticResourceAttributes.HOST_ID in resource.attributes + ); + }); }); diff --git a/packages/opentelemetry-resources/test/detectors/node/machine-id/getMachineId-bsd.test.ts b/packages/opentelemetry-resources/test/detectors/node/machine-id/getMachineId-bsd.test.ts new file mode 100644 index 0000000000..317132a968 --- /dev/null +++ b/packages/opentelemetry-resources/test/detectors/node/machine-id/getMachineId-bsd.test.ts @@ -0,0 +1,80 @@ +/* + * 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 sinon from 'sinon'; +import * as assert from 'assert'; +import * as fs from 'fs/promises'; +import { PromiseWithChild } from 'child_process'; +import * as util from '../../../../src/platform/node/machine-id/execAsync'; +import { getMachineId } from '../../../../src/platform/node/machine-id/getMachineId-bsd'; + +describe('getMachineId on BSD', () => { + const expectedMachineId = 'f2c668b579780554f70f72a063dc0864'; + const fileContents = `${expectedMachineId}\n`; + + afterEach(() => { + sinon.restore(); + }); + + it('reads machine-id from primary', async () => { + const stub = sinon + .stub(fs, 'readFile') + .returns(Promise.resolve(fileContents)); + + const machineId = await getMachineId(); + + assert.equal(machineId, expectedMachineId); + assert.equal(stub.callCount, 1); + }); + + it('reads machine-id from fallback when primary fails', async () => { + const fsStub = sinon + .stub(fs, 'readFile') + .returns(Promise.reject(new Error('not found'))); + + const execStub = sinon.stub(util, 'execAsync').returns( + Promise.resolve({ + stdout: fileContents, + stderr: '', + }) as PromiseWithChild<{ stdout: string; stderr: string }> + ); + + const machineId = await getMachineId(); + + assert.equal(machineId, expectedMachineId); + assert.equal(fsStub.callCount, 1); + assert.equal(execStub.callCount, 1); + }); + + it('handles failure to read primary and fallback', async () => { + const fsStub = sinon + .stub(fs, 'readFile') + .returns(Promise.reject(new Error('not found'))); + + const execStub = sinon.stub(util, 'execAsync').returns( + Promise.reject(new Error('not found')) as PromiseWithChild<{ + stdout: string; + stderr: string; + }> + ); + + const machineId = await getMachineId(); + + assert.equal(machineId, ''); + assert.equal(fsStub.callCount, 1); + assert.equal(execStub.callCount, 1); + }); +}); diff --git a/packages/opentelemetry-resources/test/detectors/node/machine-id/getMachineId-darwin.test.ts b/packages/opentelemetry-resources/test/detectors/node/machine-id/getMachineId-darwin.test.ts new file mode 100644 index 0000000000..06e1f942f8 --- /dev/null +++ b/packages/opentelemetry-resources/test/detectors/node/machine-id/getMachineId-darwin.test.ts @@ -0,0 +1,86 @@ +/* + * 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 sinon from 'sinon'; +import * as assert from 'assert'; +import { PromiseWithChild } from 'child_process'; +import * as util from '../../../../src/platform/node/machine-id/execAsync'; +import { getMachineId } from '../../../../src/platform/node/machine-id/getMachineId-darwin'; + +describe('getMachineId on Darwin', () => { + const expectedMachineId = '81895B8D-9EF9-4EBB-B5DE-B00069CF53F0'; + const cmdOutput = + '+-o J316sAP \n' + + '{\n' + + ' "IOPolledInterface" = "AppleARMWatchdogTimerHibernateHandler is not serializable"\n' + + ' "#address-cells" = <02000000>\n' + + ' "AAPL,phandle" = <01000000>\n' + + ' "serial-number" = <94e1c79ec04cd3f153f600000000000000000000000000000000000000000000>\n' + + ' "IOBusyInterest" = "IOCommand is not serializable"\n' + + ' "target-type" = <"J316s">\n' + + ' "platform-name" = <7436303030000000000000000000000000000000000000000000000000000000>\n' + + ' "secure-root-prefix" = <"md">\n' + + ' "name" = <"device-tree">\n' + + ' "region-info" = <4c4c2f4100000000000000000000000000000000000000000000000000000000>\n' + + ' "manufacturer" = <"Apple Inc.">\n' + + ' "compatible" = <"J316sAP","MacBookPro18,1","AppleARM">\n' + + ' "config-number" = <00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000>\n' + + ' "IOPlatformSerialNumber" = "HDWLIF2LM7"\n' + + ' "regulatory-model-number" = <4132343835000000000000000000000000000000000000000000000000000000>\n' + + ' "time-stamp" = <"Fri Aug 5 20:25:38 PDT 2022">\n' + + ' "clock-frequency" = <00366e01>\n' + + ' "model" = <"MacBookPro18,1">\n' + + ' "mlb-serial-number" = <5c92d268d6cd789e475ffafc0d363fc950000000000000000000000000000000>\n' + + ' "model-number" = <5a31345930303136430000000000000000000000000000000000000000000000>\n' + + ' "IONWInterrupts" = "IONWInterrupts"\n' + + ' "model-config" = <"ICT;MoPED=0x03D053A605C84ED11C455A18D6C643140B41A239">\n' + + ' "device_type" = <"bootrom">\n' + + ' "#size-cells" = <02000000>\n' + + ' "IOPlatformUUID" = "81895B8D-9EF9-4EBB-B5DE-B00069CF53F0"\n' + + '}\n'; + + afterEach(() => { + sinon.restore(); + }); + + it('reads machine-id', async () => { + const stub = sinon.stub(util, 'execAsync').returns( + Promise.resolve({ stdout: cmdOutput, stderr: '' }) as PromiseWithChild<{ + stdout: string; + stderr: string; + }> + ); + + const machineId = await getMachineId(); + + assert.equal(machineId, expectedMachineId); + assert.equal(stub.callCount, 1); + }); + + it('handles failure', async () => { + const stub = sinon.stub(util, 'execAsync').returns( + Promise.reject(new Error('not found')) as PromiseWithChild<{ + stdout: string; + stderr: string; + }> + ); + + const machineId = await getMachineId(); + + assert.equal(machineId, ''); + assert.equal(stub.callCount, 1); + }); +}); diff --git a/packages/opentelemetry-resources/test/detectors/node/machine-id/getMachineId-linux.test.ts b/packages/opentelemetry-resources/test/detectors/node/machine-id/getMachineId-linux.test.ts new file mode 100644 index 0000000000..d8dfe4d1da --- /dev/null +++ b/packages/opentelemetry-resources/test/detectors/node/machine-id/getMachineId-linux.test.ts @@ -0,0 +1,66 @@ +/* + * 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 sinon from 'sinon'; +import * as assert from 'assert'; +import * as fs from 'fs/promises'; + +import { getMachineId } from '../../../../src/platform/node/machine-id/getMachineId-linux'; + +describe('getMachineId on linux', () => { + const expectedMachineId = 'f2c668b579780554f70f72a063dc0864'; + const fileContents = `${expectedMachineId}\n`; + + afterEach(() => { + sinon.restore(); + }); + + it('reads machine-id from primary', async () => { + const stub = sinon + .stub(fs, 'readFile') + .returns(Promise.resolve(fileContents)); + + const machineId = await getMachineId(); + + assert.equal(machineId, expectedMachineId); + assert.equal(stub.callCount, 1); + }); + + it('reads machine-id from fallback when primary fails', async () => { + const stub = sinon + .stub(fs, 'readFile') + .onFirstCall() + .returns(Promise.reject(new Error('not found'))) + .onSecondCall() + .returns(Promise.resolve(fileContents)); + + const machineId = await getMachineId(); + + assert.equal(machineId, expectedMachineId); + assert.equal(stub.callCount, 2); + }); + + it('handles failure to read primary and fallback', async () => { + const stub = sinon + .stub(fs, 'readFile') + .returns(Promise.reject(new Error('not found'))); + + const machineId = await getMachineId(); + + assert.equal(machineId, ''); + assert.equal(stub.callCount, 2); + }); +}); diff --git a/packages/opentelemetry-resources/test/detectors/node/machine-id/getMachineId-win.test.ts b/packages/opentelemetry-resources/test/detectors/node/machine-id/getMachineId-win.test.ts new file mode 100644 index 0000000000..fba7747fee --- /dev/null +++ b/packages/opentelemetry-resources/test/detectors/node/machine-id/getMachineId-win.test.ts @@ -0,0 +1,59 @@ +/* + * 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 sinon from 'sinon'; +import * as assert from 'assert'; +import { PromiseWithChild } from 'child_process'; +import * as util from '../../../../src/platform/node/machine-id/execAsync'; +import { getMachineId } from '../../../../src/platform/node/machine-id/getMachineId-win'; + +describe('getMachineId on Windows', () => { + const expectedMachineId = 'fe11b90a-d48c-4c3b-b386-e40cc383fd30'; + const cmdOutput = + '\n MachineGuid REG_SZ fe11b90a-d48c-4c3b-b386-e40cc383fd30\n'; + + afterEach(() => { + sinon.restore(); + }); + + it('reads machine-id', async () => { + const stub = sinon.stub(util, 'execAsync').returns( + Promise.resolve({ stdout: cmdOutput, stderr: '' }) as PromiseWithChild<{ + stdout: string; + stderr: string; + }> + ); + + const machineId = await getMachineId(); + + assert.equal(machineId, expectedMachineId); + assert.equal(stub.callCount, 1); + }); + + it('handles failure', async () => { + const stub = sinon.stub(util, 'execAsync').returns( + Promise.reject(new Error('not found')) as PromiseWithChild<{ + stdout: string; + stderr: string; + }> + ); + + const machineId = await getMachineId(); + + assert.equal(machineId, ''); + assert.equal(stub.callCount, 1); + }); +}); From 74ca04f7b2253f8696013a4bf348eac905c32078 Mon Sep 17 00:00:00 2001 From: Marc Pichler Date: Wed, 1 Mar 2023 09:42:01 +0100 Subject: [PATCH 28/37] fix(test): fix failing tests by preventing source-map generation (#3642) * fix(test): fix excessive memory usage by preventing source-map generation * fix(changlog): add changelog entry. --- .nycrc | 1 + CHANGELOG.md | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.nycrc b/.nycrc index 520f016fc2..22383d8aba 100644 --- a/.nycrc +++ b/.nycrc @@ -1,4 +1,5 @@ { + "produceSourceMap": false, "extension": [ ".ts" ], diff --git a/CHANGELOG.md b/CHANGELOG.md index 2772fb4f78..607cb9544d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,7 +28,8 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/ ### :house: (Internal) -* fix(sdk-metrics): ix flaky LastValueAggregator test by using fake timer [#3587](https://github.com/open-telemetry/opentelemetry-js/pull/3587) @pichlermarc +* fix(sdk-metrics): fix flaky LastValueAggregator test by using fake timer [#3587](https://github.com/open-telemetry/opentelemetry-js/pull/3587) @pichlermarc +* fix(test): fix failing tests by preventing source-map generation [#3642](https://github.com/open-telemetry/opentelemetry-js/pull/3642) @pichlermarc ## 1.9.1 From a457d941c20fab1a920c8eb43e47d741f02c0550 Mon Sep 17 00:00:00 2001 From: kobi-co <112798986+kobi-co@users.noreply.github.com> Date: Wed, 1 Mar 2023 23:38:13 +0200 Subject: [PATCH 29/37] fix(instrumentation-http): include query params in http.target (#3646) * fix: set http.target as path (like spec) and not pathname * fix: set http.target as path (like spec) and not pathname * fix: set http.target as path (like spec) and not pathname --- CHANGELOG.md | 1 + .../src/utils.ts | 2 +- .../test/functionals/utils.test.ts | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 607cb9544d..a5014e7737 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/ ### :bug: (Bug Fix) * fix(core): added falsy check to make otel core work with browser where webpack config had process as false or null [#3613](https://github.com/open-telemetry/opentelemetry-js/issues/3613) @ravindra-dyte +* fix(instrumentation-http): include query params in http.target [#3646](https://github.com/open-telemetry/opentelemetry-js/pull/3646) @kobi-co ### :books: (Refine Doc) diff --git a/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts b/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts index 7801a7626c..a563e29471 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/src/utils.ts @@ -484,7 +484,7 @@ export const getIncomingRequestAttributes = ( } if (requestUrl) { - attributes[SemanticAttributes.HTTP_TARGET] = requestUrl.pathname || '/'; + attributes[SemanticAttributes.HTTP_TARGET] = requestUrl.path || '/'; } if (userAgent !== undefined) { diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts index 700498cfb6..35b3c1ab5b 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts @@ -503,6 +503,23 @@ describe('Utility', () => { }); assert.strictEqual(attributes[SemanticAttributes.HTTP_ROUTE], undefined); }); + + it('should set http.target as path in http span attributes', () => { + const request = { + url: 'http://hostname/user/?q=val', + method: 'GET', + } as IncomingMessage; + request.headers = { + 'user-agent': 'chrome', + }; + const attributes = utils.getIncomingRequestAttributes(request, { + component: 'http', + }); + assert.strictEqual( + attributes[SemanticAttributes.HTTP_TARGET], + '/user/?q=val' + ); + }); }); describe('headers to span attributes capture', () => { From 31dd7b72588a2490b52577efe021445426cd7dda Mon Sep 17 00:00:00 2001 From: Altinok Darici Date: Wed, 1 Mar 2023 13:53:13 -0800 Subject: [PATCH 30/37] Rename Diag/LoggerOptions to DiagLoggerOptions (#3641) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Rename LoggerOptions to DiagLoggerOptions * Change log --------- Co-authored-by: Marc Pichler Co-authored-by: Gerhard Stöbich Co-authored-by: Daniel Dyla --- api/CHANGELOG.md | 1 + api/src/diag/types.ts | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/api/CHANGELOG.md b/api/CHANGELOG.md index cc96483c76..be8c623e84 100644 --- a/api/CHANGELOG.md +++ b/api/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. ### :bug: (Bug Fix) * fix(metrics): export `MetricsAPI` type [#3535](https://github.com/open-telemetry/opentelemetry-js/pull/3535) +* fix(api): rename `LoggerOptions` to `DiagLoggerOptions` [#3641](https://github.com/open-telemetry/opentelemetry-js/pull/3641) ## 1.4.0 diff --git a/api/src/diag/types.ts b/api/src/diag/types.ts index e861d430c1..588b525ec6 100644 --- a/api/src/diag/types.ts +++ b/api/src/diag/types.ts @@ -97,7 +97,7 @@ export interface ComponentLoggerOptions { namespace: string; } -export interface LoggerOptions { +export interface DiagLoggerOptions { /** * The {@link DiagLogLevel} used to filter logs sent to the logger. * @@ -117,10 +117,10 @@ export interface DiagLoggerApi { * If a global diag logger is already set, this will override it. * * @param logger - The {@link DiagLogger} instance to set as the default logger. - * @param options - A {@link LoggerOptions} object. If not provided, default values will be set. + * @param options - A {@link DiagLoggerOptions} object. If not provided, default values will be set. * @returns `true` if the logger was successfully registered, else `false` */ - setLogger(logger: DiagLogger, options?: LoggerOptions): boolean; + setLogger(logger: DiagLogger, options?: DiagLoggerOptions): boolean; /** * From 6f3e96fd771149db0af48a7e5dc0a000a5d0ea72 Mon Sep 17 00:00:00 2001 From: Altinok Darici Date: Thu, 2 Mar 2023 04:45:58 -0800 Subject: [PATCH 31/37] Export DiagLoggerOptions Type (#3639) --- api/CHANGELOG.md | 1 + api/src/index.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/api/CHANGELOG.md b/api/CHANGELOG.md index be8c623e84..31697a308c 100644 --- a/api/CHANGELOG.md +++ b/api/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. * fix(metrics): export `MetricsAPI` type [#3535](https://github.com/open-telemetry/opentelemetry-js/pull/3535) * fix(api): rename `LoggerOptions` to `DiagLoggerOptions` [#3641](https://github.com/open-telemetry/opentelemetry-js/pull/3641) +* fix(api): export `DiagLoggerOptions` type [#3639](https://github.com/open-telemetry/opentelemetry-js/pull/3639) ## 1.4.0 diff --git a/api/src/index.ts b/api/src/index.ts index 2326aba9dc..6992005874 100644 --- a/api/src/index.ts +++ b/api/src/index.ts @@ -32,6 +32,7 @@ export { DiagLogger, DiagLogLevel, ComponentLoggerOptions, + DiagLoggerOptions, } from './diag/types'; export type { DiagAPI } from './api/diag'; From 0fb78797edaad0808b99cb1f94c48fb4a56c87ee Mon Sep 17 00:00:00 2001 From: Haddas Bronfman <85441461+haddasbronfman@users.noreply.github.com> Date: Thu, 2 Mar 2023 15:01:21 +0200 Subject: [PATCH 32/37] feat(sdk-node): override IdGenerator when using NodeSDK (#3645) --- experimental/CHANGELOG.md | 1 + .../opentelemetry-sdk-node/src/sdk.ts | 3 ++ .../opentelemetry-sdk-node/src/types.ts | 2 ++ .../opentelemetry-sdk-node/test/sdk.test.ts | 28 +++++++++++++++++++ 4 files changed, 34 insertions(+) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index b85f4c1d5f..5373a22789 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -13,6 +13,7 @@ All notable changes to experimental packages in this project will be documented * feat: use HTTP_ROUTE in span name [#3603](https://github.com/open-telemetry/opentelemetry-js/pull/3603) @Flarna * feat: add HTTP_ROUTE attribute to http incoming metrics if present [#3581](https://github.com/open-telemetry/opentelemetry-js/pull/3581) @hermogenes * feat(sdk-node): install diag logger with OTEL_LOG_LEVEL [#3627](https://github.com/open-telemetry/opentelemetry-js/pull/3627) @legendecas +* feat(sdk-node): override IdGenerator when using NodeSDK [#3645](https://github.com/open-telemetry/opentelemetry-js/pull/3645) @haddasbronfman ### :bug: (Bug Fix) diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index faae12dc7d..8b0e539eeb 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -117,6 +117,9 @@ export class NodeSDK { if (configuration.spanLimits) { tracerProviderConfig.spanLimits = configuration.spanLimits; } + if (configuration.idGenerator) { + tracerProviderConfig.idGenerator = configuration.idGenerator; + } const spanProcessor = configuration.spanProcessor ?? diff --git a/experimental/packages/opentelemetry-sdk-node/src/types.ts b/experimental/packages/opentelemetry-sdk-node/src/types.ts index 49dfae8f7c..38178e58c5 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/types.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/types.ts @@ -24,6 +24,7 @@ import { SpanExporter, SpanLimits, SpanProcessor, + IdGenerator, } from '@opentelemetry/sdk-trace-base'; export interface NodeSDKConfiguration { @@ -41,4 +42,5 @@ export interface NodeSDKConfiguration { spanProcessor: SpanProcessor; traceExporter: SpanExporter; spanLimits: SpanLimits; + idGenerator: IdGenerator; } diff --git a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts index fb43901710..5fcdb7f94d 100644 --- a/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -45,6 +45,7 @@ import { SimpleSpanProcessor, BatchSpanProcessor, NoopSpanProcessor, + IdGenerator, } from '@opentelemetry/sdk-trace-base'; import * as assert from 'assert'; import * as semver from 'semver'; @@ -691,6 +692,33 @@ describe('Node SDK', () => { }); }); }); + + describe('configure IdGenerator', async () => { + class CustomIdGenerator implements IdGenerator { + generateTraceId(): string { + return 'constant-test-trace-id'; + } + generateSpanId(): string { + return 'constant-test-span-id'; + } + } + + it('should configure IdGenerator via config', async () => { + const idGenerator = new CustomIdGenerator(); + const spanProcessor = new SimpleSpanProcessor(new ConsoleSpanExporter()); + const sdk = new NodeSDK({ + idGenerator, + spanProcessor, + }); + sdk.start(); + + const span = trace.getTracer('test').startSpan('testName'); + span.end(); + + assert.strictEqual(span.spanContext().spanId, 'constant-test-span-id'); + assert.strictEqual(span.spanContext().traceId, 'constant-test-trace-id'); + }); + }); }); describe('setup exporter from env', () => { From 494efffcf4648e3b1207c3c925c1a52bcc7f7350 Mon Sep 17 00:00:00 2001 From: Svetlana Brennan <50715937+svetlanabrennan@users.noreply.github.com> Date: Thu, 2 Mar 2023 07:31:34 -0600 Subject: [PATCH 33/37] fix(otlp-exporter-base): fix changelog for merged retry pr (#3650) --- experimental/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 5373a22789..a8140469bf 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -13,6 +13,7 @@ All notable changes to experimental packages in this project will be documented * feat: use HTTP_ROUTE in span name [#3603](https://github.com/open-telemetry/opentelemetry-js/pull/3603) @Flarna * feat: add HTTP_ROUTE attribute to http incoming metrics if present [#3581](https://github.com/open-telemetry/opentelemetry-js/pull/3581) @hermogenes * feat(sdk-node): install diag logger with OTEL_LOG_LEVEL [#3627](https://github.com/open-telemetry/opentelemetry-js/pull/3627) @legendecas +* feat(otlp-exporter-base): add retries [#3207](https://github.com/open-telemetry/opentelemetry-js/pull/3207) @svetlanabrennan * feat(sdk-node): override IdGenerator when using NodeSDK [#3645](https://github.com/open-telemetry/opentelemetry-js/pull/3645) @haddasbronfman ### :bug: (Bug Fix) @@ -76,7 +77,6 @@ All notable changes to experimental packages in this project will be documented * deps: remove unused proto-loader dependencies and update grpc-js and proto-loader versions [#3337](https://github.com/open-telemetry/opentelemetry-js/pull/3337) @seemk * feat(metrics-exporters): configure temporality via environment variable [#3305](https://github.com/open-telemetry/opentelemetry-js/pull/3305) @pichlermarc * feat(console-metric-exporter): add temporality configuration [#3387](https://github.com/open-telemetry/opentelemetry-js/pull/3387) @pichlermarc -* feat(otlp-exporter-base): add retries [#3207](https://github.com/open-telemetry/opentelemetry-js/pull/3207) @svetlanabrennan ### :bug: (Bug Fix) From 4a4484a910201466121ed45fa12566737f6ac19d Mon Sep 17 00:00:00 2001 From: Jamie Danielson Date: Thu, 2 Mar 2023 09:36:24 -0500 Subject: [PATCH 34/37] chore: update http example (#3651) * update versions in http example * use newer active span methods for http example * fix(changelog): add changelog entry * Add http example to lerna --------- Co-authored-by: Daniel Dyla --- CHANGELOG.md | 2 ++ examples/http/client.js | 3 +-- examples/http/package.json | 20 ++++++++++---------- examples/http/server.js | 5 +++-- lerna.json | 1 + 5 files changed, 17 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5014e7737..6f2bae09b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,8 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/ ### :books: (Refine Doc) +* chore: update http example [#3651](https://github.com/open-telemetry/opentelemetry-js/pull/3651) @JamieDanielson + ### :house: (Internal) * fix(sdk-metrics): fix flaky LastValueAggregator test by using fake timer [#3587](https://github.com/open-telemetry/opentelemetry-js/pull/3587) @pichlermarc diff --git a/examples/http/client.js b/examples/http/client.js index 40babbda89..168d433922 100644 --- a/examples/http/client.js +++ b/examples/http/client.js @@ -9,8 +9,7 @@ function makeRequest() { // span corresponds to outgoing requests. Here, we have manually created // the span, which is created to track work that happens outside of the // request lifecycle entirely. - const span = tracer.startSpan('makeRequest'); - api.context.with(api.trace.setSpan(api.context.active(), span), () => { + tracer.startActiveSpan('makeRequest', (span) => { http.get({ host: 'localhost', port: 8080, diff --git a/examples/http/package.json b/examples/http/package.json index 490f9dba75..f10a5992dc 100644 --- a/examples/http/package.json +++ b/examples/http/package.json @@ -1,7 +1,7 @@ { "name": "http-example", "private": true, - "version": "0.25.0", + "version": "0.35.1", "description": "Example of HTTP integration with OpenTelemetry", "main": "index.js", "scripts": { @@ -28,15 +28,15 @@ "url": "https://github.com/open-telemetry/opentelemetry-js/issues" }, "dependencies": { - "@opentelemetry/api": "^1.0.2", - "@opentelemetry/exporter-jaeger": "0.25.0", - "@opentelemetry/exporter-zipkin": "0.25.0", - "@opentelemetry/instrumentation": "0.25.0", - "@opentelemetry/instrumentation-http": "0.25.0", - "@opentelemetry/resources": "0.25.0", - "@opentelemetry/semantic-conventions": "0.25.0", - "@opentelemetry/sdk-trace-node": "0.25.0", - "@opentelemetry/sdk-trace-base": "0.25.0" + "@opentelemetry/api": "^1.3.0", + "@opentelemetry/exporter-jaeger": "1.9.1", + "@opentelemetry/exporter-zipkin": "1.9.1", + "@opentelemetry/instrumentation": "0.35.1", + "@opentelemetry/instrumentation-http": "0.35.1", + "@opentelemetry/resources": "1.9.1", + "@opentelemetry/semantic-conventions": "1.9.1", + "@opentelemetry/sdk-trace-node": "1.9.1", + "@opentelemetry/sdk-trace-base": "1.9.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/http", "devDependencies": { diff --git a/examples/http/server.js b/examples/http/server.js index 1c11f60323..ea65858b52 100644 --- a/examples/http/server.js +++ b/examples/http/server.js @@ -19,9 +19,10 @@ function startServer(port) { /** A function which handles requests and send response. */ function handleRequest(request, response) { - const currentSpan = api.trace.getSpan(api.context.active()); + const currentSpan = api.trace.getActiveSpan(); // display traceid in the terminal - console.log(`traceid: ${currentSpan.spanContext().traceId}`); + const traceId = currentSpan.spanContext().traceId; + console.log(`traceId: ${traceId}`); const span = tracer.startSpan('handleRequest', { kind: 1, // server attributes: { key: 'value' }, diff --git a/lerna.json b/lerna.json index d97ab02a62..6eba3f4874 100644 --- a/lerna.json +++ b/lerna.json @@ -11,6 +11,7 @@ "selenium-tests", "examples/otlp-exporter-node", "examples/opentelemetry-web", + "examples/http", "examples/https" ] } From cbbdfd379854fcb41bd70841ae4890200a2fda09 Mon Sep 17 00:00:00 2001 From: Marc Pichler Date: Thu, 2 Mar 2023 15:46:14 +0100 Subject: [PATCH 35/37] fix(sdk-node): update instrumentations once MeterProvider is initialized (#3624) * fix(sdk-node): provide workaround for broken metrics instrumentation registration * fix(changelog): add changelog entry --------- Co-authored-by: Daniel Dyla --- experimental/CHANGELOG.md | 1 + .../opentelemetry-sdk-node/src/sdk.ts | 11 +++++ .../opentelemetry-sdk-node/src/utils.ts | 43 +++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 experimental/packages/opentelemetry-sdk-node/src/utils.ts diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index a8140469bf..d025c4d4b7 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -19,6 +19,7 @@ All notable changes to experimental packages in this project will be documented ### :bug: (Bug Fix) * fix(prometheus-exporter): add possibility to respond to errors returned by `server.listen()` [#3552](https://github.com/open-telemetry/opentelemetry-js/pull/3402) @pichlermarc + fix(sdk-node): update instrumentations once MeterProvider is initialized [#3624](https://github.com/open-telemetry/opentelemetry-js/pull/3624) @pichlermarc ### :books: (Refine Doc) diff --git a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts index 8b0e539eeb..dc9244f3e4 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/sdk.ts @@ -48,6 +48,7 @@ import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions' import { NodeSDKConfiguration } from './types'; import { TracerProviderWithEnvExporters } from './TracerProviderWithEnvExporter'; import { getEnv } from '@opentelemetry/core'; +import { parseInstrumentationOptions } from './utils'; /** This class represents everything needed to register a fully configured OpenTelemetry Node.js SDK */ @@ -61,6 +62,7 @@ export type MeterProviderConfig = { */ views?: View[]; }; + export class NodeSDK { private _tracerProviderConfig?: { tracerConfig: NodeTracerConfig; @@ -275,6 +277,15 @@ export class NodeSDK { this._meterProvider = meterProvider; metrics.setGlobalMeterProvider(meterProvider); + + // TODO: This is a workaround to fix https://github.com/open-telemetry/opentelemetry-js/issues/3609 + // If the MeterProvider is not yet registered when instrumentations are registered, all metrics are dropped. + // This code is obsolete once https://github.com/open-telemetry/opentelemetry-js/issues/3622 is implemented. + for (const instrumentation of parseInstrumentationOptions( + this._instrumentations + )) { + instrumentation.setMeterProvider(metrics.getMeterProvider()); + } } } diff --git a/experimental/packages/opentelemetry-sdk-node/src/utils.ts b/experimental/packages/opentelemetry-sdk-node/src/utils.ts new file mode 100644 index 0000000000..a3d8314747 --- /dev/null +++ b/experimental/packages/opentelemetry-sdk-node/src/utils.ts @@ -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. + */ + +import { + Instrumentation, + InstrumentationOption, +} from '@opentelemetry/instrumentation'; + +// TODO: This part of a workaround to fix https://github.com/open-telemetry/opentelemetry-js/issues/3609 +// If the MeterProvider is not yet registered when instrumentations are registered, all metrics are dropped. +// This code is obsolete once https://github.com/open-telemetry/opentelemetry-js/issues/3622 is implemented. +export function parseInstrumentationOptions( + options: InstrumentationOption[] = [] +): Instrumentation[] { + let instrumentations: Instrumentation[] = []; + for (let i = 0, j = options.length; i < j; i++) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const option = options[i] as any; + if (Array.isArray(option)) { + const results = parseInstrumentationOptions(option); + instrumentations = instrumentations.concat(results); + } else if (typeof option === 'function') { + instrumentations.push(new option()); + } else if ((option as Instrumentation).instrumentationName) { + instrumentations.push(option); + } + } + + return instrumentations; +} From 2f715bd78dd7fb66e2aa2b76fb6e7c623deddd60 Mon Sep 17 00:00:00 2001 From: Xavier Serrano Date: Fri, 3 Mar 2023 14:16:30 +0100 Subject: [PATCH 36/37] feat: allow to add attributes from grpc metadata in the patched server (#3589) * feat(opentelemetry-instrumentation-grpc): support server calls in metadataToSpanAttributes Signed-off-by: Xavier Serrano * chore(opentelemetry-instrumentation-grpc): update changelog Signed-off-by: Xavier Serrano * chore(opentelemetry-instrumentation-grpc): update changelog * chore(opentelemetry-instrumentation-grpc): update comments and documentation Signed-off-by: Xavier Serrano --------- Signed-off-by: Xavier Serrano Co-authored-by: Daniel Dyla --- experimental/CHANGELOG.md | 1 + .../README.md | 2 +- .../src/grpc-js/index.ts | 28 +++++++++++++++++++ .../src/grpc/index.ts | 28 +++++++++++++++++++ .../src/internal-types.ts | 10 +++++++ .../src/types.ts | 4 +++ .../test/helper.ts | 11 +++++++- 7 files changed, 82 insertions(+), 2 deletions(-) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index d025c4d4b7..7d5a24082a 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -12,6 +12,7 @@ All notable changes to experimental packages in this project will be documented * feat: use HTTP_ROUTE in span name [#3603](https://github.com/open-telemetry/opentelemetry-js/pull/3603) @Flarna * feat: add HTTP_ROUTE attribute to http incoming metrics if present [#3581](https://github.com/open-telemetry/opentelemetry-js/pull/3581) @hermogenes +* feat(opentelemetry-instrumentation-grpc): allow to add attributes from grpc metadata in the patched server [#3589](https://github.com/open-telemetry/opentelemetry-js/pull/3589) @zombispormedio * feat(sdk-node): install diag logger with OTEL_LOG_LEVEL [#3627](https://github.com/open-telemetry/opentelemetry-js/pull/3627) @legendecas * feat(otlp-exporter-base): add retries [#3207](https://github.com/open-telemetry/opentelemetry-js/pull/3207) @svetlanabrennan * feat(sdk-node): override IdGenerator when using NodeSDK [#3645](https://github.com/open-telemetry/opentelemetry-js/pull/3645) @haddasbronfman diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/README.md b/experimental/packages/opentelemetry-instrumentation-grpc/README.md index 0ea8330438..2aa6038c03 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/README.md +++ b/experimental/packages/opentelemetry-instrumentation-grpc/README.md @@ -47,7 +47,7 @@ gRPC instrumentation accepts the following configuration: | Options | Type | Description | |----------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [`ignoreGrpcMethods`](https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-instrumentation-grpc/src/types.ts#L25) | `IgnoreMatcher[]` | gRPC instrumentation will not trace any methods that match anything in this list. You may pass a string (case-insensitive match), a `RegExp` object, or a filter function. | -| [`metadataToSpanAttributes`](https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-instrumentation-grpc/src/types.ts#L27) | `object` | List of case insensitive metadata to convert to span attributes. Client (outgoing requests, incoming responses) metadata attributes will be converted to span attributes in the form of `rpc.{request\response}.metadata.metadata_key`, e.g. `rpc.response.metadata.date` | +| [`metadataToSpanAttributes`](https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-instrumentation-grpc/src/types.ts#L27) | `object` | List of case insensitive metadata to convert to span attributes. Client and server (outgoing requests, incoming responses) metadata attributes will be converted to span attributes in the form of `rpc.{request\response}.metadata.metadata_key`, e.g. `rpc.response.metadata.date` | ## Useful links diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts b/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts index 2e5c833b19..6608521bd6 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts +++ b/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts @@ -212,6 +212,24 @@ export class GrpcJsInstrumentation extends InstrumentationBase { [SemanticAttributes.RPC_SERVICE]: service, }); + instrumentation._metadataCapture.server.captureRequestMetadata( + span, + call.metadata + ); + + instrumentation._wrap( + call, + 'sendMetadata', + originalSendMetadata => + (responseMetadata: grpcJs.Metadata) => { + instrumentation._metadataCapture.server.captureResponseMetadata( + span, + responseMetadata + ); + originalSendMetadata.call(call, responseMetadata); + } + ); + context.with(trace.setSpan(context.active(), span), () => { handleServerFunction.call( self, @@ -385,6 +403,16 @@ export class GrpcJsInstrumentation extends InstrumentationBase { config.metadataToSpanAttributes?.client?.responseMetadata ?? [] ), }, + server: { + captureRequestMetadata: metadataCapture( + 'request', + config.metadataToSpanAttributes?.server?.requestMetadata ?? [] + ), + captureResponseMetadata: metadataCapture( + 'response', + config.metadataToSpanAttributes?.server?.responseMetadata ?? [] + ), + }, }; } } diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts b/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts index 4df381eb66..fbeac0395e 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts +++ b/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts @@ -223,6 +223,24 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< [SemanticAttributes.RPC_SERVICE]: service, }); + instrumentation._metadataCapture.server.captureRequestMetadata( + span, + call.metadata + ); + + instrumentation._wrap( + call as any, + 'sendMetadata', + originalSendMetadata => + (responseMetadata: grpcTypes.Metadata) => { + instrumentation._metadataCapture.server.captureResponseMetadata( + span, + responseMetadata + ); + originalSendMetadata.call(call, responseMetadata); + } + ); + context.with(trace.setSpan(context.active(), span), () => { switch (type) { case 'unary': @@ -370,6 +388,16 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< config.metadataToSpanAttributes?.client?.responseMetadata ?? [] ), }, + server: { + captureRequestMetadata: metadataCapture( + 'request', + config.metadataToSpanAttributes?.server?.requestMetadata ?? [] + ), + captureResponseMetadata: metadataCapture( + 'response', + config.metadataToSpanAttributes?.server?.responseMetadata ?? [] + ), + }, }; } } diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/src/internal-types.ts b/experimental/packages/opentelemetry-instrumentation-grpc/src/internal-types.ts index d0052f7643..ebc17ae1d4 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/src/internal-types.ts +++ b/experimental/packages/opentelemetry-instrumentation-grpc/src/internal-types.ts @@ -29,4 +29,14 @@ export type metadataCaptureType = { metadata: grpcJsTypes.Metadata | grpcTypes.Metadata ) => void; }; + server: { + captureRequestMetadata: ( + span: Span, + metadata: grpcJsTypes.Metadata | grpcTypes.Metadata + ) => void; + captureResponseMetadata: ( + span: Span, + metadata: grpcJsTypes.Metadata | grpcTypes.Metadata + ) => void; + }; }; diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/src/types.ts b/experimental/packages/opentelemetry-instrumentation-grpc/src/types.ts index dd08d8224b..430476b7a4 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/src/types.ts +++ b/experimental/packages/opentelemetry-instrumentation-grpc/src/types.ts @@ -29,5 +29,9 @@ export interface GrpcInstrumentationConfig extends InstrumentationConfig { responseMetadata?: string[]; requestMetadata?: string[]; }; + server?: { + responseMetadata?: string[]; + requestMetadata?: string[]; + }; }; } diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/test/helper.ts b/experimental/packages/opentelemetry-instrumentation-grpc/test/helper.ts index 85b6a1c64e..58855a6a5f 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/test/helper.ts +++ b/experimental/packages/opentelemetry-instrumentation-grpc/test/helper.ts @@ -978,6 +978,10 @@ export const runTests = ( requestMetadata: ['client_metadata_key'], responseMetadata: ['server_metadata_key'], }, + server: { + requestMetadata: ['client_metadata_key'], + responseMetadata: ['server_metadata_key'], + }, }, }); @@ -999,13 +1003,18 @@ export const runTests = ( }); }); - describe('Capture request/response metadata in client span', () => { + describe('Capture request/response metadata in client and server spans', () => { const attributeValidation = { clientAttributes: { 'rpc.request.metadata.client_metadata_key': 'client_metadata_value', 'rpc.response.metadata.server_metadata_key': 'server_metadata_value', }, + serverAttributes: { + 'rpc.request.metadata.client_metadata_key': 'client_metadata_value', + 'rpc.response.metadata.server_metadata_key': + 'server_metadata_value', + }, }; runTestWithAttributeValidation( From ebc8575a0c215a7b7c84866116a261ffd8aff015 Mon Sep 17 00:00:00 2001 From: Marc Pichler Date: Mon, 6 Mar 2023 06:48:08 +0100 Subject: [PATCH 37/37] chore(jeager-exporter): deprecate jaeger exporter (#3585) Co-authored-by: Daniel Dyla --- CHANGELOG.md | 1 + packages/opentelemetry-exporter-jaeger/README.md | 8 +++++++- packages/opentelemetry-exporter-jaeger/src/jaeger.ts | 10 ++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f2bae09b0..e9af77be8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/ ### :house: (Internal) +* chore(exporter-jaeger): deprecate jaeger exporter [#3585](https://github.com/open-telemetry/opentelemetry-js/pull/3585) @pichlermarc * fix(sdk-metrics): fix flaky LastValueAggregator test by using fake timer [#3587](https://github.com/open-telemetry/opentelemetry-js/pull/3587) @pichlermarc * fix(test): fix failing tests by preventing source-map generation [#3642](https://github.com/open-telemetry/opentelemetry-js/pull/3642) @pichlermarc diff --git a/packages/opentelemetry-exporter-jaeger/README.md b/packages/opentelemetry-exporter-jaeger/README.md index 802debb99d..4adceb9b74 100644 --- a/packages/opentelemetry-exporter-jaeger/README.md +++ b/packages/opentelemetry-exporter-jaeger/README.md @@ -1,8 +1,14 @@ -# OpenTelemetry Jaeger Trace Exporter for Node.js +# (Deprecated) OpenTelemetry Jaeger Trace Exporter for Node.js [![NPM Published Version][npm-img]][npm-url] [![Apache License][license-image]][license-image] +**NOTE: Support for `@opentelemetry/exporter-jaeger` will end March 2024, please use any of the following packages instead:** + +- `@opentelemetry/exporter-trace-otlp-proto` +- `@opentelemetry/exporter-trace-otlp-grpc` +- `@opentelemetry/exporter-trace-otlp-http` + OpenTelemetry Jaeger Trace Exporter allows the user to send collected traces to Jaeger. [Jaeger](https://jaeger.readthedocs.io/en/latest/), inspired by [Dapper](https://research.google.com/pubs/pub36356.html) and [OpenZipkin](http://zipkin.io/), is a distributed tracing system released as open source by [Uber Technologies](http://uber.github.io/). It is used for monitoring and troubleshooting microservices-based distributed systems, including: diff --git a/packages/opentelemetry-exporter-jaeger/src/jaeger.ts b/packages/opentelemetry-exporter-jaeger/src/jaeger.ts index 2f7acabe76..28d3ccb337 100644 --- a/packages/opentelemetry-exporter-jaeger/src/jaeger.ts +++ b/packages/opentelemetry-exporter-jaeger/src/jaeger.ts @@ -29,6 +29,16 @@ import * as jaegerTypes from './types'; /** * Format and sends span information to Jaeger Exporter. + * + * @deprecated Jaeger supports the OpenTelemetry protocol natively + * (see https://www.jaegertracing.io/docs/1.41/apis/#opentelemetry-protocol-stable). + * This exporter will not be required by the OpenTelemetry specification starting July 2023, and + * will not receive any security fixes past March 2024. + * + * Please migrate to any of the following packages: + * - `@opentelemetry/exporter-trace-otlp-proto` + * - `@opentelemetry/exporter-trace-otlp-grpc` + * - `@opentelemetry/exporter-trace-otlp-http` */ export class JaegerExporter implements SpanExporter { private readonly _onShutdownFlushTimeout: number;