From a2e0867e73222ac27cb391419a713d32ab49550d Mon Sep 17 00:00:00 2001 From: David Luna Date: Thu, 22 Feb 2024 13:45:56 +0100 Subject: [PATCH 01/81] chore(instrumentation-undici): port code from core repo branch --- .../.eslintignore | 1 + .../.eslintrc.js | 7 + .../.tav.yml | 11 + .../LICENSE | 201 ++++++ .../README.md | 69 ++ .../package.json | 75 ++ .../src/enums/SemanticAttributes.ts | 168 +++++ .../src/index.ts | 17 + .../src/internal-types.ts | 43 ++ .../src/types.ts | 73 ++ .../src/undici.ts | 449 ++++++++++++ .../test/fetch.test.ts | 417 ++++++++++++ .../test/metrics.test.ts | 196 ++++++ .../test/undici.test.ts | 640 ++++++++++++++++++ .../test/utils/assertSpan.ts | 196 ++++++ .../test/utils/mock-metrics-reader.ts | 47 ++ .../test/utils/mock-propagation.ts | 52 ++ .../test/utils/mock-server.ts | 73 ++ .../tsconfig.json | 11 + 19 files changed, 2746 insertions(+) create mode 100644 plugins/node/opentelemetry-instrumentation-undici/.eslintignore create mode 100644 plugins/node/opentelemetry-instrumentation-undici/.eslintrc.js create mode 100644 plugins/node/opentelemetry-instrumentation-undici/.tav.yml create mode 100644 plugins/node/opentelemetry-instrumentation-undici/LICENSE create mode 100644 plugins/node/opentelemetry-instrumentation-undici/README.md create mode 100644 plugins/node/opentelemetry-instrumentation-undici/package.json create mode 100644 plugins/node/opentelemetry-instrumentation-undici/src/enums/SemanticAttributes.ts create mode 100644 plugins/node/opentelemetry-instrumentation-undici/src/index.ts create mode 100644 plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts create mode 100644 plugins/node/opentelemetry-instrumentation-undici/src/types.ts create mode 100644 plugins/node/opentelemetry-instrumentation-undici/src/undici.ts create mode 100644 plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts create mode 100644 plugins/node/opentelemetry-instrumentation-undici/test/metrics.test.ts create mode 100644 plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts create mode 100644 plugins/node/opentelemetry-instrumentation-undici/test/utils/assertSpan.ts create mode 100644 plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-metrics-reader.ts create mode 100644 plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-propagation.ts create mode 100644 plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-server.ts create mode 100644 plugins/node/opentelemetry-instrumentation-undici/tsconfig.json diff --git a/plugins/node/opentelemetry-instrumentation-undici/.eslintignore b/plugins/node/opentelemetry-instrumentation-undici/.eslintignore new file mode 100644 index 0000000000..378eac25d3 --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-undici/.eslintignore @@ -0,0 +1 @@ +build diff --git a/plugins/node/opentelemetry-instrumentation-undici/.eslintrc.js b/plugins/node/opentelemetry-instrumentation-undici/.eslintrc.js new file mode 100644 index 0000000000..f756f4488b --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-undici/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + "env": { + "mocha": true, + "node": true + }, + ...require('../../../eslint.config.js') +} diff --git a/plugins/node/opentelemetry-instrumentation-undici/.tav.yml b/plugins/node/opentelemetry-instrumentation-undici/.tav.yml new file mode 100644 index 0000000000..28322615d1 --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-undici/.tav.yml @@ -0,0 +1,11 @@ +undici: + jobs: + - versions: ">=5 <6" + node: '>=14' + commands: npm run test + - versions: ">=6 <7" + node: '>=18' + commands: npm run test + + # Fix missing `contrib-test-utils` package + pretest: npm run --prefix ../../../ lerna:link \ No newline at end of file diff --git a/plugins/node/opentelemetry-instrumentation-undici/LICENSE b/plugins/node/opentelemetry-instrumentation-undici/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-undici/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/node/opentelemetry-instrumentation-undici/README.md b/plugins/node/opentelemetry-instrumentation-undici/README.md new file mode 100644 index 0000000000..f507fc1157 --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-undici/README.md @@ -0,0 +1,69 @@ +# OpenTelemetry Undici/fetch Instrumentation for Node.js + +[![NPM Published Version][npm-img]][npm-url] +[![Apache License][license-image]][license-image] + +**Note: This is an experimental package under active development. New releases may include breaking changes.** + +This module provides automatic instrumentation for [`undici`](https://undici.nodejs.org/) and [`fetch`](https://nodejs.org/docs/latest/api/globals.html#fetch). + +## Installation + +```bash +npm install --save @opentelemetry/instrumentation-undici +``` + +## Usage + +OpenTelemetry Undici/fetch Instrumentation allows the user to automatically collect trace data and export them to their backend of choice, to give observability to distributed systems. + +To load a specific instrumentation (Undici in this case), specify it in the Node Tracer's configuration. + +```js +const { UndiciInstrumentation } = require('@opentelemetry/instrumentation-undici'); +const { + ConsoleSpanExporter, + NodeTracerProvider, + SimpleSpanProcessor, +} = require('@opentelemetry/sdk-trace-node'); +const { registerInstrumentations } = require('@opentelemetry/instrumentation'); + +const provider = new NodeTracerProvider(); + +provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); +provider.register(); + +registerInstrumentations({ + instrumentations: [new UndiciInstrumentation()], +}); + +``` + + +See [examples/http](https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/fetch) for a short example. + +### Undici/Fetch instrumentation Options + + + +Undici instrumentation has few options available to choose from. You can set the following: + +| Options | Type | Description | +| ------- | ---- | ----------- | +| [`onRequest`](https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-instrumentation-undici/src/types.ts#19) | `UndiciRequestHook` | Function for adding custom attributes before request is handled | + +## Useful links + +- For more information on OpenTelemetry, visit: +- For more about OpenTelemetry JavaScript: +- For help or feedback on this project, join us in [GitHub Discussions][discussions-url] + +## License + +Apache 2.0 - See [LICENSE][license-url] for more information. + +[discussions-url]: https://github.com/open-telemetry/opentelemetry-js/discussions +[license-url]: https://github.com/open-telemetry/opentelemetry-js/blob/main/LICENSE +[license-image]: https://img.shields.io/badge/license-Apache_2.0-green.svg?style=flat +[npm-url]: https://www.npmjs.com/package/@opentelemetry/instrumentation-http +[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Finstrumentation-http.svg diff --git a/plugins/node/opentelemetry-instrumentation-undici/package.json b/plugins/node/opentelemetry-instrumentation-undici/package.json new file mode 100644 index 0000000000..afd252b761 --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-undici/package.json @@ -0,0 +1,75 @@ +{ + "name": "@opentelemetry/instrumentation-undici", + "version": "0.33.0", + "description": "OpenTelemetry undici/fetch automatic instrumentation package.", + "main": "build/src/index.js", + "types": "build/src/index.d.ts", + "repository": "open-telemetry/opentelemetry-js-contrib", + "scripts": { + "prepublishOnly": "npm run compile", + "compile": "tsc -p .", + "test": "nyc ts-mocha -p tsconfig.json test/**/*.test.ts", + "test-all-versions": "tav", + "tdd": "npm run test -- --watch-extensions ts --watch", + "clean": "rimraf build/*", + "lint": "eslint . --ext .ts", + "lint:fix": "eslint . --ext .ts --fix", + "codecov": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../../", + "version": "node ../../../scripts/version-update.js", + "watch": "tsc -w", + "precompile": "tsc --version && lerna run version:update --scope @opentelemetry/instrumentation-undici --include-dependencies", + "prewatch": "npm run precompile", + "version:update": "node ../../../scripts/version-update.js" + }, + "keywords": [ + "opentelemetry", + "fetch", + "undici", + "nodejs", + "tracing", + "profiling", + "instrumentation" + ], + "author": "OpenTelemetry Authors", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + }, + "files": [ + "build/src/**/*.js", + "build/src/**/*.js.map", + "build/src/**/*.d.ts", + "doc", + "LICENSE", + "README.md" + ], + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "@opentelemetry/api": "^1.3.0", + "@opentelemetry/sdk-metrics": "^1.8.0", + "@opentelemetry/sdk-trace-base": "^1.8.0", + "@opentelemetry/sdk-trace-node": "^1.8.0", + "@types/mocha": "7.0.2", + "@types/node": "18.6.5", + "mocha": "7.2.0", + "nyc": "15.1.0", + "rimraf": "5.0.5", + "test-all-versions": "6.0.0", + "semver": "^7.6.0", + "superagent": "8.0.9", + "ts-mocha": "10.0.0", + "typescript": "4.4.4", + "undici": "^5.0.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.48.0" + }, + "homepage": "https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/node/opentelemetry-instrumentation-undici#readme", + "sideEffects": false +} diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/enums/SemanticAttributes.ts b/plugins/node/opentelemetry-instrumentation-undici/src/enums/SemanticAttributes.ts new file mode 100644 index 0000000000..0e65b51aeb --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-undici/src/enums/SemanticAttributes.ts @@ -0,0 +1,168 @@ +/* + * 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. + */ + +// DO NOT EDIT, this is an Auto-generated file from scripts/semconv/templates//templates/SemanticAttributes.ts.j2 +export const SemanticAttributes = { + /** + * State of the HTTP connection in the HTTP connection pool. + */ + HTTP_CONNECTION_STATE: 'http.connection.state', + + /** + * Describes a class of error the operation ended with. + * + * Note: The `error.type` SHOULD be predictable and SHOULD have low cardinality. +Instrumentations SHOULD document the list of errors they report. + +The cardinality of `error.type` within one instrumentation library SHOULD be low. +Telemetry consumers that aggregate data from multiple instrumentation libraries and applications +should be prepared for `error.type` to have high cardinality at query time when no +additional filters are applied. + +If the operation has completed successfully, instrumentations SHOULD NOT set `error.type`. + +If a specific domain defines its own set of error identifiers (such as HTTP or gRPC status codes), +it's RECOMMENDED to: + +* Use a domain-specific attribute +* Set `error.type` to capture all errors, regardless of whether they are defined within the domain-specific set or not. + */ + ERROR_TYPE: 'error.type', + + /** + * The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) header. For requests using transport encoding, this should be the compressed size. + */ + HTTP_REQUEST_BODY_SIZE: 'http.request.body.size', + + /** + * HTTP request method. + * + * Note: HTTP request method value SHOULD be "known" to the instrumentation. +By default, this convention defines "known" methods as the ones listed in [RFC9110](https://www.rfc-editor.org/rfc/rfc9110.html#name-methods) +and the PATCH method defined in [RFC5789](https://www.rfc-editor.org/rfc/rfc5789.html). + +If the HTTP request method is not known to instrumentation, it MUST set the `http.request.method` attribute to `_OTHER`. + +If the HTTP instrumentation could end up converting valid HTTP request methods to `_OTHER`, then it MUST provide a way to override +the list of known HTTP methods. If this override is done via environment variable, then the environment variable MUST be named +OTEL_INSTRUMENTATION_HTTP_KNOWN_METHODS and support a comma-separated list of case-sensitive known HTTP methods +(this list MUST be a full override of the default known method, it is not a list of known methods in addition to the defaults). + +HTTP method names are case-sensitive and `http.request.method` attribute value MUST match a known HTTP method name exactly. +Instrumentations for specific web frameworks that consider HTTP methods to be case insensitive, SHOULD populate a canonical equivalent. +Tracing instrumentations that do so, MUST also set `http.request.method_original` to the original value. + */ + HTTP_REQUEST_METHOD: 'http.request.method', + + /** + * Original HTTP method sent by the client in the request line. + */ + HTTP_REQUEST_METHOD_ORIGINAL: 'http.request.method_original', + + /** + * The ordinal number of request resending attempt (for any reason, including redirects). + * + * Note: The resend count SHOULD be updated each time an HTTP request gets resent by the client, regardless of what was the cause of the resending (e.g. redirection, authorization failure, 503 Server Unavailable, network issues, or any other). + */ + HTTP_REQUEST_RESEND_COUNT: 'http.request.resend_count', + + /** + * The size of the response payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) header. For requests using transport encoding, this should be the compressed size. + */ + HTTP_RESPONSE_BODY_SIZE: 'http.response.body.size', + + /** + * [HTTP response status code](https://tools.ietf.org/html/rfc7231#section-6). + */ + HTTP_RESPONSE_STATUS_CODE: 'http.response.status_code', + + /** + * The matched route, that is, the path template in the format used by the respective server framework. + * + * Note: MUST NOT be populated when this is not supported by the HTTP server framework as the route attribute should have low-cardinality and the URI path can NOT substitute it. +SHOULD include the [application root](/docs/http/http-spans.md#http-server-definitions) if there is one. + */ + HTTP_ROUTE: 'http.route', + + /** + * Peer address of the network connection - IP address or Unix domain socket name. + */ + NETWORK_PEER_ADDRESS: 'network.peer.address', + + /** + * Peer port number of the network connection. + */ + NETWORK_PEER_PORT: 'network.peer.port', + + /** + * [OSI application layer](https://osi-model.com/application-layer/) or non-OSI equivalent. + * + * Note: The value SHOULD be normalized to lowercase. + */ + NETWORK_PROTOCOL_NAME: 'network.protocol.name', + + /** + * Version of the protocol specified in `network.protocol.name`. + * + * Note: `network.protocol.version` refers to the version of the protocol used and might be different from the protocol client's version. If the HTTP client has a version of `0.27.2`, but sends HTTP version `1.1`, this attribute should be set to `1.1`. + */ + NETWORK_PROTOCOL_VERSION: 'network.protocol.version', + + /** + * Server domain name if available without reverse DNS lookup; otherwise, IP address or Unix domain socket name. + * + * Note: When observed from the client side, and when communicating through an intermediary, `server.address` SHOULD represent the server address behind any intermediaries, for example proxies, if it's available. + */ + SERVER_ADDRESS: 'server.address', + + /** + * Server port number. + * + * Note: When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD represent the server port behind any intermediaries, for example proxies, if it's available. + */ + SERVER_PORT: 'server.port', + + /** + * Absolute URL describing a network resource according to [RFC3986](https://www.rfc-editor.org/rfc/rfc3986). + * + * Note: For network calls, URL usually has `scheme://host[:port][path][?query][#fragment]` format, where the fragment is not transmitted over HTTP, but if it is known, it SHOULD be included nevertheless. +`url.full` MUST NOT contain credentials passed via URL in form of `https://username:password@www.example.com/`. In such case username and password SHOULD be redacted and attribute's value SHOULD be `https://REDACTED:REDACTED@www.example.com/`. +`url.full` SHOULD capture the absolute URL when it is available (or can be reconstructed) and SHOULD NOT be validated or modified except for sanitizing purposes. + */ + URL_FULL: 'url.full', + + /** + * The [URI path](https://www.rfc-editor.org/rfc/rfc3986#section-3.3) component. + */ + URL_PATH: 'url.path', + + /** + * The [URI query](https://www.rfc-editor.org/rfc/rfc3986#section-3.4) component. + * + * Note: Sensitive content provided in query string SHOULD be scrubbed when instrumentations can identify it. + */ + URL_QUERY: 'url.query', + + /** + * The [URI scheme](https://www.rfc-editor.org/rfc/rfc3986#section-3.1) component identifying the used protocol. + */ + URL_SCHEME: 'url.scheme', + + /** + * Value of the [HTTP User-Agent](https://www.rfc-editor.org/rfc/rfc9110.html#field.user-agent) header sent by the client. + */ + USER_AGENT_ORIGINAL: 'user_agent.original', +}; diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/index.ts b/plugins/node/opentelemetry-instrumentation-undici/src/index.ts new file mode 100644 index 0000000000..fda578e789 --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-undici/src/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './undici'; diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts b/plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts new file mode 100644 index 0000000000..e3a12c3d14 --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts @@ -0,0 +1,43 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import type { Channel } from 'diagnostics_channel'; + +import { UndiciRequest, UnidiciResponse } from './types'; + +export interface ListenerRecord { + name: string; + channel: Channel; + onMessage: (message: any, name: string) => void; +} + +export interface RequestMessage { + request: UndiciRequest; +} + +export interface RequestHeadersMessage { + request: UndiciRequest; + socket: any; +} + +export interface ResponseHeadersMessage { + request: UndiciRequest; + response: UnidiciResponse; +} + +export interface RequestErrorMessage { + request: UndiciRequest; + error: Error; +} diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/types.ts b/plugins/node/opentelemetry-instrumentation-undici/src/types.ts new file mode 100644 index 0000000000..26b8080129 --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-undici/src/types.ts @@ -0,0 +1,73 @@ +/* + * 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 type { InstrumentationConfig } from '@opentelemetry/instrumentation'; +import type { Attributes, Span } from '@opentelemetry/api'; + +// TODO: notes about support +// - `fetch` API is added in node v16.15.0 +// - `undici` supports node >=18 + +// TODO: `Request` class was added in node v16.15.0, make it work with v14 +// also we do not get that object from the diagnostics channel message but the +// core request from https://github.com/nodejs/undici/blob/main/lib/core/request.js +// which is not typed + +export interface UndiciRequest { + origin: string; + method: string; + path: string; + /** + * Serialized string of headers in the form `name: value\r\n` + */ + headers: string; + throwOnError: boolean; + completed: boolean; + aborted: boolean; + idempotent: boolean; + contentLength: number | null; + contentType: string | null; + body: any; +} + +export interface UnidiciResponse { + headers: Buffer[]; + statusCode: number; +} + +// This package will instrument HTTP requests made through `undici` or `fetch` global API +// so it seems logical to have similar options than the HTTP instrumentation +export interface UndiciInstrumentationConfig + extends InstrumentationConfig { + /** Not trace all outgoing requests that matched with custom function */ + ignoreRequestHook?: (request: RequestType) => boolean; + /** Function for adding custom attributes after response is handled */ + applyCustomAttributesOnSpan?: ( + span: Span, + request: RequestType, + response: Response + ) => void; + /** Function for adding custom attributes before request is handled */ + requestHook?: (span: Span, request: RequestType) => void; + /** Function for adding custom attributes before a span is started in outgoingRequest */ + startSpanHook?: (request: RequestType) => Attributes; + /** Require parent to create span for outgoing requests */ + requireParentforSpans?: boolean; + /** Map the following HTTP headers to span attributes. */ + headersToSpanAttributes?: { + requestHeaders?: string[]; + responseHeaders?: string[]; + }; +} diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts new file mode 100644 index 0000000000..bdca38b6bd --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts @@ -0,0 +1,449 @@ +/* + * 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 diagch from 'diagnostics_channel'; +import { URL } from 'url'; + +import { + InstrumentationBase, + safeExecuteInTheMiddle, +} from '@opentelemetry/instrumentation'; +import { + Attributes, + context, + diag, + Histogram, + HrTime, + INVALID_SPAN_CONTEXT, + propagation, + Span, + SpanKind, + SpanStatusCode, + trace, + ValueType, +} from '@opentelemetry/api'; + +import { VERSION } from './version'; + +import { + ListenerRecord, + RequestHeadersMessage, + RequestMessage, + ResponseHeadersMessage, +} from './internal-types'; +import { UndiciInstrumentationConfig, UndiciRequest } from './types'; +import { SemanticAttributes } from './enums/SemanticAttributes'; +import { + hrTime, + hrTimeDuration, + hrTimeToMilliseconds, +} from '@opentelemetry/core'; + +interface IntrumentationRecord { + span: Span; + attributes: Attributes; + startTime: HrTime; +} + +// A combination of https://github.com/elastic/apm-agent-nodejs and +// https://github.com/gadget-inc/opentelemetry-instrumentations/blob/main/packages/opentelemetry-instrumentation-undici/src/index.ts +export class UndiciInstrumentation extends InstrumentationBase { + // Keep ref to avoid https://github.com/nodejs/node/issues/42170 bug and for + // unsubscribing. + private _channelSubs!: Array; + private _recordFromReq = new WeakMap(); + + private _httpClientDurationHistogram!: Histogram; + constructor(config?: UndiciInstrumentationConfig) { + super('@opentelemetry/instrumentation-undici', VERSION, config); + // Force load fetch API (since it's lazy loaded in Node 18) + // `fetch` Added in: v17.5.0, v16.15.0 (with flag) and we suport lower verisons + // https://nodejs.org/api/globals.html#fetch + try { + fetch('').catch(() => {}); + } catch (err) { + // TODO: nicer message + diag.info('fetch API not available'); + } + + this.setConfig(config); + } + + // No need to instrument files/modules + protected override init() { + return undefined; + } + + override disable(): void { + if (!this._config.enabled) { + return; + } + + this._channelSubs.forEach(sub => sub.channel.unsubscribe(sub.onMessage)); + this._channelSubs.length = 0; + this._config.enabled = false; + } + + override enable(): void { + if (this._config.enabled) { + return; + } + this._config.enabled = true; + + // This method is called by the `InstrumentationAbstract` constructor before + // ours is called. So we need to ensure the property is initalized + this._channelSubs = this._channelSubs || []; + this.subscribeToChannel( + 'undici:request:create', + this.onRequestCreated.bind(this) + ); + this.subscribeToChannel( + 'undici:client:sendHeaders', + this.onRequestHeaders.bind(this) + ); + this.subscribeToChannel( + 'undici:request:headers', + this.onResponseHeaders.bind(this) + ); + this.subscribeToChannel('undici:request:trailers', this.onDone.bind(this)); + this.subscribeToChannel('undici:request:error', this.onError.bind(this)); + } + + override setConfig(config?: UndiciInstrumentationConfig): void { + super.setConfig(config); + + if (config?.enabled) { + this.enable(); + } else { + this.disable(); + } + } + + protected override _updateMetricInstruments() { + this._httpClientDurationHistogram = this.meter.createHistogram( + 'http.client.request.duration', + { + description: 'Measures the duration of outbound HTTP requests.', + unit: 'ms', + valueType: ValueType.DOUBLE, + } + ); + } + + private _getConfig(): UndiciInstrumentationConfig { + return this._config as UndiciInstrumentationConfig; + } + + private subscribeToChannel( + diagnosticChannel: string, + onMessage: ListenerRecord['onMessage'] + ) { + const channel = diagch.channel(diagnosticChannel); + channel.subscribe(onMessage); + this._channelSubs.push({ + name: diagnosticChannel, + channel, + onMessage, + }); + } + + // This is the 1st message we receive for each request (fired after request creation). Here we will + // create the span and populate some atttributes, then link the span to the request for further + // span processing + private onRequestCreated({ request }: RequestMessage): void { + // Ignore if: + // - instrumentation is disabled + // - ignored by config + // - method is 'CONNECT' + const config = this._getConfig(); + const shouldIgnoreReq = safeExecuteInTheMiddle( + () => + !config.enabled || + request.method === 'CONNECT' || + config.ignoreRequestHook?.(request), + e => e && this._diag.error('caught ignoreRequestHook error: ', e), + true + ); + + if (shouldIgnoreReq) { + return; + } + + const startTime = hrTime(); + const rawHeaders = request.headers.split('\r\n'); + const reqHeaders = new Map( + rawHeaders.map(h => { + const sepIndex = h.indexOf(':'); + const name = h.substring(0, sepIndex).toLowerCase(); + const val = h.substring(sepIndex + 1).trim(); + return [name, val]; + }) + ); + + const requestUrl = new URL(request.origin + request.path); + const urlScheme = requestUrl.protocol.replace(':', ''); + const attributes: Attributes = { + [SemanticAttributes.HTTP_REQUEST_METHOD]: request.method, + [SemanticAttributes.URL_FULL]: requestUrl.toString(), + [SemanticAttributes.URL_PATH]: requestUrl.pathname, + [SemanticAttributes.URL_QUERY]: requestUrl.search, + [SemanticAttributes.URL_SCHEME]: urlScheme, + }; + + const schemePorts: Record = { https: '443', http: '80' }; + const serverAddress = requestUrl.hostname; + const serverPort = requestUrl.port || schemePorts[urlScheme]; + + attributes[SemanticAttributes.SERVER_ADDRESS] = serverAddress; + if (serverPort && !isNaN(Number(serverPort))) { + attributes[SemanticAttributes.SERVER_PORT] = Number(serverPort); + } + + const userAgent = reqHeaders.get('user-agent'); + if (userAgent) { + attributes[SemanticAttributes.USER_AGENT_ORIGINAL] = userAgent; + } + + // Get attributes from the hook if present + const hookAttributes = safeExecuteInTheMiddle( + () => config.startSpanHook?.(request), + e => e && this._diag.error('caught startSpanHook error: ', e), + true + ); + if (hookAttributes) { + Object.entries(hookAttributes).forEach(([key, val]) => { + attributes[key] = val; + }); + } + + // Check if parent span is required via config and: + // - if a parent is required but not present, we use a `NoopSpan` to still + // propagate context without recording it. + // - create a span otherwise + const activeCtx = context.active(); + const currentSpan = trace.getSpan(activeCtx); + let span: Span; + + if (config.requireParentforSpans && !currentSpan) { + span = trace.wrapSpanContext(INVALID_SPAN_CONTEXT); + } else { + span = this.tracer.startSpan( + `HTTP ${request.method}`, + { + kind: SpanKind.CLIENT, + attributes: attributes, + }, + activeCtx + ); + } + + // Execute the request hook if defined + safeExecuteInTheMiddle( + () => config.requestHook?.(span, request), + e => e && this._diag.error('caught requestHook error: ', e), + true + ); + + // Context propagation goes last so no hook can tamper + // the propagation headers + const requestContext = trace.setSpan(context.active(), span); + const addedHeaders: Record = {}; + propagation.inject(requestContext, addedHeaders); + + request.headers += Object.entries(addedHeaders) + .map(([k, v]) => `${k}: ${v}\r\n`) + .join(''); + this._recordFromReq.set(request, { span, attributes, startTime }); + } + + // This is the 2nd message we recevie for each request. It is fired when connection with + // the remote is stablished and about to send the first byte. Here do have info about the + // remote addres an port so we can poupulate some `network.*` attributes into the span + private onRequestHeaders({ request, socket }: RequestHeadersMessage): void { + const record = this._recordFromReq.get(request as UndiciRequest); + + if (!record) { + return; + } + + const config = this._getConfig(); + const { span } = record; + const { remoteAddress, remotePort } = socket; + const spanAttributes: Attributes = { + [SemanticAttributes.NETWORK_PEER_ADDRESS]: remoteAddress, + [SemanticAttributes.NETWORK_PEER_PORT]: remotePort, + }; + + // After hooks have been processed (which may modify request headers) + // we can collect the headers based on the configuration + const rawHeaders = request.headers.split('\r\n'); + const reqHeaders = new Map( + rawHeaders.map(h => { + const sepIndex = h.indexOf(':'); + const name = h.substring(0, sepIndex).toLowerCase(); + const val = h.substring(sepIndex + 1).trim(); + return [name, val]; + }) + ); + + if (config.headersToSpanAttributes?.requestHeaders) { + config.headersToSpanAttributes.requestHeaders + .map(name => name.toLowerCase()) + .filter(name => reqHeaders.has(name)) + .forEach(name => { + spanAttributes[`http.request.header.${name}`] = reqHeaders.get(name); + }); + } + + span.setAttributes(spanAttributes); + } + + // This is the 3rd message we get for each request and it's fired when the server + // headers are received, body may not be accessible yet. + // From the response headers we can set the status and content length + private onResponseHeaders({ + request, + response, + }: ResponseHeadersMessage): void { + const record = this._recordFromReq.get(request); + + if (!record) { + return; + } + + const { span, attributes, startTime } = record; + // We are currently *not* capturing response headers, even though the + // intake API does allow it, because none of the other `setHttpContext` + // uses currently do + const spanAttributes: Attributes = { + [SemanticAttributes.HTTP_RESPONSE_STATUS_CODE]: response.statusCode, + }; + + // Get headers with names lowercased but values intact + const resHeaders = new Map(); + for (let idx = 0; idx < response.headers.length; idx = idx + 2) { + resHeaders.set( + response.headers[idx].toString().toLowerCase(), + response.headers[idx + 1].toString() + ); + } + + // Put response headers as attributes based on config + const config = this._getConfig(); + if (config.headersToSpanAttributes?.responseHeaders) { + config.headersToSpanAttributes.responseHeaders + .map(name => name.toLowerCase()) + .filter(name => resHeaders.has(name)) + .forEach(name => { + spanAttributes[`http.response.header.${name}`] = resHeaders.get(name); + }); + } + + // `content-length` header is a special case + if (resHeaders.has('content-length')) { + const contentLength = Number(resHeaders.get('content-length')); + if (!isNaN(contentLength)) { + spanAttributes['http.response.header.content-length'] = contentLength; + } + } + + span.setAttributes(spanAttributes); + span.setStatus({ + code: + response.statusCode >= 400 + ? SpanStatusCode.ERROR + : SpanStatusCode.UNSET, + }); + this._recordFromReq.set(request, { + span, + startTime, + attributes: Object.assign(attributes, spanAttributes), + }); + } + + // This is the last event we receive if the request went without any errors + private onDone({ request }: RequestMessage): void { + const record = this._recordFromReq.get(request); + + if (!record) { + return; + } + + const { span, attributes, startTime } = record; + // End the span + span.end(); + this._recordFromReq.delete(request); + + // Record metrics + this.recordRequestDuration(attributes, startTime); + } + + // This is the event we get when something is wrong in the request like + // - invalid options when calling `fetch` global API or any undici method for request + // - connectivity errors such as unreachable host + // - requests aborted through an `AbortController.signal` + // NOTE: server errors are considered valid responses and it's the lib consumer + // who should deal with that. + private onError({ request, error }: any): void { + const record = this._recordFromReq.get(request); + + if (!record) { + return; + } + + const { span, attributes, startTime } = record; + + // NOTE: in `undici@6.3.0` when request aborted the error type changes from + // a custom error (`RequestAbortedError`) to a built-in `DOMException` carrying + // some differences: + // - `code` is from DOMEXception (ABORT_ERR: 20) + // - `message` changes + // - stacktrace is smaller and contains node internal frames + span.recordException(error); + span.setStatus({ + code: SpanStatusCode.ERROR, + message: error.message, + }); + span.end(); + this._recordFromReq.delete(request); + + // Record metrics (with the error) + attributes[SemanticAttributes.ERROR_TYPE] = error.message; + this.recordRequestDuration(attributes, startTime); + } + + private recordRequestDuration(attributes: Attributes, startTime: HrTime) { + // Time to record metrics + const metricsAttributes: Attributes = {}; + // Get the attribs already in span attributes + const keysToCopy = [ + SemanticAttributes.HTTP_RESPONSE_STATUS_CODE, + SemanticAttributes.HTTP_REQUEST_METHOD, + SemanticAttributes.SERVER_ADDRESS, + SemanticAttributes.SERVER_PORT, + SemanticAttributes.URL_SCHEME, + SemanticAttributes.ERROR_TYPE, + ]; + keysToCopy.forEach(key => { + if (key in attributes) { + metricsAttributes[key] = attributes[key]; + } + }); + + // Take the duration and record it + const duration = hrTimeToMilliseconds(hrTimeDuration(startTime, hrTime())); + this._httpClientDurationHistogram.record(duration, metricsAttributes); + } +} diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts b/plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts new file mode 100644 index 0000000000..b78e00ea0e --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts @@ -0,0 +1,417 @@ +/* + * 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 { + SpanKind, + SpanStatusCode, + context, + propagation, + trace, +} from '@opentelemetry/api'; +import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; +import { + InMemorySpanExporter, + SimpleSpanProcessor, +} from '@opentelemetry/sdk-trace-base'; +import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; + +import { UndiciInstrumentation } from '../src/undici'; + +import { MockPropagation } from './utils/mock-propagation'; +import { MockServer } from './utils/mock-server'; +import { assertSpan } from './utils/assertSpan'; + +const instrumentation = new UndiciInstrumentation(); +instrumentation.enable(); +instrumentation.disable(); + +const protocol = 'http'; +const hostname = 'localhost'; +const mockServer = new MockServer(); +const memoryExporter = new InMemorySpanExporter(); +const provider = new NodeTracerProvider(); +provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); +instrumentation.setTracerProvider(provider); + +describe('UndiciInstrumentation `fetch` tests', function () { + before(function (done) { + // Do not test if the `fetch` global API is not available + // This applies to nodejs < v18 or nodejs < v16.15 wihtout the flag + // `--experimental-global-fetch` set + // https://nodejs.org/api/globals.html#fetch + if (typeof globalThis.fetch !== 'function') { + this.skip(); + } + + propagation.setGlobalPropagator(new MockPropagation()); + context.setGlobalContextManager(new AsyncHooksContextManager().enable()); + mockServer.start(done); + mockServer.mockListener((req, res) => { + // There are some situations where there is no way to access headers + // for trace propagation asserts like: + // const resp = await fetch('http://host:port') + // so we need to do the assertion here + try { + assert.ok( + req.headers[MockPropagation.TRACE_CONTEXT_KEY], + `trace propagation for ${MockPropagation.TRACE_CONTEXT_KEY} works` + ); + assert.ok( + req.headers[MockPropagation.SPAN_CONTEXT_KEY], + `trace propagation for ${MockPropagation.SPAN_CONTEXT_KEY} works` + ); + } catch (assertErr) { + // The exception will hang the server and the test so we set a header + // back to the test to make an assertion + res.setHeader('propagation-error', assertErr.message); + } + + // Retur a valid response always + res.statusCode = 200; + res.setHeader('content-type', 'application/json'); + res.setHeader('foo-server', 'bar'); + res.write(JSON.stringify({ success: true })); + res.end(); + }); + }); + + after(function (done) { + context.disable(); + propagation.disable(); + mockServer.mockListener(undefined); + mockServer.stop(done); + }); + + beforeEach(function () { + memoryExporter.reset(); + }); + + describe('disable()', function () { + it('should not create spans when disabled', async function () { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + + // Disable via config + instrumentation.setConfig({ enabled: false }); + + const fetchUrl = `${protocol}://${hostname}:${mockServer.port}/?query=test`; + const response = await fetch(fetchUrl); + assert.ok( + response.headers.get('propagation-error') != null, + 'propagation is not set if instrumentation disabled' + ); + + spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0, 'no spans are created'); + }); + }); + + describe('enable()', function () { + beforeEach(function () { + instrumentation.enable(); + }); + afterEach(function () { + // Empty configuration & disable + instrumentation.setConfig({ enabled: false }); + }); + + it('should create valid spans even if the configuration hooks fail', async function () { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + + // Set the bad configuration + instrumentation.setConfig({ + enabled: true, + ignoreRequestHook: () => { + throw new Error('ignoreRequestHook error'); + }, + applyCustomAttributesOnSpan: () => { + throw new Error('ignoreRequestHook error'); + }, + requestHook: () => { + throw new Error('requestHook error'); + }, + startSpanHook: () => { + throw new Error('startSpanHook error'); + }, + }); + + const fetchUrl = `${protocol}://${hostname}:${mockServer.port}/?query=test`; + const response = await fetch(fetchUrl); + assert.ok( + response.headers.get('propagation-error') == null, + 'propagation is set for instrumented requests' + ); + + spans = memoryExporter.getFinishedSpans(); + const span = spans[0]; + + assert.ok(span, 'a span is present'); + assert.strictEqual(spans.length, 1); + assertSpan(span, { + hostname: 'localhost', + httpStatusCode: response.status, + httpMethod: 'GET', + path: '/', + query: '?query=test', + resHeaders: response.headers, + }); + }); + + it('should create valid spans with empty configuration', async function () { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + + const fetchUrl = `${protocol}://${hostname}:${mockServer.port}/?query=test`; + const response = await fetch(fetchUrl); + assert.ok( + response.headers.get('propagation-error') == null, + 'propagation is set for instrumented requests' + ); + + spans = memoryExporter.getFinishedSpans(); + const span = spans[0]; + + assert.ok(span, 'a span is present'); + assert.strictEqual(spans.length, 1); + assertSpan(span, { + hostname: 'localhost', + httpStatusCode: response.status, + httpMethod: 'GET', + path: '/', + query: '?query=test', + resHeaders: response.headers, + }); + }); + + it('should create valid spans with the given configuration', async function () { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + + // Set configuration + instrumentation.setConfig({ + enabled: true, + ignoreRequestHook: req => { + return req.path.indexOf('/ignore/path') !== -1; + }, + requestHook: (span, req) => { + // TODO: maybe an intermediate request with better API + req.headers += 'x-requested-with: undici\r\n'; + }, + startSpanHook: request => { + return { + 'test.hook.attribute': 'hook-value', + }; + }, + headersToSpanAttributes: { + requestHeaders: ['foo-client', 'x-requested-with'], + responseHeaders: ['foo-server'], + }, + }); + + // Do some requests + const ignoreResponse = await fetch( + `${protocol}://${hostname}:${mockServer.port}/ignore/path` + ); + const reqInit = { + headers: new Headers({ + 'user-agent': 'custom', + 'foo-client': 'bar', + }), + }; + assert.ok( + ignoreResponse.headers.get('propagation-error'), + 'propagation is not set for ignored requests' + ); + + const queryResponse = await fetch( + `${protocol}://${hostname}:${mockServer.port}/?query=test`, + reqInit + ); + assert.ok( + queryResponse.headers.get('propagation-error') == null, + 'propagation is set for instrumented requests' + ); + + spans = memoryExporter.getFinishedSpans(); + const span = spans[0]; + assert.ok(span, 'a span is present'); + assert.strictEqual(spans.length, 1); + assertSpan(span, { + hostname: 'localhost', + httpStatusCode: queryResponse.status, + httpMethod: 'GET', + path: '/', + query: '?query=test', + reqHeaders: reqInit.headers, + resHeaders: queryResponse.headers, + }); + assert.strictEqual( + span.attributes['http.request.header.foo-client'], + 'bar', + 'request headers from fetch options are captured' + ); + assert.strictEqual( + span.attributes['http.request.header.x-requested-with'], + 'undici', + 'request headers from requestHook are captured' + ); + assert.strictEqual( + span.attributes['http.response.header.foo-server'], + 'bar', + 'response headers from the server are captured' + ); + assert.strictEqual( + span.attributes['test.hook.attribute'], + 'hook-value', + 'startSpanHook is called' + ); + }); + + it('should not create spans without parent if required in configuration', async function () { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + + instrumentation.setConfig({ + enabled: true, + requireParentforSpans: true, + }); + + const fetchUrl = `${protocol}://${hostname}:${mockServer.port}/?query=test`; + const response = await fetch(fetchUrl); + // TODO: here we're checking the propagation works even if the instrumentation + // is not starting any span. Not 100% sure this is the behaviour we want + assert.ok( + response.headers.get('propagation-error') == null, + 'propagation is set for instrumented requests' + ); + + spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0, 'no spans are created'); + }); + + it('should not create spans with parent if required in configuration', function (done) { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + + instrumentation.setConfig({ + enabled: true, + requireParentforSpans: true, + }); + + const tracer = provider.getTracer('default'); + const span = tracer.startSpan('parentSpan', { + kind: SpanKind.INTERNAL, + }); + + context.with(trace.setSpan(context.active(), span), async () => { + const fetchUrl = `${protocol}://${hostname}:${mockServer.port}/?query=test`; + const response = await fetch(fetchUrl); + + span.end(); + // TODO: here we're checking the propagation works even if the instrumentation + // is not starting any span. Not 100% sure this is the behaviour we want + assert.ok( + response.headers.get('propagation-error') == null, + 'propagation is set for instrumented requests' + ); + + spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 2, 'child span is created'); + assert.strictEqual( + spans.filter(span => span.kind === SpanKind.CLIENT).length, + 1, + 'child span is created' + ); + assert.strictEqual( + spans.filter(span => span.kind === SpanKind.INTERNAL).length, + 1, + 'parent span is present' + ); + + done(); + }); + }); + + it('should capture errors using fetch API', async function () { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + + let fetchError; + try { + const fetchUrl = 'http://unexistent-host-name/path'; + await fetch(fetchUrl); + } catch (err) { + // Expected error + fetchError = err; + } + + spans = memoryExporter.getFinishedSpans(); + const span = spans[0]; + assert.ok(span, 'a span is present'); + assert.strictEqual(spans.length, 1); + assertSpan(span, { + hostname: 'unexistent-host-name', + httpMethod: 'GET', + path: '/path', + error: fetchError, + noNetPeer: true, // do not check network attribs + forceStatus: { + code: SpanStatusCode.ERROR, + message: 'getaddrinfo ENOTFOUND unexistent-host-name', + }, + }); + }); + + it('should capture error if fetch request is aborted', async function () { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + + let fetchError; + const controller = new AbortController(); + const fetchUrl = `${protocol}://${hostname}:${mockServer.port}/?query=test`; + const fetchPromise = fetch(fetchUrl, { signal: controller.signal }); + controller.abort(); + try { + await fetchPromise; + } catch (err) { + // Expected error + fetchError = err; + } + + // Let the error be published to diagnostics channel + await new Promise(r => setTimeout(r, 5)); + + spans = memoryExporter.getFinishedSpans(); + const span = spans[0]; + assert.ok(span, 'a span is present'); + assert.strictEqual(spans.length, 1); + assertSpan(span, { + hostname: 'localhost', + httpMethod: 'GET', + path: '/', + query: '?query=test', + error: fetchError, + noNetPeer: true, // do not check network attribs + forceStatus: { + code: SpanStatusCode.ERROR, + message: 'The operation was aborted.', + }, + }); + }); + }); +}); diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/metrics.test.ts b/plugins/node/opentelemetry-instrumentation-undici/test/metrics.test.ts new file mode 100644 index 0000000000..97a4989ff7 --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-undici/test/metrics.test.ts @@ -0,0 +1,196 @@ +/* + * 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 { context, propagation } from '@opentelemetry/api'; +import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; +import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; +import { + AggregationTemporality, + DataPointType, + InMemoryMetricExporter, + MeterProvider, +} from '@opentelemetry/sdk-metrics'; + +import { UndiciInstrumentation } from '../src/undici'; + +import { MockServer } from './utils/mock-server'; +import { MockMetricsReader } from './utils/mock-metrics-reader'; +import { SemanticAttributes } from '../src/enums/SemanticAttributes'; + +const instrumentation = new UndiciInstrumentation(); +instrumentation.enable(); +instrumentation.disable(); + +const protocol = 'http'; +const hostname = 'localhost'; +const mockServer = new MockServer(); +const provider = new NodeTracerProvider(); +const meterProvider = new MeterProvider(); +// const memoryExporter = new InMemorySpanExporter(); +const metricsMemoryExporter = new InMemoryMetricExporter( + AggregationTemporality.DELTA +); +const metricReader = new MockMetricsReader(metricsMemoryExporter); +meterProvider.addMetricReader(metricReader); +// provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); +instrumentation.setTracerProvider(provider); +instrumentation.setMeterProvider(meterProvider); + +describe('UndiciInstrumentation metrics tests', function () { + before(function (done) { + // Do not test if the `fetch` global API is not available + // This applies to nodejs < v18 or nodejs < v16.15 wihtout the flag + // `--experimental-global-fetch` set + // https://nodejs.org/api/globals.html#fetch + if (typeof globalThis.fetch !== 'function') { + this.skip(); + } + + context.setGlobalContextManager(new AsyncHooksContextManager().enable()); + mockServer.start(done); + mockServer.mockListener((req, res) => { + // Return a valid response always + res.statusCode = 200; + res.setHeader('content-type', 'application/json'); + res.write(JSON.stringify({ success: true })); + res.end(); + }); + + // enable instrumentation for all tests + instrumentation.enable(); + }); + + after(function (done) { + instrumentation.disable(); + context.disable(); + propagation.disable(); + mockServer.mockListener(undefined); + mockServer.stop(done); + }); + + beforeEach(function () { + metricsMemoryExporter.reset(); + }); + + describe('with fetch API', function () { + before(function (done) { + // Do not test if the `fetch` global API is not available + // This applies to nodejs < v18 or nodejs < v16.15 wihtout the flag + // `--experimental-global-fetch` set + // https://nodejs.org/api/globals.html#fetch + if (typeof globalThis.fetch !== 'function') { + this.skip(); + } + + done(); + }); + + it('should report "http.client.request.duration" metric', async () => { + const fetchUrl = `${protocol}://${hostname}:${mockServer.port}/?query=test`; + await fetch(fetchUrl); + + await metricReader.collectAndExport(); + const resourceMetrics = metricsMemoryExporter.getMetrics(); + const scopeMetrics = resourceMetrics[0].scopeMetrics; + const metrics = scopeMetrics[0].metrics; + + assert.strictEqual(scopeMetrics.length, 1, 'scopeMetrics count'); + assert.strictEqual(metrics.length, 1, 'metrics count'); + assert.strictEqual( + metrics[0].descriptor.name, + 'http.client.request.duration' + ); + assert.strictEqual( + metrics[0].descriptor.description, + 'Measures the duration of outbound HTTP requests.' + ); + assert.strictEqual(metrics[0].descriptor.unit, 'ms'); + assert.strictEqual(metrics[0].dataPointType, DataPointType.HISTOGRAM); + assert.strictEqual(metrics[0].dataPoints.length, 1); + + const metricAttributes = metrics[0].dataPoints[0].attributes; + assert.strictEqual( + metricAttributes[SemanticAttributes.URL_SCHEME], + 'http' + ); + assert.strictEqual( + metricAttributes[SemanticAttributes.HTTP_REQUEST_METHOD], + 'GET' + ); + assert.strictEqual( + metricAttributes[SemanticAttributes.SERVER_ADDRESS], + 'localhost' + ); + assert.strictEqual( + metricAttributes[SemanticAttributes.SERVER_PORT], + mockServer.port + ); + assert.strictEqual( + metricAttributes[SemanticAttributes.HTTP_RESPONSE_STATUS_CODE], + 200 + ); + }); + + it('should have error.type in "http.client.request.duration" metric', async () => { + const fetchUrl = 'http://unknownhost/'; + + try { + await fetch(fetchUrl); + } catch (err) { + // Expected error, do nothing + } + + await metricReader.collectAndExport(); + const resourceMetrics = metricsMemoryExporter.getMetrics(); + const scopeMetrics = resourceMetrics[0].scopeMetrics; + const metrics = scopeMetrics[0].metrics; + + assert.strictEqual(scopeMetrics.length, 1, 'scopeMetrics count'); + assert.strictEqual(metrics.length, 1, 'metrics count'); + assert.strictEqual( + metrics[0].descriptor.name, + 'http.client.request.duration' + ); + assert.strictEqual( + metrics[0].descriptor.description, + 'Measures the duration of outbound HTTP requests.' + ); + assert.strictEqual(metrics[0].descriptor.unit, 'ms'); + assert.strictEqual(metrics[0].dataPointType, DataPointType.HISTOGRAM); + assert.strictEqual(metrics[0].dataPoints.length, 1); + + const metricAttributes = metrics[0].dataPoints[0].attributes; + assert.strictEqual( + metricAttributes[SemanticAttributes.URL_SCHEME], + 'http' + ); + assert.strictEqual( + metricAttributes[SemanticAttributes.HTTP_REQUEST_METHOD], + 'GET' + ); + assert.strictEqual( + metricAttributes[SemanticAttributes.SERVER_ADDRESS], + 'unknownhost' + ); + assert.strictEqual(metricAttributes[SemanticAttributes.SERVER_PORT], 80); + assert.ok( + metricAttributes[SemanticAttributes.ERROR_TYPE], + `the metric contains "${SemanticAttributes.ERROR_TYPE}" attribute if request failed` + ); + }); + }); +}); diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts new file mode 100644 index 0000000000..0ca416dd9b --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts @@ -0,0 +1,640 @@ +/* + * 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 { Writable } from 'stream'; + +import { + SpanKind, + SpanStatusCode, + context, + propagation, + trace, +} from '@opentelemetry/api'; +import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; +import { + InMemorySpanExporter, + SimpleSpanProcessor, +} from '@opentelemetry/sdk-trace-base'; +import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; + +import { UndiciInstrumentation } from '../src/undici'; + +import { MockPropagation } from './utils/mock-propagation'; +import { MockServer } from './utils/mock-server'; +import { assertSpan } from './utils/assertSpan'; + +const instrumentation = new UndiciInstrumentation(); +instrumentation.enable(); +instrumentation.disable(); + +import { satisfies } from 'semver'; +import type { Dispatcher } from 'undici'; +import * as undici from 'undici'; + +const protocol = 'http'; +const hostname = 'localhost'; +const mockServer = new MockServer(); +const memoryExporter = new InMemorySpanExporter(); +const provider = new NodeTracerProvider(); +provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); +instrumentation.setTracerProvider(provider); + +// Undici docs (https://github.com/nodejs/undici#garbage-collection) suggest +// that an undici response body should always be consumed. +async function consumeResponseBody(body: Dispatcher.ResponseData['body']) { + return new Promise(resolve => { + const devNull = new Writable({ + write(_chunk, _encoding, cb) { + setImmediate(cb); + }, + }); + body.pipe(devNull); + body.on('end', resolve); + }); +} + +describe('UndiciInstrumentation `undici` tests', function () { + before(function (done) { + // Undici >6.0.0 requires node v18 + // https://github.com/nodejs/undici/blob/e218fc61eda46da8784e0cedcaa88cd7e84dee99/package.json#L138 + if (!satisfies(process.version, '>=18.0')) { + this.skip(); + } + + propagation.setGlobalPropagator(new MockPropagation()); + context.setGlobalContextManager(new AsyncHooksContextManager().enable()); + mockServer.start(done); + mockServer.mockListener((req, res) => { + // There are some situations where there is no way to access headers + // for trace propagation asserts like: + // const resp = await fetch('http://host:port') + // so we need to do the assertion here + try { + assert.ok( + req.headers[MockPropagation.TRACE_CONTEXT_KEY], + `trace propagation for ${MockPropagation.TRACE_CONTEXT_KEY} works` + ); + assert.ok( + req.headers[MockPropagation.SPAN_CONTEXT_KEY], + `trace propagation for ${MockPropagation.SPAN_CONTEXT_KEY} works` + ); + } catch (assertErr) { + // The exception will hang the server and the test so we set a header + // back to the test to make an assertion + res.setHeader('propagation-error', assertErr.message); + } + + // Retur a valid response always + res.statusCode = 200; + res.setHeader('content-type', 'application/json'); + res.setHeader('foo-server', 'bar'); + res.write(JSON.stringify({ success: true })); + res.end(); + }); + }); + + after(function (done) { + context.disable(); + propagation.disable(); + mockServer.mockListener(undefined); + mockServer.stop(done); + }); + + beforeEach(function () { + memoryExporter.reset(); + }); + + describe('disable()', function () { + it('should not create spans when disabled', async function () { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + + // Disable via config + instrumentation.setConfig({ enabled: false }); + + const requestUrl = `${protocol}://${hostname}:${mockServer.port}/?query=test`; + const { headers, body } = await undici.request(requestUrl); + await consumeResponseBody(body); + + assert.ok( + headers['propagation-error'] != null, + 'propagation is not set if instrumentation disabled' + ); + + spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0, 'no spans are created'); + }); + }); + + describe('enable()', function () { + beforeEach(function () { + instrumentation.enable(); + // Set configuration + instrumentation.setConfig({ + enabled: true, + ignoreRequestHook: req => { + return req.path.indexOf('/ignore/path') !== -1; + }, + requestHook: (span, req) => { + // TODO: maybe an intermediate request with better API + req.headers += 'x-requested-with: undici\r\n'; + }, + startSpanHook: request => { + return { + 'test.hook.attribute': 'hook-value', + }; + }, + headersToSpanAttributes: { + requestHeaders: ['foo-client', 'x-requested-with'], + responseHeaders: ['foo-server'], + }, + }); + }); + afterEach(function () { + // Empty configuration & disable + instrumentation.setConfig({ enabled: false }); + }); + + it('should ingore requests based on the result of ignoreRequestHook', async function () { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + + // Do some requests + const headers = { + 'user-agent': 'custom', + 'foo-client': 'bar', + }; + + const ignoreRequestUrl = `${protocol}://${hostname}:${mockServer.port}/ignore/path`; + const ignoreResponse = await undici.request(ignoreRequestUrl, { + headers, + }); + await consumeResponseBody(ignoreResponse.body); + + assert.ok( + ignoreResponse.headers['propagation-error'], + 'propagation is not set for ignored requests' + ); + + spans = memoryExporter.getFinishedSpans(); + assert.ok(spans.length === 0, 'ignoreRequestHook is filtering requests'); + }); + + it('should create valid spans for "request" method', async function () { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + + // Do some requests + const headers = { + 'user-agent': 'custom', + 'foo-client': 'bar', + }; + + const ignoreRequestUrl = `${protocol}://${hostname}:${mockServer.port}/ignore/path`; + const ignoreResponse = await undici.request(ignoreRequestUrl, { + headers, + }); + await consumeResponseBody(ignoreResponse.body); + + assert.ok( + ignoreResponse.headers['propagation-error'], + 'propagation is not set for ignored requests' + ); + + const queryRequestUrl = `${protocol}://${hostname}:${mockServer.port}/?query=test`; + const queryResponse = await undici.request(queryRequestUrl, { headers }); + await consumeResponseBody(queryResponse.body); + + assert.ok( + queryResponse.headers['propagation-error'] == null, + 'propagation is set for instrumented requests' + ); + + spans = memoryExporter.getFinishedSpans(); + const span = spans[0]; + assert.ok(span, 'a span is present'); + assert.strictEqual(spans.length, 1); + assertSpan(span, { + hostname: 'localhost', + httpStatusCode: queryResponse.statusCode, + httpMethod: 'GET', + path: '/', + query: '?query=test', + reqHeaders: headers, + resHeaders: queryResponse.headers, + }); + assert.strictEqual( + span.attributes['http.request.header.foo-client'], + 'bar', + 'request headers from fetch options are captured' + ); + assert.strictEqual( + span.attributes['http.request.header.x-requested-with'], + 'undici', + 'request headers from requestHook are captured' + ); + assert.strictEqual( + span.attributes['http.response.header.foo-server'], + 'bar', + 'response headers from the server are captured' + ); + assert.strictEqual( + span.attributes['test.hook.attribute'], + 'hook-value', + 'startSpanHook is called' + ); + }); + + it('should create valid spans for "fetch" method', async function () { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + + // Do some requests + const headers = { + 'user-agent': 'custom', + 'foo-client': 'bar', + }; + const queryRequestUrl = `${protocol}://${hostname}:${mockServer.port}/?query=test`; + const queryResponse = await undici.fetch(queryRequestUrl, { headers }); + await queryResponse.text(); + + assert.ok( + queryResponse.headers.get('propagation-error') == null, + 'propagation is set for instrumented requests' + ); + + spans = memoryExporter.getFinishedSpans(); + const span = spans[0]; + assert.ok(span, 'a span is present'); + assert.strictEqual(spans.length, 1); + assertSpan(span, { + hostname: 'localhost', + httpStatusCode: queryResponse.status, + httpMethod: 'GET', + path: '/', + query: '?query=test', + reqHeaders: headers, + resHeaders: queryResponse.headers as unknown as Headers, + }); + assert.strictEqual( + span.attributes['http.request.header.foo-client'], + 'bar', + 'request headers from fetch options are captured' + ); + assert.strictEqual( + span.attributes['http.request.header.x-requested-with'], + 'undici', + 'request headers from requestHook are captured' + ); + assert.strictEqual( + span.attributes['http.response.header.foo-server'], + 'bar', + 'response headers from the server are captured' + ); + assert.strictEqual( + span.attributes['test.hook.attribute'], + 'hook-value', + 'startSpanHook is called' + ); + }); + + it('should create valid spans for "stream" method', async function () { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + + // Do some requests + const headers = { + 'user-agent': 'custom', + 'foo-client': 'bar', + }; + // https://undici.nodejs.org/#/docs/api/Dispatcher?id=example-1-basic-get-stream-request + const queryRequestUrl = `${protocol}://${hostname}:${mockServer.port}/?query=test`; + const queryResponse: Record = {}; + const bufs: any[] = []; + await undici.stream( + queryRequestUrl, + { opaque: { bufs }, headers } as any, + ({ statusCode, headers, opaque }) => { + queryResponse.statusCode = statusCode; + queryResponse.headers = headers; + return new Writable({ + write(chunk, encoding, callback) { + (opaque as any).bufs.push(chunk); + callback(); + }, + }); + } + ); + + assert.ok( + queryResponse.headers['propagation-error'] == null, + 'propagation is set for instrumented requests' + ); + + spans = memoryExporter.getFinishedSpans(); + const span = spans[0]; + assert.ok(span, 'a span is present'); + assert.strictEqual(spans.length, 1); + assertSpan(span, { + hostname: 'localhost', + httpStatusCode: queryResponse.statusCode, + httpMethod: 'GET', + path: '/', + query: '?query=test', + reqHeaders: headers, + resHeaders: queryResponse.headers as unknown as Headers, + }); + assert.strictEqual( + span.attributes['http.request.header.foo-client'], + 'bar', + 'request headers from fetch options are captured' + ); + assert.strictEqual( + span.attributes['http.request.header.x-requested-with'], + 'undici', + 'request headers from requestHook are captured' + ); + assert.strictEqual( + span.attributes['http.response.header.foo-server'], + 'bar', + 'response headers from the server are captured' + ); + assert.strictEqual( + span.attributes['test.hook.attribute'], + 'hook-value', + 'startSpanHook is called' + ); + }); + + it('should create valid spans for "dispatch" method', async function () { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + + // Do some requests + const headers = { + 'user-agent': 'custom', + 'foo-client': 'bar', + }; + + const queryRequestUrl = `${protocol}://${hostname}:${mockServer.port}`; + const queryResponse: Record = {}; + const client = new undici.Client(queryRequestUrl); + await new Promise((resolve, reject) => { + client.dispatch( + { + path: '/?query=test', + method: 'GET', + headers, + }, + { + onHeaders: (statusCode, headers) => { + queryResponse.statusCode = statusCode; + queryResponse.headers = headers; + return true; // unidici types require to return boolean + }, + onError: reject, + onComplete: resolve, + // Although the types say these following handlers are optional they must + // be defined to avoid a TypeError + onConnect: () => undefined, + onData: () => true, + } + ); + }); + + assert.ok( + queryResponse.headers['propagation-error'] == null, + 'propagation is set for instrumented requests' + ); + + spans = memoryExporter.getFinishedSpans(); + const span = spans[0]; + assert.ok(span, 'a span is present'); + assert.strictEqual(spans.length, 1); + assertSpan(span, { + hostname: 'localhost', + httpStatusCode: queryResponse.statusCode, + httpMethod: 'GET', + path: '/', + query: '?query=test', + reqHeaders: headers, + resHeaders: queryResponse.headers as unknown as Headers, + }); + assert.strictEqual( + span.attributes['http.request.header.foo-client'], + 'bar', + 'request headers from fetch options are captured' + ); + assert.strictEqual( + span.attributes['http.request.header.x-requested-with'], + 'undici', + 'request headers from requestHook are captured' + ); + assert.strictEqual( + span.attributes['http.response.header.foo-server'], + 'bar', + 'response headers from the server are captured' + ); + assert.strictEqual( + span.attributes['test.hook.attribute'], + 'hook-value', + 'startSpanHook is called' + ); + }); + + it('should create valid spans even if the configuration hooks fail', async function () { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + + // Set the bad configuration + instrumentation.setConfig({ + enabled: true, + ignoreRequestHook: () => { + throw new Error('ignoreRequestHook error'); + }, + applyCustomAttributesOnSpan: () => { + throw new Error('ignoreRequestHook error'); + }, + requestHook: () => { + throw new Error('requestHook error'); + }, + startSpanHook: () => { + throw new Error('startSpanHook error'); + }, + }); + + const requestUrl = `${protocol}://${hostname}:${mockServer.port}/?query=test`; + const { headers, statusCode, body } = await undici.request(requestUrl); + await consumeResponseBody(body); + + assert.ok( + headers['propagation-error'] == null, + 'propagation is set for instrumented requests' + ); + + spans = memoryExporter.getFinishedSpans(); + const span = spans[0]; + + assert.ok(span, 'a span is present'); + assert.strictEqual(spans.length, 1); + assertSpan(span, { + hostname: 'localhost', + httpStatusCode: statusCode, + httpMethod: 'GET', + path: '/', + query: '?query=test', + resHeaders: headers, + }); + }); + + it('should not create spans without parent if required in configuration', async function () { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + + instrumentation.setConfig({ + enabled: true, + requireParentforSpans: true, + }); + + const requestUrl = `${protocol}://${hostname}:${mockServer.port}/?query=test`; + const response = await undici.request(requestUrl); + await consumeResponseBody(response.body); + + // TODO: here we're checking the propagation works even if the instrumentation + // is not starting any span. Not 100% sure this is the behaviour we want + assert.ok( + response.headers['propagation-error'] == null, + 'propagation is set for instrumented requests' + ); + + spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0, 'no spans are created'); + }); + + it('should create spans with parent if required in configuration', function (done) { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + + instrumentation.setConfig({ + enabled: true, + requireParentforSpans: true, + }); + + const tracer = provider.getTracer('default'); + const span = tracer.startSpan('parentSpan', { + kind: SpanKind.INTERNAL, + }); + + context.with(trace.setSpan(context.active(), span), async () => { + const requestUrl = `${protocol}://${hostname}:${mockServer.port}/?query=test`; + const response = await undici.request(requestUrl); + await consumeResponseBody(response.body); + + span.end(); + // TODO: here we're checking the propagation works even if the instrumentation + // is not starting any span. Not 100% sure this is the behaviour we want + assert.ok( + response.headers['propagation-error'] == null, + 'propagation is set for instrumented requests' + ); + + spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 2, 'child span is created'); + assert.strictEqual( + spans.filter(span => span.kind === SpanKind.CLIENT).length, + 1, + 'child span is created' + ); + assert.strictEqual( + spans.filter(span => span.kind === SpanKind.INTERNAL).length, + 1, + 'parent span is present' + ); + + done(); + }); + }); + + it('should capture errors while doing request', async function () { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + + let fetchError; + try { + const requestUrl = 'http://unexistent-host-name/path'; + await undici.request(requestUrl); + } catch (err) { + // Expected error + fetchError = err; + } + + spans = memoryExporter.getFinishedSpans(); + const span = spans[0]; + assert.ok(span, 'a span is present'); + assert.strictEqual(spans.length, 1); + assertSpan(span, { + hostname: 'unexistent-host-name', + httpMethod: 'GET', + path: '/path', + error: fetchError, + noNetPeer: true, // do not check network attribs + forceStatus: { + code: SpanStatusCode.ERROR, + message: 'getaddrinfo ENOTFOUND unexistent-host-name', + }, + }); + }); + + it('should capture error if undici request is aborted', async function () { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + + let requestError; + const controller = new AbortController(); + const requestUrl = `${protocol}://${hostname}:${mockServer.port}/?query=test`; + const requestPromise = undici.request(requestUrl, { + signal: controller.signal, + }); + controller.abort(); + try { + await requestPromise; + } catch (err) { + // Expected error + requestError = err; + } + + // Let the error be published to diagnostics channel + await new Promise(r => setTimeout(r, 5)); + + spans = memoryExporter.getFinishedSpans(); + const span = spans[0]; + assert.ok(span, 'a span is present'); + assert.strictEqual(spans.length, 1); + assertSpan(span, { + hostname: 'localhost', + httpMethod: 'GET', + path: '/', + query: '?query=test', + error: requestError, + noNetPeer: true, // do not check network attribs + forceStatus: { + code: SpanStatusCode.ERROR, + message: requestError.message, + }, + }); + }); + }); +}); diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/utils/assertSpan.ts b/plugins/node/opentelemetry-instrumentation-undici/test/utils/assertSpan.ts new file mode 100644 index 0000000000..6e432618e7 --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-undici/test/utils/assertSpan.ts @@ -0,0 +1,196 @@ +/* + * 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 { + SpanKind, + SpanStatus, + Exception, + SpanStatusCode, +} from '@opentelemetry/api'; +import { hrTimeToNanoseconds } from '@opentelemetry/core'; +import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; +import * as assert from 'assert'; +import { SemanticAttributes } from '../../src/enums/SemanticAttributes'; + +type IncomingHttpHeaders = Record; + +export const assertSpan = ( + span: ReadableSpan, + validations: { + httpStatusCode?: number; + httpMethod: string; + resHeaders?: Headers | IncomingHttpHeaders; + hostname: string; + reqHeaders?: Headers | IncomingHttpHeaders; + path?: string | null; + query?: string | null; + forceStatus?: SpanStatus; + noNetPeer?: boolean; // we don't expect net peer info when request throw before being sent + error?: Exception; + } +) => { + assert.strictEqual(span.spanContext().traceId.length, 32); + assert.strictEqual(span.spanContext().spanId.length, 16); + assert.strictEqual(span.kind, SpanKind.CLIENT, 'span.kind is correct'); + assert.strictEqual( + span.name, + `HTTP ${validations.httpMethod}`, + 'span.name is correct' + ); + // TODO: check this + // assert.strictEqual( + // span.attributes[AttributeNames.HTTP_ERROR_MESSAGE], + // span.status.message, + // `attributes['${AttributeNames.HTTP_ERROR_MESSAGE}'] is correct`, + // ); + assert.strictEqual( + span.attributes[SemanticAttributes.HTTP_REQUEST_METHOD], + validations.httpMethod, + `attributes['${SemanticAttributes.HTTP_REQUEST_METHOD}'] is correct` + ); + + if (validations.path) { + assert.strictEqual( + span.attributes[SemanticAttributes.URL_PATH], + validations.path, + `attributes['${SemanticAttributes.URL_PATH}'] is correct` + ); + } + + if (validations.query) { + assert.strictEqual( + span.attributes[SemanticAttributes.URL_QUERY], + validations.query, + `attributes['${SemanticAttributes.URL_QUERY}'] is correct` + ); + } + + assert.strictEqual( + span.attributes[SemanticAttributes.HTTP_RESPONSE_STATUS_CODE], + validations.httpStatusCode, + `attributes['${SemanticAttributes.HTTP_RESPONSE_STATUS_CODE}'] is correct ${ + span.attributes[SemanticAttributes.HTTP_RESPONSE_STATUS_CODE] + }` + ); + + assert.strictEqual(span.links.length, 0, 'there are no links'); + + if (validations.error) { + assert.strictEqual(span.events.length, 1, 'span contains one error event'); + assert.strictEqual( + span.events[0].name, + 'exception', + 'error event name is correct' + ); + + const eventAttributes = span.events[0].attributes; + assert.ok(eventAttributes != null, 'event has attributes'); + assert.deepStrictEqual( + Object.keys(eventAttributes), + ['exception.type', 'exception.message', 'exception.stacktrace'], + 'the event attribute names are correct' + ); + } else { + assert.strictEqual(span.events.length, 0, 'span contains no events'); + } + + // Error message changes between version se we will + // only assert its presence + if (validations.forceStatus) { + assert.equal( + span.status.code, + validations.forceStatus.code, + 'span `status.code` is correct' + ); + assert.ok( + span.status.message, + 'span `status.message` is present' + ); + } else { + const { httpStatusCode } = validations; + const isStatusUnset = + httpStatusCode && httpStatusCode >= 100 && httpStatusCode < 400; + assert.equal( + span.status.code, + isStatusUnset ? SpanStatusCode.UNSET : SpanStatusCode.ERROR, + 'span `status.code` is correct' + ); + } + + assert.ok(span.endTime, 'must be finished'); + assert.ok( + hrTimeToNanoseconds(span.duration) > 0, + 'must have positive duration' + ); + + if (validations.resHeaders) { + // Headers were added in v17.5.0, v16.15.0 + // https://nodejs.org/api/globals.html#class-headers + const { resHeaders } = validations + const headersSupporetd = typeof Headers !== 'undefined'; + let contentLengthHeader; + + if (headersSupporetd && resHeaders instanceof Headers) { + contentLengthHeader = resHeaders.get('content-length'); + } else if (!(resHeaders instanceof Headers)) { + contentLengthHeader = resHeaders['content-length']; + } + + console.log('contentLengthHeader', contentLengthHeader); + if (contentLengthHeader) { + const contentLength = Number(contentLengthHeader); + + assert.strictEqual( + span.attributes['http.response.header.content-length'], + contentLength + ); + } + } + + assert.strictEqual( + span.attributes[SemanticAttributes.SERVER_ADDRESS], + validations.hostname, + 'must be consistent (SERVER_ADDRESS and hostname)' + ); + if (!validations.noNetPeer) { + assert.ok( + span.attributes[SemanticAttributes.NETWORK_PEER_ADDRESS], + `must have ${SemanticAttributes.NETWORK_PEER_ADDRESS}` + ); + assert.ok( + span.attributes[SemanticAttributes.NETWORK_PEER_PORT], + `must have ${SemanticAttributes.NETWORK_PEER_PORT}` + ); + } + assert.ok( + (span.attributes[SemanticAttributes.URL_FULL] as string).indexOf( + span.attributes[SemanticAttributes.SERVER_ADDRESS] as string + ) > -1, + `${SemanticAttributes.URL_FULL} & ${SemanticAttributes.SERVER_ADDRESS} must be consistent` + ); + + if (validations.reqHeaders) { + const userAgent = + validations.reqHeaders instanceof Headers + ? validations.reqHeaders.get('user-agent') + : validations.reqHeaders['user-agent']; + if (userAgent) { + assert.strictEqual( + span.attributes[SemanticAttributes.USER_AGENT_ORIGINAL], + userAgent + ); + } + } +}; diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-metrics-reader.ts b/plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-metrics-reader.ts new file mode 100644 index 0000000000..c3ffbf0ae6 --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-metrics-reader.ts @@ -0,0 +1,47 @@ +/* + * 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 { MetricReader, PushMetricExporter } from '@opentelemetry/sdk-metrics'; + +export class MockMetricsReader extends MetricReader { + constructor(private _exporter: PushMetricExporter) { + super({ + aggregationTemporalitySelector: + _exporter.selectAggregationTemporality?.bind(_exporter), + }); + } + + protected onForceFlush(): Promise { + return Promise.resolve(undefined); + } + + protected onShutdown(): Promise { + return Promise.resolve(undefined); + } + + public async collectAndExport(): Promise { + const result = await this.collect(); + await new Promise((resolve, reject) => { + this._exporter.export(result.resourceMetrics, result => { + if (result.error != null) { + reject(result.error); + } else { + resolve(); + } + }); + }); + } +} diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-propagation.ts b/plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-propagation.ts new file mode 100644 index 0000000000..5c49e661d6 --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-propagation.ts @@ -0,0 +1,52 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + Context, + TextMapPropagator, + trace, + TraceFlags, +} from '@opentelemetry/api'; + +export class MockPropagation implements TextMapPropagator { + static TRACE_CONTEXT_KEY = 'x-mock-trace-id'; + static SPAN_CONTEXT_KEY = 'x-mock-span-id'; + extract(context: Context, carrier: Record) { + const extractedSpanContext = { + traceId: carrier[MockPropagation.TRACE_CONTEXT_KEY] as string, + spanId: carrier[MockPropagation.SPAN_CONTEXT_KEY] as string, + traceFlags: TraceFlags.SAMPLED, + isRemote: true, + }; + if (extractedSpanContext.traceId && extractedSpanContext.spanId) { + return trace.setSpanContext(context, extractedSpanContext); + } + return context; + } + inject(context: Context, carrier: Record): void { + const spanContext = trace.getSpanContext(context); + + if (spanContext) { + carrier[MockPropagation.TRACE_CONTEXT_KEY] = spanContext.traceId; + carrier[MockPropagation.SPAN_CONTEXT_KEY] = spanContext.spanId; + } + } + fields(): string[] { + return [ + MockPropagation.TRACE_CONTEXT_KEY, + MockPropagation.SPAN_CONTEXT_KEY, + ]; + } +} diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-server.ts b/plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-server.ts new file mode 100644 index 0000000000..5fa464df03 --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-server.ts @@ -0,0 +1,73 @@ +/* + * 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 http from 'http'; + +export class MockServer { + private _port: number | undefined; + private _httpServer: http.Server | undefined; + private _reqListener: http.RequestListener | undefined; + + get port(): number { + return this._port || 0; + } + + mockListener(handler: http.RequestListener | undefined): void { + this._reqListener = handler; + } + + start(cb: (err?: Error) => void) { + this._httpServer = http.createServer((req, res) => { + // Use the mock listener if defined + if (typeof this._reqListener === 'function') { + return this._reqListener(req, res); + } + + // If no mock function is provided fallback to a basic response + res.statusCode = 200; + res.setHeader('content-type', 'application/json'); + res.write(JSON.stringify({ success: true })); + res.end(); + }); + + this._httpServer.listen(0, () => { + const addr = this._httpServer!.address(); + if (addr == null) { + cb(new Error('unexpected addr null')); + return; + } + + if (typeof addr === 'string') { + cb(new Error(`unexpected addr ${addr}`)); + return; + } + + if (addr.port <= 0) { + cb(new Error('Could not get port')); + return; + } + this._port = addr.port; + cb(); + }); + } + + stop(cb: (err?: Error) => void) { + if (this._httpServer) { + this._reqListener = undefined; + this._httpServer.close(); + cb(); + } + } +} diff --git a/plugins/node/opentelemetry-instrumentation-undici/tsconfig.json b/plugins/node/opentelemetry-instrumentation-undici/tsconfig.json new file mode 100644 index 0000000000..810157c218 --- /dev/null +++ b/plugins/node/opentelemetry-instrumentation-undici/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../../tsconfig.base", + "compilerOptions": { + "rootDir": ".", + "outDir": "build" + }, + "include": [ + "src/**/*.ts", + "test/**/*.ts" + ] +} \ No newline at end of file From d946809d24f34ae9c81357de1b27b22928c2c5c2 Mon Sep 17 00:00:00 2001 From: David Luna Date: Thu, 22 Feb 2024 14:02:40 +0100 Subject: [PATCH 02/81] chore(instrumentation-undici): fix compilation isues --- .../test/fetch.test.ts | 6 +++--- .../test/undici.test.ts | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts b/plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts index b78e00ea0e..d1fa37d3be 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts @@ -77,7 +77,7 @@ describe('UndiciInstrumentation `fetch` tests', function () { } catch (assertErr) { // The exception will hang the server and the test so we set a header // back to the test to make an assertion - res.setHeader('propagation-error', assertErr.message); + res.setHeader('propagation-error', (assertErr as Error).message); } // Retur a valid response always @@ -357,7 +357,7 @@ describe('UndiciInstrumentation `fetch` tests', function () { await fetch(fetchUrl); } catch (err) { // Expected error - fetchError = err; + fetchError = err as Error; } spans = memoryExporter.getFinishedSpans(); @@ -390,7 +390,7 @@ describe('UndiciInstrumentation `fetch` tests', function () { await fetchPromise; } catch (err) { // Expected error - fetchError = err; + fetchError = err as Error; } // Let the error be published to diagnostics channel diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts index 0ca416dd9b..99d2278de4 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts @@ -94,7 +94,7 @@ describe('UndiciInstrumentation `undici` tests', function () { } catch (assertErr) { // The exception will hang the server and the test so we set a header // back to the test to make an assertion - res.setHeader('propagation-error', assertErr.message); + res.setHeader('propagation-error', (assertErr as Error).message); } // Retur a valid response always @@ -578,7 +578,7 @@ describe('UndiciInstrumentation `undici` tests', function () { await undici.request(requestUrl); } catch (err) { // Expected error - fetchError = err; + fetchError = err as Error; } spans = memoryExporter.getFinishedSpans(); @@ -613,7 +613,7 @@ describe('UndiciInstrumentation `undici` tests', function () { await requestPromise; } catch (err) { // Expected error - requestError = err; + requestError = err as Error; } // Let the error be published to diagnostics channel @@ -632,7 +632,7 @@ describe('UndiciInstrumentation `undici` tests', function () { noNetPeer: true, // do not check network attribs forceStatus: { code: SpanStatusCode.ERROR, - message: requestError.message, + message: requestError?.message, }, }); }); From 51cade6f5009c1e8903770ee2f318dd5035af8de Mon Sep 17 00:00:00 2001 From: David Luna Date: Thu, 22 Feb 2024 15:38:59 +0100 Subject: [PATCH 03/81] chore(instrumentation-undici): fix tests --- .../test/undici.test.ts | 20 +++++++++++-- .../test/utils/assertSpan.ts | 28 ++++++++++--------- .../test/utils/mock-server.ts | 5 ++-- 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts index 99d2278de4..11a01a043c 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts @@ -70,9 +70,9 @@ describe('UndiciInstrumentation `undici` tests', function () { before(function (done) { // Undici >6.0.0 requires node v18 // https://github.com/nodejs/undici/blob/e218fc61eda46da8784e0cedcaa88cd7e84dee99/package.json#L138 - if (!satisfies(process.version, '>=18.0')) { - this.skip(); - } + // if (!satisfies(process.version, '>=18.0')) { + // this.skip(); + // } propagation.setGlobalPropagator(new MockPropagation()); context.setGlobalContextManager(new AsyncHooksContextManager().enable()); @@ -259,6 +259,13 @@ describe('UndiciInstrumentation `undici` tests', function () { }); it('should create valid spans for "fetch" method', async function () { + // Fetch method is available from node v16.5 + // we want to skip this test for lowe versions + // https://github.com/nodejs/undici/blob/08839e450aa6dd1b0e2c019d6e5869cd5b966be1/index.js#L95 + if (typeof undici.fetch === 'undefined') { + this.skip(); + } + let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); @@ -599,6 +606,13 @@ describe('UndiciInstrumentation `undici` tests', function () { }); it('should capture error if undici request is aborted', async function () { + // AbortController was added in: v15.0.0, v14.17.0 + // but we still run tests for node v14 + // https://nodejs.org/api/globals.html#class-abortcontroller + if (typeof AbortController === 'undefined') { + this.skip(); + } + let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/utils/assertSpan.ts b/plugins/node/opentelemetry-instrumentation-undici/test/utils/assertSpan.ts index 6e432618e7..b931b0875d 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/utils/assertSpan.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/utils/assertSpan.ts @@ -139,16 +139,8 @@ export const assertSpan = ( // Headers were added in v17.5.0, v16.15.0 // https://nodejs.org/api/globals.html#class-headers const { resHeaders } = validations - const headersSupporetd = typeof Headers !== 'undefined'; - let contentLengthHeader; + const contentLengthHeader = getHeader(resHeaders, 'content-length'); - if (headersSupporetd && resHeaders instanceof Headers) { - contentLengthHeader = resHeaders.get('content-length'); - } else if (!(resHeaders instanceof Headers)) { - contentLengthHeader = resHeaders['content-length']; - } - - console.log('contentLengthHeader', contentLengthHeader); if (contentLengthHeader) { const contentLength = Number(contentLengthHeader); @@ -182,10 +174,10 @@ export const assertSpan = ( ); if (validations.reqHeaders) { - const userAgent = - validations.reqHeaders instanceof Headers - ? validations.reqHeaders.get('user-agent') - : validations.reqHeaders['user-agent']; + const userAgent = getHeader(validations.reqHeaders, 'content-length'); + // validations.reqHeaders instanceof Headers + // ? validations.reqHeaders.get('user-agent') + // : validations.reqHeaders['user-agent']; if (userAgent) { assert.strictEqual( span.attributes[SemanticAttributes.USER_AGENT_ORIGINAL], @@ -194,3 +186,13 @@ export const assertSpan = ( } } }; + +/** + * Gets a header by name regardless of the type + */ +function getHeader(headers: Headers | IncomingHttpHeaders, name: string): string | string[] | null | undefined { + if (typeof headers.get === 'function') { + return headers.get(name); + } + return (headers as IncomingHttpHeaders)[name]; +} diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-server.ts b/plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-server.ts index 5fa464df03..8a160c8117 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-server.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-server.ts @@ -65,9 +65,10 @@ export class MockServer { stop(cb: (err?: Error) => void) { if (this._httpServer) { - this._reqListener = undefined; this._httpServer.close(); - cb(); + this._httpServer = undefined; + this._reqListener = undefined; } + cb(); } } From 92a5308cf49295ee03fde1a09cd40364a3c83d7b Mon Sep 17 00:00:00 2001 From: David Luna Date: Thu, 22 Feb 2024 16:52:10 +0100 Subject: [PATCH 04/81] chore(instrumentation-undici): sync package-lock.json --- package-lock.json | 291 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 285 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2ae8e72eea..b760a1e494 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4373,6 +4373,15 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, + "node_modules/@fastify/busboy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz", + "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==", + "dev": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@fastify/deepmerge": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-1.3.0.tgz", @@ -8088,6 +8097,10 @@ "resolved": "plugins/node/instrumentation-tedious", "link": true }, + "node_modules/@opentelemetry/instrumentation-undici": { + "resolved": "plugins/node/opentelemetry-instrumentation-undici", + "link": true + }, "node_modules/@opentelemetry/instrumentation-user-interaction": { "resolved": "plugins/web/opentelemetry-instrumentation-user-interaction", "link": true @@ -11212,6 +11225,12 @@ "node": ">=0.10.0" } }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, "node_modules/ascli": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz", @@ -13357,6 +13376,12 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true + }, "node_modules/cookies": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.9.1.tgz", @@ -13970,6 +13995,16 @@ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, "node_modules/di": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", @@ -17256,6 +17291,15 @@ "node": ">= 0.6.0" } }, + "node_modules/hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/highlight.js": { "version": "10.7.3", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", @@ -29626,6 +29670,42 @@ "node": ">=4" } }, + "node_modules/superagent": { + "version": "8.0.9", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.9.tgz", + "integrity": "sha512-4C7Bh5pyHTvU33KpZgwrNKh/VQnvgtCSqPRfJAUdmrtSYePVzVg4E4OzsrbkhJj9O7SO6Bnv75K/F8XVZT8YHA==", + "dev": true, + "dependencies": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^2.1.2", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.11.0", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=6.4.0 <13 || >=14" + } + }, + "node_modules/superagent/node_modules/formidable": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "dev": true, + "dependencies": { + "dezalgo": "^1.0.4", + "hexoid": "^1.0.0", + "once": "^1.4.0", + "qs": "^6.11.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, "node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -31221,6 +31301,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici": { + "version": "5.28.3", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.3.tgz", + "integrity": "sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA==", + "dev": true, + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -34717,7 +34809,7 @@ }, "plugins/node/opentelemetry-instrumentation-bunyan/examples": { "name": "bunyan-example", - "version": "0.45.1", + "version": "0.1.0", "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.3.0", @@ -34922,7 +35014,7 @@ }, "plugins/node/opentelemetry-instrumentation-express/examples": { "name": "express-example", - "version": "0.34.1", + "version": "0.1.0", "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.3.0", @@ -35517,7 +35609,7 @@ }, "plugins/node/opentelemetry-instrumentation-koa/examples": { "name": "koa-example", - "version": "0.23.0", + "version": "0.1.0", "license": "Apache-2.0", "dependencies": { "@koa/router": "^12.0.0", @@ -35824,7 +35916,7 @@ }, "plugins/node/opentelemetry-instrumentation-mongodb/examples": { "name": "mongodb-example", - "version": "0.28.0", + "version": "0.1.0", "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.0.0", @@ -36121,7 +36213,7 @@ }, "plugins/node/opentelemetry-instrumentation-mysql/examples": { "name": "mysql-example", - "version": "0.23.0", + "version": "0.1.0", "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.0.0", @@ -36733,7 +36825,7 @@ }, "plugins/node/opentelemetry-instrumentation-redis/examples": { "name": "redis-example", - "version": "0.23.0", + "version": "0.1.0", "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.0.0", @@ -36934,6 +37026,70 @@ "@opentelemetry/api": "^1.3.0" } }, + "plugins/node/opentelemetry-instrumentation-undici": { + "version": "0.33.0", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.48.0" + }, + "devDependencies": { + "@opentelemetry/api": "^1.3.0", + "@opentelemetry/sdk-metrics": "^1.8.0", + "@opentelemetry/sdk-trace-base": "^1.8.0", + "@opentelemetry/sdk-trace-node": "^1.8.0", + "@types/mocha": "7.0.2", + "@types/node": "18.6.5", + "mocha": "7.2.0", + "nyc": "15.1.0", + "rimraf": "5.0.5", + "semver": "^7.6.0", + "superagent": "8.0.9", + "test-all-versions": "6.0.0", + "ts-mocha": "10.0.0", + "typescript": "4.4.4", + "undici": "^5.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "plugins/node/opentelemetry-instrumentation-undici/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "plugins/node/opentelemetry-instrumentation-undici/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "plugins/node/opentelemetry-instrumentation-undici/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "plugins/node/opentelemetry-instrumentation-winston": { "name": "@opentelemetry/instrumentation-winston", "version": "0.34.0", @@ -40891,6 +41047,12 @@ } } }, + "@fastify/busboy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz", + "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==", + "dev": true + }, "@fastify/deepmerge": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-1.3.0.tgz", @@ -45668,6 +45830,54 @@ } } }, + "@opentelemetry/instrumentation-undici": { + "version": "file:plugins/node/opentelemetry-instrumentation-undici", + "requires": { + "@opentelemetry/api": "^1.3.0", + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.48.0", + "@opentelemetry/sdk-metrics": "^1.8.0", + "@opentelemetry/sdk-trace-base": "^1.8.0", + "@opentelemetry/sdk-trace-node": "^1.8.0", + "@types/mocha": "7.0.2", + "@types/node": "18.6.5", + "mocha": "7.2.0", + "nyc": "15.1.0", + "rimraf": "5.0.5", + "semver": "^7.6.0", + "superagent": "8.0.9", + "test-all-versions": "6.0.0", + "ts-mocha": "10.0.0", + "typescript": "4.4.4", + "undici": "^5.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, "@opentelemetry/instrumentation-user-interaction": { "version": "file:plugins/web/opentelemetry-instrumentation-user-interaction", "requires": { @@ -49074,6 +49284,12 @@ "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", "dev": true }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, "ascli": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz", @@ -50784,6 +51000,12 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true + }, "cookies": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.9.1.tgz", @@ -51263,6 +51485,16 @@ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true }, + "dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, "di": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", @@ -53915,6 +54147,12 @@ } } }, + "hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "dev": true + }, "highlight.js": { "version": "10.7.3", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", @@ -64030,6 +64268,38 @@ "through": "^2.3.4" } }, + "superagent": { + "version": "8.0.9", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.9.tgz", + "integrity": "sha512-4C7Bh5pyHTvU33KpZgwrNKh/VQnvgtCSqPRfJAUdmrtSYePVzVg4E4OzsrbkhJj9O7SO6Bnv75K/F8XVZT8YHA==", + "dev": true, + "requires": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^2.1.2", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.11.0", + "semver": "^7.3.8" + }, + "dependencies": { + "formidable": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "dev": true, + "requires": { + "dezalgo": "^1.0.4", + "hexoid": "^1.0.0", + "once": "^1.4.0", + "qs": "^6.11.0" + } + } + } + }, "supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -65231,6 +65501,15 @@ "which-boxed-primitive": "^1.0.2" } }, + "undici": { + "version": "5.28.3", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.3.tgz", + "integrity": "sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA==", + "dev": true, + "requires": { + "@fastify/busboy": "^2.0.0" + } + }, "undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", From 83b280ef74b9ba803386cf42235deaf74260deb4 Mon Sep 17 00:00:00 2001 From: David Luna Date: Thu, 22 Feb 2024 16:54:41 +0100 Subject: [PATCH 05/81] chore(instrumentation-undici): fix lint errors --- .../test/utils/assertSpan.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/utils/assertSpan.ts b/plugins/node/opentelemetry-instrumentation-undici/test/utils/assertSpan.ts index b931b0875d..1c06975839 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/utils/assertSpan.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/utils/assertSpan.ts @@ -114,10 +114,7 @@ export const assertSpan = ( validations.forceStatus.code, 'span `status.code` is correct' ); - assert.ok( - span.status.message, - 'span `status.message` is present' - ); + assert.ok(span.status.message, 'span `status.message` is present'); } else { const { httpStatusCode } = validations; const isStatusUnset = @@ -138,7 +135,7 @@ export const assertSpan = ( if (validations.resHeaders) { // Headers were added in v17.5.0, v16.15.0 // https://nodejs.org/api/globals.html#class-headers - const { resHeaders } = validations + const { resHeaders } = validations; const contentLengthHeader = getHeader(resHeaders, 'content-length'); if (contentLengthHeader) { @@ -175,9 +172,9 @@ export const assertSpan = ( if (validations.reqHeaders) { const userAgent = getHeader(validations.reqHeaders, 'content-length'); - // validations.reqHeaders instanceof Headers - // ? validations.reqHeaders.get('user-agent') - // : validations.reqHeaders['user-agent']; + // validations.reqHeaders instanceof Headers + // ? validations.reqHeaders.get('user-agent') + // : validations.reqHeaders['user-agent']; if (userAgent) { assert.strictEqual( span.attributes[SemanticAttributes.USER_AGENT_ORIGINAL], @@ -190,7 +187,10 @@ export const assertSpan = ( /** * Gets a header by name regardless of the type */ -function getHeader(headers: Headers | IncomingHttpHeaders, name: string): string | string[] | null | undefined { +function getHeader( + headers: Headers | IncomingHttpHeaders, + name: string +): string | string[] | null | undefined { if (typeof headers.get === 'function') { return headers.get(name); } From 22d49832879bcc352f1e7897965fe7dfa6c9ff01 Mon Sep 17 00:00:00 2001 From: David Luna Date: Thu, 22 Feb 2024 17:02:40 +0100 Subject: [PATCH 06/81] chore(instrumentation-undici): remove unused import --- .../test/undici.test.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts index 11a01a043c..a32797f40b 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts @@ -40,7 +40,6 @@ const instrumentation = new UndiciInstrumentation(); instrumentation.enable(); instrumentation.disable(); -import { satisfies } from 'semver'; import type { Dispatcher } from 'undici'; import * as undici from 'undici'; @@ -68,12 +67,6 @@ async function consumeResponseBody(body: Dispatcher.ResponseData['body']) { describe('UndiciInstrumentation `undici` tests', function () { before(function (done) { - // Undici >6.0.0 requires node v18 - // https://github.com/nodejs/undici/blob/e218fc61eda46da8784e0cedcaa88cd7e84dee99/package.json#L138 - // if (!satisfies(process.version, '>=18.0')) { - // this.skip(); - // } - propagation.setGlobalPropagator(new MockPropagation()); context.setGlobalContextManager(new AsyncHooksContextManager().enable()); mockServer.start(done); From 3c3b05561e8098124ba25f740b953a6a63ffd751 Mon Sep 17 00:00:00 2001 From: David Luna Date: Thu, 22 Feb 2024 17:39:43 +0100 Subject: [PATCH 07/81] chore(instrumentation-undici): update README --- .../README.md | 20 ++++++---- .../src/types.ts | 38 ++++++++++--------- .../test/undici.test.ts | 2 +- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/README.md b/plugins/node/opentelemetry-instrumentation-undici/README.md index f507fc1157..610b68c4de 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/README.md +++ b/plugins/node/opentelemetry-instrumentation-undici/README.md @@ -40,17 +40,23 @@ registerInstrumentations({ ``` -See [examples/http](https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/fetch) for a short example. +See [examples/http](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/examples/fetch) for a short example. ### Undici/Fetch instrumentation Options - - Undici instrumentation has few options available to choose from. You can set the following: | Options | Type | Description | | ------- | ---- | ----------- | -| [`onRequest`](https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-instrumentation-undici/src/types.ts#19) | `UndiciRequestHook` | Function for adding custom attributes before request is handled | +| [`ignoreRequestHook`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#61) | `IgnoreRequestFunction` | Undici instrumentation will not trace all incoming requests that matched with custom function. | +| [`applyCustomAttributesOnSpan`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#63) | `CustomAttributesFunction` | Function for adding custom attributes before response is handled. | +| [`requestHook`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#65) | `RequestHookFunction` | Function for adding custom attributes before request is handled. | +| [`startSpanHook`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#67) | `StartSpanHookFunction` | Function for adding custom attributes before a span is started. | +| [`requireParentforSpans`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#69) | `Boolean` | Require a parent span is present to create new span for outgoing requests. | +| [`headersToSpanAttributes`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#71) | `Object` | List of case insensitive HTTP headers to convert to span attributes. Headers will be converted to span attributes in the form of `http.{request\|response}.header.header-name` where the name is only lowecased, e.g. `http.response.header.content-length`| + + + ## Useful links @@ -63,7 +69,7 @@ Undici instrumentation has few options available to choose from. You can set the Apache 2.0 - See [LICENSE][license-url] for more information. [discussions-url]: https://github.com/open-telemetry/opentelemetry-js/discussions -[license-url]: https://github.com/open-telemetry/opentelemetry-js/blob/main/LICENSE +[license-url]: https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/LICENSE [license-image]: https://img.shields.io/badge/license-Apache_2.0-green.svg?style=flat -[npm-url]: https://www.npmjs.com/package/@opentelemetry/instrumentation-http -[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Finstrumentation-http.svg +[npm-url]: https://www.npmjs.com/package/@opentelemetry/instrumentation-router +[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Finstrumentation-router.svg diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/types.ts b/plugins/node/opentelemetry-instrumentation-undici/src/types.ts index 26b8080129..d45b18aa8d 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/types.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/types.ts @@ -16,15 +16,6 @@ import type { InstrumentationConfig } from '@opentelemetry/instrumentation'; import type { Attributes, Span } from '@opentelemetry/api'; -// TODO: notes about support -// - `fetch` API is added in node v16.15.0 -// - `undici` supports node >=18 - -// TODO: `Request` class was added in node v16.15.0, make it work with v14 -// also we do not get that object from the diagnostics channel message but the -// core request from https://github.com/nodejs/undici/blob/main/lib/core/request.js -// which is not typed - export interface UndiciRequest { origin: string; method: string; @@ -47,22 +38,33 @@ export interface UnidiciResponse { statusCode: number; } +export interface IgnoreRequestFunction { + (request: T): boolean; +} +export interface CustomAttributesFunction { + (span: Span, request: T, response: Response): void; +} + +export interface RequestHookFunction { + (span: Span, request: T): void; +} + +export interface StartSpanHookFunction { + (request: T): Attributes; +} + // This package will instrument HTTP requests made through `undici` or `fetch` global API // so it seems logical to have similar options than the HTTP instrumentation export interface UndiciInstrumentationConfig extends InstrumentationConfig { /** Not trace all outgoing requests that matched with custom function */ - ignoreRequestHook?: (request: RequestType) => boolean; + ignoreRequestHook?: IgnoreRequestFunction; /** Function for adding custom attributes after response is handled */ - applyCustomAttributesOnSpan?: ( - span: Span, - request: RequestType, - response: Response - ) => void; + applyCustomAttributesOnSpan?: CustomAttributesFunction; /** Function for adding custom attributes before request is handled */ - requestHook?: (span: Span, request: RequestType) => void; - /** Function for adding custom attributes before a span is started in outgoingRequest */ - startSpanHook?: (request: RequestType) => Attributes; + requestHook?: RequestHookFunction; + /** Function for adding custom attributes before a span is started */ + startSpanHook?: StartSpanHookFunction; /** Require parent to create span for outgoing requests */ requireParentforSpans?: boolean; /** Map the following HTTP headers to span attributes. */ diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts index a32797f40b..49ef5ad689 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts @@ -466,7 +466,7 @@ describe('UndiciInstrumentation `undici` tests', function () { throw new Error('ignoreRequestHook error'); }, applyCustomAttributesOnSpan: () => { - throw new Error('ignoreRequestHook error'); + throw new Error('applyCustomAttributesOnSpan error'); }, requestHook: () => { throw new Error('requestHook error'); From bcf1cf5200832a41ca595c699cc445040a74d45a Mon Sep 17 00:00:00 2001 From: David Luna Date: Fri, 23 Feb 2024 15:12:13 +0100 Subject: [PATCH 08/81] chore(instrumentation-undici): fix README lint issues --- .github/component_owners.yml | 2 ++ plugins/node/opentelemetry-instrumentation-undici/README.md | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/component_owners.yml b/.github/component_owners.yml index 4ee43d769d..bbb29e8d38 100644 --- a/.github/component_owners.yml +++ b/.github/component_owners.yml @@ -112,6 +112,8 @@ components: - rauno56 plugins/node/opentelemetry-instrumentation-router: - rauno56 + plugins/node/opentelemetry-instrumentation-undici: + - david-luna plugins/node/opentelemetry-instrumentation-winston: - seemk plugins/web/opentelemetry-instrumentation-document-load: diff --git a/plugins/node/opentelemetry-instrumentation-undici/README.md b/plugins/node/opentelemetry-instrumentation-undici/README.md index 610b68c4de..399f7640ba 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/README.md +++ b/plugins/node/opentelemetry-instrumentation-undici/README.md @@ -55,9 +55,6 @@ Undici instrumentation has few options available to choose from. You can set the | [`requireParentforSpans`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#69) | `Boolean` | Require a parent span is present to create new span for outgoing requests. | | [`headersToSpanAttributes`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#71) | `Object` | List of case insensitive HTTP headers to convert to span attributes. Headers will be converted to span attributes in the form of `http.{request\|response}.header.header-name` where the name is only lowecased, e.g. `http.response.header.content-length`| - - - ## Useful links - For more information on OpenTelemetry, visit: From f29ba40905c61783329dd3187b6cf16ef67c8a5c Mon Sep 17 00:00:00 2001 From: David Luna Date: Fri, 23 Feb 2024 15:25:58 +0100 Subject: [PATCH 09/81] chore(instrumentation-undici): update release please config --- .release-please-manifest.json | 2 +- release-please-config.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e197b006db..f6a70e6066 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1 +1 @@ -{"detectors/node/opentelemetry-resource-detector-alibaba-cloud":"0.28.6","detectors/node/opentelemetry-resource-detector-aws":"1.3.6","detectors/node/opentelemetry-resource-detector-azure":"0.2.4","detectors/node/opentelemetry-resource-detector-container":"0.3.6","detectors/node/opentelemetry-resource-detector-gcp":"0.29.6","detectors/node/opentelemetry-resource-detector-github":"0.28.1","detectors/node/opentelemetry-resource-detector-instana":"0.6.0","metapackages/auto-instrumentations-node":"0.41.1","metapackages/auto-instrumentations-web":"0.36.0","packages/opentelemetry-host-metrics":"0.35.0","packages/opentelemetry-id-generator-aws-xray":"1.2.1","packages/opentelemetry-propagation-utils":"0.30.6","packages/opentelemetry-redis-common":"0.36.1","packages/opentelemetry-sampler-aws-xray":"0.34.0","packages/opentelemetry-sql-common":"0.40.0","packages/opentelemetry-test-utils":"0.36.0","plugins/node/instrumentation-amqplib":"0.34.0","plugins/node/instrumentation-cucumber":"0.3.0","plugins/node/instrumentation-dataloader":"0.6.0","plugins/node/instrumentation-fs":"0.9.0","plugins/node/instrumentation-lru-memoizer":"0.34.0","plugins/node/instrumentation-mongoose":"0.35.0","plugins/node/instrumentation-perf-hooks": "0.1.0","plugins/node/instrumentation-socket.io":"0.36.0","plugins/node/instrumentation-tedious":"0.7.0","plugins/node/opentelemetry-instrumentation-aws-lambda":"0.38.0","plugins/node/opentelemetry-instrumentation-aws-sdk":"0.38.1","plugins/node/opentelemetry-instrumentation-bunyan":"0.35.0","plugins/node/opentelemetry-instrumentation-bunyan/examples":"0.1.0","plugins/node/opentelemetry-instrumentation-cassandra":"0.35.0","plugins/node/opentelemetry-instrumentation-connect":"0.33.0","plugins/node/opentelemetry-instrumentation-dns":"0.33.0","plugins/node/opentelemetry-instrumentation-express":"0.35.0","plugins/node/opentelemetry-instrumentation-express/examples":"0.1.0","plugins/node/opentelemetry-instrumentation-fastify":"0.33.0","plugins/node/opentelemetry-instrumentation-generic-pool":"0.33.0","plugins/node/opentelemetry-instrumentation-graphql":"0.37.0","plugins/node/opentelemetry-instrumentation-hapi":"0.34.0","plugins/node/opentelemetry-instrumentation-ioredis":"0.37.0","plugins/node/opentelemetry-instrumentation-knex":"0.33.0","plugins/node/opentelemetry-instrumentation-koa":"0.37.0","plugins/node/opentelemetry-instrumentation-koa/examples":"0.1.0","plugins/node/opentelemetry-instrumentation-memcached":"0.33.0","plugins/node/opentelemetry-instrumentation-mongodb":"0.39.0","plugins/node/opentelemetry-instrumentation-mongodb/examples":"0.1.0","plugins/node/opentelemetry-instrumentation-mysql":"0.35.0","plugins/node/opentelemetry-instrumentation-mysql/examples":"0.1.0","plugins/node/opentelemetry-instrumentation-mysql2":"0.35.0","plugins/node/opentelemetry-instrumentation-nestjs-core":"0.34.0","plugins/node/opentelemetry-instrumentation-net":"0.33.0","plugins/node/opentelemetry-instrumentation-pg":"0.38.0","plugins/node/opentelemetry-instrumentation-pino":"0.35.0","plugins/node/opentelemetry-instrumentation-redis":"0.36.0","plugins/node/opentelemetry-instrumentation-redis/examples":"0.1.0","plugins/node/opentelemetry-instrumentation-redis-4":"0.36.0","plugins/node/opentelemetry-instrumentation-restify":"0.35.0","plugins/node/opentelemetry-instrumentation-router":"0.34.0","plugins/node/opentelemetry-instrumentation-winston":"0.34.0","plugins/web/opentelemetry-instrumentation-document-load":"0.35.0","plugins/web/opentelemetry-instrumentation-long-task":"0.35.0","plugins/web/opentelemetry-instrumentation-user-interaction":"0.35.0","plugins/web/opentelemetry-plugin-react-load":"0.30.0","propagators/opentelemetry-propagator-aws-xray":"1.3.1","propagators/opentelemetry-propagator-grpc-census-binary":"0.27.1","propagators/opentelemetry-propagator-instana":"0.3.1","propagators/opentelemetry-propagator-ot-trace":"0.27.1"} \ No newline at end of file +{"detectors/node/opentelemetry-resource-detector-alibaba-cloud":"0.28.6","detectors/node/opentelemetry-resource-detector-aws":"1.3.6","detectors/node/opentelemetry-resource-detector-azure":"0.2.4","detectors/node/opentelemetry-resource-detector-container":"0.3.6","detectors/node/opentelemetry-resource-detector-gcp":"0.29.6","detectors/node/opentelemetry-resource-detector-github":"0.28.1","detectors/node/opentelemetry-resource-detector-instana":"0.6.0","metapackages/auto-instrumentations-node":"0.41.1","metapackages/auto-instrumentations-web":"0.36.0","packages/opentelemetry-host-metrics":"0.35.0","packages/opentelemetry-id-generator-aws-xray":"1.2.1","packages/opentelemetry-propagation-utils":"0.30.6","packages/opentelemetry-redis-common":"0.36.1","packages/opentelemetry-sampler-aws-xray":"0.34.0","packages/opentelemetry-sql-common":"0.40.0","packages/opentelemetry-test-utils":"0.36.0","plugins/node/instrumentation-amqplib":"0.34.0","plugins/node/instrumentation-cucumber":"0.3.0","plugins/node/instrumentation-dataloader":"0.6.0","plugins/node/instrumentation-fs":"0.9.0","plugins/node/instrumentation-lru-memoizer":"0.34.0","plugins/node/instrumentation-mongoose":"0.35.0","plugins/node/instrumentation-perf-hooks": "0.1.0","plugins/node/instrumentation-socket.io":"0.36.0","plugins/node/instrumentation-tedious":"0.7.0","plugins/node/opentelemetry-instrumentation-aws-lambda":"0.38.0","plugins/node/opentelemetry-instrumentation-aws-sdk":"0.38.1","plugins/node/opentelemetry-instrumentation-bunyan":"0.35.0","plugins/node/opentelemetry-instrumentation-bunyan/examples":"0.1.0","plugins/node/opentelemetry-instrumentation-cassandra":"0.35.0","plugins/node/opentelemetry-instrumentation-connect":"0.33.0","plugins/node/opentelemetry-instrumentation-dns":"0.33.0","plugins/node/opentelemetry-instrumentation-express":"0.35.0","plugins/node/opentelemetry-instrumentation-express/examples":"0.1.0","plugins/node/opentelemetry-instrumentation-fastify":"0.33.0","plugins/node/opentelemetry-instrumentation-generic-pool":"0.33.0","plugins/node/opentelemetry-instrumentation-graphql":"0.37.0","plugins/node/opentelemetry-instrumentation-hapi":"0.34.0","plugins/node/opentelemetry-instrumentation-ioredis":"0.37.0","plugins/node/opentelemetry-instrumentation-knex":"0.33.0","plugins/node/opentelemetry-instrumentation-koa":"0.37.0","plugins/node/opentelemetry-instrumentation-koa/examples":"0.1.0","plugins/node/opentelemetry-instrumentation-memcached":"0.33.0","plugins/node/opentelemetry-instrumentation-mongodb":"0.39.0","plugins/node/opentelemetry-instrumentation-mongodb/examples":"0.1.0","plugins/node/opentelemetry-instrumentation-mysql":"0.35.0","plugins/node/opentelemetry-instrumentation-mysql/examples":"0.1.0","plugins/node/opentelemetry-instrumentation-mysql2":"0.35.0","plugins/node/opentelemetry-instrumentation-nestjs-core":"0.34.0","plugins/node/opentelemetry-instrumentation-net":"0.33.0","plugins/node/opentelemetry-instrumentation-pg":"0.38.0","plugins/node/opentelemetry-instrumentation-pino":"0.35.0","plugins/node/opentelemetry-instrumentation-redis":"0.36.0","plugins/node/opentelemetry-instrumentation-redis/examples":"0.1.0","plugins/node/opentelemetry-instrumentation-redis-4":"0.36.0","plugins/node/opentelemetry-instrumentation-restify":"0.35.0","plugins/node/opentelemetry-instrumentation-router":"0.34.0","plugins/node/opentelemetry-instrumentation-undici":"0.36.0","plugins/node/opentelemetry-instrumentation-winston":"0.34.0","plugins/web/opentelemetry-instrumentation-document-load":"0.35.0","plugins/web/opentelemetry-instrumentation-long-task":"0.35.0","plugins/web/opentelemetry-instrumentation-user-interaction":"0.35.0","plugins/web/opentelemetry-plugin-react-load":"0.30.0","propagators/opentelemetry-propagator-aws-xray":"1.3.1","propagators/opentelemetry-propagator-grpc-census-binary":"0.27.1","propagators/opentelemetry-propagator-instana":"0.3.1","propagators/opentelemetry-propagator-ot-trace":"0.27.1"} \ No newline at end of file diff --git a/release-please-config.json b/release-please-config.json index 71fbcf13ad..fd8b5edcde 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -63,6 +63,7 @@ "plugins/node/opentelemetry-instrumentation-redis-4": {}, "plugins/node/opentelemetry-instrumentation-restify": {}, "plugins/node/opentelemetry-instrumentation-router": {}, + "plugins/node/opentelemetry-instrumentation-undici": {}, "plugins/node/opentelemetry-instrumentation-winston": {}, "plugins/web/opentelemetry-instrumentation-document-load": {}, "plugins/web/opentelemetry-instrumentation-long-task": {}, From 186564a1cd31ec006314df247618f337209aeffa Mon Sep 17 00:00:00 2001 From: David Luna Date: Fri, 23 Feb 2024 15:48:06 +0100 Subject: [PATCH 10/81] chore(instrumentation-undici): handle applyCustomAttributesOnSpan config --- .../src/internal-types.ts | 5 ++++ .../src/types.ts | 8 +++---- .../src/undici.ts | 12 +++++++++- .../test/fetch.test.ts | 8 +++++++ .../test/undici.test.ts | 23 +++++++++++++++++++ 5 files changed, 51 insertions(+), 5 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts b/plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts index e3a12c3d14..13271005d3 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts @@ -37,6 +37,11 @@ export interface ResponseHeadersMessage { response: UnidiciResponse; } +export interface RequestTrailersMessage { + request: UndiciRequest; + response: UnidiciResponse; +} + export interface RequestErrorMessage { request: UndiciRequest; error: Error; diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/types.ts b/plugins/node/opentelemetry-instrumentation-undici/src/types.ts index d45b18aa8d..aceb91390f 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/types.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/types.ts @@ -41,8 +41,8 @@ export interface UnidiciResponse { export interface IgnoreRequestFunction { (request: T): boolean; } -export interface CustomAttributesFunction { - (span: Span, request: T, response: Response): void; +export interface CustomAttributesFunction { + (span: Span, request: T, response: Q): void; } export interface RequestHookFunction { @@ -55,12 +55,12 @@ export interface StartSpanHookFunction { // This package will instrument HTTP requests made through `undici` or `fetch` global API // so it seems logical to have similar options than the HTTP instrumentation -export interface UndiciInstrumentationConfig +export interface UndiciInstrumentationConfig extends InstrumentationConfig { /** Not trace all outgoing requests that matched with custom function */ ignoreRequestHook?: IgnoreRequestFunction; /** Function for adding custom attributes after response is handled */ - applyCustomAttributesOnSpan?: CustomAttributesFunction; + applyCustomAttributesOnSpan?: CustomAttributesFunction; /** Function for adding custom attributes before request is handled */ requestHook?: RequestHookFunction; /** Function for adding custom attributes before a span is started */ diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts index bdca38b6bd..6ec276baf5 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts @@ -41,6 +41,7 @@ import { ListenerRecord, RequestHeadersMessage, RequestMessage, + RequestTrailersMessage, ResponseHeadersMessage, } from './internal-types'; import { UndiciInstrumentationConfig, UndiciRequest } from './types'; @@ -374,14 +375,23 @@ export class UndiciInstrumentation extends InstrumentationBase { } // This is the last event we receive if the request went without any errors - private onDone({ request }: RequestMessage): void { + private onDone({ request, response }: RequestTrailersMessage): void { const record = this._recordFromReq.get(request); if (!record) { return; } + const config = this._getConfig(); const { span, attributes, startTime } = record; + + // Let the user apply custom attribs before ending the span + safeExecuteInTheMiddle( + () => config.applyCustomAttributesOnSpan?.(span, request, response), + e => e && this._diag.error('caught applyCustomAttributesOnSpan error: ', e), + true + ); + // End the span span.end(); this._recordFromReq.delete(request); diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts b/plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts index d1fa37d3be..7c78f82824 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts @@ -221,6 +221,9 @@ describe('UndiciInstrumentation `fetch` tests', function () { requestHeaders: ['foo-client', 'x-requested-with'], responseHeaders: ['foo-server'], }, + applyCustomAttributesOnSpan: (span, req, res) => { + span.setAttribute('user.defined.attribute', 'user.defined.value'); + } }); // Do some requests @@ -280,6 +283,11 @@ describe('UndiciInstrumentation `fetch` tests', function () { 'hook-value', 'startSpanHook is called' ); + assert.strictEqual( + span.attributes['user.defined.attribute'], + 'user.defined.value', + 'applyCustomAttributesOnSpan is called' + ); }); it('should not create spans without parent if required in configuration', async function () { diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts index 49ef5ad689..7d448e7917 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts @@ -154,6 +154,9 @@ describe('UndiciInstrumentation `undici` tests', function () { requestHeaders: ['foo-client', 'x-requested-with'], responseHeaders: ['foo-server'], }, + applyCustomAttributesOnSpan: (span, req, res) => { + span.setAttribute('user.defined.attribute', 'user.defined.value'); + } }); }); afterEach(function () { @@ -249,6 +252,11 @@ describe('UndiciInstrumentation `undici` tests', function () { 'hook-value', 'startSpanHook is called' ); + assert.strictEqual( + span.attributes['user.defined.attribute'], + 'user.defined.value', + 'applyCustomAttributesOnSpan is called' + ); }); it('should create valid spans for "fetch" method', async function () { @@ -309,6 +317,11 @@ describe('UndiciInstrumentation `undici` tests', function () { 'hook-value', 'startSpanHook is called' ); + assert.strictEqual( + span.attributes['user.defined.attribute'], + 'user.defined.value', + 'applyCustomAttributesOnSpan is called' + ); }); it('should create valid spans for "stream" method', async function () { @@ -377,6 +390,11 @@ describe('UndiciInstrumentation `undici` tests', function () { 'hook-value', 'startSpanHook is called' ); + assert.strictEqual( + span.attributes['user.defined.attribute'], + 'user.defined.value', + 'applyCustomAttributesOnSpan is called' + ); }); it('should create valid spans for "dispatch" method', async function () { @@ -453,6 +471,11 @@ describe('UndiciInstrumentation `undici` tests', function () { 'hook-value', 'startSpanHook is called' ); + assert.strictEqual( + span.attributes['user.defined.attribute'], + 'user.defined.value', + 'applyCustomAttributesOnSpan is called' + ); }); it('should create valid spans even if the configuration hooks fail', async function () { From 5c91cf641e4cb5c39456548d1b4e95ec96bd5f59 Mon Sep 17 00:00:00 2001 From: David Luna Date: Fri, 23 Feb 2024 23:46:29 +0100 Subject: [PATCH 11/81] chore(instrumentation-undici): fix lint issues --- .../src/types.ts | 16 ++++++++++++---- .../src/undici.ts | 5 +++-- .../test/fetch.test.ts | 2 +- .../test/undici.test.ts | 2 +- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/types.ts b/plugins/node/opentelemetry-instrumentation-undici/src/types.ts index aceb91390f..295220c4ce 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/types.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/types.ts @@ -41,7 +41,10 @@ export interface UnidiciResponse { export interface IgnoreRequestFunction { (request: T): boolean; } -export interface CustomAttributesFunction { +export interface CustomAttributesFunction< + T = UndiciRequest, + Q = UnidiciResponse +> { (span: Span, request: T, response: Q): void; } @@ -55,12 +58,17 @@ export interface StartSpanHookFunction { // This package will instrument HTTP requests made through `undici` or `fetch` global API // so it seems logical to have similar options than the HTTP instrumentation -export interface UndiciInstrumentationConfig - extends InstrumentationConfig { +export interface UndiciInstrumentationConfig< + RequestType = UndiciRequest, + ResponseType = UnidiciResponse +> extends InstrumentationConfig { /** Not trace all outgoing requests that matched with custom function */ ignoreRequestHook?: IgnoreRequestFunction; /** Function for adding custom attributes after response is handled */ - applyCustomAttributesOnSpan?: CustomAttributesFunction; + applyCustomAttributesOnSpan?: CustomAttributesFunction< + RequestType, + ResponseType + >; /** Function for adding custom attributes before request is handled */ requestHook?: RequestHookFunction; /** Function for adding custom attributes before a span is started */ diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts index 6ec276baf5..8aa9620968 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts @@ -388,10 +388,11 @@ export class UndiciInstrumentation extends InstrumentationBase { // Let the user apply custom attribs before ending the span safeExecuteInTheMiddle( () => config.applyCustomAttributesOnSpan?.(span, request, response), - e => e && this._diag.error('caught applyCustomAttributesOnSpan error: ', e), + e => + e && this._diag.error('caught applyCustomAttributesOnSpan error: ', e), true ); - + // End the span span.end(); this._recordFromReq.delete(request); diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts b/plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts index 7c78f82824..89d8304863 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts @@ -223,7 +223,7 @@ describe('UndiciInstrumentation `fetch` tests', function () { }, applyCustomAttributesOnSpan: (span, req, res) => { span.setAttribute('user.defined.attribute', 'user.defined.value'); - } + }, }); // Do some requests diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts index 7d448e7917..f8ebdb3f57 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts @@ -156,7 +156,7 @@ describe('UndiciInstrumentation `undici` tests', function () { }, applyCustomAttributesOnSpan: (span, req, res) => { span.setAttribute('user.defined.attribute', 'user.defined.value'); - } + }, }); }); afterEach(function () { From 5f6c421bbd5fe5a152f54a61bef4b5357d56dfdd Mon Sep 17 00:00:00 2001 From: David Luna Date: Mon, 4 Mar 2024 16:31:59 +0100 Subject: [PATCH 12/81] chore(instrumentation-undici): add example app --- examples/undici/.npmrc | 1 + examples/undici/README.md | 23 ++++++++++ examples/undici/app.js | 29 ++++++++++++ examples/undici/package.json | 25 ++++++++++ examples/undici/telemetry.js | 46 +++++++++++++++++++ .../README.md | 3 +- .../src/index.ts | 11 ++++- 7 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 examples/undici/.npmrc create mode 100644 examples/undici/README.md create mode 100644 examples/undici/app.js create mode 100644 examples/undici/package.json create mode 100644 examples/undici/telemetry.js diff --git a/examples/undici/.npmrc b/examples/undici/.npmrc new file mode 100644 index 0000000000..43c97e719a --- /dev/null +++ b/examples/undici/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/examples/undici/README.md b/examples/undici/README.md new file mode 100644 index 0000000000..1b7503da23 --- /dev/null +++ b/examples/undici/README.md @@ -0,0 +1,23 @@ +This is a small example app, "app.js", that shows using the +[Undici](https://github.com/nodejs/undici) instrumentation with OpenTelemetry. + +# Usage + +```bash +npm install +node -r ./telemetry.js app.js +``` + +# Overview + +"telemetry.js" sets up the OpenTelemetry SDK to write OTel tracing spans and +log records to the *console* for simplicity. In a real setup you would +configure exporters to send to remote observability apps for viewing and +analysis. + +An example run looks like this: + +```bash +$ node -r ./telemetry.js app.js + +``` diff --git a/examples/undici/app.js b/examples/undici/app.js new file mode 100644 index 0000000000..2da65692bf --- /dev/null +++ b/examples/undici/app.js @@ -0,0 +1,29 @@ +/* + * 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. + */ + +// A small example that shows using OpenTelemetry's instrumentation of +// Undici to get spands for undici and gloabl fetch requests. Usage: +// node --require ./telemetry.js app.js + +'use strict'; + +const otel = require('@opentelemetry/api'); +const undici = require('undici'); + +const tracer = otel.trace.getTracer('example'); +tracer.startActiveSpan('manual-span', (span) => { + +}); diff --git a/examples/undici/package.json b/examples/undici/package.json new file mode 100644 index 0000000000..9afe546d86 --- /dev/null +++ b/examples/undici/package.json @@ -0,0 +1,25 @@ +{ + "name": "undici-example", + "private": true, + "version": "0.1.0", + "description": "Example of Undici/fetch instrumentation with OpenTelemetry", + "scripts": { + "start": "node -r ./telemetry.js app.js" + }, + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/open-telemetry/opentelemetry-js.git" + }, + "engines": { + "node": ">=14" + }, + "author": "OpenTelemetry Authors", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0", + "@opentelemetry/instrumentation-undici": "^0.34.0", + "@opentelemetry/resources": "^1.8.0", + "@opentelemetry/sdk-node": "^0.48.0", + "bunyan": "^1.8.15" + } +} diff --git a/examples/undici/telemetry.js b/examples/undici/telemetry.js new file mode 100644 index 0000000000..c6358f7a17 --- /dev/null +++ b/examples/undici/telemetry.js @@ -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. + */ + +// Setup telemetry for tracing and Bunyan logging. +// +// This writes OTel spans and log records to the console for simplicity. In a +// real setup you would configure exporters to send to remote observability apps +// for viewing and analysis. + +'use strict'; + +const { NodeSDK, tracing } = require('@opentelemetry/sdk-node'); + +const { UndiciInstrumentation } = require('@opentelemetry/instrumentation-undici'); + +const sdk = new NodeSDK({ + serviceName: 'undici-example', + spanProcessor: new tracing.SimpleSpanProcessor(new tracing.ConsoleSpanExporter()), + instrumentations: [ + new UndiciInstrumentation(), + ], +}); + +process.on('SIGTERM', () => { + sdk + .shutdown() + .then( + () => {}, + (err) => console.log('warning: error shutting down OTel SDK', err), + ) + .finally(() => process.exit(0)); +}); +sdk.start(); diff --git a/plugins/node/opentelemetry-instrumentation-undici/README.md b/plugins/node/opentelemetry-instrumentation-undici/README.md index 399f7640ba..e364293d4c 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/README.md +++ b/plugins/node/opentelemetry-instrumentation-undici/README.md @@ -39,8 +39,7 @@ registerInstrumentations({ ``` - -See [examples/http](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/examples/fetch) for a short example. +See [examples/undici](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/examples/undici) for a short example. ### Undici/Fetch instrumentation Options diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/index.ts b/plugins/node/opentelemetry-instrumentation-undici/src/index.ts index fda578e789..cfb7d60ef5 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/index.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/index.ts @@ -14,4 +14,13 @@ * limitations under the License. */ -export * from './undici'; +export { UndiciInstrumentation } from './undici'; +export { + UndiciRequest, + UnidiciResponse, + IgnoreRequestFunction, + CustomAttributesFunction, + RequestHookFunction, + StartSpanHookFunction, + UndiciInstrumentationConfig, +} from './types'; From 697610aa07a7a29abdd4268df32e99ceaff9f805 Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 5 Mar 2024 11:41:36 +0100 Subject: [PATCH 13/81] Update plugins/node/opentelemetry-instrumentation-undici/src/undici.ts Co-authored-by: Jamie Danielson --- plugins/node/opentelemetry-instrumentation-undici/src/undici.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts index 8aa9620968..ade9a0cae1 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts @@ -52,7 +52,7 @@ import { hrTimeToMilliseconds, } from '@opentelemetry/core'; -interface IntrumentationRecord { +interface InstrumentationRecord { span: Span; attributes: Attributes; startTime: HrTime; From cfb5d7043d79e0a0fe136e8e0311b2e5e140d435 Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 5 Mar 2024 11:41:53 +0100 Subject: [PATCH 14/81] Update plugins/node/opentelemetry-instrumentation-undici/src/undici.ts Co-authored-by: Jamie Danielson --- plugins/node/opentelemetry-instrumentation-undici/src/undici.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts index ade9a0cae1..96461bb484 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts @@ -64,7 +64,7 @@ export class UndiciInstrumentation extends InstrumentationBase { // Keep ref to avoid https://github.com/nodejs/node/issues/42170 bug and for // unsubscribing. private _channelSubs!: Array; - private _recordFromReq = new WeakMap(); + private _recordFromReq = new WeakMap(); private _httpClientDurationHistogram!: Histogram; constructor(config?: UndiciInstrumentationConfig) { From 349bf10cd0bce3d4e27ee1cc4a1379b7770833d3 Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 5 Mar 2024 11:42:00 +0100 Subject: [PATCH 15/81] Update plugins/node/opentelemetry-instrumentation-undici/src/types.ts Co-authored-by: Jamie Danielson --- plugins/node/opentelemetry-instrumentation-undici/src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/types.ts b/plugins/node/opentelemetry-instrumentation-undici/src/types.ts index 295220c4ce..572cf027c6 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/types.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/types.ts @@ -60,7 +60,7 @@ export interface StartSpanHookFunction { // so it seems logical to have similar options than the HTTP instrumentation export interface UndiciInstrumentationConfig< RequestType = UndiciRequest, - ResponseType = UnidiciResponse + ResponseType = UndiciResponse > extends InstrumentationConfig { /** Not trace all outgoing requests that matched with custom function */ ignoreRequestHook?: IgnoreRequestFunction; From fec7ed0979bb484562fc0a181c381dd86f2a1e9d Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 5 Mar 2024 11:42:07 +0100 Subject: [PATCH 16/81] Update plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts Co-authored-by: Jamie Danielson --- .../opentelemetry-instrumentation-undici/src/internal-types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts b/plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts index 13271005d3..cf1dac4a69 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts @@ -34,7 +34,7 @@ export interface RequestHeadersMessage { export interface ResponseHeadersMessage { request: UndiciRequest; - response: UnidiciResponse; + response: UndiciResponse; } export interface RequestTrailersMessage { From c68a4871d3176bed6eeedd6d462f314519ea2aae Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 5 Mar 2024 11:42:16 +0100 Subject: [PATCH 17/81] Update plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts Co-authored-by: Jamie Danielson --- .../opentelemetry-instrumentation-undici/src/internal-types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts b/plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts index cf1dac4a69..f71d749ef5 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts @@ -39,7 +39,7 @@ export interface ResponseHeadersMessage { export interface RequestTrailersMessage { request: UndiciRequest; - response: UnidiciResponse; + response: UndiciResponse; } export interface RequestErrorMessage { From 9e24242182cd7d3c097b5de5476f7c36ba3fd89a Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 5 Mar 2024 11:42:24 +0100 Subject: [PATCH 18/81] Update plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts Co-authored-by: Jamie Danielson --- .../opentelemetry-instrumentation-undici/src/internal-types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts b/plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts index f71d749ef5..fee0e7294a 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts @@ -15,7 +15,7 @@ */ import type { Channel } from 'diagnostics_channel'; -import { UndiciRequest, UnidiciResponse } from './types'; +import { UndiciRequest, UndiciResponse } from './types'; export interface ListenerRecord { name: string; From f2d0ba626b8236e69eeb015bd989ae4aad01f42e Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 5 Mar 2024 11:42:44 +0100 Subject: [PATCH 19/81] Update plugins/node/opentelemetry-instrumentation-undici/src/undici.ts Co-authored-by: Jamie Danielson --- .../node/opentelemetry-instrumentation-undici/src/undici.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts index 96461bb484..a62db2a6bb 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts @@ -269,9 +269,9 @@ export class UndiciInstrumentation extends InstrumentationBase { this._recordFromReq.set(request, { span, attributes, startTime }); } - // This is the 2nd message we recevie for each request. It is fired when connection with - // the remote is stablished and about to send the first byte. Here do have info about the - // remote addres an port so we can poupulate some `network.*` attributes into the span + // This is the 2nd message we receive for each request. It is fired when connection with + // the remote is established and about to send the first byte. Here we do have info about the + // remote address and port so we can populate some `network.*` attributes into the span private onRequestHeaders({ request, socket }: RequestHeadersMessage): void { const record = this._recordFromReq.get(request as UndiciRequest); From 36224c5290c13750d06a08e4571654cc1ed74748 Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 5 Mar 2024 11:42:58 +0100 Subject: [PATCH 20/81] Update plugins/node/opentelemetry-instrumentation-undici/src/types.ts Co-authored-by: Jamie Danielson --- plugins/node/opentelemetry-instrumentation-undici/src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/types.ts b/plugins/node/opentelemetry-instrumentation-undici/src/types.ts index 572cf027c6..4b9445a940 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/types.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/types.ts @@ -33,7 +33,7 @@ export interface UndiciRequest { body: any; } -export interface UnidiciResponse { +export interface UndiciResponse { headers: Buffer[]; statusCode: number; } From c140261400cc8cef8ac8cb1f90068ac90f950aac Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 5 Mar 2024 11:43:13 +0100 Subject: [PATCH 21/81] Update plugins/node/opentelemetry-instrumentation-undici/README.md Co-authored-by: Jamie Danielson --- plugins/node/opentelemetry-instrumentation-undici/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/README.md b/plugins/node/opentelemetry-instrumentation-undici/README.md index 399f7640ba..4b0dcb3543 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/README.md +++ b/plugins/node/opentelemetry-instrumentation-undici/README.md @@ -53,7 +53,7 @@ Undici instrumentation has few options available to choose from. You can set the | [`requestHook`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#65) | `RequestHookFunction` | Function for adding custom attributes before request is handled. | | [`startSpanHook`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#67) | `StartSpanHookFunction` | Function for adding custom attributes before a span is started. | | [`requireParentforSpans`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#69) | `Boolean` | Require a parent span is present to create new span for outgoing requests. | -| [`headersToSpanAttributes`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#71) | `Object` | List of case insensitive HTTP headers to convert to span attributes. Headers will be converted to span attributes in the form of `http.{request\|response}.header.header-name` where the name is only lowecased, e.g. `http.response.header.content-length`| +| [`headersToSpanAttributes`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#71) | `Object` | List of case insensitive HTTP headers to convert to span attributes. Headers will be converted to span attributes in the form of `http.{request\|response}.header.header-name` where the name is only lowercased, e.g. `http.response.header.content-length`| ## Useful links From abbe4267138279359f4a1d63e9016b7f55b40da5 Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 5 Mar 2024 11:43:32 +0100 Subject: [PATCH 22/81] Update plugins/node/opentelemetry-instrumentation-undici/src/types.ts Co-authored-by: Jamie Danielson --- plugins/node/opentelemetry-instrumentation-undici/src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/types.ts b/plugins/node/opentelemetry-instrumentation-undici/src/types.ts index 4b9445a940..3d35ade577 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/types.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/types.ts @@ -43,7 +43,7 @@ export interface IgnoreRequestFunction { } export interface CustomAttributesFunction< T = UndiciRequest, - Q = UnidiciResponse + Q = UndiciResponse > { (span: Span, request: T, response: Q): void; } From d7912ab22694736c5a9526b9dca83776801e593d Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 5 Mar 2024 11:45:17 +0100 Subject: [PATCH 23/81] chore(instrumentation-undici): finish example --- examples/undici/README.md | 70 ++++++++++++++++++- examples/undici/app.js | 18 +++-- examples/undici/package.json | 4 +- .../tsconfig.json | 2 +- 4 files changed, 86 insertions(+), 8 deletions(-) diff --git a/examples/undici/README.md b/examples/undici/README.md index 1b7503da23..e11fae7287 100644 --- a/examples/undici/README.md +++ b/examples/undici/README.md @@ -19,5 +19,73 @@ An example run looks like this: ```bash $ node -r ./telemetry.js app.js - +{ + traceId: '22daea1ed239a0cb7c80d9181ecc7560', + parentId: 'b8d696439544de46', + traceState: undefined, + name: 'HTTP GET', + id: '9eddb5f9fb35e8f6', + kind: 2, + timestamp: 1709568252718000, + duration: 491199.417, + attributes: { + 'http.request.method': 'GET', + 'url.full': 'https://example.com/', + 'url.path': '/', + 'url.query': '', + 'url.scheme': 'https', + 'server.address': 'example.com', + 'server.port': 443, + 'user_agent.original': 'undici', + 'network.peer.address': '93.184.216.34', + 'network.peer.port': 443, + 'http.response.status_code': 200, + 'http.response.header.content-length': 648 + }, + status: { code: 0 }, + events: [], + links: [] +} +fetched HTML size 1256 +{ + traceId: '22daea1ed239a0cb7c80d9181ecc7560', + parentId: 'b8d696439544de46', + traceState: undefined, + name: 'HTTP GET', + id: 'ca45235f853cdb68', + kind: 2, + timestamp: 1709568253216000, + duration: 112576.459, + attributes: { + 'http.request.method': 'GET', + 'url.full': 'https://example.com/', + 'url.path': '/', + 'url.query': '', + 'url.scheme': 'https', + 'server.address': 'example.com', + 'server.port': 443, + 'network.peer.address': '93.184.216.34', + 'network.peer.port': 443, + 'http.response.status_code': 200, + 'http.response.header.content-length': 1256 + }, + status: { code: 0 }, + events: [], + links: [] +} +requested HTML size 1256 +{ + traceId: '22daea1ed239a0cb7c80d9181ecc7560', + parentId: undefined, + traceState: undefined, + name: 'manual-span', + id: 'b8d696439544de46', + kind: 0, + timestamp: 1709568252715000, + duration: 614814.666, + attributes: {}, + status: { code: 0 }, + events: [], + links: [] +} ``` diff --git a/examples/undici/app.js b/examples/undici/app.js index 2da65692bf..e5b72babcb 100644 --- a/examples/undici/app.js +++ b/examples/undici/app.js @@ -15,15 +15,25 @@ */ // A small example that shows using OpenTelemetry's instrumentation of -// Undici to get spands for undici and gloabl fetch requests. Usage: +// Undici to get spans for undici and gloabl fetch requests. Usage: // node --require ./telemetry.js app.js 'use strict'; const otel = require('@opentelemetry/api'); -const undici = require('undici'); +const { request } = require('undici'); const tracer = otel.trace.getTracer('example'); -tracer.startActiveSpan('manual-span', (span) => { - +tracer.startActiveSpan('manual-span', async (span) => { + // 1st use the global fetch API + const fetchResponse = await fetch('https://example.com'); + const fetchText = await fetchResponse.text(); + console.log('fetched HTML size', fetchText.length); + + const undiciResponse = await request('https://example.com'); + const undiciText = await undiciResponse.body.text(); + console.log('requested HTML size', undiciText.length); + + // Finish the parent span + span.end(); }); diff --git a/examples/undici/package.json b/examples/undici/package.json index 9afe546d86..91f84b55ee 100644 --- a/examples/undici/package.json +++ b/examples/undici/package.json @@ -17,9 +17,9 @@ "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.3.0", - "@opentelemetry/instrumentation-undici": "^0.34.0", + "@opentelemetry/instrumentation-undici": "file:../../plugins/node/opentelemetry-instrumentation-undici", "@opentelemetry/resources": "^1.8.0", "@opentelemetry/sdk-node": "^0.48.0", - "bunyan": "^1.8.15" + "undici": "^6.7.0" } } diff --git a/plugins/node/opentelemetry-instrumentation-undici/tsconfig.json b/plugins/node/opentelemetry-instrumentation-undici/tsconfig.json index 810157c218..28be80d266 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/tsconfig.json +++ b/plugins/node/opentelemetry-instrumentation-undici/tsconfig.json @@ -8,4 +8,4 @@ "src/**/*.ts", "test/**/*.ts" ] -} \ No newline at end of file +} From d8eafc6ccf84a866abe57739bd9fb4897b4d57cb Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 5 Mar 2024 11:46:53 +0100 Subject: [PATCH 24/81] chore(instrumentation-undici): fix export --- plugins/node/opentelemetry-instrumentation-undici/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/index.ts b/plugins/node/opentelemetry-instrumentation-undici/src/index.ts index cfb7d60ef5..87ca2686d5 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/index.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/index.ts @@ -17,7 +17,7 @@ export { UndiciInstrumentation } from './undici'; export { UndiciRequest, - UnidiciResponse, + UndiciResponse, IgnoreRequestFunction, CustomAttributesFunction, RequestHookFunction, From ac2ae47bb7c0405fd418bd0a6a3b2d82c760aef4 Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 5 Mar 2024 11:56:35 +0100 Subject: [PATCH 25/81] chore(instrumentation-undici): remove example --- examples/undici/.npmrc | 1 - examples/undici/README.md | 91 ------------------------------------ examples/undici/app.js | 39 ---------------- examples/undici/package.json | 25 ---------- examples/undici/telemetry.js | 46 ------------------ 5 files changed, 202 deletions(-) delete mode 100644 examples/undici/.npmrc delete mode 100644 examples/undici/README.md delete mode 100644 examples/undici/app.js delete mode 100644 examples/undici/package.json delete mode 100644 examples/undici/telemetry.js diff --git a/examples/undici/.npmrc b/examples/undici/.npmrc deleted file mode 100644 index 43c97e719a..0000000000 --- a/examples/undici/.npmrc +++ /dev/null @@ -1 +0,0 @@ -package-lock=false diff --git a/examples/undici/README.md b/examples/undici/README.md deleted file mode 100644 index e11fae7287..0000000000 --- a/examples/undici/README.md +++ /dev/null @@ -1,91 +0,0 @@ -This is a small example app, "app.js", that shows using the -[Undici](https://github.com/nodejs/undici) instrumentation with OpenTelemetry. - -# Usage - -```bash -npm install -node -r ./telemetry.js app.js -``` - -# Overview - -"telemetry.js" sets up the OpenTelemetry SDK to write OTel tracing spans and -log records to the *console* for simplicity. In a real setup you would -configure exporters to send to remote observability apps for viewing and -analysis. - -An example run looks like this: - -```bash -$ node -r ./telemetry.js app.js -{ - traceId: '22daea1ed239a0cb7c80d9181ecc7560', - parentId: 'b8d696439544de46', - traceState: undefined, - name: 'HTTP GET', - id: '9eddb5f9fb35e8f6', - kind: 2, - timestamp: 1709568252718000, - duration: 491199.417, - attributes: { - 'http.request.method': 'GET', - 'url.full': 'https://example.com/', - 'url.path': '/', - 'url.query': '', - 'url.scheme': 'https', - 'server.address': 'example.com', - 'server.port': 443, - 'user_agent.original': 'undici', - 'network.peer.address': '93.184.216.34', - 'network.peer.port': 443, - 'http.response.status_code': 200, - 'http.response.header.content-length': 648 - }, - status: { code: 0 }, - events: [], - links: [] -} -fetched HTML size 1256 -{ - traceId: '22daea1ed239a0cb7c80d9181ecc7560', - parentId: 'b8d696439544de46', - traceState: undefined, - name: 'HTTP GET', - id: 'ca45235f853cdb68', - kind: 2, - timestamp: 1709568253216000, - duration: 112576.459, - attributes: { - 'http.request.method': 'GET', - 'url.full': 'https://example.com/', - 'url.path': '/', - 'url.query': '', - 'url.scheme': 'https', - 'server.address': 'example.com', - 'server.port': 443, - 'network.peer.address': '93.184.216.34', - 'network.peer.port': 443, - 'http.response.status_code': 200, - 'http.response.header.content-length': 1256 - }, - status: { code: 0 }, - events: [], - links: [] -} -requested HTML size 1256 -{ - traceId: '22daea1ed239a0cb7c80d9181ecc7560', - parentId: undefined, - traceState: undefined, - name: 'manual-span', - id: 'b8d696439544de46', - kind: 0, - timestamp: 1709568252715000, - duration: 614814.666, - attributes: {}, - status: { code: 0 }, - events: [], - links: [] -} -``` diff --git a/examples/undici/app.js b/examples/undici/app.js deleted file mode 100644 index e5b72babcb..0000000000 --- a/examples/undici/app.js +++ /dev/null @@ -1,39 +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. - */ - -// A small example that shows using OpenTelemetry's instrumentation of -// Undici to get spans for undici and gloabl fetch requests. Usage: -// node --require ./telemetry.js app.js - -'use strict'; - -const otel = require('@opentelemetry/api'); -const { request } = require('undici'); - -const tracer = otel.trace.getTracer('example'); -tracer.startActiveSpan('manual-span', async (span) => { - // 1st use the global fetch API - const fetchResponse = await fetch('https://example.com'); - const fetchText = await fetchResponse.text(); - console.log('fetched HTML size', fetchText.length); - - const undiciResponse = await request('https://example.com'); - const undiciText = await undiciResponse.body.text(); - console.log('requested HTML size', undiciText.length); - - // Finish the parent span - span.end(); -}); diff --git a/examples/undici/package.json b/examples/undici/package.json deleted file mode 100644 index 91f84b55ee..0000000000 --- a/examples/undici/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "undici-example", - "private": true, - "version": "0.1.0", - "description": "Example of Undici/fetch instrumentation with OpenTelemetry", - "scripts": { - "start": "node -r ./telemetry.js app.js" - }, - "repository": { - "type": "git", - "url": "git+ssh://git@github.com/open-telemetry/opentelemetry-js.git" - }, - "engines": { - "node": ">=14" - }, - "author": "OpenTelemetry Authors", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0", - "@opentelemetry/instrumentation-undici": "file:../../plugins/node/opentelemetry-instrumentation-undici", - "@opentelemetry/resources": "^1.8.0", - "@opentelemetry/sdk-node": "^0.48.0", - "undici": "^6.7.0" - } -} diff --git a/examples/undici/telemetry.js b/examples/undici/telemetry.js deleted file mode 100644 index c6358f7a17..0000000000 --- a/examples/undici/telemetry.js +++ /dev/null @@ -1,46 +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. - */ - -// Setup telemetry for tracing and Bunyan logging. -// -// This writes OTel spans and log records to the console for simplicity. In a -// real setup you would configure exporters to send to remote observability apps -// for viewing and analysis. - -'use strict'; - -const { NodeSDK, tracing } = require('@opentelemetry/sdk-node'); - -const { UndiciInstrumentation } = require('@opentelemetry/instrumentation-undici'); - -const sdk = new NodeSDK({ - serviceName: 'undici-example', - spanProcessor: new tracing.SimpleSpanProcessor(new tracing.ConsoleSpanExporter()), - instrumentations: [ - new UndiciInstrumentation(), - ], -}); - -process.on('SIGTERM', () => { - sdk - .shutdown() - .then( - () => {}, - (err) => console.log('warning: error shutting down OTel SDK', err), - ) - .finally(() => process.exit(0)); -}); -sdk.start(); From 203d4e3607db6297638bb3835cc2986083d68a5f Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 5 Mar 2024 12:07:44 +0100 Subject: [PATCH 26/81] chore(instrumentation-undici): update lock file --- package-lock.json | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index e765cec80c..49168050f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -126,6 +126,9 @@ }, "engines": { "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" } }, "detectors/node/opentelemetry-resource-detector-azure/node_modules/@types/mocha": { @@ -36417,6 +36420,7 @@ } }, "plugins/node/opentelemetry-instrumentation-undici": { + "name": "@opentelemetry/instrumentation-undici", "version": "0.33.0", "license": "Apache-2.0", "dependencies": { @@ -36447,11 +36451,28 @@ "@opentelemetry/api": "^1.3.0" } }, + "plugins/node/opentelemetry-instrumentation-undici/node_modules/@opentelemetry/instrumentation": { + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.48.0.tgz", + "integrity": "sha512-sjtZQB5PStIdCw5ovVTDGwnmQC+GGYArJNgIcydrDSqUTdYBnMrN9P4pwQZgS3vTGIp+TU1L8vMXGe51NVmIKQ==", + "dependencies": { + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "1.7.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, "plugins/node/opentelemetry-instrumentation-undici/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -36463,7 +36484,6 @@ "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -36477,8 +36497,7 @@ "plugins/node/opentelemetry-instrumentation-undici/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "plugins/node/opentelemetry-instrumentation-winston": { "name": "@opentelemetry/instrumentation-winston", @@ -45149,11 +45168,22 @@ "undici": "^5.0.0" }, "dependencies": { + "@opentelemetry/instrumentation": { + "version": "0.48.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.48.0.tgz", + "integrity": "sha512-sjtZQB5PStIdCw5ovVTDGwnmQC+GGYArJNgIcydrDSqUTdYBnMrN9P4pwQZgS3vTGIp+TU1L8vMXGe51NVmIKQ==", + "requires": { + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "1.7.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + } + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -45162,7 +45192,6 @@ "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, "requires": { "lru-cache": "^6.0.0" } @@ -45170,8 +45199,7 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, From 85ffb53687fffb42bcfb35c6b80daa29e657ad25 Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 5 Mar 2024 14:12:42 +0100 Subject: [PATCH 27/81] chore(instrumentation-undici): update release please manifest --- .release-please-manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index a0578b9a34..fb4aa5217a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1 +1 @@ -{"detectors/node/opentelemetry-resource-detector-alibaba-cloud":"0.28.6","detectors/node/opentelemetry-resource-detector-aws":"1.3.6","detectors/node/opentelemetry-resource-detector-azure":"0.2.4","detectors/node/opentelemetry-resource-detector-container":"0.3.6","detectors/node/opentelemetry-resource-detector-gcp":"0.29.6","detectors/node/opentelemetry-resource-detector-github":"0.28.1","detectors/node/opentelemetry-resource-detector-instana":"0.6.0","metapackages/auto-instrumentations-node":"0.41.1","metapackages/auto-instrumentations-web":"0.36.0","packages/opentelemetry-host-metrics":"0.35.0","packages/opentelemetry-id-generator-aws-xray":"1.2.1","packages/opentelemetry-propagation-utils":"0.30.6","packages/opentelemetry-redis-common":"0.36.1","packages/opentelemetry-sampler-aws-xray":"0.34.0","packages/opentelemetry-sql-common":"0.40.0","packages/opentelemetry-test-utils":"0.36.0","plugins/node/instrumentation-amqplib":"0.34.0","plugins/node/instrumentation-cucumber":"0.3.0","plugins/node/instrumentation-dataloader":"0.6.0","plugins/node/instrumentation-fs":"0.9.0","plugins/node/instrumentation-lru-memoizer":"0.34.0","plugins/node/instrumentation-mongoose":"0.35.0","plugins/node/instrumentation-perf-hooks": "0.1.0","plugins/node/instrumentation-socket.io":"0.36.0","plugins/node/instrumentation-tedious":"0.7.0","plugins/node/opentelemetry-instrumentation-aws-lambda":"0.38.0","plugins/node/opentelemetry-instrumentation-aws-sdk":"0.38.1","plugins/node/opentelemetry-instrumentation-bunyan":"0.35.0","plugins/node/opentelemetry-instrumentation-bunyan/examples":"0.1.0","plugins/node/opentelemetry-instrumentation-cassandra":"0.35.0","plugins/node/opentelemetry-instrumentation-connect":"0.33.0","plugins/node/opentelemetry-instrumentation-dns":"0.33.0","plugins/node/opentelemetry-instrumentation-express":"0.35.0","plugins/node/opentelemetry-instrumentation-express/examples":"0.1.0","plugins/node/opentelemetry-instrumentation-fastify":"0.33.0","plugins/node/opentelemetry-instrumentation-generic-pool":"0.33.0","plugins/node/opentelemetry-instrumentation-graphql":"0.37.0","plugins/node/opentelemetry-instrumentation-hapi":"0.34.0","plugins/node/opentelemetry-instrumentation-ioredis":"0.37.0","plugins/node/opentelemetry-instrumentation-knex":"0.33.0","plugins/node/opentelemetry-instrumentation-koa":"0.37.0","plugins/node/opentelemetry-instrumentation-koa/examples":"0.1.0","plugins/node/opentelemetry-instrumentation-memcached":"0.33.0","plugins/node/opentelemetry-instrumentation-mongodb":"0.39.0","plugins/node/opentelemetry-instrumentation-mongodb/examples":"0.1.0","plugins/node/opentelemetry-instrumentation-mysql":"0.35.0","plugins/node/opentelemetry-instrumentation-mysql/examples":"0.1.0","plugins/node/opentelemetry-instrumentation-mysql2":"0.35.0","plugins/node/opentelemetry-instrumentation-nestjs-core":"0.34.0","plugins/node/opentelemetry-instrumentation-net":"0.33.0","plugins/node/opentelemetry-instrumentation-pg":"0.38.0","plugins/node/opentelemetry-instrumentation-pino":"0.35.0","plugins/node/opentelemetry-instrumentation-redis":"0.36.0","plugins/node/opentelemetry-instrumentation-redis/examples":"0.1.0","plugins/node/opentelemetry-instrumentation-redis-4":"0.36.0","plugins/node/opentelemetry-instrumentation-restify":"0.35.0","plugins/node/opentelemetry-instrumentation-router":"0.34.0","plugins/node/opentelemetry-instrumentation-undici":"0.36.0","plugins/node/opentelemetry-instrumentation-winston":"0.34.0","plugins/web/opentelemetry-instrumentation-document-load":"0.35.0","plugins/web/opentelemetry-instrumentation-long-task":"0.35.0","plugins/web/opentelemetry-instrumentation-user-interaction":"0.35.0","plugins/web/opentelemetry-plugin-react-load":"0.30.0","propagators/opentelemetry-propagator-aws-xray":"1.3.1","propagators/opentelemetry-propagator-grpc-census-binary":"0.27.1","propagators/opentelemetry-propagator-instana":"0.3.1","propagators/opentelemetry-propagator-ot-trace":"0.27.1"} +{"detectors/node/opentelemetry-resource-detector-alibaba-cloud":"0.28.6","detectors/node/opentelemetry-resource-detector-aws":"1.3.6","detectors/node/opentelemetry-resource-detector-azure":"0.2.4","detectors/node/opentelemetry-resource-detector-container":"0.3.6","detectors/node/opentelemetry-resource-detector-gcp":"0.29.6","detectors/node/opentelemetry-resource-detector-github":"0.28.1","detectors/node/opentelemetry-resource-detector-instana":"0.6.0","metapackages/auto-instrumentations-node":"0.41.1","metapackages/auto-instrumentations-web":"0.36.0","packages/opentelemetry-host-metrics":"0.35.0","packages/opentelemetry-id-generator-aws-xray":"1.2.1","packages/opentelemetry-propagation-utils":"0.30.6","packages/opentelemetry-redis-common":"0.36.1","packages/opentelemetry-sql-common":"0.40.0","packages/opentelemetry-test-utils":"0.36.0","plugins/node/instrumentation-amqplib":"0.34.0","plugins/node/instrumentation-cucumber":"0.3.0","plugins/node/instrumentation-dataloader":"0.6.0","plugins/node/instrumentation-fs":"0.9.0","plugins/node/instrumentation-lru-memoizer":"0.34.0","plugins/node/instrumentation-mongoose":"0.35.0","plugins/node/instrumentation-runtime-node": "0.1.0","plugins/node/instrumentation-socket.io":"0.36.0","plugins/node/instrumentation-tedious":"0.7.0","plugins/node/opentelemetry-instrumentation-aws-lambda":"0.38.0","plugins/node/opentelemetry-instrumentation-aws-sdk":"0.38.1","plugins/node/opentelemetry-instrumentation-bunyan":"0.35.0","plugins/node/opentelemetry-instrumentation-cassandra":"0.35.0","plugins/node/opentelemetry-instrumentation-connect":"0.33.0","plugins/node/opentelemetry-instrumentation-dns":"0.33.0","plugins/node/opentelemetry-instrumentation-express":"0.35.0","plugins/node/opentelemetry-instrumentation-fastify":"0.33.0","plugins/node/opentelemetry-instrumentation-generic-pool":"0.33.0","plugins/node/opentelemetry-instrumentation-graphql":"0.37.0","plugins/node/opentelemetry-instrumentation-hapi":"0.34.0","plugins/node/opentelemetry-instrumentation-ioredis":"0.37.0","plugins/node/opentelemetry-instrumentation-knex":"0.33.0","plugins/node/opentelemetry-instrumentation-koa":"0.37.0","plugins/node/opentelemetry-instrumentation-memcached":"0.33.0","plugins/node/opentelemetry-instrumentation-mongodb":"0.39.0","plugins/node/opentelemetry-instrumentation-mysql":"0.35.0","plugins/node/opentelemetry-instrumentation-mysql2":"0.35.0","plugins/node/opentelemetry-instrumentation-nestjs-core":"0.34.0","plugins/node/opentelemetry-instrumentation-net":"0.33.0","plugins/node/opentelemetry-instrumentation-pg":"0.38.0","plugins/node/opentelemetry-instrumentation-pino":"0.35.0","plugins/node/opentelemetry-instrumentation-redis":"0.36.0","plugins/node/opentelemetry-instrumentation-redis-4":"0.36.0","plugins/node/opentelemetry-instrumentation-restify":"0.35.0","plugins/node/opentelemetry-instrumentation-router":"0.34.0","plugins/node/opentelemetry-instrumentation-undici":"0.36.0","plugins/node/opentelemetry-instrumentation-winston":"0.34.0","plugins/web/opentelemetry-instrumentation-document-load":"0.35.0","plugins/web/opentelemetry-instrumentation-long-task":"0.35.0","plugins/web/opentelemetry-instrumentation-user-interaction":"0.35.0","plugins/web/opentelemetry-plugin-react-load":"0.30.0","propagators/opentelemetry-propagator-aws-xray":"1.3.1","propagators/opentelemetry-propagator-grpc-census-binary":"0.27.1","propagators/opentelemetry-propagator-instana":"0.3.1","propagators/opentelemetry-propagator-ot-trace":"0.27.1"} \ No newline at end of file From 51197a20bcc66ceb6372539b34321d11ea538067 Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 6 Mar 2024 11:21:32 +0100 Subject: [PATCH 28/81] chore(instrumentation-undici): fix lock file --- .release-please-manifest.json | 63 ++++++++++++++++++- package-lock.json | 19 +++++- .../package.json | 4 +- 3 files changed, 81 insertions(+), 5 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index fb4aa5217a..13cab32dd8 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1 +1,62 @@ -{"detectors/node/opentelemetry-resource-detector-alibaba-cloud":"0.28.6","detectors/node/opentelemetry-resource-detector-aws":"1.3.6","detectors/node/opentelemetry-resource-detector-azure":"0.2.4","detectors/node/opentelemetry-resource-detector-container":"0.3.6","detectors/node/opentelemetry-resource-detector-gcp":"0.29.6","detectors/node/opentelemetry-resource-detector-github":"0.28.1","detectors/node/opentelemetry-resource-detector-instana":"0.6.0","metapackages/auto-instrumentations-node":"0.41.1","metapackages/auto-instrumentations-web":"0.36.0","packages/opentelemetry-host-metrics":"0.35.0","packages/opentelemetry-id-generator-aws-xray":"1.2.1","packages/opentelemetry-propagation-utils":"0.30.6","packages/opentelemetry-redis-common":"0.36.1","packages/opentelemetry-sql-common":"0.40.0","packages/opentelemetry-test-utils":"0.36.0","plugins/node/instrumentation-amqplib":"0.34.0","plugins/node/instrumentation-cucumber":"0.3.0","plugins/node/instrumentation-dataloader":"0.6.0","plugins/node/instrumentation-fs":"0.9.0","plugins/node/instrumentation-lru-memoizer":"0.34.0","plugins/node/instrumentation-mongoose":"0.35.0","plugins/node/instrumentation-runtime-node": "0.1.0","plugins/node/instrumentation-socket.io":"0.36.0","plugins/node/instrumentation-tedious":"0.7.0","plugins/node/opentelemetry-instrumentation-aws-lambda":"0.38.0","plugins/node/opentelemetry-instrumentation-aws-sdk":"0.38.1","plugins/node/opentelemetry-instrumentation-bunyan":"0.35.0","plugins/node/opentelemetry-instrumentation-cassandra":"0.35.0","plugins/node/opentelemetry-instrumentation-connect":"0.33.0","plugins/node/opentelemetry-instrumentation-dns":"0.33.0","plugins/node/opentelemetry-instrumentation-express":"0.35.0","plugins/node/opentelemetry-instrumentation-fastify":"0.33.0","plugins/node/opentelemetry-instrumentation-generic-pool":"0.33.0","plugins/node/opentelemetry-instrumentation-graphql":"0.37.0","plugins/node/opentelemetry-instrumentation-hapi":"0.34.0","plugins/node/opentelemetry-instrumentation-ioredis":"0.37.0","plugins/node/opentelemetry-instrumentation-knex":"0.33.0","plugins/node/opentelemetry-instrumentation-koa":"0.37.0","plugins/node/opentelemetry-instrumentation-memcached":"0.33.0","plugins/node/opentelemetry-instrumentation-mongodb":"0.39.0","plugins/node/opentelemetry-instrumentation-mysql":"0.35.0","plugins/node/opentelemetry-instrumentation-mysql2":"0.35.0","plugins/node/opentelemetry-instrumentation-nestjs-core":"0.34.0","plugins/node/opentelemetry-instrumentation-net":"0.33.0","plugins/node/opentelemetry-instrumentation-pg":"0.38.0","plugins/node/opentelemetry-instrumentation-pino":"0.35.0","plugins/node/opentelemetry-instrumentation-redis":"0.36.0","plugins/node/opentelemetry-instrumentation-redis-4":"0.36.0","plugins/node/opentelemetry-instrumentation-restify":"0.35.0","plugins/node/opentelemetry-instrumentation-router":"0.34.0","plugins/node/opentelemetry-instrumentation-undici":"0.36.0","plugins/node/opentelemetry-instrumentation-winston":"0.34.0","plugins/web/opentelemetry-instrumentation-document-load":"0.35.0","plugins/web/opentelemetry-instrumentation-long-task":"0.35.0","plugins/web/opentelemetry-instrumentation-user-interaction":"0.35.0","plugins/web/opentelemetry-plugin-react-load":"0.30.0","propagators/opentelemetry-propagator-aws-xray":"1.3.1","propagators/opentelemetry-propagator-grpc-census-binary":"0.27.1","propagators/opentelemetry-propagator-instana":"0.3.1","propagators/opentelemetry-propagator-ot-trace":"0.27.1"} \ No newline at end of file +{ + "detectors/node/opentelemetry-resource-detector-alibaba-cloud": "0.28.6", + "detectors/node/opentelemetry-resource-detector-aws": "1.3.6", + "detectors/node/opentelemetry-resource-detector-azure": "0.2.4", + "detectors/node/opentelemetry-resource-detector-container": "0.3.6", + "detectors/node/opentelemetry-resource-detector-gcp": "0.29.6", + "detectors/node/opentelemetry-resource-detector-github": "0.28.1", + "detectors/node/opentelemetry-resource-detector-instana": "0.6.0", + "metapackages/auto-instrumentations-node": "0.41.1", + "metapackages/auto-instrumentations-web": "0.36.0", + "packages/opentelemetry-host-metrics": "0.35.0", + "packages/opentelemetry-id-generator-aws-xray": "1.2.1", + "packages/opentelemetry-propagation-utils": "0.30.6", + "packages/opentelemetry-redis-common": "0.36.1", + "packages/opentelemetry-sql-common": "0.40.0", + "packages/opentelemetry-test-utils": "0.36.0", + "plugins/node/instrumentation-amqplib": "0.34.0", + "plugins/node/instrumentation-cucumber": "0.3.0", + "plugins/node/instrumentation-dataloader": "0.6.0", + "plugins/node/instrumentation-fs": "0.9.0", + "plugins/node/instrumentation-lru-memoizer": "0.34.0", + "plugins/node/instrumentation-mongoose": "0.35.0", + "plugins/node/instrumentation-runtime-node": "0.1.0", + "plugins/node/instrumentation-socket.io": "0.36.0", + "plugins/node/instrumentation-tedious": "0.7.0", + "plugins/node/opentelemetry-instrumentation-aws-lambda": "0.38.0", + "plugins/node/opentelemetry-instrumentation-aws-sdk": "0.38.1", + "plugins/node/opentelemetry-instrumentation-bunyan": "0.35.0", + "plugins/node/opentelemetry-instrumentation-cassandra": "0.35.0", + "plugins/node/opentelemetry-instrumentation-connect": "0.33.0", + "plugins/node/opentelemetry-instrumentation-dns": "0.33.0", + "plugins/node/opentelemetry-instrumentation-express": "0.35.0", + "plugins/node/opentelemetry-instrumentation-fastify": "0.33.0", + "plugins/node/opentelemetry-instrumentation-generic-pool": "0.33.0", + "plugins/node/opentelemetry-instrumentation-graphql": "0.37.0", + "plugins/node/opentelemetry-instrumentation-hapi": "0.34.0", + "plugins/node/opentelemetry-instrumentation-ioredis": "0.37.0", + "plugins/node/opentelemetry-instrumentation-knex": "0.33.0", + "plugins/node/opentelemetry-instrumentation-koa": "0.37.0", + "plugins/node/opentelemetry-instrumentation-memcached": "0.33.0", + "plugins/node/opentelemetry-instrumentation-mongodb": "0.39.0", + "plugins/node/opentelemetry-instrumentation-mysql": "0.35.0", + "plugins/node/opentelemetry-instrumentation-mysql2": "0.35.0", + "plugins/node/opentelemetry-instrumentation-nestjs-core": "0.34.0", + "plugins/node/opentelemetry-instrumentation-net": "0.33.0", + "plugins/node/opentelemetry-instrumentation-pg": "0.38.0", + "plugins/node/opentelemetry-instrumentation-pino": "0.35.0", + "plugins/node/opentelemetry-instrumentation-redis": "0.36.0", + "plugins/node/opentelemetry-instrumentation-redis-4": "0.36.0", + "plugins/node/opentelemetry-instrumentation-restify": "0.35.0", + "plugins/node/opentelemetry-instrumentation-router": "0.34.0", + "plugins/node/opentelemetry-instrumentation-undici": "0.36.0", + "plugins/node/opentelemetry-instrumentation-winston": "0.34.0", + "plugins/web/opentelemetry-instrumentation-document-load": "0.35.0", + "plugins/web/opentelemetry-instrumentation-long-task": "0.35.0", + "plugins/web/opentelemetry-instrumentation-user-interaction": "0.35.0", + "plugins/web/opentelemetry-plugin-react-load": "0.30.0", + "propagators/opentelemetry-propagator-aws-xray": "1.3.1", + "propagators/opentelemetry-propagator-grpc-census-binary": "0.27.1", + "propagators/opentelemetry-propagator-instana": "0.3.1", + "propagators/opentelemetry-propagator-ot-trace": "0.27.1" +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 49168050f3..26875d1203 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4678,6 +4678,15 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "dev": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@fastify/error": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz", @@ -36442,7 +36451,7 @@ "test-all-versions": "6.0.0", "ts-mocha": "10.0.0", "typescript": "4.4.4", - "undici": "^5.0.0" + "undici": "^5.28.3" }, "engines": { "node": ">=14" @@ -40699,6 +40708,12 @@ } } }, + "@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "dev": true + }, "@fastify/error": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz", @@ -45165,7 +45180,7 @@ "test-all-versions": "6.0.0", "ts-mocha": "10.0.0", "typescript": "4.4.4", - "undici": "^5.0.0" + "undici": "^5.28.3" }, "dependencies": { "@opentelemetry/instrumentation": { diff --git a/plugins/node/opentelemetry-instrumentation-undici/package.json b/plugins/node/opentelemetry-instrumentation-undici/package.json index afd252b761..9702891ac8 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/package.json +++ b/plugins/node/opentelemetry-instrumentation-undici/package.json @@ -56,12 +56,12 @@ "mocha": "7.2.0", "nyc": "15.1.0", "rimraf": "5.0.5", - "test-all-versions": "6.0.0", "semver": "^7.6.0", "superagent": "8.0.9", + "test-all-versions": "6.0.0", "ts-mocha": "10.0.0", "typescript": "4.4.4", - "undici": "^5.0.0" + "undici": "^5.28.3" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" From ac195600de6326329db5e54a4e282876903d7d3d Mon Sep 17 00:00:00 2001 From: David Luna Date: Thu, 7 Mar 2024 10:48:27 +0100 Subject: [PATCH 29/81] Update plugins/node/opentelemetry-instrumentation-undici/.tav.yml Co-authored-by: Trent Mick --- plugins/node/opentelemetry-instrumentation-undici/.tav.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/.tav.yml b/plugins/node/opentelemetry-instrumentation-undici/.tav.yml index 28322615d1..9720cebbca 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/.tav.yml +++ b/plugins/node/opentelemetry-instrumentation-undici/.tav.yml @@ -5,7 +5,4 @@ undici: commands: npm run test - versions: ">=6 <7" node: '>=18' - commands: npm run test - - # Fix missing `contrib-test-utils` package - pretest: npm run --prefix ../../../ lerna:link \ No newline at end of file + commands: npm run test \ No newline at end of file From f9ee701c11f66bc0a06c9846419c62880508a526 Mon Sep 17 00:00:00 2001 From: David Luna Date: Thu, 7 Mar 2024 10:54:45 +0100 Subject: [PATCH 30/81] Update plugins/node/opentelemetry-instrumentation-undici/package.json Co-authored-by: Trent Mick --- plugins/node/opentelemetry-instrumentation-undici/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/package.json b/plugins/node/opentelemetry-instrumentation-undici/package.json index 9702891ac8..fb79298d60 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/package.json +++ b/plugins/node/opentelemetry-instrumentation-undici/package.json @@ -15,7 +15,6 @@ "lint": "eslint . --ext .ts", "lint:fix": "eslint . --ext .ts --fix", "codecov": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../../", - "version": "node ../../../scripts/version-update.js", "watch": "tsc -w", "precompile": "tsc --version && lerna run version:update --scope @opentelemetry/instrumentation-undici --include-dependencies", "prewatch": "npm run precompile", From d9453916445fc75468206a431359faf1990b4e43 Mon Sep 17 00:00:00 2001 From: David Luna Date: Thu, 7 Mar 2024 11:35:01 +0100 Subject: [PATCH 31/81] chore(instrumentation-undici): improve req/res headers capturing --- .../README.md | 5 +- .../src/undici.ts | 69 ++++++++----------- 2 files changed, 30 insertions(+), 44 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/README.md b/plugins/node/opentelemetry-instrumentation-undici/README.md index 7a204dadd5..2e65964e27 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/README.md +++ b/plugins/node/opentelemetry-instrumentation-undici/README.md @@ -5,7 +5,8 @@ **Note: This is an experimental package under active development. New releases may include breaking changes.** -This module provides automatic instrumentation for [`undici`](https://undici.nodejs.org/) and [`fetch`](https://nodejs.org/docs/latest/api/globals.html#fetch). +This module provides automatic instrumentation for [`undici`](https://undici.nodejs.org/) and NodeJs global [`fetch`](https://nodejs.org/docs/latest/api/globals.html#fetch) API. +If you're looking the instrumentation for browser's `fetch` API it is located at https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation-fetch/ ## Installation @@ -39,8 +40,6 @@ registerInstrumentations({ ``` -See [examples/undici](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/examples/undici) for a short example. - ### Undici/Fetch instrumentation Options Undici instrumentation has few options available to choose from. You can set the following: diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts index a62db2a6bb..6c64477e7d 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts @@ -289,23 +289,18 @@ export class UndiciInstrumentation extends InstrumentationBase { // After hooks have been processed (which may modify request headers) // we can collect the headers based on the configuration - const rawHeaders = request.headers.split('\r\n'); - const reqHeaders = new Map( - rawHeaders.map(h => { + if (config.headersToSpanAttributes?.requestHeaders) { + const headersToAttribs = new Set(config.headersToSpanAttributes.requestHeaders.map(n => n.toLowerCase())); + const rawHeaders = request.headers.split('\r\n'); + + rawHeaders.forEach((h) => { const sepIndex = h.indexOf(':'); const name = h.substring(0, sepIndex).toLowerCase(); - const val = h.substring(sepIndex + 1).trim(); - return [name, val]; - }) - ); - if (config.headersToSpanAttributes?.requestHeaders) { - config.headersToSpanAttributes.requestHeaders - .map(name => name.toLowerCase()) - .filter(name => reqHeaders.has(name)) - .forEach(name => { - spanAttributes[`http.request.header.${name}`] = reqHeaders.get(name); - }); + if (headersToAttribs.has(name)) { + spanAttributes[`http.request.header.${name}`] = h.substring(sepIndex + 1).trim(); + } + }); } span.setAttributes(spanAttributes); @@ -324,7 +319,7 @@ export class UndiciInstrumentation extends InstrumentationBase { return; } - const { span, attributes, startTime } = record; + const { span, attributes } = record; // We are currently *not* capturing response headers, even though the // intake API does allow it, because none of the other `setHttpContext` // uses currently do @@ -332,31 +327,27 @@ export class UndiciInstrumentation extends InstrumentationBase { [SemanticAttributes.HTTP_RESPONSE_STATUS_CODE]: response.statusCode, }; - // Get headers with names lowercased but values intact - const resHeaders = new Map(); - for (let idx = 0; idx < response.headers.length; idx = idx + 2) { - resHeaders.set( - response.headers[idx].toString().toLowerCase(), - response.headers[idx + 1].toString() - ); - } - - // Put response headers as attributes based on config const config = this._getConfig(); + const headersToAttribs = new Set(); + if (config.headersToSpanAttributes?.responseHeaders) { - config.headersToSpanAttributes.responseHeaders - .map(name => name.toLowerCase()) - .filter(name => resHeaders.has(name)) - .forEach(name => { - spanAttributes[`http.response.header.${name}`] = resHeaders.get(name); - }); + config.headersToSpanAttributes?.responseHeaders + .forEach(name => headersToAttribs.add(name.toLowerCase())); } - // `content-length` header is a special case - if (resHeaders.has('content-length')) { - const contentLength = Number(resHeaders.get('content-length')); - if (!isNaN(contentLength)) { - spanAttributes['http.response.header.content-length'] = contentLength; + for (let idx = 0; idx < response.headers.length; idx = idx + 2) { + const name = response.headers[idx].toString().toLowerCase(); + const value = response.headers[idx + 1]; + + if (headersToAttribs.has(name)) { + spanAttributes[`http.response.header.${name}`] = value.toString(); + } + + if (name === 'content-length') { + const contentLength = Number(value.toString()); + if (!isNaN(contentLength)) { + spanAttributes['http.response.header.content-length'] = contentLength; + } } } @@ -367,11 +358,7 @@ export class UndiciInstrumentation extends InstrumentationBase { ? SpanStatusCode.ERROR : SpanStatusCode.UNSET, }); - this._recordFromReq.set(request, { - span, - startTime, - attributes: Object.assign(attributes, spanAttributes), - }); + record.attributes = Object.assign(attributes, spanAttributes); } // This is the last event we receive if the request went without any errors From 683a2118783007fa157072b83863e9e6144597f0 Mon Sep 17 00:00:00 2001 From: David Luna Date: Thu, 7 Mar 2024 12:13:03 +0100 Subject: [PATCH 32/81] chore(instrumentation-undici): add request method calculation --- .../src/undici.ts | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts index 6c64477e7d..d96df6d3f5 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts @@ -196,7 +196,8 @@ export class UndiciInstrumentation extends InstrumentationBase { const requestUrl = new URL(request.origin + request.path); const urlScheme = requestUrl.protocol.replace(':', ''); const attributes: Attributes = { - [SemanticAttributes.HTTP_REQUEST_METHOD]: request.method, + [SemanticAttributes.HTTP_REQUEST_METHOD]: this.getRequestMethod(request.method), + [SemanticAttributes.HTTP_REQUEST_METHOD_ORIGINAL]: request.method, [SemanticAttributes.URL_FULL]: requestUrl.toString(), [SemanticAttributes.URL_PATH]: requestUrl.pathname, [SemanticAttributes.URL_QUERY]: requestUrl.search, @@ -444,4 +445,24 @@ export class UndiciInstrumentation extends InstrumentationBase { const duration = hrTimeToMilliseconds(hrTimeDuration(startTime, hrTime())); this._httpClientDurationHistogram.record(duration, metricsAttributes); } + + private getRequestMethod(original: string): string { + const knownMethods = { + CONNECT: true, + OPTIONS: true, + HEAD: true, + GET: true, + POST: true, + PUT: true, + PATCH: true, + DELETE: true, + TRACE: true, + } + + if (original.toUpperCase() in knownMethods) { + return original.toUpperCase() + } + + return '_OTHER'; + } } From 9d01b46fb36fe9fbf9e4b70398994c03fb067c2e Mon Sep 17 00:00:00 2001 From: David Luna Date: Thu, 7 Mar 2024 12:46:23 +0100 Subject: [PATCH 33/81] chore(instrumentation-undici): add request original method attrib --- .../src/undici.ts | 5 +- .../test/undici.test.ts | 61 +++++++++++++++++++ .../test/utils/assertSpan.ts | 3 +- 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts index d96df6d3f5..00df7c00e6 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts @@ -195,8 +195,9 @@ export class UndiciInstrumentation extends InstrumentationBase { const requestUrl = new URL(request.origin + request.path); const urlScheme = requestUrl.protocol.replace(':', ''); + const requestMethod = this.getRequestMethod(request.method); const attributes: Attributes = { - [SemanticAttributes.HTTP_REQUEST_METHOD]: this.getRequestMethod(request.method), + [SemanticAttributes.HTTP_REQUEST_METHOD]: requestMethod, [SemanticAttributes.HTTP_REQUEST_METHOD_ORIGINAL]: request.method, [SemanticAttributes.URL_FULL]: requestUrl.toString(), [SemanticAttributes.URL_PATH]: requestUrl.pathname, @@ -242,7 +243,7 @@ export class UndiciInstrumentation extends InstrumentationBase { span = trace.wrapSpanContext(INVALID_SPAN_CONTEXT); } else { span = this.tracer.startSpan( - `HTTP ${request.method}`, + requestMethod === '_OTHER' ? 'HTTP' : requestMethod, { kind: SpanKind.CLIENT, attributes: attributes, diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts index f8ebdb3f57..4c26d72e58 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts @@ -189,6 +189,67 @@ describe('UndiciInstrumentation `undici` tests', function () { assert.ok(spans.length === 0, 'ignoreRequestHook is filtering requests'); }); + it('should create valid spans for different request methods', async function () { + let spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 0); + + // Do some requests + const headers = { + 'user-agent': 'custom', + 'foo-client': 'bar', + }; + + const queryRequestUrl = `${protocol}://${hostname}:${mockServer.port}/?query=test`; + // @ts-expect-error - method type expects in uppercase + const firstQueryResponse = await undici.request(queryRequestUrl, { headers, method: 'get' }); + await consumeResponseBody(firstQueryResponse.body); + // @ts-expect-error - method type expects known HTTP method (GET, POST, PUT, ...) + const secondQueryResponse = await undici.request(queryRequestUrl, { headers, method: 'custom' }); + await consumeResponseBody(secondQueryResponse.body); + + assert.ok( + firstQueryResponse.headers['propagation-error'] == null, + 'propagation is set for instrumented requests' + ); + assert.ok( + secondQueryResponse.headers['propagation-error'] == null, + 'propagation is set for instrumented requests' + ); + + spans = memoryExporter.getFinishedSpans(); + assert.strictEqual(spans.length, 2); + assertSpan(spans[0], { + hostname: 'localhost', + httpStatusCode: firstQueryResponse.statusCode, + httpMethod: 'GET', + path: '/', + query: '?query=test', + reqHeaders: headers, + resHeaders: firstQueryResponse.headers, + }); + assertSpan(spans[1], { + hostname: 'localhost', + httpStatusCode: secondQueryResponse.statusCode, + spanName: 'HTTP', + httpMethod: '_OTHER', + path: '/', + query: '?query=test', + reqHeaders: headers, + resHeaders: secondQueryResponse.headers, + }); + + assert.strictEqual( + spans[0].attributes['http.request.method_original'], + 'get', + 'request original method is captured' + ); + assert.strictEqual( + spans[1].attributes['http.request.method_original'], + 'custom', + 'request original method is captured' + ); + }); + it('should create valid spans for "request" method', async function () { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/utils/assertSpan.ts b/plugins/node/opentelemetry-instrumentation-undici/test/utils/assertSpan.ts index 1c06975839..f5bcfded0d 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/utils/assertSpan.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/utils/assertSpan.ts @@ -31,6 +31,7 @@ export const assertSpan = ( validations: { httpStatusCode?: number; httpMethod: string; + spanName?: string; resHeaders?: Headers | IncomingHttpHeaders; hostname: string; reqHeaders?: Headers | IncomingHttpHeaders; @@ -46,7 +47,7 @@ export const assertSpan = ( assert.strictEqual(span.kind, SpanKind.CLIENT, 'span.kind is correct'); assert.strictEqual( span.name, - `HTTP ${validations.httpMethod}`, + validations.spanName || validations.httpMethod, 'span.name is correct' ); // TODO: check this From 42ccf94ec6b8093dadc008842f1686086802a046 Mon Sep 17 00:00:00 2001 From: David Luna Date: Thu, 7 Mar 2024 13:04:04 +0100 Subject: [PATCH 34/81] chore(instrumentation-undici): fix lint issues --- .../src/undici.ts | 19 ++++++++++++------- .../test/undici.test.ts | 14 ++++++++++---- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts index 00df7c00e6..4cea36410a 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts @@ -292,15 +292,19 @@ export class UndiciInstrumentation extends InstrumentationBase { // After hooks have been processed (which may modify request headers) // we can collect the headers based on the configuration if (config.headersToSpanAttributes?.requestHeaders) { - const headersToAttribs = new Set(config.headersToSpanAttributes.requestHeaders.map(n => n.toLowerCase())); + const headersToAttribs = new Set( + config.headersToSpanAttributes.requestHeaders.map(n => n.toLowerCase()) + ); const rawHeaders = request.headers.split('\r\n'); - rawHeaders.forEach((h) => { + rawHeaders.forEach(h => { const sepIndex = h.indexOf(':'); const name = h.substring(0, sepIndex).toLowerCase(); if (headersToAttribs.has(name)) { - spanAttributes[`http.request.header.${name}`] = h.substring(sepIndex + 1).trim(); + spanAttributes[`http.request.header.${name}`] = h + .substring(sepIndex + 1) + .trim(); } }); } @@ -333,8 +337,9 @@ export class UndiciInstrumentation extends InstrumentationBase { const headersToAttribs = new Set(); if (config.headersToSpanAttributes?.responseHeaders) { - config.headersToSpanAttributes?.responseHeaders - .forEach(name => headersToAttribs.add(name.toLowerCase())); + config.headersToSpanAttributes?.responseHeaders.forEach(name => + headersToAttribs.add(name.toLowerCase()) + ); } for (let idx = 0; idx < response.headers.length; idx = idx + 2) { @@ -458,10 +463,10 @@ export class UndiciInstrumentation extends InstrumentationBase { PATCH: true, DELETE: true, TRACE: true, - } + }; if (original.toUpperCase() in knownMethods) { - return original.toUpperCase() + return original.toUpperCase(); } return '_OTHER'; diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts index 4c26d72e58..1c39f681c4 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts @@ -200,11 +200,17 @@ describe('UndiciInstrumentation `undici` tests', function () { }; const queryRequestUrl = `${protocol}://${hostname}:${mockServer.port}/?query=test`; - // @ts-expect-error - method type expects in uppercase - const firstQueryResponse = await undici.request(queryRequestUrl, { headers, method: 'get' }); + const firstQueryResponse = await undici.request(queryRequestUrl, { + headers, + // @ts-expect-error - method type expects in uppercase + method: 'get', + }); await consumeResponseBody(firstQueryResponse.body); - // @ts-expect-error - method type expects known HTTP method (GET, POST, PUT, ...) - const secondQueryResponse = await undici.request(queryRequestUrl, { headers, method: 'custom' }); + const secondQueryResponse = await undici.request(queryRequestUrl, { + headers, + // @ts-expect-error - method type expects known HTTP method (GET, POST, PUT, ...) + method: 'custom', + }); await consumeResponseBody(secondQueryResponse.body); assert.ok( From ee5d0f1da9bb1324643639085a2ebcc2bff88f42 Mon Sep 17 00:00:00 2001 From: David Luna Date: Thu, 7 Mar 2024 13:19:31 +0100 Subject: [PATCH 35/81] chore(instrumentation-undici): set duration metric unit to seconds --- .../src/undici.ts | 16 +++------------- .../test/metrics.test.ts | 4 ++-- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts index 4cea36410a..1322a10555 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts @@ -69,16 +69,6 @@ export class UndiciInstrumentation extends InstrumentationBase { private _httpClientDurationHistogram!: Histogram; constructor(config?: UndiciInstrumentationConfig) { super('@opentelemetry/instrumentation-undici', VERSION, config); - // Force load fetch API (since it's lazy loaded in Node 18) - // `fetch` Added in: v17.5.0, v16.15.0 (with flag) and we suport lower verisons - // https://nodejs.org/api/globals.html#fetch - try { - fetch('').catch(() => {}); - } catch (err) { - // TODO: nicer message - diag.info('fetch API not available'); - } - this.setConfig(config); } @@ -137,7 +127,7 @@ export class UndiciInstrumentation extends InstrumentationBase { 'http.client.request.duration', { description: 'Measures the duration of outbound HTTP requests.', - unit: 'ms', + unit: 's', valueType: ValueType.DOUBLE, } ); @@ -448,8 +438,8 @@ export class UndiciInstrumentation extends InstrumentationBase { }); // Take the duration and record it - const duration = hrTimeToMilliseconds(hrTimeDuration(startTime, hrTime())); - this._httpClientDurationHistogram.record(duration, metricsAttributes); + const durationSeconds = hrTimeToMilliseconds(hrTimeDuration(startTime, hrTime())) / 1000; + this._httpClientDurationHistogram.record(durationSeconds, metricsAttributes); } private getRequestMethod(original: string): string { diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/metrics.test.ts b/plugins/node/opentelemetry-instrumentation-undici/test/metrics.test.ts index 97a4989ff7..3a653b40e9 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/metrics.test.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/metrics.test.ts @@ -118,7 +118,7 @@ describe('UndiciInstrumentation metrics tests', function () { metrics[0].descriptor.description, 'Measures the duration of outbound HTTP requests.' ); - assert.strictEqual(metrics[0].descriptor.unit, 'ms'); + assert.strictEqual(metrics[0].descriptor.unit, 's'); assert.strictEqual(metrics[0].dataPointType, DataPointType.HISTOGRAM); assert.strictEqual(metrics[0].dataPoints.length, 1); @@ -169,7 +169,7 @@ describe('UndiciInstrumentation metrics tests', function () { metrics[0].descriptor.description, 'Measures the duration of outbound HTTP requests.' ); - assert.strictEqual(metrics[0].descriptor.unit, 'ms'); + assert.strictEqual(metrics[0].descriptor.unit, 's'); assert.strictEqual(metrics[0].dataPointType, DataPointType.HISTOGRAM); assert.strictEqual(metrics[0].dataPoints.length, 1); From 821ecb698f5457cdbafa0d9dc38beec5959bfd54 Mon Sep 17 00:00:00 2001 From: David Luna Date: Thu, 7 Mar 2024 15:27:26 +0100 Subject: [PATCH 36/81] chore(instrumentation-undici): add suport for string array headers --- package-lock.json | 241 +++++++++++++++++- .../package.json | 2 +- .../src/types.ts | 9 +- .../src/undici.ts | 51 ++-- .../test/fetch.test.ts | 8 +- .../test/undici.test.ts | 8 +- 6 files changed, 283 insertions(+), 36 deletions(-) diff --git a/package-lock.json b/package-lock.json index 282d2b6e90..5a9ff15413 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31775,15 +31775,15 @@ } }, "node_modules/undici": { - "version": "5.28.3", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.3.tgz", - "integrity": "sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.7.0.tgz", + "integrity": "sha512-IcWssIyDN1gk6Mcae44q04oRoWTKrW8OKz0effVK1xdWwAgMPnfpxhn9RXUSL5JlwSikO18R7Ibk7Nukz6kxWA==", "dev": true, "dependencies": { "@fastify/busboy": "^2.0.0" }, "engines": { - "node": ">=14.0" + "node": ">=18.0" } }, "node_modules/undici-types": { @@ -36144,7 +36144,7 @@ "test-all-versions": "6.0.0", "ts-mocha": "10.0.0", "typescript": "4.4.4", - "undici": "^5.28.3" + "undici": "^6.7.0" }, "engines": { "node": ">=14" @@ -36171,6 +36171,95 @@ "@opentelemetry/api": "^1.3.0" } }, + "plugins/node/opentelemetry-instrumentation-undici/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "plugins/node/opentelemetry-instrumentation-undici/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "plugins/node/opentelemetry-instrumentation-undici/node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "plugins/node/opentelemetry-instrumentation-undici/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "plugins/node/opentelemetry-instrumentation-undici/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "plugins/node/opentelemetry-instrumentation-undici/node_modules/is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "dev": true, + "dependencies": { + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "plugins/node/opentelemetry-instrumentation-undici/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "plugins/node/opentelemetry-instrumentation-undici/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -36196,6 +36285,48 @@ "node": ">=10" } }, + "plugins/node/opentelemetry-instrumentation-undici/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "plugins/node/opentelemetry-instrumentation-undici/node_modules/test-all-versions": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-all-versions/-/test-all-versions-6.0.0.tgz", + "integrity": "sha512-/9wVTBRa7+arvItGinCYy/8+z7sHTsrs9cwEY/xAnzrkSEM7Tp2Cz49ewYZYuO1YYMLqxEaQp2g7Dnns7n7BGA==", + "dev": true, + "dependencies": { + "after-all-results": "^2.0.0", + "ansi-diff-stream": "^1.2.1", + "cli-spinners": "^2.9.2", + "deepmerge": "^4.3.1", + "import-fresh": "^3.3.0", + "is-ci": "^3.0.1", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimist": "^1.2.8", + "npm-package-versions": "^1.0.1", + "once": "^1.4.0", + "parse-env-string": "^1.0.1", + "resolve": "^1.22.8", + "semver": "^7.5.4", + "spawn-npm-install": "^1.2.0", + "which": "^2.0.2" + }, + "bin": { + "tav": "index.js" + }, + "engines": { + "node": ">=14" + } + }, "plugins/node/opentelemetry-instrumentation-undici/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -44657,7 +44788,7 @@ "test-all-versions": "6.0.0", "ts-mocha": "10.0.0", "typescript": "4.4.4", - "undici": "^5.28.3" + "undici": "^6.7.0" }, "dependencies": { "@opentelemetry/instrumentation": { @@ -44672,6 +44803,65 @@ "shimmer": "^1.2.1" } }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "dev": true, + "requires": { + "ci-info": "^3.2.0" + } + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -44688,6 +44878,39 @@ "lru-cache": "^6.0.0" } }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "test-all-versions": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-all-versions/-/test-all-versions-6.0.0.tgz", + "integrity": "sha512-/9wVTBRa7+arvItGinCYy/8+z7sHTsrs9cwEY/xAnzrkSEM7Tp2Cz49ewYZYuO1YYMLqxEaQp2g7Dnns7n7BGA==", + "dev": true, + "requires": { + "after-all-results": "^2.0.0", + "ansi-diff-stream": "^1.2.1", + "cli-spinners": "^2.9.2", + "deepmerge": "^4.3.1", + "import-fresh": "^3.3.0", + "is-ci": "^3.0.1", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimist": "^1.2.8", + "npm-package-versions": "^1.0.1", + "once": "^1.4.0", + "parse-env-string": "^1.0.1", + "resolve": "^1.22.8", + "semver": "^7.5.4", + "spawn-npm-install": "^1.2.0", + "which": "^2.0.2" + } + }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -64018,9 +64241,9 @@ } }, "undici": { - "version": "5.28.3", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.3.tgz", - "integrity": "sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.7.0.tgz", + "integrity": "sha512-IcWssIyDN1gk6Mcae44q04oRoWTKrW8OKz0effVK1xdWwAgMPnfpxhn9RXUSL5JlwSikO18R7Ibk7Nukz6kxWA==", "dev": true, "requires": { "@fastify/busboy": "^2.0.0" diff --git a/plugins/node/opentelemetry-instrumentation-undici/package.json b/plugins/node/opentelemetry-instrumentation-undici/package.json index fb79298d60..df10ce7e1a 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/package.json +++ b/plugins/node/opentelemetry-instrumentation-undici/package.json @@ -60,7 +60,7 @@ "test-all-versions": "6.0.0", "ts-mocha": "10.0.0", "typescript": "4.4.4", - "undici": "^5.28.3" + "undici": "^6.7.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/types.ts b/plugins/node/opentelemetry-instrumentation-undici/src/types.ts index 3d35ade577..0d9323dec3 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/types.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/types.ts @@ -21,9 +21,14 @@ export interface UndiciRequest { method: string; path: string; /** - * Serialized string of headers in the form `name: value\r\n` + * Serialized string of headers in the form `name: value\r\n` for v5 + * Array of strings v6 */ - headers: string; + headers: string | string[]; + /** + * Helper method to add headers (from v6) + */ + addHeader: (name: string, value: string) => void; throwOnError: boolean; completed: boolean; aborted: boolean; diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts index 1322a10555..cf4198c44a 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts @@ -173,16 +173,6 @@ export class UndiciInstrumentation extends InstrumentationBase { } const startTime = hrTime(); - const rawHeaders = request.headers.split('\r\n'); - const reqHeaders = new Map( - rawHeaders.map(h => { - const sepIndex = h.indexOf(':'); - const name = h.substring(0, sepIndex).toLowerCase(); - const val = h.substring(sepIndex + 1).trim(); - return [name, val]; - }) - ); - const requestUrl = new URL(request.origin + request.path); const urlScheme = requestUrl.protocol.replace(':', ''); const requestMethod = this.getRequestMethod(request.method); @@ -204,7 +194,17 @@ export class UndiciInstrumentation extends InstrumentationBase { attributes[SemanticAttributes.SERVER_PORT] = Number(serverPort); } - const userAgent = reqHeaders.get('user-agent'); + // Get user agent from headers + let userAgent; + if (Array.isArray(request.headers)) { + const idx = request.headers.findIndex(h => h.toLowerCase() === 'user-agent'); + userAgent = request.headers[idx + 1]; + } else if (typeof request.headers === 'string') { + const headers = request.headers.split('\r\n'); + const uaHeader = headers.find(h => h.toLowerCase().startsWith('user-agent')); + userAgent = uaHeader && uaHeader.substring(uaHeader.indexOf(':') + 1).trim(); + } + if (userAgent) { attributes[SemanticAttributes.USER_AGENT_ORIGINAL] = userAgent; } @@ -255,9 +255,17 @@ export class UndiciInstrumentation extends InstrumentationBase { const addedHeaders: Record = {}; propagation.inject(requestContext, addedHeaders); - request.headers += Object.entries(addedHeaders) - .map(([k, v]) => `${k}: ${v}\r\n`) - .join(''); + const headerEntries = Object.entries(addedHeaders); + + for (let i = 0; i < headerEntries.length; i++) { + const [k, v] = headerEntries[i]; + + if (typeof request.headers === 'string') { + request.headers += `${k}: ${v}\r\n`; + } else { + request.addHeader(k, v); + } + } this._recordFromReq.set(request, { span, attributes, startTime }); } @@ -285,16 +293,19 @@ export class UndiciInstrumentation extends InstrumentationBase { const headersToAttribs = new Set( config.headersToSpanAttributes.requestHeaders.map(n => n.toLowerCase()) ); - const rawHeaders = request.headers.split('\r\n'); - rawHeaders.forEach(h => { + // headers could be in form + // ['name: value', ...] for v5 + // ['name', 'value', ...] for v6 + const rawHeaders = Array.isArray(request.headers) ? request.headers : request.headers.split('\r\n'); + rawHeaders.forEach((h, idx) => { const sepIndex = h.indexOf(':'); - const name = h.substring(0, sepIndex).toLowerCase(); + const hasSeparator = sepIndex !== -1; + const name = ((hasSeparator) ? h.substring(0, sepIndex) : h).toLowerCase(); + const value = hasSeparator ? h.substring(sepIndex + 1) : rawHeaders[idx + 1]; if (headersToAttribs.has(name)) { - spanAttributes[`http.request.header.${name}`] = h - .substring(sepIndex + 1) - .trim(); + spanAttributes[`http.request.header.${name}`] = value.trim(); } }); } diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts b/plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts index 89d8304863..f7e4321198 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts @@ -209,8 +209,12 @@ describe('UndiciInstrumentation `fetch` tests', function () { return req.path.indexOf('/ignore/path') !== -1; }, requestHook: (span, req) => { - // TODO: maybe an intermediate request with better API - req.headers += 'x-requested-with: undici\r\n'; + // We should mind the type of headers + if (typeof req.headers === 'string') { + req.headers += 'x-requested-with: undici\r\n'; + } else { + req.headers.push('x-requested-with', 'undici'); + } }, startSpanHook: request => { return { diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts index 1c39f681c4..117438c455 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts @@ -142,8 +142,12 @@ describe('UndiciInstrumentation `undici` tests', function () { return req.path.indexOf('/ignore/path') !== -1; }, requestHook: (span, req) => { - // TODO: maybe an intermediate request with better API - req.headers += 'x-requested-with: undici\r\n'; + // We should mind the type of headers + if (typeof req.headers === 'string') { + req.headers += 'x-requested-with: undici\r\n'; + } else { + req.headers.push('x-requested-with', 'undici'); + } }, startSpanHook: request => { return { From 788f6439eaa20543303b27f74a344652abeebbdf Mon Sep 17 00:00:00 2001 From: David Luna Date: Thu, 7 Mar 2024 15:30:17 +0100 Subject: [PATCH 37/81] chore(instrumentation-undici): add trentm as component owner --- .github/component_owners.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/component_owners.yml b/.github/component_owners.yml index b3d3cb21e6..a6624ffda2 100644 --- a/.github/component_owners.yml +++ b/.github/component_owners.yml @@ -116,6 +116,7 @@ components: - rauno56 plugins/node/opentelemetry-instrumentation-undici: - david-luna + - trentm plugins/node/opentelemetry-instrumentation-winston: - seemk plugins/web/opentelemetry-instrumentation-document-load: From e780be71bb72ae869b84ad6eeaf768671eec310c Mon Sep 17 00:00:00 2001 From: David Luna Date: Thu, 7 Mar 2024 15:31:00 +0100 Subject: [PATCH 38/81] chore(instrumentation-undici): fix lint issues --- .../src/undici.ts | 35 +++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts index cf4198c44a..c17e324874 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts @@ -197,14 +197,19 @@ export class UndiciInstrumentation extends InstrumentationBase { // Get user agent from headers let userAgent; if (Array.isArray(request.headers)) { - const idx = request.headers.findIndex(h => h.toLowerCase() === 'user-agent'); + const idx = request.headers.findIndex( + h => h.toLowerCase() === 'user-agent' + ); userAgent = request.headers[idx + 1]; } else if (typeof request.headers === 'string') { const headers = request.headers.split('\r\n'); - const uaHeader = headers.find(h => h.toLowerCase().startsWith('user-agent')); - userAgent = uaHeader && uaHeader.substring(uaHeader.indexOf(':') + 1).trim(); + const uaHeader = headers.find(h => + h.toLowerCase().startsWith('user-agent') + ); + userAgent = + uaHeader && uaHeader.substring(uaHeader.indexOf(':') + 1).trim(); } - + if (userAgent) { attributes[SemanticAttributes.USER_AGENT_ORIGINAL] = userAgent; } @@ -294,15 +299,21 @@ export class UndiciInstrumentation extends InstrumentationBase { config.headersToSpanAttributes.requestHeaders.map(n => n.toLowerCase()) ); - // headers could be in form + // headers could be in form // ['name: value', ...] for v5 // ['name', 'value', ...] for v6 - const rawHeaders = Array.isArray(request.headers) ? request.headers : request.headers.split('\r\n'); + const rawHeaders = Array.isArray(request.headers) + ? request.headers + : request.headers.split('\r\n'); rawHeaders.forEach((h, idx) => { const sepIndex = h.indexOf(':'); const hasSeparator = sepIndex !== -1; - const name = ((hasSeparator) ? h.substring(0, sepIndex) : h).toLowerCase(); - const value = hasSeparator ? h.substring(sepIndex + 1) : rawHeaders[idx + 1]; + const name = ( + hasSeparator ? h.substring(0, sepIndex) : h + ).toLowerCase(); + const value = hasSeparator + ? h.substring(sepIndex + 1) + : rawHeaders[idx + 1]; if (headersToAttribs.has(name)) { spanAttributes[`http.request.header.${name}`] = value.trim(); @@ -449,8 +460,12 @@ export class UndiciInstrumentation extends InstrumentationBase { }); // Take the duration and record it - const durationSeconds = hrTimeToMilliseconds(hrTimeDuration(startTime, hrTime())) / 1000; - this._httpClientDurationHistogram.record(durationSeconds, metricsAttributes); + const durationSeconds = + hrTimeToMilliseconds(hrTimeDuration(startTime, hrTime())) / 1000; + this._httpClientDurationHistogram.record( + durationSeconds, + metricsAttributes + ); } private getRequestMethod(original: string): string { From 856cff1574458a085e5a939c566d7978e79f5994 Mon Sep 17 00:00:00 2001 From: David Luna Date: Thu, 7 Mar 2024 15:43:16 +0100 Subject: [PATCH 39/81] chore(instrumentation-undici): fix lint issues --- plugins/node/opentelemetry-instrumentation-undici/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/README.md b/plugins/node/opentelemetry-instrumentation-undici/README.md index 2e65964e27..ad81cfd5ff 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/README.md +++ b/plugins/node/opentelemetry-instrumentation-undici/README.md @@ -6,7 +6,7 @@ **Note: This is an experimental package under active development. New releases may include breaking changes.** This module provides automatic instrumentation for [`undici`](https://undici.nodejs.org/) and NodeJs global [`fetch`](https://nodejs.org/docs/latest/api/globals.html#fetch) API. -If you're looking the instrumentation for browser's `fetch` API it is located at https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation-fetch/ +If you're looking the instrumentation for browser's `fetch` API it is located at [https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation-fetch/](https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation-fetch/) ## Installation From eeaed5796d74337f9d5f3271dc27f39e1700bf76 Mon Sep 17 00:00:00 2001 From: David Luna Date: Thu, 7 Mar 2024 15:44:19 +0100 Subject: [PATCH 40/81] chore(instrumentation-undici): fix compiling issues --- plugins/node/opentelemetry-instrumentation-undici/src/undici.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts index c17e324874..0e8efc2965 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts @@ -23,7 +23,6 @@ import { import { Attributes, context, - diag, Histogram, HrTime, INVALID_SPAN_CONTEXT, From 8b58516f3265645a4f1bb99cb24f83a1955a1359 Mon Sep 17 00:00:00 2001 From: David Luna Date: Fri, 8 Mar 2024 10:16:19 +0100 Subject: [PATCH 41/81] chore(instrumentation-undici): lazy load undici in tests --- .../test/undici.test.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts index 117438c455..0c43ebff55 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts @@ -36,12 +36,14 @@ import { MockPropagation } from './utils/mock-propagation'; import { MockServer } from './utils/mock-server'; import { assertSpan } from './utils/assertSpan'; +import type {fetch, stream, request, Client, Dispatcher } from 'undici'; + const instrumentation = new UndiciInstrumentation(); instrumentation.enable(); instrumentation.disable(); -import type { Dispatcher } from 'undici'; -import * as undici from 'undici'; +// Reference to the `undici` module +let undici: { fetch: typeof fetch; request: typeof request; stream: typeof stream; Client: typeof Client; }; const protocol = 'http'; const hostname = 'localhost'; @@ -67,6 +69,14 @@ async function consumeResponseBody(body: Dispatcher.ResponseData['body']) { describe('UndiciInstrumentation `undici` tests', function () { before(function (done) { + // Load `undici`. It may fail if nodejs version is <18 because the module uses + // features only available from that version. In that case skip the test. + try { + undici = require('undici'); + } catch (loadErr) { + this.skip(); + } + propagation.setGlobalPropagator(new MockPropagation()); context.setGlobalContextManager(new AsyncHooksContextManager().enable()); mockServer.start(done); From 3519b943e60e63e2301d305ceefc58242fc420fb Mon Sep 17 00:00:00 2001 From: David Luna Date: Fri, 8 Mar 2024 11:08:22 +0100 Subject: [PATCH 42/81] chore(instrumentation-undici): skip bad method request test for undici@5.x --- .../test/undici.test.ts | 67 ++++++++++++------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts index 0c43ebff55..52b924bc8a 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts @@ -213,26 +213,43 @@ describe('UndiciInstrumentation `undici` tests', function () { 'foo-client': 'bar', }; - const queryRequestUrl = `${protocol}://${hostname}:${mockServer.port}/?query=test`; - const firstQueryResponse = await undici.request(queryRequestUrl, { - headers, - // @ts-expect-error - method type expects in uppercase - method: 'get', - }); - await consumeResponseBody(firstQueryResponse.body); - const secondQueryResponse = await undici.request(queryRequestUrl, { - headers, - // @ts-expect-error - method type expects known HTTP method (GET, POST, PUT, ...) - method: 'custom', - }); - await consumeResponseBody(secondQueryResponse.body); + // In version v5 if `undici` you get the following error when requesting with a method + // that is not one of the known ones in uppercase. Using + // + // SocketError: other side closed + // at Socket.onSocketEnd (node_modules/undici/lib/client.js:1118:22) + // at endReadableNT (internal/streams/readable.js:1333:12) + // at processTicksAndRejections (internal/process/task_queues.js:82:21) + let firstQueryResponse, secondQueryResponse; + try { + const queryRequestUrl = `${protocol}://${hostname}:${mockServer.port}/?query=test`; + firstQueryResponse = await undici.request(queryRequestUrl, { + headers, + // @ts-expect-error - method type expects in uppercase + method: 'get', + }); + await consumeResponseBody(firstQueryResponse.body); + + secondQueryResponse = await undici.request(queryRequestUrl, { + headers, + // @ts-expect-error - method type expects known HTTP method (GET, POST, PUT, ...) + method: 'custom', + }); + await consumeResponseBody(secondQueryResponse.body); + } catch (undiciErr) { + const { stack } = undiciErr as Error; + + if (stack?.startsWith('SocketError: other side closed')) { + this.skip(); + } + } assert.ok( - firstQueryResponse.headers['propagation-error'] == null, + firstQueryResponse!.headers['propagation-error'] === undefined, 'propagation is set for instrumented requests' ); assert.ok( - secondQueryResponse.headers['propagation-error'] == null, + secondQueryResponse!.headers['propagation-error'] === undefined, 'propagation is set for instrumented requests' ); @@ -240,29 +257,29 @@ describe('UndiciInstrumentation `undici` tests', function () { assert.strictEqual(spans.length, 2); assertSpan(spans[0], { hostname: 'localhost', - httpStatusCode: firstQueryResponse.statusCode, + httpStatusCode: firstQueryResponse!.statusCode, httpMethod: 'GET', path: '/', query: '?query=test', reqHeaders: headers, - resHeaders: firstQueryResponse.headers, + resHeaders: firstQueryResponse!.headers, }); + assert.strictEqual( + spans[0].attributes['http.request.method_original'], + 'get', + 'request original method is captured' + ); + assertSpan(spans[1], { hostname: 'localhost', - httpStatusCode: secondQueryResponse.statusCode, + httpStatusCode: secondQueryResponse!.statusCode, spanName: 'HTTP', httpMethod: '_OTHER', path: '/', query: '?query=test', reqHeaders: headers, - resHeaders: secondQueryResponse.headers, + resHeaders: secondQueryResponse!.headers, }); - - assert.strictEqual( - spans[0].attributes['http.request.method_original'], - 'get', - 'request original method is captured' - ); assert.strictEqual( spans[1].attributes['http.request.method_original'], 'custom', From 2b7a8a3f9f001d9156da44bb4451d4f6966f577f Mon Sep 17 00:00:00 2001 From: David Luna Date: Fri, 8 Mar 2024 11:18:27 +0100 Subject: [PATCH 43/81] chore(instrumentation-undici): fix lint issues --- .../test/undici.test.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts index 52b924bc8a..16132656f2 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts @@ -36,14 +36,19 @@ import { MockPropagation } from './utils/mock-propagation'; import { MockServer } from './utils/mock-server'; import { assertSpan } from './utils/assertSpan'; -import type {fetch, stream, request, Client, Dispatcher } from 'undici'; +import type { fetch, stream, request, Client, Dispatcher } from 'undici'; const instrumentation = new UndiciInstrumentation(); instrumentation.enable(); instrumentation.disable(); // Reference to the `undici` module -let undici: { fetch: typeof fetch; request: typeof request; stream: typeof stream; Client: typeof Client; }; +let undici: { + fetch: typeof fetch; + request: typeof request; + stream: typeof stream; + Client: typeof Client; +}; const protocol = 'http'; const hostname = 'localhost'; @@ -214,7 +219,7 @@ describe('UndiciInstrumentation `undici` tests', function () { }; // In version v5 if `undici` you get the following error when requesting with a method - // that is not one of the known ones in uppercase. Using + // that is not one of the known ones in uppercase. Using // // SocketError: other side closed // at Socket.onSocketEnd (node_modules/undici/lib/client.js:1118:22) From 98419a7bd212c57e5f686d8883d7671ac7f19210 Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 12 Mar 2024 09:53:48 +0100 Subject: [PATCH 44/81] chore(instrumentation-undici): fix compile issue in tests --- .../opentelemetry-instrumentation-undici/test/undici.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts index 16132656f2..1ddcd84987 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts @@ -38,6 +38,8 @@ import { assertSpan } from './utils/assertSpan'; import type { fetch, stream, request, Client, Dispatcher } from 'undici'; +type PromisedValue = T extends Promise ? R : never; + const instrumentation = new UndiciInstrumentation(); instrumentation.enable(); instrumentation.disable(); @@ -225,7 +227,8 @@ describe('UndiciInstrumentation `undici` tests', function () { // at Socket.onSocketEnd (node_modules/undici/lib/client.js:1118:22) // at endReadableNT (internal/streams/readable.js:1333:12) // at processTicksAndRejections (internal/process/task_queues.js:82:21) - let firstQueryResponse, secondQueryResponse; + let firstQueryResponse: PromisedValue>; + let secondQueryResponse: PromisedValue>; try { const queryRequestUrl = `${protocol}://${hostname}:${mockServer.port}/?query=test`; firstQueryResponse = await undici.request(queryRequestUrl, { From f5c52b899111a43353ee805e8590ca7536548e24 Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 12 Mar 2024 10:19:47 +0100 Subject: [PATCH 45/81] chore(instrumentation-mongodb): update package-lock.json --- package-lock.json | 42 +++++++++++++++--------------------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/package-lock.json b/package-lock.json index 80a90df7fb..9218a1a9e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5068,15 +5068,6 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "node_modules/@fastify/busboy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", - "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", - "dev": true, - "engines": { - "node": ">=14" - } - }, "node_modules/@fastify/error": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz", @@ -12432,12 +12423,6 @@ "node": ">=0.10.0" } }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true - }, "node_modules/ascli": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz", @@ -34306,6 +34291,15 @@ "ieee754": "^1.1.13" } }, + "node_modules/undici": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.7.1.tgz", + "integrity": "sha512-+Wtb9bAQw6HYWzCnxrPTMVEV3Q1QjYanI0E4q02ehReMuquQdLTEFEYbfs7hcImVYKcQkWSwT6buEmSVIiDDtQ==", + "dev": true, + "engines": { + "node": ">=18.0" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -43266,12 +43260,6 @@ } } }, - "@fastify/busboy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", - "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", - "dev": true - }, "@fastify/error": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz", @@ -51323,12 +51311,6 @@ "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", "dev": true }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true - }, "ascli": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz", @@ -68602,6 +68584,12 @@ } } }, + "undici": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.7.1.tgz", + "integrity": "sha512-+Wtb9bAQw6HYWzCnxrPTMVEV3Q1QjYanI0E4q02ehReMuquQdLTEFEYbfs7hcImVYKcQkWSwT6buEmSVIiDDtQ==", + "dev": true + }, "undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", From 260723b9e20cb46626723af3fadd4071c4e20e5d Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 12 Mar 2024 23:07:52 +0100 Subject: [PATCH 46/81] Update plugins/node/opentelemetry-instrumentation-undici/package.json Co-authored-by: Trent Mick --- .../node/opentelemetry-instrumentation-undici/package.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/package.json b/plugins/node/opentelemetry-instrumentation-undici/package.json index df10ce7e1a..dec2cbc99a 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/package.json +++ b/plugins/node/opentelemetry-instrumentation-undici/package.json @@ -37,10 +37,7 @@ "files": [ "build/src/**/*.js", "build/src/**/*.js.map", - "build/src/**/*.d.ts", - "doc", - "LICENSE", - "README.md" + "build/src/**/*.d.ts" ], "publishConfig": { "access": "public" From 91adf8fa96aa29dcc04cf98fe2d22093f0c3027e Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 12 Mar 2024 23:08:42 +0100 Subject: [PATCH 47/81] Update plugins/node/opentelemetry-instrumentation-undici/package.json Co-authored-by: Trent Mick --- plugins/node/opentelemetry-instrumentation-undici/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/package.json b/plugins/node/opentelemetry-instrumentation-undici/package.json index dec2cbc99a..c67f2dd698 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/package.json +++ b/plugins/node/opentelemetry-instrumentation-undici/package.json @@ -64,7 +64,7 @@ }, "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.48.0" + "@opentelemetry/instrumentation": "^0.49.1" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/node/opentelemetry-instrumentation-undici#readme", "sideEffects": false From 914f692e2d7833a77d97bdc782607dab4f09b7e3 Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 12 Mar 2024 23:11:38 +0100 Subject: [PATCH 48/81] fix(instrumentation-undici): update package-lock.json --- package-lock.json | 85 ++--------------------------------------------- 1 file changed, 2 insertions(+), 83 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9218a1a9e3..74eb976992 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38686,7 +38686,7 @@ "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.48.0" + "@opentelemetry/instrumentation": "^0.49.1" }, "devDependencies": { "@opentelemetry/api": "^1.3.0", @@ -38712,24 +38712,6 @@ "@opentelemetry/api": "^1.3.0" } }, - "plugins/node/opentelemetry-instrumentation-undici/node_modules/@opentelemetry/instrumentation": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.48.0.tgz", - "integrity": "sha512-sjtZQB5PStIdCw5ovVTDGwnmQC+GGYArJNgIcydrDSqUTdYBnMrN9P4pwQZgS3vTGIp+TU1L8vMXGe51NVmIKQ==", - "dependencies": { - "@types/shimmer": "^1.0.2", - "import-in-the-middle": "1.7.1", - "require-in-the-middle": "^7.1.1", - "semver": "^7.5.2", - "shimmer": "^1.2.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, "plugins/node/opentelemetry-instrumentation-undici/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -38819,31 +38801,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "plugins/node/opentelemetry-instrumentation-undici/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "plugins/node/opentelemetry-instrumentation-undici/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "plugins/node/opentelemetry-instrumentation-undici/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -38886,11 +38843,6 @@ "node": ">=14" } }, - "plugins/node/opentelemetry-instrumentation-undici/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "plugins/node/opentelemetry-instrumentation-winston": { "name": "@opentelemetry/instrumentation-winston", "version": "0.35.0", @@ -47492,7 +47444,7 @@ "requires": { "@opentelemetry/api": "^1.3.0", "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.48.0", + "@opentelemetry/instrumentation": "^0.49.1", "@opentelemetry/sdk-metrics": "^1.8.0", "@opentelemetry/sdk-trace-base": "^1.8.0", "@opentelemetry/sdk-trace-node": "^1.8.0", @@ -47509,18 +47461,6 @@ "undici": "^6.7.0" }, "dependencies": { - "@opentelemetry/instrumentation": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.48.0.tgz", - "integrity": "sha512-sjtZQB5PStIdCw5ovVTDGwnmQC+GGYArJNgIcydrDSqUTdYBnMrN9P4pwQZgS3vTGIp+TU1L8vMXGe51NVmIKQ==", - "requires": { - "@types/shimmer": "^1.0.2", - "import-in-the-middle": "1.7.1", - "require-in-the-middle": "^7.1.1", - "semver": "^7.5.2", - "shimmer": "^1.2.1" - } - }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -47580,22 +47520,6 @@ "is-unicode-supported": "^0.1.0" } }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "requires": { - "lru-cache": "^6.0.0" - } - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -47628,11 +47552,6 @@ "spawn-npm-install": "^1.2.0", "which": "^2.0.2" } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, From b1ece2ae7769454691a2f4ef9d0d0d3415272c31 Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 12 Mar 2024 23:26:54 +0100 Subject: [PATCH 49/81] chore(instrumentation-undici): remove comment --- .../node/opentelemetry-instrumentation-undici/src/undici.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts index 0e8efc2965..27e652e340 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts +++ b/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts @@ -337,9 +337,6 @@ export class UndiciInstrumentation extends InstrumentationBase { } const { span, attributes } = record; - // We are currently *not* capturing response headers, even though the - // intake API does allow it, because none of the other `setHttpContext` - // uses currently do const spanAttributes: Attributes = { [SemanticAttributes.HTTP_RESPONSE_STATUS_CODE]: response.statusCode, }; From 1196e2ceb29d869d88e4fcbd4693c64eb5b5af11 Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 13 Mar 2024 10:38:41 +0100 Subject: [PATCH 50/81] chore(instrumentation-undici): update README.md --- .../README.md | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/plugins/node/opentelemetry-instrumentation-undici/README.md b/plugins/node/opentelemetry-instrumentation-undici/README.md index ad81cfd5ff..d6acd0e061 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/README.md +++ b/plugins/node/opentelemetry-instrumentation-undici/README.md @@ -53,6 +53,33 @@ Undici instrumentation has few options available to choose from. You can set the | [`requireParentforSpans`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#69) | `Boolean` | Require a parent span is present to create new span for outgoing requests. | | [`headersToSpanAttributes`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#71) | `Object` | List of case insensitive HTTP headers to convert to span attributes. Headers will be converted to span attributes in the form of `http.{request\|response}.header.header-name` where the name is only lowercased, e.g. `http.response.header.content-length`| +## Semantic Conventions + +This package uses Semantic Conventions [Version 1.24.0](https://github.com/open-telemetry/semantic-conventions/tree/v1.24.0/docs/http). As for now the Semantic Conventions +are bundled in this package but eventually will be imported from `@opentelemetry/semantic-conventions` package when it is updated to latests version. +Ref: https://github.com/open-telemetry/opentelemetry-js/issues/4235 + +Attributes collected: + +| Attribute | Short Description | Notes | +| ------------ | ---------------------------------- | ----------------- | +| `http.request.method` | HTTP request method. | Key: `HTTP_REQUEST_METHOD` | +| `http.request.method_original` | Original HTTP method sent by the client in the request line. | Key: `HTTP_REQUEST_METHOD_ORIGINAL` | +| `url.full` | Absolute URL describing a network resource according to [RFC3986](https://www.rfc-editor.org/rfc/rfc3986). | Key: `URL_FULL` | +| `url.path` | The [URI path](https://www.rfc-editor.org/rfc/rfc3986#section-3.3) component. | Key: `URL_PATH` | +| `url.query` | The [URI query](https://www.rfc-editor.org/rfc/rfc3986#section-3.4) component. | Key: `URL_QUERY` | +| `url.scheme` | HTTP request method. | Key: `URL_SCHEME` | +| `server.address` | Server domain name, IP address or Unix domain socket name. | Key: `HTTP_REQUEST_METHOD` | +| `server.port` | Server port number. | Key: `HTTP_REQUEST_METHOD` | +| `user_agent.original` | Value of the HTTP User-Agent header sent by the client. | Key: `USER_AGENT_ORIGINAL` | +| `network.peer.address` | Peer address of the network connection - IP address or Unix domain socket name. | Key: `NETWORK_PEER_ADDRESS` | +| `network.peer.port` | Peer port number of the network connection. | Key: `NETWORK_PEER_PORT` | +| `http.response.status_code` | [HTTP response status code](https://tools.ietf.org/html/rfc7231#section-6). | Key: `HTTP_RESPONSE_STATUS_CODE` | +| `error.type` | Describes a class of error the operation ended with. | Key: `ERROR_TYPE` | + + + + ## Useful links - For more information on OpenTelemetry, visit: From 3cdc2e436660576393c358ef556cbe30a5f0c300 Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 13 Mar 2024 10:43:06 +0100 Subject: [PATCH 51/81] chore(instrumentation-undici): update README.md --- plugins/node/opentelemetry-instrumentation-undici/README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/README.md b/plugins/node/opentelemetry-instrumentation-undici/README.md index d6acd0e061..605da7bd2a 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/README.md +++ b/plugins/node/opentelemetry-instrumentation-undici/README.md @@ -57,7 +57,7 @@ Undici instrumentation has few options available to choose from. You can set the This package uses Semantic Conventions [Version 1.24.0](https://github.com/open-telemetry/semantic-conventions/tree/v1.24.0/docs/http). As for now the Semantic Conventions are bundled in this package but eventually will be imported from `@opentelemetry/semantic-conventions` package when it is updated to latests version. -Ref: https://github.com/open-telemetry/opentelemetry-js/issues/4235 +Ref: [opentelemetry-js/issues/4235](https://github.com/open-telemetry/opentelemetry-js/issues/4235) Attributes collected: @@ -77,9 +77,6 @@ Attributes collected: | `http.response.status_code` | [HTTP response status code](https://tools.ietf.org/html/rfc7231#section-6). | Key: `HTTP_RESPONSE_STATUS_CODE` | | `error.type` | Describes a class of error the operation ended with. | Key: `ERROR_TYPE` | - - - ## Useful links - For more information on OpenTelemetry, visit: From a3ec3c4294c392554fb2ce0007ad2fa3ccc97623 Mon Sep 17 00:00:00 2001 From: David Luna Date: Thu, 14 Mar 2024 19:41:44 +0100 Subject: [PATCH 52/81] test(instrumentation-undici): collect coverage for tests ran in node v18 --- .github/workflows/unit-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index af0bd5cdc5..16e44a4a2c 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -11,7 +11,7 @@ jobs: matrix: node: ["14", "16", "18.18.2"] include: - - node: 14 + - node: 18 code-coverage: true runs-on: ubuntu-latest services: From ae40ade3d122a706cbd127554983b67a7fe9d009 Mon Sep 17 00:00:00 2001 From: David Luna Date: Tue, 19 Mar 2024 14:38:50 +0100 Subject: [PATCH 53/81] Update .github/workflows/unit-test.yml Co-authored-by: Jamie Danielson --- .github/workflows/unit-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 16e44a4a2c..025b727e9b 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -11,7 +11,7 @@ jobs: matrix: node: ["14", "16", "18.18.2"] include: - - node: 18 + - node: 18.18.2 code-coverage: true runs-on: ubuntu-latest services: From 2f25acd4371c7187f95e5467b447748e43f7cc57 Mon Sep 17 00:00:00 2001 From: David Luna Date: Mon, 25 Mar 2024 15:46:09 +0100 Subject: [PATCH 54/81] Update plugins/node/opentelemetry-instrumentation-undici/package.json Co-authored-by: Marc Pichler --- plugins/node/opentelemetry-instrumentation-undici/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/node/opentelemetry-instrumentation-undici/package.json b/plugins/node/opentelemetry-instrumentation-undici/package.json index c67f2dd698..f99e66dadb 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/package.json +++ b/plugins/node/opentelemetry-instrumentation-undici/package.json @@ -26,7 +26,6 @@ "undici", "nodejs", "tracing", - "profiling", "instrumentation" ], "author": "OpenTelemetry Authors", From b91771d7dda45b9b31feeb9ce4918d4c3fb0e301 Mon Sep 17 00:00:00 2001 From: David Luna Date: Mon, 25 Mar 2024 16:29:16 +0100 Subject: [PATCH 55/81] chore(instrumentation-undici): rename folder --- .release-please-manifest.json | 2 +- .../.eslintignore | 0 .../.eslintrc.js | 0 .../.tav.yml | 0 .../LICENSE | 0 .../README.md | 0 .../package.json | 4 ++-- .../src/enums/SemanticAttributes.ts | 0 .../src/index.ts | 0 .../src/internal-types.ts | 0 .../src/types.ts | 0 .../src/undici.ts | 0 .../test/fetch.test.ts | 0 .../test/metrics.test.ts | 0 .../test/undici.test.ts | 0 .../test/utils/assertSpan.ts | 6 ++---- .../test/utils/mock-metrics-reader.ts | 0 .../test/utils/mock-propagation.ts | 0 .../test/utils/mock-server.ts | 0 .../tsconfig.json | 0 20 files changed, 5 insertions(+), 7 deletions(-) rename plugins/node/{opentelemetry-instrumentation-undici => instrumentation-undici}/.eslintignore (100%) rename plugins/node/{opentelemetry-instrumentation-undici => instrumentation-undici}/.eslintrc.js (100%) rename plugins/node/{opentelemetry-instrumentation-undici => instrumentation-undici}/.tav.yml (100%) rename plugins/node/{opentelemetry-instrumentation-undici => instrumentation-undici}/LICENSE (100%) rename plugins/node/{opentelemetry-instrumentation-undici => instrumentation-undici}/README.md (100%) rename plugins/node/{opentelemetry-instrumentation-undici => instrumentation-undici}/package.json (97%) rename plugins/node/{opentelemetry-instrumentation-undici => instrumentation-undici}/src/enums/SemanticAttributes.ts (100%) rename plugins/node/{opentelemetry-instrumentation-undici => instrumentation-undici}/src/index.ts (100%) rename plugins/node/{opentelemetry-instrumentation-undici => instrumentation-undici}/src/internal-types.ts (100%) rename plugins/node/{opentelemetry-instrumentation-undici => instrumentation-undici}/src/types.ts (100%) rename plugins/node/{opentelemetry-instrumentation-undici => instrumentation-undici}/src/undici.ts (100%) rename plugins/node/{opentelemetry-instrumentation-undici => instrumentation-undici}/test/fetch.test.ts (100%) rename plugins/node/{opentelemetry-instrumentation-undici => instrumentation-undici}/test/metrics.test.ts (100%) rename plugins/node/{opentelemetry-instrumentation-undici => instrumentation-undici}/test/undici.test.ts (100%) rename plugins/node/{opentelemetry-instrumentation-undici => instrumentation-undici}/test/utils/assertSpan.ts (96%) rename plugins/node/{opentelemetry-instrumentation-undici => instrumentation-undici}/test/utils/mock-metrics-reader.ts (100%) rename plugins/node/{opentelemetry-instrumentation-undici => instrumentation-undici}/test/utils/mock-propagation.ts (100%) rename plugins/node/{opentelemetry-instrumentation-undici => instrumentation-undici}/test/utils/mock-server.ts (100%) rename plugins/node/{opentelemetry-instrumentation-undici => instrumentation-undici}/tsconfig.json (100%) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 8752ada998..6f71c7a7a2 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -49,7 +49,7 @@ "plugins/node/opentelemetry-instrumentation-redis-4": "0.37.0", "plugins/node/opentelemetry-instrumentation-restify": "0.36.0", "plugins/node/opentelemetry-instrumentation-router": "0.35.0", - "plugins/node/opentelemetry-instrumentation-undici": "0.36.0", + "plugins/node/opentelemetry-instrumentation-undici": "0.1.0", "plugins/node/opentelemetry-instrumentation-winston": "0.35.0", "plugins/web/opentelemetry-instrumentation-document-load": "0.36.0", "plugins/web/opentelemetry-instrumentation-long-task": "0.36.0", diff --git a/plugins/node/opentelemetry-instrumentation-undici/.eslintignore b/plugins/node/instrumentation-undici/.eslintignore similarity index 100% rename from plugins/node/opentelemetry-instrumentation-undici/.eslintignore rename to plugins/node/instrumentation-undici/.eslintignore diff --git a/plugins/node/opentelemetry-instrumentation-undici/.eslintrc.js b/plugins/node/instrumentation-undici/.eslintrc.js similarity index 100% rename from plugins/node/opentelemetry-instrumentation-undici/.eslintrc.js rename to plugins/node/instrumentation-undici/.eslintrc.js diff --git a/plugins/node/opentelemetry-instrumentation-undici/.tav.yml b/plugins/node/instrumentation-undici/.tav.yml similarity index 100% rename from plugins/node/opentelemetry-instrumentation-undici/.tav.yml rename to plugins/node/instrumentation-undici/.tav.yml diff --git a/plugins/node/opentelemetry-instrumentation-undici/LICENSE b/plugins/node/instrumentation-undici/LICENSE similarity index 100% rename from plugins/node/opentelemetry-instrumentation-undici/LICENSE rename to plugins/node/instrumentation-undici/LICENSE diff --git a/plugins/node/opentelemetry-instrumentation-undici/README.md b/plugins/node/instrumentation-undici/README.md similarity index 100% rename from plugins/node/opentelemetry-instrumentation-undici/README.md rename to plugins/node/instrumentation-undici/README.md diff --git a/plugins/node/opentelemetry-instrumentation-undici/package.json b/plugins/node/instrumentation-undici/package.json similarity index 97% rename from plugins/node/opentelemetry-instrumentation-undici/package.json rename to plugins/node/instrumentation-undici/package.json index f99e66dadb..c40c74e0f2 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/package.json +++ b/plugins/node/instrumentation-undici/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/instrumentation-undici", - "version": "0.33.0", + "version": "0.1.0", "description": "OpenTelemetry undici/fetch automatic instrumentation package.", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -56,7 +56,7 @@ "test-all-versions": "6.0.0", "ts-mocha": "10.0.0", "typescript": "4.4.4", - "undici": "^6.7.0" + "undici": "6.10.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/enums/SemanticAttributes.ts b/plugins/node/instrumentation-undici/src/enums/SemanticAttributes.ts similarity index 100% rename from plugins/node/opentelemetry-instrumentation-undici/src/enums/SemanticAttributes.ts rename to plugins/node/instrumentation-undici/src/enums/SemanticAttributes.ts diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/index.ts b/plugins/node/instrumentation-undici/src/index.ts similarity index 100% rename from plugins/node/opentelemetry-instrumentation-undici/src/index.ts rename to plugins/node/instrumentation-undici/src/index.ts diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts b/plugins/node/instrumentation-undici/src/internal-types.ts similarity index 100% rename from plugins/node/opentelemetry-instrumentation-undici/src/internal-types.ts rename to plugins/node/instrumentation-undici/src/internal-types.ts diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/types.ts b/plugins/node/instrumentation-undici/src/types.ts similarity index 100% rename from plugins/node/opentelemetry-instrumentation-undici/src/types.ts rename to plugins/node/instrumentation-undici/src/types.ts diff --git a/plugins/node/opentelemetry-instrumentation-undici/src/undici.ts b/plugins/node/instrumentation-undici/src/undici.ts similarity index 100% rename from plugins/node/opentelemetry-instrumentation-undici/src/undici.ts rename to plugins/node/instrumentation-undici/src/undici.ts diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts b/plugins/node/instrumentation-undici/test/fetch.test.ts similarity index 100% rename from plugins/node/opentelemetry-instrumentation-undici/test/fetch.test.ts rename to plugins/node/instrumentation-undici/test/fetch.test.ts diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/metrics.test.ts b/plugins/node/instrumentation-undici/test/metrics.test.ts similarity index 100% rename from plugins/node/opentelemetry-instrumentation-undici/test/metrics.test.ts rename to plugins/node/instrumentation-undici/test/metrics.test.ts diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts b/plugins/node/instrumentation-undici/test/undici.test.ts similarity index 100% rename from plugins/node/opentelemetry-instrumentation-undici/test/undici.test.ts rename to plugins/node/instrumentation-undici/test/undici.test.ts diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/utils/assertSpan.ts b/plugins/node/instrumentation-undici/test/utils/assertSpan.ts similarity index 96% rename from plugins/node/opentelemetry-instrumentation-undici/test/utils/assertSpan.ts rename to plugins/node/instrumentation-undici/test/utils/assertSpan.ts index f5bcfded0d..24f451be42 100644 --- a/plugins/node/opentelemetry-instrumentation-undici/test/utils/assertSpan.ts +++ b/plugins/node/instrumentation-undici/test/utils/assertSpan.ts @@ -172,10 +172,8 @@ export const assertSpan = ( ); if (validations.reqHeaders) { - const userAgent = getHeader(validations.reqHeaders, 'content-length'); - // validations.reqHeaders instanceof Headers - // ? validations.reqHeaders.get('user-agent') - // : validations.reqHeaders['user-agent']; + const userAgent = getHeader(validations.reqHeaders, 'user-agent'); + if (userAgent) { assert.strictEqual( span.attributes[SemanticAttributes.USER_AGENT_ORIGINAL], diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-metrics-reader.ts b/plugins/node/instrumentation-undici/test/utils/mock-metrics-reader.ts similarity index 100% rename from plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-metrics-reader.ts rename to plugins/node/instrumentation-undici/test/utils/mock-metrics-reader.ts diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-propagation.ts b/plugins/node/instrumentation-undici/test/utils/mock-propagation.ts similarity index 100% rename from plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-propagation.ts rename to plugins/node/instrumentation-undici/test/utils/mock-propagation.ts diff --git a/plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-server.ts b/plugins/node/instrumentation-undici/test/utils/mock-server.ts similarity index 100% rename from plugins/node/opentelemetry-instrumentation-undici/test/utils/mock-server.ts rename to plugins/node/instrumentation-undici/test/utils/mock-server.ts diff --git a/plugins/node/opentelemetry-instrumentation-undici/tsconfig.json b/plugins/node/instrumentation-undici/tsconfig.json similarity index 100% rename from plugins/node/opentelemetry-instrumentation-undici/tsconfig.json rename to plugins/node/instrumentation-undici/tsconfig.json From b95aae6e75a7cd0a76bbe01fe29a4b2bc26d10e5 Mon Sep 17 00:00:00 2001 From: David Luna Date: Mon, 25 Mar 2024 16:35:10 +0100 Subject: [PATCH 56/81] chore(instrumentation-undici): update package-lock.json --- package-lock.json | 312 +++++++++++++++++++++++++--------------------- 1 file changed, 172 insertions(+), 140 deletions(-) diff --git a/package-lock.json b/package-lock.json index 74eb976992..18a96e847f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8512,7 +8512,7 @@ "link": true }, "node_modules/@opentelemetry/instrumentation-undici": { - "resolved": "plugins/node/opentelemetry-instrumentation-undici", + "resolved": "plugins/node/instrumentation-undici", "link": true }, "node_modules/@opentelemetry/instrumentation-user-interaction": { @@ -34292,9 +34292,9 @@ } }, "node_modules/undici": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.7.1.tgz", - "integrity": "sha512-+Wtb9bAQw6HYWzCnxrPTMVEV3Q1QjYanI0E4q02ehReMuquQdLTEFEYbfs7hcImVYKcQkWSwT6buEmSVIiDDtQ==", + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.10.1.tgz", + "integrity": "sha512-kSzmWrOx3XBKTgPm4Tal8Hyl3yf+hzlA00SAf4goxv8LZYafKmS6gJD/7Fe5HH/DMNiFTRXvkwhLo7mUn5fuQQ==", "dev": true, "engines": { "node": ">=18.0" @@ -37256,6 +37256,168 @@ "@opentelemetry/api": "^1.3.0" } }, + "plugins/node/instrumentation-undici": { + "version": "0.1.0", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.49.1" + }, + "devDependencies": { + "@opentelemetry/api": "^1.3.0", + "@opentelemetry/sdk-metrics": "^1.8.0", + "@opentelemetry/sdk-trace-base": "^1.8.0", + "@opentelemetry/sdk-trace-node": "^1.8.0", + "@types/mocha": "7.0.2", + "@types/node": "18.6.5", + "mocha": "7.2.0", + "nyc": "15.1.0", + "rimraf": "5.0.5", + "semver": "^7.6.0", + "superagent": "8.0.9", + "test-all-versions": "6.0.0", + "ts-mocha": "10.0.0", + "typescript": "4.4.4", + "undici": "6.10.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "plugins/node/instrumentation-undici/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "plugins/node/instrumentation-undici/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "plugins/node/instrumentation-undici/node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "plugins/node/instrumentation-undici/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "plugins/node/instrumentation-undici/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "plugins/node/instrumentation-undici/node_modules/is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "dev": true, + "dependencies": { + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "plugins/node/instrumentation-undici/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "plugins/node/instrumentation-undici/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "plugins/node/instrumentation-undici/node_modules/test-all-versions": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-all-versions/-/test-all-versions-6.0.0.tgz", + "integrity": "sha512-/9wVTBRa7+arvItGinCYy/8+z7sHTsrs9cwEY/xAnzrkSEM7Tp2Cz49ewYZYuO1YYMLqxEaQp2g7Dnns7n7BGA==", + "dev": true, + "dependencies": { + "after-all-results": "^2.0.0", + "ansi-diff-stream": "^1.2.1", + "cli-spinners": "^2.9.2", + "deepmerge": "^4.3.1", + "import-fresh": "^3.3.0", + "is-ci": "^3.0.1", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimist": "^1.2.8", + "npm-package-versions": "^1.0.1", + "once": "^1.4.0", + "parse-env-string": "^1.0.1", + "resolve": "^1.22.8", + "semver": "^7.5.4", + "spawn-npm-install": "^1.2.0", + "which": "^2.0.2" + }, + "bin": { + "tav": "index.js" + }, + "engines": { + "node": ">=14" + } + }, "plugins/node/opentelemetry-instrumentation-aws-lambda": { "name": "@opentelemetry/instrumentation-aws-lambda", "version": "0.39.0", @@ -38683,6 +38845,7 @@ "plugins/node/opentelemetry-instrumentation-undici": { "name": "@opentelemetry/instrumentation-undici", "version": "0.33.0", + "extraneous": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^1.8.0", @@ -38712,137 +38875,6 @@ "@opentelemetry/api": "^1.3.0" } }, - "plugins/node/opentelemetry-instrumentation-undici/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "plugins/node/opentelemetry-instrumentation-undici/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "plugins/node/opentelemetry-instrumentation-undici/node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "plugins/node/opentelemetry-instrumentation-undici/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "plugins/node/opentelemetry-instrumentation-undici/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "plugins/node/opentelemetry-instrumentation-undici/node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "dev": true, - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "plugins/node/opentelemetry-instrumentation-undici/node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "plugins/node/opentelemetry-instrumentation-undici/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "plugins/node/opentelemetry-instrumentation-undici/node_modules/test-all-versions": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-all-versions/-/test-all-versions-6.0.0.tgz", - "integrity": "sha512-/9wVTBRa7+arvItGinCYy/8+z7sHTsrs9cwEY/xAnzrkSEM7Tp2Cz49ewYZYuO1YYMLqxEaQp2g7Dnns7n7BGA==", - "dev": true, - "dependencies": { - "after-all-results": "^2.0.0", - "ansi-diff-stream": "^1.2.1", - "cli-spinners": "^2.9.2", - "deepmerge": "^4.3.1", - "import-fresh": "^3.3.0", - "is-ci": "^3.0.1", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimist": "^1.2.8", - "npm-package-versions": "^1.0.1", - "once": "^1.4.0", - "parse-env-string": "^1.0.1", - "resolve": "^1.22.8", - "semver": "^7.5.4", - "spawn-npm-install": "^1.2.0", - "which": "^2.0.2" - }, - "bin": { - "tav": "index.js" - }, - "engines": { - "node": ">=14" - } - }, "plugins/node/opentelemetry-instrumentation-winston": { "name": "@opentelemetry/instrumentation-winston", "version": "0.35.0", @@ -47440,7 +47472,7 @@ } }, "@opentelemetry/instrumentation-undici": { - "version": "file:plugins/node/opentelemetry-instrumentation-undici", + "version": "file:plugins/node/instrumentation-undici", "requires": { "@opentelemetry/api": "^1.3.0", "@opentelemetry/core": "^1.8.0", @@ -47458,7 +47490,7 @@ "test-all-versions": "6.0.0", "ts-mocha": "10.0.0", "typescript": "4.4.4", - "undici": "^6.7.0" + "undici": "6.10.1" }, "dependencies": { "ansi-styles": { @@ -68504,9 +68536,9 @@ } }, "undici": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.7.1.tgz", - "integrity": "sha512-+Wtb9bAQw6HYWzCnxrPTMVEV3Q1QjYanI0E4q02ehReMuquQdLTEFEYbfs7hcImVYKcQkWSwT6buEmSVIiDDtQ==", + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.10.1.tgz", + "integrity": "sha512-kSzmWrOx3XBKTgPm4Tal8Hyl3yf+hzlA00SAf4goxv8LZYafKmS6gJD/7Fe5HH/DMNiFTRXvkwhLo7mUn5fuQQ==", "dev": true }, "undici-types": { From b438dfc0a4137a28ea5c865dc275332e2dfdad05 Mon Sep 17 00:00:00 2001 From: David Luna Date: Mon, 25 Mar 2024 16:40:10 +0100 Subject: [PATCH 57/81] chore(instrumentation-undici): update release please manifest --- .release-please-manifest.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 6f71c7a7a2..07ff591427 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -14,6 +14,7 @@ "packages/opentelemetry-redis-common": "0.36.1", "packages/opentelemetry-sql-common": "0.40.0", "packages/opentelemetry-test-utils": "0.37.0", + "packages/winston-transport": "0.1.0", "plugins/node/instrumentation-amqplib": "0.35.0", "plugins/node/instrumentation-cucumber": "0.4.0", "plugins/node/instrumentation-dataloader": "0.7.0", From 4a3962001a2be7728fb1d6714009de6c5068fa7b Mon Sep 17 00:00:00 2001 From: David Luna Date: Mon, 25 Mar 2024 16:41:39 +0100 Subject: [PATCH 58/81] chore(instrumentation-undici): remove test leftovers --- plugins/node/instrumentation-undici/test/metrics.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/node/instrumentation-undici/test/metrics.test.ts b/plugins/node/instrumentation-undici/test/metrics.test.ts index 3a653b40e9..ee8efbfcb7 100644 --- a/plugins/node/instrumentation-undici/test/metrics.test.ts +++ b/plugins/node/instrumentation-undici/test/metrics.test.ts @@ -40,7 +40,6 @@ const hostname = 'localhost'; const mockServer = new MockServer(); const provider = new NodeTracerProvider(); const meterProvider = new MeterProvider(); -// const memoryExporter = new InMemorySpanExporter(); const metricsMemoryExporter = new InMemoryMetricExporter( AggregationTemporality.DELTA ); From 44f53e22cf6942d5f70bb00e26c0d884566d6abc Mon Sep 17 00:00:00 2001 From: David Luna Date: Mon, 25 Mar 2024 17:43:48 +0100 Subject: [PATCH 59/81] chore(instrumentation-undici): add explicitBucketBoundaries --- .release-please-manifest.json | 2 +- plugins/node/instrumentation-undici/src/undici.ts | 3 +++ release-please-config.json | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 07ff591427..7b6cc8da32 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -24,6 +24,7 @@ "plugins/node/instrumentation-runtime-node": "0.2.1", "plugins/node/instrumentation-socket.io": "0.37.0", "plugins/node/instrumentation-tedious": "0.8.0", + "plugins/node/instrumentation-undici": "0.1.0", "plugins/node/opentelemetry-instrumentation-aws-lambda": "0.39.0", "plugins/node/opentelemetry-instrumentation-aws-sdk": "0.39.1", "plugins/node/opentelemetry-instrumentation-bunyan": "0.36.0", @@ -50,7 +51,6 @@ "plugins/node/opentelemetry-instrumentation-redis-4": "0.37.0", "plugins/node/opentelemetry-instrumentation-restify": "0.36.0", "plugins/node/opentelemetry-instrumentation-router": "0.35.0", - "plugins/node/opentelemetry-instrumentation-undici": "0.1.0", "plugins/node/opentelemetry-instrumentation-winston": "0.35.0", "plugins/web/opentelemetry-instrumentation-document-load": "0.36.0", "plugins/web/opentelemetry-instrumentation-long-task": "0.36.0", diff --git a/plugins/node/instrumentation-undici/src/undici.ts b/plugins/node/instrumentation-undici/src/undici.ts index 27e652e340..1e0fd8e843 100644 --- a/plugins/node/instrumentation-undici/src/undici.ts +++ b/plugins/node/instrumentation-undici/src/undici.ts @@ -128,6 +128,9 @@ export class UndiciInstrumentation extends InstrumentationBase { description: 'Measures the duration of outbound HTTP requests.', unit: 's', valueType: ValueType.DOUBLE, + advice: { + explicitBucketBoundaries: [0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10] + } } ); } diff --git a/release-please-config.json b/release-please-config.json index 1d30957152..6abdc0d279 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -30,6 +30,7 @@ "plugins/node/instrumentation-runtime-node": {}, "plugins/node/instrumentation-socket.io": {}, "plugins/node/instrumentation-tedious": {}, + "plugins/node/instrumentation-undici": {}, "plugins/node/opentelemetry-instrumentation-aws-lambda": {}, "plugins/node/opentelemetry-instrumentation-aws-sdk": {}, "plugins/node/opentelemetry-instrumentation-bunyan": {}, @@ -56,7 +57,6 @@ "plugins/node/opentelemetry-instrumentation-redis-4": {}, "plugins/node/opentelemetry-instrumentation-restify": {}, "plugins/node/opentelemetry-instrumentation-router": {}, - "plugins/node/opentelemetry-instrumentation-undici": {}, "plugins/node/opentelemetry-instrumentation-winston": {}, "plugins/web/opentelemetry-instrumentation-document-load": {}, "plugins/web/opentelemetry-instrumentation-long-task": {}, From 46226e1bbee4d9586379045fd9d326d8ba7a15f2 Mon Sep 17 00:00:00 2001 From: David Luna Date: Mon, 25 Mar 2024 17:53:49 +0100 Subject: [PATCH 60/81] chore(instrumentation-undici): fix lint errors --- plugins/node/instrumentation-undici/src/undici.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/node/instrumentation-undici/src/undici.ts b/plugins/node/instrumentation-undici/src/undici.ts index 1e0fd8e843..98eee28774 100644 --- a/plugins/node/instrumentation-undici/src/undici.ts +++ b/plugins/node/instrumentation-undici/src/undici.ts @@ -129,8 +129,11 @@ export class UndiciInstrumentation extends InstrumentationBase { unit: 's', valueType: ValueType.DOUBLE, advice: { - explicitBucketBoundaries: [0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10] - } + explicitBucketBoundaries: [ + 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, + 7.5, 10, + ], + }, } ); } From f7deea833af8896217991abd5c6e57a4ea755d8e Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 27 Mar 2024 16:42:38 +0100 Subject: [PATCH 61/81] Update plugins/node/instrumentation-undici/README.md Co-authored-by: Marc Pichler --- plugins/node/instrumentation-undici/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/instrumentation-undici/README.md b/plugins/node/instrumentation-undici/README.md index 605da7bd2a..801474972f 100644 --- a/plugins/node/instrumentation-undici/README.md +++ b/plugins/node/instrumentation-undici/README.md @@ -5,7 +5,7 @@ **Note: This is an experimental package under active development. New releases may include breaking changes.** -This module provides automatic instrumentation for [`undici`](https://undici.nodejs.org/) and NodeJs global [`fetch`](https://nodejs.org/docs/latest/api/globals.html#fetch) API. +This module provides automatic instrumentation for [`undici`](https://undici.nodejs.org/) and Node.js global [`fetch`](https://nodejs.org/docs/latest/api/globals.html#fetch) API. If you're looking the instrumentation for browser's `fetch` API it is located at [https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation-fetch/](https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation-fetch/) ## Installation From 782679887a5ca0ed894d7fe19b6c72f49cafe860 Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 27 Mar 2024 16:42:47 +0100 Subject: [PATCH 62/81] Update plugins/node/instrumentation-undici/.tav.yml Co-authored-by: Marc Pichler --- plugins/node/instrumentation-undici/.tav.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/instrumentation-undici/.tav.yml b/plugins/node/instrumentation-undici/.tav.yml index 9720cebbca..9ef69e2462 100644 --- a/plugins/node/instrumentation-undici/.tav.yml +++ b/plugins/node/instrumentation-undici/.tav.yml @@ -5,4 +5,4 @@ undici: commands: npm run test - versions: ">=6 <7" node: '>=18' - commands: npm run test \ No newline at end of file + commands: npm run test From 6996d5e6015944ef615bfaddde89f5f35bcbd073 Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 27 Mar 2024 16:43:11 +0100 Subject: [PATCH 63/81] Update plugins/node/instrumentation-undici/test/fetch.test.ts Co-authored-by: Marc Pichler --- plugins/node/instrumentation-undici/test/fetch.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/node/instrumentation-undici/test/fetch.test.ts b/plugins/node/instrumentation-undici/test/fetch.test.ts index f7e4321198..ed4719983f 100644 --- a/plugins/node/instrumentation-undici/test/fetch.test.ts +++ b/plugins/node/instrumentation-undici/test/fetch.test.ts @@ -305,8 +305,6 @@ describe('UndiciInstrumentation `fetch` tests', function () { const fetchUrl = `${protocol}://${hostname}:${mockServer.port}/?query=test`; const response = await fetch(fetchUrl); - // TODO: here we're checking the propagation works even if the instrumentation - // is not starting any span. Not 100% sure this is the behaviour we want assert.ok( response.headers.get('propagation-error') == null, 'propagation is set for instrumented requests' From ed33c22025684f63dc05604ae80f76e3adfef96d Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 27 Mar 2024 16:43:21 +0100 Subject: [PATCH 64/81] Update plugins/node/instrumentation-undici/test/metrics.test.ts Co-authored-by: Marc Pichler --- plugins/node/instrumentation-undici/test/metrics.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/node/instrumentation-undici/test/metrics.test.ts b/plugins/node/instrumentation-undici/test/metrics.test.ts index ee8efbfcb7..952c02676a 100644 --- a/plugins/node/instrumentation-undici/test/metrics.test.ts +++ b/plugins/node/instrumentation-undici/test/metrics.test.ts @@ -45,7 +45,6 @@ const metricsMemoryExporter = new InMemoryMetricExporter( ); const metricReader = new MockMetricsReader(metricsMemoryExporter); meterProvider.addMetricReader(metricReader); -// provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); instrumentation.setTracerProvider(provider); instrumentation.setMeterProvider(meterProvider); From f26802e38a1b080ea6b8b41e857e206dafcd32d9 Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 27 Mar 2024 16:43:34 +0100 Subject: [PATCH 65/81] Update plugins/node/instrumentation-undici/test/undici.test.ts Co-authored-by: Marc Pichler --- plugins/node/instrumentation-undici/test/undici.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/node/instrumentation-undici/test/undici.test.ts b/plugins/node/instrumentation-undici/test/undici.test.ts index 1ddcd84987..5e236fc337 100644 --- a/plugins/node/instrumentation-undici/test/undici.test.ts +++ b/plugins/node/instrumentation-undici/test/undici.test.ts @@ -673,8 +673,6 @@ describe('UndiciInstrumentation `undici` tests', function () { await consumeResponseBody(response.body); span.end(); - // TODO: here we're checking the propagation works even if the instrumentation - // is not starting any span. Not 100% sure this is the behaviour we want assert.ok( response.headers['propagation-error'] == null, 'propagation is set for instrumented requests' From 3a88a0d0c2f09da8bfb094bd122d760876ff55d1 Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 27 Mar 2024 16:44:07 +0100 Subject: [PATCH 66/81] Update plugins/node/instrumentation-undici/package.json Co-authored-by: Marc Pichler --- plugins/node/instrumentation-undici/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/instrumentation-undici/package.json b/plugins/node/instrumentation-undici/package.json index c40c74e0f2..8edc466830 100644 --- a/plugins/node/instrumentation-undici/package.json +++ b/plugins/node/instrumentation-undici/package.json @@ -59,7 +59,7 @@ "undici": "6.10.1" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@opentelemetry/api": "^1.7.0" }, "dependencies": { "@opentelemetry/core": "^1.8.0", From 11087b63d434f41fe7a66db6d85111df9b3bfeb1 Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 27 Mar 2024 16:44:35 +0100 Subject: [PATCH 67/81] Update plugins/node/instrumentation-undici/test/metrics.test.ts Co-authored-by: Marc Pichler --- plugins/node/instrumentation-undici/test/metrics.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/instrumentation-undici/test/metrics.test.ts b/plugins/node/instrumentation-undici/test/metrics.test.ts index 952c02676a..e6770d6a22 100644 --- a/plugins/node/instrumentation-undici/test/metrics.test.ts +++ b/plugins/node/instrumentation-undici/test/metrics.test.ts @@ -51,7 +51,7 @@ instrumentation.setMeterProvider(meterProvider); describe('UndiciInstrumentation metrics tests', function () { before(function (done) { // Do not test if the `fetch` global API is not available - // This applies to nodejs < v18 or nodejs < v16.15 wihtout the flag + // This applies to nodejs < v18 or nodejs < v16.15 without the flag // `--experimental-global-fetch` set // https://nodejs.org/api/globals.html#fetch if (typeof globalThis.fetch !== 'function') { From e90516d76372c795aa08385ceadfd5f0e7d193a7 Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 27 Mar 2024 16:44:45 +0100 Subject: [PATCH 68/81] Update plugins/node/instrumentation-undici/test/metrics.test.ts Co-authored-by: Marc Pichler --- plugins/node/instrumentation-undici/test/metrics.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/instrumentation-undici/test/metrics.test.ts b/plugins/node/instrumentation-undici/test/metrics.test.ts index e6770d6a22..0fc633ef19 100644 --- a/plugins/node/instrumentation-undici/test/metrics.test.ts +++ b/plugins/node/instrumentation-undici/test/metrics.test.ts @@ -87,7 +87,7 @@ describe('UndiciInstrumentation metrics tests', function () { describe('with fetch API', function () { before(function (done) { // Do not test if the `fetch` global API is not available - // This applies to nodejs < v18 or nodejs < v16.15 wihtout the flag + // This applies to nodejs < v18 or nodejs < v16.15 without the flag // `--experimental-global-fetch` set // https://nodejs.org/api/globals.html#fetch if (typeof globalThis.fetch !== 'function') { From 185bb521f0052d94e424319fa1038c486d4fae37 Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 27 Mar 2024 17:15:53 +0100 Subject: [PATCH 69/81] Update plugins/node/instrumentation-undici/test/utils/assertSpan.ts Co-authored-by: Marc Pichler --- .../node/instrumentation-undici/test/utils/assertSpan.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/plugins/node/instrumentation-undici/test/utils/assertSpan.ts b/plugins/node/instrumentation-undici/test/utils/assertSpan.ts index 24f451be42..3ab0a66d7d 100644 --- a/plugins/node/instrumentation-undici/test/utils/assertSpan.ts +++ b/plugins/node/instrumentation-undici/test/utils/assertSpan.ts @@ -50,12 +50,6 @@ export const assertSpan = ( validations.spanName || validations.httpMethod, 'span.name is correct' ); - // TODO: check this - // assert.strictEqual( - // span.attributes[AttributeNames.HTTP_ERROR_MESSAGE], - // span.status.message, - // `attributes['${AttributeNames.HTTP_ERROR_MESSAGE}'] is correct`, - // ); assert.strictEqual( span.attributes[SemanticAttributes.HTTP_REQUEST_METHOD], validations.httpMethod, From c9aa8def635fe3df4ce8ab96526d034d139c59b6 Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 27 Mar 2024 17:16:07 +0100 Subject: [PATCH 70/81] Update plugins/node/instrumentation-undici/test/fetch.test.ts Co-authored-by: Marc Pichler --- plugins/node/instrumentation-undici/test/fetch.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/node/instrumentation-undici/test/fetch.test.ts b/plugins/node/instrumentation-undici/test/fetch.test.ts index ed4719983f..93b6259158 100644 --- a/plugins/node/instrumentation-undici/test/fetch.test.ts +++ b/plugins/node/instrumentation-undici/test/fetch.test.ts @@ -333,8 +333,6 @@ describe('UndiciInstrumentation `fetch` tests', function () { const response = await fetch(fetchUrl); span.end(); - // TODO: here we're checking the propagation works even if the instrumentation - // is not starting any span. Not 100% sure this is the behaviour we want assert.ok( response.headers.get('propagation-error') == null, 'propagation is set for instrumented requests' From 82388af68fd557f17574fa41cbcb420ddc533252 Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 27 Mar 2024 18:59:59 +0100 Subject: [PATCH 71/81] chore: update package-lock.json --- package-lock.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index e2990c2133..c51457962f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37324,6 +37324,7 @@ } }, "plugins/node/instrumentation-undici": { + "name": "@opentelemetry/instrumentation-undici", "version": "0.1.0", "license": "Apache-2.0", "dependencies": { @@ -37351,7 +37352,7 @@ "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@opentelemetry/api": "^1.7.0" } }, "plugins/node/instrumentation-undici/node_modules/ansi-styles": { From 03b5c5d91f2d6983b4e095b9c4a0a35618da4066 Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 27 Mar 2024 23:24:02 +0100 Subject: [PATCH 72/81] chore: fix peer api check issue --- plugins/node/instrumentation-undici/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/instrumentation-undici/package.json b/plugins/node/instrumentation-undici/package.json index 8edc466830..f5465d3317 100644 --- a/plugins/node/instrumentation-undici/package.json +++ b/plugins/node/instrumentation-undici/package.json @@ -42,7 +42,7 @@ "access": "public" }, "devDependencies": { - "@opentelemetry/api": "^1.3.0", + "@opentelemetry/api": "^1.7.0", "@opentelemetry/sdk-metrics": "^1.8.0", "@opentelemetry/sdk-trace-base": "^1.8.0", "@opentelemetry/sdk-trace-node": "^1.8.0", From 3f638828b2d7c46c852b331ec236c3c27e427425 Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 3 Apr 2024 11:00:43 +0200 Subject: [PATCH 73/81] Update plugins/node/instrumentation-undici/test/undici.test.ts Co-authored-by: Marc Pichler --- plugins/node/instrumentation-undici/test/undici.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/node/instrumentation-undici/test/undici.test.ts b/plugins/node/instrumentation-undici/test/undici.test.ts index 5e236fc337..7bd57d3e0e 100644 --- a/plugins/node/instrumentation-undici/test/undici.test.ts +++ b/plugins/node/instrumentation-undici/test/undici.test.ts @@ -642,8 +642,6 @@ describe('UndiciInstrumentation `undici` tests', function () { const response = await undici.request(requestUrl); await consumeResponseBody(response.body); - // TODO: here we're checking the propagation works even if the instrumentation - // is not starting any span. Not 100% sure this is the behaviour we want assert.ok( response.headers['propagation-error'] == null, 'propagation is set for instrumented requests' From 38ba3b81b73bd85e6f9eaef22f30ec3eddf34bbd Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 3 Apr 2024 12:36:10 +0200 Subject: [PATCH 74/81] chore(instrumentation-undici): remove applyCustomAttributesOnSpan config --- package-lock.json | 4 +-- plugins/node/instrumentation-undici/README.md | 3 +-- .../node/instrumentation-undici/src/index.ts | 1 - .../node/instrumentation-undici/src/types.ts | 17 ++---------- .../node/instrumentation-undici/src/undici.ts | 11 +------- .../instrumentation-undici/test/fetch.test.ts | 11 -------- .../test/undici.test.ts | 26 ------------------- 7 files changed, 6 insertions(+), 67 deletions(-) diff --git a/package-lock.json b/package-lock.json index 801a84a4ca..93351b5a2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37385,7 +37385,7 @@ "@opentelemetry/instrumentation": "^0.49.1" }, "devDependencies": { - "@opentelemetry/api": "^1.3.0", + "@opentelemetry/api": "^1.7.0", "@opentelemetry/sdk-metrics": "^1.8.0", "@opentelemetry/sdk-trace-base": "^1.8.0", "@opentelemetry/sdk-trace-node": "^1.8.0", @@ -47390,7 +47390,7 @@ "@opentelemetry/instrumentation-undici": { "version": "file:plugins/node/instrumentation-undici", "requires": { - "@opentelemetry/api": "^1.3.0", + "@opentelemetry/api": "^1.7.0", "@opentelemetry/core": "^1.8.0", "@opentelemetry/instrumentation": "^0.49.1", "@opentelemetry/sdk-metrics": "^1.8.0", diff --git a/plugins/node/instrumentation-undici/README.md b/plugins/node/instrumentation-undici/README.md index 801474972f..eb6f6b1775 100644 --- a/plugins/node/instrumentation-undici/README.md +++ b/plugins/node/instrumentation-undici/README.md @@ -46,8 +46,7 @@ Undici instrumentation has few options available to choose from. You can set the | Options | Type | Description | | ------- | ---- | ----------- | -| [`ignoreRequestHook`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#61) | `IgnoreRequestFunction` | Undici instrumentation will not trace all incoming requests that matched with custom function. | -| [`applyCustomAttributesOnSpan`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#63) | `CustomAttributesFunction` | Function for adding custom attributes before response is handled. | +| [`ignoreRequestHook`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#63) | `IgnoreRequestFunction` | Undici instrumentation will not trace all incoming requests that matched with custom function. | | [`requestHook`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#65) | `RequestHookFunction` | Function for adding custom attributes before request is handled. | | [`startSpanHook`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#67) | `StartSpanHookFunction` | Function for adding custom attributes before a span is started. | | [`requireParentforSpans`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#69) | `Boolean` | Require a parent span is present to create new span for outgoing requests. | diff --git a/plugins/node/instrumentation-undici/src/index.ts b/plugins/node/instrumentation-undici/src/index.ts index 87ca2686d5..80c6504fbc 100644 --- a/plugins/node/instrumentation-undici/src/index.ts +++ b/plugins/node/instrumentation-undici/src/index.ts @@ -19,7 +19,6 @@ export { UndiciRequest, UndiciResponse, IgnoreRequestFunction, - CustomAttributesFunction, RequestHookFunction, StartSpanHookFunction, UndiciInstrumentationConfig, diff --git a/plugins/node/instrumentation-undici/src/types.ts b/plugins/node/instrumentation-undici/src/types.ts index 0d9323dec3..0d28e1fff9 100644 --- a/plugins/node/instrumentation-undici/src/types.ts +++ b/plugins/node/instrumentation-undici/src/types.ts @@ -46,12 +46,6 @@ export interface UndiciResponse { export interface IgnoreRequestFunction { (request: T): boolean; } -export interface CustomAttributesFunction< - T = UndiciRequest, - Q = UndiciResponse -> { - (span: Span, request: T, response: Q): void; -} export interface RequestHookFunction { (span: Span, request: T): void; @@ -63,17 +57,10 @@ export interface StartSpanHookFunction { // This package will instrument HTTP requests made through `undici` or `fetch` global API // so it seems logical to have similar options than the HTTP instrumentation -export interface UndiciInstrumentationConfig< - RequestType = UndiciRequest, - ResponseType = UndiciResponse -> extends InstrumentationConfig { +export interface UndiciInstrumentationConfig + extends InstrumentationConfig { /** Not trace all outgoing requests that matched with custom function */ ignoreRequestHook?: IgnoreRequestFunction; - /** Function for adding custom attributes after response is handled */ - applyCustomAttributesOnSpan?: CustomAttributesFunction< - RequestType, - ResponseType - >; /** Function for adding custom attributes before request is handled */ requestHook?: RequestHookFunction; /** Function for adding custom attributes before a span is started */ diff --git a/plugins/node/instrumentation-undici/src/undici.ts b/plugins/node/instrumentation-undici/src/undici.ts index 98eee28774..262bea42cd 100644 --- a/plugins/node/instrumentation-undici/src/undici.ts +++ b/plugins/node/instrumentation-undici/src/undici.ts @@ -383,24 +383,15 @@ export class UndiciInstrumentation extends InstrumentationBase { } // This is the last event we receive if the request went without any errors - private onDone({ request, response }: RequestTrailersMessage): void { + private onDone({ request }: RequestTrailersMessage): void { const record = this._recordFromReq.get(request); if (!record) { return; } - const config = this._getConfig(); const { span, attributes, startTime } = record; - // Let the user apply custom attribs before ending the span - safeExecuteInTheMiddle( - () => config.applyCustomAttributesOnSpan?.(span, request, response), - e => - e && this._diag.error('caught applyCustomAttributesOnSpan error: ', e), - true - ); - // End the span span.end(); this._recordFromReq.delete(request); diff --git a/plugins/node/instrumentation-undici/test/fetch.test.ts b/plugins/node/instrumentation-undici/test/fetch.test.ts index 93b6259158..96fa7090f4 100644 --- a/plugins/node/instrumentation-undici/test/fetch.test.ts +++ b/plugins/node/instrumentation-undici/test/fetch.test.ts @@ -139,9 +139,6 @@ describe('UndiciInstrumentation `fetch` tests', function () { ignoreRequestHook: () => { throw new Error('ignoreRequestHook error'); }, - applyCustomAttributesOnSpan: () => { - throw new Error('ignoreRequestHook error'); - }, requestHook: () => { throw new Error('requestHook error'); }, @@ -225,9 +222,6 @@ describe('UndiciInstrumentation `fetch` tests', function () { requestHeaders: ['foo-client', 'x-requested-with'], responseHeaders: ['foo-server'], }, - applyCustomAttributesOnSpan: (span, req, res) => { - span.setAttribute('user.defined.attribute', 'user.defined.value'); - }, }); // Do some requests @@ -287,11 +281,6 @@ describe('UndiciInstrumentation `fetch` tests', function () { 'hook-value', 'startSpanHook is called' ); - assert.strictEqual( - span.attributes['user.defined.attribute'], - 'user.defined.value', - 'applyCustomAttributesOnSpan is called' - ); }); it('should not create spans without parent if required in configuration', async function () { diff --git a/plugins/node/instrumentation-undici/test/undici.test.ts b/plugins/node/instrumentation-undici/test/undici.test.ts index 7bd57d3e0e..d364226e16 100644 --- a/plugins/node/instrumentation-undici/test/undici.test.ts +++ b/plugins/node/instrumentation-undici/test/undici.test.ts @@ -175,9 +175,6 @@ describe('UndiciInstrumentation `undici` tests', function () { requestHeaders: ['foo-client', 'x-requested-with'], responseHeaders: ['foo-server'], }, - applyCustomAttributesOnSpan: (span, req, res) => { - span.setAttribute('user.defined.attribute', 'user.defined.value'); - }, }); }); afterEach(function () { @@ -358,11 +355,6 @@ describe('UndiciInstrumentation `undici` tests', function () { 'hook-value', 'startSpanHook is called' ); - assert.strictEqual( - span.attributes['user.defined.attribute'], - 'user.defined.value', - 'applyCustomAttributesOnSpan is called' - ); }); it('should create valid spans for "fetch" method', async function () { @@ -423,11 +415,6 @@ describe('UndiciInstrumentation `undici` tests', function () { 'hook-value', 'startSpanHook is called' ); - assert.strictEqual( - span.attributes['user.defined.attribute'], - 'user.defined.value', - 'applyCustomAttributesOnSpan is called' - ); }); it('should create valid spans for "stream" method', async function () { @@ -496,11 +483,6 @@ describe('UndiciInstrumentation `undici` tests', function () { 'hook-value', 'startSpanHook is called' ); - assert.strictEqual( - span.attributes['user.defined.attribute'], - 'user.defined.value', - 'applyCustomAttributesOnSpan is called' - ); }); it('should create valid spans for "dispatch" method', async function () { @@ -577,11 +559,6 @@ describe('UndiciInstrumentation `undici` tests', function () { 'hook-value', 'startSpanHook is called' ); - assert.strictEqual( - span.attributes['user.defined.attribute'], - 'user.defined.value', - 'applyCustomAttributesOnSpan is called' - ); }); it('should create valid spans even if the configuration hooks fail', async function () { @@ -594,9 +571,6 @@ describe('UndiciInstrumentation `undici` tests', function () { ignoreRequestHook: () => { throw new Error('ignoreRequestHook error'); }, - applyCustomAttributesOnSpan: () => { - throw new Error('applyCustomAttributesOnSpan error'); - }, requestHook: () => { throw new Error('requestHook error'); }, From a9ad5118bc7e0b24e670126e3f651cb57c41da0a Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 3 Apr 2024 13:20:55 +0200 Subject: [PATCH 75/81] chore(instrumentation-undici): update README --- plugins/node/instrumentation-undici/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/node/instrumentation-undici/README.md b/plugins/node/instrumentation-undici/README.md index eb6f6b1775..c9e67d98c8 100644 --- a/plugins/node/instrumentation-undici/README.md +++ b/plugins/node/instrumentation-undici/README.md @@ -52,6 +52,13 @@ Undici instrumentation has few options available to choose from. You can set the | [`requireParentforSpans`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#69) | `Boolean` | Require a parent span is present to create new span for outgoing requests. | | [`headersToSpanAttributes`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-undici/src/types.ts#71) | `Object` | List of case insensitive HTTP headers to convert to span attributes. Headers will be converted to span attributes in the form of `http.{request\|response}.header.header-name` where the name is only lowercased, e.g. `http.response.header.content-length`| +### Observations + +This instrumetation subscribes to certain [diagnostics_channel](https://nodejs.org/api/diagnostics_channel.html) to intercept the client requests +and generate traces and metrics. In particular tracing spans are started when [undici:request:create](https://undici.nodejs.org/#/docs/api/DiagnosticsChannel?id=undicirequestcreate) +channel receives a message and ended when [undici:request:trailers](https://undici.nodejs.org/#/docs/api/DiagnosticsChannel?id=undicirequesttrailers) channel receive a message. +This means the full reponse body is received when the instrumentation ends the span. + ## Semantic Conventions This package uses Semantic Conventions [Version 1.24.0](https://github.com/open-telemetry/semantic-conventions/tree/v1.24.0/docs/http). As for now the Semantic Conventions From fe37e9b6c14eab321ba360464b9925ae76cc5a6b Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 3 Apr 2024 13:43:06 +0200 Subject: [PATCH 76/81] chore(instrumentation-undici): update README --- plugins/node/instrumentation-undici/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/instrumentation-undici/README.md b/plugins/node/instrumentation-undici/README.md index c9e67d98c8..45e79e7706 100644 --- a/plugins/node/instrumentation-undici/README.md +++ b/plugins/node/instrumentation-undici/README.md @@ -57,7 +57,7 @@ Undici instrumentation has few options available to choose from. You can set the This instrumetation subscribes to certain [diagnostics_channel](https://nodejs.org/api/diagnostics_channel.html) to intercept the client requests and generate traces and metrics. In particular tracing spans are started when [undici:request:create](https://undici.nodejs.org/#/docs/api/DiagnosticsChannel?id=undicirequestcreate) channel receives a message and ended when [undici:request:trailers](https://undici.nodejs.org/#/docs/api/DiagnosticsChannel?id=undicirequesttrailers) channel receive a message. -This means the full reponse body is received when the instrumentation ends the span. +This means the full response body has been received when the instrumentation ends the span. ## Semantic Conventions From abbc3adbd361735b157cf590e974c77a967e98d6 Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 3 Apr 2024 17:16:58 +0200 Subject: [PATCH 77/81] Update plugins/node/instrumentation-undici/README.md Co-authored-by: Marc Pichler --- plugins/node/instrumentation-undici/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/instrumentation-undici/README.md b/plugins/node/instrumentation-undici/README.md index 45e79e7706..029fd9c45b 100644 --- a/plugins/node/instrumentation-undici/README.md +++ b/plugins/node/instrumentation-undici/README.md @@ -62,7 +62,7 @@ This means the full response body has been received when the instrumentation end ## Semantic Conventions This package uses Semantic Conventions [Version 1.24.0](https://github.com/open-telemetry/semantic-conventions/tree/v1.24.0/docs/http). As for now the Semantic Conventions -are bundled in this package but eventually will be imported from `@opentelemetry/semantic-conventions` package when it is updated to latests version. +are bundled in this package but eventually will be imported from `@opentelemetry/semantic-conventions` package when it is updated to latest version. Ref: [opentelemetry-js/issues/4235](https://github.com/open-telemetry/opentelemetry-js/issues/4235) Attributes collected: From d6e1d6befa9ffd6ab34469ebc98ac5665f803947 Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 3 Apr 2024 17:17:06 +0200 Subject: [PATCH 78/81] Update plugins/node/instrumentation-undici/test/undici.test.ts Co-authored-by: Marc Pichler --- plugins/node/instrumentation-undici/test/undici.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/instrumentation-undici/test/undici.test.ts b/plugins/node/instrumentation-undici/test/undici.test.ts index d364226e16..fd48803a3f 100644 --- a/plugins/node/instrumentation-undici/test/undici.test.ts +++ b/plugins/node/instrumentation-undici/test/undici.test.ts @@ -182,7 +182,7 @@ describe('UndiciInstrumentation `undici` tests', function () { instrumentation.setConfig({ enabled: false }); }); - it('should ingore requests based on the result of ignoreRequestHook', async function () { + it('should ignore requests based on the result of ignoreRequestHook', async function () { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); From 88c6ce58bc27cdb846187472fea4fd9d2f4fce02 Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 3 Apr 2024 17:17:14 +0200 Subject: [PATCH 79/81] Update plugins/node/instrumentation-undici/README.md Co-authored-by: Marc Pichler --- plugins/node/instrumentation-undici/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/instrumentation-undici/README.md b/plugins/node/instrumentation-undici/README.md index 029fd9c45b..38b6fd6e9e 100644 --- a/plugins/node/instrumentation-undici/README.md +++ b/plugins/node/instrumentation-undici/README.md @@ -54,7 +54,7 @@ Undici instrumentation has few options available to choose from. You can set the ### Observations -This instrumetation subscribes to certain [diagnostics_channel](https://nodejs.org/api/diagnostics_channel.html) to intercept the client requests +This instrumentation subscribes to certain [diagnostics_channel](https://nodejs.org/api/diagnostics_channel.html) to intercept the client requests and generate traces and metrics. In particular tracing spans are started when [undici:request:create](https://undici.nodejs.org/#/docs/api/DiagnosticsChannel?id=undicirequestcreate) channel receives a message and ended when [undici:request:trailers](https://undici.nodejs.org/#/docs/api/DiagnosticsChannel?id=undicirequesttrailers) channel receive a message. This means the full response body has been received when the instrumentation ends the span. From f36a8377d00594a385432f1fd0d9090e88b69681 Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 3 Apr 2024 17:17:21 +0200 Subject: [PATCH 80/81] Update plugins/node/instrumentation-undici/package.json Co-authored-by: Marc Pichler --- plugins/node/instrumentation-undici/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/node/instrumentation-undici/package.json b/plugins/node/instrumentation-undici/package.json index f5465d3317..b235be769a 100644 --- a/plugins/node/instrumentation-undici/package.json +++ b/plugins/node/instrumentation-undici/package.json @@ -63,7 +63,7 @@ }, "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.49.1" + "@opentelemetry/instrumentation": "^0.50.0" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/node/opentelemetry-instrumentation-undici#readme", "sideEffects": false From 1388087e7d48afdfc1e7c20f1984015efe382226 Mon Sep 17 00:00:00 2001 From: David Luna Date: Wed, 3 Apr 2024 19:20:31 +0200 Subject: [PATCH 81/81] chore(instrumentation-undici): update release please manifest --- .release-please-manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 5013c8454b..da8e4144db 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -59,5 +59,5 @@ "propagators/opentelemetry-propagator-aws-xray": "1.3.1", "propagators/opentelemetry-propagator-grpc-census-binary": "0.27.1", "propagators/opentelemetry-propagator-instana": "0.3.1", - "propagators/opentelemetry-propagator-ot-trace": "0.27.1", + "propagators/opentelemetry-propagator-ot-trace": "0.27.1" }