From 8900cfdbbab27f464df569a4c8309b96594c276b Mon Sep 17 00:00:00 2001 From: Marc Pichler Date: Mon, 23 Sep 2024 17:44:37 +0200 Subject: [PATCH 01/20] fix(sdk-trace-base): avoid keeping non-string status.message on Span#setStatus() (#4999) --- CHANGELOG.md | 2 ++ .../opentelemetry-sdk-trace-base/src/Span.ts | 14 +++++++++- .../test/common/Span.test.ts | 26 +++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec32110381..33e57f2fa9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ For semantic convention package changes, see the [semconv CHANGELOG](packages/se ### :bug: (Bug Fix) +* fix(sdk-trace-base): avoid keeping non-string `status.message` on `Span#setStatus()` [#4999](https://github.com/open-telemetry/opentelemetry-js/pull/4999) @pichlermarc + ### :books: (Refine Doc) ### :house: (Internal) diff --git a/packages/opentelemetry-sdk-trace-base/src/Span.ts b/packages/opentelemetry-sdk-trace-base/src/Span.ts index 3b7758f138..72eb541341 100644 --- a/packages/opentelemetry-sdk-trace-base/src/Span.ts +++ b/packages/opentelemetry-sdk-trace-base/src/Span.ts @@ -226,7 +226,19 @@ export class Span implements APISpan, ReadableSpan { setStatus(status: SpanStatus): this { if (this._isSpanEnded()) return this; - this.status = status; + this.status = { ...status }; + + // When using try-catch, the caught "error" is of type `any`. When then assigning `any` to `status.message`, + // TypeScript will not error. While this can happen during use of any API, it is more common on Span#setStatus() + // as it's likely used in a catch-block. Therefore, we validate if `status.message` is actually a string, null, or + // undefined to avoid an incorrect type causing issues downstream. + if (this.status.message != null && typeof status.message !== 'string') { + diag.warn( + `Dropping invalid status.message of type '${typeof status.message}', expected 'string'` + ); + delete this.status.message; + } + return this; } diff --git a/packages/opentelemetry-sdk-trace-base/test/common/Span.test.ts b/packages/opentelemetry-sdk-trace-base/test/common/Span.test.ts index 33808acd54..ef06a1d884 100644 --- a/packages/opentelemetry-sdk-trace-base/test/common/Span.test.ts +++ b/packages/opentelemetry-sdk-trace-base/test/common/Span.test.ts @@ -853,6 +853,32 @@ describe('Span', () => { message: 'This is an error', }); span.end(); + + assert.strictEqual(span.status.code, SpanStatusCode.ERROR); + assert.strictEqual(span.status.message, 'This is an error'); + }); + + it('should drop non-string status message', function () { + const warnStub = sinon.spy(diag, 'warn'); + const span = new Span( + tracer, + ROOT_CONTEXT, + name, + spanContext, + SpanKind.CLIENT + ); + span.setStatus({ + code: SpanStatusCode.ERROR, + message: new Error('this is not a string') as any, + }); + span.end(); + + assert.strictEqual(span.status.code, SpanStatusCode.ERROR); + assert.strictEqual(span.status.message, undefined); + sinon.assert.calledOnceWithExactly( + warnStub, + "Dropping invalid status.message of type 'object', expected 'string'" + ); }); it('should return ReadableSpan', () => { From 42be9519581c8faf64fdc13688abd49b50f3245b Mon Sep 17 00:00:00 2001 From: Marc Pichler Date: Mon, 23 Sep 2024 17:44:49 +0200 Subject: [PATCH 02/20] chore: remove --openssl-legacy-provider option (#5012) --- .github/workflows/unit-test.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 49448d0baa..11bf4a9c7a 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -45,12 +45,7 @@ jobs: run: npm run compile - name: Unit tests - run: | - # TODO(legendecas): webpack https://stackoverflow.com/questions/69692842/error-message-error0308010cdigital-envelope-routinesunsupported - if [ "${{ matrix.node_version }}" = "18" ] || [ "${{ matrix.node_version }}" == "20" ] || [ "${{ matrix.node_version }}" == "22" ]; then - export NODE_OPTIONS=--openssl-legacy-provider - fi - npm run test + run: npm run test - name: Report Coverage uses: codecov/codecov-action@v4 with: @@ -81,8 +76,6 @@ jobs: npm run compile - name: Unit tests - env: - NODE_OPTIONS: --openssl-legacy-provider run: npm run test browser-tests: runs-on: ubuntu-latest From 4574bf51c0f41c95217981836c32548bff02467c Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Mon, 23 Sep 2024 18:28:24 +0200 Subject: [PATCH 03/20] chore(deps): lock file maintenance (#5008) --- package-lock.json | 246 +++++++++++++++++++++------------------------- 1 file changed, 114 insertions(+), 132 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7ca6f61f9d..38d5519ac8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3864,9 +3864,9 @@ "dev": true }, "node_modules/@grpc/grpc-js": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.11.2.tgz", - "integrity": "sha512-DWp92gDD7/Qkj7r8kus6/HCINeo3yPZWZ3paKgDgsbKbSpoxKg1yvN8xe2Q8uE3zOsPe3bX8FQX2+XValq2yTw==", + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.11.3.tgz", + "integrity": "sha512-i9UraDzFHMR+Iz/MhFLljT+fCpgxZ3O6CxwGJ8YuNYHJItIHUzKJpW2LvoFZNnGPwqc9iWy9RAucxV0JoR9aUQ==", "dependencies": { "@grpc/proto-loader": "^0.7.13", "@js-sdsl/ordered-map": "^4.4.2" @@ -6862,9 +6862,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "dev": true }, "node_modules/@types/express": { @@ -8188,9 +8188,9 @@ } }, "node_modules/aria-query": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz", - "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", "dev": true, "engines": { "node": ">= 0.4" @@ -10336,9 +10336,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001660", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz", - "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==", + "version": "1.0.30001663", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001663.tgz", + "integrity": "sha512-o9C3X27GLKbLeTYZ6HBOLU1tsAcBZsLis28wrVzddShCS16RujjHp9GDHKZqrB3meE0YjhawvMFsGb/igqiPzA==", "dev": true, "funding": [ { @@ -10732,7 +10732,6 @@ "integrity": "sha512-Y8Hw+V3HgR7V71xWH2vQ9lyS358CbGCldWlJFR0JirqoGtOoas3R3/OclRTvgUYFK29mmJICDPauVKmpqbwhOA==", "deprecated": "https://about.codecov.io/blog/codecov-uploader-deprecation-plan/", "dev": true, - "license": "MIT", "dependencies": { "argv": "0.0.2", "ignore-walk": "3.0.4", @@ -10752,17 +10751,34 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, - "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" } }, + "node_modules/codecov/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/codecov/node_modules/ignore-walk": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz", + "integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==", + "dev": true, + "dependencies": { + "minimatch": "^3.0.4" + } + }, "node_modules/codecov/node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -10771,12 +10787,23 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/codecov/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/codecov/node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" + "dev": true }, "node_modules/color-convert": { "version": "2.0.1", @@ -12538,9 +12565,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.24", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.24.tgz", - "integrity": "sha512-0x0wLCmpdKFCi9ulhvYZebgcPmHTkFVUfU2wzDykadkslKwT4oAmDTHEKLnlrDsMGZe4B+ksn8quZfZjYsBetA==", + "version": "1.5.27", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.27.tgz", + "integrity": "sha512-o37j1vZqCoEgBuWWXLHQgTN/KDKe7zwpiY5CPeq2RvUqOyJw9xnrULzZAEVQ5p4h+zjMk7hgtOoPdnLxr7m/jw==", "dev": true }, "node_modules/email-addresses": { @@ -12613,9 +12640,9 @@ } }, "node_modules/engine.io": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", - "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.1.tgz", + "integrity": "sha512-NEpDCw9hrvBW+hVEOK4T7v0jFJ++KgtPl4jKFwsZVfG1XhS0dCrSb3VMb9gPAd7VAdW52VT1EnaNiU2vM8C0og==", "dev": true, "dependencies": { "@types/cookie": "^0.4.1", @@ -13776,7 +13803,6 @@ "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==", "dev": true, - "license": "MIT", "dependencies": { "punycode": "^1.3.2" } @@ -15547,37 +15573,15 @@ } }, "node_modules/ignore-walk": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz", - "integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "minimatch": "^3.0.4" - } - }, - "node_modules/ignore-walk/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/ignore-walk/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz", + "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "minimatch": "^5.0.1" }, "engines": { - "node": "*" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/immediate": { @@ -20415,18 +20419,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/npm-packlist/node_modules/ignore-walk": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz", - "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==", - "dev": true, - "dependencies": { - "minimatch": "^5.0.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/npm-packlist/node_modules/npm-bundled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", @@ -24786,16 +24778,16 @@ } }, "node_modules/socket.io": { - "version": "4.7.5", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", - "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.0.tgz", + "integrity": "sha512-8U6BEgGjQOfGz3HHTYaC/L1GaxDCJ/KM0XTkJly0EhZ5U/du9uNEZy4ZgYzEzIqlx2CMm25CrCqr1ck899eLNA==", "dev": true, "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", "debug": "~4.3.2", - "engine.io": "~6.5.2", + "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" }, @@ -25262,7 +25254,6 @@ "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", "dev": true, - "license": "MIT", "dependencies": { "stubs": "^3.0.0" } @@ -25520,8 +25511,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/subarg": { "version": "1.0.0", @@ -25761,7 +25751,6 @@ "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.1.1.tgz", "integrity": "sha512-iwY6rkW5DDGq8hE2YgNQlKbptYpY5Nn2xecjQiNjOXWbKzPGUfmeUBCSQbbr306d7Z7U2N0TPl+/SwYRfua1Dg==", "dev": true, - "license": "Apache-2.0", "dependencies": { "http-proxy-agent": "^4.0.0", "https-proxy-agent": "^5.0.0", @@ -25778,7 +25767,6 @@ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 6" } @@ -25788,7 +25776,6 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, - "license": "MIT", "dependencies": { "debug": "4" }, @@ -25801,7 +25788,6 @@ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", "dev": true, - "license": "MIT", "dependencies": { "@tootallnate/once": "1", "agent-base": "6", @@ -25816,7 +25802,6 @@ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, - "license": "MIT", "dependencies": { "agent-base": "6", "debug": "4" @@ -27296,7 +27281,6 @@ "resolved": "https://registry.npmjs.org/urlgrey/-/urlgrey-1.0.0.tgz", "integrity": "sha512-hJfIzMPJmI9IlLkby8QrsCykQ+SXDeO2W5Q9QTW3QpqZVTx4a/K7p8/5q+/isD8vsbVaFgql/gvAoQCRQ2Cb5w==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "fast-url-parser": "^1.1.3" } @@ -30978,9 +30962,9 @@ "dev": true }, "@grpc/grpc-js": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.11.2.tgz", - "integrity": "sha512-DWp92gDD7/Qkj7r8kus6/HCINeo3yPZWZ3paKgDgsbKbSpoxKg1yvN8xe2Q8uE3zOsPe3bX8FQX2+XValq2yTw==", + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.11.3.tgz", + "integrity": "sha512-i9UraDzFHMR+Iz/MhFLljT+fCpgxZ3O6CxwGJ8YuNYHJItIHUzKJpW2LvoFZNnGPwqc9iWy9RAucxV0JoR9aUQ==", "requires": { "@grpc/proto-loader": "^0.7.13", "@js-sdsl/ordered-map": "^4.4.2" @@ -34774,9 +34758,9 @@ } }, "@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "dev": true }, "@types/express": { @@ -35861,9 +35845,9 @@ "dev": true }, "aria-query": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz", - "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", "dev": true }, "array-differ": { @@ -37771,9 +37755,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001660", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz", - "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==", + "version": "1.0.30001663", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001663.tgz", + "integrity": "sha512-o9C3X27GLKbLeTYZ6HBOLU1tsAcBZsLis28wrVzddShCS16RujjHp9GDHKZqrB3meE0YjhawvMFsGb/igqiPzA==", "dev": true }, "caseless": { @@ -38075,6 +38059,25 @@ "sprintf-js": "~1.0.2" } }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "ignore-walk": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz", + "integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, "js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -38085,6 +38088,15 @@ "esprima": "^4.0.0" } }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -39481,9 +39493,9 @@ } }, "electron-to-chromium": { - "version": "1.5.24", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.24.tgz", - "integrity": "sha512-0x0wLCmpdKFCi9ulhvYZebgcPmHTkFVUfU2wzDykadkslKwT4oAmDTHEKLnlrDsMGZe4B+ksn8quZfZjYsBetA==", + "version": "1.5.27", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.27.tgz", + "integrity": "sha512-o37j1vZqCoEgBuWWXLHQgTN/KDKe7zwpiY5CPeq2RvUqOyJw9xnrULzZAEVQ5p4h+zjMk7hgtOoPdnLxr7m/jw==", "dev": true }, "email-addresses": { @@ -39549,9 +39561,9 @@ } }, "engine.io": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", - "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.1.tgz", + "integrity": "sha512-NEpDCw9hrvBW+hVEOK4T7v0jFJ++KgtPl4jKFwsZVfG1XhS0dCrSb3VMb9gPAd7VAdW52VT1EnaNiU2vM8C0og==", "dev": true, "requires": { "@types/cookie": "^0.4.1", @@ -41780,33 +41792,12 @@ "dev": true }, "ignore-walk": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz", - "integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz", + "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==", "dev": true, "requires": { - "minimatch": "^3.0.4" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } + "minimatch": "^5.0.1" } }, "immediate": { @@ -45573,15 +45564,6 @@ "once": "^1.3.0" } }, - "ignore-walk": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz", - "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==", - "dev": true, - "requires": { - "minimatch": "^5.0.1" - } - }, "npm-bundled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", @@ -48964,16 +48946,16 @@ "dev": true }, "socket.io": { - "version": "4.7.5", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", - "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.0.tgz", + "integrity": "sha512-8U6BEgGjQOfGz3HHTYaC/L1GaxDCJ/KM0XTkJly0EhZ5U/du9uNEZy4ZgYzEzIqlx2CMm25CrCqr1ck899eLNA==", "dev": true, "requires": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", "debug": "~4.3.2", - "engine.io": "~6.5.2", + "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" } From 5627d8451ee8240e0c206d57f0c7ad639f2742a3 Mon Sep 17 00:00:00 2001 From: Jonathan Lee <107072447+jj22ee@users.noreply.github.com> Date: Tue, 24 Sep 2024 01:26:39 -0700 Subject: [PATCH 04/20] fix(sdk-metrics): Add missing catch and handle error in promise of `PeriodicExportingMetricReader` (#5006) --- CHANGELOG.md | 1 + .../sdk-metrics/src/export/PeriodicExportingMetricReader.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33e57f2fa9..9f89cae9e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ For semantic convention package changes, see the [semconv CHANGELOG](packages/se ### :bug: (Bug Fix) * fix(sdk-trace-base): avoid keeping non-string `status.message` on `Span#setStatus()` [#4999](https://github.com/open-telemetry/opentelemetry-js/pull/4999) @pichlermarc +* fix(sdk-metrics): Add missing catch and handle error in promise of `PeriodicExportingMetricReader` [#5006](https://github.com/open-telemetry/opentelemetry-js/pull/5006) @jj22ee ### :books: (Refine Doc) diff --git a/packages/sdk-metrics/src/export/PeriodicExportingMetricReader.ts b/packages/sdk-metrics/src/export/PeriodicExportingMetricReader.ts index 5ecafff682..b6730033e0 100644 --- a/packages/sdk-metrics/src/export/PeriodicExportingMetricReader.ts +++ b/packages/sdk-metrics/src/export/PeriodicExportingMetricReader.ts @@ -142,7 +142,8 @@ export class PeriodicExportingMetricReader extends MetricReader { .waitForAsyncAttributes?.() .then(doExport, err => diag.debug('Error while resolving async portion of resource: ', err) - ); + ) + .catch(globalErrorHandler); } else { await doExport(); } From 1ce52050bd525266cb8239a691600052d6119173 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Tue, 24 Sep 2024 10:27:26 +0200 Subject: [PATCH 05/20] fix(deps): update dependency express to v4.20.0 [security] (#4989) --- .../package.json | 2 +- package-lock.json | 218 +++++++++++------- 2 files changed, 135 insertions(+), 85 deletions(-) diff --git a/integration-tests/propagation-validation-server/package.json b/integration-tests/propagation-validation-server/package.json index 0fdc51d4e3..8334545392 100644 --- a/integration-tests/propagation-validation-server/package.json +++ b/integration-tests/propagation-validation-server/package.json @@ -17,7 +17,7 @@ "@opentelemetry/sdk-trace-base": "1.26.0", "axios": "1.7.4", "body-parser": "1.20.3", - "express": "4.19.2" + "express": "4.20.0" }, "devDependencies": { "typescript": "4.4.4" diff --git a/package-lock.json b/package-lock.json index 38d5519ac8..f1f92f2909 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1698,7 +1698,7 @@ "@opentelemetry/sdk-trace-base": "1.26.0", "axios": "1.7.4", "body-parser": "1.20.3", - "express": "4.19.2" + "express": "4.20.0" }, "devDependencies": { "typescript": "4.4.4" @@ -13590,36 +13590,36 @@ "dev": true }, "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.20.0.tgz", + "integrity": "sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.2.0", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.0", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -13630,29 +13630,6 @@ "node": ">= 0.10.0" } }, - "node_modules/express/node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -13661,11 +13638,30 @@ "ms": "2.0.0" } }, - "node_modules/express/node_modules/ms": { + "node_modules/express/node_modules/debug/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/express/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/express/node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -13680,6 +13676,37 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/express/node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express/node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -18948,9 +18975,12 @@ "dev": true }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/merge-stream": { "version": "2.0.0", @@ -22145,9 +22175,9 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" }, "node_modules/path-type": { "version": "4.0.0", @@ -24368,9 +24398,9 @@ } }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.0.tgz", + "integrity": "sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==", "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", @@ -40271,36 +40301,36 @@ "dev": true }, "express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.20.0.tgz", + "integrity": "sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.2.0", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.0", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -40308,37 +40338,30 @@ "vary": "~1.1.2" }, "dependencies": { - "body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - } - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } } }, - "ms": { + "encodeurl": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "qs": { "version": "6.11.0", @@ -40347,6 +40370,33 @@ "requires": { "side-channel": "^1.0.4" } + }, + "send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + } + } } } }, @@ -44423,9 +44473,9 @@ } }, "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==" }, "merge-stream": { "version": "2.0.0", @@ -46900,9 +46950,9 @@ } }, "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" }, "path-type": { "version": "4.0.0", @@ -47222,7 +47272,7 @@ "@opentelemetry/sdk-trace-base": "1.26.0", "axios": "1.7.4", "body-parser": "1.20.3", - "express": "4.19.2", + "express": "4.20.0", "typescript": "4.4.4" }, "dependencies": { @@ -48625,9 +48675,9 @@ } }, "serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.0.tgz", + "integrity": "sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==", "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", From ee40d5d9b79a1131c49531ad2771784d4b8c78b5 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Tue, 24 Sep 2024 10:28:26 +0200 Subject: [PATCH 06/20] chore(deps): update dependency chromedriver to v129 (#5007) --- package-lock.json | 16 ++++++++-------- selenium-tests/package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index f1f92f2909..dc54529e58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10474,9 +10474,9 @@ } }, "node_modules/chromedriver": { - "version": "128.0.1", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-128.0.1.tgz", - "integrity": "sha512-UmWqZXXAyuRa37pE+lnU46vJcCM/y0ddF015LHxycEOYfuqsK7k9ZxJuXCQNWbws9e7FAMQj/GJZT92WPgis0g==", + "version": "129.0.0", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-129.0.0.tgz", + "integrity": "sha512-B1ccqD6hDjNrw94FeqdynIotn1ZV/TnFrkRz2Rync2kzSnq6D6IrSkN1w5Pnuvnc98QhN2xujxDXxkqEqy/PWg==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -29339,7 +29339,7 @@ "babel-loader": "8.4.1", "babel-polyfill": "6.26.0", "browserstack-local": "1.4.8", - "chromedriver": "128.0.1", + "chromedriver": "129.0.0", "dotenv": "16.0.0", "fast-safe-stringify": "2.1.1", "geckodriver": "3.0.1", @@ -33980,7 +33980,7 @@ "babel-loader": "8.4.1", "babel-polyfill": "6.26.0", "browserstack-local": "1.4.8", - "chromedriver": "128.0.1", + "chromedriver": "129.0.0", "dotenv": "16.0.0", "fast-safe-stringify": "2.1.1", "geckodriver": "3.0.1", @@ -37876,9 +37876,9 @@ "dev": true }, "chromedriver": { - "version": "128.0.1", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-128.0.1.tgz", - "integrity": "sha512-UmWqZXXAyuRa37pE+lnU46vJcCM/y0ddF015LHxycEOYfuqsK7k9ZxJuXCQNWbws9e7FAMQj/GJZT92WPgis0g==", + "version": "129.0.0", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-129.0.0.tgz", + "integrity": "sha512-B1ccqD6hDjNrw94FeqdynIotn1ZV/TnFrkRz2Rync2kzSnq6D6IrSkN1w5Pnuvnc98QhN2xujxDXxkqEqy/PWg==", "dev": true, "requires": { "@testim/chrome-version": "^1.1.4", diff --git a/selenium-tests/package.json b/selenium-tests/package.json index 091bc32009..5223c29cc9 100644 --- a/selenium-tests/package.json +++ b/selenium-tests/package.json @@ -41,7 +41,7 @@ "babel-loader": "8.4.1", "babel-polyfill": "6.26.0", "browserstack-local": "1.4.8", - "chromedriver": "128.0.1", + "chromedriver": "129.0.0", "dotenv": "16.0.0", "fast-safe-stringify": "2.1.1", "geckodriver": "3.0.1", From 395010f96b2d6eaa634cc353c3a729ce267a1be5 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Tue, 24 Sep 2024 04:29:08 -0400 Subject: [PATCH 07/20] Http server semconv span stable (#4978) Co-authored-by: Marc Pichler Co-authored-by: Mend Renovate Co-authored-by: Marc Pichler --- experimental/CHANGELOG.md | 5 +- .../README.md | 4 +- .../package.json | 1 + .../src/http.ts | 4 +- .../src/utils.ts | 290 ++++++++++++++++-- .../test/functionals/http-enable.test.ts | 92 +++++- .../test/functionals/utils.test.ts | 17 +- .../test/utils/httpRequest.ts | 8 + package-lock.json | 13 + 9 files changed, 396 insertions(+), 38 deletions(-) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 4ac335a9cc..13454c6eb4 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -10,7 +10,10 @@ All notable changes to experimental packages in this project will be documented ### :rocket: (Enhancement) * feat(api-logs): Add delegating no-op logger provider [#4861](https://github.com/open-telemetry/opentelemetry-js/pull/4861) @hectorhdzg -* feat(instrumentation-http): Add support for client span semantic conventions 1.27 [#4940](https://github.com/open-telemetry/opentelemetry-js/pull/4940) @dyladan +* feat(instrumentation-http): Add support for [Semantic Conventions 1.27+](https://github.com/open-telemetry/semantic-conventions/releases/tag/v1.27.0) [#4940](https://github.com/open-telemetry/opentelemetry-js/pull/4940) [#4978](https://github.com/open-telemetry/opentelemetry-js/pull/4978) @dyladan + * Applies to both client and server spans + * Generate spans compliant with Semantic Conventions 1.27+ when `OTEL_SEMCONV_STABILITY_OPT_IN` contains `http` or `http/dup` + * Generate spans backwards compatible with previous attributes when `OTEL_SEMCONV_STABILITY_OPT_IN` contains `http/dup` or DOES NOT contain `http` ### :bug: (Bug Fix) diff --git a/experimental/packages/opentelemetry-instrumentation-http/README.md b/experimental/packages/opentelemetry-instrumentation-http/README.md index cdd2f2337f..456d5b3473 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/README.md +++ b/experimental/packages/opentelemetry-instrumentation-http/README.md @@ -76,7 +76,7 @@ The following options are deprecated: ## Semantic Conventions -### Client Spans +### Client and Server Spans Prior to version `0.54`, this instrumentation created spans targeting an experimental semantic convention [Version 1.7.0](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/semantic_conventions/README.md). @@ -91,7 +91,7 @@ If neither `http` or `http/dup` is included in `OTEL_SEMCONV_STABILITY_OPT_IN`, Enabled when `OTEL_SEMCONV_STABILITY_OPT_IN` contains `http` OR `http/dup`. This is the recommended configuration, and will soon become the default behavior. -Follow all requirements and recommendations of HTTP Client Span Semantic Conventions [Version 1.27.0](https://github.com/open-telemetry/semantic-conventions/blob/v1.27.0/docs/http/http-spans.md), including all required and recommended attributes. +Follow all requirements and recommendations of HTTP Client and Server Span Semantic Conventions [Version 1.27.0](https://github.com/open-telemetry/semantic-conventions/blob/v1.27.0/docs/http/http-spans.md), including all required and recommended attributes. #### Legacy Behavior (default) diff --git a/experimental/packages/opentelemetry-instrumentation-http/package.json b/experimental/packages/opentelemetry-instrumentation-http/package.json index 6951a7ed53..aadf9a5663 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/package.json +++ b/experimental/packages/opentelemetry-instrumentation-http/package.json @@ -78,6 +78,7 @@ "@opentelemetry/core": "1.26.0", "@opentelemetry/instrumentation": "0.53.0", "@opentelemetry/semantic-conventions": "1.27.0", + "forwarded-parse": "2.1.2", "semver": "^7.5.2" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation-http", diff --git a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts index 06bc3cac50..81e56183a1 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts @@ -528,6 +528,7 @@ export class HttpInstrumentation extends InstrumentationBase { const hostname = options.hostname; const port = options.port; - const requestMethod = requestOptions.method; - const method = requestMethod ? requestMethod.toUpperCase() : 'GET'; + const method = requestOptions.method ?? 'GET'; + const normalizedMethod = normalizeMethod(method); const headers = requestOptions.headers || {}; const userAgent = headers['user-agent']; const urlFull = getAbsoluteUrl( @@ -409,7 +414,7 @@ export const getOutgoingRequestAttributes = ( const newAttributes: Attributes = { // Required attributes - [ATTR_HTTP_REQUEST_METHOD]: method, + [ATTR_HTTP_REQUEST_METHOD]: normalizedMethod, [ATTR_SERVER_ADDRESS]: hostname, [ATTR_SERVER_PORT]: Number(port), [ATTR_URL_FULL]: urlFull, @@ -421,8 +426,8 @@ export const getOutgoingRequestAttributes = ( }; // conditionally required if request method required case normalization - if (requestMethod && method !== requestMethod) { - newAttributes[ATTR_HTTP_REQUEST_METHOD_ORIGINAL] = requestMethod; + if (method !== normalizedMethod) { + newAttributes[ATTR_HTTP_REQUEST_METHOD_ORIGINAL] = method; } if (userAgent !== undefined) { @@ -536,23 +541,174 @@ export const getOutgoingRequestMetricAttributesOnResponse = ( return metricAttributes; }; +function parseHostHeader( + hostHeader: string, + proto?: string +): { host: string; port?: string } { + const parts = hostHeader.split(':'); + + // no semicolon implies ipv4 dotted syntax or host name without port + // x.x.x.x + // example.com + if (parts.length === 1) { + if (proto === 'http') { + return { host: parts[0], port: '80' }; + } + + if (proto === 'https') { + return { host: parts[0], port: '443' }; + } + + return { host: parts[0] }; + } + + // single semicolon implies ipv4 dotted syntax or host name with port + // x.x.x.x:yyyy + // example.com:yyyy + if (parts.length === 2) { + return { + host: parts[0], + port: parts[1], + }; + } + + // more than 2 parts implies ipv6 syntax with multiple colons + // [x:x:x:x:x:x:x:x] + // [x:x:x:x:x:x:x:x]:yyyy + if (parts[0].startsWith('[')) { + if (parts[parts.length - 1].endsWith(']')) { + if (proto === 'http') { + return { host: hostHeader, port: '80' }; + } + + if (proto === 'https') { + return { host: hostHeader, port: '443' }; + } + } else if (parts[parts.length - 2].endsWith(']')) { + return { + host: parts.slice(0, -1).join(':'), + port: parts[parts.length - 1], + }; + } + } + + // if nothing above matches just return the host header + return { host: hostHeader }; +} + +/** + * Get server.address and port according to http semconv 1.27 + * https://github.com/open-telemetry/semantic-conventions/blob/bf0a2c1134f206f034408b201dbec37960ed60ec/docs/http/http-spans.md#setting-serveraddress-and-serverport-attributes + */ +function getServerAddress( + request: IncomingMessage, + component: 'http' | 'https' +): { host: string; port?: string } | null { + const forwardedHeader = request.headers['forwarded']; + if (forwardedHeader) { + for (const entry of forwardedParse(forwardedHeader)) { + if (entry.host) { + return parseHostHeader(entry.host, entry.proto); + } + } + } + + const xForwardedHost = request.headers['x-forwarded-host']; + if (typeof xForwardedHost === 'string') { + if (typeof request.headers['x-forwarded-proto'] === 'string') { + return parseHostHeader( + xForwardedHost, + request.headers['x-forwarded-proto'] + ); + } + + if (Array.isArray(request.headers['x-forwarded-proto'])) { + return parseHostHeader( + xForwardedHost, + request.headers['x-forwarded-proto'][0] + ); + } + + return parseHostHeader(xForwardedHost); + } else if ( + Array.isArray(xForwardedHost) && + typeof xForwardedHost[0] === 'string' && + xForwardedHost[0].length > 0 + ) { + if (typeof request.headers['x-forwarded-proto'] === 'string') { + return parseHostHeader( + xForwardedHost[0], + request.headers['x-forwarded-proto'] + ); + } + + if (Array.isArray(request.headers['x-forwarded-proto'])) { + return parseHostHeader( + xForwardedHost[0], + request.headers['x-forwarded-proto'][0] + ); + } + + return parseHostHeader(xForwardedHost[0]); + } + + const host = request.headers['host']; + if (typeof host === 'string' && host.length > 0) { + return parseHostHeader(host, component); + } + + return null; +} + +/** + * Get server.address and port according to http semconv 1.27 + * https://github.com/open-telemetry/semantic-conventions/blob/bf0a2c1134f206f034408b201dbec37960ed60ec/docs/http/http-spans.md#setting-serveraddress-and-serverport-attributes + */ +export function getRemoteClientAddress( + request: IncomingMessage +): string | null { + const forwardedHeader = request.headers['forwarded']; + if (forwardedHeader) { + for (const entry of forwardedParse(forwardedHeader)) { + if (entry.for) { + return entry.for; + } + } + } + + const xForwardedFor = request.headers['x-forwarded-for']; + if (typeof xForwardedFor === 'string') { + return xForwardedFor; + } else if (Array.isArray(xForwardedFor)) { + return xForwardedFor[0]; + } + + const remote = request.socket.remoteAddress; + if (remote) { + return remote; + } + + return null; +} + /** * Returns incoming request attributes scoped to the request data * @param {IncomingMessage} request the request object * @param {{ component: string, serverName?: string, hookAttributes?: SpanAttributes }} options used to pass data needed to create attributes + * @param {SemconvStability} semconvStability determines which semconv version to use */ export const getIncomingRequestAttributes = ( request: IncomingMessage, options: { - component: string; + component: 'http' | 'https'; serverName?: string; hookAttributes?: SpanAttributes; + semconvStability: SemconvStability; } ): SpanAttributes => { const headers = request.headers; const userAgent = headers['user-agent']; const ips = headers['x-forwarded-for']; - const method = request.method || 'GET'; const httpVersion = request.httpVersion; const requestUrl = request.url ? url.parse(request.url) : null; const host = requestUrl?.host || headers.host; @@ -560,8 +716,43 @@ export const getIncomingRequestAttributes = ( requestUrl?.hostname || host?.replace(/^(.*)(:[0-9]{1,5})/, '$1') || 'localhost'; + + const method = request.method; + const normalizedMethod = normalizeMethod(method); + + const serverAddress = getServerAddress(request, options.component); const serverName = options.serverName; - const attributes: SpanAttributes = { + + const remoteClientAddress = getRemoteClientAddress(request); + + const newAttributes: Attributes = { + [ATTR_HTTP_REQUEST_METHOD]: normalizedMethod, + [ATTR_URL_SCHEME]: options.component, + [ATTR_SERVER_ADDRESS]: serverAddress?.host, + [ATTR_NETWORK_PEER_ADDRESS]: request.socket.remoteAddress, + [ATTR_NETWORK_PEER_PORT]: request.socket.remotePort, + [ATTR_NETWORK_PROTOCOL_VERSION]: request.httpVersion, + [ATTR_USER_AGENT_ORIGINAL]: userAgent, + }; + + if (requestUrl?.pathname != null) { + newAttributes[ATTR_URL_PATH] = requestUrl.pathname; + } + + if (remoteClientAddress != null) { + newAttributes[ATTR_CLIENT_ADDRESS] = remoteClientAddress; + } + + if (serverAddress?.port != null) { + newAttributes[ATTR_SERVER_PORT] = Number(serverAddress.port); + } + + // conditionally required if request method required case normalization + if (method !== normalizedMethod) { + newAttributes[ATTR_HTTP_REQUEST_METHOD_ORIGINAL] = method; + } + + const oldAttributes: Attributes = { [SEMATTRS_HTTP_URL]: getAbsoluteUrl( requestUrl, headers, @@ -574,23 +765,31 @@ export const getIncomingRequestAttributes = ( }; if (typeof ips === 'string') { - attributes[SEMATTRS_HTTP_CLIENT_IP] = ips.split(',')[0]; + oldAttributes[SEMATTRS_HTTP_CLIENT_IP] = ips.split(',')[0]; } if (typeof serverName === 'string') { - attributes[SEMATTRS_HTTP_SERVER_NAME] = serverName; + oldAttributes[SEMATTRS_HTTP_SERVER_NAME] = serverName; } if (requestUrl) { - attributes[SEMATTRS_HTTP_TARGET] = requestUrl.path || '/'; + oldAttributes[SEMATTRS_HTTP_TARGET] = requestUrl.path || '/'; } if (userAgent !== undefined) { - attributes[SEMATTRS_HTTP_USER_AGENT] = userAgent; + oldAttributes[SEMATTRS_HTTP_USER_AGENT] = userAgent; } - setRequestContentLengthAttribute(request, attributes); - setAttributesFromHttpKind(httpVersion, attributes); - return Object.assign(attributes, options.hookAttributes); + setRequestContentLengthAttribute(request, oldAttributes); + setAttributesFromHttpKind(httpVersion, oldAttributes); + + switch (options.semconvStability) { + case SemconvStability.STABLE: + return Object.assign(newAttributes, options.hookAttributes); + case SemconvStability.OLD: + return Object.assign(oldAttributes, options.hookAttributes); + } + + return Object.assign(oldAttributes, newAttributes, options.hookAttributes); }; /** @@ -617,31 +816,44 @@ export const getIncomingRequestMetricAttributes = ( */ export const getIncomingRequestAttributesOnResponse = ( request: IncomingMessage, - response: ServerResponse + response: ServerResponse, + semconvStability: SemconvStability ): SpanAttributes => { // take socket from the request, // since it may be detached from the response object in keep-alive mode const { socket } = request; const { statusCode, statusMessage } = response; + const newAttributes = { + [ATTR_HTTP_RESPONSE_STATUS_CODE]: statusCode, + }; + const rpcMetadata = getRPCMetadata(context.active()); - const attributes: SpanAttributes = {}; + const oldAttributes: SpanAttributes = {}; if (socket) { const { localAddress, localPort, remoteAddress, remotePort } = socket; - attributes[SEMATTRS_NET_HOST_IP] = localAddress; - attributes[SEMATTRS_NET_HOST_PORT] = localPort; - attributes[SEMATTRS_NET_PEER_IP] = remoteAddress; - attributes[SEMATTRS_NET_PEER_PORT] = remotePort; + oldAttributes[SEMATTRS_NET_HOST_IP] = localAddress; + oldAttributes[SEMATTRS_NET_HOST_PORT] = localPort; + oldAttributes[SEMATTRS_NET_PEER_IP] = remoteAddress; + oldAttributes[SEMATTRS_NET_PEER_PORT] = remotePort; } - attributes[SEMATTRS_HTTP_STATUS_CODE] = statusCode; - attributes[AttributeNames.HTTP_STATUS_TEXT] = ( + oldAttributes[SEMATTRS_HTTP_STATUS_CODE] = statusCode; + oldAttributes[AttributeNames.HTTP_STATUS_TEXT] = ( statusMessage || '' ).toUpperCase(); if (rpcMetadata?.type === RPCType.HTTP && rpcMetadata.route !== undefined) { - attributes[SEMATTRS_HTTP_ROUTE] = rpcMetadata.route; + oldAttributes[SEMATTRS_HTTP_ROUTE] = rpcMetadata.route; + } + + switch (semconvStability) { + case SemconvStability.STABLE: + return newAttributes; + case SemconvStability.OLD: + return oldAttributes; } - return attributes; + + return Object.assign(oldAttributes, newAttributes); }; /** @@ -693,3 +905,31 @@ export function headerCapture(type: 'request' | 'response', headers: string[]) { } }; } + +const KNOWN_METHODS = new Set([ + // methods from https://www.rfc-editor.org/rfc/rfc9110.html#name-methods + 'GET', + 'HEAD', + 'POST', + 'PUT', + 'DELETE', + 'CONNECT', + 'OPTIONS', + 'TRACE', + + // PATCH from https://www.rfc-editor.org/rfc/rfc5789.html + 'PATCH', +]); + +function normalizeMethod(method?: string | null) { + if (method == null) { + return 'GET'; + } + + const upper = method.toUpperCase(); + if (KNOWN_METHODS.has(upper)) { + return upper; + } + + return '_OTHER'; +} 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 047089c9d8..e7270ebdc9 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 @@ -30,6 +30,7 @@ import { SimpleSpanProcessor, } from '@opentelemetry/sdk-trace-base'; import { + ATTR_CLIENT_ADDRESS, ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_NETWORK_PEER_ADDRESS, @@ -38,6 +39,8 @@ import { ATTR_SERVER_ADDRESS, ATTR_SERVER_PORT, ATTR_URL_FULL, + ATTR_URL_PATH, + ATTR_URL_SCHEME, HTTP_REQUEST_METHOD_VALUE_GET, NETTRANSPORTVALUES_IP_TCP, SEMATTRS_HTTP_CLIENT_IP, @@ -46,9 +49,12 @@ import { SEMATTRS_HTTP_METHOD, SEMATTRS_HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, SEMATTRS_HTTP_ROUTE, + SEMATTRS_HTTP_SCHEME, SEMATTRS_HTTP_STATUS_CODE, SEMATTRS_HTTP_TARGET, SEMATTRS_HTTP_URL, + SEMATTRS_NET_HOST_IP, + SEMATTRS_NET_HOST_NAME, SEMATTRS_NET_HOST_PORT, SEMATTRS_NET_PEER_IP, SEMATTRS_NET_PEER_NAME, @@ -80,6 +86,7 @@ instrumentation.disable(); import * as http from 'http'; import { AttributeNames } from '../../src/enums/AttributeNames'; +import { getRemoteClientAddress } from '../../src/utils'; const applyCustomAttributesOnSpanErrorMessage = 'bad applyCustomAttributesOnSpan function'; @@ -1068,7 +1075,10 @@ describe('HttpInstrumentation', () => { assert.strictEqual(rpcData.route, undefined); rpcData.route = 'TheRoute'; } - response.end('Test Server Response'); + response.setHeader('Content-Type', 'application/json'); + response.end( + JSON.stringify({ address: getRemoteClientAddress(request) }) + ); }); await new Promise(resolve => server.listen(serverPort, resolve)); @@ -1079,7 +1089,7 @@ describe('HttpInstrumentation', () => { instrumentation.disable(); }); - it('should generate semconv 1.27 spans', async () => { + it('should generate semconv 1.27 client spans', async () => { const response = await httpRequest.get( `${protocol}://${hostname}:${serverPort}${pathname}` ); @@ -1099,6 +1109,31 @@ describe('HttpInstrumentation', () => { [ATTR_NETWORK_PROTOCOL_VERSION]: '1.1', }); }); + + it('should generate semconv 1.27 server spans', async () => { + const response = await httpRequest.get( + `${protocol}://${hostname}:${serverPort}${pathname}` + ); + const spans = memoryExporter.getFinishedSpans(); + const [incomingSpan, _] = spans; + assert.strictEqual(spans.length, 2); + + const body = JSON.parse(response.data); + + // should have only required and recommended attributes for semconv 1.27 + assert.deepStrictEqual(incomingSpan.attributes, { + [ATTR_CLIENT_ADDRESS]: body.address, + [ATTR_HTTP_REQUEST_METHOD]: HTTP_REQUEST_METHOD_VALUE_GET, + [ATTR_SERVER_ADDRESS]: hostname, + [ATTR_SERVER_PORT]: serverPort, + [ATTR_HTTP_RESPONSE_STATUS_CODE]: 200, + [ATTR_NETWORK_PEER_ADDRESS]: body.address, + [ATTR_NETWORK_PEER_PORT]: response.clientRemotePort, + [ATTR_NETWORK_PROTOCOL_VERSION]: '1.1', + [ATTR_URL_PATH]: pathname, + [ATTR_URL_SCHEME]: protocol, + }); + }); }); describe('with semconv stability set to http/dup', () => { @@ -1110,8 +1145,11 @@ describe('HttpInstrumentation', () => { before(async () => { instrumentation['_semconvStability'] = SemconvStability.DUPLICATE; instrumentation.enable(); - server = http.createServer((_, response) => { - response.end('Test Server Response'); + server = http.createServer((request, response) => { + response.setHeader('Content-Type', 'application/json'); + response.end( + JSON.stringify({ address: getRemoteClientAddress(request) }) + ); }); await new Promise(resolve => server.listen(serverPort, resolve)); @@ -1146,7 +1184,8 @@ describe('HttpInstrumentation', () => { [SEMATTRS_HTTP_FLAVOR]: '1.1', [SEMATTRS_HTTP_HOST]: `${hostname}:${serverPort}`, [SEMATTRS_HTTP_METHOD]: 'GET', - [SEMATTRS_HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED]: 20, + [SEMATTRS_HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED]: + response.data.length, [SEMATTRS_HTTP_STATUS_CODE]: 200, [SEMATTRS_HTTP_TARGET]: '/test', [SEMATTRS_HTTP_URL]: `http://${hostname}:${serverPort}${pathname}`, @@ -1159,6 +1198,49 @@ describe('HttpInstrumentation', () => { [AttributeNames.HTTP_STATUS_TEXT]: 'OK', }); }); + + it('should create server spans with semconv 1.27 and old 1.7', async () => { + const response = await httpRequest.get( + `${protocol}://${hostname}:${serverPort}${pathname}` + ); + const spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 2); + const incomingSpan = spans[0]; + const body = JSON.parse(response.data); + + // should have only required and recommended attributes for semconv 1.27 + assert.deepStrictEqual(incomingSpan.attributes, { + // 1.27 attributes + [ATTR_CLIENT_ADDRESS]: body.address, + [ATTR_HTTP_REQUEST_METHOD]: HTTP_REQUEST_METHOD_VALUE_GET, + [ATTR_SERVER_ADDRESS]: hostname, + [ATTR_SERVER_PORT]: serverPort, + [ATTR_HTTP_RESPONSE_STATUS_CODE]: 200, + [ATTR_NETWORK_PEER_ADDRESS]: body.address, + [ATTR_NETWORK_PEER_PORT]: response.clientRemotePort, + [ATTR_NETWORK_PROTOCOL_VERSION]: '1.1', + [ATTR_URL_PATH]: pathname, + [ATTR_URL_SCHEME]: protocol, + + // 1.7 attributes + [SEMATTRS_HTTP_FLAVOR]: '1.1', + [SEMATTRS_HTTP_HOST]: `${hostname}:${serverPort}`, + [SEMATTRS_HTTP_METHOD]: 'GET', + [SEMATTRS_HTTP_SCHEME]: protocol, + [SEMATTRS_HTTP_STATUS_CODE]: 200, + [SEMATTRS_HTTP_TARGET]: '/test', + [SEMATTRS_HTTP_URL]: `http://${hostname}:${serverPort}${pathname}`, + [SEMATTRS_NET_TRANSPORT]: 'ip_tcp', + [SEMATTRS_NET_HOST_IP]: body.address, + [SEMATTRS_NET_HOST_NAME]: hostname, + [SEMATTRS_NET_HOST_PORT]: serverPort, + [SEMATTRS_NET_PEER_IP]: body.address, + [SEMATTRS_NET_PEER_PORT]: response.clientRemotePort, + + // unspecified old names + [AttributeNames.HTTP_STATUS_TEXT]: 'OK', + }); + }); }); describe('with require parent span', () => { 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 4b64e93681..d64f795383 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts @@ -313,7 +313,8 @@ describe('Utility', () => { () => { const attributes = utils.getIncomingRequestAttributesOnResponse( request, - {} as ServerResponse + {} as ServerResponse, + SemconvStability.OLD ); assert.deepStrictEqual(attributes[SEMATTRS_HTTP_ROUTE], '/user/:id'); context.disable(); @@ -326,9 +327,13 @@ describe('Utility', () => { const request = { socket: {}, } as IncomingMessage; - const attributes = utils.getIncomingRequestAttributesOnResponse(request, { - socket: {}, - } as ServerResponse & { socket: Socket }); + const attributes = utils.getIncomingRequestAttributesOnResponse( + request, + { + socket: {}, + } as ServerResponse & { socket: Socket }, + SemconvStability.OLD + ); assert.deepEqual(attributes[SEMATTRS_HTTP_ROUTE], undefined); }); }); @@ -501,6 +506,7 @@ describe('Utility', () => { const request = { url: 'http://hostname/user/:id', method: 'GET', + socket: {}, } as IncomingMessage; request.headers = { 'user-agent': 'chrome', @@ -508,6 +514,7 @@ describe('Utility', () => { }; const attributes = utils.getIncomingRequestAttributes(request, { component: 'http', + semconvStability: SemconvStability.OLD, }); assert.strictEqual(attributes[SEMATTRS_HTTP_ROUTE], undefined); }); @@ -516,12 +523,14 @@ describe('Utility', () => { const request = { url: 'http://hostname/user/?q=val', method: 'GET', + socket: {}, } as IncomingMessage; request.headers = { 'user-agent': 'chrome', }; const attributes = utils.getIncomingRequestAttributes(request, { component: 'http', + semconvStability: SemconvStability.OLD, }); assert.strictEqual(attributes[SEMATTRS_HTTP_TARGET], '/user/?q=val'); }); diff --git a/experimental/packages/opentelemetry-instrumentation-http/test/utils/httpRequest.ts b/experimental/packages/opentelemetry-instrumentation-http/test/utils/httpRequest.ts index 14be1f8b11..76d1b600c1 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/utils/httpRequest.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/utils/httpRequest.ts @@ -23,6 +23,10 @@ type GetResult = Promise<{ reqHeaders: http.OutgoingHttpHeaders; method: string | undefined; address?: string; + clientRemotePort?: number; + clientRemoteAddress?: string; + req: http.OutgoingMessage; + res: http.IncomingMessage; }>; function get(input: string | URL, options?: http.RequestOptions): GetResult; @@ -48,6 +52,10 @@ function get(input: any, options?: any): GetResult { resHeaders: res.headers, method: res.req.method, address: req.socket?.remoteAddress, + clientRemotePort: res.req.socket?.localPort, + clientRemoteAddress: res.req.socket?.localAddress, + req, + res, }); }); resp.on('error', err => { diff --git a/package-lock.json b/package-lock.json index dc54529e58..b850aa1ca9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -958,6 +958,7 @@ "@opentelemetry/core": "1.26.0", "@opentelemetry/instrumentation": "0.53.0", "@opentelemetry/semantic-conventions": "1.27.0", + "forwarded-parse": "2.1.2", "semver": "^7.5.2" }, "devDependencies": { @@ -14213,6 +14214,12 @@ "node": ">= 0.6" } }, + "node_modules/forwarded-parse": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", + "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", + "license": "MIT" + }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -33217,6 +33224,7 @@ "@types/superagent": "8.1.9", "axios": "1.7.4", "cross-var": "1.1.0", + "forwarded-parse": "2.1.2", "lerna": "6.6.2", "mocha": "10.7.3", "nock": "13.3.8", @@ -40790,6 +40798,11 @@ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" }, + "forwarded-parse": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", + "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==" + }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", From 2a4919c1cf99d3403d387d7589836fd9e3018896 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Tue, 24 Sep 2024 11:46:21 +0200 Subject: [PATCH 08/20] chore(deps): update dependency @types/mocha to v10.0.8 (#4986) --- api/package.json | 2 +- experimental/packages/api-events/package.json | 2 +- experimental/packages/api-logs/package.json | 2 +- .../exporter-logs-otlp-grpc/package.json | 2 +- .../exporter-logs-otlp-http/package.json | 2 +- .../exporter-logs-otlp-proto/package.json | 2 +- .../exporter-trace-otlp-grpc/package.json | 2 +- .../exporter-trace-otlp-http/package.json | 2 +- .../exporter-trace-otlp-proto/package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../opentelemetry-sdk-node/package.json | 2 +- .../packages/otlp-exporter-base/package.json | 2 +- .../otlp-grpc-exporter-base/package.json | 2 +- .../packages/otlp-transformer/package.json | 2 +- .../propagator-aws-xray-lambda/package.json | 2 +- .../sampler-jaeger-remote/package.json | 2 +- experimental/packages/sdk-events/package.json | 2 +- experimental/packages/sdk-logs/package.json | 2 +- .../packages/shim-opencensus/package.json | 2 +- experimental/packages/web-common/package.json | 2 +- integration-tests/api/package.json | 2 +- package-lock.json | 192 +++++++++--------- .../package.json | 2 +- .../package.json | 2 +- packages/opentelemetry-core/package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../opentelemetry-propagator-b3/package.json | 2 +- .../package.json | 2 +- packages/opentelemetry-resources/package.json | 2 +- .../opentelemetry-sdk-trace-base/package.json | 2 +- .../opentelemetry-sdk-trace-node/package.json | 2 +- .../opentelemetry-sdk-trace-web/package.json | 2 +- .../package.json | 2 +- packages/propagator-aws-xray/package.json | 2 +- packages/sdk-metrics/package.json | 2 +- semantic-conventions/package.json | 2 +- 46 files changed, 141 insertions(+), 141 deletions(-) diff --git a/api/package.json b/api/package.json index 28ee7e3598..78f9f03b79 100644 --- a/api/package.json +++ b/api/package.json @@ -77,7 +77,7 @@ "access": "public" }, "devDependencies": { - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack": "5.28.5", diff --git a/experimental/packages/api-events/package.json b/experimental/packages/api-events/package.json index 228913124b..66a7a27879 100644 --- a/experimental/packages/api-events/package.json +++ b/experimental/packages/api-events/package.json @@ -64,7 +64,7 @@ "@opentelemetry/api-logs": "0.53.0" }, "devDependencies": { - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/webpack-env": "1.16.3", "babel-plugin-istanbul": "7.0.0", diff --git a/experimental/packages/api-logs/package.json b/experimental/packages/api-logs/package.json index 63db9d1d15..c5204f6311 100644 --- a/experimental/packages/api-logs/package.json +++ b/experimental/packages/api-logs/package.json @@ -63,7 +63,7 @@ "@opentelemetry/api": "^1.3.0" }, "devDependencies": { - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/webpack-env": "1.16.3", "babel-plugin-istanbul": "7.0.0", diff --git a/experimental/packages/exporter-logs-otlp-grpc/package.json b/experimental/packages/exporter-logs-otlp-grpc/package.json index 41de38c0df..9db1a4cb18 100644 --- a/experimental/packages/exporter-logs-otlp-grpc/package.json +++ b/experimental/packages/exporter-logs-otlp-grpc/package.json @@ -54,7 +54,7 @@ "@opentelemetry/api-logs": "0.53.0", "@opentelemetry/otlp-exporter-base": "0.53.0", "@opentelemetry/resources": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", diff --git a/experimental/packages/exporter-logs-otlp-http/package.json b/experimental/packages/exporter-logs-otlp-http/package.json index 18f756211c..e60e0fbf8f 100644 --- a/experimental/packages/exporter-logs-otlp-http/package.json +++ b/experimental/packages/exporter-logs-otlp-http/package.json @@ -74,7 +74,7 @@ "@babel/preset-env": "7.25.4", "@opentelemetry/api": "1.9.0", "@opentelemetry/resources": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", diff --git a/experimental/packages/exporter-logs-otlp-proto/package.json b/experimental/packages/exporter-logs-otlp-proto/package.json index 92e9d1a0b7..03cbfef200 100644 --- a/experimental/packages/exporter-logs-otlp-proto/package.json +++ b/experimental/packages/exporter-logs-otlp-proto/package.json @@ -66,7 +66,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": "1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "babel-plugin-istanbul": "7.0.0", diff --git a/experimental/packages/exporter-trace-otlp-grpc/package.json b/experimental/packages/exporter-trace-otlp-grpc/package.json index 802819b618..dda793159e 100644 --- a/experimental/packages/exporter-trace-otlp-grpc/package.json +++ b/experimental/packages/exporter-trace-otlp-grpc/package.json @@ -51,7 +51,7 @@ "@grpc/proto-loader": "^0.7.10", "@opentelemetry/api": "1.9.0", "@opentelemetry/otlp-exporter-base": "0.53.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", diff --git a/experimental/packages/exporter-trace-otlp-http/package.json b/experimental/packages/exporter-trace-otlp-http/package.json index b0fe1b39c0..d1570027ba 100644 --- a/experimental/packages/exporter-trace-otlp-http/package.json +++ b/experimental/packages/exporter-trace-otlp-http/package.json @@ -65,7 +65,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": "1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", diff --git a/experimental/packages/exporter-trace-otlp-proto/package.json b/experimental/packages/exporter-trace-otlp-proto/package.json index e18942f901..fdb271449e 100644 --- a/experimental/packages/exporter-trace-otlp-proto/package.json +++ b/experimental/packages/exporter-trace-otlp-proto/package.json @@ -65,7 +65,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": "1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "babel-plugin-istanbul": "7.0.0", diff --git a/experimental/packages/opentelemetry-browser-detector/package.json b/experimental/packages/opentelemetry-browser-detector/package.json index 117b9efd5a..fd5d7b4083 100644 --- a/experimental/packages/opentelemetry-browser-detector/package.json +++ b/experimental/packages/opentelemetry-browser-detector/package.json @@ -56,7 +56,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": "1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "babel-plugin-istanbul": "7.0.0", diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/package.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/package.json index e9e89dba2c..04e19ffb1e 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/package.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/package.json @@ -50,7 +50,7 @@ "devDependencies": { "@grpc/proto-loader": "^0.7.10", "@opentelemetry/api": "1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/package.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/package.json index 79812d5a6c..c71ed8cbf2 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/package.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/package.json @@ -65,7 +65,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": "1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json index 0e2b49108f..ee104b0016 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/package.json @@ -56,7 +56,7 @@ }, "devDependencies": { "@opentelemetry/api": "1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", diff --git a/experimental/packages/opentelemetry-exporter-prometheus/package.json b/experimental/packages/opentelemetry-exporter-prometheus/package.json index f92af3f641..e31c73beba 100644 --- a/experimental/packages/opentelemetry-exporter-prometheus/package.json +++ b/experimental/packages/opentelemetry-exporter-prometheus/package.json @@ -45,7 +45,7 @@ "devDependencies": { "@opentelemetry/api": "1.9.0", "@opentelemetry/semantic-conventions": "1.27.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", diff --git a/experimental/packages/opentelemetry-instrumentation-fetch/package.json b/experimental/packages/opentelemetry-instrumentation-fetch/package.json index d320f1d597..8e13ecc1dc 100644 --- a/experimental/packages/opentelemetry-instrumentation-fetch/package.json +++ b/experimental/packages/opentelemetry-instrumentation-fetch/package.json @@ -60,7 +60,7 @@ "@opentelemetry/context-zone": "1.26.0", "@opentelemetry/propagator-b3": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/package.json b/experimental/packages/opentelemetry-instrumentation-grpc/package.json index 60ec8d4041..b9beb6d1ca 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/package.json +++ b/experimental/packages/opentelemetry-instrumentation-grpc/package.json @@ -57,7 +57,7 @@ "@protobuf-ts/grpc-transport": "2.9.4", "@protobuf-ts/runtime": "2.9.4", "@protobuf-ts/runtime-rpc": "2.9.4", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/semver": "7.5.8", "@types/sinon": "17.0.3", diff --git a/experimental/packages/opentelemetry-instrumentation-http/package.json b/experimental/packages/opentelemetry-instrumentation-http/package.json index aadf9a5663..5242743725 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/package.json +++ b/experimental/packages/opentelemetry-instrumentation-http/package.json @@ -53,7 +53,7 @@ "@opentelemetry/sdk-metrics": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", "@opentelemetry/sdk-trace-node": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/request-promise-native": "1.0.21", "@types/semver": "7.5.8", diff --git a/experimental/packages/opentelemetry-instrumentation-xml-http-request/package.json b/experimental/packages/opentelemetry-instrumentation-xml-http-request/package.json index b35868ddf0..a72efde5c1 100644 --- a/experimental/packages/opentelemetry-instrumentation-xml-http-request/package.json +++ b/experimental/packages/opentelemetry-instrumentation-xml-http-request/package.json @@ -60,7 +60,7 @@ "@opentelemetry/context-zone": "1.26.0", "@opentelemetry/propagator-b3": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", diff --git a/experimental/packages/opentelemetry-instrumentation/package.json b/experimental/packages/opentelemetry-instrumentation/package.json index f6b8500a85..64dfb4a58b 100644 --- a/experimental/packages/opentelemetry-instrumentation/package.json +++ b/experimental/packages/opentelemetry-instrumentation/package.json @@ -85,7 +85,7 @@ "@babel/preset-env": "7.25.4", "@opentelemetry/api": "1.9.0", "@opentelemetry/sdk-metrics": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/semver": "7.5.8", "@types/sinon": "17.0.3", diff --git a/experimental/packages/opentelemetry-sdk-node/package.json b/experimental/packages/opentelemetry-sdk-node/package.json index b4a6ce7beb..b8d33a82fa 100644 --- a/experimental/packages/opentelemetry-sdk-node/package.json +++ b/experimental/packages/opentelemetry-sdk-node/package.json @@ -68,7 +68,7 @@ "@opentelemetry/api": "1.9.0", "@opentelemetry/context-async-hooks": "1.26.0", "@opentelemetry/exporter-jaeger": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/semver": "7.5.8", "@types/sinon": "17.0.3", diff --git a/experimental/packages/otlp-exporter-base/package.json b/experimental/packages/otlp-exporter-base/package.json index 5502d846c9..9f1f1d0db7 100644 --- a/experimental/packages/otlp-exporter-base/package.json +++ b/experimental/packages/otlp-exporter-base/package.json @@ -67,7 +67,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": "1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "babel-plugin-istanbul": "7.0.0", diff --git a/experimental/packages/otlp-grpc-exporter-base/package.json b/experimental/packages/otlp-grpc-exporter-base/package.json index 5204486be1..a9f9677c29 100644 --- a/experimental/packages/otlp-grpc-exporter-base/package.json +++ b/experimental/packages/otlp-grpc-exporter-base/package.json @@ -49,7 +49,7 @@ "@opentelemetry/api": "1.9.0", "@opentelemetry/resources": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", diff --git a/experimental/packages/otlp-transformer/package.json b/experimental/packages/otlp-transformer/package.json index 6c17cf0704..413fc04ac7 100644 --- a/experimental/packages/otlp-transformer/package.json +++ b/experimental/packages/otlp-transformer/package.json @@ -62,7 +62,7 @@ }, "devDependencies": { "@opentelemetry/api": "1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/webpack-env": "1.16.3", "babel-plugin-istanbul": "7.0.0", "cross-var": "1.1.0", diff --git a/experimental/packages/propagator-aws-xray-lambda/package.json b/experimental/packages/propagator-aws-xray-lambda/package.json index a123511330..fe49aa4ce7 100644 --- a/experimental/packages/propagator-aws-xray-lambda/package.json +++ b/experimental/packages/propagator-aws-xray-lambda/package.json @@ -48,7 +48,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": "1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "babel-loader": "8.4.1", diff --git a/experimental/packages/sampler-jaeger-remote/package.json b/experimental/packages/sampler-jaeger-remote/package.json index 4e8d6b2fec..c2c2572881 100644 --- a/experimental/packages/sampler-jaeger-remote/package.json +++ b/experimental/packages/sampler-jaeger-remote/package.json @@ -48,7 +48,7 @@ "@opentelemetry/api": "^1.3.0" }, "devDependencies": { - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", diff --git a/experimental/packages/sdk-events/package.json b/experimental/packages/sdk-events/package.json index 3e4688366e..6ea9047708 100644 --- a/experimental/packages/sdk-events/package.json +++ b/experimental/packages/sdk-events/package.json @@ -70,7 +70,7 @@ "@opentelemetry/api": "1.9.0", "@opentelemetry/api-events": "0.52.0", "@opentelemetry/api-logs": "0.52.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "10.0.20", "@types/webpack-env": "1.16.3", diff --git a/experimental/packages/sdk-logs/package.json b/experimental/packages/sdk-logs/package.json index 5159214bb6..a7938ee0d2 100644 --- a/experimental/packages/sdk-logs/package.json +++ b/experimental/packages/sdk-logs/package.json @@ -76,7 +76,7 @@ "@opentelemetry/api": ">=1.4.0 <1.10.0", "@opentelemetry/api-logs": "0.52.0", "@opentelemetry/resources_1.9.0": "npm:@opentelemetry/resources@1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "babel-plugin-istanbul": "7.0.0", diff --git a/experimental/packages/shim-opencensus/package.json b/experimental/packages/shim-opencensus/package.json index 9d816cae00..240c506b76 100644 --- a/experimental/packages/shim-opencensus/package.json +++ b/experimental/packages/shim-opencensus/package.json @@ -52,7 +52,7 @@ "@opentelemetry/api": "1.9.0", "@opentelemetry/context-async-hooks": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", diff --git a/experimental/packages/web-common/package.json b/experimental/packages/web-common/package.json index 83dd8abd3c..c0ba90589c 100644 --- a/experimental/packages/web-common/package.json +++ b/experimental/packages/web-common/package.json @@ -75,7 +75,7 @@ "@opentelemetry/api": "1.9.0", "@opentelemetry/api-events": "0.53.0", "@opentelemetry/api-logs": "0.53.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "10.0.20", "@types/webpack-env": "1.16.3", diff --git a/integration-tests/api/package.json b/integration-tests/api/package.json index d80581a552..1edf1de0c2 100644 --- a/integration-tests/api/package.json +++ b/integration-tests/api/package.json @@ -29,7 +29,7 @@ "devDependencies": { "@opentelemetry/api": "^1.3.0", "@opentelemetry/core": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "cross-var": "1.1.0", "lerna": "6.6.2", diff --git a/package-lock.json b/package-lock.json index b850aa1ca9..3978ff726b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,7 +61,7 @@ "version": "1.9.0", "license": "Apache-2.0", "devDependencies": { - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack": "5.28.5", @@ -334,7 +334,7 @@ "@opentelemetry/api-logs": "0.53.0" }, "devDependencies": { - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/webpack-env": "1.16.3", "babel-plugin-istanbul": "7.0.0", @@ -364,7 +364,7 @@ "@opentelemetry/api": "^1.3.0" }, "devDependencies": { - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/webpack-env": "1.16.3", "babel-plugin-istanbul": "7.0.0", @@ -403,7 +403,7 @@ "@opentelemetry/api-logs": "0.53.0", "@opentelemetry/otlp-exporter-base": "0.53.0", "@opentelemetry/resources": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", @@ -437,7 +437,7 @@ "@babel/preset-env": "7.25.4", "@opentelemetry/api": "1.9.0", "@opentelemetry/resources": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -484,7 +484,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": "1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "babel-plugin-istanbul": "7.0.0", @@ -528,7 +528,7 @@ "@grpc/proto-loader": "^0.7.10", "@opentelemetry/api": "1.9.0", "@opentelemetry/otlp-exporter-base": "0.53.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", @@ -561,7 +561,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": "1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -606,7 +606,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": "1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "babel-plugin-istanbul": "7.0.0", @@ -645,7 +645,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": "1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "babel-plugin-istanbul": "7.0.0", @@ -690,7 +690,7 @@ "devDependencies": { "@grpc/proto-loader": "^0.7.10", "@opentelemetry/api": "1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", @@ -723,7 +723,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": "1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -767,7 +767,7 @@ }, "devDependencies": { "@opentelemetry/api": "1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", @@ -797,7 +797,7 @@ "devDependencies": { "@opentelemetry/api": "1.9.0", "@opentelemetry/semantic-conventions": "1.27.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", @@ -831,7 +831,7 @@ "@babel/preset-env": "7.25.4", "@opentelemetry/api": "1.9.0", "@opentelemetry/sdk-metrics": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/semver": "7.5.8", "@types/sinon": "17.0.3", @@ -881,7 +881,7 @@ "@opentelemetry/context-zone": "1.26.0", "@opentelemetry/propagator-b3": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -931,7 +931,7 @@ "@protobuf-ts/grpc-transport": "2.9.4", "@protobuf-ts/runtime": "2.9.4", "@protobuf-ts/runtime-rpc": "2.9.4", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/semver": "7.5.8", "@types/sinon": "17.0.3", @@ -967,7 +967,7 @@ "@opentelemetry/sdk-metrics": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", "@opentelemetry/sdk-trace-node": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/request-promise-native": "1.0.21", "@types/semver": "7.5.8", @@ -1020,7 +1020,7 @@ "@opentelemetry/context-zone": "1.26.0", "@opentelemetry/propagator-b3": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -1076,7 +1076,7 @@ "@opentelemetry/api": "1.9.0", "@opentelemetry/context-async-hooks": "1.26.0", "@opentelemetry/exporter-jaeger": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/semver": "7.5.8", "@types/sinon": "17.0.3", @@ -1108,7 +1108,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": "1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "babel-plugin-istanbul": "7.0.0", @@ -1150,7 +1150,7 @@ "@opentelemetry/api": "1.9.0", "@opentelemetry/resources": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", @@ -1183,7 +1183,7 @@ }, "devDependencies": { "@opentelemetry/api": "1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/webpack-env": "1.16.3", "babel-plugin-istanbul": "7.0.0", "cross-var": "1.1.0", @@ -1219,7 +1219,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": "1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "babel-loader": "8.4.1", @@ -1247,7 +1247,7 @@ "@opentelemetry/sdk-trace-base": "1.26.0" }, "devDependencies": { - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", @@ -1415,7 +1415,7 @@ "@opentelemetry/api": "1.9.0", "@opentelemetry/api-events": "0.52.0", "@opentelemetry/api-logs": "0.52.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "10.0.20", "@types/webpack-env": "1.16.3", @@ -1494,7 +1494,7 @@ "@opentelemetry/api": ">=1.4.0 <1.10.0", "@opentelemetry/api-logs": "0.52.0", "@opentelemetry/resources_1.9.0": "npm:@opentelemetry/resources@1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "babel-plugin-istanbul": "7.0.0", @@ -1600,7 +1600,7 @@ "@opentelemetry/api": "1.9.0", "@opentelemetry/context-async-hooks": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", @@ -1632,7 +1632,7 @@ "@opentelemetry/api": "1.9.0", "@opentelemetry/api-events": "0.53.0", "@opentelemetry/api-logs": "0.53.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "10.0.20", "@types/webpack-env": "1.16.3", @@ -1678,7 +1678,7 @@ "devDependencies": { "@opentelemetry/api": "^1.3.0", "@opentelemetry/core": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "cross-var": "1.1.0", "lerna": "6.6.2", @@ -6984,9 +6984,9 @@ "dev": true }, "node_modules/@types/mocha": { - "version": "10.0.7", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.7.tgz", - "integrity": "sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw==", + "version": "10.0.8", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.8.tgz", + "integrity": "sha512-HfMcUmy9hTMJh66VNcmeC9iVErIZJli2bszuXc6julh5YGuRb/W5OnkHjwLNYdFlMis0sY3If5SEAp+PktdJjw==", "dev": true }, "node_modules/@types/node": { @@ -28736,7 +28736,7 @@ "license": "Apache-2.0", "devDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "cross-var": "1.1.0", "lerna": "6.6.2", @@ -28776,7 +28776,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": ">=1.0.0 <1.10.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -28826,7 +28826,7 @@ }, "devDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -28866,7 +28866,7 @@ "devDependencies": { "@opentelemetry/api": "^1.0.0", "@opentelemetry/resources": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", @@ -28898,7 +28898,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": "^1.0.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -28938,7 +28938,7 @@ }, "devDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "cross-var": "1.1.0", "lerna": "6.6.2", @@ -28963,7 +28963,7 @@ }, "devDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -29001,7 +29001,7 @@ "devDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0", "@opentelemetry/resources_1.9.0": "npm:@opentelemetry/resources@1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -29091,7 +29091,7 @@ }, "devDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -29135,7 +29135,7 @@ "@opentelemetry/api": ">=1.0.0 <1.10.0", "@opentelemetry/resources": "1.26.0", "@opentelemetry/semantic-conventions": "1.27.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/semver": "7.5.8", "@types/sinon": "17.0.3", @@ -29170,7 +29170,7 @@ "@opentelemetry/propagator-b3": "1.26.0", "@opentelemetry/resources": "1.26.0", "@types/jquery": "3.5.30", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -29216,7 +29216,7 @@ "@opentelemetry/propagator-b3": "1.26.0", "@opentelemetry/propagator-jaeger": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "cross-var": "1.1.0", "lerna": "6.6.2", @@ -29240,7 +29240,7 @@ }, "devDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/webpack-env": "1.16.3", "babel-plugin-istanbul": "7.0.0", @@ -29277,7 +29277,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": ">=1.3.0 <1.10.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "babel-plugin-istanbul": "7.0.0", @@ -29501,7 +29501,7 @@ "@size-limit/file": "^11.0.1", "@size-limit/time": "^11.0.1", "@size-limit/webpack": "^11.0.1", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", @@ -32590,7 +32590,7 @@ "@opentelemetry/api": { "version": "file:api", "requires": { - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack": "5.28.5", @@ -32621,7 +32621,7 @@ "requires": { "@opentelemetry/api": "^1.3.0", "@opentelemetry/api-logs": "0.53.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/webpack-env": "1.16.3", "babel-plugin-istanbul": "7.0.0", @@ -32644,7 +32644,7 @@ "version": "file:experimental/packages/api-logs", "requires": { "@opentelemetry/api": "^1.3.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/webpack-env": "1.16.3", "babel-plugin-istanbul": "7.0.0", @@ -32667,7 +32667,7 @@ "version": "file:packages/opentelemetry-context-async-hooks", "requires": { "@opentelemetry/api": ">=1.0.0 <1.10.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "cross-var": "1.1.0", "lerna": "6.6.2", @@ -32692,7 +32692,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": ">=1.0.0 <1.10.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -32733,7 +32733,7 @@ "requires": { "@opentelemetry/api": ">=1.0.0 <1.10.0", "@opentelemetry/semantic-conventions": "1.27.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -32762,7 +32762,7 @@ "@opentelemetry/resources": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", "@opentelemetry/semantic-conventions": "1.27.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", @@ -32788,7 +32788,7 @@ "@opentelemetry/otlp-transformer": "0.53.0", "@opentelemetry/resources": "1.26.0", "@opentelemetry/sdk-logs": "0.53.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", @@ -32812,7 +32812,7 @@ "@opentelemetry/otlp-transformer": "0.53.0", "@opentelemetry/resources": "1.26.0", "@opentelemetry/sdk-logs": "0.53.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -32849,7 +32849,7 @@ "@opentelemetry/resources": "1.26.0", "@opentelemetry/sdk-logs": "0.53.0", "@opentelemetry/sdk-trace-base": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "babel-plugin-istanbul": "7.0.0", @@ -32884,7 +32884,7 @@ "@opentelemetry/otlp-transformer": "0.53.0", "@opentelemetry/resources": "1.26.0", "@opentelemetry/sdk-metrics": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", @@ -32907,7 +32907,7 @@ "@opentelemetry/otlp-transformer": "0.53.0", "@opentelemetry/resources": "1.26.0", "@opentelemetry/sdk-metrics": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -32941,7 +32941,7 @@ "@opentelemetry/otlp-transformer": "0.53.0", "@opentelemetry/resources": "1.26.0", "@opentelemetry/sdk-metrics": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", @@ -32961,7 +32961,7 @@ "@opentelemetry/resources": "1.26.0", "@opentelemetry/sdk-metrics": "1.26.0", "@opentelemetry/semantic-conventions": "1.27.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", @@ -32984,7 +32984,7 @@ "@opentelemetry/otlp-transformer": "0.53.0", "@opentelemetry/resources": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", @@ -33007,7 +33007,7 @@ "@opentelemetry/otlp-transformer": "0.53.0", "@opentelemetry/resources": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -33042,7 +33042,7 @@ "@opentelemetry/otlp-transformer": "0.53.0", "@opentelemetry/resources": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "babel-plugin-istanbul": "7.0.0", @@ -33074,7 +33074,7 @@ "@opentelemetry/resources": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", "@opentelemetry/semantic-conventions": "1.27.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -33107,7 +33107,7 @@ "@opentelemetry/api": "1.9.0", "@opentelemetry/api-logs": "0.53.0", "@opentelemetry/sdk-metrics": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/semver": "7.5.8", "@types/shimmer": "^1.2.0", @@ -33152,7 +33152,7 @@ "@opentelemetry/sdk-trace-base": "1.26.0", "@opentelemetry/sdk-trace-web": "1.26.0", "@opentelemetry/semantic-conventions": "1.27.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -33192,7 +33192,7 @@ "@protobuf-ts/grpc-transport": "2.9.4", "@protobuf-ts/runtime": "2.9.4", "@protobuf-ts/runtime-rpc": "2.9.4", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/semver": "7.5.8", "@types/sinon": "17.0.3", @@ -33216,7 +33216,7 @@ "@opentelemetry/sdk-trace-base": "1.26.0", "@opentelemetry/sdk-trace-node": "1.26.0", "@opentelemetry/semantic-conventions": "1.27.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/request-promise-native": "1.0.21", "@types/semver": "7.5.8", @@ -33263,7 +33263,7 @@ "@opentelemetry/sdk-trace-base": "1.26.0", "@opentelemetry/sdk-trace-web": "1.26.0", "@opentelemetry/semantic-conventions": "1.27.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -33292,7 +33292,7 @@ "requires": { "@opentelemetry/api": "^1.3.0", "@opentelemetry/core": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "cross-var": "1.1.0", "lerna": "6.6.2", @@ -33307,7 +33307,7 @@ "@babel/preset-env": "7.25.4", "@opentelemetry/api": "1.9.0", "@opentelemetry/resources": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "babel-plugin-istanbul": "7.0.0", @@ -33337,7 +33337,7 @@ "@opentelemetry/api": "1.9.0", "@opentelemetry/core": "1.26.0", "@opentelemetry/otlp-transformer": "0.53.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "babel-plugin-istanbul": "7.0.0", @@ -33369,7 +33369,7 @@ "@opentelemetry/otlp-transformer": "0.53.0", "@opentelemetry/resources": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", @@ -33391,7 +33391,7 @@ "@opentelemetry/sdk-logs": "0.53.0", "@opentelemetry/sdk-metrics": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/webpack-env": "1.16.3", "babel-plugin-istanbul": "7.0.0", "cross-var": "1.1.0", @@ -33416,7 +33416,7 @@ "requires": { "@opentelemetry/api": ">=1.0.0 <1.10.0", "@opentelemetry/core": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/webpack-env": "1.16.3", "babel-plugin-istanbul": "7.0.0", @@ -33442,7 +33442,7 @@ "@babel/preset-env": "7.25.4", "@opentelemetry/api": "1.9.0", "@opentelemetry/propagator-aws-xray": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "babel-loader": "8.4.1", @@ -33461,7 +33461,7 @@ "requires": { "@opentelemetry/api": ">=1.0.0 <1.10.0", "@opentelemetry/core": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "cross-var": "1.1.0", "lerna": "6.6.2", @@ -33476,7 +33476,7 @@ "requires": { "@opentelemetry/api": ">=1.0.0 <1.10.0", "@opentelemetry/core": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -33504,7 +33504,7 @@ "@opentelemetry/core": "1.26.0", "@opentelemetry/resources_1.9.0": "npm:@opentelemetry/resources@1.9.0", "@opentelemetry/semantic-conventions": "1.27.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -33566,7 +33566,7 @@ "version": "file:experimental/packages/sampler-jaeger-remote", "requires": { "@opentelemetry/sdk-trace-base": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", @@ -33678,7 +33678,7 @@ "@opentelemetry/api-events": "0.52.0", "@opentelemetry/api-logs": "0.52.0", "@opentelemetry/sdk-logs": "0.53.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "10.0.20", "@types/webpack-env": "1.16.3", @@ -33742,7 +33742,7 @@ "@opentelemetry/core": "1.26.0", "@opentelemetry/resources": "1.26.0", "@opentelemetry/resources_1.9.0": "npm:@opentelemetry/resources@1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "babel-plugin-istanbul": "7.0.0", @@ -33816,7 +33816,7 @@ "@opentelemetry/api": ">=1.3.0 <1.10.0", "@opentelemetry/core": "1.26.0", "@opentelemetry/resources": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "babel-plugin-istanbul": "7.0.0", @@ -33860,7 +33860,7 @@ "@opentelemetry/sdk-trace-base": "1.26.0", "@opentelemetry/sdk-trace-node": "1.26.0", "@opentelemetry/semantic-conventions": "1.27.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/semver": "7.5.8", "@types/sinon": "17.0.3", @@ -33881,7 +33881,7 @@ "@opentelemetry/core": "1.26.0", "@opentelemetry/resources": "1.26.0", "@opentelemetry/semantic-conventions": "1.27.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -33914,7 +33914,7 @@ "@opentelemetry/resources": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", "@opentelemetry/semantic-conventions": "1.27.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/semver": "7.5.8", "@types/sinon": "17.0.3", @@ -33940,7 +33940,7 @@ "@opentelemetry/sdk-trace-base": "1.26.0", "@opentelemetry/semantic-conventions": "1.27.0", "@types/jquery": "3.5.30", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", @@ -34096,7 +34096,7 @@ "@size-limit/file": "^11.0.1", "@size-limit/time": "^11.0.1", "@size-limit/webpack": "^11.0.1", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", @@ -34120,7 +34120,7 @@ "@opentelemetry/resources": "1.26.0", "@opentelemetry/sdk-metrics": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", @@ -34142,7 +34142,7 @@ "@opentelemetry/propagator-jaeger": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", "@opentelemetry/semantic-conventions": "1.27.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "cross-var": "1.1.0", "lerna": "6.6.2", @@ -34171,7 +34171,7 @@ "@opentelemetry/sdk-logs": "^0.53.0", "@opentelemetry/sdk-trace-base": "^1.26.0", "@opentelemetry/semantic-conventions": "1.27.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "10.0.20", "@types/webpack-env": "1.16.3", @@ -34917,9 +34917,9 @@ "dev": true }, "@types/mocha": { - "version": "10.0.7", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.7.tgz", - "integrity": "sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw==", + "version": "10.0.8", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.8.tgz", + "integrity": "sha512-HfMcUmy9hTMJh66VNcmeC9iVErIZJli2bszuXc6julh5YGuRb/W5OnkHjwLNYdFlMis0sY3If5SEAp+PktdJjw==", "dev": true }, "@types/node": { diff --git a/packages/opentelemetry-context-async-hooks/package.json b/packages/opentelemetry-context-async-hooks/package.json index a55c4c589d..47cd865ea3 100644 --- a/packages/opentelemetry-context-async-hooks/package.json +++ b/packages/opentelemetry-context-async-hooks/package.json @@ -45,7 +45,7 @@ }, "devDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "cross-var": "1.1.0", "lerna": "6.6.2", diff --git a/packages/opentelemetry-context-zone-peer-dep/package.json b/packages/opentelemetry-context-zone-peer-dep/package.json index afc1984499..a577bece4d 100644 --- a/packages/opentelemetry-context-zone-peer-dep/package.json +++ b/packages/opentelemetry-context-zone-peer-dep/package.json @@ -56,7 +56,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": ">=1.0.0 <1.10.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", diff --git a/packages/opentelemetry-core/package.json b/packages/opentelemetry-core/package.json index edaad660fb..7b82133314 100644 --- a/packages/opentelemetry-core/package.json +++ b/packages/opentelemetry-core/package.json @@ -64,7 +64,7 @@ }, "devDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", diff --git a/packages/opentelemetry-exporter-jaeger/package.json b/packages/opentelemetry-exporter-jaeger/package.json index da9edd2427..5d0f3c4487 100644 --- a/packages/opentelemetry-exporter-jaeger/package.json +++ b/packages/opentelemetry-exporter-jaeger/package.json @@ -46,7 +46,7 @@ "devDependencies": { "@opentelemetry/api": "^1.0.0", "@opentelemetry/resources": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", diff --git a/packages/opentelemetry-exporter-zipkin/package.json b/packages/opentelemetry-exporter-zipkin/package.json index 937ad37cdc..2ef3da6a87 100644 --- a/packages/opentelemetry-exporter-zipkin/package.json +++ b/packages/opentelemetry-exporter-zipkin/package.json @@ -62,7 +62,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": "^1.0.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", diff --git a/packages/opentelemetry-propagator-b3/package.json b/packages/opentelemetry-propagator-b3/package.json index 486c0e7b9d..e0f5a038bf 100644 --- a/packages/opentelemetry-propagator-b3/package.json +++ b/packages/opentelemetry-propagator-b3/package.json @@ -58,7 +58,7 @@ }, "devDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "cross-var": "1.1.0", "lerna": "6.6.2", diff --git a/packages/opentelemetry-propagator-jaeger/package.json b/packages/opentelemetry-propagator-jaeger/package.json index add389c4f7..d20ccdcbf4 100644 --- a/packages/opentelemetry-propagator-jaeger/package.json +++ b/packages/opentelemetry-propagator-jaeger/package.json @@ -55,7 +55,7 @@ }, "devDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", diff --git a/packages/opentelemetry-resources/package.json b/packages/opentelemetry-resources/package.json index 6ef15b5fff..b1d3a2a230 100644 --- a/packages/opentelemetry-resources/package.json +++ b/packages/opentelemetry-resources/package.json @@ -65,7 +65,7 @@ "devDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0", "@opentelemetry/resources_1.9.0": "npm:@opentelemetry/resources@1.9.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", diff --git a/packages/opentelemetry-sdk-trace-base/package.json b/packages/opentelemetry-sdk-trace-base/package.json index 55ebaf5c23..273d612630 100644 --- a/packages/opentelemetry-sdk-trace-base/package.json +++ b/packages/opentelemetry-sdk-trace-base/package.json @@ -65,7 +65,7 @@ }, "devDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", diff --git a/packages/opentelemetry-sdk-trace-node/package.json b/packages/opentelemetry-sdk-trace-node/package.json index cdee11074e..4b5c7e9786 100644 --- a/packages/opentelemetry-sdk-trace-node/package.json +++ b/packages/opentelemetry-sdk-trace-node/package.json @@ -48,7 +48,7 @@ "@opentelemetry/api": ">=1.0.0 <1.10.0", "@opentelemetry/resources": "1.26.0", "@opentelemetry/semantic-conventions": "1.27.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/semver": "7.5.8", "@types/sinon": "17.0.3", diff --git a/packages/opentelemetry-sdk-trace-web/package.json b/packages/opentelemetry-sdk-trace-web/package.json index 6beb69fde1..af7c50fae3 100644 --- a/packages/opentelemetry-sdk-trace-web/package.json +++ b/packages/opentelemetry-sdk-trace-web/package.json @@ -61,7 +61,7 @@ "@opentelemetry/propagator-b3": "1.26.0", "@opentelemetry/resources": "1.26.0", "@types/jquery": "3.5.30", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "@types/webpack-env": "1.16.3", diff --git a/packages/opentelemetry-shim-opentracing/package.json b/packages/opentelemetry-shim-opentracing/package.json index b86af4e300..4c2d9ccb1c 100644 --- a/packages/opentelemetry-shim-opentracing/package.json +++ b/packages/opentelemetry-shim-opentracing/package.json @@ -46,7 +46,7 @@ "@opentelemetry/propagator-b3": "1.26.0", "@opentelemetry/propagator-jaeger": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "cross-var": "1.1.0", "lerna": "6.6.2", diff --git a/packages/propagator-aws-xray/package.json b/packages/propagator-aws-xray/package.json index fb6a041eab..6d457caa99 100644 --- a/packages/propagator-aws-xray/package.json +++ b/packages/propagator-aws-xray/package.json @@ -57,7 +57,7 @@ }, "devDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/webpack-env": "1.16.3", "babel-plugin-istanbul": "7.0.0", diff --git a/packages/sdk-metrics/package.json b/packages/sdk-metrics/package.json index 4bda0b890b..c29543a223 100644 --- a/packages/sdk-metrics/package.json +++ b/packages/sdk-metrics/package.json @@ -57,7 +57,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": ">=1.3.0 <1.10.0", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "babel-plugin-istanbul": "7.0.0", diff --git a/semantic-conventions/package.json b/semantic-conventions/package.json index 5d9e471d1f..8e64d9c9cf 100644 --- a/semantic-conventions/package.json +++ b/semantic-conventions/package.json @@ -76,7 +76,7 @@ "@size-limit/file": "^11.0.1", "@size-limit/time": "^11.0.1", "@size-limit/webpack": "^11.0.1", - "@types/mocha": "10.0.7", + "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", "cross-var": "1.1.0", From 9de31518e76a38050f1a5676124fffd259085263 Mon Sep 17 00:00:00 2001 From: Marc Pichler Date: Wed, 25 Sep 2024 16:32:43 +0200 Subject: [PATCH 09/20] fix(events,logs): drop outdated API from devDependencies and align types (#5013) --- experimental/CHANGELOG.md | 3 + experimental/packages/sdk-events/package.json | 2 - experimental/packages/sdk-logs/package.json | 1 - .../packages/sdk-logs/src/LogRecord.ts | 4 +- package-lock.json | 74 +------------------ 5 files changed, 8 insertions(+), 76 deletions(-) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 13454c6eb4..e1ba48d160 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -19,6 +19,9 @@ All notable changes to experimental packages in this project will be documented * fix(sampler-jaeger-remote): fixes an issue where package could emit unhandled promise rejections @Just-Sieb * fix(otlp-grpc-exporter-base): default compression to `'none'` if env vars `OTEL_EXPORTER_OTLP_TRACES_COMPRESSION` and `OTEL_EXPORTER_OTLP_COMPRESSION` are falsy @sjvans +* fix(sdk-events): remove devDependencies to old `@opentelemetry/api-logs@0.52.0`, `@opentelemetry/api-events@0.52.0` packages [#5013](https://github.com/open-telemetry/opentelemetry-js/pull/5013) @pichlermarc +* fix(sdk-logs): remove devDependencies to old `@opentelemetry/api-logs@0.52.0` [#5013](https://github.com/open-telemetry/opentelemetry-js/pull/5013) @pichlermarc +* fix(sdk-logs): align LogRecord#setAttribute type with types from `@opentelemetry/api-logs@0.53.0` [#5013](https://github.com/open-telemetry/opentelemetry-js/pull/5013) @pichlermarc ### :books: (Refine Doc) diff --git a/experimental/packages/sdk-events/package.json b/experimental/packages/sdk-events/package.json index 6ea9047708..03c72bb8ad 100644 --- a/experimental/packages/sdk-events/package.json +++ b/experimental/packages/sdk-events/package.json @@ -68,8 +68,6 @@ "devDependencies": { "@babel/core": "7.25.2", "@opentelemetry/api": "1.9.0", - "@opentelemetry/api-events": "0.52.0", - "@opentelemetry/api-logs": "0.52.0", "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "10.0.20", diff --git a/experimental/packages/sdk-logs/package.json b/experimental/packages/sdk-logs/package.json index a7938ee0d2..a9d09bec30 100644 --- a/experimental/packages/sdk-logs/package.json +++ b/experimental/packages/sdk-logs/package.json @@ -74,7 +74,6 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": ">=1.4.0 <1.10.0", - "@opentelemetry/api-logs": "0.52.0", "@opentelemetry/resources_1.9.0": "npm:@opentelemetry/resources@1.9.0", "@types/mocha": "10.0.8", "@types/node": "18.6.5", diff --git a/experimental/packages/sdk-logs/src/LogRecord.ts b/experimental/packages/sdk-logs/src/LogRecord.ts index e7704ec389..f678648e75 100644 --- a/experimental/packages/sdk-logs/src/LogRecord.ts +++ b/experimental/packages/sdk-logs/src/LogRecord.ts @@ -26,7 +26,7 @@ import type { IResource } from '@opentelemetry/resources'; import type { ReadableLogRecord } from './export/ReadableLogRecord'; import type { LogRecordLimits } from './types'; -import { LogAttributes, LogBody } from '@opentelemetry/api-logs'; +import { AnyValue, LogAttributes, LogBody } from '@opentelemetry/api-logs'; import { LoggerProviderSharedState } from './internal/LoggerProviderSharedState'; export class LogRecord implements ReadableLogRecord { @@ -112,7 +112,7 @@ export class LogRecord implements ReadableLogRecord { this.setAttributes(attributes); } - public setAttribute(key: string, value?: LogAttributes | AttributeValue) { + public setAttribute(key: string, value?: AnyValue) { if (this._isLogRecordReadonly()) { return this; } diff --git a/package-lock.json b/package-lock.json index 3978ff726b..cf2acf603b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1413,8 +1413,6 @@ "devDependencies": { "@babel/core": "7.25.2", "@opentelemetry/api": "1.9.0", - "@opentelemetry/api-events": "0.52.0", - "@opentelemetry/api-logs": "0.52.0", "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "10.0.20", @@ -1445,31 +1443,6 @@ "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, - "experimental/packages/sdk-events/node_modules/@opentelemetry/api-events": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-events/-/api-events-0.52.0.tgz", - "integrity": "sha512-+LdOC1OK9tINoj6KQT0FZkX3enQElzLkuwAbzF7Lrdp7x7XrhQFhMz7PwfTYCgnVDOqc7pRGw0jIfmj+vJ5t4g==", - "dev": true, - "dependencies": { - "@opentelemetry/api": "^1.0.0", - "@opentelemetry/api-logs": "0.52.0" - }, - "engines": { - "node": ">=14" - } - }, - "experimental/packages/sdk-events/node_modules/@opentelemetry/api-logs": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.52.0.tgz", - "integrity": "sha512-HxjD7xH9iAE4OyhNaaSec65i1H6QZYBWSwWkowFfsc5YAcDvJG30/J1sRKXEQqdmUcKTXEAnA66UciqZha/4+Q==", - "dev": true, - "dependencies": { - "@opentelemetry/api": "^1.0.0" - }, - "engines": { - "node": ">=14" - } - }, "experimental/packages/sdk-events/node_modules/@types/sinon": { "version": "10.0.20", "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.20.tgz", @@ -1492,7 +1465,6 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": ">=1.4.0 <1.10.0", - "@opentelemetry/api-logs": "0.52.0", "@opentelemetry/resources_1.9.0": "npm:@opentelemetry/resources@1.9.0", "@types/mocha": "10.0.8", "@types/node": "18.6.5", @@ -1531,18 +1503,6 @@ "node": ">=8.0.0" } }, - "experimental/packages/sdk-logs/node_modules/@opentelemetry/api-logs": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.52.0.tgz", - "integrity": "sha512-HxjD7xH9iAE4OyhNaaSec65i1H6QZYBWSwWkowFfsc5YAcDvJG30/J1sRKXEQqdmUcKTXEAnA66UciqZha/4+Q==", - "dev": true, - "dependencies": { - "@opentelemetry/api": "^1.0.0" - }, - "engines": { - "node": ">=14" - } - }, "experimental/packages/sdk-logs/node_modules/@opentelemetry/resources_1.9.0": { "name": "@opentelemetry/resources", "version": "1.9.0", @@ -33675,8 +33635,8 @@ "requires": { "@babel/core": "7.25.2", "@opentelemetry/api": "1.9.0", - "@opentelemetry/api-events": "0.52.0", - "@opentelemetry/api-logs": "0.52.0", + "@opentelemetry/api-events": "0.53.0", + "@opentelemetry/api-logs": "0.53.0", "@opentelemetry/sdk-logs": "0.53.0", "@types/mocha": "10.0.8", "@types/node": "18.6.5", @@ -33702,25 +33662,6 @@ "webpack-merge": "5.10.0" }, "dependencies": { - "@opentelemetry/api-events": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-events/-/api-events-0.52.0.tgz", - "integrity": "sha512-+LdOC1OK9tINoj6KQT0FZkX3enQElzLkuwAbzF7Lrdp7x7XrhQFhMz7PwfTYCgnVDOqc7pRGw0jIfmj+vJ5t4g==", - "dev": true, - "requires": { - "@opentelemetry/api": "^1.0.0", - "@opentelemetry/api-logs": "0.52.0" - } - }, - "@opentelemetry/api-logs": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.52.0.tgz", - "integrity": "sha512-HxjD7xH9iAE4OyhNaaSec65i1H6QZYBWSwWkowFfsc5YAcDvJG30/J1sRKXEQqdmUcKTXEAnA66UciqZha/4+Q==", - "dev": true, - "requires": { - "@opentelemetry/api": "^1.0.0" - } - }, "@types/sinon": { "version": "10.0.20", "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.20.tgz", @@ -33738,7 +33679,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@opentelemetry/api": ">=1.4.0 <1.10.0", - "@opentelemetry/api-logs": "0.52.0", + "@opentelemetry/api-logs": "0.53.0", "@opentelemetry/core": "1.26.0", "@opentelemetry/resources": "1.26.0", "@opentelemetry/resources_1.9.0": "npm:@opentelemetry/resources@1.9.0", @@ -33770,15 +33711,6 @@ "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==", "dev": true }, - "@opentelemetry/api-logs": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.52.0.tgz", - "integrity": "sha512-HxjD7xH9iAE4OyhNaaSec65i1H6QZYBWSwWkowFfsc5YAcDvJG30/J1sRKXEQqdmUcKTXEAnA66UciqZha/4+Q==", - "dev": true, - "requires": { - "@opentelemetry/api": "^1.0.0" - } - }, "@opentelemetry/resources_1.9.0": { "version": "npm:@opentelemetry/resources@1.9.0", "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.9.0.tgz", From a5dade5462bcc550b67e15ed6da7d867166dc998 Mon Sep 17 00:00:00 2001 From: Richard Versteeg Date: Thu, 26 Sep 2024 16:00:40 +0200 Subject: [PATCH 10/20] fix(opentelemetry-core): confusing log extract of composite propagator (#5017) --- CHANGELOG.md | 1 + packages/opentelemetry-core/src/propagation/composite.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f89cae9e1..cb6a91648c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ For semantic convention package changes, see the [semconv CHANGELOG](packages/se * fix(sdk-trace-base): avoid keeping non-string `status.message` on `Span#setStatus()` [#4999](https://github.com/open-telemetry/opentelemetry-js/pull/4999) @pichlermarc * fix(sdk-metrics): Add missing catch and handle error in promise of `PeriodicExportingMetricReader` [#5006](https://github.com/open-telemetry/opentelemetry-js/pull/5006) @jj22ee +* fix(opentelemetry-core): confusing log extract of composite propagator [#5017](https://github.com/open-telemetry/opentelemetry-js/pull/5017) @rv2673 ### :books: (Refine Doc) diff --git a/packages/opentelemetry-core/src/propagation/composite.ts b/packages/opentelemetry-core/src/propagation/composite.ts index 245507a110..0654dfa031 100644 --- a/packages/opentelemetry-core/src/propagation/composite.ts +++ b/packages/opentelemetry-core/src/propagation/composite.ts @@ -91,7 +91,7 @@ export class CompositePropagator implements TextMapPropagator { return propagator.extract(ctx, carrier, getter); } catch (err) { diag.warn( - `Failed to inject with ${propagator.constructor.name}. Err: ${err.message}` + `Failed to extract with ${propagator.constructor.name}. Err: ${err.message}` ); } return ctx; From 2f919a43fd2d8067d39d573c6e03b830dee516f0 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Thu, 26 Sep 2024 16:46:13 +0200 Subject: [PATCH 11/20] chore(deps): update dependency @types/jquery to v3.5.31 (#5018) --- package-lock.json | 16 ++++++++-------- .../opentelemetry-sdk-trace-web/package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index cf2acf603b..7c2b6ecdd6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6874,9 +6874,9 @@ } }, "node_modules/@types/jquery": { - "version": "3.5.30", - "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.30.tgz", - "integrity": "sha512-nbWKkkyb919DOUxjmRVk8vwtDb0/k8FKncmUKFi+NY+QXqWltooxTrswvz4LspQwxvLdvzBN1TImr6cw3aQx2A==", + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.31.tgz", + "integrity": "sha512-rf/iB+cPJ/YZfMwr+FVuQbm7IaWC4y3FVYfVDxRGqmUCFjjPII0HWaP0vTPJGp6m4o13AXySCcMbWfrWtBFAKw==", "dev": true, "dependencies": { "@types/sizzle": "*" @@ -29129,7 +29129,7 @@ "@opentelemetry/context-zone": "1.26.0", "@opentelemetry/propagator-b3": "1.26.0", "@opentelemetry/resources": "1.26.0", - "@types/jquery": "3.5.30", + "@types/jquery": "3.5.31", "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", @@ -33871,7 +33871,7 @@ "@opentelemetry/resources": "1.26.0", "@opentelemetry/sdk-trace-base": "1.26.0", "@opentelemetry/semantic-conventions": "1.27.0", - "@types/jquery": "3.5.30", + "@types/jquery": "3.5.31", "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", @@ -34779,9 +34779,9 @@ } }, "@types/jquery": { - "version": "3.5.30", - "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.30.tgz", - "integrity": "sha512-nbWKkkyb919DOUxjmRVk8vwtDb0/k8FKncmUKFi+NY+QXqWltooxTrswvz4LspQwxvLdvzBN1TImr6cw3aQx2A==", + "version": "3.5.31", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.31.tgz", + "integrity": "sha512-rf/iB+cPJ/YZfMwr+FVuQbm7IaWC4y3FVYfVDxRGqmUCFjjPII0HWaP0vTPJGp6m4o13AXySCcMbWfrWtBFAKw==", "dev": true, "requires": { "@types/sizzle": "*" diff --git a/packages/opentelemetry-sdk-trace-web/package.json b/packages/opentelemetry-sdk-trace-web/package.json index af7c50fae3..16b0002bb2 100644 --- a/packages/opentelemetry-sdk-trace-web/package.json +++ b/packages/opentelemetry-sdk-trace-web/package.json @@ -60,7 +60,7 @@ "@opentelemetry/context-zone": "1.26.0", "@opentelemetry/propagator-b3": "1.26.0", "@opentelemetry/resources": "1.26.0", - "@types/jquery": "3.5.30", + "@types/jquery": "3.5.31", "@types/mocha": "10.0.8", "@types/node": "18.6.5", "@types/sinon": "17.0.3", From 466140bdf8c29dd36f666ee24db9fadbe9ff8041 Mon Sep 17 00:00:00 2001 From: David Luna Date: Fri, 27 Sep 2024 09:42:21 +0200 Subject: [PATCH 12/20] refactor(sdk-metrics): replace `MetricsAttributes` with `Attributes` (#5021) --- CHANGELOG.md | 1 + packages/sdk-metrics/src/Instruments.ts | 12 ++++++------ packages/sdk-metrics/src/ObservableResult.ts | 6 +++--- packages/sdk-metrics/src/aggregator/types.ts | 4 ++-- .../AlignedHistogramBucketExemplarReservoir.ts | 6 +++--- .../src/exemplar/AlwaysSampleExemplarFilter.ts | 4 ++-- packages/sdk-metrics/src/exemplar/Exemplar.ts | 4 ++-- .../sdk-metrics/src/exemplar/ExemplarFilter.ts | 6 +++--- .../src/exemplar/ExemplarReservoir.ts | 16 ++++++++-------- .../src/exemplar/NeverSampleExemplarFilter.ts | 4 ++-- .../exemplar/SimpleFixedSizeExemplarReservoir.ts | 6 +++--- .../src/exemplar/WithTraceExemplarFilter.ts | 4 ++-- packages/sdk-metrics/src/export/MetricData.ts | 4 ++-- .../src/state/DeltaMetricProcessor.ts | 4 ++-- packages/sdk-metrics/src/state/HashMap.ts | 4 ++-- .../src/state/MultiWritableMetricStorage.ts | 4 ++-- .../sdk-metrics/src/state/SyncMetricStorage.ts | 4 ++-- .../src/state/WritableMetricStorage.ts | 4 ++-- packages/sdk-metrics/src/types.ts | 6 ++---- packages/sdk-metrics/src/utils.ts | 6 +++--- packages/sdk-metrics/test/MeterProvider.test.ts | 2 +- packages/sdk-metrics/test/state/HashMap.test.ts | 8 ++++---- .../state/MultiWritableMetricStorage.test.ts | 4 ++-- packages/sdk-metrics/test/util.ts | 9 ++++----- packages/sdk-metrics/test/utils.test.ts | 4 ++-- 25 files changed, 67 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb6a91648c..4ac5bb96d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ For semantic convention package changes, see the [semconv CHANGELOG](packages/se * deps: set `@opentelemetry/api` dependency min version to 1.3.0 in `examples`, `experimental/packages`, `integration-tests` and `selenium-tests` [#4992](https://github.com/open-telemetry/opentelemetry-js/pull/4992) +* refactor(sdk-metrics): replace `MetricsAttributes` with `Attributes` [#5021](https://github.com/open-telemetry/opentelemetry-js/pull/5021) @david-luna ## 1.26.0 diff --git a/packages/sdk-metrics/src/Instruments.ts b/packages/sdk-metrics/src/Instruments.ts index 0113abd190..7e27a56454 100644 --- a/packages/sdk-metrics/src/Instruments.ts +++ b/packages/sdk-metrics/src/Instruments.ts @@ -18,7 +18,7 @@ import { context as contextApi, diag, Context, - MetricAttributes, + Attributes, ValueType, UpDownCounter, Counter, @@ -46,7 +46,7 @@ export class SyncInstrument { protected _record( value: number, - attributes: MetricAttributes = {}, + attributes: Attributes = {}, context: Context = contextApi.active() ) { if (typeof value !== 'number') { @@ -87,7 +87,7 @@ export class UpDownCounterInstrument /** * Increment value of counter by the input. Inputs may be negative. */ - add(value: number, attributes?: MetricAttributes, ctx?: Context): void { + add(value: number, attributes?: Attributes, ctx?: Context): void { this._record(value, attributes, ctx); } } @@ -99,7 +99,7 @@ export class CounterInstrument extends SyncInstrument implements Counter { /** * Increment value of counter by the input. Inputs may not be negative. */ - add(value: number, attributes?: MetricAttributes, ctx?: Context): void { + add(value: number, attributes?: Attributes, ctx?: Context): void { if (value < 0) { diag.warn( `negative value provided to counter ${this._descriptor.name}: ${value}` @@ -118,7 +118,7 @@ export class GaugeInstrument extends SyncInstrument implements Gauge { /** * Records a measurement. */ - record(value: number, attributes?: MetricAttributes, ctx?: Context): void { + record(value: number, attributes?: Attributes, ctx?: Context): void { this._record(value, attributes, ctx); } } @@ -130,7 +130,7 @@ export class HistogramInstrument extends SyncInstrument implements Histogram { /** * Records a measurement. Value of the measurement must not be negative. */ - record(value: number, attributes?: MetricAttributes, ctx?: Context): void { + record(value: number, attributes?: Attributes, ctx?: Context): void { if (value < 0) { diag.warn( `negative value provided to histogram ${this._descriptor.name}: ${value}` diff --git a/packages/sdk-metrics/src/ObservableResult.ts b/packages/sdk-metrics/src/ObservableResult.ts index c9a7b202ea..842a270e59 100644 --- a/packages/sdk-metrics/src/ObservableResult.ts +++ b/packages/sdk-metrics/src/ObservableResult.ts @@ -17,7 +17,7 @@ import { diag, ObservableResult, - MetricAttributes, + Attributes, ValueType, BatchObservableResult, Observable, @@ -42,7 +42,7 @@ export class ObservableResultImpl implements ObservableResult { /** * Observe a measurement of the value associated with the given attributes. */ - observe(value: number, attributes: MetricAttributes = {}): void { + observe(value: number, attributes: Attributes = {}): void { if (typeof value !== 'number') { diag.warn( `non-number value provided to metric ${this._instrumentName}: ${value}` @@ -78,7 +78,7 @@ export class BatchObservableResultImpl implements BatchObservableResult { observe( metric: Observable, value: number, - attributes: MetricAttributes = {} + attributes: Attributes = {} ): void { if (!isObservableInstrument(metric)) { return; diff --git a/packages/sdk-metrics/src/aggregator/types.ts b/packages/sdk-metrics/src/aggregator/types.ts index afbc35464b..c97aa60865 100644 --- a/packages/sdk-metrics/src/aggregator/types.ts +++ b/packages/sdk-metrics/src/aggregator/types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { HrTime, MetricAttributes } from '@opentelemetry/api'; +import { HrTime, Attributes } from '@opentelemetry/api'; import { AggregationTemporality } from '../export/AggregationTemporality'; import { MetricData, MetricDescriptor } from '../export/MetricData'; import { Maybe } from '../utils'; @@ -88,7 +88,7 @@ export interface Accumulation { record(value: number): void; } -export type AccumulationRecord = [MetricAttributes, T]; +export type AccumulationRecord = [Attributes, T]; /** * Base interface for aggregators. Aggregators are responsible for holding diff --git a/packages/sdk-metrics/src/exemplar/AlignedHistogramBucketExemplarReservoir.ts b/packages/sdk-metrics/src/exemplar/AlignedHistogramBucketExemplarReservoir.ts index 11aab6da23..b7b9c471fd 100644 --- a/packages/sdk-metrics/src/exemplar/AlignedHistogramBucketExemplarReservoir.ts +++ b/packages/sdk-metrics/src/exemplar/AlignedHistogramBucketExemplarReservoir.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Context, HrTime, MetricAttributes } from '@opentelemetry/api'; +import { Context, HrTime, Attributes } from '@opentelemetry/api'; import { FixedSizeExemplarReservoirBase } from './ExemplarReservoir'; /** @@ -32,7 +32,7 @@ export class AlignedHistogramBucketExemplarReservoir extends FixedSizeExemplarRe private _findBucketIndex( value: number, _timestamp: HrTime, - _attributes: MetricAttributes, + _attributes: Attributes, _ctx: Context ) { for (let i = 0; i < this._boundaries.length; i++) { @@ -46,7 +46,7 @@ export class AlignedHistogramBucketExemplarReservoir extends FixedSizeExemplarRe offer( value: number, timestamp: HrTime, - attributes: MetricAttributes, + attributes: Attributes, ctx: Context ): void { const index = this._findBucketIndex(value, timestamp, attributes, ctx); diff --git a/packages/sdk-metrics/src/exemplar/AlwaysSampleExemplarFilter.ts b/packages/sdk-metrics/src/exemplar/AlwaysSampleExemplarFilter.ts index 9a4511e669..b2e7fb161d 100644 --- a/packages/sdk-metrics/src/exemplar/AlwaysSampleExemplarFilter.ts +++ b/packages/sdk-metrics/src/exemplar/AlwaysSampleExemplarFilter.ts @@ -14,14 +14,14 @@ * limitations under the License. */ -import { Context, HrTime, MetricAttributes } from '@opentelemetry/api'; +import { Context, HrTime, Attributes } from '@opentelemetry/api'; import { ExemplarFilter } from './ExemplarFilter'; export class AlwaysSampleExemplarFilter implements ExemplarFilter { shouldSample( _value: number, _timestamp: HrTime, - _attributes: MetricAttributes, + _attributes: Attributes, _ctx: Context ): boolean { return true; diff --git a/packages/sdk-metrics/src/exemplar/Exemplar.ts b/packages/sdk-metrics/src/exemplar/Exemplar.ts index d98246dc49..4de413b102 100644 --- a/packages/sdk-metrics/src/exemplar/Exemplar.ts +++ b/packages/sdk-metrics/src/exemplar/Exemplar.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { HrTime, MetricAttributes } from '@opentelemetry/api'; +import { HrTime, Attributes } from '@opentelemetry/api'; /** * A representation of an exemplar, which is a sample input measurement. @@ -26,7 +26,7 @@ export type Exemplar = { // The set of key/value pairs that were filtered out by the aggregator, but // recorded alongside the original measurement. Only key/value pairs that were // filtered out by the aggregator should be included - filteredAttributes: MetricAttributes; + filteredAttributes: Attributes; // The value of the measurement that was recorded. value: number; diff --git a/packages/sdk-metrics/src/exemplar/ExemplarFilter.ts b/packages/sdk-metrics/src/exemplar/ExemplarFilter.ts index 78b8ca0f39..ad5a79d9ed 100644 --- a/packages/sdk-metrics/src/exemplar/ExemplarFilter.ts +++ b/packages/sdk-metrics/src/exemplar/ExemplarFilter.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Context, HrTime, MetricAttributes } from '@opentelemetry/api'; +import { Context, HrTime, Attributes } from '@opentelemetry/api'; /** * This interface represents a ExemplarFilter. Exemplar filters are @@ -27,13 +27,13 @@ export interface ExemplarFilter { * * @param value The value of the measurement * @param timestamp A timestamp that best represents when the measurement was taken - * @param attributes The complete set of MetricAttributes of the measurement + * @param attributes The complete set of Attributes of the measurement * @param ctx The Context of the measurement */ shouldSample( value: number, timestamp: HrTime, - attributes: MetricAttributes, + attributes: Attributes, ctx: Context ): boolean; } diff --git a/packages/sdk-metrics/src/exemplar/ExemplarReservoir.ts b/packages/sdk-metrics/src/exemplar/ExemplarReservoir.ts index ef1e8d14e6..229f4991bd 100644 --- a/packages/sdk-metrics/src/exemplar/ExemplarReservoir.ts +++ b/packages/sdk-metrics/src/exemplar/ExemplarReservoir.ts @@ -19,7 +19,7 @@ import { HrTime, isSpanContextValid, trace, - MetricAttributes, + Attributes, } from '@opentelemetry/api'; import { Exemplar } from './Exemplar'; @@ -31,7 +31,7 @@ export interface ExemplarReservoir { offer( value: number, timestamp: HrTime, - attributes: MetricAttributes, + attributes: Attributes, ctx: Context ): void; /** @@ -43,12 +43,12 @@ export interface ExemplarReservoir { * @returns a list of {@link Exemplar}s. Returned exemplars contain the attributes that were filtered out by the * aggregator, but recorded alongside the original measurement. */ - collect(pointAttributes: MetricAttributes): Exemplar[]; + collect(pointAttributes: Attributes): Exemplar[]; } class ExemplarBucket { private value: number = 0; - private attributes: MetricAttributes = {}; + private attributes: Attributes = {}; private timestamp: HrTime = [0, 0]; private spanId?: string; private traceId?: string; @@ -57,7 +57,7 @@ class ExemplarBucket { offer( value: number, timestamp: HrTime, - attributes: MetricAttributes, + attributes: Attributes, ctx: Context ) { this.value = value; @@ -71,7 +71,7 @@ class ExemplarBucket { this._offered = true; } - collect(pointAttributes: MetricAttributes): Exemplar | null { + collect(pointAttributes: Attributes): Exemplar | null { if (!this._offered) return null; const currentAttributes = this.attributes; // filter attributes @@ -114,7 +114,7 @@ export abstract class FixedSizeExemplarReservoirBase abstract offer( value: number, timestamp: HrTime, - attributes: MetricAttributes, + attributes: Attributes, ctx: Context ): void; @@ -127,7 +127,7 @@ export abstract class FixedSizeExemplarReservoirBase */ protected reset(): void {} - collect(pointAttributes: MetricAttributes): Exemplar[] { + collect(pointAttributes: Attributes): Exemplar[] { const exemplars: Exemplar[] = []; this._reservoirStorage.forEach(storageItem => { const res = storageItem.collect(pointAttributes); diff --git a/packages/sdk-metrics/src/exemplar/NeverSampleExemplarFilter.ts b/packages/sdk-metrics/src/exemplar/NeverSampleExemplarFilter.ts index 19697ba3a1..8df455d41e 100644 --- a/packages/sdk-metrics/src/exemplar/NeverSampleExemplarFilter.ts +++ b/packages/sdk-metrics/src/exemplar/NeverSampleExemplarFilter.ts @@ -14,14 +14,14 @@ * limitations under the License. */ -import { Context, HrTime, MetricAttributes } from '@opentelemetry/api'; +import { Context, HrTime, Attributes } from '@opentelemetry/api'; import { ExemplarFilter } from './ExemplarFilter'; export class NeverSampleExemplarFilter implements ExemplarFilter { shouldSample( _value: number, _timestamp: HrTime, - _attributes: MetricAttributes, + _attributes: Attributes, _ctx: Context ): boolean { return false; diff --git a/packages/sdk-metrics/src/exemplar/SimpleFixedSizeExemplarReservoir.ts b/packages/sdk-metrics/src/exemplar/SimpleFixedSizeExemplarReservoir.ts index f51a979376..659f9cee50 100644 --- a/packages/sdk-metrics/src/exemplar/SimpleFixedSizeExemplarReservoir.ts +++ b/packages/sdk-metrics/src/exemplar/SimpleFixedSizeExemplarReservoir.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Context, HrTime, MetricAttributes } from '@opentelemetry/api'; +import { Context, HrTime, Attributes } from '@opentelemetry/api'; import { FixedSizeExemplarReservoirBase } from './ExemplarReservoir'; /** @@ -37,7 +37,7 @@ export class SimpleFixedSizeExemplarReservoir extends FixedSizeExemplarReservoir private _findBucketIndex( _value: number, _timestamp: HrTime, - _attributes: MetricAttributes, + _attributes: Attributes, _ctx: Context ) { if (this._numMeasurementsSeen < this._size) @@ -49,7 +49,7 @@ export class SimpleFixedSizeExemplarReservoir extends FixedSizeExemplarReservoir offer( value: number, timestamp: HrTime, - attributes: MetricAttributes, + attributes: Attributes, ctx: Context ): void { const index = this._findBucketIndex(value, timestamp, attributes, ctx); diff --git a/packages/sdk-metrics/src/exemplar/WithTraceExemplarFilter.ts b/packages/sdk-metrics/src/exemplar/WithTraceExemplarFilter.ts index 0977281d85..3683461257 100644 --- a/packages/sdk-metrics/src/exemplar/WithTraceExemplarFilter.ts +++ b/packages/sdk-metrics/src/exemplar/WithTraceExemplarFilter.ts @@ -20,7 +20,7 @@ import { isSpanContextValid, trace, TraceFlags, - MetricAttributes, + Attributes, } from '@opentelemetry/api'; import { ExemplarFilter } from './ExemplarFilter'; @@ -28,7 +28,7 @@ export class WithTraceExemplarFilter implements ExemplarFilter { shouldSample( value: number, timestamp: HrTime, - attributes: MetricAttributes, + attributes: Attributes, ctx: Context ): boolean { const spanContext = trace.getSpanContext(ctx); diff --git a/packages/sdk-metrics/src/export/MetricData.ts b/packages/sdk-metrics/src/export/MetricData.ts index d4ad0c7ad2..4f39db0505 100644 --- a/packages/sdk-metrics/src/export/MetricData.ts +++ b/packages/sdk-metrics/src/export/MetricData.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { HrTime, MetricAttributes, ValueType } from '@opentelemetry/api'; +import { HrTime, Attributes, ValueType } from '@opentelemetry/api'; import { InstrumentationScope } from '@opentelemetry/core'; import { IResource } from '@opentelemetry/resources'; import { InstrumentType } from '../InstrumentDescriptor'; @@ -159,7 +159,7 @@ export interface DataPoint { /** * The attributes associated with this DataPoint. */ - readonly attributes: MetricAttributes; + readonly attributes: Attributes; /** * The value for this DataPoint. The type of the value is indicated by the * {@link DataPointType}. diff --git a/packages/sdk-metrics/src/state/DeltaMetricProcessor.ts b/packages/sdk-metrics/src/state/DeltaMetricProcessor.ts index e9b16f0e03..2764727de2 100644 --- a/packages/sdk-metrics/src/state/DeltaMetricProcessor.ts +++ b/packages/sdk-metrics/src/state/DeltaMetricProcessor.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Context, HrTime, MetricAttributes } from '@opentelemetry/api'; +import { Context, HrTime, Attributes } from '@opentelemetry/api'; import { Maybe } from '../utils'; import { Accumulation, Aggregator } from '../aggregator/types'; import { AttributeHashMap } from './HashMap'; @@ -36,7 +36,7 @@ export class DeltaMetricProcessor> { record( value: number, - attributes: MetricAttributes, + attributes: Attributes, _context: Context, collectionTime: HrTime ) { diff --git a/packages/sdk-metrics/src/state/HashMap.ts b/packages/sdk-metrics/src/state/HashMap.ts index 36011d6675..1036b66f77 100644 --- a/packages/sdk-metrics/src/state/HashMap.ts +++ b/packages/sdk-metrics/src/state/HashMap.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { MetricAttributes } from '@opentelemetry/api'; +import { Attributes } from '@opentelemetry/api'; import { hashAttributes } from '../utils'; export interface Hash { @@ -84,7 +84,7 @@ export class HashMap { } export class AttributeHashMap extends HashMap< - MetricAttributes, + Attributes, ValueType, string > { diff --git a/packages/sdk-metrics/src/state/MultiWritableMetricStorage.ts b/packages/sdk-metrics/src/state/MultiWritableMetricStorage.ts index 1cf51f6728..9a60a0573c 100644 --- a/packages/sdk-metrics/src/state/MultiWritableMetricStorage.ts +++ b/packages/sdk-metrics/src/state/MultiWritableMetricStorage.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Context, HrTime, MetricAttributes } from '@opentelemetry/api'; +import { Context, HrTime, Attributes } from '@opentelemetry/api'; import { WritableMetricStorage } from './WritableMetricStorage'; /** @@ -25,7 +25,7 @@ export class MultiMetricStorage implements WritableMetricStorage { record( value: number, - attributes: MetricAttributes, + attributes: Attributes, context: Context, recordTime: HrTime ) { diff --git a/packages/sdk-metrics/src/state/SyncMetricStorage.ts b/packages/sdk-metrics/src/state/SyncMetricStorage.ts index bb546e1271..2e97d20d8d 100644 --- a/packages/sdk-metrics/src/state/SyncMetricStorage.ts +++ b/packages/sdk-metrics/src/state/SyncMetricStorage.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Context, HrTime, MetricAttributes } from '@opentelemetry/api'; +import { Context, HrTime, Attributes } from '@opentelemetry/api'; import { WritableMetricStorage } from './WritableMetricStorage'; import { Accumulation, Aggregator } from '../aggregator/types'; import { InstrumentDescriptor } from '../InstrumentDescriptor'; @@ -54,7 +54,7 @@ export class SyncMetricStorage> record( value: number, - attributes: MetricAttributes, + attributes: Attributes, context: Context, recordTime: HrTime ) { diff --git a/packages/sdk-metrics/src/state/WritableMetricStorage.ts b/packages/sdk-metrics/src/state/WritableMetricStorage.ts index 223f34d9e9..6d738156ac 100644 --- a/packages/sdk-metrics/src/state/WritableMetricStorage.ts +++ b/packages/sdk-metrics/src/state/WritableMetricStorage.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Context, HrTime, MetricAttributes } from '@opentelemetry/api'; +import { Context, HrTime, Attributes } from '@opentelemetry/api'; import { AttributeHashMap } from './HashMap'; /** @@ -27,7 +27,7 @@ export interface WritableMetricStorage { /** Records a measurement. */ record( value: number, - attributes: MetricAttributes, + attributes: Attributes, context: Context, recordTime: HrTime ): void; diff --git a/packages/sdk-metrics/src/types.ts b/packages/sdk-metrics/src/types.ts index d89aa22399..2ad1cefcfd 100644 --- a/packages/sdk-metrics/src/types.ts +++ b/packages/sdk-metrics/src/types.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Context, MetricAttributes } from '@opentelemetry/api'; +import { Context, Attributes } from '@opentelemetry/api'; export type CommonReaderOptions = { timeoutMillis?: number; @@ -29,9 +29,7 @@ export type ForceFlushOptions = CommonReaderOptions; * This is intentionally not using the API's type as it's only available from @opentelemetry/api 1.9.0 and up. * In SDK 2.0 we'll be able to bump the minimum API version and remove this workaround. */ -export interface Gauge< - AttributesTypes extends MetricAttributes = MetricAttributes, -> { +export interface Gauge { /** * Records a measurement. Value of the measurement must not be negative. */ diff --git a/packages/sdk-metrics/src/utils.ts b/packages/sdk-metrics/src/utils.ts index 8648a12361..77c9364c26 100644 --- a/packages/sdk-metrics/src/utils.ts +++ b/packages/sdk-metrics/src/utils.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { MetricAttributes } from '@opentelemetry/api'; +import { Attributes } from '@opentelemetry/api'; import { InstrumentationScope } from '@opentelemetry/core'; export type Maybe = T | undefined; @@ -25,9 +25,9 @@ export function isNotNullish(item: Maybe): item is T { /** * Converting the unordered attributes into unique identifier string. - * @param attributes user provided unordered MetricAttributes. + * @param attributes user provided unordered Attributes. */ -export function hashAttributes(attributes: MetricAttributes): string { +export function hashAttributes(attributes: Attributes): string { let keys = Object.keys(attributes); if (keys.length === 0) return ''; diff --git a/packages/sdk-metrics/test/MeterProvider.test.ts b/packages/sdk-metrics/test/MeterProvider.test.ts index 615fcffab3..f06305ad2a 100644 --- a/packages/sdk-metrics/test/MeterProvider.test.ts +++ b/packages/sdk-metrics/test/MeterProvider.test.ts @@ -189,7 +189,7 @@ describe('MeterProvider', () => { assertPartialDeepStrictEqual( resourceMetrics.scopeMetrics[0].metrics[0].dataPoints[0], { - // MetricAttributes are still there. + // Attributes are still there. attributes: { attrib1: 'attrib_value1', attrib2: 'attrib_value2', diff --git a/packages/sdk-metrics/test/state/HashMap.test.ts b/packages/sdk-metrics/test/state/HashMap.test.ts index 8ae772989b..4d387300ea 100644 --- a/packages/sdk-metrics/test/state/HashMap.test.ts +++ b/packages/sdk-metrics/test/state/HashMap.test.ts @@ -15,14 +15,14 @@ */ import * as assert from 'assert'; -import { MetricAttributes } from '@opentelemetry/api'; +import { Attributes } from '@opentelemetry/api'; import { HashMap } from '../../src/state/HashMap'; import { hashAttributes } from '../../src/utils'; describe('HashMap', () => { describe('set & get', () => { it('should get and set with attributes', () => { - const map = new HashMap(hashAttributes); + const map = new HashMap(hashAttributes); const hash = hashAttributes({ foo: 'bar' }); map.set({ foo: 'bar' }, 1); @@ -41,7 +41,7 @@ describe('HashMap', () => { describe('has', () => { it('should return if the key exists in the value map', () => { - const map = new HashMap(hashAttributes); + const map = new HashMap(hashAttributes); const hash = hashAttributes({ foo: 'bar' }); // with pinned hash code @@ -58,7 +58,7 @@ describe('HashMap', () => { describe('entries', () => { it('iterating with entries', () => { - const map = new HashMap(hashAttributes); + const map = new HashMap(hashAttributes); map.set({ foo: '1' }, 1); map.set({ foo: '2' }, 2); map.set({ foo: '3' }, 3); diff --git a/packages/sdk-metrics/test/state/MultiWritableMetricStorage.test.ts b/packages/sdk-metrics/test/state/MultiWritableMetricStorage.test.ts index f2ee109f13..ed4c125920 100644 --- a/packages/sdk-metrics/test/state/MultiWritableMetricStorage.test.ts +++ b/packages/sdk-metrics/test/state/MultiWritableMetricStorage.test.ts @@ -15,7 +15,7 @@ */ import * as api from '@opentelemetry/api'; -import { MetricAttributes } from '@opentelemetry/api'; +import { Attributes } from '@opentelemetry/api'; import { hrTime } from '@opentelemetry/core'; import * as assert from 'assert'; import { MultiMetricStorage } from '../../src/state/MultiWritableMetricStorage'; @@ -44,7 +44,7 @@ describe('MultiMetricStorage', () => { records: Measurement[] = []; record( value: number, - attributes: MetricAttributes, + attributes: Attributes, context: api.Context ): void { this.records.push({ value, attributes, context }); diff --git a/packages/sdk-metrics/test/util.ts b/packages/sdk-metrics/test/util.ts index 75d7e66f03..52d89e5169 100644 --- a/packages/sdk-metrics/test/util.ts +++ b/packages/sdk-metrics/test/util.ts @@ -17,7 +17,7 @@ import { Context, BatchObservableCallback, - MetricAttributes, + Attributes, ObservableCallback, ValueType, } from '@opentelemetry/api'; @@ -42,8 +42,7 @@ import { AggregationTemporality } from '../src/export/AggregationTemporality'; export type Measurement = { value: number; - // TODO: use common attributes - attributes: MetricAttributes; + attributes: Attributes; context?: Context; }; @@ -80,7 +79,7 @@ export const validNames = [ ]; export const commonValues: number[] = [1, -1, 1.0, Infinity, -Infinity, NaN]; -export const commonAttributes: MetricAttributes[] = [ +export const commonAttributes: Attributes[] = [ {}, { 1: '1' }, { a: '2' }, @@ -126,7 +125,7 @@ export function assertMetricData( export function assertDataPoint( actual: unknown, - attributes: MetricAttributes, + attributes: Attributes, point: Histogram | number, startTime?: HrTime, endTime?: HrTime diff --git a/packages/sdk-metrics/test/utils.test.ts b/packages/sdk-metrics/test/utils.test.ts index 03df6e863d..68116959d3 100644 --- a/packages/sdk-metrics/test/utils.test.ts +++ b/packages/sdk-metrics/test/utils.test.ts @@ -23,7 +23,7 @@ import { TimeoutError, } from '../src/utils'; import { assertRejects } from './test-utils'; -import { MetricAttributes } from '@opentelemetry/api'; +import { Attributes } from '@opentelemetry/api'; describe('utils', () => { afterEach(() => { @@ -42,7 +42,7 @@ describe('utils', () => { describe('hashAttributes', () => { it('should hash all types of attribute values', () => { - const cases: [MetricAttributes, string][] = [ + const cases: [Attributes, string][] = [ [{ string: 'bar' }, '[["string","bar"]]'], [{ number: 1 }, '[["number",1]]'], [{ false: false, true: true }, '[["false",false],["true",true]]'], From d1cd7f23d018fcc512df875124d099c34cc4a941 Mon Sep 17 00:00:00 2001 From: David Luna Date: Fri, 27 Sep 2024 12:57:58 +0200 Subject: [PATCH 13/20] refactor(instrumentation-http): replace `SpanAttributes` and `MetricAttributes` with `Attributes` (#5023) --- CHANGELOG.md | 1 + .../src/http.ts | 12 ++-- .../src/types.ts | 6 +- .../src/utils.ts | 68 +++++++++---------- .../test/functionals/http-enable.test.ts | 6 +- .../test/functionals/utils.test.ts | 19 +++--- 6 files changed, 55 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ac5bb96d0..3dc782cfe6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ For semantic convention package changes, see the [semconv CHANGELOG](packages/se * deps: set `@opentelemetry/api` dependency min version to 1.3.0 in `examples`, `experimental/packages`, `integration-tests` and `selenium-tests` [#4992](https://github.com/open-telemetry/opentelemetry-js/pull/4992) * refactor(sdk-metrics): replace `MetricsAttributes` with `Attributes` [#5021](https://github.com/open-telemetry/opentelemetry-js/pull/5021) @david-luna +* refactor(instrumentation-http): replace `SpanAttributes` and `MetricsAttributes` with `Attributes` [#5023](https://github.com/open-telemetry/opentelemetry-js/pull/5023) @david-luna ## 1.26.0 diff --git a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts index 81e56183a1..456b8d08e0 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts @@ -26,7 +26,7 @@ import { SpanStatusCode, trace, Histogram, - MetricAttributes, + Attributes, ValueType, } from '@opentelemetry/api'; import { @@ -331,7 +331,7 @@ export class HttpInstrumentation extends InstrumentationBase { const length = getContentLength(request.headers); if (length === null) return; @@ -231,13 +229,13 @@ export const setRequestContentLengthAttribute = ( /** * Adds attributes for response content-length and content-encoding HTTP headers * @param { IncomingMessage } Response object whose headers will be analyzed - * @param { SpanAttributes } SpanAttributes object to be modified + * @param { Attributes } Attributes object to be modified * * @deprecated this is for an older version of semconv. It is retained for compatibility using OTEL_SEMCONV_STABILITY_OPT_IN */ export const setResponseContentLengthAttribute = ( response: IncomingMessage, - attributes: SpanAttributes + attributes: Attributes ): void => { const length = getContentLength(response.headers); if (length === null) return; @@ -380,7 +378,7 @@ export const extractHostnameAndPort = ( /** * Returns outgoing request attributes scoped to the options passed to the request * @param {ParsedRequestOptions} requestOptions the same options used to make the request - * @param {{ component: string, hostname: string, hookAttributes?: SpanAttributes }} options used to pass data needed to create attributes + * @param {{ component: string, hostname: string, hookAttributes?: Attributes }} options used to pass data needed to create attributes * @param {SemconvStability} semconvStability determines which semconv version to use */ export const getOutgoingRequestAttributes = ( @@ -389,10 +387,10 @@ export const getOutgoingRequestAttributes = ( component: string; hostname: string; port: string | number; - hookAttributes?: SpanAttributes; + hookAttributes?: Attributes; }, semconvStability: SemconvStability -): SpanAttributes => { +): Attributes => { const hostname = options.hostname; const port = options.port; const method = requestOptions.method ?? 'GET'; @@ -404,7 +402,7 @@ export const getOutgoingRequestAttributes = ( headers, `${options.component}:` ); - const oldAttributes: SpanAttributes = { + const oldAttributes: Attributes = { [SEMATTRS_HTTP_URL]: urlFull, [SEMATTRS_HTTP_METHOD]: method, [SEMATTRS_HTTP_TARGET]: requestOptions.path || '/', @@ -446,12 +444,12 @@ export const getOutgoingRequestAttributes = ( /** * Returns outgoing request Metric attributes scoped to the request data - * @param {SpanAttributes} spanAttributes the span attributes + * @param {Attributes} spanAttributes the span attributes */ export const getOutgoingRequestMetricAttributes = ( - spanAttributes: SpanAttributes -): MetricAttributes => { - const metricAttributes: MetricAttributes = {}; + spanAttributes: Attributes +): Attributes => { + const metricAttributes: Attributes = {}; metricAttributes[SEMATTRS_HTTP_METHOD] = spanAttributes[SEMATTRS_HTTP_METHOD]; metricAttributes[SEMATTRS_NET_PEER_NAME] = spanAttributes[SEMATTRS_NET_PEER_NAME]; @@ -465,7 +463,7 @@ export const getOutgoingRequestMetricAttributes = ( */ export const setAttributesFromHttpKind = ( kind: string | undefined, - attributes: SpanAttributes + attributes: Attributes ): void => { if (kind) { attributes[SEMATTRS_HTTP_FLAVOR] = kind; @@ -485,9 +483,9 @@ export const setAttributesFromHttpKind = ( export const getOutgoingRequestAttributesOnResponse = ( response: IncomingMessage, semconvStability: SemconvStability -): SpanAttributes => { +): Attributes => { const { statusCode, statusMessage, httpVersion, socket } = response; - const oldAttributes: SpanAttributes = {}; + const oldAttributes: Attributes = {}; const stableAttributes: Attributes = {}; if (statusCode != null) { @@ -527,12 +525,12 @@ export const getOutgoingRequestAttributesOnResponse = ( /** * Returns outgoing request Metric attributes scoped to the response data - * @param {SpanAttributes} spanAttributes the span attributes + * @param {Attributes} spanAttributes the span attributes */ export const getOutgoingRequestMetricAttributesOnResponse = ( - spanAttributes: SpanAttributes -): MetricAttributes => { - const metricAttributes: MetricAttributes = {}; + spanAttributes: Attributes +): Attributes => { + const metricAttributes: Attributes = {}; metricAttributes[SEMATTRS_NET_PEER_PORT] = spanAttributes[SEMATTRS_NET_PEER_PORT]; metricAttributes[SEMATTRS_HTTP_STATUS_CODE] = @@ -694,7 +692,7 @@ export function getRemoteClientAddress( /** * Returns incoming request attributes scoped to the request data * @param {IncomingMessage} request the request object - * @param {{ component: string, serverName?: string, hookAttributes?: SpanAttributes }} options used to pass data needed to create attributes + * @param {{ component: string, serverName?: string, hookAttributes?: Attributes }} options used to pass data needed to create attributes * @param {SemconvStability} semconvStability determines which semconv version to use */ export const getIncomingRequestAttributes = ( @@ -702,10 +700,10 @@ export const getIncomingRequestAttributes = ( options: { component: 'http' | 'https'; serverName?: string; - hookAttributes?: SpanAttributes; + hookAttributes?: Attributes; semconvStability: SemconvStability; } -): SpanAttributes => { +): Attributes => { const headers = request.headers; const userAgent = headers['user-agent']; const ips = headers['x-forwarded-for']; @@ -794,13 +792,13 @@ export const getIncomingRequestAttributes = ( /** * Returns incoming request Metric attributes scoped to the request data - * @param {SpanAttributes} spanAttributes the span attributes + * @param {Attributes} spanAttributes the span attributes * @param {{ component: string }} options used to pass data needed to create attributes */ export const getIncomingRequestMetricAttributes = ( - spanAttributes: SpanAttributes -): MetricAttributes => { - const metricAttributes: MetricAttributes = {}; + spanAttributes: Attributes +): Attributes => { + const metricAttributes: Attributes = {}; metricAttributes[SEMATTRS_HTTP_SCHEME] = spanAttributes[SEMATTRS_HTTP_SCHEME]; metricAttributes[SEMATTRS_HTTP_METHOD] = spanAttributes[SEMATTRS_HTTP_METHOD]; metricAttributes[SEMATTRS_NET_HOST_NAME] = @@ -818,7 +816,7 @@ export const getIncomingRequestAttributesOnResponse = ( request: IncomingMessage, response: ServerResponse, semconvStability: SemconvStability -): SpanAttributes => { +): Attributes => { // take socket from the request, // since it may be detached from the response object in keep-alive mode const { socket } = request; @@ -829,7 +827,7 @@ export const getIncomingRequestAttributesOnResponse = ( }; const rpcMetadata = getRPCMetadata(context.active()); - const oldAttributes: SpanAttributes = {}; + const oldAttributes: Attributes = {}; if (socket) { const { localAddress, localPort, remoteAddress, remotePort } = socket; oldAttributes[SEMATTRS_NET_HOST_IP] = localAddress; @@ -858,12 +856,12 @@ export const getIncomingRequestAttributesOnResponse = ( /** * Returns incoming request Metric attributes scoped to the request data - * @param {SpanAttributes} spanAttributes the span attributes + * @param {Attributes} spanAttributes the span attributes */ export const getIncomingRequestMetricAttributesOnResponse = ( - spanAttributes: SpanAttributes -): MetricAttributes => { - const metricAttributes: MetricAttributes = {}; + spanAttributes: Attributes +): Attributes => { + const metricAttributes: Attributes = {}; metricAttributes[SEMATTRS_HTTP_STATUS_CODE] = spanAttributes[SEMATTRS_HTTP_STATUS_CODE]; metricAttributes[SEMATTRS_NET_HOST_PORT] = 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 e7270ebdc9..b6470e1f3e 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 @@ -21,7 +21,7 @@ import { Span as ISpan, SpanKind, trace, - SpanAttributes, + Attributes, DiagConsoleLogger, } from '@opentelemetry/api'; import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; @@ -144,13 +144,13 @@ export const responseHookFunction = ( export const startIncomingSpanHookFunction = ( request: IncomingMessage -): SpanAttributes => { +): Attributes => { return { guid: request.headers?.guid }; }; export const startOutgoingSpanHookFunction = ( request: RequestOptions -): SpanAttributes => { +): Attributes => { return { guid: request.headers?.guid }; }; 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 d64f795383..c731a296d9 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts @@ -14,13 +14,12 @@ * limitations under the License. */ import { - SpanAttributes, + Attributes, SpanStatusCode, ROOT_CONTEXT, SpanKind, TraceFlags, context, - Attributes, } from '@opentelemetry/api'; import { BasicTracerProvider, Span } from '@opentelemetry/sdk-trace-base'; import { @@ -362,7 +361,7 @@ describe('Utility', () => { // 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( - attributes: SpanAttributes, + attributes: Attributes, key: string | undefined, value: number ) { @@ -384,7 +383,7 @@ describe('Utility', () => { describe('setRequestContentLengthAttributes()', () => { it('should set request content-length uncompressed attribute with no content-encoding header', () => { - const attributes: SpanAttributes = {}; + const attributes: Attributes = {}; const request = {} as IncomingMessage; request.headers = { @@ -400,7 +399,7 @@ describe('Utility', () => { }); it('should set request content-length uncompressed attribute with "identity" content-encoding header', () => { - const attributes: SpanAttributes = {}; + const attributes: Attributes = {}; const request = {} as IncomingMessage; request.headers = { 'content-length': '1200', @@ -416,7 +415,7 @@ describe('Utility', () => { }); it('should set request content-length compressed attribute with "gzip" content-encoding header', () => { - const attributes: SpanAttributes = {}; + const attributes: Attributes = {}; const request = {} as IncomingMessage; request.headers = { 'content-length': '1200', @@ -434,7 +433,7 @@ describe('Utility', () => { describe('setResponseContentLengthAttributes()', () => { it('should set response content-length uncompressed attribute with no content-encoding header', () => { - const attributes: SpanAttributes = {}; + const attributes: Attributes = {}; const response = {} as IncomingMessage; @@ -451,7 +450,7 @@ describe('Utility', () => { }); it('should set response content-length uncompressed attribute with "identity" content-encoding header', () => { - const attributes: SpanAttributes = {}; + const attributes: Attributes = {}; const response = {} as IncomingMessage; @@ -470,7 +469,7 @@ describe('Utility', () => { }); it('should set response content-length compressed attribute with "gzip" content-encoding header', () => { - const attributes: SpanAttributes = {}; + const attributes: Attributes = {}; const response = {} as IncomingMessage; @@ -489,7 +488,7 @@ describe('Utility', () => { }); it('should set no attributes with no content-length header', () => { - const attributes: SpanAttributes = {}; + const attributes: Attributes = {}; const message = {} as IncomingMessage; message.headers = { From 77f12c57bf7658112bd80ced8cae01d7de491785 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Mon, 30 Sep 2024 09:44:38 +0200 Subject: [PATCH 14/20] chore(deps): lock file maintenance (#5027) From 3007d3e749029deb9dbb27dede6408a8d27a4786 Mon Sep 17 00:00:00 2001 From: Marc Pichler Date: Mon, 30 Sep 2024 11:33:17 +0200 Subject: [PATCH 15/20] feat(exporters)!: rewrite exporter config logic (#4971) --- experimental/CHANGELOG.md | 24 + .../src/OTLPLogExporter.ts | 34 +- .../test/OTLPLogExporter.test.ts | 96 ---- .../src/platform/browser/OTLPLogExporter.ts | 11 +- .../src/platform/config.ts | 50 -- .../src/platform/node/OTLPLogExporter.ts | 21 +- .../test/browser/OTLPLogExporter.test.ts | 11 - .../test/config.test.ts | 136 ----- .../test/node/OTLPLogExporter.test.ts | 21 - .../src/platform/browser/OTLPLogExporter.ts | 18 +- .../src/platform/node/OTLPLogExporter.ts | 45 +- .../test/browser/OTLPLogExporter.test.ts | 12 - .../test/node/OTLPLogExporter.test.ts | 140 ----- .../src/OTLPTraceExporter.ts | 34 +- .../test/OTLPTraceExporter.test.ts | 96 ---- .../src/platform/browser/OTLPTraceExporter.ts | 16 +- .../src/platform/node/OTLPTraceExporter.ts | 54 +- .../browser/CollectorTraceExporter.test.ts | 87 +--- .../test/node/CollectorTraceExporter.test.ts | 163 ------ .../src/platform/browser/OTLPTraceExporter.ts | 16 +- .../src/platform/node/OTLPTraceExporter.ts | 46 +- .../browser/CollectorTraceExporter.test.ts | 12 - .../test/node/OTLPTraceExporter.test.ts | 131 ----- .../src/OTLPMetricExporter.ts | 36 +- .../test/OTLPMetricExporter.test.ts | 107 ---- .../platform/browser/OTLPMetricExporter.ts | 16 +- .../src/platform/node/OTLPMetricExporter.ts | 46 +- .../common/CollectorMetricExporter.test.ts | 21 - .../test/node/CollectorMetricExporter.test.ts | 242 --------- .../src/OTLPMetricExporter.ts | 46 +- .../test/OTLPMetricExporter.test.ts | 144 ------ .../src/OTLPExporterBase.ts | 19 - .../configuration/otlp-http-configuration.ts | 104 ++++ .../otlp-http-env-configuration.ts | 131 +++++ .../src/configuration/shared-configuration.ts | 77 +++ .../configuration/shared-env-configuration.ts | 88 ++++ .../packages/otlp-exporter-base/src/index.ts | 16 +- .../browser/OTLPExporterBrowserBase.ts | 47 +- .../src/platform/browser/xhr-transport.ts | 5 +- .../src/platform/node/OTLPExporterNodeBase.ts | 63 ++- .../node/convert-legacy-agent-options.ts | 46 ++ .../src/platform/node/util.ts | 33 -- .../packages/otlp-exporter-base/src/types.ts | 3 +- .../packages/otlp-exporter-base/src/util.ts | 106 +--- .../test/common/CollectorExporter.test.ts | 20 - .../otlp-http-configuration.test.ts | 133 +++++ .../shared-configuration.test.ts | 136 +++++ .../test/common/is-export-retryable.test.ts | 45 ++ .../test/common/util.test.ts | 145 +----- .../otlp-http-env-configuration.test.ts | 244 +++++++++ .../shared-env-configuration.test.ts | 208 ++++++++ .../otlp-exporter-base/test/node/util.test.ts | 134 +---- .../src/OTLPGRPCExporterNodeBase.ts | 90 ++-- .../configuration/otlp-grpc-configuration.ts | 141 +++++ .../otlp-grpc-env-configuration.ts | 259 ++++++++++ .../otlp-grpc-exporter-base/src/index.ts | 1 - .../otlp-grpc-exporter-base/src/types.ts | 11 - .../otlp-grpc-exporter-base/src/util.ts | 176 ------- .../test/OTLPGRPCExporterNodeBase.test.ts | 18 +- .../otlp-grpc-configuration.test.ts | 259 ++++++++++ .../otlp-grpc-env-configuration.test.ts | 488 ++++++++++++++++++ .../test/otlp-grpc-configuration.test.ts | 83 +++ .../otlp-grpc-exporter-base/test/util.test.ts | 220 -------- 63 files changed, 2722 insertions(+), 2759 deletions(-) delete mode 100644 experimental/packages/exporter-logs-otlp-http/src/platform/config.ts delete mode 100644 experimental/packages/exporter-logs-otlp-http/test/config.test.ts create mode 100644 experimental/packages/otlp-exporter-base/src/configuration/otlp-http-configuration.ts create mode 100644 experimental/packages/otlp-exporter-base/src/configuration/otlp-http-env-configuration.ts create mode 100644 experimental/packages/otlp-exporter-base/src/configuration/shared-configuration.ts create mode 100644 experimental/packages/otlp-exporter-base/src/configuration/shared-env-configuration.ts create mode 100644 experimental/packages/otlp-exporter-base/src/platform/node/convert-legacy-agent-options.ts delete mode 100644 experimental/packages/otlp-exporter-base/src/platform/node/util.ts create mode 100644 experimental/packages/otlp-exporter-base/test/common/configuration/otlp-http-configuration.test.ts create mode 100644 experimental/packages/otlp-exporter-base/test/common/configuration/shared-configuration.test.ts create mode 100644 experimental/packages/otlp-exporter-base/test/common/is-export-retryable.test.ts create mode 100644 experimental/packages/otlp-exporter-base/test/node/configuration/otlp-http-env-configuration.test.ts create mode 100644 experimental/packages/otlp-exporter-base/test/node/configuration/shared-env-configuration.test.ts create mode 100644 experimental/packages/otlp-grpc-exporter-base/src/configuration/otlp-grpc-configuration.ts create mode 100644 experimental/packages/otlp-grpc-exporter-base/src/configuration/otlp-grpc-env-configuration.ts delete mode 100644 experimental/packages/otlp-grpc-exporter-base/src/util.ts create mode 100644 experimental/packages/otlp-grpc-exporter-base/test/configuration/otlp-grpc-configuration.test.ts create mode 100644 experimental/packages/otlp-grpc-exporter-base/test/configuration/otlp-grpc-env-configuration.test.ts create mode 100644 experimental/packages/otlp-grpc-exporter-base/test/otlp-grpc-configuration.test.ts delete mode 100644 experimental/packages/otlp-grpc-exporter-base/test/util.test.ts diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index e1ba48d160..04ea9a97b3 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -22,6 +22,30 @@ All notable changes to experimental packages in this project will be documented * fix(sdk-events): remove devDependencies to old `@opentelemetry/api-logs@0.52.0`, `@opentelemetry/api-events@0.52.0` packages [#5013](https://github.com/open-telemetry/opentelemetry-js/pull/5013) @pichlermarc * fix(sdk-logs): remove devDependencies to old `@opentelemetry/api-logs@0.52.0` [#5013](https://github.com/open-telemetry/opentelemetry-js/pull/5013) @pichlermarc * fix(sdk-logs): align LogRecord#setAttribute type with types from `@opentelemetry/api-logs@0.53.0` [#5013](https://github.com/open-telemetry/opentelemetry-js/pull/5013) @pichlermarc +* feat(exporter-*-otlp-*)!: rewrite exporter config logic for testability [#4971](https://github.com/open-telemetry/opentelemetry-js/pull/4971) @pichlermarc + * (user-facing) `getDefaultUrl` was intended for internal use has been removed from all exporters + * (user-facing) `getUrlFromConfig` was intended for internal use and has been removed from all exporters + * (user-facing) `hostname` was intended for internal use and has been removed from all exporters + * (user-facing) `url` was intended for internal use and has been removed from all exporters + * (user-facing) `timeoutMillis` was intended for internal use and has been removed from all exporters + * (user-facing) `onInit` was intended for internal use and has been removed from all exporters +* fix(exporter-*-otlp-*): fixes a bug where signal-specific environment variables would not be applied and the trace-specific one was used instead [#4971](https://github.com/open-telemetry/opentelemetry-js/pull/4971) @pichlermarc + * Fixes: + * `OTEL_EXPORTER_OTLP_METRICS_COMPRESSION` + * `OTEL_EXPORTER_OTLP_LOGS_COMPRESSION` + * `OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE` + * `OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE` + * `OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY` + * `OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY` + * `OTEL_EXPORTER_OTLP_METRICS_INSECURE` + * `OTEL_EXPORTER_OTLP_LOGS_INSECURE` +* feat(otlp-exporter-base)!: do not export functions that are intended for internal use [#4971](https://github.com/open-telemetry/opentelemetry-js/pull/4971) @pichlermarc + * Drops the following functions and types that were intended for internal use from the package exports: + * `parseHeaders` + * `appendResourcePathToUrl` + * `appendResourcePathToUrlIfNeeded` + * `configureExporterTimeout` + * `invalidTimeout` ### :books: (Refine Doc) diff --git a/experimental/packages/exporter-logs-otlp-grpc/src/OTLPLogExporter.ts b/experimental/packages/exporter-logs-otlp-grpc/src/OTLPLogExporter.ts index 1172126cbd..31f1daf95e 100644 --- a/experimental/packages/exporter-logs-otlp-grpc/src/OTLPLogExporter.ts +++ b/experimental/packages/exporter-logs-otlp-grpc/src/OTLPLogExporter.ts @@ -15,22 +15,14 @@ */ import { LogRecordExporter, ReadableLogRecord } from '@opentelemetry/sdk-logs'; -import { baggageUtils, getEnv } from '@opentelemetry/core'; import { OTLPGRPCExporterConfigNode, OTLPGRPCExporterNodeBase, - validateAndNormalizeUrl, - DEFAULT_COLLECTOR_URL, } from '@opentelemetry/otlp-grpc-exporter-base'; import { IExportLogsServiceResponse, ProtobufLogsSerializer, } from '@opentelemetry/otlp-transformer'; -import { VERSION } from './version'; - -const USER_AGENT = { - 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, -}; /** * OTLP Logs Exporter for Node @@ -43,34 +35,12 @@ export class OTLPLogExporter implements LogRecordExporter { constructor(config: OTLPGRPCExporterConfigNode = {}) { - const signalSpecificMetadata = { - ...USER_AGENT, - ...baggageUtils.parseKeyPairsIntoRecord( - getEnv().OTEL_EXPORTER_OTLP_LOGS_HEADERS - ), - }; super( config, - signalSpecificMetadata, + ProtobufLogsSerializer, 'LogsExportService', '/opentelemetry.proto.collector.logs.v1.LogsService/Export', - ProtobufLogsSerializer - ); - } - - getDefaultUrl(config: OTLPGRPCExporterConfigNode) { - return validateAndNormalizeUrl(this.getUrlFromConfig(config)); - } - - getUrlFromConfig(config: OTLPGRPCExporterConfigNode): string { - if (typeof config.url === 'string') { - return config.url; - } - - return ( - getEnv().OTEL_EXPORTER_OTLP_LOGS_ENDPOINT || - getEnv().OTEL_EXPORTER_OTLP_ENDPOINT || - DEFAULT_COLLECTOR_URL + 'LOGS' ); } } diff --git a/experimental/packages/exporter-logs-otlp-grpc/test/OTLPLogExporter.test.ts b/experimental/packages/exporter-logs-otlp-grpc/test/OTLPLogExporter.test.ts index a0d3c7eb66..996b5180ac 100644 --- a/experimental/packages/exporter-logs-otlp-grpc/test/OTLPLogExporter.test.ts +++ b/experimental/packages/exporter-logs-otlp-grpc/test/OTLPLogExporter.test.ts @@ -37,7 +37,6 @@ import { IExportLogsServiceRequest, IResourceLogs, } from '@opentelemetry/otlp-transformer'; -import { VERSION } from '../src/version'; const logsServiceProtoPath = 'opentelemetry/proto/collector/logs/v1/logs_service.proto'; @@ -294,104 +293,9 @@ const testCollectorExporter = (params: TestParams) => { }, 500); }); }); - describe('Logs Exporter with compression', () => { - const envSource = process.env; - it('should return gzip compression algorithm on exporter', () => { - const credentials = useTLS - ? grpc.credentials.createSsl( - fs.readFileSync('./test/certs/ca.crt'), - fs.readFileSync('./test/certs/client.key'), - fs.readFileSync('./test/certs/client.crt') - ) - : grpc.credentials.createInsecure(); - - envSource.OTEL_EXPORTER_OTLP_COMPRESSION = 'gzip'; - collectorExporter = new OTLPLogExporter({ - url: address, - credentials, - metadata: metadata, - }); - assert.strictEqual( - collectorExporter.compression, - CompressionAlgorithm.GZIP - ); - delete envSource.OTEL_EXPORTER_OTLP_COMPRESSION; - }); - }); }); }; -describe('OTLPLogExporter - node (getDefaultUrl)', () => { - it('should default to localhost', done => { - const collectorExporter = new OTLPLogExporter({}); - setTimeout(() => { - assert.strictEqual(collectorExporter['url'], 'localhost:4317'); - done(); - }); - }); - it('should keep the URL if included', done => { - const url = 'http://foo.bar.com'; - const collectorExporter = new OTLPLogExporter({ url }); - setTimeout(() => { - assert.strictEqual(collectorExporter['url'], 'foo.bar.com'); - done(); - }); - }); -}); - -describe('when configuring via environment', () => { - const envSource = process.env; - - afterEach(function () { - // Ensure we don't pollute other tests if assertions fail - delete envSource.OTEL_EXPORTER_OTLP_ENDPOINT; - delete envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT; - delete envSource.OTEL_EXPORTER_OTLP_HEADERS; - delete envSource.OTEL_EXPORTER_OTLP_LOGS_HEADERS; - sinon.restore(); - }); - - it('should use url defined in env', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar'; - const collectorExporter = new OTLPLogExporter(); - assert.strictEqual(collectorExporter.url, 'foo.bar'); - }); - it('should override global exporter url with signal url defined in env', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar'; - envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = 'http://foo.logs'; - const collectorExporter = new OTLPLogExporter(); - assert.strictEqual(collectorExporter.url, 'foo.logs'); - }); - it('should include user-agent header by default', () => { - const collectorExporter = new OTLPLogExporter(); - const actualMetadata = - collectorExporter['_transport']['_parameters'].metadata(); - assert.deepStrictEqual(actualMetadata.get('User-Agent'), [ - `OTel-OTLP-Exporter-JavaScript/${VERSION}`, - ]); - }); - it('should use headers defined via env', () => { - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar'; - const collectorExporter = new OTLPLogExporter(); - const actualMetadata = - collectorExporter['_transport']['_parameters'].metadata(); - assert.deepStrictEqual(actualMetadata.get('foo'), ['bar']); - }); - it('should not override hard-coded headers config with headers defined via env', () => { - const metadata = new grpc.Metadata(); - metadata.set('foo', 'bar'); - metadata.set('goo', 'lol'); - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=jar,bar=foo'; - envSource.OTEL_EXPORTER_OTLP_LOGS_HEADERS = 'foo=boo'; - const collectorExporter = new OTLPLogExporter({ metadata }); - const actualMetadata = - collectorExporter['_transport']['_parameters'].metadata(); - assert.deepStrictEqual(actualMetadata.get('foo'), ['bar']); - assert.deepStrictEqual(actualMetadata.get('goo'), ['lol']); - assert.deepStrictEqual(actualMetadata.get('bar'), ['foo']); - }); -}); - testCollectorExporter({ useTLS: true }); testCollectorExporter({ useTLS: false }); testCollectorExporter({ metadata }); diff --git a/experimental/packages/exporter-logs-otlp-http/src/platform/browser/OTLPLogExporter.ts b/experimental/packages/exporter-logs-otlp-http/src/platform/browser/OTLPLogExporter.ts index 4a9ce917e2..3f4f5c60ff 100644 --- a/experimental/packages/exporter-logs-otlp-http/src/platform/browser/OTLPLogExporter.ts +++ b/experimental/packages/exporter-logs-otlp-http/src/platform/browser/OTLPLogExporter.ts @@ -23,8 +23,6 @@ import type { IExportLogsServiceResponse } from '@opentelemetry/otlp-transformer import { OTLPExporterBrowserBase } from '@opentelemetry/otlp-exporter-base'; import { JsonLogsSerializer } from '@opentelemetry/otlp-transformer'; -import { getDefaultUrl } from '../config'; - /** * Collector Logs Exporter for Web */ @@ -38,11 +36,10 @@ export class OTLPLogExporter ...config, }, JsonLogsSerializer, - 'application/json' + { + 'Content-Type': 'application/json', + }, + 'v1/logs' ); } - - getDefaultUrl(config: OTLPExporterConfigBase): string { - return getDefaultUrl(config); - } } diff --git a/experimental/packages/exporter-logs-otlp-http/src/platform/config.ts b/experimental/packages/exporter-logs-otlp-http/src/platform/config.ts deleted file mode 100644 index 5ce6eb75f7..0000000000 --- a/experimental/packages/exporter-logs-otlp-http/src/platform/config.ts +++ /dev/null @@ -1,50 +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 { getEnv } from '@opentelemetry/core'; -import { - appendResourcePathToUrl, - appendRootPathToUrlIfNeeded, - OTLPExporterConfigBase, -} from '@opentelemetry/otlp-exporter-base'; - -const DEFAULT_COLLECTOR_RESOURCE_PATH = 'v1/logs'; -export const DEFAULT_COLLECTOR_URL = `http://localhost:4318/${DEFAULT_COLLECTOR_RESOURCE_PATH}`; - -/** - * common get default url - * @param config exporter config - * @returns url string - */ -export function getDefaultUrl(config: OTLPExporterConfigBase): string { - if (typeof config.url === 'string') { - return config.url; - } - - const env = getEnv(); - if (env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT.length > 0) { - return appendRootPathToUrlIfNeeded(env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT); - } - - if (env.OTEL_EXPORTER_OTLP_ENDPOINT.length > 0) { - return appendResourcePathToUrl( - env.OTEL_EXPORTER_OTLP_ENDPOINT, - DEFAULT_COLLECTOR_RESOURCE_PATH - ); - } - - return DEFAULT_COLLECTOR_URL; -} diff --git a/experimental/packages/exporter-logs-otlp-http/src/platform/node/OTLPLogExporter.ts b/experimental/packages/exporter-logs-otlp-http/src/platform/node/OTLPLogExporter.ts index 1837993e91..93efa8fed9 100644 --- a/experimental/packages/exporter-logs-otlp-http/src/platform/node/OTLPLogExporter.ts +++ b/experimental/packages/exporter-logs-otlp-http/src/platform/node/OTLPLogExporter.ts @@ -20,14 +20,9 @@ import type { } from '@opentelemetry/sdk-logs'; import type { OTLPExporterNodeConfigBase } from '@opentelemetry/otlp-exporter-base'; import type { IExportLogsServiceResponse } from '@opentelemetry/otlp-transformer'; -import { getEnv, baggageUtils } from '@opentelemetry/core'; -import { - OTLPExporterNodeBase, - parseHeaders, -} from '@opentelemetry/otlp-exporter-base'; +import { OTLPExporterNodeBase } from '@opentelemetry/otlp-exporter-base'; import { JsonLogsSerializer } from '@opentelemetry/otlp-transformer'; -import { getDefaultUrl } from '../config'; import { VERSION } from '../../version'; const USER_AGENT = { @@ -42,25 +37,17 @@ export class OTLPLogExporter implements LogRecordExporter { constructor(config: OTLPExporterNodeConfigBase = {}) { - // load OTEL_EXPORTER_OTLP_LOGS_TIMEOUT env super( { - timeoutMillis: getEnv().OTEL_EXPORTER_OTLP_LOGS_TIMEOUT, ...config, }, JsonLogsSerializer, { - ...baggageUtils.parseKeyPairsIntoRecord( - getEnv().OTEL_EXPORTER_OTLP_LOGS_HEADERS - ), - ...parseHeaders(config?.headers), ...USER_AGENT, 'Content-Type': 'application/json', - } + }, + 'LOGS', + 'v1/logs' ); } - - getDefaultUrl(config: OTLPExporterNodeConfigBase): string { - return getDefaultUrl(config); - } } diff --git a/experimental/packages/exporter-logs-otlp-http/test/browser/OTLPLogExporter.test.ts b/experimental/packages/exporter-logs-otlp-http/test/browser/OTLPLogExporter.test.ts index 362a3d65d2..69f93c6ea9 100644 --- a/experimental/packages/exporter-logs-otlp-http/test/browser/OTLPLogExporter.test.ts +++ b/experimental/packages/exporter-logs-otlp-http/test/browser/OTLPLogExporter.test.ts @@ -16,7 +16,6 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; -import * as Config from '../../src/platform/config'; import { OTLPLogExporter } from '../../src/platform/browser'; import { OTLPExporterConfigBase } from '@opentelemetry/otlp-exporter-base'; import { ReadableLogRecord } from '@opentelemetry/sdk-logs'; @@ -38,16 +37,6 @@ describe('OTLPLogExporter', () => { }); }); - describe('getDefaultUrl', () => { - it('should call getDefaultUrl', () => { - const getDefaultUrl = sinon.stub(Config, 'getDefaultUrl'); - const exporter = new OTLPLogExporter(); - exporter.getDefaultUrl({}); - // this callCount is 2, because new OTLPLogExporter also call it - assert.strictEqual(getDefaultUrl.callCount, 2); - }); - }); - describe('export - common', () => { let spySend: any; beforeEach(() => { diff --git a/experimental/packages/exporter-logs-otlp-http/test/config.test.ts b/experimental/packages/exporter-logs-otlp-http/test/config.test.ts deleted file mode 100644 index 2f5e8dd1d2..0000000000 --- a/experimental/packages/exporter-logs-otlp-http/test/config.test.ts +++ /dev/null @@ -1,136 +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 * as assert from 'assert'; - -import { getDefaultUrl } from '../src/platform/config'; - -describe('getDefaultUrl', () => { - let envSource: Record; - - if (global.process?.versions?.node === undefined) { - envSource = globalThis as unknown as Record; - } else { - envSource = process.env as Record; - } - - it('should use config url if config url is defined', () => { - const configUrl = 'http://foo.bar/v1/logs/'; - envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = 'http://foo.bar.logs/'; - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/'; - const defaultUrl = getDefaultUrl({ url: configUrl }); - assert.strictEqual(defaultUrl, configUrl); - delete envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT; - delete envSource.OTEL_EXPORTER_OTLP_ENDPOINT; - }); - - it('should use url defined in env that ends with root path and append version and signal path', () => { - envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = ''; - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/'; - const defaultUrl = getDefaultUrl({}); - assert.strictEqual( - defaultUrl, - `${envSource.OTEL_EXPORTER_OTLP_ENDPOINT}v1/logs` - ); - delete envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT; - delete envSource.OTEL_EXPORTER_OTLP_ENDPOINT; - }); - - it('should use url defined in env without checking if path is already present', () => { - envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = ''; - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/v1/logs'; - const defaultUrl = getDefaultUrl({}); - assert.strictEqual( - defaultUrl, - `${envSource.OTEL_EXPORTER_OTLP_ENDPOINT}/v1/logs` - ); - delete envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT; - delete envSource.OTEL_EXPORTER_OTLP_ENDPOINT; - }); - - it('should use url defined in env and append version and signal', () => { - envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = ''; - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar'; - const defaultUrl = getDefaultUrl({}); - assert.strictEqual( - defaultUrl, - `${envSource.OTEL_EXPORTER_OTLP_ENDPOINT}/v1/logs` - ); - delete envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT; - delete envSource.OTEL_EXPORTER_OTLP_ENDPOINT; - }); - - it('should override global exporter url with signal url defined in env', () => { - envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = 'http://foo.logs/'; - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/'; - const defaultUrl = getDefaultUrl({}); - assert.strictEqual( - defaultUrl, - `${envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT}` - ); - delete envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT; - delete envSource.OTEL_EXPORTER_OTLP_ENDPOINT; - }); - - it('should add root path when signal url defined in env contains no path and no root path', () => { - envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = 'http://foo.logs'; - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - - const defaultUrl = getDefaultUrl({}); - assert.strictEqual( - defaultUrl, - `${envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT}/` - ); - delete envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT; - delete envSource.OTEL_EXPORTER_OTLP_ENDPOINT; - }); - - it('should not add root path when signal url defined in env contains root path but no path', () => { - envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = 'http://foo.bar/'; - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - const defaultUrl = getDefaultUrl({}); - assert.strictEqual( - defaultUrl, - `${envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT}` - ); - delete envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT; - delete envSource.OTEL_EXPORTER_OTLP_ENDPOINT; - }); - - it('should not add root path when signal url defined in env contains path', () => { - envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = 'http://foo.bar/v1/logs'; - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - const defaultUrl = getDefaultUrl({}); - assert.strictEqual( - defaultUrl, - `${envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT}` - ); - delete envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT; - delete envSource.OTEL_EXPORTER_OTLP_ENDPOINT; - }); - - it('should not add root path when signal url defined in env contains path and ends in /', () => { - envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = 'http://foo.bar/v1/logs/'; - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - const defaultUrl = getDefaultUrl({}); - assert.strictEqual( - defaultUrl, - `${envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT}` - ); - delete envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT; - delete envSource.OTEL_EXPORTER_OTLP_ENDPOINT; - }); -}); diff --git a/experimental/packages/exporter-logs-otlp-http/test/node/OTLPLogExporter.test.ts b/experimental/packages/exporter-logs-otlp-http/test/node/OTLPLogExporter.test.ts index 8e6b076ed6..726be702fa 100644 --- a/experimental/packages/exporter-logs-otlp-http/test/node/OTLPLogExporter.test.ts +++ b/experimental/packages/exporter-logs-otlp-http/test/node/OTLPLogExporter.test.ts @@ -18,7 +18,6 @@ import { diag } from '@opentelemetry/api'; import * as assert from 'assert'; import * as http from 'http'; import * as sinon from 'sinon'; -import * as Config from '../../src/platform/config'; import { OTLPLogExporter } from '../../src/platform/node'; import { OTLPExporterNodeConfigBase } from '@opentelemetry/otlp-exporter-base'; @@ -103,15 +102,6 @@ describe('OTLPLogExporter', () => { delete envSource.OTEL_EXPORTER_OTLP_LOGS_HEADERS; }); - it('should use timeout defined via env', () => { - envSource.OTEL_EXPORTER_OTLP_LOGS_HEADERS = ''; - envSource.OTEL_EXPORTER_OTLP_LOGS_TIMEOUT = 30000; - const exporter = new OTLPLogExporter(); - assert.strictEqual(exporter.timeoutMillis, 30000); - delete envSource.OTEL_EXPORTER_OTLP_LOGS_HEADERS; - delete envSource.OTEL_EXPORTER_OTLP_LOGS_TIMEOUT; - }); - it('should override headers defined via env with headers defined in constructor', () => { envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar,bar=foo'; const exporter = new OTLPLogExporter({ @@ -131,23 +121,12 @@ describe('OTLPLogExporter', () => { }); }); - describe('getDefaultUrl', () => { - it('should call getDefaultUrl', () => { - const getDefaultUrl = sinon.stub(Config, 'getDefaultUrl'); - const exporter = new OTLPLogExporter(); - exporter.getDefaultUrl({}); - // this callCount is 2, because new OTLPLogExporter also call it - assert.strictEqual(getDefaultUrl.callCount, 2); - }); - }); - describe('export', () => { beforeEach(() => { collectorExporterConfig = { headers: { foo: 'bar', }, - hostname: 'foo', url: 'http://foo.bar.com', keepAlive: true, httpAgentOptions: { keepAliveMsecs: 2000 }, diff --git a/experimental/packages/exporter-logs-otlp-proto/src/platform/browser/OTLPLogExporter.ts b/experimental/packages/exporter-logs-otlp-proto/src/platform/browser/OTLPLogExporter.ts index ede7ae6c15..470a40f5fb 100644 --- a/experimental/packages/exporter-logs-otlp-proto/src/platform/browser/OTLPLogExporter.ts +++ b/experimental/packages/exporter-logs-otlp-proto/src/platform/browser/OTLPLogExporter.ts @@ -25,9 +25,6 @@ import { import { ReadableLogRecord, LogRecordExporter } from '@opentelemetry/sdk-logs'; -const DEFAULT_COLLECTOR_RESOURCE_PATH = 'v1/logs'; -const DEFAULT_COLLECTOR_URL = `http://localhost:4318/${DEFAULT_COLLECTOR_RESOURCE_PATH}`; - /** * Collector Trace Exporter for Web */ @@ -36,14 +33,11 @@ export class OTLPLogExporter implements LogRecordExporter { constructor(config: OTLPExporterConfigBase = {}) { - super(config, ProtobufLogsSerializer, 'application/x-protobuf'); - } - - getDefaultUrl(config: OTLPExporterConfigBase): string { - if (typeof config.url === 'string') { - return config.url; - } - - return DEFAULT_COLLECTOR_URL; + super( + config, + ProtobufLogsSerializer, + { 'Content-Type': 'application/x-protobuf' }, + 'v1/logs' + ); } } diff --git a/experimental/packages/exporter-logs-otlp-proto/src/platform/node/OTLPLogExporter.ts b/experimental/packages/exporter-logs-otlp-proto/src/platform/node/OTLPLogExporter.ts index 893a06b4ab..828a11cbc6 100644 --- a/experimental/packages/exporter-logs-otlp-proto/src/platform/node/OTLPLogExporter.ts +++ b/experimental/packages/exporter-logs-otlp-proto/src/platform/node/OTLPLogExporter.ts @@ -14,13 +14,9 @@ * limitations under the License. */ -import { getEnv, baggageUtils } from '@opentelemetry/core'; import { OTLPExporterConfigBase, - appendResourcePathToUrl, - appendRootPathToUrlIfNeeded, OTLPExporterNodeBase, - parseHeaders, } from '@opentelemetry/otlp-exporter-base'; import { IExportLogsServiceResponse, @@ -34,9 +30,6 @@ const USER_AGENT = { 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, }; -const DEFAULT_COLLECTOR_RESOURCE_PATH = 'v1/logs'; -const DEFAULT_COLLECTOR_URL = `http://localhost:4318/${DEFAULT_COLLECTOR_RESOURCE_PATH}`; - /** * Collector Trace Exporter for Node */ @@ -45,33 +38,15 @@ export class OTLPLogExporter implements LogRecordExporter { constructor(config: OTLPExporterConfigBase = {}) { - super(config, ProtobufLogsSerializer, { - ...baggageUtils.parseKeyPairsIntoRecord( - getEnv().OTEL_EXPORTER_OTLP_LOGS_HEADERS - ), - ...parseHeaders(config?.headers), - ...USER_AGENT, - 'Content-Type': 'application/x-protobuf', - }); - } - - getDefaultUrl(config: OTLPExporterConfigBase): string { - if (typeof config.url === 'string') { - return config.url; - } - - const env = getEnv(); - if (env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT.length > 0) { - return appendRootPathToUrlIfNeeded(env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT); - } - - if (env.OTEL_EXPORTER_OTLP_ENDPOINT.length > 0) { - return appendResourcePathToUrl( - env.OTEL_EXPORTER_OTLP_ENDPOINT, - DEFAULT_COLLECTOR_RESOURCE_PATH - ); - } - - return DEFAULT_COLLECTOR_URL; + super( + config, + ProtobufLogsSerializer, + { + ...USER_AGENT, + 'Content-Type': 'application/x-protobuf', + }, + 'LOGS', + 'v1/logs' + ); } } diff --git a/experimental/packages/exporter-logs-otlp-proto/test/browser/OTLPLogExporter.test.ts b/experimental/packages/exporter-logs-otlp-proto/test/browser/OTLPLogExporter.test.ts index 6a76ed24a0..29ed48ff4a 100644 --- a/experimental/packages/exporter-logs-otlp-proto/test/browser/OTLPLogExporter.test.ts +++ b/experimental/packages/exporter-logs-otlp-proto/test/browser/OTLPLogExporter.test.ts @@ -21,9 +21,7 @@ import { OTLPLogExporter } from '../../src/platform/browser/index'; describe('OTLPLogExporter - web', () => { let collectorLogsExporter: OTLPLogExporter; describe('constructor', () => { - let onInitSpy: any; beforeEach(() => { - onInitSpy = sinon.stub(OTLPLogExporter.prototype, 'onInit'); const collectorExporterConfig = { hostname: 'foo', url: 'http://foo.bar.com', @@ -36,15 +34,5 @@ describe('OTLPLogExporter - web', () => { it('should create an instance', () => { assert.ok(typeof collectorLogsExporter !== 'undefined'); }); - it('should call onInit', () => { - assert.strictEqual(onInitSpy.callCount, 1); - }); - it('should set hostname', () => { - assert.strictEqual(collectorLogsExporter.hostname, 'foo'); - }); - - it('should set url', () => { - assert.strictEqual(collectorLogsExporter.url, 'http://foo.bar.com'); - }); }); }); diff --git a/experimental/packages/exporter-logs-otlp-proto/test/node/OTLPLogExporter.test.ts b/experimental/packages/exporter-logs-otlp-proto/test/node/OTLPLogExporter.test.ts index 9cf3961fe5..475b00ad74 100644 --- a/experimental/packages/exporter-logs-otlp-proto/test/node/OTLPLogExporter.test.ts +++ b/experimental/packages/exporter-logs-otlp-proto/test/node/OTLPLogExporter.test.ts @@ -35,7 +35,6 @@ import { } from '@opentelemetry/otlp-exporter-base'; import { IExportLogsServiceRequest } from '@opentelemetry/otlp-transformer'; import { ReadableLogRecord } from '@opentelemetry/sdk-logs'; -import { VERSION } from '../../src/version'; import { Root } from 'protobufjs'; import * as path from 'path'; @@ -67,150 +66,12 @@ describe('OTLPLogExporter - node with proto over http', () => { sinon.restore(); }); - describe('when configuring via environment', () => { - const envSource = process.env; - it('should use url defined in env that ends with root path and append version and signal path', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/'; - const collectorExporter = new OTLPLogExporter(); - assert.strictEqual( - collectorExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_ENDPOINT}v1/logs` - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - }); - it('should use url defined in env without checking if path is already present', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/v1/logs'; - const collectorExporter = new OTLPLogExporter(); - assert.strictEqual( - collectorExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_ENDPOINT}/v1/logs` - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - }); - it('should use url defined in env and append version and signal', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar'; - const collectorExporter = new OTLPLogExporter(); - assert.strictEqual( - collectorExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_ENDPOINT}/v1/logs` - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - }); - it('should override global exporter url with signal url defined in env', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/'; - envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = 'http://foo.logs/'; - const collectorExporter = new OTLPLogExporter(); - assert.strictEqual( - collectorExporter.url, - envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = ''; - }); - it('should override url defined in env with url defined in constructor', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/'; - const constructorDefinedEndpoint = 'http://constructor/v1/logs'; - const collectorExporter = new OTLPLogExporter({ - url: constructorDefinedEndpoint, - }); - assert.strictEqual(collectorExporter.url, constructorDefinedEndpoint); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - }); - it('should add root path when signal url defined in env contains no path and no root path', () => { - envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = 'http://foo.bar'; - const collectorExporter = new OTLPLogExporter(); - assert.strictEqual( - collectorExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT}/` - ); - envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = ''; - }); - it('should not add root path when signal url defined in env contains root path but no path', () => { - envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = 'http://foo.bar/'; - const collectorExporter = new OTLPLogExporter(); - assert.strictEqual( - collectorExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT}` - ); - envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = ''; - }); - it('should not add root path when signal url defined in env contains path', () => { - envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = 'http://foo.bar/v1/logs'; - const collectorExporter = new OTLPLogExporter(); - assert.strictEqual( - collectorExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT}` - ); - envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = ''; - }); - it('should not add root path when signal url defined in env contains path and ends in /', () => { - envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = 'http://foo.bar/v1/logs/'; - const collectorExporter = new OTLPLogExporter(); - assert.strictEqual( - collectorExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT}` - ); - envSource.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = ''; - }); - it('should include user-agent header by default', () => { - const exporter = new OTLPLogExporter(); - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers'][ - 'User-Agent' - ], - `OTel-OTLP-Exporter-JavaScript/${VERSION}` - ); - }); - it('should use headers defined via env', () => { - envSource.OTEL_EXPORTER_OTLP_LOGS_HEADERS = 'foo=bar'; - const exporter = new OTLPLogExporter(); - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers']['foo'], - 'bar' - ); - envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; - }); - it('should override global headers config with signal headers defined via env', () => { - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar,bar=foo'; - envSource.OTEL_EXPORTER_OTLP_LOGS_HEADERS = 'foo=boo'; - const exporter = new OTLPLogExporter(); - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers']['foo'], - 'boo' - ); - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers']['bar'], - 'foo' - ); - envSource.OTEL_EXPORTER_OTLP_LOGS_HEADERS = ''; - envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; - }); - it('should override headers defined via env with headers defined in constructor', () => { - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar,bar=foo'; - const exporter = new OTLPLogExporter({ - headers: { - foo: 'constructor', - }, - }); - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers']['foo'], - 'constructor' - ); - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers']['bar'], - 'foo' - ); - envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; - }); - }); - describe('export', () => { beforeEach(() => { collectorExporterConfig = { headers: { foo: 'bar', }, - hostname: 'foo', url: 'http://foo.bar.com', keepAlive: true, httpAgentOptions: { keepAliveMsecs: 2000 }, @@ -346,7 +207,6 @@ describe('OTLPLogExporter - node with proto over http', () => { headers: { foo: 'bar', }, - hostname: 'foo', url: 'http://foo.bar.com', keepAlive: true, compression: CompressionAlgorithm.GZIP, diff --git a/experimental/packages/exporter-trace-otlp-grpc/src/OTLPTraceExporter.ts b/experimental/packages/exporter-trace-otlp-grpc/src/OTLPTraceExporter.ts index cffa9b0749..e785475a1d 100644 --- a/experimental/packages/exporter-trace-otlp-grpc/src/OTLPTraceExporter.ts +++ b/experimental/packages/exporter-trace-otlp-grpc/src/OTLPTraceExporter.ts @@ -15,22 +15,14 @@ */ import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'; -import { baggageUtils, getEnv } from '@opentelemetry/core'; import { OTLPGRPCExporterConfigNode, OTLPGRPCExporterNodeBase, - validateAndNormalizeUrl, - DEFAULT_COLLECTOR_URL, } from '@opentelemetry/otlp-grpc-exporter-base'; import { IExportTraceServiceResponse, ProtobufTraceSerializer, } from '@opentelemetry/otlp-transformer'; -import { VERSION } from './version'; - -const USER_AGENT = { - 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, -}; /** * OTLP Trace Exporter for Node @@ -40,34 +32,12 @@ export class OTLPTraceExporter implements SpanExporter { constructor(config: OTLPGRPCExporterConfigNode = {}) { - const signalSpecificMetadata = { - ...USER_AGENT, - ...baggageUtils.parseKeyPairsIntoRecord( - getEnv().OTEL_EXPORTER_OTLP_TRACES_HEADERS - ), - }; super( config, - signalSpecificMetadata, + ProtobufTraceSerializer, 'TraceExportService', '/opentelemetry.proto.collector.trace.v1.TraceService/Export', - ProtobufTraceSerializer - ); - } - - getDefaultUrl(config: OTLPGRPCExporterConfigNode) { - return validateAndNormalizeUrl(this.getUrlFromConfig(config)); - } - - getUrlFromConfig(config: OTLPGRPCExporterConfigNode): string { - if (typeof config.url === 'string') { - return config.url; - } - - return ( - getEnv().OTEL_EXPORTER_OTLP_TRACES_ENDPOINT || - getEnv().OTEL_EXPORTER_OTLP_ENDPOINT || - DEFAULT_COLLECTOR_URL + 'TRACES' ); } } diff --git a/experimental/packages/exporter-trace-otlp-grpc/test/OTLPTraceExporter.test.ts b/experimental/packages/exporter-trace-otlp-grpc/test/OTLPTraceExporter.test.ts index 60f250d7f6..801f85a36b 100644 --- a/experimental/packages/exporter-trace-otlp-grpc/test/OTLPTraceExporter.test.ts +++ b/experimental/packages/exporter-trace-otlp-grpc/test/OTLPTraceExporter.test.ts @@ -28,7 +28,6 @@ import * as grpc from '@grpc/grpc-js'; import * as path from 'path'; import * as sinon from 'sinon'; import { OTLPTraceExporter } from '../src'; -import { VERSION } from '../src/version'; import { ensureExportedSpanIsCorrect, @@ -299,104 +298,9 @@ const testCollectorExporter = (params: TestParams) => { }, 500); }); }); - describe('Trace Exporter with compression', () => { - const envSource = process.env; - it('should return gzip compression algorithm on exporter', () => { - const credentials = useTLS - ? grpc.credentials.createSsl( - fs.readFileSync('./test/certs/ca.crt'), - fs.readFileSync('./test/certs/client.key'), - fs.readFileSync('./test/certs/client.crt') - ) - : grpc.credentials.createInsecure(); - - envSource.OTEL_EXPORTER_OTLP_COMPRESSION = 'gzip'; - collectorExporter = new OTLPTraceExporter({ - url: address, - credentials, - metadata: metadata, - }); - assert.strictEqual( - collectorExporter.compression, - CompressionAlgorithm.GZIP - ); - delete envSource.OTEL_EXPORTER_OTLP_COMPRESSION; - }); - }); }); }; -describe('OTLPTraceExporter - node (getDefaultUrl)', () => { - it('should default to localhost', done => { - const collectorExporter = new OTLPTraceExporter({}); - setTimeout(() => { - assert.strictEqual(collectorExporter['url'], 'localhost:4317'); - done(); - }); - }); - it('should keep the URL if included', done => { - const url = 'http://foo.bar.com'; - const collectorExporter = new OTLPTraceExporter({ url }); - setTimeout(() => { - assert.strictEqual(collectorExporter['url'], 'foo.bar.com'); - done(); - }); - }); -}); - -describe('when configuring via environment', () => { - const envSource = process.env; - - afterEach(function () { - // Ensure we don't pollute other tests if assertions fail - delete envSource.OTEL_EXPORTER_OTLP_ENDPOINT; - delete envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT; - delete envSource.OTEL_EXPORTER_OTLP_HEADERS; - delete envSource.OTEL_EXPORTER_OTLP_TRACES_HEADERS; - sinon.restore(); - }); - - it('should use url defined in env', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar'; - const collectorExporter = new OTLPTraceExporter(); - assert.strictEqual(collectorExporter.url, 'foo.bar'); - }); - it('should override global exporter url with signal url defined in env', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar'; - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = 'http://foo.traces'; - const collectorExporter = new OTLPTraceExporter(); - assert.strictEqual(collectorExporter.url, 'foo.traces'); - }); - it('should use headers defined via env', () => { - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar'; - const collectorExporter = new OTLPTraceExporter(); - const actualMetadata = - collectorExporter['_transport']['_parameters'].metadata(); - assert.deepStrictEqual(actualMetadata.get('foo'), ['bar']); - }); - it('should include user agent in header', () => { - const collectorExporter = new OTLPTraceExporter(); - const actualMetadata = - collectorExporter['_transport']['_parameters'].metadata(); - assert.deepStrictEqual(actualMetadata.get('User-Agent'), [ - `OTel-OTLP-Exporter-JavaScript/${VERSION}`, - ]); - }); - it('should not override hard-coded headers config with headers defined via env', () => { - const metadata = new grpc.Metadata(); - metadata.set('foo', 'bar'); - metadata.set('goo', 'lol'); - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=jar,bar=foo'; - envSource.OTEL_EXPORTER_OTLP_TRACES_HEADERS = 'foo=boo'; - const collectorExporter = new OTLPTraceExporter({ metadata }); - const actualMetadata = - collectorExporter['_transport']['_parameters'].metadata(); - assert.deepStrictEqual(actualMetadata.get('foo'), ['bar']); - assert.deepStrictEqual(actualMetadata.get('goo'), ['lol']); - assert.deepStrictEqual(actualMetadata.get('bar'), ['foo']); - }); -}); - testCollectorExporter({ useTLS: true }); testCollectorExporter({ useTLS: false }); testCollectorExporter({ metadata }); diff --git a/experimental/packages/exporter-trace-otlp-http/src/platform/browser/OTLPTraceExporter.ts b/experimental/packages/exporter-trace-otlp-http/src/platform/browser/OTLPTraceExporter.ts index c9bc8e9242..2e03ef845e 100644 --- a/experimental/packages/exporter-trace-otlp-http/src/platform/browser/OTLPTraceExporter.ts +++ b/experimental/packages/exporter-trace-otlp-http/src/platform/browser/OTLPTraceExporter.ts @@ -25,7 +25,6 @@ import { } from '@opentelemetry/otlp-transformer'; const DEFAULT_COLLECTOR_RESOURCE_PATH = 'v1/traces'; -const DEFAULT_COLLECTOR_URL = `http://localhost:4318/${DEFAULT_COLLECTOR_RESOURCE_PATH}`; /** * Collector Trace Exporter for Web @@ -35,14 +34,11 @@ export class OTLPTraceExporter implements SpanExporter { constructor(config: OTLPExporterConfigBase = {}) { - super(config, JsonTraceSerializer, `application/json`); - } - - getDefaultUrl(config: OTLPExporterConfigBase): string { - if (typeof config.url === 'string') { - return config.url; - } - - return DEFAULT_COLLECTOR_URL; + super( + config, + JsonTraceSerializer, + { 'Content-Type': 'application/json' }, + DEFAULT_COLLECTOR_RESOURCE_PATH + ); } } diff --git a/experimental/packages/exporter-trace-otlp-http/src/platform/node/OTLPTraceExporter.ts b/experimental/packages/exporter-trace-otlp-http/src/platform/node/OTLPTraceExporter.ts index aa91b8c237..8d41df8f0d 100644 --- a/experimental/packages/exporter-trace-otlp-http/src/platform/node/OTLPTraceExporter.ts +++ b/experimental/packages/exporter-trace-otlp-http/src/platform/node/OTLPTraceExporter.ts @@ -15,22 +15,12 @@ */ import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'; -import { getEnv, baggageUtils } from '@opentelemetry/core'; -import { - OTLPExporterNodeBase, - parseHeaders, -} from '@opentelemetry/otlp-exporter-base'; -import { - OTLPExporterNodeConfigBase, - appendResourcePathToUrl, - appendRootPathToUrlIfNeeded, -} from '@opentelemetry/otlp-exporter-base'; +import { OTLPExporterNodeBase } from '@opentelemetry/otlp-exporter-base'; +import { OTLPExporterNodeConfigBase } from '@opentelemetry/otlp-exporter-base'; import { IExportTraceServiceResponse } from '@opentelemetry/otlp-transformer'; import { VERSION } from '../../version'; import { JsonTraceSerializer } from '@opentelemetry/otlp-transformer'; -const DEFAULT_COLLECTOR_RESOURCE_PATH = 'v1/traces'; -const DEFAULT_COLLECTOR_URL = `http://localhost:4318/${DEFAULT_COLLECTOR_RESOURCE_PATH}`; const USER_AGENT = { 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, }; @@ -43,35 +33,15 @@ export class OTLPTraceExporter implements SpanExporter { constructor(config: OTLPExporterNodeConfigBase = {}) { - super(config, JsonTraceSerializer, { - ...baggageUtils.parseKeyPairsIntoRecord( - getEnv().OTEL_EXPORTER_OTLP_TRACES_HEADERS - ), - ...parseHeaders(config?.headers), - ...USER_AGENT, - 'Content-Type': 'application/json', - }); - } - - getDefaultUrl(config: OTLPExporterNodeConfigBase): string { - if (typeof config.url === 'string') { - return config.url; - } - - const env = getEnv(); - if (env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT.length > 0) { - return appendRootPathToUrlIfNeeded( - env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT - ); - } - - if (env.OTEL_EXPORTER_OTLP_ENDPOINT.length > 0) { - return appendResourcePathToUrl( - env.OTEL_EXPORTER_OTLP_ENDPOINT, - DEFAULT_COLLECTOR_RESOURCE_PATH - ); - } - - return DEFAULT_COLLECTOR_URL; + super( + config, + JsonTraceSerializer, + { + ...USER_AGENT, + 'Content-Type': 'application/json', + }, + 'TRACES', + 'v1/traces' + ); } } 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 2e234fb2fa..9049caf60d 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 @@ -54,12 +54,8 @@ describe('OTLPTraceExporter - web', () => { }); describe('constructor', () => { - let onInitSpy: any; - beforeEach(() => { - onInitSpy = sinon.stub(OTLPTraceExporter.prototype, 'onInit'); collectorExporterConfig = { - hostname: 'foo', url: 'http://foo.bar.com', }; collectorTraceExporter = new OTLPTraceExporter(collectorExporterConfig); @@ -68,26 +64,11 @@ describe('OTLPTraceExporter - web', () => { it('should create an instance', () => { assert.ok(typeof collectorTraceExporter !== 'undefined'); }); - - it('should call onInit', () => { - assert.strictEqual(onInitSpy.callCount, 1); - }); - - describe('when config contains certain params', () => { - it('should set hostname', () => { - assert.strictEqual(collectorTraceExporter.hostname, 'foo'); - }); - - it('should set url', () => { - assert.strictEqual(collectorTraceExporter.url, 'http://foo.bar.com'); - }); - }); }); describe('export', () => { beforeEach(() => { collectorExporterConfig = { - hostname: 'foo', url: 'http://foo.bar.com', }; }); @@ -202,29 +183,36 @@ describe('OTLPTraceExporter - web', () => { collectorTraceExporter.export(spans, () => {}); queueMicrotask(async () => { - const request = server.requests[0]; - assert.strictEqual(request.method, 'POST'); - assert.strictEqual(request.url, 'http://foo.bar.com'); - - const body = request.requestBody as Blob; - const decoder = new TextDecoder(); - const json = JSON.parse( - decoder.decode(await body.arrayBuffer()) - ) as IExportTraceServiceRequest; - const span1 = json.resourceSpans?.[0].scopeSpans?.[0].spans?.[0]; + try { + const request = server.requests[0]; + assert.strictEqual(request.method, 'POST'); + assert.strictEqual(request.url, 'http://foo.bar.com'); + + const body = request.requestBody as Blob; + const decoder = new TextDecoder(); + const json = JSON.parse( + decoder.decode(await body.arrayBuffer()) + ) as IExportTraceServiceRequest; + const span1 = json.resourceSpans?.[0].scopeSpans?.[0].spans?.[0]; - assert.ok(typeof span1 !== 'undefined', "span doesn't exist"); - ensureSpanIsCorrect(span1); + assert.ok(typeof span1 !== 'undefined', "span doesn't exist"); + ensureSpanIsCorrect(span1); - const resource = json.resourceSpans?.[0].resource; - assert.ok(typeof resource !== 'undefined', "resource doesn't exist"); - ensureWebResourceIsCorrect(resource); + const resource = json.resourceSpans?.[0].resource; + assert.ok( + typeof resource !== 'undefined', + "resource doesn't exist" + ); + ensureWebResourceIsCorrect(resource); - assert.strictEqual(stubBeacon.callCount, 0); - ensureExportTraceServiceRequestIsSet(json); + assert.strictEqual(stubBeacon.callCount, 0); + ensureExportTraceServiceRequestIsSet(json); - clock.restore(); - done(); + clock.restore(); + done(); + } catch (e) { + done(e); + } }); }); @@ -496,27 +484,6 @@ describe('OTLPTraceExporter - web', () => { }); }); -describe('OTLPTraceExporter - browser (getDefaultUrl)', () => { - it('should default to v1/trace', done => { - const collectorExporter = new OTLPTraceExporter({}); - setTimeout(() => { - assert.strictEqual( - collectorExporter['url'], - 'http://localhost:4318/v1/traces' - ); - done(); - }); - }); - it('should keep the URL if included', done => { - const url = 'http://foo.bar.com'; - const collectorExporter = new OTLPTraceExporter({ url }); - setTimeout(() => { - assert.strictEqual(collectorExporter['url'], url); - done(); - }); - }); -}); - describe('export with retry - real http request destroyed', () => { let server: any; let collectorTraceExporter: OTLPTraceExporter; @@ -563,7 +530,7 @@ describe('export with retry - real http request destroyed', () => { error.message, 'Export failed with retryable status' ); - assert.strictEqual(calls, 6); + assert.strictEqual(calls, 2); done(); } catch (e) { done(e); 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 56f72c08d7..b36be29534 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 @@ -35,7 +35,6 @@ import { } from '../traceHelper'; import { MockedResponse } from './nodeHelpers'; import { IExportTraceServiceRequest } from '@opentelemetry/otlp-transformer'; -import { VERSION } from '../../src/version'; let fakeRequest: PassThrough; @@ -80,144 +79,6 @@ describe('OTLPTraceExporter - node with json over http', () => { }); }); - describe('when configuring via environment', () => { - const envSource = process.env; - it('should use url defined in env that ends with root path and append version and signal path', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/'; - const collectorExporter = new OTLPTraceExporter(); - assert.strictEqual( - collectorExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_ENDPOINT}v1/traces` - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - }); - it('should use url defined in env without checking if path is already present', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/v1/traces'; - const collectorExporter = new OTLPTraceExporter(); - assert.strictEqual( - collectorExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_ENDPOINT}/v1/traces` - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - }); - it('should use url defined in env and append version and signal', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar'; - const collectorExporter = new OTLPTraceExporter(); - assert.strictEqual( - collectorExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_ENDPOINT}/v1/traces` - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - }); - it('should override global exporter url with signal url defined in env', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/'; - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = 'http://foo.traces/'; - const collectorExporter = new OTLPTraceExporter(); - assert.strictEqual( - collectorExporter.url, - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = ''; - }); - it('should override url defined in env with url defined in constructor', () => { - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = 'http://foo.bar'; - const constructorDefinedEndpoint = 'http://constructor/v1/traces'; - const collectorExporter = new OTLPTraceExporter({ - url: constructorDefinedEndpoint, - }); - assert.strictEqual(collectorExporter.url, constructorDefinedEndpoint); - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = ''; - }); - it('should add root path when signal url defined in env contains no path and no root path', () => { - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = 'http://foo.bar'; - const collectorExporter = new OTLPTraceExporter(); - assert.strictEqual( - collectorExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT}/` - ); - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = ''; - }); - it('should not add root path when signal url defined in env contains root path but no path', () => { - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = 'http://foo.bar/'; - const collectorExporter = new OTLPTraceExporter(); - assert.strictEqual( - collectorExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT}` - ); - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = ''; - }); - it('should not add root path when signal url defined in env contains path', () => { - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = 'http://foo.bar/v1/traces'; - const collectorExporter = new OTLPTraceExporter(); - assert.strictEqual( - collectorExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT}` - ); - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = ''; - }); - it('should not add root path when signal url defined in env contains path and ends in /', () => { - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = - 'http://foo.bar/v1/traces/'; - const collectorExporter = new OTLPTraceExporter(); - assert.strictEqual( - collectorExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT}` - ); - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = ''; - }); - it('should use headers defined via env', () => { - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar'; - const exporter = new OTLPTraceExporter(); - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers']['foo'], - 'bar' - ); - envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; - }); - it('should include user agent in header', () => { - const exporter = new OTLPTraceExporter(); - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers'][ - 'User-Agent' - ], - `OTel-OTLP-Exporter-JavaScript/${VERSION}` - ); - }); - it('should override global headers config with signal headers defined via env', () => { - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar,bar=foo'; - envSource.OTEL_EXPORTER_OTLP_TRACES_HEADERS = 'foo=boo'; - const exporter = new OTLPTraceExporter(); - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers']['foo'], - 'boo' - ); - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers']['bar'], - 'foo' - ); - envSource.OTEL_EXPORTER_OTLP_TRACES_HEADERS = ''; - envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; - }); - it('should override headers defined via env with headers defined in constructor', () => { - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar,bar=foo'; - const exporter = new OTLPTraceExporter({ - headers: { - foo: 'constructor', - }, - }); - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers']['foo'], - 'constructor' - ); - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers']['bar'], - 'foo' - ); - envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; - }); - }); - describe('export', () => { beforeEach(() => { stubRequest = sinon.stub(http, 'request').returns(fakeRequest as any); @@ -225,7 +86,6 @@ describe('OTLPTraceExporter - node with json over http', () => { headers: { foo: 'bar', }, - hostname: 'foo', url: 'http://foo.bar.com', keepAlive: true, httpAgentOptions: { keepAliveMsecs: 2000 }, @@ -439,7 +299,6 @@ describe('OTLPTraceExporter - node with json over http', () => { headers: { foo: 'bar', }, - hostname: 'foo', url: 'http://foo.bar.com', keepAlive: true, compression: CompressionAlgorithm.GZIP, @@ -481,27 +340,6 @@ describe('OTLPTraceExporter - node with json over http', () => { }); }); - describe('OTLPTraceExporter - node (getDefaultUrl)', () => { - it('should default to localhost', done => { - const collectorExporter = new OTLPTraceExporter(); - setTimeout(() => { - assert.strictEqual( - collectorExporter['url'], - 'http://localhost:4318/v1/traces' - ); - done(); - }); - }); - - it('should keep the URL if included', done => { - const url = 'http://foo.bar.com'; - const collectorExporter = new OTLPTraceExporter({ url }); - setTimeout(() => { - assert.strictEqual(collectorExporter['url'], url); - done(); - }); - }); - }); describe('export - with timeout', () => { beforeEach(() => { fakeRequest = new Stream.PassThrough(); @@ -516,7 +354,6 @@ describe('OTLPTraceExporter - node with json over http', () => { headers: { foo: 'bar', }, - hostname: 'foo', url: 'http://foo.bar.com', keepAlive: true, httpAgentOptions: { keepAliveMsecs: 2000 }, diff --git a/experimental/packages/exporter-trace-otlp-proto/src/platform/browser/OTLPTraceExporter.ts b/experimental/packages/exporter-trace-otlp-proto/src/platform/browser/OTLPTraceExporter.ts index 82d4bc389d..85d208b741 100644 --- a/experimental/packages/exporter-trace-otlp-proto/src/platform/browser/OTLPTraceExporter.ts +++ b/experimental/packages/exporter-trace-otlp-proto/src/platform/browser/OTLPTraceExporter.ts @@ -25,7 +25,6 @@ import { } from '@opentelemetry/otlp-transformer'; const DEFAULT_COLLECTOR_RESOURCE_PATH = 'v1/traces'; -const DEFAULT_COLLECTOR_URL = `http://localhost:4318/${DEFAULT_COLLECTOR_RESOURCE_PATH}`; /** * Collector Trace Exporter for Web @@ -35,14 +34,11 @@ export class OTLPTraceExporter implements SpanExporter { constructor(config: OTLPExporterConfigBase = {}) { - super(config, ProtobufTraceSerializer, 'application/x-protobuf'); - } - - getDefaultUrl(config: OTLPExporterConfigBase): string { - if (typeof config.url === 'string') { - return config.url; - } - - return DEFAULT_COLLECTOR_URL; + super( + config, + ProtobufTraceSerializer, + { 'Content-Type': 'application/x-protobuf' }, + DEFAULT_COLLECTOR_RESOURCE_PATH + ); } } diff --git a/experimental/packages/exporter-trace-otlp-proto/src/platform/node/OTLPTraceExporter.ts b/experimental/packages/exporter-trace-otlp-proto/src/platform/node/OTLPTraceExporter.ts index 79da4ddc28..1e1b35a230 100644 --- a/experimental/packages/exporter-trace-otlp-proto/src/platform/node/OTLPTraceExporter.ts +++ b/experimental/packages/exporter-trace-otlp-proto/src/platform/node/OTLPTraceExporter.ts @@ -15,13 +15,9 @@ */ import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'; -import { getEnv, baggageUtils } from '@opentelemetry/core'; import { OTLPExporterNodeConfigBase, - appendResourcePathToUrl, - appendRootPathToUrlIfNeeded, OTLPExporterNodeBase, - parseHeaders, } from '@opentelemetry/otlp-exporter-base'; import { IExportTraceServiceResponse, @@ -29,8 +25,6 @@ import { } from '@opentelemetry/otlp-transformer'; import { VERSION } from '../../version'; -const DEFAULT_COLLECTOR_RESOURCE_PATH = 'v1/traces'; -const DEFAULT_COLLECTOR_URL = `http://localhost:4318/${DEFAULT_COLLECTOR_RESOURCE_PATH}`; const USER_AGENT = { 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, }; @@ -43,35 +37,15 @@ export class OTLPTraceExporter implements SpanExporter { constructor(config: OTLPExporterNodeConfigBase = {}) { - super(config, ProtobufTraceSerializer, { - ...baggageUtils.parseKeyPairsIntoRecord( - getEnv().OTEL_EXPORTER_OTLP_TRACES_HEADERS - ), - ...parseHeaders(config?.headers), - ...USER_AGENT, - 'Content-Type': 'application/x-protobuf', - }); - } - - getDefaultUrl(config: OTLPExporterNodeConfigBase) { - if (typeof config.url === 'string') { - return config.url; - } - - const env = getEnv(); - if (env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT.length > 0) { - return appendRootPathToUrlIfNeeded( - env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT - ); - } - - if (env.OTEL_EXPORTER_OTLP_ENDPOINT.length > 0) { - return appendResourcePathToUrl( - env.OTEL_EXPORTER_OTLP_ENDPOINT, - DEFAULT_COLLECTOR_RESOURCE_PATH - ); - } - - return DEFAULT_COLLECTOR_URL; + super( + config, + ProtobufTraceSerializer, + { + ...USER_AGENT, + 'Content-Type': 'application/x-protobuf', + }, + 'TRACES', + 'v1/traces' + ); } } diff --git a/experimental/packages/exporter-trace-otlp-proto/test/browser/CollectorTraceExporter.test.ts b/experimental/packages/exporter-trace-otlp-proto/test/browser/CollectorTraceExporter.test.ts index e8187e7296..264d0051f5 100644 --- a/experimental/packages/exporter-trace-otlp-proto/test/browser/CollectorTraceExporter.test.ts +++ b/experimental/packages/exporter-trace-otlp-proto/test/browser/CollectorTraceExporter.test.ts @@ -21,9 +21,7 @@ import { OTLPTraceExporter } from '../../src/platform/browser/index'; describe('OTLPTraceExporter - web', () => { let collectorTraceExporter: OTLPTraceExporter; describe('constructor', () => { - let onInitSpy: any; beforeEach(() => { - onInitSpy = sinon.stub(OTLPTraceExporter.prototype, 'onInit'); const collectorExporterConfig = { hostname: 'foo', url: 'http://foo.bar.com', @@ -36,15 +34,5 @@ describe('OTLPTraceExporter - web', () => { it('should create an instance', () => { assert.ok(typeof collectorTraceExporter !== 'undefined'); }); - it('should call onInit', () => { - assert.strictEqual(onInitSpy.callCount, 1); - }); - it('should set hostname', () => { - assert.strictEqual(collectorTraceExporter.hostname, 'foo'); - }); - - it('should set url', () => { - assert.strictEqual(collectorTraceExporter.url, 'http://foo.bar.com'); - }); }); }); diff --git a/experimental/packages/exporter-trace-otlp-proto/test/node/OTLPTraceExporter.test.ts b/experimental/packages/exporter-trace-otlp-proto/test/node/OTLPTraceExporter.test.ts index 6adf335c1a..b034778e33 100644 --- a/experimental/packages/exporter-trace-otlp-proto/test/node/OTLPTraceExporter.test.ts +++ b/experimental/packages/exporter-trace-otlp-proto/test/node/OTLPTraceExporter.test.ts @@ -81,142 +81,12 @@ describe('OTLPTraceExporter - node with proto over http', () => { }); }); - describe('when configuring via environment', () => { - const envSource = process.env; - it('should use url defined in env that ends with root path and append version and signal path', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/'; - const collectorExporter = new OTLPTraceExporter(); - assert.strictEqual( - collectorExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_ENDPOINT}v1/traces` - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - }); - it('should use url defined in env without checking if path is already present', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/v1/traces'; - const collectorExporter = new OTLPTraceExporter(); - assert.strictEqual( - collectorExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_ENDPOINT}/v1/traces` - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - }); - it('should use url defined in env and append version and signal', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar'; - const collectorExporter = new OTLPTraceExporter(); - assert.strictEqual( - collectorExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_ENDPOINT}/v1/traces` - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - }); - it('should override global exporter url with signal url defined in env', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/'; - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = 'http://foo.traces/'; - const collectorExporter = new OTLPTraceExporter(); - assert.strictEqual( - collectorExporter.url, - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = ''; - }); - it('should override url defined in env with url defined in constructor', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/'; - const constructorDefinedEndpoint = 'http://constructor/v1/traces'; - const collectorExporter = new OTLPTraceExporter({ - url: constructorDefinedEndpoint, - }); - assert.strictEqual(collectorExporter.url, constructorDefinedEndpoint); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - }); - it('should add root path when signal url defined in env contains no path and no root path', () => { - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = 'http://foo.bar'; - const collectorExporter = new OTLPTraceExporter(); - assert.strictEqual( - collectorExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT}/` - ); - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = ''; - }); - it('should not add root path when signal url defined in env contains root path but no path', () => { - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = 'http://foo.bar/'; - const collectorExporter = new OTLPTraceExporter(); - assert.strictEqual( - collectorExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT}` - ); - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = ''; - }); - it('should not add root path when signal url defined in env contains path', () => { - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = 'http://foo.bar/v1/traces'; - const collectorExporter = new OTLPTraceExporter(); - assert.strictEqual( - collectorExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT}` - ); - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = ''; - }); - it('should not add root path when signal url defined in env contains path and ends in /', () => { - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = - 'http://foo.bar/v1/traces/'; - const collectorExporter = new OTLPTraceExporter(); - assert.strictEqual( - collectorExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT}` - ); - envSource.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = ''; - }); - it('should use headers defined via env', () => { - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar'; - const exporter = new OTLPTraceExporter(); - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers']['foo'], - 'bar' - ); - envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; - }); - it('should override global headers config with signal headers defined via env', () => { - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar,bar=foo'; - envSource.OTEL_EXPORTER_OTLP_TRACES_HEADERS = 'foo=boo'; - const exporter = new OTLPTraceExporter(); - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers']['foo'], - 'boo' - ); - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers']['bar'], - 'foo' - ); - envSource.OTEL_EXPORTER_OTLP_TRACES_HEADERS = ''; - envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; - }); - it('should override headers defined via env with headers defined in constructor', () => { - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar,bar=foo'; - const exporter = new OTLPTraceExporter({ - headers: { - foo: 'constructor', - }, - }); - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers']['foo'], - 'constructor' - ); - assert.strictEqual( - exporter['_transport']['_transport']['_parameters']['headers']['bar'], - 'foo' - ); - envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; - }); - }); - describe('export', () => { beforeEach(() => { collectorExporterConfig = { headers: { foo: 'bar', }, - hostname: 'foo', url: 'http://foo.bar.com', keepAlive: true, httpAgentOptions: { keepAliveMsecs: 2000 }, @@ -366,7 +236,6 @@ describe('OTLPTraceExporter - node with proto over http', () => { headers: { foo: 'bar', }, - hostname: 'foo', url: 'http://foo.bar.com', keepAlive: true, compression: CompressionAlgorithm.GZIP, diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/src/OTLPMetricExporter.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/src/OTLPMetricExporter.ts index d7d3c1eea3..2602911aa1 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/src/OTLPMetricExporter.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/src/OTLPMetricExporter.ts @@ -22,55 +22,23 @@ import { ResourceMetrics } from '@opentelemetry/sdk-metrics'; import { OTLPGRPCExporterConfigNode, OTLPGRPCExporterNodeBase, - validateAndNormalizeUrl, - DEFAULT_COLLECTOR_URL, } from '@opentelemetry/otlp-grpc-exporter-base'; -import { baggageUtils, getEnv } from '@opentelemetry/core'; import { IExportMetricsServiceResponse, ProtobufMetricsSerializer, } from '@opentelemetry/otlp-transformer'; -import { VERSION } from './version'; -import { parseHeaders } from '@opentelemetry/otlp-exporter-base'; - -const USER_AGENT = { - 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, -}; class OTLPMetricExporterProxy extends OTLPGRPCExporterNodeBase< ResourceMetrics, IExportMetricsServiceResponse > { constructor(config?: OTLPGRPCExporterConfigNode & OTLPMetricExporterOptions) { - const signalSpecificMetadata = { - ...USER_AGENT, - ...baggageUtils.parseKeyPairsIntoRecord( - getEnv().OTEL_EXPORTER_OTLP_METRICS_HEADERS - ), - ...parseHeaders(config?.headers), - }; super( config, - signalSpecificMetadata, + ProtobufMetricsSerializer, 'MetricsExportService', '/opentelemetry.proto.collector.metrics.v1.MetricsService/Export', - ProtobufMetricsSerializer - ); - } - - getDefaultUrl(config: OTLPGRPCExporterConfigNode): string { - return validateAndNormalizeUrl(this.getUrlFromConfig(config)); - } - - getUrlFromConfig(config: OTLPGRPCExporterConfigNode): string { - if (typeof config.url === 'string') { - return config.url; - } - - return ( - getEnv().OTEL_EXPORTER_OTLP_METRICS_ENDPOINT || - getEnv().OTEL_EXPORTER_OTLP_ENDPOINT || - DEFAULT_COLLECTOR_URL + 'METRICS' ); } } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/OTLPMetricExporter.test.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/OTLPMetricExporter.test.ts index 9ffd1d0d95..833fa2ba7d 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/OTLPMetricExporter.test.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-grpc/test/OTLPMetricExporter.test.ts @@ -41,7 +41,6 @@ import { IExportMetricsServiceRequest, IResourceMetrics, } from '@opentelemetry/otlp-transformer'; -import { VERSION } from '../src/version'; import { AggregationTemporalityPreference } from '@opentelemetry/exporter-metrics-otlp-http'; const metricsServiceProtoPath = @@ -302,112 +301,6 @@ const testOTLPMetricExporter = (params: TestParams) => { }); }; -describe('OTLPMetricExporter - node (getDefaultUrl)', () => { - it('should default to localhost', done => { - const collectorExporter = new OTLPMetricExporter(); - setTimeout(() => { - assert.strictEqual(collectorExporter._otlpExporter.url, 'localhost:4317'); - done(); - }); - }); - it('should keep the URL if included', done => { - const url = 'http://foo.bar.com'; - const collectorExporter = new OTLPMetricExporter({ - url, - temporalityPreference: AggregationTemporalityPreference.CUMULATIVE, - }); - setTimeout(() => { - assert.strictEqual(collectorExporter._otlpExporter.url, 'foo.bar.com'); - done(); - }); - }); -}); - -describe('when configuring via environment', () => { - afterEach(function () { - // Ensure we don't pollute other tests if assertions fail - delete envSource.OTEL_EXPORTER_OTLP_ENDPOINT; - delete envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT; - delete envSource.OTEL_EXPORTER_OTLP_HEADERS; - delete envSource.OTEL_EXPORTER_OTLP_METRICS_HEADERS; - sinon.restore(); - }); - - const envSource = process.env; - it('should use url defined in env', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar'; - const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual(collectorExporter._otlpExporter.url, 'foo.bar'); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - }); - it('should override global exporter url with signal url defined in env', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar'; - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = 'http://foo.metrics'; - const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual(collectorExporter._otlpExporter.url, 'foo.metrics'); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = ''; - }); - it('should use override url defined in env with url defined in constructor', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/v1/metrics'; - const constructorDefinedEndpoint = 'http://constructor/v1/metrics'; - const collectorExporter = new OTLPMetricExporter({ - url: constructorDefinedEndpoint, - }); - assert.strictEqual(collectorExporter._otlpExporter.url, 'constructor'); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - }); - it('should use headers defined via env', () => { - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar'; - const collectorExporter = new OTLPMetricExporter(); - const actualMetadata = - collectorExporter._otlpExporter['_transport']['_parameters'].metadata(); - assert.deepStrictEqual(actualMetadata.get('foo'), ['bar']); - envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; - }); - it('should include user agent in header', () => { - const collectorExporter = new OTLPMetricExporter(); - const actualMetadata = - collectorExporter._otlpExporter['_transport']['_parameters'].metadata(); - assert.deepStrictEqual(actualMetadata.get('User-Agent'), [ - `OTel-OTLP-Exporter-JavaScript/${VERSION}`, - ]); - }); - it('should not override hard-coded headers config with headers defined via env', () => { - const metadata = new grpc.Metadata(); - metadata.set('foo', 'bar'); - metadata.set('goo', 'lol'); - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=jar,bar=foo'; - envSource.OTEL_EXPORTER_OTLP_METRICS_HEADERS = 'foo=boo'; - const collectorExporter = new OTLPMetricExporter({ - metadata, - temporalityPreference: AggregationTemporalityPreference.CUMULATIVE, - }); - const actualMetadata = - collectorExporter._otlpExporter['_transport']['_parameters'].metadata(); - assert.deepStrictEqual(actualMetadata.get('foo'), ['bar']); - assert.deepStrictEqual(actualMetadata.get('bar'), ['foo']); - assert.deepStrictEqual(actualMetadata.get('goo'), ['lol']); - envSource.OTEL_EXPORTER_OTLP_METRICS_HEADERS = ''; - envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; - }); - - it('should override headers defined via env with headers defined in constructor', () => { - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar,bar=foo'; - const collectorExporter = new OTLPMetricExporter({ - headers: { - foo: 'constructor', - }, - }); - - const actualMetadata = - collectorExporter._otlpExporter['_transport']['_parameters'].metadata(); - assert.deepStrictEqual(actualMetadata.get('foo'), ['constructor']); - assert.deepStrictEqual(actualMetadata.get('bar'), ['foo']); - envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; - }); -}); - testOTLPMetricExporter({ useTLS: true }); testOTLPMetricExporter({ useTLS: false }); testOTLPMetricExporter({ metadata }); diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/browser/OTLPMetricExporter.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/browser/OTLPMetricExporter.ts index b84c194f91..828cd61b0c 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/browser/OTLPMetricExporter.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/browser/OTLPMetricExporter.ts @@ -27,22 +27,18 @@ import { } from '@opentelemetry/otlp-transformer'; const DEFAULT_COLLECTOR_RESOURCE_PATH = 'v1/metrics'; -const DEFAULT_COLLECTOR_URL = `http://localhost:4318/${DEFAULT_COLLECTOR_RESOURCE_PATH}`; class OTLPExporterBrowserProxy extends OTLPExporterBrowserBase< ResourceMetrics, IExportMetricsServiceResponse > { constructor(config?: OTLPMetricExporterOptions & OTLPExporterConfigBase) { - super(config, JsonMetricsSerializer, 'application/json'); - } - - getDefaultUrl(config: OTLPExporterConfigBase): string { - if (typeof config.url === 'string') { - return config.url; - } - - return DEFAULT_COLLECTOR_URL; + super( + config, + JsonMetricsSerializer, + { 'Content-Type': 'application/json' }, + DEFAULT_COLLECTOR_RESOURCE_PATH + ); } } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/node/OTLPMetricExporter.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/node/OTLPMetricExporter.ts index bf57b807dc..368858190a 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/node/OTLPMetricExporter.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/src/platform/node/OTLPMetricExporter.ts @@ -15,15 +15,11 @@ */ import { ResourceMetrics } from '@opentelemetry/sdk-metrics'; -import { getEnv, baggageUtils } from '@opentelemetry/core'; import { OTLPMetricExporterOptions } from '../../OTLPMetricExporterOptions'; import { OTLPMetricExporterBase } from '../../OTLPMetricExporterBase'; import { OTLPExporterNodeBase, OTLPExporterNodeConfigBase, - appendResourcePathToUrl, - appendRootPathToUrlIfNeeded, - parseHeaders, } from '@opentelemetry/otlp-exporter-base'; import { IExportMetricsServiceResponse, @@ -31,8 +27,6 @@ import { } from '@opentelemetry/otlp-transformer'; import { VERSION } from '../../version'; -const DEFAULT_COLLECTOR_RESOURCE_PATH = 'v1/metrics'; -const DEFAULT_COLLECTOR_URL = `http://localhost:4318/${DEFAULT_COLLECTOR_RESOURCE_PATH}`; const USER_AGENT = { 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, }; @@ -42,36 +36,16 @@ class OTLPExporterNodeProxy extends OTLPExporterNodeBase< IExportMetricsServiceResponse > { constructor(config?: OTLPExporterNodeConfigBase & OTLPMetricExporterOptions) { - super(config, JsonMetricsSerializer, { - ...baggageUtils.parseKeyPairsIntoRecord( - getEnv().OTEL_EXPORTER_OTLP_METRICS_HEADERS - ), - ...parseHeaders(config?.headers), - ...USER_AGENT, - 'Content-Type': 'application/json', - }); - } - - getDefaultUrl(config: OTLPExporterNodeConfigBase): string { - if (typeof config.url === 'string') { - return config.url; - } - - const env = getEnv(); - if (env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT.length > 0) { - return appendRootPathToUrlIfNeeded( - env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT - ); - } - - if (env.OTEL_EXPORTER_OTLP_ENDPOINT.length > 0) { - return appendResourcePathToUrl( - env.OTEL_EXPORTER_OTLP_ENDPOINT, - DEFAULT_COLLECTOR_RESOURCE_PATH - ); - } - - return DEFAULT_COLLECTOR_URL; + super( + config, + JsonMetricsSerializer, + { + ...USER_AGENT, + 'Content-Type': 'application/json', + }, + 'METRICS', + 'v1/metrics' + ); } } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/CollectorMetricExporter.test.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/CollectorMetricExporter.test.ts index e4ae9f3926..e6e646f06a 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/CollectorMetricExporter.test.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/common/CollectorMetricExporter.test.ts @@ -36,8 +36,6 @@ class OTLPMetricExporter extends OTLPExporterBase< CollectorExporterConfig, ResourceMetrics > { - onInit() {} - onShutdown() {} send() {} @@ -62,12 +60,8 @@ describe('OTLPMetricExporter - common', () => { }); describe('constructor', () => { - let onInitSpy: any; - beforeEach(async () => { - onInitSpy = sinon.stub(OTLPMetricExporter.prototype, 'onInit'); collectorExporterConfig = { - hostname: 'foo', url: 'http://foo.bar.com', }; collectorExporter = new OTLPMetricExporter(collectorExporterConfig); @@ -87,20 +81,6 @@ describe('OTLPMetricExporter - common', () => { assert.ok(typeof collectorExporter !== 'undefined'); }); - it('should call onInit', () => { - assert.strictEqual(onInitSpy.callCount, 1); - }); - - describe('when config contains certain params', () => { - it('should set hostname', () => { - assert.strictEqual(collectorExporter.hostname, 'foo'); - }); - - it('should set url', () => { - assert.strictEqual(collectorExporter.url, 'http://foo.bar.com'); - }); - }); - describe('when config is missing certain params', () => { beforeEach(() => { collectorExporter = new OTLPMetricExporter(); @@ -180,7 +160,6 @@ describe('OTLPMetricExporter - common', () => { beforeEach(() => { onShutdownSpy = sinon.stub(OTLPMetricExporter.prototype, 'onShutdown'); collectorExporterConfig = { - hostname: 'foo', url: 'http://foo.bar.com', }; collectorExporter = new OTLPMetricExporter(collectorExporterConfig); diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/node/CollectorMetricExporter.test.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/node/CollectorMetricExporter.test.ts index 7b19b84f02..4b11c946cc 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/node/CollectorMetricExporter.test.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-http/test/node/CollectorMetricExporter.test.ts @@ -21,9 +21,6 @@ import * as http from 'http'; import * as sinon from 'sinon'; import { AggregationTemporalityPreference, - CumulativeTemporalitySelector, - DeltaTemporalitySelector, - LowMemoryTemporalitySelector, OTLPMetricExporterOptions, } from '../../src'; @@ -55,7 +52,6 @@ import { OTLPExporterNodeConfigBase, } from '@opentelemetry/otlp-exporter-base'; import { IExportMetricsServiceRequest } from '@opentelemetry/otlp-transformer'; -import { VERSION } from '../../src/version'; let fakeRequest: PassThrough; @@ -248,219 +244,6 @@ describe('OTLPMetricExporter - node with json over http', () => { }); }); - describe('when configuring via environment', () => { - const envSource = process.env; - it('should use url defined in env that ends with root path and append version and signal path', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/'; - const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual( - collectorExporter._otlpExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_ENDPOINT}v1/metrics` - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - }); - it('should use url defined in env without checking if path is already present', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/v1/metrics'; - const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual( - collectorExporter._otlpExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_ENDPOINT}/v1/metrics` - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - }); - it('should use url defined in env and append version and signal', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar'; - const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual( - collectorExporter._otlpExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_ENDPOINT}/v1/metrics` - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - }); - it('should override global exporter url with signal url defined in env', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/'; - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = 'http://foo.metrics/'; - const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual( - collectorExporter._otlpExporter.url, - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = ''; - }); - it('should add root path when signal url defined in env contains no path and no root path', () => { - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = 'http://foo.bar'; - const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual( - collectorExporter._otlpExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT}/` - ); - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = ''; - }); - it('should not add root path when signal url defined in env contains root path but no path', () => { - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = 'http://foo.bar/'; - const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual( - collectorExporter._otlpExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT}` - ); - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = ''; - }); - it('should not add root path when signal url defined in env contains path', () => { - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = - 'http://foo.bar/v1/metrics'; - const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual( - collectorExporter._otlpExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT}` - ); - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = ''; - }); - it('should not add root path when signal url defined in env contains path and ends in /', () => { - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = - 'http://foo.bar/v1/metrics/'; - const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual( - collectorExporter._otlpExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT}` - ); - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = ''; - }); - it('should use override url defined in env with url defined in constructor', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/v1/metrics'; - const constructorDefinedEndpoint = 'http://constructor/v1/metrics'; - const collectorExporter = new OTLPMetricExporter({ - url: constructorDefinedEndpoint, - }); - assert.strictEqual( - collectorExporter._otlpExporter.url, - constructorDefinedEndpoint - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - }); - it('should use headers defined via env', () => { - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar'; - const exporter = new OTLPMetricExporter(); - assert.strictEqual( - exporter._otlpExporter['_transport']['_transport']['_parameters'][ - 'headers' - ]['foo'], - 'bar' - ); - envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; - }); - it('should include user agent in header', () => { - const exporter = new OTLPMetricExporter(); - assert.strictEqual( - exporter._otlpExporter['_transport']['_transport']['_parameters'][ - 'headers' - ]['User-Agent'], - `OTel-OTLP-Exporter-JavaScript/${VERSION}` - ); - }); - it('should override global headers config with signal headers defined via env', () => { - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar,bar=foo'; - envSource.OTEL_EXPORTER_OTLP_METRICS_HEADERS = 'foo=boo'; - const exporter = new OTLPMetricExporter(); - assert.strictEqual( - exporter._otlpExporter['_transport']['_transport']['_parameters'][ - 'headers' - ]['foo'], - 'boo' - ); - assert.strictEqual( - exporter._otlpExporter['_transport']['_transport']['_parameters'][ - 'headers' - ]['bar'], - 'foo' - ); - envSource.OTEL_EXPORTER_OTLP_METRICS_HEADERS = ''; - envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; - }); - it('should override headers defined via env with headers defined in constructor', () => { - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar,bar=foo'; - const exporter = new OTLPMetricExporter({ - headers: { - foo: 'constructor', - }, - }); - assert.strictEqual( - exporter._otlpExporter['_transport']['_transport']['_parameters'][ - 'headers' - ]['foo'], - 'constructor' - ); - assert.strictEqual( - exporter._otlpExporter['_transport']['_transport']['_parameters'][ - 'headers' - ]['bar'], - 'foo' - ); - envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; - }); - it('should use delta temporality defined via env', () => { - for (const envValue of ['delta', 'DELTA', 'DeLTa', 'delta ']) { - envSource.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE = envValue; - const exporter = new OTLPMetricExporter(); - assert.strictEqual( - exporter['_aggregationTemporalitySelector'], - DeltaTemporalitySelector - ); - } - }); - it('should use cumulative temporality defined via env', () => { - for (const envValue of [ - 'cumulative', - 'CUMULATIVE', - 'CuMULaTIvE', - 'cumulative ', - ]) { - envSource.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE = envValue; - const exporter = new OTLPMetricExporter(); - assert.strictEqual( - exporter['_aggregationTemporalitySelector'], - CumulativeTemporalitySelector - ); - } - }); - it('should use low memory temporality defined via env', () => { - for (const envValue of [ - 'lowmemory', - 'LOWMEMORY', - 'LoWMeMOrY', - 'lowmemory ', - ]) { - envSource.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE = envValue; - const exporter = new OTLPMetricExporter(); - assert.strictEqual( - exporter['_aggregationTemporalitySelector'], - LowMemoryTemporalitySelector - ); - } - }); - it('should configure cumulative temporality with invalid value in env', () => { - for (const envValue of ['invalid', ' ']) { - envSource.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE = envValue; - const exporter = new OTLPMetricExporter(); - assert.strictEqual( - exporter['_aggregationTemporalitySelector'], - CumulativeTemporalitySelector - ); - } - }); - it('should respect explicit config over environment variable', () => { - envSource.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE = - 'cumulative'; - const exporter = new OTLPMetricExporter({ - temporalityPreference: AggregationTemporalityPreference.DELTA, - }); - assert.strictEqual( - exporter['_aggregationTemporalitySelector'], - DeltaTemporalitySelector - ); - }); - }); - describe('export', () => { beforeEach(async () => { stubRequest = sinon.stub(http, 'request').returns(fakeRequest as any); @@ -468,7 +251,6 @@ describe('OTLPMetricExporter - node with json over http', () => { headers: { foo: 'bar', }, - hostname: 'foo', url: 'http://foo.bar.com', keepAlive: true, httpAgentOptions: { keepAliveMsecs: 2000 }, @@ -677,28 +459,4 @@ describe('OTLPMetricExporter - node with json over http', () => { }); }); }); - describe('OTLPMetricExporter - node (getDefaultUrl)', () => { - it('should default to localhost', done => { - const collectorExporter = new OTLPMetricExporter(); - setTimeout(() => { - assert.strictEqual( - collectorExporter._otlpExporter.url, - 'http://localhost:4318/v1/metrics' - ); - done(); - }); - }); - - it('should keep the URL if included', done => { - const url = 'http://foo.bar.com'; - const collectorExporter = new OTLPMetricExporter({ - url, - temporalityPreference: AggregationTemporalityPreference.CUMULATIVE, - }); - setTimeout(() => { - assert.strictEqual(collectorExporter._otlpExporter.url, url); - done(); - }); - }); - }); }); diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/src/OTLPMetricExporter.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/src/OTLPMetricExporter.ts index 4834c5a698..9f594055a5 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/src/OTLPMetricExporter.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/src/OTLPMetricExporter.ts @@ -15,14 +15,10 @@ */ import { OTLPMetricExporterOptions } from '@opentelemetry/exporter-metrics-otlp-http'; -import { getEnv, baggageUtils } from '@opentelemetry/core'; import { ResourceMetrics } from '@opentelemetry/sdk-metrics'; import { OTLPMetricExporterBase } from '@opentelemetry/exporter-metrics-otlp-http'; import { OTLPExporterNodeConfigBase, - appendResourcePathToUrl, - appendRootPathToUrlIfNeeded, - parseHeaders, OTLPExporterNodeBase, } from '@opentelemetry/otlp-exporter-base'; import { @@ -31,8 +27,6 @@ import { } from '@opentelemetry/otlp-transformer'; import { VERSION } from './version'; -const DEFAULT_COLLECTOR_RESOURCE_PATH = 'v1/metrics'; -const DEFAULT_COLLECTOR_URL = `http://localhost:4318/${DEFAULT_COLLECTOR_RESOURCE_PATH}`; const USER_AGENT = { 'User-Agent': `OTel-OTLP-Exporter-JavaScript/${VERSION}`, }; @@ -42,36 +36,16 @@ class OTLPMetricExporterNodeProxy extends OTLPExporterNodeBase< IExportMetricsServiceResponse > { constructor(config?: OTLPExporterNodeConfigBase & OTLPMetricExporterOptions) { - super(config, ProtobufMetricsSerializer, { - ...baggageUtils.parseKeyPairsIntoRecord( - getEnv().OTEL_EXPORTER_OTLP_METRICS_HEADERS - ), - ...parseHeaders(config?.headers), - ...USER_AGENT, - 'Content-Type': 'application/x-protobuf', - }); - } - - getDefaultUrl(config: OTLPExporterNodeConfigBase): string { - if (typeof config.url === 'string') { - return config.url; - } - - const env = getEnv(); - if (env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT.length > 0) { - return appendRootPathToUrlIfNeeded( - env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT - ); - } - - if (env.OTEL_EXPORTER_OTLP_ENDPOINT.length > 0) { - return appendResourcePathToUrl( - env.OTEL_EXPORTER_OTLP_ENDPOINT, - DEFAULT_COLLECTOR_RESOURCE_PATH - ); - } - - return DEFAULT_COLLECTOR_URL; + super( + config, + ProtobufMetricsSerializer, + { + ...USER_AGENT, + 'Content-Type': 'application/x-protobuf', + }, + 'METRICS', + 'v1/metrics' + ); } } diff --git a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/OTLPMetricExporter.test.ts b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/OTLPMetricExporter.test.ts index 300fd8eb05..42408adab9 100644 --- a/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/OTLPMetricExporter.test.ts +++ b/experimental/packages/opentelemetry-exporter-metrics-otlp-proto/test/OTLPMetricExporter.test.ts @@ -88,156 +88,12 @@ describe('OTLPMetricExporter - node with proto over http', () => { }); }); - describe('when configuring via environment', () => { - const envSource = process.env; - it('should use url defined in env that ends with root path and append version and signal path', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/'; - const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual( - collectorExporter._otlpExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_ENDPOINT}v1/metrics` - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - }); - it('should use url defined in env without checking if path is already present', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/v1/metrics'; - const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual( - collectorExporter._otlpExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_ENDPOINT}/v1/metrics` - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - }); - it('should use url defined in env and append version and signal', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar'; - const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual( - collectorExporter._otlpExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_ENDPOINT}/v1/metrics` - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - }); - it('should override global exporter url with signal url defined in env', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/'; - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = 'http://foo.metrics/'; - const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual( - collectorExporter._otlpExporter.url, - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = ''; - }); - it('should add root path when signal url defined in env contains no path and no root path', () => { - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = 'http://foo.bar'; - const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual( - collectorExporter._otlpExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT}/` - ); - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = ''; - }); - it('should not add root path when signal url defined in env contains root path but no path', () => { - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = 'http://foo.bar/'; - const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual( - collectorExporter._otlpExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT}` - ); - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = ''; - }); - it('should not add root path when signal url defined in env contains path', () => { - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = - 'http://foo.bar/v1/metrics'; - const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual( - collectorExporter._otlpExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT}` - ); - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = ''; - }); - it('should not add root path when signal url defined in env contains path and ends in /', () => { - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = - 'http://foo.bar/v1/metrics/'; - const collectorExporter = new OTLPMetricExporter(); - assert.strictEqual( - collectorExporter._otlpExporter.url, - `${envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT}` - ); - envSource.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = ''; - }); - it('should use override url defined in env with url defined in constructor', () => { - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/v1/metrics'; - const constructorDefinedEndpoint = 'http://constructor/v1/metrics'; - const collectorExporter = new OTLPMetricExporter({ - url: constructorDefinedEndpoint, - }); - assert.strictEqual( - collectorExporter._otlpExporter.url, - constructorDefinedEndpoint - ); - envSource.OTEL_EXPORTER_OTLP_ENDPOINT = ''; - }); - it('should use headers defined via env', () => { - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar'; - const exporter = new OTLPMetricExporter(); - assert.strictEqual( - exporter._otlpExporter['_transport']['_transport']['_parameters'][ - 'headers' - ]['foo'], - 'bar' - ); - envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; - }); - it('should override global headers config with signal headers defined via env', () => { - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar,bar=foo'; - envSource.OTEL_EXPORTER_OTLP_METRICS_HEADERS = 'foo=boo'; - const exporter = new OTLPMetricExporter(); - assert.strictEqual( - exporter._otlpExporter['_transport']['_transport']['_parameters'][ - 'headers' - ]['foo'], - 'boo' - ); - assert.strictEqual( - exporter._otlpExporter['_transport']['_transport']['_parameters'][ - 'headers' - ]['bar'], - 'foo' - ); - envSource.OTEL_EXPORTER_OTLP_METRICS_HEADERS = ''; - envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; - }); - it('should override headers defined via env with headers defined in constructor', () => { - envSource.OTEL_EXPORTER_OTLP_HEADERS = 'foo=bar,bar=foo'; - const exporter = new OTLPMetricExporter({ - headers: { - foo: 'constructor', - }, - }); - assert.strictEqual( - exporter._otlpExporter['_transport']['_transport']['_parameters'][ - 'headers' - ]['foo'], - 'constructor' - ); - assert.strictEqual( - exporter._otlpExporter['_transport']['_transport']['_parameters'][ - 'headers' - ]['bar'], - 'foo' - ); - envSource.OTEL_EXPORTER_OTLP_HEADERS = ''; - }); - }); - describe('export', () => { beforeEach(async () => { collectorExporterConfig = { headers: { foo: 'bar', }, - hostname: 'foo', url: 'http://foo.bar.com', keepAlive: true, httpAgentOptions: { keepAliveMsecs: 2000 }, diff --git a/experimental/packages/otlp-exporter-base/src/OTLPExporterBase.ts b/experimental/packages/otlp-exporter-base/src/OTLPExporterBase.ts index 0e8c37f00d..986447add2 100644 --- a/experimental/packages/otlp-exporter-base/src/OTLPExporterBase.ts +++ b/experimental/packages/otlp-exporter-base/src/OTLPExporterBase.ts @@ -25,7 +25,6 @@ import { OTLPExporterConfigBase, ExportServiceError, } from './types'; -import { configureExporterTimeout } from './util'; /** * Collector Exporter abstract base class @@ -34,12 +33,6 @@ export abstract class OTLPExporterBase< T extends OTLPExporterConfigBase, ExportItem, > { - public readonly url: string; - /** - * @deprecated scheduled for removal. This is only used in tests. - */ - public readonly hostname: string | undefined; - public readonly timeoutMillis: number; protected _concurrencyLimit: number; protected _sendingPromises: Promise[] = []; protected _shutdownOnce: BindOnceFuture; @@ -48,11 +41,6 @@ export abstract class OTLPExporterBase< * @param config */ constructor(config: T = {} as T) { - this.url = this.getDefaultUrl(config); - if (typeof config.hostname === 'string') { - this.hostname = config.hostname; - } - this.shutdown = this.shutdown.bind(this); this._shutdownOnce = new BindOnceFuture(this._shutdown, this); @@ -60,11 +48,6 @@ export abstract class OTLPExporterBase< typeof config.concurrencyLimit === 'number' ? config.concurrencyLimit : 30; - - this.timeoutMillis = configureExporterTimeout(config.timeoutMillis); - - // platform dependent - this.onInit(config); } /** @@ -138,11 +121,9 @@ export abstract class OTLPExporterBase< } abstract onShutdown(): void; - abstract onInit(config: T): void; abstract send( items: ExportItem[], onSuccess: () => void, onError: (error: OTLPExporterError) => void ): void; - abstract getDefaultUrl(config: T): string; } diff --git a/experimental/packages/otlp-exporter-base/src/configuration/otlp-http-configuration.ts b/experimental/packages/otlp-exporter-base/src/configuration/otlp-http-configuration.ts new file mode 100644 index 0000000000..a303bd5c73 --- /dev/null +++ b/experimental/packages/otlp-exporter-base/src/configuration/otlp-http-configuration.ts @@ -0,0 +1,104 @@ +/* + * 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 { + getSharedConfigurationDefaults, + mergeOtlpSharedConfigurationWithDefaults, + OtlpSharedConfiguration, +} from './shared-configuration'; +import { validateAndNormalizeHeaders } from '../util'; + +export interface OtlpHttpConfiguration extends OtlpSharedConfiguration { + url: string; + headers: Record; +} + +function mergeHeaders( + userProvidedHeaders: Record | undefined | null, + fallbackHeaders: Record | undefined | null, + defaultHeaders: Record +): Record { + const requiredHeaders = { + ...defaultHeaders, + }; + const headers = {}; + + // add fallback ones first + if (fallbackHeaders != null) { + Object.assign(headers, fallbackHeaders); + } + + // override with user-provided ones + if (userProvidedHeaders != null) { + Object.assign(headers, userProvidedHeaders); + } + + // override required ones. + return Object.assign(headers, requiredHeaders); +} + +function validateUserProvidedUrl(url: string | undefined): string | undefined { + if (url == null) { + return undefined; + } + try { + new URL(url); + return url; + } catch (e) { + throw new Error( + `Configuration: Could not parse user-provided export URL: '${url}'` + ); + } +} + +/** + * @param userProvidedConfiguration Configuration options provided by the user in code. + * @param fallbackConfiguration Fallback to use when the {@link userProvidedConfiguration} does not specify an option. + * @param defaultConfiguration The defaults as defined by the exporter specification + */ +export function mergeOtlpHttpConfigurationWithDefaults( + userProvidedConfiguration: Partial, + fallbackConfiguration: Partial, + defaultConfiguration: OtlpHttpConfiguration +): OtlpHttpConfiguration { + return { + ...mergeOtlpSharedConfigurationWithDefaults( + userProvidedConfiguration, + fallbackConfiguration, + defaultConfiguration + ), + headers: mergeHeaders( + validateAndNormalizeHeaders(userProvidedConfiguration.headers), + fallbackConfiguration.headers, + defaultConfiguration.headers + ), + url: + validateUserProvidedUrl(userProvidedConfiguration.url) ?? + fallbackConfiguration.url ?? + defaultConfiguration.url, + }; +} + +export function getHttpConfigurationDefaults( + requiredHeaders: Record, + signalResourcePath: string +): OtlpHttpConfiguration { + return { + ...getSharedConfigurationDefaults(), + headers: requiredHeaders, + url: 'http://localhost:4318/' + signalResourcePath, + }; +} diff --git a/experimental/packages/otlp-exporter-base/src/configuration/otlp-http-env-configuration.ts b/experimental/packages/otlp-exporter-base/src/configuration/otlp-http-env-configuration.ts new file mode 100644 index 0000000000..af15de4c85 --- /dev/null +++ b/experimental/packages/otlp-exporter-base/src/configuration/otlp-http-env-configuration.ts @@ -0,0 +1,131 @@ +/* + * 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 { baggageUtils } from '@opentelemetry/core'; +import { diag } from '@opentelemetry/api'; +import { getSharedConfigurationFromEnvironment } from './shared-env-configuration'; +import { OtlpHttpConfiguration } from './otlp-http-configuration'; + +function getHeadersFromEnv(signalIdentifier: string) { + const signalSpecificRawHeaders = + process.env[`OTEL_EXPORTER_OTLP_${signalIdentifier}_HEADERS`]?.trim(); + const nonSignalSpecificRawHeaders = + process.env['OTEL_EXPORTER_OTLP_HEADERS']?.trim(); + + const signalSpecificHeaders = baggageUtils.parseKeyPairsIntoRecord( + signalSpecificRawHeaders + ); + const nonSignalSpecificHeaders = baggageUtils.parseKeyPairsIntoRecord( + nonSignalSpecificRawHeaders + ); + + if ( + Object.keys(signalSpecificHeaders).length === 0 && + Object.keys(nonSignalSpecificHeaders).length === 0 + ) { + return undefined; + } + + // headers are combined instead of overwritten, with the specific headers taking precedence over + // the non-specific ones. + return Object.assign( + {}, + baggageUtils.parseKeyPairsIntoRecord(nonSignalSpecificRawHeaders), + baggageUtils.parseKeyPairsIntoRecord(signalSpecificRawHeaders) + ); +} + +function appendRootPathToUrlIfNeeded(url: string): string | undefined { + try { + const parsedUrl = new URL(url); + // This will automatically append '/' if there's no root path. + return parsedUrl.toString(); + } catch { + diag.warn( + `Configuration: Could not parse environment-provided export URL: '${url}', falling back to undefined` + ); + return undefined; + } +} + +function appendResourcePathToUrl( + url: string, + path: string +): string | undefined { + try { + // just try to parse, if it fails we catch and warn. + new URL(url); + } catch { + diag.warn( + `Configuration: Could not parse environment-provided export URL: '${url}', falling back to undefined` + ); + return undefined; + } + + if (!url.endsWith('/')) { + url = url + '/'; + } + url += path; + + try { + // just try to parse, if it fails we catch and warn. + new URL(url); + } catch { + diag.warn( + `Configuration: Provided URL appended with '${path}' is not a valid URL, using 'undefined' instead of '${url}'` + ); + return undefined; + } + + return url; +} + +function getNonSpecificUrlFromEnv( + signalResourcePath: string +): string | undefined { + const envUrl = process.env.OTEL_EXPORTER_OTLP_ENDPOINT?.trim(); + if (envUrl == null || envUrl === '') { + return undefined; + } + return appendResourcePathToUrl(envUrl, signalResourcePath); +} + +function getSpecificUrlFromEnv(signalIdentifier: string): string | undefined { + const envUrl = + process.env[`OTEL_EXPORTER_OTLP_${signalIdentifier}_ENDPOINT`]?.trim(); + if (envUrl == null || envUrl === '') { + return undefined; + } + return appendRootPathToUrlIfNeeded(envUrl); +} + +/** + * Reads and returns configuration from the environment + * + * @param signalIdentifier all caps part in environment variables that identifies the signal (e.g.: METRICS, TRACES, LOGS) + * @param signalResourcePath signal resource path to append if necessary (e.g.: v1/metrics, v1/traces, v1/logs) + */ +export function getHttpConfigurationFromEnvironment( + signalIdentifier: string, + signalResourcePath: string +): Partial { + return { + ...getSharedConfigurationFromEnvironment(signalIdentifier), + url: + getSpecificUrlFromEnv(signalIdentifier) ?? + getNonSpecificUrlFromEnv(signalResourcePath), + headers: getHeadersFromEnv(signalIdentifier), + }; +} diff --git a/experimental/packages/otlp-exporter-base/src/configuration/shared-configuration.ts b/experimental/packages/otlp-exporter-base/src/configuration/shared-configuration.ts new file mode 100644 index 0000000000..e69fe35bee --- /dev/null +++ b/experimental/packages/otlp-exporter-base/src/configuration/shared-configuration.ts @@ -0,0 +1,77 @@ +/* + * 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. + */ + +/** + * Configuration shared across all OTLP exporters + * + * Implementation note: anything added here MUST be + * - platform-agnostic + * - signal-agnostic + * - transport-agnostic + */ +export interface OtlpSharedConfiguration { + timeoutMillis: number; + concurrencyLimit: number; + compression: 'gzip' | 'none'; +} + +export function validateTimeoutMillis(timeoutMillis: number) { + if ( + !Number.isNaN(timeoutMillis) && + Number.isFinite(timeoutMillis) && + timeoutMillis > 0 + ) { + return timeoutMillis; + } + throw new Error( + `Configuration: timeoutMillis is invalid, expected number greater than 0 (actual: '${timeoutMillis}')` + ); +} + +/** + * @param userProvidedConfiguration Configuration options provided by the user in code. + * @param fallbackConfiguration Fallback to use when the {@link userProvidedConfiguration} does not specify an option. + * @param defaultConfiguration The defaults as defined by the exporter specification + */ +export function mergeOtlpSharedConfigurationWithDefaults( + userProvidedConfiguration: Partial, + fallbackConfiguration: Partial, + defaultConfiguration: OtlpSharedConfiguration +): OtlpSharedConfiguration { + return { + timeoutMillis: validateTimeoutMillis( + userProvidedConfiguration.timeoutMillis ?? + fallbackConfiguration.timeoutMillis ?? + defaultConfiguration.timeoutMillis + ), + concurrencyLimit: + userProvidedConfiguration.concurrencyLimit ?? + fallbackConfiguration.concurrencyLimit ?? + defaultConfiguration.concurrencyLimit, + compression: + userProvidedConfiguration.compression ?? + fallbackConfiguration.compression ?? + defaultConfiguration.compression, + }; +} + +export function getSharedConfigurationDefaults(): OtlpSharedConfiguration { + return { + timeoutMillis: 10000, + concurrencyLimit: 30, + compression: 'none', + }; +} diff --git a/experimental/packages/otlp-exporter-base/src/configuration/shared-env-configuration.ts b/experimental/packages/otlp-exporter-base/src/configuration/shared-env-configuration.ts new file mode 100644 index 0000000000..767ec05f4a --- /dev/null +++ b/experimental/packages/otlp-exporter-base/src/configuration/shared-env-configuration.ts @@ -0,0 +1,88 @@ +/* + * 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 { OtlpSharedConfiguration } from './shared-configuration'; +import { diag } from '@opentelemetry/api'; + +function parseAndValidateTimeoutFromEnv( + timeoutEnvVar: string +): number | undefined { + const envTimeout = process.env[timeoutEnvVar]?.trim(); + if (envTimeout != null && envTimeout !== '') { + const definedTimeout = Number(envTimeout); + if ( + !Number.isNaN(definedTimeout) && + Number.isFinite(definedTimeout) && + definedTimeout > 0 + ) { + return definedTimeout; + } + diag.warn( + `Configuration: ${timeoutEnvVar} is invalid, expected number greater than 0 (actual: ${envTimeout})` + ); + } + return undefined; +} + +function getTimeoutFromEnv(signalIdentifier: string) { + const specificTimeout = parseAndValidateTimeoutFromEnv( + `OTEL_EXPORTER_OTLP_${signalIdentifier}_TIMEOUT` + ); + const nonSpecificTimeout = parseAndValidateTimeoutFromEnv( + 'OTEL_EXPORTER_OTLP_TIMEOUT' + ); + + return specificTimeout ?? nonSpecificTimeout; +} + +function parseAndValidateCompressionFromEnv( + compressionEnvVar: string +): 'none' | 'gzip' | undefined { + const compression = process.env[compressionEnvVar]?.trim(); + if (compression === '') { + return undefined; + } + + if (compression == null || compression === 'none' || compression === 'gzip') { + return compression; + } + + diag.warn( + `Configuration: ${compressionEnvVar} is invalid, expected 'none' or 'gzip' (actual: '${compression}')` + ); + return undefined; +} + +function getCompressionFromEnv( + signalIdentifier: string +): 'none' | 'gzip' | undefined { + const specificCompression = parseAndValidateCompressionFromEnv( + `OTEL_EXPORTER_OTLP_${signalIdentifier}_COMPRESSION` + ); + const nonSpecificCompression = parseAndValidateCompressionFromEnv( + 'OTEL_EXPORTER_OTLP_COMPRESSION' + ); + + return specificCompression ?? nonSpecificCompression; +} + +export function getSharedConfigurationFromEnvironment( + signalIdentifier: string +): Partial { + return { + timeoutMillis: getTimeoutFromEnv(signalIdentifier), + compression: getCompressionFromEnv(signalIdentifier), + }; +} diff --git a/experimental/packages/otlp-exporter-base/src/index.ts b/experimental/packages/otlp-exporter-base/src/index.ts index 2150f6c2c5..5edc2d680a 100644 --- a/experimental/packages/otlp-exporter-base/src/index.ts +++ b/experimental/packages/otlp-exporter-base/src/index.ts @@ -24,13 +24,7 @@ export { OTLPExporterConfigBase, ExportServiceError, } from './types'; -export { - parseHeaders, - appendResourcePathToUrl, - appendRootPathToUrlIfNeeded, - configureExporterTimeout, - invalidTimeout, -} from './util'; +export { validateAndNormalizeHeaders } from './util'; export { ExportResponse, @@ -40,3 +34,11 @@ export { } from './export-response'; export { IExporterTransport } from './exporter-transport'; + +export { + OtlpSharedConfiguration, + mergeOtlpSharedConfigurationWithDefaults, + getSharedConfigurationDefaults, +} from './configuration/shared-configuration'; + +export { getSharedConfigurationFromEnvironment } from './configuration/shared-env-configuration'; diff --git a/experimental/packages/otlp-exporter-base/src/platform/browser/OTLPExporterBrowserBase.ts b/experimental/packages/otlp-exporter-base/src/platform/browser/OTLPExporterBrowserBase.ts index 3313c283d7..da5401698c 100644 --- a/experimental/packages/otlp-exporter-base/src/platform/browser/OTLPExporterBrowserBase.ts +++ b/experimental/packages/otlp-exporter-base/src/platform/browser/OTLPExporterBrowserBase.ts @@ -16,14 +16,16 @@ import { OTLPExporterBase } from '../../OTLPExporterBase'; import { OTLPExporterConfigBase, OTLPExporterError } from '../../types'; -import { parseHeaders } from '../../util'; import { diag } from '@opentelemetry/api'; -import { getEnv, baggageUtils } from '@opentelemetry/core'; import { ISerializer } from '@opentelemetry/otlp-transformer'; import { IExporterTransport } from '../../exporter-transport'; import { createXhrTransport } from './xhr-transport'; import { createSendBeaconTransport } from './send-beacon-transport'; import { createRetryingTransport } from '../../retrying-transport'; +import { + getHttpConfigurationDefaults, + mergeOtlpHttpConfigurationWithDefaults, +} from '../../configuration/otlp-http-configuration'; /** * Collector Metric Exporter abstract base class @@ -34,46 +36,55 @@ export abstract class OTLPExporterBrowserBase< > extends OTLPExporterBase { private _serializer: ISerializer; private _transport: IExporterTransport; + private _timeoutMillis: number; /** * @param config * @param serializer - * @param contentType + * @param requiredHeaders + * @param signalResourcePath */ constructor( config: OTLPExporterConfigBase = {}, serializer: ISerializer, - contentType: string + requiredHeaders: Record, + signalResourcePath: string ) { super(config); this._serializer = serializer; const useXhr = !!config.headers || typeof navigator.sendBeacon !== 'function'; + + const actualConfig = mergeOtlpHttpConfigurationWithDefaults( + { + url: config.url, + timeoutMillis: config.timeoutMillis, + headers: config.headers, + concurrencyLimit: config.concurrencyLimit, + }, + {}, // no fallback for browser case + getHttpConfigurationDefaults(requiredHeaders, signalResourcePath) + ); + + this._timeoutMillis = actualConfig.timeoutMillis; + this._concurrencyLimit = actualConfig.concurrencyLimit; + if (useXhr) { this._transport = createRetryingTransport({ transport: createXhrTransport({ - headers: Object.assign( - {}, - parseHeaders(config.headers), - baggageUtils.parseKeyPairsIntoRecord( - getEnv().OTEL_EXPORTER_OTLP_HEADERS - ), - { 'Content-Type': contentType } - ), - url: this.url, + headers: actualConfig.headers, + url: actualConfig.url, }), }); } else { // sendBeacon has no way to signal retry, so we do not wrap it in a RetryingTransport this._transport = createSendBeaconTransport({ - url: this.url, - blobType: contentType, + url: actualConfig.url, + blobType: actualConfig.headers['Content-Type'], }); } } - onInit(): void {} - onShutdown(): void {} send( @@ -94,7 +105,7 @@ export abstract class OTLPExporterBrowserBase< } const promise = this._transport - .send(data, this.timeoutMillis) + .send(data, this._timeoutMillis) .then(response => { if (response.status === 'success') { onSuccess(); diff --git a/experimental/packages/otlp-exporter-base/src/platform/browser/xhr-transport.ts b/experimental/packages/otlp-exporter-base/src/platform/browser/xhr-transport.ts index 7e36929a6a..6517a994b6 100644 --- a/experimental/packages/otlp-exporter-base/src/platform/browser/xhr-transport.ts +++ b/experimental/packages/otlp-exporter-base/src/platform/browser/xhr-transport.ts @@ -16,8 +16,11 @@ import { IExporterTransport } from '../../exporter-transport'; import { ExportResponse } from '../../export-response'; -import { isExportRetryable, parseRetryAfterToMills } from '../../util'; import { diag } from '@opentelemetry/api'; +import { + isExportRetryable, + parseRetryAfterToMills, +} from '../../is-export-retryable'; export interface XhrRequestParameters { url: string; diff --git a/experimental/packages/otlp-exporter-base/src/platform/node/OTLPExporterNodeBase.ts b/experimental/packages/otlp-exporter-base/src/platform/node/OTLPExporterNodeBase.ts index 2c66d8bca9..f5d858016c 100644 --- a/experimental/packages/otlp-exporter-base/src/platform/node/OTLPExporterNodeBase.ts +++ b/experimental/packages/otlp-exporter-base/src/platform/node/OTLPExporterNodeBase.ts @@ -16,14 +16,18 @@ import { OTLPExporterBase } from '../../OTLPExporterBase'; import { OTLPExporterNodeConfigBase } from './types'; -import { configureCompression } from './util'; import { diag } from '@opentelemetry/api'; -import { getEnv, baggageUtils } from '@opentelemetry/core'; import { ISerializer } from '@opentelemetry/otlp-transformer'; import { IExporterTransport } from '../../exporter-transport'; import { createHttpExporterTransport } from './http-exporter-transport'; import { OTLPExporterError } from '../../types'; import { createRetryingTransport } from '../../retrying-transport'; +import { convertLegacyAgentOptions } from './convert-legacy-agent-options'; +import { + getHttpConfigurationDefaults, + mergeOtlpHttpConfigurationWithDefaults, +} from '../../configuration/otlp-http-configuration'; +import { getHttpConfigurationFromEnvironment } from '../../configuration/otlp-http-env-configuration'; /** * Collector Metric Exporter abstract base class @@ -34,54 +38,47 @@ export abstract class OTLPExporterNodeBase< > extends OTLPExporterBase { private _serializer: ISerializer; private _transport: IExporterTransport; + private _timeoutMillis: number; constructor( config: OTLPExporterNodeConfigBase = {}, serializer: ISerializer, - signalSpecificHeaders: Record + requiredHeaders: Record, + signalIdentifier: string, + signalResourcePath: string ) { super(config); + const actualConfig = mergeOtlpHttpConfigurationWithDefaults( + { + url: config.url, + headers: config.headers, + concurrencyLimit: config.concurrencyLimit, + timeoutMillis: config.timeoutMillis, + compression: config.compression, + }, + getHttpConfigurationFromEnvironment(signalIdentifier, signalResourcePath), + getHttpConfigurationDefaults(requiredHeaders, signalResourcePath) + ); + + this._timeoutMillis = actualConfig.timeoutMillis; + this._concurrencyLimit = actualConfig.concurrencyLimit; + // eslint-disable-next-line @typescript-eslint/no-explicit-any if ((config as any).metadata) { diag.warn('Metadata cannot be set when using http'); } this._serializer = serializer; - // populate keepAlive for use with new settings - if (config?.keepAlive != null) { - if (config.httpAgentOptions != null) { - if (config.httpAgentOptions.keepAlive == null) { - // specific setting is not set, populate with non-specific setting. - config.httpAgentOptions.keepAlive = config.keepAlive; - } - // do nothing, use specific setting otherwise - } else { - // populate specific option if AgentOptions does not exist. - config.httpAgentOptions = { - keepAlive: config.keepAlive, - }; - } - } - const nonSignalSpecificHeaders = baggageUtils.parseKeyPairsIntoRecord( - getEnv().OTEL_EXPORTER_OTLP_HEADERS - ); - this._transport = createRetryingTransport({ transport: createHttpExporterTransport({ - agentOptions: config.httpAgentOptions ?? { keepAlive: true }, - compression: configureCompression(config.compression), - headers: Object.assign( - {}, - nonSignalSpecificHeaders, - signalSpecificHeaders - ), - url: this.url, + agentOptions: convertLegacyAgentOptions(config), + compression: actualConfig.compression, + headers: actualConfig.headers, + url: actualConfig.url, }), }); } - onInit(_config: OTLPExporterNodeConfigBase): void {} - send( objects: ExportItem[], onSuccess: () => void, @@ -100,7 +97,7 @@ export abstract class OTLPExporterNodeBase< } const promise = this._transport - .send(data, this.timeoutMillis) + .send(data, this._timeoutMillis) .then(response => { if (response.status === 'success') { onSuccess(); diff --git a/experimental/packages/otlp-exporter-base/src/platform/node/convert-legacy-agent-options.ts b/experimental/packages/otlp-exporter-base/src/platform/node/convert-legacy-agent-options.ts new file mode 100644 index 0000000000..a73c5b7ead --- /dev/null +++ b/experimental/packages/otlp-exporter-base/src/platform/node/convert-legacy-agent-options.ts @@ -0,0 +1,46 @@ +/* + * 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 { OTLPExporterNodeConfigBase } from './types'; +import type * as http from 'http'; +import type * as https from 'https'; + +/** + * Replicates old config behavior where there's two ways to set keepAlive. + * This function sets keepAlive in AgentOptions if it is defined. In the future, we will remove + * this duplicate to only allow setting keepAlive in AgentOptions. + * @param config + */ +export function convertLegacyAgentOptions( + config: OTLPExporterNodeConfigBase +): http.AgentOptions | https.AgentOptions { + // populate keepAlive for use with new settings + if (config?.keepAlive != null) { + if (config.httpAgentOptions != null) { + if (config.httpAgentOptions.keepAlive == null) { + // specific setting is not set, populate with non-specific setting. + config.httpAgentOptions.keepAlive = config.keepAlive; + } + // do nothing, use specific setting otherwise + } else { + // populate specific option if AgentOptions does not exist. + config.httpAgentOptions = { + keepAlive: config.keepAlive, + }; + } + } + + return config.httpAgentOptions ?? { keepAlive: true }; +} diff --git a/experimental/packages/otlp-exporter-base/src/platform/node/util.ts b/experimental/packages/otlp-exporter-base/src/platform/node/util.ts deleted file mode 100644 index 68125fb299..0000000000 --- a/experimental/packages/otlp-exporter-base/src/platform/node/util.ts +++ /dev/null @@ -1,33 +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 { CompressionAlgorithm } from './types'; -import { getEnv } from '@opentelemetry/core'; - -export function configureCompression( - compression: CompressionAlgorithm | undefined -): CompressionAlgorithm { - if (compression) { - return compression; - } else { - const definedCompression = - getEnv().OTEL_EXPORTER_OTLP_TRACES_COMPRESSION || - getEnv().OTEL_EXPORTER_OTLP_COMPRESSION; - return definedCompression === CompressionAlgorithm.GZIP - ? CompressionAlgorithm.GZIP - : CompressionAlgorithm.NONE; - } -} diff --git a/experimental/packages/otlp-exporter-base/src/types.ts b/experimental/packages/otlp-exporter-base/src/types.ts index a8ee1db743..7964f0048c 100644 --- a/experimental/packages/otlp-exporter-base/src/types.ts +++ b/experimental/packages/otlp-exporter-base/src/types.ts @@ -45,8 +45,7 @@ export interface ExportServiceError { * Collector Exporter base config */ export interface OTLPExporterConfigBase { - headers?: Partial>; - hostname?: string; + headers?: Record; url?: string; concurrencyLimit?: number; /** Maximum time the OTLP exporter will wait for each batch export. diff --git a/experimental/packages/otlp-exporter-base/src/util.ts b/experimental/packages/otlp-exporter-base/src/util.ts index 2824752e10..515b913402 100644 --- a/experimental/packages/otlp-exporter-base/src/util.ts +++ b/experimental/packages/otlp-exporter-base/src/util.ts @@ -15,19 +15,12 @@ */ 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 * @param partialHeaders */ -export function parseHeaders( +export function validateAndNormalizeHeaders( partialHeaders: Partial> = {} ): Record { const headers: Record = {}; @@ -42,100 +35,3 @@ export function parseHeaders( }); return headers; } - -/** - * Adds path (version + signal) to a no per-signal endpoint - * @param url - * @param path - * @returns url + path - */ -export function appendResourcePathToUrl(url: string, path: string): string { - if (!url.endsWith('/')) { - url = url + '/'; - } - return url + path; -} - -/** - * Adds root path to signal specific endpoint when endpoint contains no path part and no root path - * @param url - * @returns url - */ -export function appendRootPathToUrlIfNeeded(url: string): string { - try { - const parsedUrl = new URL(url); - if (parsedUrl.pathname === '') { - parsedUrl.pathname = parsedUrl.pathname + '/'; - } - return parsedUrl.toString(); - } catch { - diag.warn(`Could not parse export URL: '${url}'`); - return url; - } -} - -/** - * Configure exporter trace timeout value from passed in value or environment variables - * @param timeoutMillis - * @returns timeout value in milliseconds - */ -export function configureExporterTimeout( - timeoutMillis: number | undefined -): number { - if (typeof timeoutMillis === 'number') { - if (timeoutMillis <= 0) { - // OTLP exporter configured timeout - using default value of 10000ms - return invalidTimeout(timeoutMillis, DEFAULT_TRACE_TIMEOUT); - } - return timeoutMillis; - } else { - return getExporterTimeoutFromEnv(); - } -} - -function getExporterTimeoutFromEnv(): number { - const definedTimeout = Number( - getEnv().OTEL_EXPORTER_OTLP_TRACES_TIMEOUT ?? - getEnv().OTEL_EXPORTER_OTLP_TIMEOUT - ); - - if (definedTimeout <= 0) { - // OTLP exporter configured timeout - using default value of 10000ms - return invalidTimeout(definedTimeout, DEFAULT_TRACE_TIMEOUT); - } else { - return definedTimeout; - } -} - -// OTLP exporter configured timeout - using default value of 10000ms -export function invalidTimeout( - timeout: number, - defaultTimeout: number -): number { - diag.warn('Timeout must be greater than 0', timeout); - - 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/CollectorExporter.test.ts b/experimental/packages/otlp-exporter-base/test/common/CollectorExporter.test.ts index c6be6965e6..89874f353d 100644 --- a/experimental/packages/otlp-exporter-base/test/common/CollectorExporter.test.ts +++ b/experimental/packages/otlp-exporter-base/test/common/CollectorExporter.test.ts @@ -27,7 +27,6 @@ class OTLPTraceExporter extends OTLPExporterBase< CollectorExporterConfig, ComplexTestObject > { - onInit() {} onShutdown() {} send( items: any[], @@ -55,12 +54,8 @@ describe('OTLPTraceExporter - common', () => { }); describe('constructor', () => { - let onInitSpy: any; - beforeEach(() => { - onInitSpy = sinon.stub(OTLPTraceExporter.prototype, 'onInit'); collectorExporterConfig = { - hostname: 'foo', url: 'http://foo.bar.com', }; collectorExporter = new OTLPTraceExporter(collectorExporterConfig); @@ -69,20 +64,6 @@ describe('OTLPTraceExporter - common', () => { it('should create an instance', () => { assert.ok(typeof collectorExporter !== 'undefined'); }); - - it('should call onInit', () => { - assert.strictEqual(onInitSpy.callCount, 1); - }); - - describe('when config contains certain params', () => { - it('should set hostname', () => { - assert.strictEqual(collectorExporter.hostname, 'foo'); - }); - - it('should set url', () => { - assert.strictEqual(collectorExporter.url, 'http://foo.bar.com'); - }); - }); }); describe('export', () => { @@ -190,7 +171,6 @@ describe('OTLPTraceExporter - common', () => { beforeEach(() => { onShutdownSpy = sinon.stub(OTLPTraceExporter.prototype, 'onShutdown'); collectorExporterConfig = { - hostname: 'foo', url: 'http://foo.bar.com', }; collectorExporter = new OTLPTraceExporter(collectorExporterConfig); diff --git a/experimental/packages/otlp-exporter-base/test/common/configuration/otlp-http-configuration.test.ts b/experimental/packages/otlp-exporter-base/test/common/configuration/otlp-http-configuration.test.ts new file mode 100644 index 0000000000..03d040b14f --- /dev/null +++ b/experimental/packages/otlp-exporter-base/test/common/configuration/otlp-http-configuration.test.ts @@ -0,0 +1,133 @@ +/* + * 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 { + mergeOtlpHttpConfigurationWithDefaults, + OtlpHttpConfiguration, +} from '../../../src/configuration/otlp-http-configuration'; +import * as assert from 'assert'; +import { testSharedConfigBehavior } from './shared-configuration.test'; + +describe('mergeOtlpHttpConfigurationWithDefaults', function () { + const testDefaults: OtlpHttpConfiguration = { + url: 'http://default.example.test', + timeoutMillis: 1, + compression: 'none', + concurrencyLimit: 2, + headers: { 'User-Agent': 'default-user-agent' }, + }; + + describe('headers', function () { + it('merges headers instead of overriding', function () { + const config = mergeOtlpHttpConfigurationWithDefaults( + { + headers: { foo: 'user' }, + }, + { + headers: { foo: 'fallback', bar: 'fallback' }, + }, + testDefaults + ); + assert.deepStrictEqual(config.headers, { + 'User-Agent': 'default-user-agent', + foo: 'user', + bar: 'fallback', + }); + }); + + it('does not override default (required) header', function () { + const config = mergeOtlpHttpConfigurationWithDefaults( + { + headers: { 'User-Agent': 'custom' }, + }, + { + headers: { 'User-Agent': 'custom-fallback' }, + }, + testDefaults + ); + assert.deepStrictEqual(config.headers, { + 'User-Agent': 'default-user-agent', + }); + }); + + it('drops user-provided headers with undefined values', function () { + const config = mergeOtlpHttpConfigurationWithDefaults( + { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore simulating plain JavaScript usage, ignoring types + headers: { foo: 'foo-user-provided', bar: undefined, baz: null }, + }, + {}, + testDefaults + ); + assert.deepStrictEqual(config.headers, { + foo: 'foo-user-provided', + baz: 'null', + 'User-Agent': 'default-user-agent', + }); + }); + }); + + describe('url', function () { + it('uses user provided url over fallback', function () { + const config = mergeOtlpHttpConfigurationWithDefaults( + { + url: 'https://example.test/user-provided', + }, + { + url: 'https://example.test/fallback', + }, + testDefaults + ); + assert.strictEqual(config.url, 'https://example.test/user-provided'); + }); + + it('uses fallback url over default', function () { + const config = mergeOtlpHttpConfigurationWithDefaults( + {}, + { + url: 'https://example.test/fallback', + }, + testDefaults + ); + assert.strictEqual(config.url, 'https://example.test/fallback'); + }); + + it('uses default if none other are specified', function () { + const config = mergeOtlpHttpConfigurationWithDefaults( + {}, + {}, + testDefaults + ); + assert.strictEqual(config.url, testDefaults.url); + }); + + it('throws error when the user-provided url is not parseable', function () { + assert.throws(() => { + mergeOtlpHttpConfigurationWithDefaults( + { url: 'this is not a URL' }, + {}, + testDefaults + ); + }, new Error("Configuration: Could not parse user-provided export URL: 'this is not a URL'")); + }); + }); + + testSharedConfigBehavior( + mergeOtlpHttpConfigurationWithDefaults, + testDefaults + ); +}); diff --git a/experimental/packages/otlp-exporter-base/test/common/configuration/shared-configuration.test.ts b/experimental/packages/otlp-exporter-base/test/common/configuration/shared-configuration.test.ts new file mode 100644 index 0000000000..413ec5e5e6 --- /dev/null +++ b/experimental/packages/otlp-exporter-base/test/common/configuration/shared-configuration.test.ts @@ -0,0 +1,136 @@ +/* + * 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 { + mergeOtlpSharedConfigurationWithDefaults, + OtlpSharedConfiguration, +} from '../../../src'; + +export function testSharedConfigBehavior( + sut: ( + userProvidedConfiguration: Partial, + fallbackConfiguration: Partial, + defaultConfiguration: T + ) => OtlpSharedConfiguration, + defaults: T +): void { + // copy so that we don't modify the original and pollute other tests. + const testDefaults = Object.assign({}, defaults); + + testDefaults.timeoutMillis = 1; + testDefaults.compression = 'none'; + testDefaults.concurrencyLimit = 2; + + describe('timeout', function () { + it('uses user provided timeout over fallback', () => { + const config = sut( + { + timeoutMillis: 222, + }, + { + timeoutMillis: 333, + }, + testDefaults + ); + assert.deepEqual(config.timeoutMillis, 222); + }); + it('uses fallback timeout over default', () => { + const config = sut( + {}, + { + timeoutMillis: 444, + }, + testDefaults + ); + assert.deepEqual(config.timeoutMillis, 444); + }); + it('uses default if none other are specified', () => { + const config = sut({}, {}, testDefaults); + assert.deepEqual(config.timeoutMillis, testDefaults.timeoutMillis); + }); + + it('throws when value is negative', function () { + assert.throws(() => sut({ timeoutMillis: -1 }, {}, testDefaults)); + }); + }); + + describe('compression', function () { + it('uses user provided compression over fallback', () => { + const config = sut( + { + compression: 'gzip', + }, + { + compression: 'none', + }, + testDefaults + ); + assert.deepEqual(config.compression, 'gzip'); + }); + it('uses fallback compression over default', () => { + const config = sut( + {}, + { + compression: 'gzip', + }, + testDefaults + ); + assert.deepEqual(config.compression, 'gzip'); + }); + it('uses default if none other are specified', () => { + const config = sut({}, {}, testDefaults); + assert.deepEqual(config.compression, testDefaults.compression); + }); + }); + + describe('concurrency limit', function () { + it('uses user provided limit over fallback', () => { + const config = sut( + { + concurrencyLimit: 20, + }, + { + concurrencyLimit: 40, + }, + testDefaults + ); + assert.deepEqual(config.concurrencyLimit, 20); + }); + it('uses fallback limit over default', () => { + const config = sut( + {}, + { + concurrencyLimit: 50, + }, + testDefaults + ); + assert.deepEqual(config.concurrencyLimit, 50); + }); + it('uses default if none other are specified', () => { + const config = sut({}, {}, testDefaults); + assert.deepEqual(config.concurrencyLimit, testDefaults.concurrencyLimit); + }); + }); +} + +describe('mergeOtlpSharedConfigurationWithDefaults', function () { + testSharedConfigBehavior(mergeOtlpSharedConfigurationWithDefaults, { + timeoutMillis: 1, + compression: 'none', + concurrencyLimit: 2, + }); +}); diff --git a/experimental/packages/otlp-exporter-base/test/common/is-export-retryable.test.ts b/experimental/packages/otlp-exporter-base/test/common/is-export-retryable.test.ts new file mode 100644 index 0000000000..458bf75a76 --- /dev/null +++ b/experimental/packages/otlp-exporter-base/test/common/is-export-retryable.test.ts @@ -0,0 +1,45 @@ +/* + * 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 { parseRetryAfterToMills } from '../../src/is-export-retryable'; + +describe('parseRetryAfterToMills', function () { + // now: 2023-01-20T00:00:00.000Z + const tests = [ + [null, undefined], + // 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/common/util.test.ts b/experimental/packages/otlp-exporter-base/test/common/util.test.ts index 4448ec06da..3bd00a2f22 100644 --- a/experimental/packages/otlp-exporter-base/test/common/util.test.ts +++ b/experimental/packages/otlp-exporter-base/test/common/util.test.ts @@ -17,131 +17,34 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; import { diag } from '@opentelemetry/api'; -import { - parseHeaders, - appendResourcePathToUrl, - appendRootPathToUrlIfNeeded, - parseRetryAfterToMills, -} from '../../src/util'; +import { validateAndNormalizeHeaders } from '../../src/util'; -describe('utils', () => { - afterEach(() => { +describe('parseHeaders', function () { + afterEach(function () { sinon.restore(); }); - - describe('parseHeaders', () => { - it('should ignore undefined headers', () => { - // Need to stub/spy on the underlying logger as the "diag" instance is global - const spyWarn = sinon.stub(diag, 'warn'); - const headers: Partial> = { - foo1: undefined, - foo2: 'bar', - foo3: 1, - }; - const result = parseHeaders(headers); - assert.deepStrictEqual(result, { - foo2: 'bar', - foo3: '1', - }); - const args = spyWarn.args[0]; - assert.strictEqual( - args[0], - 'Header "foo1" has invalid value (undefined) and will be ignored' - ); - }); - - it('should parse undefined', () => { - const result = parseHeaders(undefined); - assert.deepStrictEqual(result, {}); - }); + it('should ignore undefined headers', function () { + // Need to stub/spy on the underlying logger as the "diag" instance is global + const spyWarn = sinon.stub(diag, 'warn'); + const headers: Partial> = { + foo1: undefined, + foo2: 'bar', + foo3: 1, + }; + const result = validateAndNormalizeHeaders(headers); + assert.deepStrictEqual(result, { + foo2: 'bar', + foo3: '1', + }); + const args = spyWarn.args[0]; + assert.strictEqual( + args[0], + 'Header "foo1" has invalid value (undefined) and will be ignored' + ); }); - // only invoked with general endpoint (not signal specific endpoint) - describe('appendResourcePathToUrl - general http endpoint', () => { - it('should append resource path when missing', () => { - const url = 'http://foo.bar/'; - const resourcePath = 'v1/traces'; - - const finalUrl = appendResourcePathToUrl(url, resourcePath); - assert.strictEqual(finalUrl, url + resourcePath); - }); - it('should append root path and resource path when missing', () => { - const url = 'http://foo.bar'; - const resourcePath = 'v1/traces'; - - const finalUrl = appendResourcePathToUrl(url, resourcePath); - assert.strictEqual(finalUrl, url + '/' + resourcePath); - }); - it('should append resource path even when url already contains path ', () => { - const url = 'http://foo.bar/v1/traces'; - const resourcePath = 'v1/traces'; - - const finalUrl = appendResourcePathToUrl(url, resourcePath); - assert.strictEqual(finalUrl, url + '/' + resourcePath); - }); + it('should parse undefined', function () { + const result = validateAndNormalizeHeaders(undefined); + assert.deepStrictEqual(result, {}); }); - - // only invoked with signal specific endpoint - describe('appendRootPathToUrlIfNeeded - specific signal http endpoint', () => { - it('should append root path when missing', () => { - const url = 'http://foo.bar'; - - const finalUrl = appendRootPathToUrlIfNeeded(url); - assert.strictEqual(finalUrl, url + '/'); - }); - it('should not append root path and return same url', () => { - const url = 'http://foo.bar/'; - - const finalUrl = appendRootPathToUrlIfNeeded(url); - assert.strictEqual(finalUrl, url); - }); - it('should not append root path when url contains resource path', () => { - { - const url = 'http://foo.bar/v1/traces'; - - const finalUrl = appendRootPathToUrlIfNeeded(url); - assert.strictEqual(finalUrl, url); - } - { - const url = 'https://endpoint/something'; - - const finalUrl = appendRootPathToUrlIfNeeded(url); - assert.strictEqual(finalUrl, url); - } - }); - - it('should not change string when url is not parsable', () => { - const url = 'this is not a URL'; - - const finalUrl = appendRootPathToUrlIfNeeded(url); - assert.strictEqual(finalUrl, url); - }); - }); -}); - -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/configuration/otlp-http-env-configuration.test.ts b/experimental/packages/otlp-exporter-base/test/node/configuration/otlp-http-env-configuration.test.ts new file mode 100644 index 0000000000..5b4bc4dc47 --- /dev/null +++ b/experimental/packages/otlp-exporter-base/test/node/configuration/otlp-http-env-configuration.test.ts @@ -0,0 +1,244 @@ +/* + * 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 * as sinon from 'sinon'; + +import { diag } from '@opentelemetry/api'; + +import { getHttpConfigurationFromEnvironment } from '../../../src/configuration/otlp-http-env-configuration'; +import { testSharedConfigurationFromEnvironment } from './shared-env-configuration.test'; + +describe('getHttpConfigurationFromEnvironment', function () { + describe('headers', function () { + afterEach(function () { + delete process.env.OTEL_EXPORTER_OTLP_HEADERS; + delete process.env.OTEL_EXPORTER_OTLP_METRICS_HEADERS; + }); + + it('unset if env vars are not set', function () { + // ensure both are not set + delete process.env.OTEL_EXPORTER_OTLP_HEADERS; + delete process.env.OTEL_EXPORTER_OTLP_METRICS_HEADERS; + + const config = getHttpConfigurationFromEnvironment( + 'METRICS', + 'v1/metrics' + ); + assert.strictEqual(config.headers, undefined); + }); + + it('merges headers instead of overriding', function () { + process.env.OTEL_EXPORTER_OTLP_HEADERS = 'key1=value1,key2=value2'; + process.env.OTEL_EXPORTER_OTLP_METRICS_HEADERS = 'key1=metrics'; + + const config = getHttpConfigurationFromEnvironment( + 'METRICS', + 'v1/metrics' + ); + assert.deepEqual(config.headers, { + key1: 'metrics', + key2: 'value2', + }); + }); + + it('allows non-specific only headers', function () { + process.env.OTEL_EXPORTER_OTLP_HEADERS = 'key1=value1,key2=value2'; + + const config = getHttpConfigurationFromEnvironment( + 'METRICS', + 'v1/metrics' + ); + assert.deepEqual(config.headers, { + key1: 'value1', + key2: 'value2', + }); + }); + + it('allows specific only headers', function () { + process.env.OTEL_EXPORTER_OTLP_METRICS_HEADERS = + 'key1=value1,key2=value2'; + + const config = getHttpConfigurationFromEnvironment( + 'METRICS', + 'v1/metrics' + ); + assert.deepEqual(config.headers, { + key1: 'value1', + key2: 'value2', + }); + }); + + it('remains unset if specific headers are lists of empty strings', function () { + process.env.OTEL_EXPORTER_OTLP_METRICS_HEADERS = ' , , ,'; + + const config = getHttpConfigurationFromEnvironment( + 'METRICS', + 'v1/metrics' + ); + assert.equal(config.headers, undefined); + }); + }); + + describe('url', function () { + afterEach(function () { + delete process.env.OTEL_EXPORTER_OTLP_ENDPOINT; + delete process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT; + sinon.restore(); + }); + + it('should use url defined in env that ends with root path and append version and signal path', function () { + process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/'; + const config = getHttpConfigurationFromEnvironment( + 'METRICS', + 'v1/metrics' + ); + assert.strictEqual( + config.url, + `${process.env.OTEL_EXPORTER_OTLP_ENDPOINT}v1/metrics` + ); + }); + + it('should use url defined in env without checking if path is already present', function () { + process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/v1/metrics'; + const config = getHttpConfigurationFromEnvironment( + 'METRICS', + 'v1/metrics' + ); + assert.strictEqual( + config.url, + `${process.env.OTEL_EXPORTER_OTLP_ENDPOINT}/v1/metrics` + ); + }); + + it('should use url defined in env and append version and signal', function () { + process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar'; + const config = getHttpConfigurationFromEnvironment( + 'METRICS', + 'v1/metrics' + ); + assert.strictEqual( + config.url, + `${process.env.OTEL_EXPORTER_OTLP_ENDPOINT}/v1/metrics` + ); + }); + + it('should override global exporter url with signal url defined in env', function () { + process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://foo.bar/'; + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = 'http://foo.metrics/'; + const config = getHttpConfigurationFromEnvironment( + 'METRICS', + 'v1/metrics' + ); + assert.strictEqual( + config.url, + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT + ); + }); + + it('should add root path when signal url defined in env contains no path and no root path', function () { + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = 'http://foo.bar'; + const config = getHttpConfigurationFromEnvironment( + 'METRICS', + 'v1/metrics' + ); + assert.strictEqual( + config.url, + `${process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT}/` + ); + }); + + it('should not add root path when signal url defined in env contains root path but no path', function () { + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = 'http://foo.bar/'; + const config = getHttpConfigurationFromEnvironment( + 'METRICS', + 'v1/metrics' + ); + assert.strictEqual( + config.url, + `${process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT}` + ); + }); + + it('should not add root path when signal url defined in env contains path', function () { + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = + 'http://foo.bar/v1/metrics'; + const config = getHttpConfigurationFromEnvironment( + 'METRICS', + 'v1/metrics' + ); + assert.strictEqual( + config.url, + `${process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT}` + ); + }); + + it('should not add root path when signal url defined in env contains path and ends in /', function () { + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = + 'http://foo.bar/v1/metrics/'; + const config = getHttpConfigurationFromEnvironment( + 'METRICS', + 'v1/metrics' + ); + assert.strictEqual( + config.url, + `${process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT}` + ); + }); + + it('should warn on invalid specific url', function () { + const spyLoggerWarn = sinon.stub(diag, 'warn'); + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = 'not a url'; + const config = getHttpConfigurationFromEnvironment( + 'METRICS', + 'v1/metrics' + ); + assert.strictEqual(config.url, undefined); + sinon.assert.calledOnceWithExactly( + spyLoggerWarn, + "Configuration: Could not parse environment-provided export URL: 'not a url', falling back to undefined" + ); + }); + + it('should warn on invalid non-specific url', function () { + const spyLoggerWarn = sinon.stub(diag, 'warn'); + process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'not a url'; + const config = getHttpConfigurationFromEnvironment( + 'METRICS', + 'v1/metrics' + ); + assert.strictEqual(config.url, undefined); + sinon.assert.calledOnceWithExactly( + spyLoggerWarn, + "Configuration: Could not parse environment-provided export URL: 'not a url', falling back to undefined" + ); + }); + + it('should treat empty urls as not set', function () { + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = ''; + process.env.OTEL_EXPORTER_OTLP_ENDPOINT = ''; + const config = getHttpConfigurationFromEnvironment( + 'METRICS', + 'v1/metrics' + ); + assert.strictEqual(config.url, undefined); + }); + }); + + testSharedConfigurationFromEnvironment(signalIdentifier => + getHttpConfigurationFromEnvironment(signalIdentifier, 'v1/metrics') + ); +}); diff --git a/experimental/packages/otlp-exporter-base/test/node/configuration/shared-env-configuration.test.ts b/experimental/packages/otlp-exporter-base/test/node/configuration/shared-env-configuration.test.ts new file mode 100644 index 0000000000..51d55d4980 --- /dev/null +++ b/experimental/packages/otlp-exporter-base/test/node/configuration/shared-env-configuration.test.ts @@ -0,0 +1,208 @@ +/* + * 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 * as sinon from 'sinon'; +import { diag } from '@opentelemetry/api'; +import * as process from 'process'; +import { + getSharedConfigurationFromEnvironment, + OtlpSharedConfiguration, +} from '../../../src'; + +export function testSharedConfigurationFromEnvironment( + sut: (signalIdentifier: string) => Partial +): void { + describe('timeout', function () { + afterEach(function () { + delete process.env.OTEL_EXPORTER_OTLP_TIMEOUT; + delete process.env.OTEL_EXPORTER_OTLP_METRICS_TIMEOUT; + sinon.restore(); + }); + + it('should not define timeoutMillis if no env var is set', function () { + const config = sut('METRICS'); + assert.strictEqual(config.timeoutMillis, undefined); + }); + + it('should use specific timeout value', function () { + process.env.OTEL_EXPORTER_OTLP_METRICS_TIMEOUT = '15000'; + const config = sut('METRICS'); + assert.strictEqual(config.timeoutMillis, 15000); + }); + + it('should not define timeoutMillis when specific timeout value is negative', function () { + process.env.OTEL_EXPORTER_OTLP_METRICS_TIMEOUT = '-15000'; + const config = sut('METRICS'); + assert.strictEqual(config.timeoutMillis, undefined); + }); + + it('should not define timeoutMillis when specific and non-specific timeout values are negative', function () { + const spyLoggerWarn = sinon.stub(diag, 'warn'); + process.env.OTEL_EXPORTER_OTLP_METRICS_TIMEOUT = '-11000'; + process.env.OTEL_EXPORTER_OTLP_TIMEOUT = '-9000'; + + const config = sut('METRICS'); + + sinon.assert.calledTwice(spyLoggerWarn); + sinon.assert.calledWithExactly( + spyLoggerWarn, + 'Configuration: OTEL_EXPORTER_OTLP_METRICS_TIMEOUT is invalid, expected number greater than 0 (actual: -11000)' + ); + sinon.assert.calledWithExactly( + spyLoggerWarn, + 'Configuration: OTEL_EXPORTER_OTLP_TIMEOUT is invalid, expected number greater than 0 (actual: -9000)' + ); + + assert.strictEqual(config.timeoutMillis, undefined); + }); + + it('should not define timeoutMillis when specific and non-specific timeout values are NaN', function () { + const spyLoggerWarn = sinon.stub(diag, 'warn'); + process.env.OTEL_EXPORTER_OTLP_METRICS_TIMEOUT = 'NaN'; + process.env.OTEL_EXPORTER_OTLP_TIMEOUT = 'foo'; + + const config = sut('METRICS'); + + sinon.assert.calledTwice(spyLoggerWarn); + sinon.assert.calledWithExactly( + spyLoggerWarn, + 'Configuration: OTEL_EXPORTER_OTLP_METRICS_TIMEOUT is invalid, expected number greater than 0 (actual: NaN)' + ); + sinon.assert.calledWithExactly( + spyLoggerWarn, + 'Configuration: OTEL_EXPORTER_OTLP_TIMEOUT is invalid, expected number greater than 0 (actual: foo)' + ); + assert.strictEqual(config.timeoutMillis, undefined); + }); + it('should not define timeoutMillis when specific and non-specific timeout values are infinite', function () { + const spyLoggerWarn = sinon.stub(diag, 'warn'); + process.env.OTEL_EXPORTER_OTLP_METRICS_TIMEOUT = '-Infinitiy'; + process.env.OTEL_EXPORTER_OTLP_TIMEOUT = 'Infinity'; + + const config = sut('METRICS'); + + sinon.assert.calledTwice(spyLoggerWarn); + sinon.assert.calledWithExactly( + spyLoggerWarn, + 'Configuration: OTEL_EXPORTER_OTLP_METRICS_TIMEOUT is invalid, expected number greater than 0 (actual: -Infinitiy)' + ); + sinon.assert.calledWithExactly( + spyLoggerWarn, + 'Configuration: OTEL_EXPORTER_OTLP_TIMEOUT is invalid, expected number greater than 0 (actual: Infinity)' + ); + assert.strictEqual(config.timeoutMillis, undefined); + }); + + it('should not define timeoutMillis when specific and non-specific timeout values are empty strings', function () { + const spyLoggerWarn = sinon.stub(diag, 'warn'); + process.env.OTEL_EXPORTER_OTLP_METRICS_TIMEOUT = ''; + process.env.OTEL_EXPORTER_OTLP_TIMEOUT = ''; + + const config = sut('METRICS'); + + sinon.assert.notCalled(spyLoggerWarn); + assert.strictEqual(config.timeoutMillis, undefined); + }); + + it('should not define timeoutMillis when specific and non-specific timeout values are blank strings', function () { + const spyLoggerWarn = sinon.stub(diag, 'warn'); + process.env.OTEL_EXPORTER_OTLP_METRICS_TIMEOUT = ' '; + process.env.OTEL_EXPORTER_OTLP_TIMEOUT = ' '; + + const config = sut('METRICS'); + + sinon.assert.notCalled(spyLoggerWarn); + assert.strictEqual(config.timeoutMillis, undefined); + }); + }); + + describe('compression', function () { + afterEach(function () { + delete process.env.OTEL_EXPORTER_OTLP_COMPRESSION; + delete process.env.OTEL_EXPORTER_OTLP_METRICS_COMPRESSION; + sinon.restore(); + }); + + it('should not define compression if no env var is set', function () { + const config = sut('METRICS'); + assert.strictEqual(config.compression, undefined); + }); + + it('should use specific compression value', function () { + process.env.OTEL_EXPORTER_OTLP_METRICS_COMPRESSION = 'gzip'; + const config = sut('METRICS'); + assert.strictEqual(config.compression, 'gzip'); + }); + + it('should not define when specific compression value is invalid', function () { + const spyLoggerWarn = sinon.stub(diag, 'warn'); + process.env.OTEL_EXPORTER_OTLP_METRICS_COMPRESSION = 'bla'; + const config = sut('METRICS'); + sinon.assert.calledOnceWithExactly( + spyLoggerWarn, + "Configuration: OTEL_EXPORTER_OTLP_METRICS_COMPRESSION is invalid, expected 'none' or 'gzip' (actual: 'bla')" + ); + assert.strictEqual(config.compression, undefined); + }); + + it('should not define when non-specific compression value is invalid', function () { + const spyLoggerWarn = sinon.stub(diag, 'warn'); + process.env.OTEL_EXPORTER_OTLP_COMPRESSION = 'bla'; + const config = sut('METRICS'); + sinon.assert.calledOnceWithExactly( + spyLoggerWarn, + "Configuration: OTEL_EXPORTER_OTLP_COMPRESSION is invalid, expected 'none' or 'gzip' (actual: 'bla')" + ); + assert.strictEqual(config.compression, undefined); + }); + + it('should use signal specific over non-specific', function () { + process.env.OTEL_EXPORTER_OTLP_COMPRESSION = 'none'; + process.env.OTEL_EXPORTER_OTLP_METRICS_COMPRESSION = 'gzip'; + + const config = sut('METRICS'); + + assert.strictEqual(config.compression, 'gzip'); + }); + + it('should treat empty string values as undefined', function () { + const spyLoggerWarn = sinon.stub(diag, 'warn'); + process.env.OTEL_EXPORTER_OTLP_COMPRESSION = ''; + process.env.OTEL_EXPORTER_OTLP_METRICS_COMPRESSION = ''; + + const config = sut('METRICS'); + + sinon.assert.notCalled(spyLoggerWarn); + assert.strictEqual(config.compression, undefined); + }); + + it('should use fallback if value is blank', function () { + const spyLoggerWarn = sinon.stub(diag, 'warn'); + process.env.OTEL_EXPORTER_OTLP_COMPRESSION = 'gzip'; + process.env.OTEL_EXPORTER_OTLP_METRICS_COMPRESSION = ' '; + + const config = sut('METRICS'); + + sinon.assert.notCalled(spyLoggerWarn); + assert.strictEqual(config.compression, 'gzip'); + }); + }); +} + +describe('getSharedConfigurationFromEnvironment', function () { + testSharedConfigurationFromEnvironment(getSharedConfigurationFromEnvironment); +}); 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 19abbee952..93b70db197 100644 --- a/experimental/packages/otlp-exporter-base/test/node/util.test.ts +++ b/experimental/packages/otlp-exporter-base/test/node/util.test.ts @@ -14,24 +14,10 @@ * limitations under the License. */ -import * as assert from 'assert'; -import { configureExporterTimeout, invalidTimeout } from '../../src/util'; -import { CompressionAlgorithm } from '../../src/platform/node/types'; -import { configureCompression } from '../../src/platform/node/util'; -import { diag } from '@opentelemetry/api'; -import * as sinon from 'sinon'; - import { OTLPExporterNodeBase } from '../../src/platform/node/OTLPExporterNodeBase'; -import { OTLPExporterNodeConfigBase } from '../../src/platform/node/types'; import { ISerializer } from '@opentelemetry/otlp-transformer'; -// Barebones exporter for use by sendWithHttp -type ExporterConfig = OTLPExporterNodeConfigBase; -class Exporter extends OTLPExporterNodeBase { - getDefaultUrl(config: ExporterConfig): string { - return config.url || ''; - } -} +class Exporter extends OTLPExporterNodeBase {} const noopSerializer: ISerializer = { serializeRequest(request: object): Uint8Array | undefined { @@ -44,123 +30,7 @@ const noopSerializer: ISerializer = { describe('force flush', () => { it('forceFlush should flush spans and return', async () => { - const exporter = new Exporter({}, noopSerializer, {}); + const exporter = new Exporter({}, noopSerializer, {}, 'TEST', 'v1/test'); await exporter.forceFlush(); }); }); - -describe('configureExporterTimeout', () => { - const envSource = process.env; - it('should use timeoutMillis parameter as export timeout value', () => { - const exporterTimeout = configureExporterTimeout(9000); - assert.strictEqual(exporterTimeout, 9000); - }); - it('should use default trace export timeout env variable value when timeoutMillis parameter is undefined', () => { - const exporterTimeout = configureExporterTimeout(undefined); - assert.strictEqual(exporterTimeout, 10000); - }); - it('should use default trace export timeout env variable value when timeoutMillis parameter is negative', () => { - const exporterTimeout = configureExporterTimeout(-18000); - assert.strictEqual(exporterTimeout, 10000); - }); - it('should use trace export timeout value defined in env', () => { - envSource.OTEL_EXPORTER_OTLP_TRACES_TIMEOUT = '15000'; - const exporterTimeout = configureExporterTimeout(undefined); - assert.strictEqual(exporterTimeout, 15000); - delete envSource.OTEL_EXPORTER_OTLP_TRACES_TIMEOUT; - }); - it('should use default trace export timeout env variable value when trace export timeout value defined in env is negative', () => { - envSource.OTEL_EXPORTER_OTLP_TRACES_TIMEOUT = '-15000'; - const exporterTimeout = configureExporterTimeout(undefined); - assert.strictEqual(exporterTimeout, 10000); - delete envSource.OTEL_EXPORTER_OTLP_TRACES_TIMEOUT; - }); - it('should use default trace export timeout when timeoutMillis parameter is negative', () => { - const exporterTimeout = configureExporterTimeout(-15000); - assert.strictEqual(exporterTimeout, 10000); - }); - it('should use timeoutMillis parameter over trace export timeout value defined in env', () => { - envSource.OTEL_EXPORTER_OTLP_TRACES_TIMEOUT = '11000'; - const exporterTimeout = configureExporterTimeout(9000); - assert.strictEqual(exporterTimeout, 9000); - delete envSource.OTEL_EXPORTER_OTLP_TRACES_TIMEOUT; - }); - it('should use default value when both timeoutMillis parameter and export timeout values defined in env are negative', () => { - envSource.OTEL_EXPORTER_OTLP_TRACES_TIMEOUT = '-11000'; - envSource.OTEL_EXPORTER_OTLP_TIMEOUT = '-9000'; - const exporterTimeout = configureExporterTimeout(-5000); - assert.strictEqual(exporterTimeout, 10000); - delete envSource.OTEL_EXPORTER_OTLP_TRACES_TIMEOUT; - delete envSource.OTEL_EXPORTER_OTLP_TIMEOUT; - }); - it('should use default value export timeout value defined in env are negative', () => { - envSource.OTEL_EXPORTER_OTLP_TRACES_TIMEOUT = '-11000'; - envSource.OTEL_EXPORTER_OTLP_TIMEOUT = '-9000'; - const exporterTimeout = configureExporterTimeout(undefined); - assert.strictEqual(exporterTimeout, 10000); - delete envSource.OTEL_EXPORTER_OTLP_TRACES_TIMEOUT; - delete envSource.OTEL_EXPORTER_OTLP_TIMEOUT; - }); - it('should warn user about invalid timeout', () => { - const spyLoggerWarn = sinon.stub(diag, 'warn'); - configureExporterTimeout(-15000); - const args = spyLoggerWarn.args[0]; - assert.strictEqual(args[0], 'Timeout must be greater than 0'); - assert.strictEqual(args[1], -15000); - sinon.restore(); - }); -}); - -describe('invalidTimeout', () => { - it('should warn user about invalid timeout', () => { - const spyLoggerWarn = sinon.stub(diag, 'warn'); - invalidTimeout(-9000, 10000); - const args = spyLoggerWarn.args[0]; - assert.strictEqual(args[0], 'Timeout must be greater than 0'); - assert.strictEqual(args[1], -9000); - sinon.restore(); - }); - it('diag warn was called', () => { - const spyLoggerWarn = sinon.stub(diag, 'warn'); - invalidTimeout(-9000, 10000); - assert(spyLoggerWarn.calledOnce); - sinon.restore(); - }); - it('should return default timeout', () => { - const defaultTimeout = invalidTimeout(-9000, 10000); - assert.strictEqual(defaultTimeout, 10000); - }); -}); - -describe('configureCompression', () => { - const envSource = process.env; - it('should return none for compression', () => { - const compression = CompressionAlgorithm.NONE; - assert.strictEqual( - configureCompression(compression), - CompressionAlgorithm.NONE - ); - }); - it('should return gzip compression defined via env', () => { - envSource.OTEL_EXPORTER_OTLP_TRACES_COMPRESSION = 'gzip'; - assert.strictEqual( - configureCompression(undefined), - CompressionAlgorithm.GZIP - ); - delete envSource.OTEL_EXPORTER_OTLP_TRACES_COMPRESSION; - }); - it('should return none for compression defined via env', () => { - envSource.OTEL_EXPORTER_OTLP_TRACES_COMPRESSION = 'none'; - assert.strictEqual( - configureCompression(undefined), - CompressionAlgorithm.NONE - ); - delete envSource.OTEL_EXPORTER_OTLP_TRACES_COMPRESSION; - }); - it('should return none for compression when no compression is set', () => { - assert.strictEqual( - configureCompression(undefined), - CompressionAlgorithm.NONE - ); - }); -}); diff --git a/experimental/packages/otlp-grpc-exporter-base/src/OTLPGRPCExporterNodeBase.ts b/experimental/packages/otlp-grpc-exporter-base/src/OTLPGRPCExporterNodeBase.ts index 6ad33ec77c..e2ba61779b 100644 --- a/experimental/packages/otlp-grpc-exporter-base/src/OTLPGRPCExporterNodeBase.ts +++ b/experimental/packages/otlp-grpc-exporter-base/src/OTLPGRPCExporterNodeBase.ts @@ -15,10 +15,8 @@ */ import { diag } from '@opentelemetry/api'; -import { GRPCQueueItem, OTLPGRPCExporterConfigNode } from './types'; -import { baggageUtils, getEnv } from '@opentelemetry/core'; +import { OTLPGRPCExporterConfigNode } from './types'; import { - CompressionAlgorithm, OTLPExporterBase, OTLPExporterError, } from '@opentelemetry/otlp-exporter-base'; @@ -26,9 +24,13 @@ import { createEmptyMetadata, GrpcExporterTransport, } from './grpc-exporter-transport'; -import { configureCompression, configureCredentials } from './util'; import { ISerializer } from '@opentelemetry/otlp-transformer'; import { IExporterTransport } from '@opentelemetry/otlp-exporter-base'; +import { + getOtlpGrpcDefaultConfiguration, + mergeOtlpGrpcConfigurationWithDefaults, +} from './configuration/otlp-grpc-configuration'; +import { getOtlpGrpcConfigurationFromEnv } from './configuration/otlp-grpc-env-configuration'; /** * OTLP Exporter abstract base class @@ -37,73 +39,55 @@ export abstract class OTLPGRPCExporterNodeBase< ExportItem, ServiceResponse, > extends OTLPExporterBase { - grpcQueue: GRPCQueueItem[] = []; - compression: CompressionAlgorithm; private _transport: IExporterTransport; private _serializer: ISerializer; + private _timeoutMillis: number; constructor( config: OTLPGRPCExporterConfigNode = {}, - signalSpecificMetadata: Record, + serializer: ISerializer, grpcName: string, grpcPath: string, - serializer: ISerializer + signalIdentifier: string ) { super(config); + // keep credentials locally in case user updates the reference on the config object + const userProvidedCredentials = config.credentials; + const actualConfig = mergeOtlpGrpcConfigurationWithDefaults( + { + url: config.url, + metadata: () => { + // metadata resolution strategy is merge, so we can return empty here, and it will not override the rest of the settings. + return config.metadata ?? createEmptyMetadata(); + }, + compression: config.compression, + timeoutMillis: config.timeoutMillis, + concurrencyLimit: config.concurrencyLimit, + credentials: + userProvidedCredentials != null + ? () => userProvidedCredentials + : undefined, + }, + getOtlpGrpcConfigurationFromEnv(signalIdentifier), + getOtlpGrpcDefaultConfiguration() + ); this._serializer = serializer; + this._timeoutMillis = actualConfig.timeoutMillis; + this._concurrencyLimit = actualConfig.concurrencyLimit; if (config.headers) { diag.warn('Headers cannot be set when using grpc'); } - const nonSignalSpecificMetadata = baggageUtils.parseKeyPairsIntoRecord( - getEnv().OTEL_EXPORTER_OTLP_HEADERS - ); - const rawMetadata = Object.assign( - {}, - nonSignalSpecificMetadata, - signalSpecificMetadata - ); - - let credentialProvider = () => { - return configureCredentials(undefined, this.getUrlFromConfig(config)); - }; - - if (config.credentials != null) { - const credentials = config.credentials; - credentialProvider = () => { - return credentials; - }; - } - // Ensure we don't modify the original. - const configMetadata = config.metadata?.clone(); - const metadataProvider = () => { - const metadata = configMetadata ?? createEmptyMetadata(); - for (const [key, value] of Object.entries(rawMetadata)) { - // only override with env var data if the key has no values. - // not using Metadata.merge() as it will keep both values. - if (metadata.get(key).length < 1) { - metadata.set(key, value); - } - } - - return metadata; - }; - - this.compression = configureCompression(config.compression); this._transport = new GrpcExporterTransport({ - address: this.getDefaultUrl(config), - compression: this.compression, - credentials: credentialProvider, + address: actualConfig.url, + compression: actualConfig.compression, + credentials: actualConfig.credentials, grpcName: grpcName, grpcPath: grpcPath, - metadata: metadataProvider, + metadata: actualConfig.metadata, }); } - onInit() { - // Intentionally left empty; nothing to do. - } - override onShutdown() { this._transport.shutdown(); } @@ -126,7 +110,7 @@ export abstract class OTLPGRPCExporterNodeBase< } const promise = this._transport - .send(data, this.timeoutMillis) + .send(data, this._timeoutMillis) .then(response => { if (response.status === 'success') { onSuccess(); @@ -146,6 +130,4 @@ export abstract class OTLPGRPCExporterNodeBase< }; promise.then(popPromise, popPromise); } - - abstract getUrlFromConfig(config: OTLPGRPCExporterConfigNode): string; } diff --git a/experimental/packages/otlp-grpc-exporter-base/src/configuration/otlp-grpc-configuration.ts b/experimental/packages/otlp-grpc-exporter-base/src/configuration/otlp-grpc-configuration.ts new file mode 100644 index 0000000000..af757e3f03 --- /dev/null +++ b/experimental/packages/otlp-grpc-exporter-base/src/configuration/otlp-grpc-configuration.ts @@ -0,0 +1,141 @@ +/* + * 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 { + getSharedConfigurationDefaults, + mergeOtlpSharedConfigurationWithDefaults, + OtlpSharedConfiguration, +} from '@opentelemetry/otlp-exporter-base'; +import { + createEmptyMetadata, + createInsecureCredentials, + createSslCredentials, +} from '../grpc-exporter-transport'; +import { VERSION } from '../version'; +import { URL } from 'url'; +import { diag } from '@opentelemetry/api'; + +// NOTE: do not change this to be an actual import, doing so will break `@opentelemetry/instrumentation-grpc` +import type { ChannelCredentials, Metadata } from '@grpc/grpc-js'; + +export interface OtlpGrpcConfiguration extends OtlpSharedConfiguration { + url: string; + metadata: () => Metadata; + credentials: () => ChannelCredentials; +} + +/** + * Unresolved configuration where parts of the config may depend on other config options being resolved first. + */ +export interface UnresolvedOtlpGrpcConfiguration + extends OtlpSharedConfiguration { + url: string; + metadata: () => Metadata; + /** + * Credentials are based on the final resolved URL + */ + credentials: (url: string) => () => ChannelCredentials; +} + +export function validateAndNormalizeUrl(url: string): string { + url = url.trim(); + const hasProtocol = url.match(/^([\w]{1,8}):\/\//); + if (!hasProtocol) { + url = `https://${url}`; + } + const target = new URL(url); + if (target.protocol === 'unix:') { + return url; + } + if (target.pathname && target.pathname !== '/') { + diag.warn( + 'URL path should not be set when using grpc, the path part of the URL will be ignored.' + ); + } + if (target.protocol !== '' && !target.protocol?.match(/^(http)s?:$/)) { + diag.warn('URL protocol should be http(s)://. Using http://.'); + } + return target.host; +} + +function overrideMetadataEntriesIfNotPresent( + metadata: Metadata, + additionalMetadata: Metadata +): void { + for (const [key, value] of Object.entries(additionalMetadata.getMap())) { + // only override with env var data if the key has no values. + // not using Metadata.merge() as it will keep both values. + if (metadata.get(key).length < 1) { + metadata.set(key, value); + } + } +} + +export function mergeOtlpGrpcConfigurationWithDefaults( + userProvidedConfiguration: Partial, + fallbackConfiguration: Partial, + defaultConfiguration: UnresolvedOtlpGrpcConfiguration +): OtlpGrpcConfiguration { + const rawUrl = + userProvidedConfiguration.url ?? + fallbackConfiguration.url ?? + defaultConfiguration.url; + + return { + ...mergeOtlpSharedConfigurationWithDefaults( + userProvidedConfiguration, + fallbackConfiguration, + defaultConfiguration + ), + metadata: () => { + const metadata = defaultConfiguration.metadata(); + overrideMetadataEntriesIfNotPresent( + metadata, + // clone to ensure we don't modify what the user gave us in case they hold on to the returned reference + userProvidedConfiguration.metadata?.().clone() ?? createEmptyMetadata() + ); + overrideMetadataEntriesIfNotPresent( + metadata, + fallbackConfiguration.metadata?.() ?? createEmptyMetadata() + ); + return metadata; + }, + url: validateAndNormalizeUrl(rawUrl), + credentials: + userProvidedConfiguration.credentials ?? + fallbackConfiguration.credentials?.(rawUrl) ?? + defaultConfiguration.credentials(rawUrl), + }; +} + +export function getOtlpGrpcDefaultConfiguration(): UnresolvedOtlpGrpcConfiguration { + return { + ...getSharedConfigurationDefaults(), + metadata: () => { + const metadata = createEmptyMetadata(); + metadata.set('User-Agent', `OTel-OTLP-Exporter-JavaScript/${VERSION}`); + return metadata; + }, + url: 'http://localhost:4317', + credentials: (url: string) => { + if (url.startsWith('http://')) { + return () => createInsecureCredentials(); + } else { + return () => createSslCredentials(); + } + }, + }; +} diff --git a/experimental/packages/otlp-grpc-exporter-base/src/configuration/otlp-grpc-env-configuration.ts b/experimental/packages/otlp-grpc-exporter-base/src/configuration/otlp-grpc-env-configuration.ts new file mode 100644 index 0000000000..6ad8926604 --- /dev/null +++ b/experimental/packages/otlp-grpc-exporter-base/src/configuration/otlp-grpc-env-configuration.ts @@ -0,0 +1,259 @@ +/* + * 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 { UnresolvedOtlpGrpcConfiguration } from './otlp-grpc-configuration'; +import type { ChannelCredentials, Metadata } from '@grpc/grpc-js'; +import { baggageUtils } from '@opentelemetry/core'; +import { + createEmptyMetadata, + createInsecureCredentials, + createSslCredentials, +} from '../grpc-exporter-transport'; +import { getSharedConfigurationFromEnvironment } from '@opentelemetry/otlp-exporter-base'; +import * as fs from 'fs'; +import * as path from 'path'; +import { diag } from '@opentelemetry/api'; + +function fallbackIfNullishOrBlank( + signalSpecific?: string, + nonSignalSpecific?: string +): string | undefined { + if (signalSpecific != null && signalSpecific !== '') { + return signalSpecific; + } + + if (nonSignalSpecific != null && nonSignalSpecific !== '') { + return nonSignalSpecific; + } + + return undefined; +} + +function getMetadataFromEnv(signalIdentifier: string): Metadata | undefined { + const signalSpecificRawHeaders = + process.env[`OTEL_EXPORTER_OTLP_${signalIdentifier}_HEADERS`]?.trim(); + const nonSignalSpecificRawHeaders = + process.env['OTEL_EXPORTER_OTLP_HEADERS']?.trim(); + + const signalSpecificHeaders = baggageUtils.parseKeyPairsIntoRecord( + signalSpecificRawHeaders + ); + const nonSignalSpecificHeaders = baggageUtils.parseKeyPairsIntoRecord( + nonSignalSpecificRawHeaders + ); + + if ( + Object.keys(signalSpecificHeaders).length === 0 && + Object.keys(nonSignalSpecificHeaders).length === 0 + ) { + return undefined; + } + + const mergeHeaders = Object.assign( + {}, + nonSignalSpecificHeaders, + signalSpecificHeaders + ); + + const metadata = createEmptyMetadata(); + + // for this to work, metadata MUST be empty - otherwise `Metadata#set()` will merge items. + for (const [key, value] of Object.entries(mergeHeaders)) { + metadata.set(key, value); + } + + return metadata; +} + +function getMetadataProviderFromEnv( + signalIdentifier: string +): (() => Metadata) | undefined { + const metadata = getMetadataFromEnv(signalIdentifier); + if (metadata == null) { + return undefined; + } + + return () => metadata; +} + +function getUrlFromEnv(signalIdentifier: string) { + // This does not change the string beyond trimming on purpose. + // Normally a user would just use a host and port for gRPC, but the OTLP Exporter specification requires us to + // use the raw provided endpoint to derive credential settings. Therefore, we only normalize right when + // we merge user-provided, env-provided and defaults together, and we have determined which credentials to use. + // + // Examples: + // - example.test:4317 -> use secure credentials from environment (or provided via code) + // - http://example.test:4317 -> use insecure credentials if nothing else is provided + // - https://example.test:4317 -> use secure credentials from environment (or provided via code) + + const specificEndpoint = + process.env[`OTEL_EXPORTER_OTLP_${signalIdentifier}_ENDPOINT`]?.trim(); + const nonSpecificEndpoint = + process.env[`OTEL_EXPORTER_OTLP_ENDPOINT`]?.trim(); + + return fallbackIfNullishOrBlank(specificEndpoint, nonSpecificEndpoint); +} + +/** + * Determines whether the env var for insecure credentials is set to {@code true}. + * + * It will allow the following values as {@code true} + * - 'true' + * - 'true ' + * - ' true' + * - 'TrUE' + * - 'TRUE' + * + * It will not allow: + * - 'true false' + * - 'false true' + * - 'true!' + * - 'true,true' + * - '1' + * - ' ' + * + * @param signalIdentifier + */ +function getInsecureSettingFromEnv(signalIdentifier: string): boolean { + const signalSpecificInsecureValue = process.env[ + `OTEL_EXPORTER_OTLP_${signalIdentifier}_INSECURE` + ] + ?.toLowerCase() + .trim(); + const nonSignalSpecificInsecureValue = process.env[ + `OTEL_EXPORTER_OTLP_INSECURE` + ] + ?.toLowerCase() + .trim(); + + return ( + fallbackIfNullishOrBlank( + signalSpecificInsecureValue, + nonSignalSpecificInsecureValue + ) === 'true' + ); +} + +function readFileFromEnv( + signalSpecificEnvVar: string, + nonSignalSpecificEnvVar: string, + warningMessage: string +): Buffer | undefined { + const signalSpecificPath = process.env[signalSpecificEnvVar]?.trim(); + const nonSignalSpecificPath = process.env[nonSignalSpecificEnvVar]?.trim(); + + const filePath = fallbackIfNullishOrBlank( + signalSpecificPath, + nonSignalSpecificPath + ); + + if (filePath != null) { + try { + return fs.readFileSync(path.resolve(process.cwd(), filePath)); + } catch { + diag.warn(warningMessage); + return undefined; + } + } else { + return undefined; + } +} + +function getClientCertificateFromEnv( + signalIdentifier: string +): Buffer | undefined { + return readFileFromEnv( + `OTEL_EXPORTER_OTLP_${signalIdentifier}_CLIENT_CERTIFICATE`, + 'OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE', + 'Failed to read client certificate chain file' + ); +} + +function getClientKeyFromEnv(signalIdentifier: string): Buffer | undefined { + return readFileFromEnv( + `OTEL_EXPORTER_OTLP_${signalIdentifier}_CLIENT_KEY`, + 'OTEL_EXPORTER_OTLP_CLIENT_KEY', + 'Failed to read client certificate private key file' + ); +} + +function getRootCertificateFromEnv( + signalIdentifier: string +): Buffer | undefined { + return readFileFromEnv( + `OTEL_EXPORTER_OTLP_${signalIdentifier}_CERTIFICATE`, + 'OTEL_EXPORTER_OTLP_CERTIFICATE', + 'Failed to read root certificate file' + ); +} + +function getCredentialsFromEnvIgnoreInsecure( + signalIdentifier: string +): ChannelCredentials { + const clientKey = getClientKeyFromEnv(signalIdentifier); + const clientCertificate = getClientCertificateFromEnv(signalIdentifier); + const rootCertificate = getRootCertificateFromEnv(signalIdentifier); + + // if the chain is not intact, @grpc/grpc-js will throw. This is fine when a user provides it in code, but env var + // config is not allowed to throw, so we add this safeguard and try to make the best of it here. + const clientChainIntact = clientKey != null && clientCertificate != null; + if (rootCertificate != null && !clientChainIntact) { + diag.warn( + 'Client key and certificate must both be provided, but one was missing - attempting to create credentials from just the root certificate' + ); + return createSslCredentials(getRootCertificateFromEnv(signalIdentifier)); + } + + return createSslCredentials(rootCertificate, clientKey, clientCertificate); +} + +function getCredentialsFromEnv(signalIdentifier: string): ChannelCredentials { + if (getInsecureSettingFromEnv(signalIdentifier)) { + return createInsecureCredentials(); + } + + return getCredentialsFromEnvIgnoreInsecure(signalIdentifier); +} + +export function getOtlpGrpcConfigurationFromEnv( + signalIdentifier: string +): Partial { + return { + ...getSharedConfigurationFromEnvironment(signalIdentifier), + metadata: getMetadataProviderFromEnv(signalIdentifier), + url: getUrlFromEnv(signalIdentifier), + credentials: (finalResolvedUrl: string) => { + // Always assume insecure on http:// and secure on https://, the protocol always takes precedence over the insecure setting. + // note: the spec does not make any exception for + // - "localhost:4317". If the protocol is omitted, credentials are required unless insecure is set + // - "unix://", as it's neither http:// nor https:// and therefore credentials are required unless insecure is set + if (finalResolvedUrl.startsWith('http://')) { + return () => { + return createInsecureCredentials(); + }; + } else if (finalResolvedUrl.startsWith('https://')) { + return () => { + return getCredentialsFromEnvIgnoreInsecure(signalIdentifier); + }; + } + + // defer to env settings in this case + return () => { + return getCredentialsFromEnv(signalIdentifier); + }; + }, + }; +} diff --git a/experimental/packages/otlp-grpc-exporter-base/src/index.ts b/experimental/packages/otlp-grpc-exporter-base/src/index.ts index 566b12f42f..3a445ed91b 100644 --- a/experimental/packages/otlp-grpc-exporter-base/src/index.ts +++ b/experimental/packages/otlp-grpc-exporter-base/src/index.ts @@ -16,4 +16,3 @@ export { OTLPGRPCExporterNodeBase } from './OTLPGRPCExporterNodeBase'; export { OTLPGRPCExporterConfigNode } from './types'; -export { DEFAULT_COLLECTOR_URL, validateAndNormalizeUrl } from './util'; diff --git a/experimental/packages/otlp-grpc-exporter-base/src/types.ts b/experimental/packages/otlp-grpc-exporter-base/src/types.ts index 43caad1371..61f5613de3 100644 --- a/experimental/packages/otlp-grpc-exporter-base/src/types.ts +++ b/experimental/packages/otlp-grpc-exporter-base/src/types.ts @@ -19,19 +19,8 @@ import type { ChannelCredentials, Metadata } from '@grpc/grpc-js'; import { CompressionAlgorithm, OTLPExporterConfigBase, - OTLPExporterError, } from '@opentelemetry/otlp-exporter-base'; -/** - * Queue item to be used to save temporary spans/metrics/logs in case the GRPC service - * hasn't been fully initialized yet - */ -export interface GRPCQueueItem { - objects: ExportedItem[]; - onSuccess: () => void; - onError: (error: OTLPExporterError) => void; -} - /** * OTLP Exporter Config for Node */ diff --git a/experimental/packages/otlp-grpc-exporter-base/src/util.ts b/experimental/packages/otlp-grpc-exporter-base/src/util.ts deleted file mode 100644 index 88a7cc37ec..0000000000 --- a/experimental/packages/otlp-grpc-exporter-base/src/util.ts +++ /dev/null @@ -1,176 +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 { diag } from '@opentelemetry/api'; -import { getEnv } from '@opentelemetry/core'; -import * as path from 'path'; -import { URL } from 'url'; -import * as fs from 'fs'; -import { CompressionAlgorithm } from '@opentelemetry/otlp-exporter-base'; -import { - createInsecureCredentials, - createSslCredentials, -} from './grpc-exporter-transport'; - -// NOTE: do not change these type imports to actual imports. Doing so WILL break `@opentelemetry/instrumentation-http`, -// as they'd be imported before the http/https modules can be wrapped. -import type { ChannelCredentials } from '@grpc/grpc-js'; - -export const DEFAULT_COLLECTOR_URL = 'http://localhost:4317'; - -export function validateAndNormalizeUrl(url: string): string { - const hasProtocol = url.match(/^([\w]{1,8}):\/\//); - if (!hasProtocol) { - url = `https://${url}`; - } - const target = new URL(url); - if (target.protocol === 'unix:') { - return url; - } - if (target.pathname && target.pathname !== '/') { - diag.warn( - 'URL path should not be set when using grpc, the path part of the URL will be ignored.' - ); - } - if (target.protocol !== '' && !target.protocol?.match(/^(http)s?:$/)) { - diag.warn('URL protocol should be http(s)://. Using http://.'); - } - return target.host; -} - -export function configureCredentials( - credentials: ChannelCredentials | undefined, - endpoint: string -): ChannelCredentials { - let insecure: boolean; - - if (credentials) { - return credentials; - } else if (endpoint.startsWith('https://')) { - insecure = false; - } else if ( - endpoint.startsWith('http://') || - endpoint === DEFAULT_COLLECTOR_URL - ) { - insecure = true; - } else { - insecure = getSecurityFromEnv(); - } - - if (insecure) { - return createInsecureCredentials(); - } else { - return getCredentialsFromEnvironment(); - } -} - -function getSecurityFromEnv(): boolean { - const definedInsecure = - getEnv().OTEL_EXPORTER_OTLP_TRACES_INSECURE || - getEnv().OTEL_EXPORTER_OTLP_INSECURE; - - if (definedInsecure) { - return definedInsecure.toLowerCase() === 'true'; - } else { - return false; - } -} - -/** - * Exported for testing - */ -export function getCredentialsFromEnvironment(): ChannelCredentials { - const rootCert = retrieveRootCert(); - const privateKey = retrievePrivateKey(); - const certChain = retrieveCertChain(); - - return createSslCredentials(rootCert, privateKey, certChain); -} - -function retrieveRootCert(): Buffer | undefined { - const rootCertificate = - getEnv().OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE || - getEnv().OTEL_EXPORTER_OTLP_CERTIFICATE; - - if (rootCertificate) { - try { - return fs.readFileSync(path.resolve(process.cwd(), rootCertificate)); - } catch { - diag.warn('Failed to read root certificate file'); - return undefined; - } - } else { - return undefined; - } -} - -function retrievePrivateKey(): Buffer | undefined { - const clientKey = - getEnv().OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY || - getEnv().OTEL_EXPORTER_OTLP_CLIENT_KEY; - - if (clientKey) { - try { - return fs.readFileSync(path.resolve(process.cwd(), clientKey)); - } catch { - diag.warn('Failed to read client certificate private key file'); - return undefined; - } - } else { - return undefined; - } -} - -function retrieveCertChain(): Buffer | undefined { - const clientChain = - getEnv().OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE || - getEnv().OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE; - - if (clientChain) { - try { - return fs.readFileSync(path.resolve(process.cwd(), clientChain)); - } catch { - diag.warn('Failed to read client certificate chain file'); - return undefined; - } - } else { - return undefined; - } -} - -export function configureCompression( - compression: CompressionAlgorithm | undefined -): CompressionAlgorithm { - if (compression != null) { - return compression; - } - - const envCompression = - getEnv().OTEL_EXPORTER_OTLP_TRACES_COMPRESSION || - getEnv().OTEL_EXPORTER_OTLP_COMPRESSION || - 'none'; - - if (envCompression === 'gzip') { - return CompressionAlgorithm.GZIP; - } else if (envCompression === 'none') { - return CompressionAlgorithm.NONE; - } - - diag.warn( - 'Unknown compression "' + envCompression + '", falling back to "none"' - ); - return CompressionAlgorithm.NONE; -} diff --git a/experimental/packages/otlp-grpc-exporter-base/test/OTLPGRPCExporterNodeBase.test.ts b/experimental/packages/otlp-grpc-exporter-base/test/OTLPGRPCExporterNodeBase.test.ts index 99677dd05a..037fc53895 100644 --- a/experimental/packages/otlp-grpc-exporter-base/test/OTLPGRPCExporterNodeBase.test.ts +++ b/experimental/packages/otlp-grpc-exporter-base/test/OTLPGRPCExporterNodeBase.test.ts @@ -17,7 +17,6 @@ import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; import * as assert from 'assert'; import { OTLPGRPCExporterNodeBase } from '../src/OTLPGRPCExporterNodeBase'; -import { OTLPGRPCExporterConfigNode } from '../src/types'; import { mockedReadableSpan } from './traceHelper'; import { ExportResponse, @@ -30,15 +29,7 @@ import sinon = require('sinon'); class MockCollectorExporter extends OTLPGRPCExporterNodeBase< ReadableSpan, any -> { - getDefaultUrl(config: OTLPGRPCExporterConfigNode): string { - return ''; - } - - getUrlFromConfig(config: OTLPGRPCExporterConfigNode): string { - return ''; - } -} +> {} const successfulResponse: ExportResponseSuccess = { status: 'success', @@ -55,9 +46,6 @@ describe('OTLPGRPCExporterNodeBase', () => { shutdown: sinon.stub(), }; const mockTransport = transportStubs; - const signalSpecificMetadata: Record = { - key: 'signal-specific-metadata', - }; const serializerStubs = { serializeRequest: sinon.stub().resolves(Buffer.from([1, 2, 3])), @@ -70,10 +58,10 @@ describe('OTLPGRPCExporterNodeBase', () => { exporter = new MockCollectorExporter( { concurrencyLimit }, - signalSpecificMetadata, + serializer, 'grpcName', 'grpcPath', - serializer + 'SIGNAL' ); exporter['_transport'] = mockTransport; diff --git a/experimental/packages/otlp-grpc-exporter-base/test/configuration/otlp-grpc-configuration.test.ts b/experimental/packages/otlp-grpc-exporter-base/test/configuration/otlp-grpc-configuration.test.ts new file mode 100644 index 0000000000..097612a18d --- /dev/null +++ b/experimental/packages/otlp-grpc-exporter-base/test/configuration/otlp-grpc-configuration.test.ts @@ -0,0 +1,259 @@ +/* + * 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 { + getOtlpGrpcDefaultConfiguration, + mergeOtlpGrpcConfigurationWithDefaults, +} from '../../src/configuration/otlp-grpc-configuration'; +import { + createEmptyMetadata, + createInsecureCredentials, + createSslCredentials, +} from '../../src/grpc-exporter-transport'; +import * as fs from 'fs'; + +describe('mergeOtlpGrpcConfigurationWithDefaults', function () { + describe('metadata', function () { + it('merges metadata instead of overriding', function () { + // act + const config = mergeOtlpGrpcConfigurationWithDefaults( + { + metadata: () => { + const metadata = createEmptyMetadata(); + metadata.set('foo', 'foo-user'); + metadata.set('baz', 'baz-user'); + return metadata; + }, + }, + { + metadata: () => { + const metadata = createEmptyMetadata(); + metadata.set('foo', 'foo-fallback'); + metadata.set('bar', 'bar-fallback'); + return metadata; + }, + }, + getOtlpGrpcDefaultConfiguration() + ); + + // assert + assert.deepStrictEqual(config.metadata().getMap(), { + foo: 'foo-user', // does not use fallback if the user has set something + bar: 'bar-fallback', // uses fallback if there is no value set + baz: 'baz-user', // does not drop user-set metadata if there is no fallback for it + 'user-agent': 'OTel-OTLP-Exporter-JavaScript/0.53.0', + }); + }); + + it('does not override default (required) metadata', function () { + // act + const config = mergeOtlpGrpcConfigurationWithDefaults( + { + metadata: () => { + const metadata = createEmptyMetadata(); + metadata.set('user-agent', 'user-provided-user-agent'); + return metadata; + }, + }, + { + metadata: () => { + const metadata = createEmptyMetadata(); + metadata.set('user-agent', 'fallback-user-agent'); + return metadata; + }, + }, + getOtlpGrpcDefaultConfiguration() + ); + + assert.deepStrictEqual(config.metadata().getMap(), { + 'user-agent': 'OTel-OTLP-Exporter-JavaScript/0.53.0', + }); + }); + + it('does use default metadata if nothing is provided', function () { + // act + const config = mergeOtlpGrpcConfigurationWithDefaults( + {}, + {}, + getOtlpGrpcDefaultConfiguration() + ); + + assert.deepStrictEqual(config.metadata().getMap(), { + 'user-agent': 'OTel-OTLP-Exporter-JavaScript/0.53.0', + }); + }); + }); + + describe('url', function () { + it('uses user-provided url over fallback', function () { + // act + const config = mergeOtlpGrpcConfigurationWithDefaults( + { url: 'http://user-provided.example.test:8000' }, + { url: 'http://fallback.example.test:8001' }, + getOtlpGrpcDefaultConfiguration() + ); + + assert.deepStrictEqual(config.url, 'user-provided.example.test:8000'); + }); + + it('should trim user-provided url', function () { + // act + const config = mergeOtlpGrpcConfigurationWithDefaults( + { url: ' http://user-provided.example.test:8000 ' }, + { url: 'http://fallback.example.test:8001' }, + getOtlpGrpcDefaultConfiguration() + ); + + assert.deepStrictEqual(config.url, 'user-provided.example.test:8000'); + }); + + it('uses fallback url over default', function () { + // act + const config = mergeOtlpGrpcConfigurationWithDefaults( + {}, + { url: 'http://fallback.example.test:8001' }, + getOtlpGrpcDefaultConfiguration() + ); + + assert.deepStrictEqual(config.url, 'fallback.example.test:8001'); + }); + + it('should trim fallback url', function () { + // act + const config = mergeOtlpGrpcConfigurationWithDefaults( + {}, + { url: ' http://fallback.example.test:8001 ' }, + getOtlpGrpcDefaultConfiguration() + ); + + assert.deepStrictEqual(config.url, 'fallback.example.test:8001'); + }); + + it('should use default if nothing is provided', function () { + // act + const config = mergeOtlpGrpcConfigurationWithDefaults( + {}, + {}, + getOtlpGrpcDefaultConfiguration() + ); + + // assert + assert.deepStrictEqual(config.url, 'localhost:4317'); + }); + }); + + describe('credentials', function () { + it('uses user-provided credentials over fallback', function () { + // arrange + const userProvidedCredentials = createSslCredentials( + Buffer.from(fs.readFileSync('./test/certs/ca.crt')), + Buffer.from(fs.readFileSync('./test/certs/client.key')), + Buffer.from(fs.readFileSync('./test/certs/client.crt')) + ); + + const fallbackCredentials = createInsecureCredentials(); + + // act + const config = mergeOtlpGrpcConfigurationWithDefaults( + { + credentials: () => { + return userProvidedCredentials; + }, + }, + { + credentials: _url => { + return () => fallbackCredentials; + }, + }, + getOtlpGrpcDefaultConfiguration() + ); + + assert.deepStrictEqual(config.credentials(), userProvidedCredentials); + }); + + it('uses fallback credentials over default', function () { + // arrange + const fallbackCredentials = createSslCredentials( + Buffer.from(fs.readFileSync('./test/certs/ca.crt')), + Buffer.from(fs.readFileSync('./test/certs/client.key')), + Buffer.from(fs.readFileSync('./test/certs/client.crt')) + ); + + // act + const config = mergeOtlpGrpcConfigurationWithDefaults( + {}, + { + credentials: _url => { + return () => fallbackCredentials; + }, + }, + getOtlpGrpcDefaultConfiguration() + ); + + assert.deepStrictEqual(config.credentials(), fallbackCredentials); + }); + + it('uses default (insecure) if nothing is provided', function () { + // act + const config = mergeOtlpGrpcConfigurationWithDefaults( + {}, + {}, + getOtlpGrpcDefaultConfiguration() + ); + + // assert + assert.deepStrictEqual(config.credentials(), createInsecureCredentials()); + }); + + it('uses default (secure) if https url is user-provided', function () { + // act + const config = mergeOtlpGrpcConfigurationWithDefaults( + { url: 'https://user-provided.example.test:8000' }, + {}, + getOtlpGrpcDefaultConfiguration() + ); + + // assert + assert.deepStrictEqual(config.credentials(), createSslCredentials()); + }); + + it('uses default (insecure) if http url is user-provided', function () { + // act + const config = mergeOtlpGrpcConfigurationWithDefaults( + { url: 'http://user-provided.example.test:8000' }, + {}, + getOtlpGrpcDefaultConfiguration() + ); + + // assert + assert.deepStrictEqual(config.credentials(), createInsecureCredentials()); + }); + + it('uses default (secure) if no protocol is provided', function () { + // act + const config = mergeOtlpGrpcConfigurationWithDefaults( + { url: 'user-provided.example.test:8000' }, + {}, + getOtlpGrpcDefaultConfiguration() + ); + + // assert + assert.deepStrictEqual(config.credentials(), createSslCredentials()); + }); + }); +}); diff --git a/experimental/packages/otlp-grpc-exporter-base/test/configuration/otlp-grpc-env-configuration.test.ts b/experimental/packages/otlp-grpc-exporter-base/test/configuration/otlp-grpc-env-configuration.test.ts new file mode 100644 index 0000000000..9c410a5fed --- /dev/null +++ b/experimental/packages/otlp-grpc-exporter-base/test/configuration/otlp-grpc-env-configuration.test.ts @@ -0,0 +1,488 @@ +/* + * 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 * as fs from 'fs'; +import * as sinon from 'sinon'; + +import { getOtlpGrpcConfigurationFromEnv } from '../../src/configuration/otlp-grpc-env-configuration'; +import { + createInsecureCredentials, + createSslCredentials, +} from '../../src/grpc-exporter-transport'; +import { diag } from '@opentelemetry/api'; + +describe('getOtlpGrpcConfigurationFromEnvironment', function () { + describe('metadata', function () { + afterEach(function () { + delete process.env.OTEL_EXPORTER_OTLP_HEADERS; + delete process.env.OTEL_EXPORTER_OTLP_METRICS_HEADERS; + }); + + it('remains unset if env vars are not set', function () { + // ensure both are not set + delete process.env.OTEL_EXPORTER_OTLP_HEADERS; + delete process.env.OTEL_EXPORTER_OTLP_METRICS_HEADERS; + + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.strictEqual(config.metadata, undefined); + }); + + it('remains unset if env vars are set to empty string', function () { + process.env.OTEL_EXPORTER_OTLP_HEADERS = ''; + process.env.OTEL_EXPORTER_OTLP_METRICS_HEADERS = ''; + + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.strictEqual(config.metadata, undefined); + }); + + it('remains unset if non-specific env var is set to empty string, specific is undefined', function () { + process.env.OTEL_EXPORTER_OTLP_HEADERS = ''; + + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.strictEqual(config.metadata, undefined); + }); + + it('remains unset if non-specific env var is a list of empty strings, specific is undefined', function () { + process.env.OTEL_EXPORTER_OTLP_HEADERS = ', , , '; + + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.strictEqual(config.metadata, undefined); + }); + + it('remains unset if specific env var is set to empty string, non-specific is undefined', function () { + process.env.OTEL_EXPORTER_OTLP_METRICS_HEADERS = ''; + + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.strictEqual(config.metadata, undefined); + }); + + it('remains unset if specific env var is a list of empty strings, non-specific is undefined', function () { + process.env.OTEL_EXPORTER_OTLP_METRICS_HEADERS = ', , , '; + + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.strictEqual(config.metadata, undefined); + }); + + it('merges metadata instead of overriding', function () { + process.env.OTEL_EXPORTER_OTLP_HEADERS = + 'foo=foo-non-specific,bar=bar-non-specific'; + process.env.OTEL_EXPORTER_OTLP_METRICS_HEADERS = + 'foo=foo-specific,baz=baz-specific'; + + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.deepEqual(config.metadata?.().getMap(), { + foo: 'foo-specific', // does not use specific if the user has set something + bar: 'bar-non-specific', // uses non-specific if there is nothing specific set + baz: 'baz-specific', // does not drop user-set metadata if there is no non-specific key for it + }); + }); + + it('allows non-specific only metadata', function () { + process.env.OTEL_EXPORTER_OTLP_HEADERS = + 'foo=foo-non-specific,bar=bar-non-specific'; + + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.deepEqual(config.metadata?.().getMap(), { + foo: 'foo-non-specific', + bar: 'bar-non-specific', + }); + }); + + it('allows specific only metadata', function () { + process.env.OTEL_EXPORTER_OTLP_METRICS_HEADERS = + 'foo=foo-specific,baz=baz-specific'; + + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.deepEqual(config.metadata?.().getMap(), { + foo: 'foo-specific', + baz: 'baz-specific', + }); + }); + }); + + describe('url', function () { + afterEach(function () { + delete process.env.OTEL_EXPORTER_OTLP_ENDPOINT; + delete process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT; + sinon.restore(); + }); + + it('should override non-signal specific exporter url with specific url', function () { + process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://example.test/'; + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = + 'http://metrics.example.test/'; + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.strictEqual( + config.url, + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT + ); + }); + + it('should use non-specific url defined in env', function () { + process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://example.test/'; + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.strictEqual(config.url, process.env.OTEL_EXPORTER_OTLP_ENDPOINT); + }); + + it('should use specific url defined in env', function () { + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = 'http://example.test/'; + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.strictEqual( + config.url, + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT + ); + }); + + it('should keep non-specific url as-is when no protocol is given', function () { + process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'example.test'; + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.strictEqual(config.url, process.env.OTEL_EXPORTER_OTLP_ENDPOINT); + }); + + it('should keep specific url as-is when no protocol is given', function () { + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = 'example.test'; + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.strictEqual( + config.url, + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT + ); + }); + + it('should not drop any protocol that is unknown with non-specific url', function () { + process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'foo://example.test'; + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.strictEqual(config.url, process.env.OTEL_EXPORTER_OTLP_ENDPOINT); + }); + + it('should not drop any protocol that is unknown with specific url', function () { + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = 'foo://example.test'; + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.strictEqual( + config.url, + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT + ); + }); + + it('should keep https protocol with non-specific url', function () { + process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'https://example.test'; + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.strictEqual(config.url, process.env.OTEL_EXPORTER_OTLP_ENDPOINT); + }); + + it('should keep https protocol with specific url', function () { + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = 'https://example.test'; + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.strictEqual( + config.url, + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT + ); + }); + + it('should trim non-specific url', function () { + process.env.OTEL_EXPORTER_OTLP_ENDPOINT = ' http://example.test:4317 '; + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.strictEqual(config.url, 'http://example.test:4317'); + }); + + it('should trim specific url', function () { + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = + ' http://example.test:4317 '; + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.strictEqual(config.url, 'http://example.test:4317'); + }); + + it('should treat empty non-specific url as undefined', function () { + process.env.OTEL_EXPORTER_OTLP_ENDPOINT = ''; + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.strictEqual(config.url, undefined); + }); + + it('should treat empty specific url as undefined', function () { + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = ''; + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.strictEqual(config.url, undefined); + }); + + it('should treat space-only non-specific url as undefined', function () { + process.env.OTEL_EXPORTER_OTLP_ENDPOINT = ' '; + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.strictEqual(config.url, undefined); + }); + + it('should treat space-only specific url as undefined', function () { + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = ' '; + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + assert.strictEqual(config.url, undefined); + }); + }); + + describe('credentials', function () { + afterEach(function () { + delete process.env.OTEL_EXPORTER_OTLP_ENDPOINT; + delete process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT; + delete process.env.OTEL_EXPORTER_OTLP_INSECURE; + delete process.env.OTEL_EXPORTER_OTLP_METRICS_INSECURE; + delete process.env.OTEL_EXPORTER_OTLP_INSECURE; + delete process.env.OTEL_EXPORTER_OTLP_METRICS_INSECURE; + delete process.env.OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE; + delete process.env.OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE; + delete process.env.OTEL_EXPORTER_OTLP_CLIENT_KEY; + delete process.env.OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY; + delete process.env.OTEL_EXPORTER_OTLP_CERTIFICATE; + delete process.env.OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE; + sinon.restore(); + }); + + it('should select insecure credentials on http protocol', function () { + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + const credentials = config.credentials?.('http://example.test:4317')(); + + assert.deepStrictEqual(credentials, createInsecureCredentials()); + }); + + it('should select secure credentials on https protocol', function () { + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + const credentials = config.credentials?.('https://example.test:4317')(); + + assert.deepStrictEqual(credentials, createSslCredentials()); + }); + + it('should select secure credentials on https protocol even when insecure env var is set to true', function () { + // From the spec: + // "Insecure: This option only applies to OTLP/gRPC when an endpoint is provided without the http or https scheme" + + // arrange + const expectedCredentials = createSslCredentials( + Buffer.from(fs.readFileSync('./test/certs/ca.crt')), + Buffer.from(fs.readFileSync('./test/certs/client.key')), + Buffer.from(fs.readFileSync('./test/certs/client.crt')) + ); + process.env.OTEL_EXPORTER_OTLP_INSECURE = 'true'; + process.env.OTEL_EXPORTER_OTLP_METRICS_INSECURE = 'true'; + process.env.OTEL_EXPORTER_OTLP_CERTIFICATE = './test/certs/ca.crt'; + process.env.OTEL_EXPORTER_OTLP_CLIENT_KEY = './test/certs/client.key'; + process.env.OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE = + './test/certs/client.crt'; + + // act + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + const credentials = config.credentials?.('https://example.test:4317')(); + + // assert + assert.deepStrictEqual(credentials, expectedCredentials); + }); + + it('should select insecure credentials on http protocol even when insecure env var is set to false', function () { + // From the spec: + // "Insecure: This option only applies to OTLP/gRPC when an endpoint is provided without the http or https scheme" + + process.env.OTEL_EXPORTER_OTLP_INSECURE = 'false'; + process.env.OTEL_EXPORTER_OTLP_METRICS_INSECURE = 'false'; + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + const credentials = config.credentials?.('http://example.test:4317')(); + + assert.deepStrictEqual(credentials, createInsecureCredentials()); + }); + + it('should select insecure credentials on http protocol even when insecure env var is set to true', function () { + // From the spec: + // "Insecure: This option only applies to OTLP/gRPC when an endpoint is provided without the http or https scheme" + + process.env.OTEL_EXPORTER_OTLP_INSECURE = 'true'; + process.env.OTEL_EXPORTER_OTLP_METRICS_INSECURE = 'true'; + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + const credentials = config.credentials?.('example.test:4317')(); + + assert.deepStrictEqual(credentials, createInsecureCredentials()); + }); + + it('should select insecure credentials on no protocol when insecure env var is set to true', function () { + // From the spec: + // "Insecure: This option only applies to OTLP/gRPC when an endpoint is provided without the http or https scheme" + + process.env.OTEL_EXPORTER_OTLP_INSECURE = 'true'; + process.env.OTEL_EXPORTER_OTLP_METRICS_INSECURE = 'true'; + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + const credentials = config.credentials?.('example.test:4317')(); + + assert.deepStrictEqual(credentials, createInsecureCredentials()); + }); + + it('should select non-specific secure credentials on no protocol when insecure env var is set to false', function () { + // From the spec: + // "Insecure: This option only applies to OTLP/gRPC when an endpoint is provided without the http or https scheme" + + // arrange + const expectedCredentials = createSslCredentials( + Buffer.from(fs.readFileSync('./test/certs/ca.crt')), + Buffer.from(fs.readFileSync('./test/certs/client.key')), + Buffer.from(fs.readFileSync('./test/certs/client.crt')) + ); + process.env.OTEL_EXPORTER_OTLP_INSECURE = 'false'; + process.env.OTEL_EXPORTER_OTLP_METRICS_INSECURE = 'false'; + process.env.OTEL_EXPORTER_OTLP_CERTIFICATE = './test/certs/ca.crt'; + process.env.OTEL_EXPORTER_OTLP_CLIENT_KEY = './test/certs/client.key'; + process.env.OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE = + './test/certs/client.crt'; + + // act + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + const credentials = config.credentials?.('example.test:4317')(); + + // assert + assert.deepStrictEqual(credentials, expectedCredentials); + }); + + it('should select specific secure credentials on no protocol when insecure env var is set to false', function () { + // From the spec: + // "Insecure: This option only applies to OTLP/gRPC when an endpoint is provided without the http or https scheme" + + // arrange + const expectedCredentials = createSslCredentials( + Buffer.from(fs.readFileSync('./test/certs/ca.crt')), + Buffer.from(fs.readFileSync('./test/certs/client.key')), + Buffer.from(fs.readFileSync('./test/certs/client.crt')) + ); + process.env.OTEL_EXPORTER_OTLP_METRICS_INSECURE = 'false'; + process.env.OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE = + './test/certs/ca.crt'; + process.env.OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY = + './test/certs/client.key'; + process.env.OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE = + './test/certs/client.crt'; + + // act + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + const credentials = config.credentials?.('example.test:4317')(); + + // assert + assert.deepStrictEqual(credentials, expectedCredentials); + }); + + it('should warn when client key is missing but client cert is there', function () { + // arrange + const spyLoggerWarn = sinon.stub(diag, 'warn'); + const expectedCredentials = createSslCredentials( + Buffer.from(fs.readFileSync('./test/certs/ca.crt')), + undefined, + undefined + ); + + process.env.OTEL_EXPORTER_OTLP_CERTIFICATE = './test/certs/ca.crt'; + // OTEL_EXPORTER_OTLP_CLIENT_KEY not set + process.env.OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE = + './test/certs/client.crt'; + + // act + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + const credentials = config.credentials?.('example.test:4317')(); + + // assert + sinon.assert.calledOnceWithExactly( + spyLoggerWarn, + 'Client key and certificate must both be provided, but one was missing - attempting to create credentials from just the root certificate' + ); + assert.deepStrictEqual(credentials, expectedCredentials); + }); + + it('should warn when client cert is missing but client key is there', function () { + // arrange + const spyLoggerWarn = sinon.stub(diag, 'warn'); + const expectedCredentials = createSslCredentials( + Buffer.from(fs.readFileSync('./test/certs/ca.crt')), + undefined, + undefined + ); + + process.env.OTEL_EXPORTER_OTLP_CERTIFICATE = './test/certs/ca.crt'; + process.env.OTEL_EXPORTER_OTLP_CLIENT_KEY = './test/certs/client.key'; + // OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE not set + + // act + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + const credentials = config.credentials?.('example.test:4317')(); + + // assert + sinon.assert.calledOnceWithExactly( + spyLoggerWarn, + 'Client key and certificate must both be provided, but one was missing - attempting to create credentials from just the root certificate' + ); + assert.deepStrictEqual(credentials, expectedCredentials); + }); + + it('should not warn when root certificate is missing', function () { + // arrange + const spyLoggerWarn = sinon.stub(diag, 'warn'); + const expectedCredentials = createSslCredentials( + undefined, + Buffer.from(fs.readFileSync('./test/certs/client.key')), + Buffer.from(fs.readFileSync('./test/certs/client.crt')) + ); + + // OTEL_EXPORTER_OTLP_CERTIFICATE not set + process.env.OTEL_EXPORTER_OTLP_CLIENT_KEY = './test/certs/client.key'; + process.env.OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE = + './test/certs/client.crt'; + + // act + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + const credentials = config.credentials?.('example.test:4317')(); + + // assert + sinon.assert.notCalled(spyLoggerWarn); + assert.deepStrictEqual(credentials, expectedCredentials); + }); + + it('should warn when files are not accessible', function () { + // arrange + const spyLoggerWarn = sinon.stub(diag, 'warn'); + const expectedCredentials = createSslCredentials(); + + // OTEL_EXPORTER_OTLP_CERTIFICATE not set + process.env.OTEL_EXPORTER_OTLP_CLIENT_KEY = './test/certs/client.key'; + process.env.OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE = + './test/certs/client.crt'; + + process.env.OTEL_EXPORTER_OTLP_CERTIFICATE = + './test/certs/non-existent-ca.crt'; + process.env.OTEL_EXPORTER_OTLP_CLIENT_KEY = + './test/certs/non-existent-client.key'; + process.env.OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE = + './test/certs/non-existent-client.crt'; + + // act + const config = getOtlpGrpcConfigurationFromEnv('METRICS'); + const credentials = config.credentials?.('example.test:4317')(); + + // assert + sinon.assert.callCount(spyLoggerWarn, 3); + sinon.assert.calledWithExactly( + spyLoggerWarn, + 'Failed to read root certificate file' + ); + sinon.assert.calledWithExactly( + spyLoggerWarn, + 'Failed to read client certificate private key file' + ); + sinon.assert.calledWithExactly( + spyLoggerWarn, + 'Failed to read client certificate chain file' + ); + assert.deepStrictEqual(credentials, expectedCredentials); + }); + }); +}); diff --git a/experimental/packages/otlp-grpc-exporter-base/test/otlp-grpc-configuration.test.ts b/experimental/packages/otlp-grpc-exporter-base/test/otlp-grpc-configuration.test.ts new file mode 100644 index 0000000000..85652af5f8 --- /dev/null +++ b/experimental/packages/otlp-grpc-exporter-base/test/otlp-grpc-configuration.test.ts @@ -0,0 +1,83 @@ +/* + * 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 sinon from 'sinon'; +import * as assert from 'assert'; +import { validateAndNormalizeUrl } from '../src/configuration/otlp-grpc-configuration'; + +describe('validateAndNormalizeUrl()', function () { + const tests = [ + { + name: 'bare hostname should return same value', + input: 'api.datacat.io', + expected: 'api.datacat.io', + }, + { + name: 'host:port should return same value', + input: 'api.datacat.io:1234', + expected: 'api.datacat.io:1234', + }, + { + name: 'https://host:port should trim off protocol', + input: 'https://api.datacat.io:1234', + expected: 'api.datacat.io:1234', + }, + { + name: 'should accept unix socket', + input: 'unix:///tmp/grpc.sock', + expected: 'unix:///tmp/grpc.sock', + }, + { + name: 'bad protocol should warn but return host:port', + input: 'badproto://api.datacat.io:1234', + expected: 'api.datacat.io:1234', + warn: 'URL protocol should be http(s)://. Using http://.', + }, + { + name: 'path on end of url should warn but return host:port', + input: 'http://api.datacat.io:1234/a/b/c', + expected: 'api.datacat.io:1234', + warn: 'URL path should not be set when using grpc, the path part of the URL will be ignored.', + }, + { + name: ':// in path should not be used for protocol even if protocol not specified', + input: 'api.datacat.io/a/b://c', + expected: 'api.datacat.io', + warn: 'URL path should not be set when using grpc, the path part of the URL will be ignored.', + }, + { + name: ':// in path is valid when a protocol is specified', + input: 'http://api.datacat.io/a/b://c', + expected: 'api.datacat.io', + warn: 'URL path should not be set when using grpc, the path part of the URL will be ignored.', + }, + ]; + tests.forEach(test => { + it(test.name, function () { + const diagWarn = sinon.stub(diag, 'warn'); + try { + assert.strictEqual(validateAndNormalizeUrl(test.input), test.expected); + if (test.warn) { + sinon.assert.calledWith(diagWarn, test.warn); + } else { + sinon.assert.notCalled(diagWarn); + } + } finally { + diagWarn.restore(); + } + }); + }); +}); diff --git a/experimental/packages/otlp-grpc-exporter-base/test/util.test.ts b/experimental/packages/otlp-grpc-exporter-base/test/util.test.ts deleted file mode 100644 index 1da067b559..0000000000 --- a/experimental/packages/otlp-grpc-exporter-base/test/util.test.ts +++ /dev/null @@ -1,220 +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 * as sinon from 'sinon'; -import * as assert from 'assert'; - -import { diag } from '@opentelemetry/api'; -import * as grpc from '@grpc/grpc-js'; -import { - validateAndNormalizeUrl, - configureCompression, - configureCredentials, - getCredentialsFromEnvironment, - DEFAULT_COLLECTOR_URL, -} from '../src/util'; -import { CompressionAlgorithm } from '@opentelemetry/otlp-exporter-base'; - -// Tests added to detect breakage released in #2130 -describe('validateAndNormalizeUrl()', () => { - const tests = [ - { - name: 'bare hostname should return same value', - input: 'api.datacat.io', - expected: 'api.datacat.io', - }, - { - name: 'host:port should return same value', - input: 'api.datacat.io:1234', - expected: 'api.datacat.io:1234', - }, - { - name: 'https://host:port should trim off protocol', - input: 'https://api.datacat.io:1234', - expected: 'api.datacat.io:1234', - }, - { - name: 'should accept unix socket', - input: 'unix:///tmp/grpc.sock', - expected: 'unix:///tmp/grpc.sock', - }, - { - name: 'bad protocol should warn but return host:port', - input: 'badproto://api.datacat.io:1234', - expected: 'api.datacat.io:1234', - warn: 'URL protocol should be http(s)://. Using http://.', - }, - { - name: 'path on end of url should warn but return host:port', - input: 'http://api.datacat.io:1234/a/b/c', - expected: 'api.datacat.io:1234', - warn: 'URL path should not be set when using grpc, the path part of the URL will be ignored.', - }, - { - name: ':// in path should not be used for protocol even if protocol not specified', - input: 'api.datacat.io/a/b://c', - expected: 'api.datacat.io', - warn: 'URL path should not be set when using grpc, the path part of the URL will be ignored.', - }, - { - name: ':// in path is valid when a protocol is specified', - input: 'http://api.datacat.io/a/b://c', - expected: 'api.datacat.io', - warn: 'URL path should not be set when using grpc, the path part of the URL will be ignored.', - }, - ]; - tests.forEach(test => { - it(test.name, () => { - const diagWarn = sinon.stub(diag, 'warn'); - try { - assert.strictEqual(validateAndNormalizeUrl(test.input), test.expected); - if (test.warn) { - sinon.assert.calledWith(diagWarn, test.warn); - } else { - sinon.assert.notCalled(diagWarn); - } - } finally { - diagWarn.restore(); - } - }); - }); -}); - -describe('utils - configureCredentials', () => { - const envSource = process.env; - it('should return insecure channel when using all defaults', () => { - const credentials = configureCredentials(undefined, DEFAULT_COLLECTOR_URL); - assert.ok(credentials._isSecure() === false); - }); - it('should return user defined channel credentials', () => { - const userDefinedCredentials = grpc.credentials.createSsl(); - const credentials = configureCredentials( - userDefinedCredentials, - 'http://foo.bar' - ); - - assert.ok(userDefinedCredentials === credentials); - assert.ok(credentials._isSecure() === true); - }); - it('should return secure channel when endpoint contains https scheme - no matter insecure env settings,', () => { - envSource.OTEL_EXPORTER_OTLP_TRACES_INSECURE = 'true'; - const credentials = configureCredentials(undefined, 'https://foo.bar'); - assert.ok(credentials._isSecure() === true); - delete envSource.OTEL_EXPORTER_OTLP_TRACES_INSECURE; - }); - - it('should return insecure channel when endpoint contains http scheme and no insecure env settings', () => { - const credentials = configureCredentials(undefined, 'http://foo.bar'); - assert.ok(credentials._isSecure() === false); - }); - it('should return secure channel when endpoint does not contain scheme and no insecure env settings', () => { - const credentials = configureCredentials(undefined, 'foo.bar'); - assert.ok(credentials._isSecure() === true); - }); - it('should return insecure channel when endpoint contains http scheme and insecure env set to false', () => { - envSource.OTEL_EXPORTER_OTLP_TRACES_INSECURE = 'false'; - const credentials = configureCredentials(undefined, 'http://foo.bar'); - assert.ok(credentials._isSecure() === false); - delete envSource.OTEL_EXPORTER_OTLP_TRACES_INSECURE; - }); - it('should return insecure channel when endpoint contains http scheme and insecure env set to true', () => { - envSource.OTEL_EXPORTER_OTLP_INSECURE = 'true'; - const credentials = configureCredentials(undefined, 'http://localhost'); - assert.ok(credentials._isSecure() === false); - delete envSource.OTEL_EXPORTER_OTLP_INSECURE; - }); - it('should return secure channel when endpoint does not contain scheme and insecure env set to false', () => { - envSource.OTEL_EXPORTER_OTLP_TRACES_INSECURE = 'false'; - const credentials = configureCredentials(undefined, 'foo.bar'); - assert.ok(credentials._isSecure() === true); - delete envSource.OTEL_EXPORTER_OTLP_TRACES_INSECURE; - }); - it('should return insecure channel when endpoint does not contain scheme and insecure env set to true', () => { - envSource.OTEL_EXPORTER_OTLP_INSECURE = 'true'; - const credentials = configureCredentials(undefined, 'foo.bar'); - assert.ok(credentials._isSecure() === false); - delete envSource.OTEL_EXPORTER_OTLP_INSECURE; - }); -}); - -describe('useSecureConnection', () => { - const envSource = process.env; - it('should return secure connection using all credentials', () => { - envSource.OTEL_EXPORTER_OTLP_CERTIFICATE = './test/certs/ca.crt'; - envSource.OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY = './test/certs/client.key'; - envSource.OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE = - './test/certs/client.crt'; - - const credentials = getCredentialsFromEnvironment(); - assert.ok(credentials._isSecure() === true); - - delete envSource.OTEL_EXPORTER_OTLP_CERTIFICATE; - delete envSource.OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY; - delete envSource.OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE; - }); - it('should return secure connection using only root certificate', () => { - envSource.OTEL_EXPORTER_OTLP_CERTIFICATE = './test/certs/ca.crt'; - const credentials = getCredentialsFromEnvironment(); - assert.ok(credentials._isSecure() === true); - delete envSource.OTEL_EXPORTER_OTLP_CERTIFICATE; - }); - it('should warn user when file cannot be read and use default root certificate', () => { - envSource.OTEL_EXPORTER_OTLP_CERTIFICATE = './wrongpath/test/certs/ca.crt'; - const diagWarn = sinon.stub(diag, 'warn'); - const credentials = getCredentialsFromEnvironment(); - const args = diagWarn.args[0]; - - assert.strictEqual(args[0], 'Failed to read root certificate file'); - sinon.assert.calledOnce(diagWarn); - assert.ok(credentials._isSecure() === true); - - delete envSource.OTEL_EXPORTER_OTLP_CERTIFICATE; - diagWarn.restore(); - }); -}); - -describe('configureCompression', () => { - const envSource = process.env; - it('should return none for compression', () => { - const compression = CompressionAlgorithm.NONE; - assert.strictEqual( - configureCompression(compression), - CompressionAlgorithm.NONE - ); - }); - it('should return gzip compression defined via env', () => { - envSource.OTEL_EXPORTER_OTLP_TRACES_COMPRESSION = 'gzip'; - assert.strictEqual( - configureCompression(undefined), - CompressionAlgorithm.GZIP - ); - delete envSource.OTEL_EXPORTER_OTLP_TRACES_COMPRESSION; - }); - it('should return none for compression defined via env', () => { - envSource.OTEL_EXPORTER_OTLP_TRACES_COMPRESSION = 'none'; - assert.strictEqual( - configureCompression(undefined), - CompressionAlgorithm.NONE - ); - delete envSource.OTEL_EXPORTER_OTLP_TRACES_COMPRESSION; - }); - it('should return none for compression when no compression is set', () => { - assert.strictEqual( - configureCompression(undefined), - CompressionAlgorithm.NONE - ); - }); -}); From e0e2b4ad815ef31b249ab4b19c78c268d3facf9e Mon Sep 17 00:00:00 2001 From: Richard Chukwu <79311274+RichardChukwu@users.noreply.github.com> Date: Thu, 3 Oct 2024 15:53:13 +0100 Subject: [PATCH 16/20] fix(sdk-node): use warn instead of error on unknown OTEL_NODE_RESOURCE_DETECTORS values (#5034) Co-authored-by: Marc Pichler --- experimental/CHANGELOG.md | 1 + experimental/packages/opentelemetry-sdk-node/src/utils.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 04ea9a97b3..49eb60e237 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -46,6 +46,7 @@ All notable changes to experimental packages in this project will be documented * `appendResourcePathToUrlIfNeeded` * `configureExporterTimeout` * `invalidTimeout` +* fix(sdk-node): use warn instead of error on unknown OTEL_NODE_RESOURCE_DETECTORS values [#5034](https://github.com/open-telemetry/opentelemetry-js/pull/5034) ### :books: (Refine Doc) diff --git a/experimental/packages/opentelemetry-sdk-node/src/utils.ts b/experimental/packages/opentelemetry-sdk-node/src/utils.ts index bae8a028e6..293a3e3485 100644 --- a/experimental/packages/opentelemetry-sdk-node/src/utils.ts +++ b/experimental/packages/opentelemetry-sdk-node/src/utils.ts @@ -54,7 +54,7 @@ export function getResourceDetectorsFromEnv(): Array { return resourceDetectorsFromEnv.flatMap(detector => { const resourceDetector = resourceDetectors.get(detector); if (!resourceDetector) { - diag.error( + diag.warn( `Invalid resource detector "${detector}" specified in the environment variable OTEL_NODE_RESOURCE_DETECTORS` ); } From 7baa493f50fd0a967d169ddfa2b38137b8a3b330 Mon Sep 17 00:00:00 2001 From: David Luna Date: Fri, 4 Oct 2024 10:00:03 +0200 Subject: [PATCH 17/20] test(instr-http): remove usages of `new Span` (#5035) --- CHANGELOG.md | 1 + .../test/functionals/utils.test.ts | 120 +++++++++--------- 2 files changed, 64 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dc782cfe6..1e42a90f7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ For semantic convention package changes, see the [semconv CHANGELOG](packages/se [#4992](https://github.com/open-telemetry/opentelemetry-js/pull/4992) * refactor(sdk-metrics): replace `MetricsAttributes` with `Attributes` [#5021](https://github.com/open-telemetry/opentelemetry-js/pull/5021) @david-luna * refactor(instrumentation-http): replace `SpanAttributes` and `MetricsAttributes` with `Attributes` [#5023](https://github.com/open-telemetry/opentelemetry-js/pull/5023) @david-luna +* test(instrumentation-http): remove usages of `new Span` in tests [#5035](https://github.com/open-telemetry/opentelemetry-js/pull/5035) @david-luna ## 1.26.0 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 c731a296d9..c091529c9f 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts @@ -16,12 +16,10 @@ import { Attributes, SpanStatusCode, - ROOT_CONTEXT, SpanKind, - TraceFlags, context, + Span, } from '@opentelemetry/api'; -import { BasicTracerProvider, Span } from '@opentelemetry/sdk-trace-base'; import { SEMATTRS_HTTP_REQUEST_CONTENT_LENGTH, SEMATTRS_HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, @@ -257,26 +255,28 @@ describe('Utility', () => { describe('setSpanWithError()', () => { it('should have error attributes', () => { const errorMessage = 'test error'; - const span = new Span( - new BasicTracerProvider().getTracer('default'), - ROOT_CONTEXT, - 'test', - { spanId: '', traceId: '', traceFlags: TraceFlags.SAMPLED }, - SpanKind.INTERNAL - ); - utils.setSpanWithError( - span, - new Error(errorMessage), - SemconvStability.OLD - ); - const attributes = span.attributes; - assert.strictEqual( - attributes[AttributeNames.HTTP_ERROR_MESSAGE], - errorMessage - ); - assert.strictEqual(span.events.length, 1); - assert.strictEqual(span.events[0].name, 'exception'); - assert.ok(attributes[AttributeNames.HTTP_ERROR_NAME]); + const error = new Error(errorMessage); + const span = { + setAttribute: () => undefined, + setStatus: () => undefined, + recordException: () => undefined, + } as unknown as Span; + const mock = sinon.mock(span); + + mock + .expects('setAttribute') + .calledWithExactly(AttributeNames.HTTP_ERROR_NAME, 'error'); + mock + .expects('setAttribute') + .calledWithExactly(AttributeNames.HTTP_ERROR_MESSAGE, errorMessage); + mock.expects('setStatus').calledWithExactly({ + code: SpanStatusCode.ERROR, + message: errorMessage, + }); + mock.expects('recordException').calledWithExactly(error); + + utils.setSpanWithError(span, error, SemconvStability.OLD); + mock.verify(); }); }); @@ -537,40 +537,51 @@ describe('Utility', () => { describe('headers to span attributes capture', () => { let span: Span; + let mock: sinon.SinonMock; beforeEach(() => { - span = new Span( - new BasicTracerProvider().getTracer('default'), - ROOT_CONTEXT, - 'test', - { spanId: '', traceId: '', traceFlags: TraceFlags.SAMPLED }, - SpanKind.INTERNAL - ); + span = { + setAttribute: () => undefined, + } as unknown as Span; + mock = sinon.mock(span); }); it('should set attributes for request and response keys', () => { + mock + .expects('setAttribute') + .calledWithExactly('http.request.header.origin', ['localhost']); + mock + .expects('setAttribute') + .calledWithExactly('http.response.header.cookie', ['token=123']); + utils.headerCapture('request', ['Origin'])(span, () => 'localhost'); utils.headerCapture('response', ['Cookie'])(span, () => 'token=123'); - assert.deepStrictEqual(span.attributes['http.request.header.origin'], [ - 'localhost', - ]); - assert.deepStrictEqual(span.attributes['http.response.header.cookie'], [ - 'token=123', - ]); + mock.verify(); }); it('should set attributes for multiple values', () => { + mock + .expects('setAttribute') + .calledWithExactly('http.request.header.origin', [ + 'localhost', + 'www.example.com', + ]); + utils.headerCapture('request', ['Origin'])(span, () => [ 'localhost', 'www.example.com', ]); - assert.deepStrictEqual(span.attributes['http.request.header.origin'], [ - 'localhost', - 'www.example.com', - ]); + mock.verify(); }); it('sets attributes for multiple headers', () => { + mock + .expects('setAttribute') + .calledWithExactly('http.request.header.origin', ['localhost']); + mock + .expects('setAttribute') + .calledWithExactly('http.request.header.foo', [42]); + utils.headerCapture('request', ['Origin', 'Foo'])(span, header => { if (header === 'origin') { return 'localhost'; @@ -582,22 +593,24 @@ describe('Utility', () => { return undefined; }); - - assert.deepStrictEqual(span.attributes['http.request.header.origin'], [ - 'localhost', - ]); - assert.deepStrictEqual(span.attributes['http.request.header.foo'], [42]); + mock.verify(); }); it('should normalize header names', () => { + mock + .expects('setAttribute') + .calledWithExactly('http.request.header.x_forwarded_for', ['foo']); + utils.headerCapture('request', ['X-Forwarded-For'])(span, () => 'foo'); - assert.deepStrictEqual( - span.attributes['http.request.header.x_forwarded_for'], - ['foo'] - ); + mock.verify(); }); it('ignores non-existent headers', () => { + mock + .expects('setAttribute') + .once() + .calledWithExactly('http.request.header.origin', ['localhost']); + utils.headerCapture('request', ['Origin', 'Accept'])(span, header => { if (header === 'origin') { return 'localhost'; @@ -605,14 +618,7 @@ describe('Utility', () => { return undefined; }); - - assert.deepStrictEqual(span.attributes['http.request.header.origin'], [ - 'localhost', - ]); - assert.deepStrictEqual( - span.attributes['http.request.header.accept'], - undefined - ); + mock.verify(); }); }); From 776993f0fcefd150d1215042b5515d99a9f247b1 Mon Sep 17 00:00:00 2001 From: Daniel Dyla Date: Fri, 4 Oct 2024 04:35:04 -0400 Subject: [PATCH 18/20] feat(instrumentation-http): emit semconv 1.27 metrics (#5026) --- experimental/CHANGELOG.md | 8 +- .../README.md | 27 +- .../src/http.ts | 231 +++++++++-- .../test/functionals/http-metrics.test.ts | 387 ++++++++++++++---- 4 files changed, 526 insertions(+), 127 deletions(-) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 49eb60e237..37252e92cc 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -10,10 +10,10 @@ All notable changes to experimental packages in this project will be documented ### :rocket: (Enhancement) * feat(api-logs): Add delegating no-op logger provider [#4861](https://github.com/open-telemetry/opentelemetry-js/pull/4861) @hectorhdzg -* feat(instrumentation-http): Add support for [Semantic Conventions 1.27+](https://github.com/open-telemetry/semantic-conventions/releases/tag/v1.27.0) [#4940](https://github.com/open-telemetry/opentelemetry-js/pull/4940) [#4978](https://github.com/open-telemetry/opentelemetry-js/pull/4978) @dyladan - * Applies to both client and server spans - * Generate spans compliant with Semantic Conventions 1.27+ when `OTEL_SEMCONV_STABILITY_OPT_IN` contains `http` or `http/dup` - * Generate spans backwards compatible with previous attributes when `OTEL_SEMCONV_STABILITY_OPT_IN` contains `http/dup` or DOES NOT contain `http` +* feat(instrumentation-http): Add support for [Semantic Conventions 1.27+](https://github.com/open-telemetry/semantic-conventions/releases/tag/v1.27.0) [#4940](https://github.com/open-telemetry/opentelemetry-js/pull/4940) [#4978](https://github.com/open-telemetry/opentelemetry-js/pull/4978) [#5026](https://github.com/open-telemetry/opentelemetry-js/pull/5026) @dyladan + * Applies to client and server spans and metrics + * Generate spans and metrics compliant with Semantic Conventions 1.27+ when `OTEL_SEMCONV_STABILITY_OPT_IN` contains `http` or `http/dup` + * Generate spans and metrics backwards compatible with previous attributes when `OTEL_SEMCONV_STABILITY_OPT_IN` contains `http/dup` or DOES NOT contain `http` ### :bug: (Bug Fix) diff --git a/experimental/packages/opentelemetry-instrumentation-http/README.md b/experimental/packages/opentelemetry-instrumentation-http/README.md index 456d5b3473..030e8f327b 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/README.md +++ b/experimental/packages/opentelemetry-instrumentation-http/README.md @@ -76,8 +76,6 @@ The following options are deprecated: ## Semantic Conventions -### Client and Server Spans - Prior to version `0.54`, this instrumentation created spans targeting an experimental semantic convention [Version 1.7.0](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/semantic_conventions/README.md). This package is capable of emitting both Semantic Convention [Version 1.7.0](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/semantic_conventions/README.md) and [Version 1.27.0](https://github.com/open-telemetry/semantic-conventions/blob/v1.27.0/docs/http/http-spans.md). @@ -86,21 +84,19 @@ The values `http` and `http/dup` control this instrumentation. See details for the behavior of each of these values below. If neither `http` or `http/dup` is included in `OTEL_SEMCONV_STABILITY_OPT_IN`, the old experimental semantic conventions will be used by default. -#### Stable Semantic Conventions 1.27 +### Stable Semantic Conventions 1.27 Enabled when `OTEL_SEMCONV_STABILITY_OPT_IN` contains `http` OR `http/dup`. This is the recommended configuration, and will soon become the default behavior. -Follow all requirements and recommendations of HTTP Client and Server Span Semantic Conventions [Version 1.27.0](https://github.com/open-telemetry/semantic-conventions/blob/v1.27.0/docs/http/http-spans.md), including all required and recommended attributes. - -#### Legacy Behavior (default) +Follow all requirements and recommendations of HTTP Client and Server Semantic Conventions Version 1.27.0 for [spans](https://github.com/open-telemetry/semantic-conventions/blob/v1.27.0/docs/http/http-spans.md) and [metrics](https://github.com/open-telemetry/semantic-conventions/blob/v1.27.0/docs/http/http-metrics.md), including all required and recommended attributes. -Enabled when `OTEL_SEMCONV_STABILITY_OPT_IN` contains `http/dup` or DOES NOT CONTAIN `http`. -This is the current default behavior. +Metrics Exported: -Create HTTP client spans which implement Semantic Convention [Version 1.7.0](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/semantic_conventions/README.md). +- [`http.server.request.duration`](https://github.com/open-telemetry/semantic-conventions/blob/v1.27.0/docs/http/http-metrics.md#metric-httpserverrequestduration) +- [`http.client.request.duration`](https://github.com/open-telemetry/semantic-conventions/blob/v1.27.0/docs/http/http-metrics.md#metric-httpclientrequestduration) -#### Upgrading Semantic Conventions +### Upgrading Semantic Conventions When upgrading to the new semantic conventions, it is recommended to do so in the following order: @@ -111,9 +107,16 @@ When upgrading to the new semantic conventions, it is recommended to do so in th This will cause both the old and new semantic conventions to be emitted during the transition period. -### Server Spans +### Legacy Behavior (default) + +Enabled when `OTEL_SEMCONV_STABILITY_OPT_IN` contains `http/dup` or DOES NOT CONTAIN `http`. +This is the current default behavior. + +Create HTTP client spans which implement Semantic Convention [Version 1.7.0](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/semantic_conventions/README.md). + +#### Server Spans (legacy) -This package uses `@opentelemetry/semantic-conventions` version `1.22+`, which implements Semantic Convention [Version 1.7.0](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/semantic_conventions/README.md). +When `OTEL_SEMCONV_STABILITY_OPT_IN` is not set or includes `http/dup`, this module implements Semantic Convention [Version 1.7.0](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/semantic_conventions/README.md). Attributes collected: diff --git a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts index 456b8d08e0..0d0a63920b 100644 --- a/experimental/packages/opentelemetry-instrumentation-http/src/http.ts +++ b/experimental/packages/opentelemetry-instrumentation-http/src/http.ts @@ -62,7 +62,18 @@ import { getEnv, } from '@opentelemetry/core'; import { errorMonitor } from 'events'; -import { SEMATTRS_HTTP_ROUTE } from '@opentelemetry/semantic-conventions'; +import { + ATTR_HTTP_REQUEST_METHOD, + ATTR_HTTP_RESPONSE_STATUS_CODE, + ATTR_HTTP_ROUTE, + ATTR_NETWORK_PROTOCOL_VERSION, + ATTR_SERVER_ADDRESS, + ATTR_SERVER_PORT, + ATTR_URL_SCHEME, + METRIC_HTTP_CLIENT_REQUEST_DURATION, + METRIC_HTTP_SERVER_REQUEST_DURATION, + SEMATTRS_HTTP_ROUTE, +} from '@opentelemetry/semantic-conventions'; import { extractHostnameAndPort, getIncomingRequestAttributes, @@ -88,8 +99,10 @@ export class HttpInstrumentation extends InstrumentationBase = new WeakSet(); private _headerCapture; - private _httpServerDurationHistogram!: Histogram; - private _httpClientDurationHistogram!: Histogram; + private _oldHttpServerDurationHistogram!: Histogram; + private _stableHttpServerDurationHistogram!: Histogram; + private _oldHttpClientDurationHistogram!: Histogram; + private _stableHttpClientDurationHistogram!: Histogram; private _semconvStability = SemconvStability.OLD; @@ -109,7 +122,7 @@ export class HttpInstrumentation extends InstrumentationBase { this._diag.debug('outgoingRequest on request error()', error); @@ -458,7 +558,13 @@ export class HttpInstrumentation extends InstrumentationBase { + let contextManager: ContextManager; + beforeEach(() => { + contextManager = new AsyncHooksContextManager().enable(); + context.setGlobalContextManager(contextManager); + instrumentation['_updateMetricInstruments'](); metricsMemoryExporter.reset(); }); before(() => { + instrumentation.setConfig({}); instrumentation.enable(); server = http.createServer((request, response) => { + 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'); }); server.listen(serverPort); @@ -74,87 +95,289 @@ describe('metrics', () => { instrumentation.disable(); }); - it('should add server/client duration metrics', async () => { - const requestCount = 3; - for (let i = 0; i < requestCount; i++) { - await httpRequest.get( - `${protocol}://${hostname}:${serverPort}${pathname}` - ); - } - await metricReader.collectAndExport(); - const resourceMetrics = metricsMemoryExporter.getMetrics(); - const scopeMetrics = resourceMetrics[0].scopeMetrics; - assert.strictEqual(scopeMetrics.length, 1, 'scopeMetrics count'); - const metrics = scopeMetrics[0].metrics; - assert.strictEqual(metrics.length, 2, 'metrics count'); - assert.strictEqual(metrics[0].dataPointType, DataPointType.HISTOGRAM); - assert.strictEqual( - metrics[0].descriptor.description, - 'Measures the duration of inbound HTTP requests.' - ); - assert.strictEqual(metrics[0].descriptor.name, 'http.server.duration'); - assert.strictEqual(metrics[0].descriptor.unit, 'ms'); - assert.strictEqual(metrics[0].dataPoints.length, 1); - assert.strictEqual( - (metrics[0].dataPoints[0].value as any).count, - requestCount - ); - assert.strictEqual( - metrics[0].dataPoints[0].attributes[SEMATTRS_HTTP_SCHEME], - 'http' - ); - assert.strictEqual( - metrics[0].dataPoints[0].attributes[SEMATTRS_HTTP_METHOD], - 'GET' - ); - assert.strictEqual( - metrics[0].dataPoints[0].attributes[SEMATTRS_HTTP_FLAVOR], - '1.1' - ); - assert.strictEqual( - metrics[0].dataPoints[0].attributes[SEMATTRS_NET_HOST_NAME], - 'localhost' - ); - assert.strictEqual( - metrics[0].dataPoints[0].attributes[SEMATTRS_HTTP_STATUS_CODE], - 200 - ); - assert.strictEqual( - metrics[0].dataPoints[0].attributes[SEMATTRS_NET_HOST_PORT], - 22346 - ); - - assert.strictEqual(metrics[1].dataPointType, DataPointType.HISTOGRAM); - assert.strictEqual( - metrics[1].descriptor.description, - 'Measures the duration of outbound HTTP requests.' - ); - assert.strictEqual(metrics[1].descriptor.name, 'http.client.duration'); - assert.strictEqual(metrics[1].descriptor.unit, 'ms'); - assert.strictEqual(metrics[1].dataPoints.length, 1); - assert.strictEqual( - (metrics[1].dataPoints[0].value as any).count, - requestCount - ); - assert.strictEqual( - metrics[1].dataPoints[0].attributes[SEMATTRS_HTTP_METHOD], - 'GET' - ); - assert.strictEqual( - metrics[1].dataPoints[0].attributes[SEMATTRS_NET_PEER_NAME], - 'localhost' - ); - assert.strictEqual( - metrics[1].dataPoints[0].attributes[SEMATTRS_NET_PEER_PORT], - 22346 - ); - assert.strictEqual( - metrics[1].dataPoints[0].attributes[SEMATTRS_HTTP_STATUS_CODE], - 200 - ); - assert.strictEqual( - metrics[1].dataPoints[0].attributes[SEMATTRS_HTTP_FLAVOR], - '1.1' - ); + describe('with no stability set', () => { + it('should add server/client duration metrics', async () => { + const requestCount = 3; + for (let i = 0; i < requestCount; i++) { + await httpRequest.get( + `${protocol}://${hostname}:${serverPort}${pathname}` + ); + } + await metricReader.collectAndExport(); + const resourceMetrics = metricsMemoryExporter.getMetrics(); + const scopeMetrics = resourceMetrics[0].scopeMetrics; + assert.strictEqual(scopeMetrics.length, 1, 'scopeMetrics count'); + const metrics = scopeMetrics[0].metrics; + assert.strictEqual(metrics.length, 2, 'metrics count'); + assert.strictEqual(metrics[0].dataPointType, DataPointType.HISTOGRAM); + assert.strictEqual( + metrics[0].descriptor.description, + 'Measures the duration of inbound HTTP requests.' + ); + assert.strictEqual(metrics[0].descriptor.name, 'http.server.duration'); + assert.strictEqual(metrics[0].descriptor.unit, 'ms'); + assert.strictEqual(metrics[0].dataPoints.length, 1); + assert.strictEqual( + (metrics[0].dataPoints[0].value as any).count, + requestCount + ); + assert.strictEqual( + metrics[0].dataPoints[0].attributes[SEMATTRS_HTTP_SCHEME], + 'http' + ); + assert.strictEqual( + metrics[0].dataPoints[0].attributes[SEMATTRS_HTTP_METHOD], + 'GET' + ); + assert.strictEqual( + metrics[0].dataPoints[0].attributes[SEMATTRS_HTTP_FLAVOR], + '1.1' + ); + assert.strictEqual( + metrics[0].dataPoints[0].attributes[SEMATTRS_NET_HOST_NAME], + 'localhost' + ); + assert.strictEqual( + metrics[0].dataPoints[0].attributes[SEMATTRS_HTTP_STATUS_CODE], + 200 + ); + assert.strictEqual( + metrics[0].dataPoints[0].attributes[SEMATTRS_NET_HOST_PORT], + 22346 + ); + + assert.strictEqual(metrics[1].dataPointType, DataPointType.HISTOGRAM); + assert.strictEqual( + metrics[1].descriptor.description, + 'Measures the duration of outbound HTTP requests.' + ); + assert.strictEqual(metrics[1].descriptor.name, 'http.client.duration'); + assert.strictEqual(metrics[1].descriptor.unit, 'ms'); + assert.strictEqual(metrics[1].dataPoints.length, 1); + assert.strictEqual( + (metrics[1].dataPoints[0].value as any).count, + requestCount + ); + assert.strictEqual( + metrics[1].dataPoints[0].attributes[SEMATTRS_HTTP_METHOD], + 'GET' + ); + assert.strictEqual( + metrics[1].dataPoints[0].attributes[SEMATTRS_NET_PEER_NAME], + 'localhost' + ); + assert.strictEqual( + metrics[1].dataPoints[0].attributes[SEMATTRS_NET_PEER_PORT], + 22346 + ); + assert.strictEqual( + metrics[1].dataPoints[0].attributes[SEMATTRS_HTTP_STATUS_CODE], + 200 + ); + assert.strictEqual( + metrics[1].dataPoints[0].attributes[SEMATTRS_HTTP_FLAVOR], + '1.1' + ); + }); + }); + + describe('with no semconv stability set to stable', () => { + before(() => { + instrumentation['_semconvStability'] = SemconvStability.STABLE; + }); + + it('should add server/client duration metrics', async () => { + const requestCount = 3; + for (let i = 0; i < requestCount; i++) { + await httpRequest.get( + `${protocol}://${hostname}:${serverPort}${pathname}` + ); + } + await metricReader.collectAndExport(); + const resourceMetrics = metricsMemoryExporter.getMetrics(); + const scopeMetrics = resourceMetrics[0].scopeMetrics; + assert.strictEqual(scopeMetrics.length, 1, 'scopeMetrics count'); + const metrics = scopeMetrics[0].metrics; + assert.strictEqual(metrics.length, 2, 'metrics count'); + assert.strictEqual(metrics[0].dataPointType, DataPointType.HISTOGRAM); + assert.strictEqual( + metrics[0].descriptor.description, + 'Duration of HTTP server requests.' + ); + assert.strictEqual( + metrics[0].descriptor.name, + 'http.server.request.duration' + ); + assert.strictEqual(metrics[0].descriptor.unit, 's'); + assert.strictEqual(metrics[0].dataPoints.length, 1); + assert.strictEqual( + (metrics[0].dataPoints[0].value as any).count, + requestCount + ); + assert.deepStrictEqual(metrics[0].dataPoints[0].attributes, { + [ATTR_HTTP_REQUEST_METHOD]: 'GET', + [ATTR_URL_SCHEME]: 'http', + [ATTR_NETWORK_PROTOCOL_VERSION]: '1.1', + }); + + assert.strictEqual(metrics[1].dataPointType, DataPointType.HISTOGRAM); + assert.strictEqual( + metrics[1].descriptor.description, + 'Duration of HTTP client requests.' + ); + assert.strictEqual( + metrics[1].descriptor.name, + 'http.client.request.duration' + ); + assert.strictEqual(metrics[1].descriptor.unit, 's'); + assert.strictEqual(metrics[1].dataPoints.length, 1); + assert.strictEqual( + (metrics[1].dataPoints[0].value as any).count, + requestCount + ); + + assert.deepStrictEqual(metrics[1].dataPoints[0].attributes, { + [ATTR_HTTP_REQUEST_METHOD]: 'GET', + [ATTR_SERVER_ADDRESS]: 'localhost', + [ATTR_SERVER_PORT]: 22346, + }); + }); + }); + + describe('with no semconv stability set to duplicate', () => { + before(() => { + instrumentation['_semconvStability'] = SemconvStability.DUPLICATE; + }); + + it('should add server/client duration metrics', async () => { + const requestCount = 3; + for (let i = 0; i < requestCount; i++) { + await httpRequest.get( + `${protocol}://${hostname}:${serverPort}${pathname}` + ); + } + await metricReader.collectAndExport(); + const resourceMetrics = metricsMemoryExporter.getMetrics(); + const scopeMetrics = resourceMetrics[0].scopeMetrics; + assert.strictEqual(scopeMetrics.length, 1, 'scopeMetrics count'); + const metrics = scopeMetrics[0].metrics; + assert.strictEqual(metrics.length, 4, 'metrics count'); + + // old metrics + assert.strictEqual(metrics[0].dataPointType, DataPointType.HISTOGRAM); + assert.strictEqual( + metrics[0].descriptor.description, + 'Measures the duration of inbound HTTP requests.' + ); + assert.strictEqual(metrics[0].descriptor.name, 'http.server.duration'); + assert.strictEqual(metrics[0].descriptor.unit, 'ms'); + assert.strictEqual(metrics[0].dataPoints.length, 1); + assert.strictEqual( + (metrics[0].dataPoints[0].value as any).count, + requestCount + ); + assert.strictEqual( + metrics[0].dataPoints[0].attributes[SEMATTRS_HTTP_SCHEME], + 'http' + ); + assert.strictEqual( + metrics[0].dataPoints[0].attributes[SEMATTRS_HTTP_METHOD], + 'GET' + ); + assert.strictEqual( + metrics[0].dataPoints[0].attributes[SEMATTRS_HTTP_FLAVOR], + '1.1' + ); + assert.strictEqual( + metrics[0].dataPoints[0].attributes[SEMATTRS_NET_HOST_NAME], + 'localhost' + ); + assert.strictEqual( + metrics[0].dataPoints[0].attributes[SEMATTRS_HTTP_STATUS_CODE], + 200 + ); + assert.strictEqual( + metrics[0].dataPoints[0].attributes[SEMATTRS_NET_HOST_PORT], + 22346 + ); + + assert.strictEqual(metrics[1].dataPointType, DataPointType.HISTOGRAM); + assert.strictEqual( + metrics[1].descriptor.description, + 'Measures the duration of outbound HTTP requests.' + ); + assert.strictEqual(metrics[1].descriptor.name, 'http.client.duration'); + assert.strictEqual(metrics[1].descriptor.unit, 'ms'); + assert.strictEqual(metrics[1].dataPoints.length, 1); + assert.strictEqual( + (metrics[1].dataPoints[0].value as any).count, + requestCount + ); + assert.strictEqual( + metrics[1].dataPoints[0].attributes[SEMATTRS_HTTP_METHOD], + 'GET' + ); + assert.strictEqual( + metrics[1].dataPoints[0].attributes[SEMATTRS_NET_PEER_NAME], + 'localhost' + ); + assert.strictEqual( + metrics[1].dataPoints[0].attributes[SEMATTRS_NET_PEER_PORT], + 22346 + ); + assert.strictEqual( + metrics[1].dataPoints[0].attributes[SEMATTRS_HTTP_STATUS_CODE], + 200 + ); + assert.strictEqual( + metrics[1].dataPoints[0].attributes[SEMATTRS_HTTP_FLAVOR], + '1.1' + ); + + // Stable metrics + assert.strictEqual(metrics[2].dataPointType, DataPointType.HISTOGRAM); + assert.strictEqual( + metrics[2].descriptor.description, + 'Duration of HTTP server requests.' + ); + assert.strictEqual( + metrics[2].descriptor.name, + 'http.server.request.duration' + ); + assert.strictEqual(metrics[2].descriptor.unit, 's'); + assert.strictEqual(metrics[2].dataPoints.length, 1); + assert.strictEqual( + (metrics[2].dataPoints[0].value as any).count, + requestCount + ); + assert.deepStrictEqual(metrics[2].dataPoints[0].attributes, { + [ATTR_HTTP_REQUEST_METHOD]: 'GET', + [ATTR_URL_SCHEME]: 'http', + [ATTR_NETWORK_PROTOCOL_VERSION]: '1.1', + [ATTR_HTTP_ROUTE]: 'TheRoute', + }); + + assert.strictEqual(metrics[3].dataPointType, DataPointType.HISTOGRAM); + assert.strictEqual( + metrics[3].descriptor.description, + 'Duration of HTTP client requests.' + ); + assert.strictEqual( + metrics[3].descriptor.name, + 'http.client.request.duration' + ); + assert.strictEqual(metrics[3].descriptor.unit, 's'); + assert.strictEqual(metrics[3].dataPoints.length, 1); + assert.strictEqual( + (metrics[3].dataPoints[0].value as any).count, + requestCount + ); + + assert.deepStrictEqual(metrics[3].dataPoints[0].attributes, { + [ATTR_HTTP_REQUEST_METHOD]: 'GET', + [ATTR_SERVER_ADDRESS]: 'localhost', + [ATTR_SERVER_PORT]: 22346, + }); + }); }); }); From 4947c2d6a4d33343194879c3b3eb18040eb9683b Mon Sep 17 00:00:00 2001 From: David Luna Date: Fri, 4 Oct 2024 10:42:35 +0200 Subject: [PATCH 19/20] chore(exporter-zipkin): remove usages of Span constructor (#5030) Co-authored-by: Marc Pichler --- CHANGELOG.md | 1 + .../test/common/transform.test.ts | 229 +++++++++--------- 2 files changed, 109 insertions(+), 121 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e42a90f7c..7ad9ff304d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ For semantic convention package changes, see the [semconv CHANGELOG](packages/se [#4992](https://github.com/open-telemetry/opentelemetry-js/pull/4992) * refactor(sdk-metrics): replace `MetricsAttributes` with `Attributes` [#5021](https://github.com/open-telemetry/opentelemetry-js/pull/5021) @david-luna * refactor(instrumentation-http): replace `SpanAttributes` and `MetricsAttributes` with `Attributes` [#5023](https://github.com/open-telemetry/opentelemetry-js/pull/5023) @david-luna +* chore(exporter-zipkin): remove usages of Span constructor [#5030](https://github.com/open-telemetry/opentelemetry-js/pull/5030) @david-luna * test(instrumentation-http): remove usages of `new Span` in tests [#5035](https://github.com/open-telemetry/opentelemetry-js/pull/5035) @david-luna ## 1.26.0 diff --git a/packages/opentelemetry-exporter-zipkin/test/common/transform.test.ts b/packages/opentelemetry-exporter-zipkin/test/common/transform.test.ts index 223257c45a..0c78dbbcf8 100644 --- a/packages/opentelemetry-exporter-zipkin/test/common/transform.test.ts +++ b/packages/opentelemetry-exporter-zipkin/test/common/transform.test.ts @@ -16,17 +16,16 @@ import * as api from '@opentelemetry/api'; import { + hrTime, hrTimeDuration, hrTimeToMicroseconds, + millisToHrTime, VERSION, } from '@opentelemetry/core'; -import { Resource } from '@opentelemetry/resources'; -import { BasicTracerProvider, Span } from '@opentelemetry/sdk-trace-base'; +import { IResource } from '@opentelemetry/resources'; +import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; import * as assert from 'assert'; -import { - SEMRESATTRS_SERVICE_NAME, - SEMRESATTRS_TELEMETRY_SDK_LANGUAGE, -} from '@opentelemetry/semantic-conventions'; +import { SEMRESATTRS_SERVICE_NAME } from '@opentelemetry/semantic-conventions'; import { defaultStatusCodeTagName, defaultStatusErrorTagName, @@ -35,43 +34,69 @@ import { _toZipkinTags, } from '../../src/transform'; import * as zipkinTypes from '../../src/types'; -const tracer = new BasicTracerProvider({ - resource: Resource.default().merge( - new Resource({ - [SEMRESATTRS_SERVICE_NAME]: 'zipkin-test', - cost: '112.12', - service: 'ui', - version: '1', - }) - ), -}).getTracer('default'); - -const language = tracer.resource.attributes[SEMRESATTRS_TELEMETRY_SDK_LANGUAGE]; +const resource = { + attributes: { + [SEMRESATTRS_SERVICE_NAME]: 'zipkin-test', + cost: '112.12', + service: 'ui', + version: '1', + 'telemetry.sdk.language': 'nodejs', + 'telemetry.sdk.name': 'opentelemetry', + 'telemetry.sdk.version': VERSION, + }, +} as unknown as IResource; const parentId = '5c1c63257de34c67'; const spanContext: api.SpanContext = { traceId: 'd4cda95b652f4a1592b449d5929fda1b', spanId: '6e0c63257de34c92', traceFlags: api.TraceFlags.SAMPLED, }; +const currentTime = Date.now(); +const durationMs = 10; +const startTime = hrTime(currentTime - durationMs); +const endTime = hrTime(currentTime); +const duration = millisToHrTime(durationMs); + +function getSpan(options: Partial): ReadableSpan { + const span = { + name: options.name || 'my-span', + kind: typeof options.kind === 'number' ? options.kind : api.SpanKind.SERVER, + startTime: options.startTime || startTime, + endTime: options.endTime || endTime, + duration: options.duration || duration, + spanContext: () => spanContext, + parentSpanId: options.parentSpanId || parentId, + attributes: options.attributes || {}, + events: options.events || [], + status: options.status || { code: api.SpanStatusCode.UNSET }, + resource, + } as ReadableSpan; + + // Expicit `undefined` properties in options will be removed from the + // result span. + Object.keys(options).forEach(k => { + if (options[k as keyof ReadableSpan] === undefined) { + delete span[k as keyof ReadableSpan]; + } + }); + + return span; +} describe('transform', () => { describe('toZipkinSpan', () => { it('should convert an OpenTelemetry span to a Zipkin span', () => { - const span = new Span( - tracer, - api.ROOT_CONTEXT, - 'my-span', - spanContext, - api.SpanKind.SERVER, - parentId - ); - span.setAttributes({ - key1: 'value1', - key2: 'value2', + const span = getSpan({ + attributes: { key1: 'value1', key2: 'value2' }, + events: [ + { + name: 'my-event', + time: hrTime(Date.now() + 5), + attributes: { key3: 'value 3' }, + }, + ], }); - span.addEvent('my-event', { key3: 'value3' }); - span.end(); const zipkinSpan = toZipkinSpan( span, @@ -103,7 +128,7 @@ describe('transform', () => { cost: '112.12', service: 'ui', version: '1', - 'telemetry.sdk.language': language, + 'telemetry.sdk.language': 'nodejs', 'telemetry.sdk.name': 'opentelemetry', 'telemetry.sdk.version': VERSION, }, @@ -112,14 +137,9 @@ describe('transform', () => { }); }); it("should skip parentSpanId if doesn't exist", () => { - const span = new Span( - tracer, - api.ROOT_CONTEXT, - 'my-span', - spanContext, - api.SpanKind.SERVER - ); - span.end(); + const span = getSpan({ + parentSpanId: undefined, + }); const zipkinSpan = toZipkinSpan( span, @@ -144,7 +164,7 @@ describe('transform', () => { cost: '112.12', service: 'ui', version: '1', - 'telemetry.sdk.language': language, + 'telemetry.sdk.language': 'nodejs', 'telemetry.sdk.name': 'opentelemetry', 'telemetry.sdk.version': VERSION, }, @@ -163,15 +183,10 @@ describe('transform', () => { it(`should map OpenTelemetry SpanKind ${ api.SpanKind[item.ot] } to Zipkin ${item.zipkin}`, () => { - const span = new Span( - tracer, - api.ROOT_CONTEXT, - 'my-span', - spanContext, - item.ot - ); - span.end(); - + const span = getSpan({ + kind: item.ot, + parentSpanId: undefined, + }); const zipkinSpan = toZipkinSpan( span, 'my-service', @@ -195,7 +210,7 @@ describe('transform', () => { cost: '112.12', service: 'ui', version: '1', - 'telemetry.sdk.language': language, + 'telemetry.sdk.language': 'nodejs', 'telemetry.sdk.name': 'opentelemetry', 'telemetry.sdk.version': VERSION, }, @@ -208,17 +223,12 @@ describe('transform', () => { describe('_toZipkinTags', () => { it('should convert OpenTelemetry attributes to Zipkin tags', () => { - const span = new Span( - tracer, - api.ROOT_CONTEXT, - 'my-span', - spanContext, - api.SpanKind.SERVER, - parentId - ); - span.setAttributes({ - key1: 'value1', - key2: 'value2', + const span = getSpan({ + parentSpanId: undefined, + attributes: { + key1: 'value1', + key2: 'value2', + }, }); const tags: zipkinTypes.Tags = _toZipkinTags( span, @@ -230,7 +240,7 @@ describe('transform', () => { key1: 'value1', key2: 'value2', [SEMRESATTRS_SERVICE_NAME]: 'zipkin-test', - 'telemetry.sdk.language': language, + 'telemetry.sdk.language': 'nodejs', 'telemetry.sdk.name': 'opentelemetry', 'telemetry.sdk.version': VERSION, cost: '112.12', @@ -239,21 +249,14 @@ describe('transform', () => { }); }); it('should map OpenTelemetry constructor attributes to a Zipkin tag', () => { - const span = new Span( - tracer, - api.ROOT_CONTEXT, - 'my-span', - spanContext, - api.SpanKind.SERVER, - parentId, - [], - undefined, - undefined, - { + const span = getSpan({ + parentSpanId: undefined, + attributes: { key1: 'value1', key2: 'value2', - } - ); + }, + }); + const tags: zipkinTypes.Tags = _toZipkinTags( span, defaultStatusCodeTagName, @@ -264,7 +267,7 @@ describe('transform', () => { key1: 'value1', key2: 'value2', [SEMRESATTRS_SERVICE_NAME]: 'zipkin-test', - 'telemetry.sdk.language': language, + 'telemetry.sdk.language': 'nodejs', 'telemetry.sdk.name': 'opentelemetry', 'telemetry.sdk.version': VERSION, cost: '112.12', @@ -273,21 +276,13 @@ describe('transform', () => { }); }); it('should map OpenTelemetry SpanStatus.code to a Zipkin tag', () => { - const span = new Span( - tracer, - api.ROOT_CONTEXT, - 'my-span', - spanContext, - api.SpanKind.SERVER, - parentId - ); - const status: api.SpanStatus = { - code: api.SpanStatusCode.ERROR, - }; - span.setStatus(status); - span.setAttributes({ - key1: 'value1', - key2: 'value2', + const span = getSpan({ + parentSpanId: undefined, + attributes: { + key1: 'value1', + key2: 'value2', + }, + status: { code: api.SpanStatusCode.ERROR }, }); const tags: zipkinTypes.Tags = _toZipkinTags( span, @@ -300,7 +295,7 @@ describe('transform', () => { key2: 'value2', [defaultStatusCodeTagName]: 'ERROR', [SEMRESATTRS_SERVICE_NAME]: 'zipkin-test', - 'telemetry.sdk.language': language, + 'telemetry.sdk.language': 'nodejs', 'telemetry.sdk.name': 'opentelemetry', 'telemetry.sdk.version': VERSION, cost: '112.12', @@ -309,22 +304,13 @@ describe('transform', () => { }); }); it('should map OpenTelemetry SpanStatus.message to a Zipkin tag', () => { - const span = new Span( - tracer, - api.ROOT_CONTEXT, - 'my-span', - spanContext, - api.SpanKind.SERVER, - parentId - ); - const status: api.SpanStatus = { - code: api.SpanStatusCode.ERROR, - message: 'my-message', - }; - span.setStatus(status); - span.setAttributes({ - key1: 'value1', - key2: 'value2', + const span = getSpan({ + parentSpanId: undefined, + attributes: { + key1: 'value1', + key2: 'value2', + }, + status: { code: api.SpanStatusCode.ERROR, message: 'my-message' }, }); const tags: zipkinTypes.Tags = _toZipkinTags( span, @@ -336,9 +322,9 @@ describe('transform', () => { key1: 'value1', key2: 'value2', [defaultStatusCodeTagName]: 'ERROR', - [defaultStatusErrorTagName]: status.message, + [defaultStatusErrorTagName]: 'my-message', [SEMRESATTRS_SERVICE_NAME]: 'zipkin-test', - 'telemetry.sdk.language': language, + 'telemetry.sdk.language': 'nodejs', 'telemetry.sdk.name': 'opentelemetry', 'telemetry.sdk.version': VERSION, cost: '112.12', @@ -350,16 +336,17 @@ describe('transform', () => { describe('_toZipkinAnnotations', () => { it('should convert OpenTelemetry events to Zipkin annotations', () => { - const span = new Span( - tracer, - api.ROOT_CONTEXT, - 'my-span', - spanContext, - api.SpanKind.SERVER, - parentId - ); - span.addEvent('my-event1'); - span.addEvent('my-event2', { key1: 'value1' }); + const span = getSpan({ + parentSpanId: undefined, + events: [ + { name: 'my-event1', time: hrTime(Date.now()) }, + { + name: 'my-event2', + time: hrTime(Date.now()), + attributes: { key1: 'value1' }, + }, + ], + }); const annotations = _toZipkinAnnotations(span.events); assert.deepStrictEqual(annotations, [ From 6ccd4df33a47eb9b3e56a675a4f217b4844e18cd Mon Sep 17 00:00:00 2001 From: Marc Pichler Date: Mon, 7 Oct 2024 22:02:54 +0200 Subject: [PATCH 20/20] docs: use npm ci in CONTRIBUTING.md (#5040) --- CONTRIBUTING.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 516e67d524..374f0470d0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,9 +29,9 @@ detailed instructions, see [development](#development) below. ```sh git clone https://github.com/open-telemetry/opentelemetry-js.git cd opentelemetry-js -npm install +npm ci npm run compile -npm test +npm run test ``` ## Pull Request Merge Guidelines @@ -164,7 +164,7 @@ Most of the commands needed for development are accessed as [npm scripts](https: This will install all dependencies for the root project and all modules managed by `npm workspaces`. ```sh -npm install +npm ci ``` ### Compile modules @@ -293,10 +293,10 @@ export const _globalThis = typeof globalThis === 'object' ? globalThis : global; /// packages/opentelemetry-core/src/platform/browser/globalThis.ts export const _globalThis: typeof globalThis = typeof globalThis === 'object' ? globalThis : - typeof self === 'object' ? self : - typeof window === 'object' ? window : - typeof global === 'object' ? global : - {} as typeof globalThis; + typeof self === 'object' ? self : + typeof window === 'object' ? window : + typeof global === 'object' ? global : + {} as typeof globalThis; ``` Even though the implementation may differ, the exported names must be aligned.